This includes all distributable packages,patches etc. Toolchain packages remain part of the bockbuild repository.
The profiles were also renamed:
'mono-mac-release' -> 'mac-sdk'
'mono-mac-xamarin' -> 'mac-sdk-xamarin'
--- /dev/null
+from bockbuild.package import Package
+
+
+class MonoExtensionsPackage(Package):
+
+ def __init__(self):
+ Package.__init__(self, 'mono-extensions', None,
+ sources=['git@github.com:xamarin/mono-extensions.git'],
+ git_branch=self.profile.release_packages[
+ 'mono'].git_branch
+ )
+ self.source_dir_name = 'mono-extensions'
+
+ # Mono pull requests won't have mono-extensions branches
+ if not self.git_branch or 'pull/' in self.git_branch:
+ warn('Using master branch for mono_extensions')
+ self.git_branch = 'master'
+
+ def build(self):
+ pass
+
+ def install(self):
+ pass
+
+MonoExtensionsPackage()
--- /dev/null
+../mono-mac-release/packaging
\ No newline at end of file
--- /dev/null
+#!/usr/bin/python -u -OO
+
+import itertools
+import os
+import re
+import shutil
+import string
+import sys
+import tempfile
+import traceback
+
+from glob import glob
+
+if __name__ == "__main__":
+ sys.path.append(os.path.realpath('../..'))
+ sys.path.append(os.path.realpath('../../packages'))
+ sys.path.append(os.path.realpath('../mono-mac'))
+
+from MonoReleaseProfile import MonoReleaseProfile
+from bockbuild.util.util import *
+
+
+class MonoXamarinPackageProfile(MonoReleaseProfile):
+
+ def __init__(self):
+ MonoReleaseProfile.__init__(self)
+
+ # add the private stuff
+ self.packages_to_build.extend(['mono-extensions'])
+
+ if self.cmd_options.release_build:
+ self.setup_codesign()
+ else:
+ info("'--release' option not set, will not attempt to sign package!")
+
+ self.cache_host = 'http://storage.bos.xamarin.com/bockbuild_cache/'
+
+ def setup_codesign(self):
+ self.identity = "Developer ID Installer: Xamarin Inc"
+
+ output = backtick("security -v find-identity")
+ if self.identity not in " ".join(output):
+ error("Identity '%s' was not found. You can create an unsigned package by removing '--release' to your command line." % self.identity)
+
+ password = os.getenv("CODESIGN_KEYCHAIN_PASSWORD")
+ if password:
+ print "Unlocking the keychain"
+ run_shell("security unlock-keychain -p %s" % password)
+ else:
+ error("CODESIGN_KEYCHAIN_PASSWORD needs to be defined.")
+
+ def setup_release(self):
+ MonoReleaseProfile.setup_release(self)
+ self.release_packages['mono'].configure_flags.extend(
+ ['--enable-extension-module=xamarin --enable-native-types --enable-pecrypt'])
+ info('Xamarin extensions enabled')
+
+ def run_pkgbuild(self, working_dir, package_type):
+ output = MonoReleaseProfile.run_pkgbuild(
+ self, working_dir, package_type)
+
+ output_unsigned = os.path.join(os.path.dirname(
+ output), os.path.basename(output).replace('.pkg', '.UNSIGNED.pkg'))
+ shutil.move(output, output_unsigned)
+
+ if not self.cmd_options.release_build:
+ return output_unsigned
+
+ productsign = "/usr/bin/productsign"
+ productsign_cmd = ' '.join([productsign,
+ "-s '%s'" % self.identity,
+ "'%s'" % output_unsigned,
+ "'%s'" % output])
+ run_shell(productsign_cmd)
+ os.remove(output_unsigned)
+ self.verify_codesign(output)
+
+ return output
+
+ def verify_codesign(self, pkg):
+ oldcwd = os.getcwd()
+ try:
+ name = os.path.basename(pkg)
+ pkgdir = os.path.dirname(pkg)
+ os.chdir(pkgdir)
+ spctl = "/usr/sbin/spctl"
+ spctl_cmd = ' '.join(
+ [spctl, "-vvv", "--assess", "--type install", name, "2>&1"])
+ output = backtick(spctl_cmd)
+
+ if "accepted" in " ".join(output):
+ warn("%s IS SIGNED" % pkg)
+ else:
+ error("%s IS NOT SIGNED:" % pkg)
+ finally:
+ os.chdir(oldcwd)
+
+if __name__ == "__main__":
+ try:
+ MonoXamarinPackageProfile().build()
+ except Exception as e:
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ error('%s (%s)' % (e, exc_type.__name__), more_output=True)
+ error(('%s:%s @%s\t\t"%s"' % p for p in traceback.extract_tb(
+ exc_traceback)[-3:]), more_output=True)
+ except KeyboardInterrupt:
+ error('Interrupted.')
--- /dev/null
+GnomeXzPackage('atk', version_major='2.8', version_minor='0')
--- /dev/null
+class CairoPackage (CairoGraphicsXzPackage):
+
+ def __init__(self):
+ CairoGraphicsXzPackage.__init__(self, 'cairo', '1.12.14')
+ self.sources.extend([
+ 'patches/cairo-quartz-crash.patch',
+ 'patches/cairo-fix-color-bitmap-fonts.patch',
+ 'patches/cairo-fix-CGFontGetGlyphPath-deprecation.patch',
+ # 'patches/cairo-cglayer.patch',
+ ])
+
+ def prep(self):
+ Package.prep(self)
+
+ if Package.profile.name == 'darwin':
+ for p in range(1, len(self.local_sources)):
+ self.sh('patch -p1 < "%{local_sources[' + str(p) + ']}"')
+
+ def build(self):
+ self.configure_flags = [
+ '--enable-pdf',
+ ]
+
+ if Package.profile.name == 'darwin':
+ self.configure_flags.extend([
+ '--enable-quartz',
+ '--enable-quartz-font',
+ '--enable-quartz-image',
+ '--disable-xlib',
+ '--without-x'
+ ])
+ elif Package.profile.name == 'linux':
+ self.configure_flags.extend([
+ '--disable-quartz',
+ '--with-x'
+ ])
+
+ Package.build(self)
+
+CairoPackage()
--- /dev/null
+SourceForgePackage('expat', 'expat', '2.0.1')
--- /dev/null
+class FontConfigPackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'fontconfig', '2.10.2',
+ configure_flags=['--disable-docs'],
+ sources=[
+ 'http://www.fontconfig.org/release/%{name}-%{version}.tar.gz'
+ ],
+ # note: a non-empty DESTDIR keeps fc-cache from running at
+ # install-time
+ )
+
+ def build(self):
+ if Package.profile.name == 'darwin':
+ self.configure_flags.extend([
+ '--with-cache-dir="~/Library/Caches/com.xamarin.fontconfig"',
+ '--with-default-fonts=/System/Library/Fonts',
+ '--with-add-fonts=/Library/Fonts,/Network/Library/Fonts,/System/Library/Fonts'
+ ])
+ Package.build(self)
+
+FontConfigPackage()
--- /dev/null
+SourceForgePackage('%{name}', 'freetype', '2.5.0.1')
--- /dev/null
+class FsharpPackage(GitHubTarballPackage):
+
+ def __init__(self):
+ GitHubTarballPackage.__init__(self,
+ 'fsharp', 'fsharp',
+ '4.0.1.9',
+ '0a6c66a8f18eb8a5c4d0bfac61d883b6994a918a',
+ configure='./configure --prefix="%{package_prefix}"')
+
+ self.extra_stage_files = [
+ 'lib/mono/xbuild/Microsoft/VisualStudio/v/FSharp/Microsoft.FSharp.Targets']
+
+ def prep(self):
+ Package.prep(self)
+
+ for p in range(1, len(self.sources)):
+ self.sh('patch -p1 < "%{local_sources[' + str(p) + ']}"')
+
+ def build(self):
+ self.sh('autoreconf')
+ Package.configure(self)
+ Package.make(self)
+
+FsharpPackage()
--- /dev/null
+class GdkPixbufPackage (GnomeXzPackage):
+
+ def __init__(self):
+ GnomeXzPackage.__init__(
+ self,
+ 'gdk-pixbuf',
+ version_major='2.28',
+ version_minor='2')
+
+ if Package.profile.name == 'darwin':
+ self.sources.extend([
+ 'patches/gdk-pixbuf/0001-pixbuf-load-2x-variants-as-pixbuf-gobject-data.patch',
+ 'patches/gdk-pixbuf/0001-pixbuf-Add-getter-setter-for-the-2x-variants.patch',
+ ])
+
+ self.configure_flags.extend(['--enable-gtk-doc-html=no'])
+
+ def prep(self):
+ Package.prep(self)
+ if Package.profile.name == 'darwin':
+ for p in range(1, len(self.local_sources)):
+ self.sh(
+ 'patch -p1 --ignore-whitespace < "%{local_sources[' + str(p) + ']}"')
+
+ def deploy(self):
+ self.loaders_cache = 'lib/gdk-pixbuf-2.0/2.10.0/loaders.cache'
+ self.sh('gdk-pixbuf-query-loaders --update-cache ')
+
+ # mark the file for destaging
+ self.sh(
+ 'cp %{staged_profile}/%{loaders_cache} %{staged_profile}/%{loaders_cache}.release')
+ self.extra_stage_files = [self.loaders_cache]
+
+GdkPixbufPackage()
--- /dev/null
+From f6d2db5a0c105785ee6f03717966ef0dbb1421f6 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Tue, 16 Jul 2013 10:32:11 +0200
+Subject: [PATCH] pixbuf: Add getter/setter for the 2x variants
+
+---
+ gdk-pixbuf/gdk-pixbuf-core.h | 3 +++
+ gdk-pixbuf/gdk-pixbuf.c | 22 ++++++++++++++++++++++
+ 2 files changed, 25 insertions(+)
+
+diff --git a/gdk-pixbuf/gdk-pixbuf-core.h b/gdk-pixbuf/gdk-pixbuf-core.h
+index eb4d0a1..60c4ea3 100644
+--- a/gdk-pixbuf/gdk-pixbuf-core.h
++++ b/gdk-pixbuf/gdk-pixbuf-core.h
+@@ -474,6 +474,9 @@ GdkPixbuf *gdk_pixbuf_apply_embedded_orientation (GdkPixbuf *src);
+ const gchar * gdk_pixbuf_get_option (GdkPixbuf *pixbuf,
+ const gchar *key);
+
++GdkPixbuf * gdk_pixbuf_get_hires_variant (GdkPixbuf *pixbuf);
++void gdk_pixbuf_set_hires_variant (GdkPixbuf *pixbuf,
++ GdkPixbuf *hires);
+
+ G_END_DECLS
+
+diff --git a/gdk-pixbuf/gdk-pixbuf.c b/gdk-pixbuf/gdk-pixbuf.c
+index 0e13f27..d61f2c7 100644
+--- a/gdk-pixbuf/gdk-pixbuf.c
++++ b/gdk-pixbuf/gdk-pixbuf.c
+@@ -990,3 +990,25 @@ gdk_pixbuf_get_property (GObject *object,
+ break;
+ }
+ }
++
++GdkPixbuf *
++gdk_pixbuf_get_hires_variant (GdkPixbuf *pixbuf)
++{
++ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
++
++ return g_object_get_data (G_OBJECT (pixbuf),
++ "gdk-pixbuf-2x-variant");
++}
++
++void
++gdk_pixbuf_set_hires_variant (GdkPixbuf *pixbuf,
++ GdkPixbuf *hires)
++{
++ g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
++ g_return_if_fail (GDK_IS_PIXBUF (hires));
++
++ g_object_set_data_full (G_OBJECT (pixbuf),
++ "gdk-pixbuf-2x-variant",
++ g_object_ref (hires),
++ (GDestroyNotify) g_object_unref);
++}
+--
+1.8.3.2
--- /dev/null
+>From de5d91aa15cc98795a68c8e553eb4baadaa0e501 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Fri, 17 May 2013 15:56:28 +0200
+Subject: [PATCH] pixbuf: load "@2x" variants as pixbuf gobject data
+
+if a variant of the filename is found that has a "@2x" appended
+to the file name (before the extension), such file is loaded
+and added as GObject data to the pixbuf
+---
+ gdk-pixbuf/gdk-pixbuf-io.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+
+diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c
+index dac21b8..ed98cd3 100644
+--- a/gdk-pixbuf/gdk-pixbuf-io.c
++++ b/gdk-pixbuf/gdk-pixbuf-io.c
+@@ -1025,6 +1025,40 @@ _gdk_pixbuf_generic_image_load (GdkPixbufModule *module,
+ return pixbuf;
+ }
+
++static gboolean
++_gdk_pixbuf_file_is_scaled (const gchar *filename)
++{
++ gchar *basename, *ext;
++
++ basename = g_path_get_basename (filename);
++ ext = strrchr (basename, '.');
++
++ if (!ext)
++ ext = &basename[strlen(basename)];
++
++ if (ext > basename + 3 && strncmp (ext - 3, "@2x", 3) == 0)
++ return TRUE;
++
++ return FALSE;
++}
++
++static gchar *
++_gdk_pixbuf_compose_scaled_filename (const gchar *filename)
++{
++ gchar *ext, *first, *composed;
++
++ ext = strrchr (filename, '.');
++
++ if (!ext)
++ return NULL;
++
++ first = g_strndup (filename, ext - filename);
++ composed = g_strdup_printf ("%s@2x%s", first, ext);
++ g_free (first);
++
++ return composed;
++}
++
+ /**
+ * gdk_pixbuf_new_from_file:
+ * @filename: Name of file to load, in the GLib file name encoding
+@@ -1049,11 +1083,13 @@ gdk_pixbuf_new_from_file (const char *filename,
+ guchar buffer[SNIFF_BUFFER_SIZE];
+ GdkPixbufModule *image_module;
+ gchar *display_name;
++ gboolean filename_is_scaled;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ display_name = g_filename_display_name (filename);
++ filename_is_scaled = _gdk_pixbuf_file_is_scaled (filename);
+
+ f = g_fopen (filename, "rb");
+ if (!f) {
+@@ -1097,6 +1133,25 @@ gdk_pixbuf_new_from_file (const char *filename,
+ pixbuf = _gdk_pixbuf_generic_image_load (image_module, f, error);
+ fclose (f);
+
++ if (pixbuf && !filename_is_scaled) {
++ GdkPixbuf *scaled_pixbuf = NULL;
++ gchar *scaled_filename;
++
++ scaled_filename = _gdk_pixbuf_compose_scaled_filename (filename);
++
++ if (scaled_filename) {
++ scaled_pixbuf = gdk_pixbuf_new_from_file (scaled_filename, NULL);
++ g_free (scaled_filename);
++ }
++
++ if (scaled_pixbuf) {
++ g_object_set_data_full (G_OBJECT (pixbuf),
++ "gdk-pixbuf-2x-variant",
++ scaled_pixbuf,
++ (GDestroyNotify) g_object_unref);
++ }
++ }
++
+ if (pixbuf == NULL && error != NULL && *error == NULL) {
+
+ /* I don't trust these crufty longjmp()'ing image libs
+--
+1.8.3.rc1
--- /dev/null
+class GettextPackage (GnuPackage):
+
+ def __init__(self):
+ GnuPackage.__init__(self, 'gettext', '0.18.2',
+ configure_flags=[
+ '--disable-java',
+ '--disable-libasprintf',
+ '--disable-openmp',
+ '--with-included-glib'
+ ]
+ )
+
+ if Package.profile.name == 'darwin':
+ self.configure_flags.extend([
+ # only build the tools, osx has the lib
+ # https://github.com/mxcl/homebrew/blob/master/Library/Formula/gettext.rb
+ #'--without-included-gettext',
+ ])
+ self.sources.extend([
+ # Don't build samples
+ # https://trac.macports.org/export/79183/trunk/dports/devel/gettext/files/patch-gettext-tools-Makefile.in
+ 'patches/gettext-no-samples.patch',
+ ])
+
+ def prep(self):
+ Package.prep(self)
+ if Package.profile.name == 'darwin':
+ for p in range(1, len(self.local_sources)):
+ self.sh('patch -p1 < "%{local_sources[' + str(p) + ']}"')
+
+GettextPackage()
--- /dev/null
+class GlibPackage (GnomeXzPackage):
+
+ def __init__(self):
+ GnomeXzPackage.__init__(self,
+ 'glib',
+ version_major='2.36',
+ version_minor='4')
+
+ self.darwin = Package.profile.name == 'darwin'
+
+ if self.darwin:
+ # link to specific revisions for glib 2.30.x
+ self.sources.extend([
+ # https://trac.macports.org/export/91680/trunk/dports/devel/glib2/files/config.h.ed
+ 'patches/glib/config.h.ed',
+ # https://trac.macports.org/export/98985/trunk/dports/devel/glib2/files/patch-configure.diff
+ 'patches/glib/patch-configure.diff',
+ # https://trac.macports.org/export/42728/trunk/dports/devel/glib2/files/patch-gi18n.h.diff
+ 'patches/glib/patch-gi18n.h.diff',
+ # https://trac.macports.org/export/92608/trunk/dports/devel/glib2/files/patch-gio_gdbusprivate.c.diff
+ 'patches/glib/patch-gio_gdbusprivate.c.diff',
+ # https://trac.macports.org/export/49466/trunk/dports/devel/glib2/files/patch-gio_xdgmime_xdgmime.c.diff
+ 'patches/glib/patch-gio_xdgmime_xdgmime.c.diff',
+ # https://trac.macports.org/export/91680/trunk/dports/devel/glib2/files/patch-glib-2.0.pc.in.diff
+ 'patches/glib/patch-glib-2.0.pc.in.diff',
+ # https://trac.macports.org/export/64476/trunk/dports/devel/glib2/files/patch-glib_gunicollate.c.diff
+ 'patches/glib/patch-glib_gunicollate.c.diff',
+
+ # Bug 6156 - [gtk] Quitting the application with unsaved file and answering Cancel results in crash
+ # https://bugzilla.xamarin.com/attachment.cgi?id=2214
+ 'patches/glib-recursive-poll.patch',
+ ])
+
+ def prep(self):
+ Package.prep(self)
+ if self.darwin:
+ for p in range(2, 8):
+ self.sh('patch -p0 < %{local_sources[' + str(p) + ']}')
+ for p in range(8, len(self.local_sources)):
+ self.sh(
+ 'patch --ignore-whitespace -p1 < %{local_sources[' + str(p) + ']}')
+
+ def arch_build(self, arch):
+ if arch == 'darwin-universal': # multi-arch build pass
+ self.local_ld_flags = ['-arch i386', '-arch x86_64']
+ self.local_gcc_flags = ['-arch i386', '-arch x86_64', '-Os']
+ self.local_configure_flags = ['--disable-dependency-tracking']
+ else:
+ Package.arch_build(self, arch)
+
+ if self.darwin:
+ self.local_configure_flags.extend(['--disable-compile-warnings'])
+
+ def build(self):
+ # modified build for darwin
+ if self.darwin:
+ self.local_configure_flags.extend(['--disable-compile-warnings'])
+ Package.configure(self)
+ self.sh([
+ # 'autoconf',
+ #'%{configure} --disable-compile-warnings',
+ 'ed - config.h < %{local_sources[1]}',
+ # work around
+ # https://bugzilla.gnome.org/show_bug.cgi?id=700350
+ 'touch docs/reference/*/Makefile.in',
+ 'touch docs/reference/*/*/Makefile.in',
+ #'%{make}'
+ ])
+ Package.make(self)
+ else:
+ Package.build(self)
+
+ def install(self):
+ Package.install(self)
+ if self.darwin:
+ # FIXME: necessary?
+ self.sh('rm -f %{staged_prefix}/lib/charset.alias')
+
+GlibPackage()
--- /dev/null
+class GtkPackage (GnomeGitPackage):
+
+ def __init__(self):
+ GnomeGitPackage.__init__(self, 'gtk+', '2.24', '280fc402be5fb46b66bcd32056963bb1afb8b54b',
+ configure_flags=[
+ '--with-gdktarget=%{gdk_target}',
+ # '--disable-cups',
+ ]
+ )
+ self.gdk_target = 'x11'
+
+ if Package.profile.name == 'darwin':
+ self.gdk_target = 'quartz'
+ self.sources.extend([
+ # Custom gtkrc
+ 'patches/gtkrc',
+
+ # smooth scrolling, scrollbars, overscroll, retina,
+ # gtknsview
+ 'patches/gtk/0001-Add-invariant-that-a-child-is-unmapped-if-parent-is-.patch',
+ 'patches/gtk/0002-Maintain-map-unmap-invariants-in-GtkRecentChooserDia.patch',
+ 'patches/gtk/0003-GtkPlug-preserve-map-unmap-invariants.patch',
+ 'patches/gtk/0004-Add-gdk_screen_get_monitor_workarea-and-use-it-all-o.patch',
+ 'patches/gtk/0005-gtk-don-t-scroll-combo-box-menus-if-less-than-3-item.patch',
+ 'patches/gtk/0006-gtk-paint-only-the-exposed-region-in-gdk_window_expo.patch',
+ 'patches/gtk/0007-Implement-gtk-enable-overlay-scrollbars-GtkSetting.patch',
+ 'patches/gtk/0008-Smooth-scrolling.patch',
+ 'patches/gtk/0009-gtk-Add-a-way-to-do-event-capture.patch',
+ 'patches/gtk/0010-gtk-don-t-let-insensitive-children-eat-scroll-events.patch',
+ 'patches/gtk/0011-scrolledwindow-Kinetic-scrolling-support.patch',
+ 'patches/gtk/0012-gtk-paint-to-the-right-windows-in-gtk_scrolled_windo.patch',
+ 'patches/gtk/0013-GtkScrolledWindow-add-overlay-scrollbars.patch',
+ 'patches/gtk/0014-gtk-add-event-handling-to-GtkScrolledWindow-s-overla.patch',
+ 'patches/gtk/0015-Use-gtk-enable-overlay-scrollbars-in-GtkScrolledWind.patch',
+ 'patches/gtk/0016-gtk-correctly-handle-toggling-of-the-scrollbar-visib.patch',
+ 'patches/gtk/0017-gtk-handle-gtk-primary-button-warps-slider-for-the-o.patch',
+ 'patches/gtk/0018-Introduce-phase-field-in-GdkEventScroll.patch',
+ 'patches/gtk/0019-Add-hack-to-lock-flow-of-scroll-events-to-window-whe.patch',
+ 'patches/gtk/0020-Introduce-a-background-window.patch',
+ 'patches/gtk/0021-Make-scrolled-window-work-well-with-Mac-touchpad.patch',
+ 'patches/gtk/0022-Use-start-end-phase-in-event-handling.patch',
+ 'patches/gtk/0023-Improve-overshooting-behavior.patch',
+ 'patches/gtk/0024-Cancel-out-smaller-delta-component.patch',
+ 'patches/gtk/0025-quartz-Add-a-dummy-NSView-serving-as-layer-view.patch',
+ 'patches/gtk/0026-gtk-port-overlay-scrollbars-to-native-CALayers.patch',
+ 'patches/gtk/0027-Refrain-from-starting-fading-out-while-a-gesture-is-.patch',
+ 'patches/gtk/0028-gtk-don-t-show-the-olverlay-scrollbars-if-only-the-s.patch',
+ 'patches/gtk/0029-Reclamp-unclamped-adjustments-after-resize.patch',
+ 'patches/gtk/0030-gtk-fix-size_request-of-scrolled-window.patch',
+ 'patches/gtk/0031-Hackish-fix-for-bug-8493-Min-size-of-GtkScrolledWind.patch',
+ 'patches/gtk/0032-Add-momentum_phase-to-GdkEventScroll.patch',
+ 'patches/gtk/0033-Never-intervene-in-the-event-stream-for-legacy-mice.patch',
+ 'patches/gtk/0034-Do-not-start-overshooting-for-legacy-mouse-scroll-ev.patch',
+ 'patches/gtk/0035-gtk-remove-the-overlay-scrollbar-grab-on-unrealize.patch',
+ 'patches/gtk/0036-gtk-add-GtkScrolledWindow-API-to-disable-overshoot-p.patch',
+ 'patches/gtk/0037-quartz-return-events-on-embedded-foreign-NSViews-bac.patch',
+ 'patches/gtk/0038-quartz-don-t-forward-events-to-the-toplevel-nswindow.patch',
+ 'patches/gtk/0039-gdk-add-a-move-native-children-signal-to-GdkWindow.patch',
+ 'patches/gtk/0040-gtk-add-new-widget-GtkNSView-which-alows-to-embed-an.patch',
+ 'patches/gtk/0041-tests-add-a-notebook-to-testnsview.c.patch',
+ 'patches/gtk/0042-gtk-connect-GtkNSView-to-move-native-children-and-re.patch',
+ 'patches/gtk/0043-tests-add-a-scrolled-window-test-widget-to-testnsvie.patch',
+ 'patches/gtk/0044-gtknsview-clip-drawRect-to-a-parent-GtkViewport-s-wi.patch',
+ 'patches/gtk/0045-gtk-clip-NSViews-to-the-scrolled-window-s-overshoot_.patch',
+ 'patches/gtk/0046-gtk-implement-clipping-to-multiple-parent-viewports-.patch',
+ 'patches/gtk/0047-gtk-first-attempt-to-also-clip-NSWindow-s-field-edit.patch',
+ 'patches/gtk/0048-gtk-also-clip-the-NSView-s-subviews.patch',
+ 'patches/gtk/0049-nsview-also-swizzle-DidAddSubview-and-clip-all-subvi.patch',
+ 'patches/gtk/0050-nsview-clip-text-field-cursor-drawing.patch',
+ 'patches/gtk/0051-nsview-factor-out-almost-all-code-from-the-overridde.patch',
+ 'patches/gtk/0052-nsview-also-focus-the-GtkNSView-if-a-focussable-subv.patch',
+ 'patches/gtk/0053-gtk-add-an-overlay-policy-API-to-GtkScrolledWindow.patch',
+ 'patches/gtk/0054-quartz-add-gdk_screen_-and-gdk_window_get_scale_fact.patch',
+ 'patches/gtk/0055-gtk-add-gtk_widget_get_scale_factor.patch',
+ 'patches/gtk/0056-iconfactory-Add-_scaled-variants.patch',
+ 'patches/gtk/0057-widget-Add-_scaled-variants-for-icon-rendering.patch',
+ 'patches/gtk/0058-icontheme-Add-support-for-high-resolution-icons.patch',
+ 'patches/gtk/0059-iconfactory-Add-scale-info-to-GtkIconSource.patch',
+ 'patches/gtk/0060-iconfactory-Add-gtk_cairo_set_source_icon_set.patch',
+ 'patches/gtk/0061-image-Use-scaled-icons-on-windows-with-a-scaling-fac.patch',
+ 'patches/gtk/0062-cellrendererpixbuf-Use-scaled-icons-on-windows-with-.patch',
+ 'patches/gtk/0063-entry-Use-scaled-icons-on-windows-with-a-scale-facto.patch',
+ 'patches/gtk/0064-gdk-Lookup-double-scaled-variants-on-pixbufs.patch',
+ 'patches/gtk/0065-Make-usual-calls-to-get-a-GdkPixbuf-attach-a-2x-vari.patch',
+ 'patches/gtk/0066-cellrendererpixbuf-let-2x-variants-go-through-pixel-.patch',
+ 'patches/gtk/0067-quartz-Make-event-loop-deal-with-recursive-poll-invo.patch',
+ 'patches/gtk/0068-nsview-implement-a-few-text-view-command-accelerator.patch',
+ 'patches/gtk/0069-menu-scrolling.patch',
+ 'patches/gtk/0070-tooltips-focus.patch',
+ 'patches/gtk/0071-light-and-dark-overlay-scrollbars.patch',
+ 'patches/gtk/0072-let-global-keyboard-shortcuts-pass-through.patch',
+ 'patches/gtk/0073-disable-combobox-scrolling.patch',
+ 'patches/gtk/0074-fix-NULL-pointer-in-clipboard.patch',
+ 'patches/gtk/0075-filechooserwidget-location-entry-activation.patch',
+ 'patches/gtk/0076-iconfactory-treat-gt-1-0-icons-as-2-0.patch',
+
+ # Bug 702841 - GdkQuartz does not distinguish Eisu, Kana and Space keys on Japanese keyrboard
+ # https://bugzilla.gnome.org/show_bug.cgi?id=702841
+ 'patches/gtk/bgo702841-fix-kana-eisu-keys.patch',
+
+ # make new modifier behviour opt-in, so as not to break old
+ # versions of MonoDevelop
+ 'patches/gdk-quartz-set-fix-modifiers-hack-v3.patch',
+
+ # attempt to work around 2158 - [GTK] crash triggering context menu
+ # also prints some warnings that may help to debug the real issue
+ # https://bugzilla.xamarin.com/attachment.cgi?id=1644
+ 'patches/gtk/bxc2158_workaround_crash_triggering_context_menu.patch',
+
+ # Zoom, rotate, swipe events
+ 'patches/gtk-gestures.patch',
+
+ # Fix gtk_window_begin_move_drag on Quartz
+ 'patches/gtk-quartz-move-drag.patch',
+
+ # Bug 3457 - [GTK] Support more standard keyboard shortcuts in dialogs
+ # https://bugzilla.xamarin.com/attachment.cgi?id=2240
+ 'patches/gtk/bxc3457_more_standard_keyboard_shortcuts.patch',
+
+ # Bug 10256 - Mac window manipulation tools get confused by Xamarin Studio
+ # https://bugzilla.xamarin.com/attachment.cgi?id=3465
+ 'patches/gtk/bxc_10256_window_tools_get_confused.diff',
+
+ # 'patches/gtk/gdk-pixmap-get-cgimage-2.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=18157
+ 'patches/gtk/gtk-check-grab_toplevel-is-destroyed.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=18241
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=17631
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=17692
+ 'patches/gtk/gtk-imquartz-defer-signals-in-output_result.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=17401
+ 'patches/gtk/gtknsview-defer-map-and-lock-in-clipping.patch',
+ 'patches/gtk/gtknsview-timeout-fix.patch',
+
+ 'patches/gtk/nsview-embedding.patch',
+
+ 'patches/gtk/enable-swizzle-property.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=12618
+ 'patches/gtk/disable-eye-dropper.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=13100
+ 'patches/gtk/flip-command-mask-between-mod1-and-mod2.patch',
+ 'patches/gtk/nsview-embedding-fix-keyboard-routing.patch',
+ 'patches/gtk/nsview-check-for-superview.patch',
+
+ 'patches/gtk/gtknsview-forward-cmdz-to-textview-undo-manager.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=20732
+ 'patches/gtk/embedded-nstextview-has-focus.patch',
+ 'patches/gtk/remove-demos-from-build.patch',
+
+ # This fixes an issue in where in some situations the user needed
+ # to click a native text entry twice in order to be able to
+ # focus it.
+ 'patches/gtk/gtknsview-only-unset-first-responder-if-it-is-our-view.patch',
+
+ # For the test framework to be able to traverse down the
+ # NSView hierarchy
+ 'patches/gtk/gtknsview-getter.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=29301#c3
+ 'patches/gtk/gtknsview-fix-invalid-casts.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=29001
+ 'patches/gtk/quartz-call-undo-redo-on-cmdz.patch',
+
+ 'patches/gtk/scrolled-window-draw-child-bg.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=37239
+ 'patches/gtk/fix-imquartz-crasher.patch',
+
+ # https://bugzilla.gnome.org/show_bug.cgi?id=630226
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=34973
+ 'patches/gtk/remove-mouse-scrolling-from-GtkNotebook-tabs.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=37951
+ 'patches/gtk/dont-call-CopySymbolicHotKeys-so-much.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=38664
+ 'patches/gtk/combobox-crossing-events.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=41657
+ 'patches/gtk/bxc-41657.patch',
+
+ 'patches/gtk/emit-container-add.patch',
+ 'patches/gtk/create-accessibility-object.patch',
+ 'patches/gtk/make-gtkpaned-emit-signals.patch'
+ ])
+
+ def prep(self):
+ Package.prep(self)
+ if Package.profile.name == 'darwin':
+ for p in range(2, len(self.local_sources)):
+ self.sh(
+ 'patch -p1 --ignore-whitespace < "%{local_sources[' + str(p) + ']}"')
+
+ def install(self):
+ Package.install(self)
+ if Package.profile.name == 'darwin':
+ self.install_gtkrc()
+
+ def install_gtkrc(self):
+ gtkrc = self.local_sources[1]
+ destdir = os.path.join(self.staged_prefix, "etc", "gtk-2.0")
+ if not os.path.exists(destdir):
+ os.makedirs(destdir)
+ self.sh('cp %s %s' % (gtkrc, destdir))
+
+GtkPackage()
--- /dev/null
+GnomePackage('gtk-engines', version_major='2.20', version_minor='2',
+ configure_flags=[''])
--- /dev/null
+class GtkQuartzEnginePackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'gtk-quartz-engine',
+ sources=[
+ 'git://github.com/mono/gtk-quartz-engine.git'],
+ override_properties={
+ 'configure': './autogen.sh --prefix=%{package_prefix}',
+ 'needs_lipo': True
+ },
+ revision='9555a08f0c9c98d02153c9d77b54a2dd83ce5d6f')
+
+GtkQuartzEnginePackage()
--- /dev/null
+class GtkSharp212ReleasePackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'gtk-sharp',
+ sources=['git://github.com/mono/gtk-sharp.git'],
+ git_branch='gtk-sharp-2-12-branch',
+ revision='f092864bce996c4ac51a13281069067d1e7e6d4b',
+ override_properties={
+ 'configure': './bootstrap-2.12 --prefix=%{package_prefix}',
+ }
+ )
+ self.make = 'make CSC=mcs'
+
+GtkSharp212ReleasePackage()
--- /dev/null
+include "/Library/Frameworks/Mono.framework/Versions/Current/share/themes/Clearlooks/gtk-2.0/gtkrc"
+#gtk-icon-theme-name = "OSX"
+gtk-icon-theme-name = "Tango"
+gtk_color_scheme = "fg_color:#222\nbg_color:#e6e6e6\nbase_color:#f9f9f9\ntext_color:#222\nselected_bg_color:#788ab0\nselected_fg_color:#fff"
+gtk-menu-popup-delay = 1
+gtk-button-images = 0
+gtk-menu-images = 0
+gtk-enable-mnemonics = 0
+
+style "theme-default"
+{
+ GtkButton ::default_border = { 0, 0, 0, 0 }
+ GtkRange ::trough_border = 0
+ GtkPaned ::handle_size = 8
+ GtkRange ::slider_width = 15
+ GtkRange ::stepper_size = 15
+ GtkScrollbar ::min_slider_length = 30
+ GtkCheckButton ::indicator_size = 14
+ GtkMenuBar ::internal-padding = 0
+ GtkTreeView ::expander_size = 12
+ GtkExpander ::expander_size = 14
+
+ xthickness = 2
+ ythickness = 2
+
+ fg[NORMAL] = @fg_color #"#000000" # black
+ fg[PRELIGHT] = @fg_color #"#000000" # black
+ fg[SELECTED] = @selected_fg_color #"#ffffff" # white
+ fg[ACTIVE] = @fg_color #"#000000" # black
+ fg[INSENSITIVE] = darker (@bg_color) #"#b5b3ac" # dark beige
+
+ bg[NORMAL] = @bg_color # "#ede9e3"
+ bg[PRELIGHT] = shade (1.02, @bg_color) #"#f9f7f3" # very light beige
+ bg[SELECTED] = @selected_bg_color # "#5598d7" # deepsky
+ bg[INSENSITIVE] = @bg_color # "#efebe5" # beige
+ bg[ACTIVE] = shade (0.9, @bg_color) #"#dcd4c9" #"#d7d3ca" # dark beige
+
+ base[NORMAL] = @base_color # "#ffffff" # white
+ base[PRELIGHT] = shade (0.95, @bg_color) # "#5f8ec4" # dark beige
+ base[ACTIVE] = shade (0.9, @selected_bg_color) # "#a69f91" # darker deepsky
+ base[SELECTED] = @selected_bg_color # "#5598d7" # deepsky
+ base[INSENSITIVE] = @bg_color # "#e8e5de" # beige
+
+ text[NORMAL] = @text_color # "#000000" # black
+ text[PRELIGHT] = @text_color # "#000000" # black
+ text[ACTIVE] = @selected_fg_color # "#ffffff" # white
+ text[SELECTED] = @selected_fg_color # "#ffffff" # white
+ text[INSENSITIVE] = darker (@bg_color) # "#b5b3ac" # dark beige
+
+ engine "clearlooks" {
+ style = GUMMY # gummy look
+ toolbarstyle = 0 # flat toolbars
+ animation = TRUE # animated progressbars
+ menubarstyle = 2 # rounded menus
+ colorize_scrollbar = TRUE # colored slider
+ }
+
+ font = "Lucida Grande 14"
+}
+
+style "theme-wide" = "theme-default"
+{
+ xthickness = 3
+ ythickness = 3
+}
+
+style "theme-text" = "theme-default"
+{
+ #base[SELECTED] = "#fc9747" # Outline?
+}
+
+style "theme-toolbar" = "theme-default"
+{
+ #top and bottom border
+ bg[NORMAL] = @bg_color
+}
+
+style "theme-scrollbar" = "theme-default"
+{
+ bg[SELECTED] = shade (1.1, @selected_bg_color)
+}
+
+style "theme-tasklist" = "theme-default"
+{
+ xthickness = 5
+ ythickness = 3
+}
+
+style "theme-menu" = "theme-default"
+{
+ xthickness = 3
+ ythickness = 3
+ bg[NORMAL] = shade (1.1,@bg_color)
+}
+
+style "theme-menu-item" = "theme-default"
+{
+ xthickness = 2
+ ythickness = 4
+ fg[PRELIGHT] = @selected_fg_color
+ text[PRELIGHT] = @selected_fg_color
+ base[PRELIGHT] = @selected_bg_color # Selection color
+}
+
+style "theme-menu-itembar" = "theme-default"
+{
+ xthickness = 0
+ ythickness = 0
+}
+
+style "theme-tree" = "theme-default"
+{
+ xthickness = 2
+ ythickness = 2
+ GtkTreeView::odd-row-color = shade(0.9, @base_color)
+ GtkTreeView::even-row-color = @base_color
+}
+
+style "theme-frame-title" = "theme-default"
+{
+ #fg[NORMAL] = "#f00" #button frames
+}
+
+style "theme-tooltips" = "theme-default"
+{
+ xthickness = 4
+ ythickness = 4
+ bg[NORMAL] = { 1.0,1.0,0.75 }
+}
+
+style "theme-progressbar" = "theme-default"
+{
+ xthickness = 1
+ ythickness = 1
+ fg[PRELIGHT] = @base_color
+}
+
+style "theme-combo" = "theme-default"
+{
+ xthickness = 2
+ ythickness = 4
+}
+
+style "theme-button" = "theme-wide"
+{
+ bg[NORMAL] = @bg_color
+ bg[PRELIGHT] = shade (1.1, @bg_color)
+ bg[ACTIVE] = shade (0.9, @bg_color)
+ #xthickness = 4
+ #ythickness = 2
+}
+
+style "theme-check" = "theme-button"
+{
+}
+
+style "theme-panel" = "theme-default"
+{
+ xthickness = 3
+ ythickness = 3
+ bg[ACTIVE] = shade (1.1, @selected_bg_color)
+ fg[ACTIVE] = @selected_fg_color
+}
+
+style "theme-notebook" = "theme-wide"
+{
+ base[SELECTED] = @selected_bg_color # Tab selection color
+ bg[ACTIVE] = shade(0.9, @bg_color) # Unselected tabs
+
+# engine "clearlooks" {
+# style = CLASSIC
+# }
+}
+
+# widget styles
+class "GtkWidget" style "theme-default"
+class "GtkButton" style "theme-button"
+class "GtkCombo" style "theme-button"
+class "GtkRange" style "theme-wide"
+class "GtkFrame" style "theme-wide"
+class "GtkMenu" style "theme-menu"
+class "GtkEntry" style "theme-button"
+class "GtkMenuItem" style "theme-menu-item"
+class "GtkStatusbar" style "theme-wide"
+class "GtkNotebook" style "theme-notebook"
+class "GtkProgressBar" style "theme-progressbar"
+class "GtkCheckButton" style "theme-check"
+class "GtkRadioButton" style "theme-check"
+class "GtkToolbar" style "theme-toolbar"
+
+widget_class "*MenuItem.*" style "theme-menu-item"
+
+# combobox stuff
+widget_class "*.GtkComboBox.GtkButton" style "theme-combo"
+widget_class "*.GtkCombo.GtkButton" style "theme-combo"
+
+# tooltips stuff
+widget_class "*.tooltips.*.GtkToggleButton" style "theme-tasklist"
+widget "gtk-tooltips" style "theme-tooltips"
+
+# treeview stuff
+widget "*GtkTreeView*" style "theme-tree"
+widget_class "*.GtkTreeView.GtkButton" style "theme-tree"
+widget_class "*.GtkCTree.GtkButton" style "theme-tree"
+widget_class "*.GtkList.GtkButton" style "theme-tree"
+widget_class "*.GtkCList.GtkButton" style "theme-tree"
+widget_class "*.GtkFrame.GtkLabel" style "theme-frame-title"
+
+# notebook stuff
+widget_class "*.GtkNotebook.*.GtkEventBox" style "theme-notebook"
+widget_class "*.GtkNotebook.*.GtkViewport" style "theme-notebook"
+
+# scrollbar stuff
+class "GtkScrollbar" style "theme-scrollbar"
+
+gtk-font-name = "Lucida Grande 12"
--- /dev/null
+FreeDesktopPackage('icon-theme', 'hicolor-icon-theme', '0.12')
--- /dev/null
+SourceForgePackage('gtk-osx', 'ige-mac-integration', '0.9.4', ['--without-compile-warnings'],
+ override_properties={'configure': './configure --prefix="%{staged_prefix}"',
+ 'makeinstall': 'make install'})
--- /dev/null
+class IntltoolPackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'intltool', '0.50.2',
+ sources=[
+ 'https://launchpad.net/%{name}/trunk/%{version}/+download/%{name}-%{version}.tar.gz'
+ ]
+ )
+
+IntltoolPackage()
--- /dev/null
+import os
+import string
+
+
+class IronLanguagesPackage(GitHubTarballPackage):
+
+ def __init__(self):
+ GitHubTarballPackage.__init__(self,
+ 'IronLanguages', 'iron-languages',
+ '2.11',
+ 'de63773744ccf9873c1826470730ae0446fd64d7',
+ configure='')
+
+ # override: avoid naming the package 'main' because of the repo name
+ self.sources = [
+ 'https://github.com/%{organization}/main/tarball/%{revision}']
+ self.source_dir_name = '%s-%s-%s' % (
+ self.organization, 'main', self.revision[:7])
+
+ def build(self):
+ self.ironruby = os.path.join(
+ self.workspace, 'ironruby', 'bin') + os.sep
+ self.ironpython = os.path.join(
+ self.workspace, 'ironpython', 'bin') + os.sep
+ self.sh(
+ 'xbuild /p:Configuration=Release /p:OutDir="%{ironruby}" Solutions/Ruby.sln')
+ self.sh(
+ 'xbuild /p:Configuration=Release /p:OutDir="%{ironpython}" Solutions/IronPython.Mono.sln')
+
+ def install_ruby_scripts(self, path, installdir):
+ for cmd, ext in map(os.path.splitext, os.listdir(path)):
+ if ext != '.exe':
+ continue
+ wrapper = os.path.join(self.staged_prefix, "bin", cmd)
+ with open(wrapper, "w") as output:
+ output.write("#!/bin/sh\n")
+ output.write(
+ "exec {0}/bin/mono {0}/lib/{1}/{2}.exe \"$@\"\n".format(
+ self.staged_prefix, installdir, cmd))
+ os.chmod(wrapper, 0o755)
+
+ def install_python_scripts(self, path, installdir):
+ for cmd, ext in map(os.path.splitext, os.listdir(path)):
+ if ext != '.exe':
+ continue
+ wrapper = os.path.join(self.staged_prefix, "bin", cmd)
+ with open(wrapper, "w") as output:
+ output.write("#!/bin/sh\n")
+ output.write(
+ 'export IRONPYTHONPATH=/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/\n')
+ output.write(
+ "exec {0}/bin/mono {0}/lib/{1}/{2}.exe \"$@\"\n".format(
+ self.staged_prefix, installdir, cmd))
+ os.chmod(wrapper, 0o755)
+
+ def install(self):
+ self.sh("mkdir -p %{staged_prefix}/lib/ironruby/")
+ self.sh("mkdir -p %{staged_prefix}/bin/")
+ self.sh("cp -R %{ironruby} %{staged_prefix}/lib/ironruby/")
+ self.install_ruby_scripts(self.ironruby, 'ironruby')
+
+ self.sh("mkdir -p %{staged_prefix}/lib/ironpython/")
+ self.sh("cp -R %{ironpython} %{staged_prefix}/lib/ironpython/")
+ self.install_python_scripts(self.ironpython, 'ironpython')
+
+IronLanguagesPackage()
--- /dev/null
+GnomeXzPackage('libcroco', version_major='0.6', version_minor='8',
+ configure_flags=[
+ '--host=i386-apple-darwin',
+ '--disable-Bsymbolic',
+ '--enable-gtk-doc-html=no'
+ ])
--- /dev/null
+class LibFfiPackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'libffi', '3.0.13', sources=[
+ 'ftp://sourceware.org/pub/%{name}/%{name}-%{version}.tar.gz'])
+
+LibFfiPackage()
--- /dev/null
+GitHubTarballPackage(
+ 'mono',
+ 'libgdiplus',
+ '2.11',
+ '4e7ab0f555a13a6b2f954c714c4ee5213954ff79',
+ configure='CFLAGS="%{gcc_flags} %{local_gcc_flags} -I/opt/X11/include" ./autogen.sh --prefix="%{package_prefix}"',
+ override_properties={
+ 'make': 'C_INCLUDE_PATH="" make'})
--- /dev/null
+SourceForgePackage('giflib', 'giflib', '4.1.6')
--- /dev/null
+GnomePackage('libglade', '2.6', '4')
--- /dev/null
+class LibJpegPackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'libjpeg', '8', sources=[
+ 'http://www.ijg.org/files/jpegsrc.v8.tar.gz'])
+ self.source_dir_name = 'jpeg-8'
+
+LibJpegPackage()
--- /dev/null
+class LibPngPackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'libpng', '1.4.12',
+ sources=[
+ 'http://downloads.sourceforge.net/project/libpng/libpng14/older-releases/1.4.12/libpng-1.4.12.tar.xz'],
+ configure_flags=['--enable-shared'])
+
+LibPngPackage()
--- /dev/null
+class LibrsvgPackage(GnomeXzPackage):
+
+ def __init__(self):
+ GnomeXzPackage.__init__(self, 'librsvg', version_major='2.37', version_minor='0',
+ configure_flags=['--disable-Bsymbolic', '--disable-introspection'])
+
+ make = 'make DESTDIR=%{stage_root}'
+
+ def install(self):
+ # handle some mislocation
+ misdir = '%s%s' % (self.stage_root, self.staged_profile)
+ unprotect_dir(self.stage_root)
+
+ Package.install(self)
+ # scoop up
+ if not os.path.exists(misdir):
+ error('Could not find mislocated libsrvg files')
+
+ self.sh(
+ 'rsync -a --ignore-existing %s/* %s' %
+ (misdir, self.staged_prefix))
+ self.sh('rm -rf %s/*' % misdir)
+
+ def deploy(self):
+ self.sh('gdk-pixbuf-query-loaders --update-cache')
+
+LibrsvgPackage()
--- /dev/null
+class LibTiffPackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'tiff', '4.0.3',
+ configure_flags=[
+ ],
+ sources=[
+ 'http://download.osgeo.org/libtiff/tiff-%{version}.tar.gz',
+ ])
+
+ self.needs_lipo = True
+
+ if Package.profile.name == 'darwin':
+ self.sources.extend([
+ # Fix Snow Leopard build
+ # http://jira.freeswitch.org/secure/attachment/17487/tiff-4.0.2-macosx-2.patch
+ 'patches/tiff-4.0.2-macosx-2.patch'
+ ])
+
+ def prep(self):
+ Package.prep(self)
+ if Package.profile.name == 'darwin':
+ for p in range(1, len(self.local_sources)):
+ self.sh('patch -p1 < "%{local_sources[' + str(p) + ']}"')
+
+LibTiffPackage()
--- /dev/null
+class LibXmlPackage (Package):
+
+ def __init__(self):
+ Package.__init__(self,
+ 'libxml2',
+ '2.9.1',
+ configure_flags=['--with-python=no'],
+ sources=[
+ 'ftp://xmlsoft.org/%{name}/%{name}-%{version}.tar.gz',
+ ]
+ )
+
+ def prep(self):
+ Package.prep(self)
+ if Package.profile.name == 'darwin':
+ for p in range(1, len(self.local_sources)):
+ self.sh('patch -p1 < "%{local_sources[' + str(p) + ']}"')
+
+LibXmlPackage()
--- /dev/null
+
+class MonoBasicPackage (GitHubTarballPackage):
+
+ def __init__(self):
+ GitHubTarballPackage.__init__(self, 'mono', 'mono-basic', '4.6', 'c93133db1d511f994918391f429fee29b9250004',
+ configure='./configure --prefix="%{staged_profile}"')
+
+ def install(self):
+ self.sh('./configure --prefix="%{staged_prefix}"')
+ self.sh('make install')
+
+MonoBasicPackage()
--- /dev/null
+import os
+
+
+class MonoLlvmPackage (GitHubPackage):
+
+ def __init__(self):
+ GitHubPackage.__init__(self, 'mono', 'llvm', '3.0',
+ revision='8b1520c8aae53e219cf80cdc0f02ad96600887d6',
+ configure_flags=[
+ '--enable-optimized',
+ '--enable-assertions=no',
+ '--enable-targets="x86,x86_64"']
+ )
+
+ # This package would like to be lipoed.
+ self.needs_lipo = True
+
+ # TODO: find out which flags are causing issues. reset ld_flags for the
+ # package
+ self.ld_flags = []
+ self.cpp_flags = []
+
+ def arch_build(self, arch):
+ if arch == 'darwin-64': # 64-bit build pass
+ self.local_configure_flags = ['--build=x86_64-apple-darwin11.2.0']
+
+ if arch == 'darwin-32':
+ self.local_configure_flags = ['--build=i386-apple-darwin11.2.0']
+
+ # LLVM says that libstdc++4.6 is broken and we should use libstdc++4.7.
+ # This switches it to the right libstdc++.
+ if Package.profile.name == 'darwin':
+ self.local_configure_flags.extend(['--enable-libcpp=yes'])
+
+MonoLlvmPackage()
--- /dev/null
+import os
+import re
+
+from bockbuild.package import Package
+from bockbuild.util.util import *
+
+
+class MonoMasterPackage(Package):
+
+ def __init__(self):
+ Package.__init__(self, 'mono', None,
+ sources=[
+ Package.profile.git_root],
+ git_branch=os.getenv('MONO_BRANCH') or None,
+ revision=os.getenv('MONO_BUILD_REVISION'),
+ configure_flags=[
+ '--enable-nls=no',
+ '--with-ikvm=yes'
+ ]
+ )
+ self.source_dir_name = 'mono'
+ # This package would like to be lipoed.
+ self.needs_lipo = True
+
+ # Don't clean the workspace, so we can run 'make check' afterwards
+ self.dont_clean = True
+
+ if Package.profile.name == 'darwin':
+ self.configure_flags.extend([
+ '--with-libgdiplus=%s/lib/libgdiplus.dylib' % Package.profile.staged_prefix,
+ '--enable-loadedllvm',
+ 'CXXFLAGS=-stdlib=libc++'
+ ])
+
+ self.sources.extend([
+ # Fixes up pkg-config usage on the Mac
+ 'patches/mcs-pkgconfig.patch'
+ ])
+ else:
+ self.configure_flags.extend([
+ '--with-libgdiplus=%s/lib/libgdiplus.so' % Package.profile.staged_prefix
+ ])
+
+ self.gcc_flags.extend(['-O2'])
+
+ self.configure = './autogen.sh --prefix="%{package_prefix}"'
+
+ self.extra_stage_files = ['etc/mono/config']
+
+ def build(self):
+ self.make = '%s EXTERNAL_MCS=%s EXTERNAL_RUNTIME=%s' % (
+ self.make, self.profile.env.system_mcs, self.profile.env.system_mono)
+ Package.build(self)
+
+ def prep(self):
+ Package.prep(self)
+ for p in range(1, len(self.local_sources)):
+ self.sh('patch -p1 < "%{local_sources[' + str(p) + ']}"')
+
+ def arch_build(self, arch):
+ if arch == 'darwin-64': # 64-bit build pass
+ self.local_gcc_flags = ['-m64']
+ self.local_configure_flags = ['--build=x86_64-apple-darwin11.2.0']
+
+ if arch == 'darwin-32': # 32-bit build pass
+ self.local_gcc_flags = ['-m32']
+ self.local_configure_flags = ['--build=i386-apple-darwin11.2.0']
+
+ self.local_configure_flags.extend(
+ ['--cache-file=%s/%s-%s.cache' % (self.profile.build_root, self.name, arch)])
+
+ def install(self):
+ Package.install(self)
+
+ registry_dir = os.path.join(
+ self.staged_prefix,
+ "etc",
+ "mono",
+ "registry",
+ "LocalMachine")
+ ensure_dir(registry_dir)
+
+ # Add ImportBefore/ImportAfter files from xbuild to the msbuild
+ # directories
+ xbuild_dir = os.path.join(self.staged_prefix, 'lib/mono/xbuild')
+ new_xbuild_tv_dir = os.path.join(xbuild_dir, self.version)
+ os.makedirs(new_xbuild_tv_dir)
+
+ self.sh('cp -R %s/14.0/Imports %s' % (xbuild_dir, new_xbuild_tv_dir))
+ self.sh(
+ 'cp -R %s/14.0/Microsoft.Common.targets %s' %
+ (xbuild_dir, new_xbuild_tv_dir))
+
+ def deploy(self):
+ if self.profile.arch == 'darwin-universal':
+ os.symlink('mono-sgen64', '%s/bin/mono64' % self.staged_profile)
+ os.symlink('mono-sgen32', '%s/bin/mono32' % self.staged_profile)
+
+ text = " ".join(open('%s/bin/mcs' % self.staged_profile).readlines())
+ regex = os.path.join(
+ self.profile.MONO_ROOT,
+ "Versions",
+ r"(\d+\.\d+\.\d+)")
+ match = re.search(regex, text)
+ if match is None:
+ return
+ token = match.group(1)
+
+ trace(token)
+ trace(self.package_prefix)
+ if self.package_prefix not in match:
+ error("%s references Mono %s\n%s" % ('mcs', match, text))
+
+MonoMasterPackage()
--- /dev/null
+import fileinput
+
+
+class MSBuild (GitHubPackage):
+
+ def __init__(self):
+ GitHubPackage.__init__(self, 'mono', 'msbuild', '15.0',
+ git_branch='xplat-c8p')
+
+ def build(self):
+ self.sh('./cibuild.sh --scope Compile --target Mono --host Mono')
+
+ def install(self):
+ # adjusted from 'install-mono-prefix.sh'
+
+ build_output = 'bin/Debug-MONO/OSX_Deployment'
+ new_location = os.path.join(
+ self.staged_prefix,
+ 'lib/mono/msbuild/%s/bin' %
+ self.version)
+ bindir = os.path.join(self.staged_prefix, 'bin')
+
+ os.makedirs(new_location)
+ self.sh('cp -R %s/* %s' % (build_output, new_location))
+
+ os.makedirs(bindir)
+
+ self.sh('cp msbuild-mono-deploy.in %s/msbuild' % bindir)
+
+ for line in fileinput.input('%s/msbuild' % bindir, inplace=True):
+ line = line.replace('@bindir@', '%s/bin' % self.staged_prefix)
+ line = line.replace(
+ '@mono_instdir@',
+ '%s/lib/mono' %
+ self.staged_prefix)
+ print line
+
+ for excluded in glob.glob("%s/*UnitTests*" % new_location):
+ self.rm(excluded)
+
+ for excluded in glob.glob("%s/*xunit*" % new_location):
+ self.rm(excluded)
+
+MSBuild()
--- /dev/null
+class MurrinePackage (GnomeXzPackage):
+
+ def __init__(self):
+ GnomePackage.__init__(self,
+ 'murrine',
+ version_major='0.98',
+ version_minor='2')
+
+ # FIXME: this may need porting
+ # self.sources.append ('patches/murrine-osx.patch')
+
+ def prep(self):
+ Package.prep(self)
+
+MurrinePackage()
--- /dev/null
+
+class NuGetPackage(GitHubPackage):
+
+ def __init__(self):
+ GitHubPackage.__init__(self,
+ 'mono', 'nuget',
+ '2.12.0',
+ '9e2d2c1cc09d2a40eeb72e8c5db789e3b9bf2586',
+ configure='')
+
+ def build(self):
+ self.sh('%{make} update_submodules')
+ self.sh('%{make} PREFIX=%{package_prefix}')
+
+ def install(self):
+ self.sh('%{makeinstall} PREFIX=%{staged_prefix}')
+
+NuGetPackage()
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleGetInfoString</key>
+ <string>@@MONO_VERSION_RELEASE@@</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.ximian.mono-@@MONO_VERSION@@</string>
+ <key>CFBundleName</key>
+ <string>Mono.framework</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@@MONO_VERSION@@</string>
+ <key>IFPkgFlagAllowBackRev</key>
+ <true/>
+ <key>IFPkgFlagAuthorizationAction</key>
+ <string>AdminAuthorization</string>
+ <key>IFPkgFlagDefaultLocation</key>
+ <string>/</string>
+ <key>IFPkgFlagInstallFat</key>
+ <false/>
+ <key>IFPkgFlagIsRequired</key>
+ <false/>
+ <key>IFPkgFlagRelocatable</key>
+ <false/>
+ <key>IFPkgFlagRestartAction</key>
+ <string>NoRestart</string>
+ <key>IFPkgFlagRootVolumeOnly</key>
+ <true/>
+ <key>IFPkgFlagUpdateInstalledLanguages</key>
+ <false/>
+ <key>IFPkgFormatVersion</key>
+ <real>0.10000000149011612</real>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleGetInfoString</key>
+ <string>@@MONO_VERSION_RELEASE@@</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.ximian.mono-@@MONO_VERSION@@-csdk</string>
+ <key>CFBundleName</key>
+ <string>Mono.framework</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@@MONO_VERSION@@</string>
+ <key>IFPkgFlagAllowBackRev</key>
+ <true/>
+ <key>IFPkgFlagAuthorizationAction</key>
+ <string>AdminAuthorization</string>
+ <key>IFPkgFlagDefaultLocation</key>
+ <string>/</string>
+ <key>IFPkgFlagInstallFat</key>
+ <false/>
+ <key>IFPkgFlagIsRequired</key>
+ <false/>
+ <key>IFPkgFlagRelocatable</key>
+ <false/>
+ <key>IFPkgFlagRestartAction</key>
+ <string>NoRestart</string>
+ <key>IFPkgFlagRootVolumeOnly</key>
+ <true/>
+ <key>IFPkgFlagUpdateInstalledLanguages</key>
+ <false/>
+ <key>IFPkgFormatVersion</key>
+ <real>0.10000000149011612</real>
+</dict>
+</plist>
--- /dev/null
+#!/bin/bash
+
+if test x$1 = x; then
+ echo usage is cleanup MONODIR
+ exit 1
+fi
+
+MONODIR=$1
+
+cd $MONODIR
+rm -rf lib/gtk-2.0/2.10.0/engines/libcrux-engine.so
+rm -rf lib/gtk-2.0/2.10.0/engines/libglide.so
+rm -rf lib/gtk-2.0/2.10.0/engines/libhcengine.so
+rm -rf lib/gtk-2.0/2.10.0/engines/libindustrial.so
+rm -rf lib/gtk-2.0/2.10.0/engines/libmist.so
+rm -rf lib/gtk-2.0/2.10.0/engines/libpixmap.so
+rm -rf lib/gtk-2.0/2.10.0/engines/libredmond95.so
+rm -rf lib/gtk-2.0/2.10.0/engines/libthinice.so
+rm -rf gtk-2.0/modules/libferret.*
+rm -rf gtk-2.0/modules/libgail.*
+rm -rf share/gtk-2.0/demo/*
+rm -rf share/man/man1/oldmono.1
+rm -rf share/themes/Crux
+rm -rf share/themes/Default
+rm -rf share/themes/Emacs
+rm -rf share/themes/Industrial
+rm -rf share/themes/Mist
+rm -rf share/themes/Raleigh
+rm -rf share/themes/Redmond
+rm -rf share/themes/ThinIce
+rm -rf share/info
+rm -rf share/icons/gnome
+rm -rf share/icons/hicolor
+rm -rf share/gtk-doc
+rm -rf share/gettext/*.class
+rm -rf share/doc
+rm -rf share/emacs
+rm -rf share/strings
+rm -rf share/pixmaps
+rm -rf share/intltool
+rm -rf var/cache/fontconfig
+
+# delete most of the *.a files
+rm -rf lib/cairo/libcairo-trace.a
+rm -rf lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.a
+rm -rf lib/gtk-2.0/2.10.0/engines/libsvg.a
+rm -rf lib/libCompilerDriver.a
+rm -rf lib/libEnhancedDisassembly.a
+rm -rf lib/libLLVMAnalysis.a
+rm -rf lib/libLLVMArchive.a
+rm -rf lib/libLLVMAsmParser.a
+rm -rf lib/libLLVMAsmPrinter.a
+rm -rf lib/libLLVMBitReader.a
+rm -rf lib/libLLVMBitWriter.a
+rm -rf lib/libLLVMCodeGen.a
+rm -rf lib/libLLVMCore.a
+rm -rf lib/libLLVMExecutionEngine.a
+rm -rf lib/libLLVMInstCombine.a
+rm -rf lib/libLLVMInstrumentation.a
+rm -rf lib/libLLVMInterpreter.a
+rm -rf lib/libLLVMJIT.a
+rm -rf lib/libLLVMLinker.a
+rm -rf lib/libLLVMMC.a
+rm -rf lib/libLLVMMCDisassembler.a
+rm -rf lib/libLLVMMCJIT.a
+rm -rf lib/libLLVMMCParser.a
+rm -rf lib/libLLVMObject.a
+rm -rf lib/libLLVMScalarOpts.a
+rm -rf lib/libLLVMSelectionDAG.a
+rm -rf lib/libLLVMSupport.a
+rm -rf lib/libLLVMTarget.a
+rm -rf lib/libLLVMTransformUtils.a
+rm -rf lib/libLLVMX86AsmParser.a
+rm -rf lib/libLLVMX86AsmPrinter.a
+rm -rf lib/libLLVMX86CodeGen.a
+rm -rf lib/libLLVMX86Disassembler.a
+rm -rf lib/libLLVMX86Info.a
+rm -rf lib/libLLVMipa.a
+rm -rf lib/libLLVMipo.a
+rm -rf lib/libLTO.a
+# rm -rf lib/libMonoPosixHelper.a
+# rm -rf lib/libMonoSupportW.a
+rm -rf lib/libUnitTestMain.a
+rm -rf lib/libatksharpglue-2.a
+rm -rf lib/libcairo-gobject.a
+rm -rf lib/libcairo-script-interpreter.a
+rm -rf lib/libcairo.a
+rm -rf lib/libcroco-0.6.a
+rm -rf lib/libexpat.a
+rm -rf lib/libffi.a
+rm -rf lib/libfontconfig.a
+rm -rf lib/libfreetype.a
+rm -rf lib/libgdiplus.a
+rm -rf lib/libgdksharpglue-2.a
+rm -rf lib/libgettextpo.a
+rm -rf lib/libgif.a
+rm -rf lib/libglade-2.0.a
+rm -rf lib/libgladesharpglue-2.a
+rm -rf lib/libglibsharpglue-2.a
+rm -rf lib/libgtksharpglue-2.a
+rm -rf lib/libikvm-native.a
+rm -rf lib/libintl.a
+rm -rf lib/libjpeg.a
+rm -rf lib/liblzma.a
+# rm -rf lib/libmono-2.0.a
+# rm -rf lib/libmono-llvm.a
+# rm -rf lib/libmono-profiler-aot.a
+# rm -rf lib/libmono-profiler-cov.a
+# rm -rf lib/libmono-profiler-iomap.a
+# rm -rf lib/libmono-profiler-log.a
+# rm -rf lib/libmonosgen-2.0.a
+rm -rf lib/libpangosharpglue-2.a
+rm -rf lib/libpixman-1.a
+rm -rf lib/libpng.a
+rm -rf lib/libpng14.a
+rm -rf lib/librsvg-2.a
+rm -rf lib/libsqlite3.a
+rm -rf lib/libtiff.a
+rm -rf lib/libtiffxx.a
+rm -rf lib/libxml2.a
+
+# we don't need any of the llvm executables except llc and opt
+rm -rf bin/bugpoint
+rm -rf bin/lli
+rm -rf bin/llvm-*
+rm -rf bin/macho-dump
+rm -rf bin/ccache
+
+#
+# 14:39 <baulig> the install script needs to be modified not to
+# install .mdb's for these
+# 14:39 <baulig> System.Windows.dll, System.Xml.Serialization.dll and
+# everything in Facades
+
+find ./lib/mono/4.5/Facades -name "*.mdb" -delete
--- /dev/null
+{\rtf1\mac\ansicpg10000\cocoartf102
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww9000\viewh9000\viewkind0
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
+
+\f0\fs24 \cf0 The software included in the package is licensed under several different agreements.\
+\
+MIT License:\
+\
+http://www.opensource.org/licenses/mit-license.php\
+\
+LGPL:\
+\
+http://www.opensource.org/licenses/lgpl-2.1.php\
+\
+GPL:\
+\
+http://www.opensource.org/licenses/gpl-2.0.php\
+\
+You can develop commercial applications and redistribute the code in this package.
+You only need to obtain a commercial license if you wish to make changes to Mono or
+if you are using Mono as an embedded runtime into your application.\
+\
+Contact contact@xamarin.com if you think you need a license.
+}
--- /dev/null
+{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww15940\viewh15760\viewkind0
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640
+
+\f0\fs24 \cf0 This README is for
+\b Mono.framework @@MONO_VERSION_RELEASE@@
+\b0 .\
+\
+This is the Mono Runtime and Development Platform (http://www.mono-project.com/).\
+\
+This package installs Mono and all of its dependencies inside of /Library/Frameworks/Mono.framework. This behavior is likely to change with a future release so that dependancies will get their own frameworks.\
+\
+The following components are included inside Mono.framework:\
+@@PACKAGES@@\
+\
+Other packages used to build Mono.framework:\
+@@DEP_PACKAGES@@\
+If you want to build native Mac applications with Mono, you can use the MonoMac bindings, an add-on to this product available from http://www.mono-project.com/MonoMac\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720
+\cf0 \
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640
+\cf0 \
+\
+A simple uninstallMono.sh script is included in the disk image. This is shell script that must be run as root, and it will remove the Mono.framework and the links in /usr/bin.\
+\
+This package was created by the Mono team. Major contributors to this team include (in alphabetical order): \
+\
+Wade Berrier\
+Adhamh Findlay\
+Miguel de Icaza\
+Urs Muff\
+Geoff Norton\
+Andy Satori\
+\
+Questions or problems related directly to the Mono.framework should be addressed to mono-osx@lists.xamarin.com.\
+\
+Questions about Mono should be directed to an appropriate resource that can be found on http://www.mono-project.com/about. \
+}
\ No newline at end of file
--- /dev/null
+{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf360
+{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww9000\viewh9000\viewkind0
+\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural
+
+\f0\fs32 \cf0 Welcome to
+\b Mono.framework @@MONO_VERSION_RELEASE@@
+\b0 for OS X.
+\fs36 \
+\
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<installer-gui-script minSpecVersion="1">
+ <title>Mono Framework</title>
+ <license file="License.rtf" mime-type="application/rtf" />
+ <readme file="ReadMe.rtf" mime-type="application/rtf" />
+ <pkg-ref id="mono">
+ <bundle-version/>
+ </pkg-ref>
+ <choices-outline>
+ <line choice="default">
+ <line choice="mono"/>
+ </line>
+ </choices-outline>
+ <choice id="default"/>
+ <choice id="mono" visible="false">
+ <pkg-ref id="mono"/>
+ </choice>
+ <pkg-ref id="mono">#mono.pkg</pkg-ref>
+</installer-gui-script>
--- /dev/null
+#!/bin/sh -x
+
+FW=/Library/Frameworks/Mono.framework
+FW_CURRENT=${FW}/Versions/Current
+CURRENT=`basename $(readlink ${FW_CURRENT})`
+
+# Remove PCL assemblies that we installed from Mono 3.1.1
+LICENSE="Portable Class Library Reference Assemblies License-07JUN2013.docx"
+if [ -f "$FW/External/xbuild-frameworks/.NETPortable/$LICENSE" ]; then
+ echo "Removing PCL because we're upgrading from 3.1.1" >> /tmp/mono-installation
+ rm -rf $FW/External/xbuild-frameworks/.NETPortable
+fi
+
+# Remove /usr/local/bin/pkg-config if it's a symlink to the Mono-installed one
+PKG_CONFIG_LINK="/usr/local/bin/pkg-config"
+if [ -L $PKG_CONFIG_LINK ]; then
+ location=`readlink $PKG_CONFIG_LINK`
+ case "$location" in
+ *Mono.framework*) rm $PKG_CONFIG_LINK;;
+ esac
+fi
+
+WHITELIST=$(cat "$(dirname "$0")/whitelist.txt")
+MONO_COMMANDS_FILE=/etc/paths.d/mono-commands
+FW_WHITELISTED_COMMANDS=${FW_CURRENT}/Commands
+
+mkdir ${FW_WHITELISTED_COMMANDS}
+
+if test -e ${MONO_COMMANDS_FILE}; then
+ rm "${MONO_COMMANDS_FILE}"
+fi
+
+echo "${FW_WHITELISTED_COMMANDS}" >> "${MONO_COMMANDS_FILE}"
+
+if [ -d "${FW}"/Commands ]; then
+ for i in ${WHITELIST}; do
+ if test -e "${FW}/Commands/${i}"; then
+ ln -s "${FW}/Commands/${i}" "${FW_WHITELISTED_COMMANDS}/${i}"
+ fi
+ done;
+else
+ echo "${FW}/Commands does not exist"
+ echo "Can not add command links to $PATH."
+fi
+
+if [ -d ${FW_CURRENT} ]; then
+ cd ${FW_CURRENT}/share/man
+ for i in ${WHITELIST}; do
+ for j in $(ls man*/${i}.*); do
+ if test ! -e "/usr/local/share/man/${j}"; then
+ ln -sf "${FW_CURRENT}/share/man/${j}" "/usr/local/share/man/${j}"
+ fi
+ done
+ done
+
+ cd ${FW_CURRENT}/etc
+ # Make sure we run the files we lay down, and not other stuff installed on the system
+ export PATH=${FW_CURRENT}/bin:$PATH
+ # gtk+ setup
+ gdk-pixbuf-query-loaders --update-cache
+ # pango setup
+ mkdir -p pango
+ pango-querymodules > pango/pango.modules
+ pango-querymodules --update-cache
+ fc-cache
+
+ cd ${FW_CURRENT}/lib/gtk-2.0/2.10.0
+ gtk-query-immodules-2.0 > immodules.cache
+fi
+
+# Delete older Monos
+#
+# - keep if the major version is different
+# - keep if 'Versions/x.y.z/keep' exists
+# - Keep if it is greater than $CURRENT
+#
+echo "Current:" $CURRENT >> /tmp/mono-installation
+
+pushd ${FW}/Versions
+for i in `ls -d *`; do
+ result=`echo "${i:0:1} == ${CURRENT:0:1}" | bc`
+ if [ $result -ne 1 ]; then
+ echo "keeping" $i "because it has a different major version" >> /tmp/mono-installation
+ continue
+ fi
+
+ if [ -f $i/keep ]; then
+ echo "Keeping" $i "because of keep file" >> /tmp/mono-installation
+ continue
+ fi
+
+ # A magical bit of Perl: http://stackoverflow.com/a/7366753/494990
+ result=$(perl -e '($a,$b)=@ARGV; for ($a,$b) {s/(\d+)/sprintf "%5d", $1/ge}; print $a cmp $b;' $i $CURRENT)
+ if [ $result -ge 0 ]; then
+ echo "Skipping" $i "because $i >= $CURRENT" >> /tmp/mono-installation
+ continue
+
+ else
+ echo "rm -rf" $i >> /tmp/mono-installation
+ rm -rf $i
+ fi
+done
+popd
+
+# Mono framework should be owned by root
+chown -R root:admin ${FW}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>BuildVersion</key>
+ <string>@@MONO_VERSION_RELEASE@@</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@@MONO_VERSION_RELEASE@@</string>
+ <key>CFBundleVersion</key>
+ <string>@@MONO_VERSION_RELEASE@@</string>
+ <key>ProjectName</key>
+ <string>Mono</string>
+ <key>SourceVersion</key>
+ <string>@@MONO_VERSION_RELEASE@@</string>
+</dict>
+</plist>
--- /dev/null
+al
+al2
+asp-state
+asp-state2
+asp-state4
+booc
+booi
+booish
+caspol
+ccrewrite
+cccheck
+cert2spc
+certmgr
+chktrust
+ClassInitGenerator
+csharp
+csharp2
+dbsessmgr
+dbsessmgr2
+dbsessmgr4
+disco
+dmcs
+dtd2rng
+dtd2xsd
+fastcgi-mono-server
+fastcgi-mono-server2
+fastcgi-mono-server4
+fsharpc
+fsharpc2
+fsharpi
+fsharpi2
+gacutil
+gacutil2
+gapi2-codegen
+gapi2-fixup
+gapi2-parser
+genxs
+gmcs
+httpcfg
+ikdasm
+ilasm
+installvst
+ipy
+ipy64
+ipyw
+ipyw64
+ir
+ir64
+IronRuby.Tests
+irw
+irw64
+lc
+macpack
+makecert
+mautil
+mconfig
+mcs
+mdassembler
+mdoc
+mdoc-assemble
+mdoc-export-html
+mdoc-export-msxdoc
+mdoc-update
+mdoc-validate
+mdvalidater
+mkbundle
+mod
+mod-mono-server
+mod-mono-server2
+mod-mono-server4
+mono
+mono64
+mono-boehm
+mono-api-info
+mono-api-html
+mono-cil-strip
+mono-configuration-crypto
+monodis
+monodocer
+monodocs2html
+monodocs2slashdoc
+mono-find-provides
+mono-find-requires
+mono-gdb.py
+monograph
+mono-heapviz
+monolinker
+monop
+monop2
+mono-service
+mono-service2
+mono-sgen
+mono-shlib-cop
+mono-symbolicate
+mono-test-install
+mono-xmltool
+mozroots
+mprof-report
+msbuild
+nant
+nuget
+nunit-console
+nunit-console2
+nunit-console4
+pdb2mdb
+pedump
+permview
+peverify
+prj2make
+resgen
+resgen2
+secutil
+setreg
+sgen
+signcode
+sn
+soapsuds
+sqlmetal
+sqlsharp
+svcutil
+vbnc
+vbnc2
+wsdl
+wsdl2
+xbuild
+xsd
+xsp
+xsp2
+xsp4
--- /dev/null
+#!/bin/sh -x
+
+#This script removes Mono from an OS X System. It must be run as root
+
+rm -r /Library/Frameworks/Mono.framework
+
+# In 10.6+ the receipts are stored here
+rm /var/db/receipts/com.ximian.mono*
+
+for dir in /usr/local/bin; do
+ (cd ${dir};
+ for i in `ls -al | grep /Library/Frameworks/Mono.framework/ | awk '{print $9}'`; do
+ rm ${i}
+ done);
+done
--- /dev/null
+class PangoPackage (GnomeXzPackage):
+
+ def __init__(self):
+ GnomePackage.__init__(self,
+ 'pango',
+ version_major='1.35',
+ version_minor='0',
+ configure_flags=[
+ '--without-x',
+ '--enable-gtk-doc-html=no'
+ ]
+ )
+
+ self.sources.extend([
+ # 1
+ # Bug 321419 - Allow environment var substitution in Pango config
+ # https://bugzilla.gnome.org/show_bug.cgi?id=321419
+ 'patches/pango-relative-config-file.patch',
+
+ # BXC 10257 - Characters outside the Basic Multilingual Plane don't render correctly
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=10257
+ 'patches/pango-coretext-astral-plane-1.patch',
+ 'patches/pango-coretext-astral-plane-2.patch',
+
+ # Bug 15787 - Caret position is wrong when there are ligatures
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=15787
+ 'patches/pango-disable-ligatures.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=22199
+ 'patches/pango-fix-ct_font_descriptor_get_weight-crasher.patch',
+
+ # https://bugzilla.gnome.org/show_bug.cgi?id=734372
+ 'patches/pango-coretext-condensed-trait.patch',
+
+ # https://bugzilla.xamarin.com/show_bug.cgi?id=32938
+ 'patches/pango-coretext-fix-yosemite-crasher.patch',
+
+ 'patches/pango-system-font-single.patch',
+ 'patches/pango-system-font-check-version.patch'
+ ])
+
+ def prep(self):
+ GnomePackage.prep(self)
+ if Package.profile.name == 'darwin':
+ for p in range(1, len(self.local_sources)):
+ self.sh('patch -p1 < "%{local_sources[' + str(p) + ']}"')
+
+ def deploy(self):
+ self.sh('pango-querymodules --update-cache')
+
+PangoPackage()
--- /dev/null
+--- a/src/cairo-quartz-font.c 2012-11-13 18:20:00.000000000 -0800
++++ b/src/cairo-quartz-font.c 2012-11-13 18:06:56.000000000 -0800
+@@ -90,8 +90,9 @@ static int (*CGFontGetAscentPtr) (CGFont
+ static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
+ static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
+
+-/* Not public anymore in 64-bits nor in 10.7 */
+-static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
++/* CTFontCreateWithGraphicsFont is not public until 10.5. */
++typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
++static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
+
+ static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
+ static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
+@@ -130,7 +131,7 @@ quartz_font_ensure_symbols(void)
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+
+- FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
++ CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
+
+ if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
+ CGFontGetGlyphBBoxesPtr &&
+@@ -155,6 +156,7 @@ struct _cairo_quartz_font_face {
+ cairo_font_face_t base;
+
+ CGFontRef cgFont;
++ CTFontRef ctFont;
+ };
+
+ /*
+@@ -239,6 +241,10 @@ _cairo_quartz_font_face_destroy (void *a
+ {
+ cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
+
++ if (font_face->ctFont) {
++ CFRelease (font_face->ctFont);
++ }
++
+ CGFontRelease (font_face->cgFont);
+ }
+
+@@ -363,6 +369,12 @@ cairo_quartz_font_face_create_for_cgfont
+
+ font_face->cgFont = CGFontRetain (font);
+
++ if (CTFontCreateWithGraphicsFontPtr) {
++ font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL);
++ } else {
++ font_face->ctFont = NULL;
++ }
++
+ _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
+
+ return &font_face->base;
+@@ -782,49 +794,10 @@ _cairo_quartz_scaled_font_get_cg_font_re
+ return ffont->cgFont;
+ }
+
+-/*
+- * compat with old ATSUI backend
+- */
+-
+-/**
+- * cairo_quartz_font_face_create_for_atsu_font_id
+- * @font_id: an ATSUFontID for the font.
+- *
+- * Creates a new font for the Quartz font backend based on an
+- * #ATSUFontID. This font can then be used with
+- * cairo_set_font_face() or cairo_scaled_font_create().
+- *
+- * Return value: a newly created #cairo_font_face_t. Free with
+- * cairo_font_face_destroy() when you are done using it.
+- *
+- * Since: 1.6
+- **/
+-cairo_font_face_t *
+-cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
++CTFontRef
++_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
+ {
+- quartz_font_ensure_symbols();
+-
+- if (FMGetATSFontRefFromFontPtr != NULL) {
+- ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
+- CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
+- cairo_font_face_t *ff;
+-
+- ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
+-
+- CGFontRelease (cgFont);
+-
+- return ff;
+- } else {
+- _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+- return (cairo_font_face_t *)&_cairo_font_face_nil;
+- }
+-}
+-
+-/* This is the old name for the above function, exported for compat purposes */
+-cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
++ cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
+
+-cairo_font_face_t *
+-cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
+-{
+- return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
++ return ffont->ctFont;
+ }
+--- a/src/cairo-quartz-image-surface.c 2010-06-18 04:47:13.000000000 -0700
++++ b/src/cairo-quartz-image-surface.c 2012-11-13 18:06:56.000000000 -0800
+@@ -148,6 +148,8 @@ _cairo_quartz_image_surface_flush (void
+ surface->image = newImage;
+ CGImageRelease (oldImage);
+
++ surface->base.is_clear = surface->imageSurface->base.is_clear;
++
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+@@ -270,6 +272,8 @@ cairo_quartz_image_surface_create (cairo
+ qisurf->image = image;
+ qisurf->imageSurface = image_surface;
+
++ qisurf->base.is_clear = image_surface->base.is_clear;
++
+ return &qisurf->base;
+ }
+
+--- a/src/cairo-quartz-private.h 2010-12-25 06:21:34.000000000 -0800
++++ b/src/cairo-quartz-private.h 2012-11-13 18:06:56.000000000 -0800
+@@ -50,6 +50,9 @@ typedef CGFloat cairo_quartz_float_t;
+ typedef float cairo_quartz_float_t;
+ #endif
+
++/* define CTFontRef for pre-10.5 SDKs */
++typedef const struct __CTFont *CTFontRef;
++
+ typedef struct cairo_quartz_surface {
+ cairo_surface_t base;
+
+@@ -60,21 +63,22 @@ typedef struct cairo_quartz_surface {
+ cairo_surface_t *imageSurfaceEquiv;
+
+ cairo_surface_clipper_t clipper;
+- cairo_rectangle_int_t extents;
+
+- /* These are stored while drawing operations are in place, set up
+- * by quartz_setup_source() and quartz_finish_source()
++ /**
++ * If non-null, this is a CGImage representing the contents of the surface.
++ * We clear this out before any painting into the surface, so that we
++ * don't force a copy to be created.
+ */
+- CGAffineTransform sourceTransform;
++ CGImageRef bitmapContextImage;
+
+- CGImageRef sourceImage;
+- cairo_surface_t *sourceImageSurface;
+- CGRect sourceImageRect;
++ /**
++ * If non-null, this is the CGLayer for the surface.
++ */
++ CGLayerRef cgLayer;
+
+- CGShadingRef sourceShading;
+- CGPatternRef sourcePattern;
++ cairo_rectangle_int_t extents;
+
+- CGInterpolationQuality oldInterpolationQuality;
++ cairo_bool_t ownsData;
+ } cairo_quartz_surface_t;
+
+ typedef struct cairo_quartz_image_surface {
+@@ -103,6 +107,9 @@ _cairo_quartz_create_cgimage (cairo_form
+ CGFontRef
+ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
+
++CTFontRef
++_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
++
+ #else
+
+ # error Cairo was not compiled with support for the quartz backend
+--- a/src/cairo-quartz-surface.c 2012-11-13 18:20:00.000000000 -0800
++++ b/src/cairo-quartz-surface.c 2012-11-13 18:06:56.000000000 -0800
+@@ -41,6 +41,8 @@
+
+ #include "cairo-error-private.h"
+ #include "cairo-surface-clipper-private.h"
++#include "cairo-gstate-private.h"
++#include "cairo-private.h"
+
+ #include <dlfcn.h>
+
+@@ -77,6 +79,11 @@
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
++/* Here are some of the differences between cairo and CoreGraphics
++ - cairo has only a single source active at once vs. CoreGraphics having
++ separate sources for stroke and fill
++*/
++
+ /* This method is private, but it exists. Its params are are exposed
+ * as args to the NS* method, but not as CG.
+ */
+@@ -126,6 +133,12 @@ static void (*CGContextSetShouldAntialia
+ static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+ static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
++static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
++
++/* CTFontDrawGlyphs is not available until 10.7 */
++static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
++
++static SInt32 _cairo_quartz_osx_version = 0x0;
+
+ static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
+
+@@ -160,6 +173,14 @@ static void quartz_ensure_symbols(void)
+ CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
++ CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
++
++ CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
++
++ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
++ // assume 10.5
++ _cairo_quartz_osx_version = 0x1050;
++ }
+
+ _cairo_quartz_symbol_lookup_done = TRUE;
+ }
+@@ -430,6 +446,7 @@ _cairo_quartz_cairo_operator_to_quartz_c
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ default:
+ assert (0);
++ return kPrivateCGCompositeClear;
+ }
+ }
+
+@@ -598,10 +615,13 @@ _cairo_quartz_cairo_matrix_to_quartz (co
+ typedef struct {
+ bool isClipping;
+ CGGlyph *cg_glyphs;
+- CGSize *cg_advances;
++ union {
++ CGSize *cg_advances;
++ CGPoint *cg_positions;
++ } u;
+ size_t nglyphs;
+ CGAffineTransform textTransform;
+- CGFontRef font;
++ cairo_scaled_font_t *scaled_font;
+ CGPoint origin;
+ } unbounded_show_glyphs_t;
+
+@@ -679,12 +699,6 @@ _cairo_quartz_fixup_unbounded_operation
+ else
+ CGContextEOFillPath (cgc);
+ } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
+- CGContextSetFont (cgc, op->u.show_glyphs.font);
+- CGContextSetFontSize (cgc, 1.0);
+- CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+- CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
+- CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
+-
+ if (op->u.show_glyphs.isClipping) {
+ /* Note that the comment in show_glyphs about kCGTextClip
+ * and the text transform still applies here; however, the
+@@ -693,12 +707,25 @@ _cairo_quartz_fixup_unbounded_operation
+ CGContextSetTextDrawingMode (cgc, kCGTextClip);
+ CGContextSaveGState (cgc);
+ }
++ CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
++ CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
++ if (CTFontDrawGlyphsPtr) {
++ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font),
++ op->u.show_glyphs.cg_glyphs,
++ op->u.show_glyphs.u.cg_positions,
++ op->u.show_glyphs.nglyphs,
++ cgc);
++ } else {
++ CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font));
++ CGContextSetFontSize (cgc, 1.0);
++ CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
++
++ CGContextShowGlyphsWithAdvances (cgc,
++ op->u.show_glyphs.cg_glyphs,
++ op->u.show_glyphs.u.cg_advances,
++ op->u.show_glyphs.nglyphs);
+
+- CGContextShowGlyphsWithAdvances (cgc,
+- op->u.show_glyphs.cg_glyphs,
+- op->u.show_glyphs.cg_advances,
+- op->u.show_glyphs.nglyphs);
+-
++ }
+ if (op->u.show_glyphs.isClipping) {
+ CGContextClearRect (cgc, clipBoxRound);
+ CGContextRestoreGState (cgc);
+@@ -1102,12 +1129,12 @@ DataProviderReleaseCallback (void *info,
+ {
+ quartz_source_image_t *source_img = info;
+ _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
++ cairo_surface_destroy (source_img->surface);
+ free (source_img);
+ }
+
+ static cairo_status_t
+-_cairo_surface_to_cgimage (cairo_surface_t *target,
+- cairo_surface_t *source,
++_cairo_surface_to_cgimage (cairo_surface_t *source,
+ CGImageRef *image_out)
+ {
+ cairo_status_t status;
+@@ -1127,9 +1154,14 @@ _cairo_surface_to_cgimage (cairo_surface
+ }
+
+ if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
+- *image_out = CGBitmapContextCreateImage (surface->cgContext);
+- if (*image_out)
+- return CAIRO_STATUS_SUCCESS;
++ if (!surface->bitmapContextImage) {
++ surface->bitmapContextImage =
++ CGBitmapContextCreateImage (surface->cgContext);
++ }
++ if (surface->bitmapContextImage) {
++ *image_out = CGImageRetain (surface->bitmapContextImage);
++ return CAIRO_STATUS_SUCCESS;
++ }
+ }
+ }
+
+@@ -1137,10 +1169,11 @@ _cairo_surface_to_cgimage (cairo_surface
+ if (source_img == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+- source_img->surface = source;
++ source_img->surface = cairo_surface_reference(source);
+
+ status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
+ if (status) {
++ cairo_surface_destroy (source_img->surface);
+ free (source_img);
+ return status;
+ }
+@@ -1251,7 +1284,7 @@ _cairo_quartz_cairo_repeating_surface_pa
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image);
++ status = _cairo_surface_to_cgimage (pat_surf, &image);
+ if (status)
+ return status;
+ if (image == NULL)
+@@ -1322,16 +1355,43 @@ typedef enum {
+ DO_SHADING,
+ DO_PATTERN,
+ DO_IMAGE,
++ DO_TILED_IMAGE,
++ DO_LAYER,
+ DO_UNSUPPORTED,
+- DO_NOTHING,
+- DO_TILED_IMAGE
++ DO_NOTHING
+ } cairo_quartz_action_t;
+
+-static cairo_quartz_action_t
++/* State used during a drawing operation. */
++typedef struct {
++ CGContextRef context;
++ cairo_quartz_action_t action;
++
++ // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
++ CGAffineTransform transform;
++
++ // Used with DO_IMAGE and DO_TILED_IMAGE
++ CGImageRef image;
++ cairo_surface_t *imageSurface;
++
++ // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
++ CGRect imageRect;
++
++ // Used with DO_LAYER
++ CGLayerRef layer;
++
++ // Used with DO_SHADING
++ CGShadingRef shading;
++
++ // Used with DO_PATTERN
++ CGPatternRef pattern;
++} cairo_quartz_drawing_state_t;
++
++static void
+ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source)
++ const cairo_pattern_t *source,
++ cairo_quartz_drawing_state_t *state)
+ {
+- CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
++ CGRect clipBox = CGContextGetClipBoundingBox (state->context);
+ double x0, y0, w, h;
+
+ cairo_surface_t *fallback;
+@@ -1340,8 +1400,10 @@ _cairo_quartz_setup_fallback_source (cai
+ cairo_status_t status;
+
+ if (clipBox.size.width == 0.0f ||
+- clipBox.size.height == 0.0f)
+- return DO_NOTHING;
++ clipBox.size.height == 0.0f) {
++ state->action = DO_NOTHING;
++ return;
++ }
+
+ x0 = floor(clipBox.origin.x);
+ y0 = floor(clipBox.origin.y);
+@@ -1384,18 +1446,21 @@ _cairo_quartz_setup_fallback_source (cai
+ }
+ #endif
+
+- status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
+- if (status)
+- return DO_UNSUPPORTED;
+- if (img == NULL)
+- return DO_NOTHING;
+-
+- surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
+- surface->sourceImage = img;
+- surface->sourceImageSurface = fallback;
+- surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
++ status = _cairo_surface_to_cgimage (fallback, &img);
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
++ }
++ if (img == NULL) {
++ state->action = DO_NOTHING;
++ return;
++ }
+
+- return DO_IMAGE;
++ state->imageRect = CGRectMake (0.0, 0.0, w, h);
++ state->image = img;
++ state->imageSurface = fallback;
++ state->transform = CGAffineTransformMakeTranslation (x0, y0);
++ state->action = DO_IMAGE;
+ }
+
+ /*
+@@ -1411,10 +1476,11 @@ based on the extents of the object (the
+ we don't want the rasterization of the entire gradient to depend on the
+ clip region).
+ */
+-static cairo_quartz_action_t
++static void
+ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
+ const cairo_linear_pattern_t *lpat,
+- cairo_rectangle_int_t *extents)
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
+ {
+ const cairo_pattern_t *abspat = &lpat->base.base;
+ cairo_matrix_t mat;
+@@ -1424,9 +1490,10 @@ _cairo_quartz_setup_linear_source (cairo
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+
+ if (lpat->base.n_stops == 0) {
+- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+- return DO_SOLID;
++ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
++ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
++ state->action = DO_SOLID;
++ return;
+ }
+
+ if (lpat->p1.x == lpat->p2.x &&
+@@ -1436,12 +1503,13 @@ _cairo_quartz_setup_linear_source (cairo
+ * Whatever the correct behaviour is, let's at least have only pixman's
+ * implementation to worry about.
+ */
+- return _cairo_quartz_setup_fallback_source (surface, abspat);
++ _cairo_quartz_setup_fallback_source (surface, abspat, state);
++ return;
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
++ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+@@ -1461,21 +1529,22 @@ _cairo_quartz_setup_linear_source (cairo
+ extents);
+ }
+
+- surface->sourceShading = CGShadingCreateAxial (rgb,
+- start, end,
+- gradFunc,
+- extend, extend);
++ state->shading = CGShadingCreateAxial (rgb,
++ start, end,
++ gradFunc,
++ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+- return DO_SHADING;
++ state->action = DO_SHADING;
+ }
+
+-static cairo_quartz_action_t
++static void
+ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
+ const cairo_radial_pattern_t *rpat,
+- cairo_rectangle_int_t *extents)
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
+ {
+ const cairo_pattern_t *abspat = &rpat->base.base;
+ cairo_matrix_t mat;
+@@ -1494,9 +1563,10 @@ _cairo_quartz_setup_radial_source (cairo
+ double centerDistance = sqrt (dx*dx + dy*dy);
+
+ if (rpat->base.n_stops == 0) {
+- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+- return DO_SOLID;
++ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
++ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
++ state->action = DO_SOLID;
++ return;
+ }
+
+ if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
+@@ -1507,12 +1577,13 @@ _cairo_quartz_setup_radial_source (cairo
+ * implementation to worry about.
+ * Note that this also catches the cases where r1 == r2.
+ */
+- return _cairo_quartz_setup_fallback_source (surface, abspat);
++ _cairo_quartz_setup_fallback_source (surface, abspat, state);
++ return;
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
++ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+@@ -1531,90 +1602,79 @@ _cairo_quartz_setup_radial_source (cairo
+ extents);
+ }
+
+- surface->sourceShading = CGShadingCreateRadial (rgb,
+- start,
+- r1,
+- end,
+- r2,
+- gradFunc,
+- extend, extend);
++ state->shading = CGShadingCreateRadial (rgb,
++ start,
++ r1,
++ end,
++ r2,
++ gradFunc,
++ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+- return DO_SHADING;
++ state->action = DO_SHADING;
+ }
+
+-static cairo_quartz_action_t
+-_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source,
+- cairo_rectangle_int_t *extents)
++static void
++_cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface,
++ const cairo_surface_pattern_t *spat,
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
+ {
+- assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
+-
+- surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
+- CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
++ const cairo_pattern_t *source = &spat->base;
++ CGContextRef context = state->context;
+
+- if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+- cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+-
+- CGContextSetRGBStrokeColor (surface->cgContext,
+- solid->color.red,
+- solid->color.green,
+- solid->color.blue,
+- solid->color.alpha);
+- CGContextSetRGBFillColor (surface->cgContext,
+- solid->color.red,
+- solid->color.green,
+- solid->color.blue,
+- solid->color.alpha);
+-
+- return DO_SOLID;
+- }
+-
+- if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
+- const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
+- return _cairo_quartz_setup_linear_source (surface, lpat, extents);
+- }
+-
+- if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
+- const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
+- return _cairo_quartz_setup_radial_source (surface, rpat, extents);
+- }
+-
+- if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+- (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
++ if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD ||
++ (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
+ {
+- const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ cairo_surface_t *pat_surf = spat->surface;
+ CGImageRef img;
+ cairo_matrix_t m = spat->base.matrix;
+ cairo_rectangle_int_t extents;
+- cairo_status_t status;
+ CGAffineTransform xform;
+ CGRect srcRect;
+ cairo_fixed_t fw, fh;
+ cairo_bool_t is_bounded;
++ cairo_bool_t repeat = source->extend == CAIRO_EXTEND_REPEAT;
++ cairo_status_t status;
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
+- if (status)
+- return DO_UNSUPPORTED;
+- if (img == NULL)
+- return DO_NOTHING;
++ cairo_matrix_invert(&m);
++ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
+
+- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
++ /* Draw nonrepeating CGLayer surface using DO_LAYER */
++ if (!repeat && cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
++ cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
++ if (quartz_surf->cgLayer) {
++ state->imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
++ state->layer = quartz_surf->cgLayer;
++ state->action = DO_LAYER;
++ return;
++ }
++ }
++
++ status = _cairo_surface_to_cgimage (pat_surf, &img);
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
++ }
++ if (img == NULL) {
++ state->action = DO_NOTHING;
++ return;
++ }
+
+- surface->sourceImage = img;
++ /* XXXroc what is this for? */
++ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+
+- cairo_matrix_invert(&m);
+- _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
++ state->image = img;
+
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+- if (source->extend == CAIRO_EXTEND_NONE) {
+- surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
+- return DO_IMAGE;
++ if (!repeat) {
++ state->imageRect = CGRectMake (0, 0, extents.width, extents.height);
++ state->action = DO_IMAGE;
++ return;
+ }
+
+ /* Quartz seems to tile images at pixel-aligned regions only -- this
+@@ -1624,8 +1684,8 @@ _cairo_quartz_setup_source (cairo_quartz
+ * epsilon), and if not, fall back to the CGPattern type.
+ */
+
+- xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
+- surface->sourceTransform);
++ xform = CGAffineTransformConcat (CGContextGetCTM (context),
++ state->transform);
+
+ srcRect = CGRectMake (0, 0, extents.width, extents.height);
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+@@ -1646,101 +1706,218 @@ _cairo_quartz_setup_source (cairo_quartz
+
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+- surface->sourceImageRect = srcRect;
+-
+- return DO_TILED_IMAGE;
++ state->imageRect = srcRect;
++ state->action = DO_TILED_IMAGE;
++ return;
+ }
+
+ /* Fall through to generic SURFACE case */
+ }
+
+- if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+- cairo_quartz_float_t patternAlpha = 1.0f;
+- CGColorSpaceRef patternSpace;
+- CGPatternRef pattern;
+- cairo_int_status_t status;
+-
+- status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
+- if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+- return DO_NOTHING;
+- if (status)
+- return DO_UNSUPPORTED;
+-
+- // Save before we change the pattern, colorspace, etc. so that
+- // we can restore and make sure that quartz releases our
+- // pattern (which may be stack allocated)
+- CGContextSaveGState(surface->cgContext);
+-
+- patternSpace = CGColorSpaceCreatePattern(NULL);
+- CGContextSetFillColorSpace (surface->cgContext, patternSpace);
+- CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
+- CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
+- CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
+- CGColorSpaceRelease (patternSpace);
+-
+- /* Quartz likes to munge the pattern phase (as yet unexplained
+- * why); force it to 0,0 as we've already baked in the correct
+- * pattern translation into the pattern matrix
+- */
+- CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
+-
+- surface->sourcePattern = pattern;
++ CGFloat patternAlpha = 1.0f;
++ CGColorSpaceRef patternSpace;
++ CGPatternRef pattern;
++ cairo_int_status_t status;
+
+- return DO_PATTERN;
++ status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ state->action = DO_NOTHING;
++ return;
++ }
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
+ }
+
+- return DO_UNSUPPORTED;
++ patternSpace = CGColorSpaceCreatePattern (NULL);
++ CGContextSetFillColorSpace (context, patternSpace);
++ CGContextSetFillPattern (context, pattern, &patternAlpha);
++ CGContextSetStrokeColorSpace (context, patternSpace);
++ CGContextSetStrokePattern (context, pattern, &patternAlpha);
++ CGColorSpaceRelease (patternSpace);
++
++ /* Quartz likes to munge the pattern phase (as yet unexplained
++ * why); force it to 0,0 as we've already baked in the correct
++ * pattern translation into the pattern matrix
++ */
++ CGContextSetPatternPhase (context, CGSizeMake(0,0));
++
++ state->pattern = pattern;
++ state->action = DO_PATTERN;
++ return;
+ }
+
++/**
++ * Call this before any operation that can modify the contents of a
++ * cairo_quartz_surface_t.
++ */
+ static void
+-_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source)
++_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface)
+ {
+- CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
++ if (surface->bitmapContextImage) {
++ CGImageRelease (surface->bitmapContextImage);
++ surface->bitmapContextImage = NULL;
++ }
++}
+
+- if (surface->sourceImage) {
+- CGImageRelease(surface->sourceImage);
+- surface->sourceImage = NULL;
++/**
++ * Sets up internal state to be used to draw the source mask, stored in
++ * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on
++ * surface->cgContext.
++ */
++static cairo_quartz_drawing_state_t
++_cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
++ const cairo_pattern_t *source,
++ cairo_operator_t op,
++ cairo_rectangle_int_t *extents)
++{
++ CGContextRef context = surface->cgContext;
++ cairo_quartz_drawing_state_t state;
++ cairo_status_t status;
+
+- cairo_surface_destroy(surface->sourceImageSurface);
+- surface->sourceImageSurface = NULL;
++ state.context = context;
++ state.image = NULL;
++ state.imageSurface = NULL;
++ state.layer = NULL;
++ state.shading = NULL;
++ state.pattern = NULL;
++
++ _cairo_quartz_surface_will_change (surface);
++
++ // Save before we change the pattern, colorspace, etc. so that
++ // we can restore and make sure that quartz releases our
++ // pattern (which may be stack allocated)
++ CGContextSaveGState(context);
++
++ CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
++
++ status = _cairo_quartz_surface_set_cairo_operator (surface, op);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ state.action = DO_NOTHING;
++ return state;
++ }
++ if (status) {
++ state.action = DO_UNSUPPORTED;
++ return state;
+ }
+
+- if (surface->sourceShading) {
+- CGShadingRelease(surface->sourceShading);
+- surface->sourceShading = NULL;
++ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
++ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
++
++ CGContextSetRGBStrokeColor (context,
++ solid->color.red,
++ solid->color.green,
++ solid->color.blue,
++ solid->color.alpha);
++ CGContextSetRGBFillColor (context,
++ solid->color.red,
++ solid->color.green,
++ solid->color.blue,
++ solid->color.alpha);
++
++ state.action = DO_SOLID;
++ return state;
++ }
++
++ if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
++ const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
++ _cairo_quartz_setup_linear_source (surface, lpat, extents, &state);
++ return state;
++ }
++
++ if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
++ const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
++ _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
++ return state;
+ }
+
+- if (surface->sourcePattern) {
+- CGPatternRelease(surface->sourcePattern);
+- // To tear down the pattern and colorspace
+- CGContextRestoreGState(surface->cgContext);
++ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
++ if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (source, NULL) &&
++ CGContextGetAlphaPtr &&
++ CGContextGetAlphaPtr (surface->cgContext) == 1.0) {
++ // Quartz won't touch pixels outside the bounds of the
++ // source surface, so we can just go ahead and use Copy here
++ // to accelerate things.
++ // Quartz won't necessarily be able to do this optimization internally;
++ // for CGLayer surfaces, we can know all the pixels are opaque
++ // (because it's CONTENT_COLOR), but Quartz won't know.
++ CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy);
++ }
+
+- surface->sourcePattern = NULL;
++ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
++ _cairo_quartz_setup_surface_source (surface, spat, extents, &state);
++ return state;
+ }
+-}
+
++ state.action = DO_UNSUPPORTED;
++ return state;
++}
+
++/**
++ * 1) Tears down internal state used to draw the source
++ * 2) Does CGContextRestoreGState(state->context)
++ */
+ static void
+-_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_action_t action)
++_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
+ {
+- assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE));
++ if (state->image) {
++ CGImageRelease(state->image);
++ }
+
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
+- CGContextScaleCTM (surface->cgContext, 1, -1);
+-
+- if (action == DO_IMAGE) {
+- CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+- if (!_cairo_operator_bounded_by_source(op)) {
+- CGContextBeginPath (surface->cgContext);
+- CGContextAddRect (surface->cgContext, surface->sourceImageRect);
+- CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
+- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0);
+- CGContextEOFillPath (surface->cgContext);
++ if (state->imageSurface) {
++ cairo_surface_destroy(state->imageSurface);
++ }
++
++ if (state->shading) {
++ CGShadingRelease(state->shading);
++ }
++
++ if (state->pattern) {
++ CGPatternRelease(state->pattern);
++ }
++
++ CGContextRestoreGState(state->context);
++}
++
++
++static void
++_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
++{
++ assert (state &&
++ ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) ||
++ (state->layer && state->action == DO_LAYER)));
++
++ CGContextConcatCTM (state->context, state->transform);
++ CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
++ CGContextScaleCTM (state->context, 1, -1);
++
++ if (state->action == DO_TILED_IMAGE) {
++ CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
++ /* no need to worry about unbounded operators, since tiled images
++ fill the entire clip region */
++ } else {
++ if (state->action == DO_LAYER) {
++ /* Note that according to Apple docs it's completely legal
++ * to draw a CGLayer to any CGContext, even one it wasn't
++ * created for.
++ */
++ CGContextSetInterpolationQuality (state->context, kCGInterpolationNone);
++ CGContextDrawLayerAtPoint (state->context, state->imageRect.origin,
++ state->layer);
++ } else {
++ CGContextDrawImage (state->context, state->imageRect, state->image);
++ }
++
++ /* disable this EXTEND_NONE correctness code because we use this path
++ * for both EXTEND_NONE and EXTEND_PAD */
++ if (0 && !_cairo_operator_bounded_by_source (op)) {
++ CGContextBeginPath (state->context);
++ CGContextAddRect (state->context, state->imageRect);
++ CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
++ CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
++ CGContextEOFillPath (state->context);
+ }
+- } else
+- CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
++ }
+ }
+
+
+@@ -1762,6 +1939,7 @@ _cairo_quartz_get_image (cairo_quartz_su
+ }
+
+ if (surface->imageSurfaceEquiv) {
++ CGContextFlush(surface->cgContext);
+ *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
+ return CAIRO_STATUS_SUCCESS;
+ }
+@@ -1773,6 +1951,7 @@ _cairo_quartz_get_image (cairo_quartz_su
+ CGColorSpaceRef colorspace;
+ unsigned int color_comps;
+
++ CGContextFlush(surface->cgContext);
+ imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
+
+ #ifdef USE_10_3_WORKAROUNDS
+@@ -1860,53 +2039,79 @@ _cairo_quartz_surface_finish (void *abst
+
+ surface->cgContext = NULL;
+
++ if (surface->bitmapContextImage) {
++ CGImageRelease (surface->bitmapContextImage);
++ surface->bitmapContextImage = NULL;
++ }
++
+ if (surface->imageSurfaceEquiv) {
++ if (surface->ownsData)
++ _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv);
+ cairo_surface_destroy (surface->imageSurfaceEquiv);
+ surface->imageSurfaceEquiv = NULL;
++ } else if (surface->imageData && surface->ownsData) {
++ free (surface->imageData);
+ }
+
+- if (surface->imageData) {
+- free (surface->imageData);
+- surface->imageData = NULL;
++ surface->imageData = NULL;
++
++ if (surface->cgLayer) {
++ CGLayerRelease (surface->cgLayer);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static cairo_status_t
+-_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
+- cairo_image_surface_t **image_out,
+- void **image_extra)
++_cairo_quartz_surface_acquire_image (void *abstract_surface,
++ cairo_image_surface_t **image_out,
++ void **image_extra)
+ {
+ cairo_int_status_t status;
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+- //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
+-
+- status = _cairo_quartz_get_image (surface, image_out);
+- if (status)
+- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+-
+ *image_extra = NULL;
+
+- return CAIRO_STATUS_SUCCESS;
+-}
++ /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */
+
+-static cairo_surface_t *
+-_cairo_quartz_surface_snapshot (void *abstract_surface)
+-{
+- cairo_int_status_t status;
+- cairo_quartz_surface_t *surface = abstract_surface;
+- cairo_image_surface_t *image;
++ status = _cairo_quartz_get_image (surface, image_out);
+
+- if (surface->imageSurfaceEquiv)
+- return NULL;
++ if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) {
++ /* copy the layer into a Quartz bitmap context so we can get the data */
++ cairo_surface_t *tmp =
++ cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32,
++ surface->extents.width,
++ surface->extents.height);
++ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp;
++
++ /* if surface creation failed, we won't have a Quartz surface here */
++ if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ &&
++ tmp_surface->imageSurfaceEquiv) {
++ CGContextSaveGState (tmp_surface->cgContext);
++ CGContextTranslateCTM (tmp_surface->cgContext, 0, surface->extents.height);
++ CGContextScaleCTM (tmp_surface->cgContext, 1, -1);
++ /* Note that according to Apple docs it's completely legal
++ * to draw a CGLayer to any CGContext, even one it wasn't
++ * created for.
++ */
++ CGContextDrawLayerAtPoint (tmp_surface->cgContext,
++ CGPointMake (0.0, 0.0),
++ surface->cgLayer);
++ CGContextRestoreGState (tmp_surface->cgContext);
++
++ *image_out = (cairo_image_surface_t*)
++ cairo_surface_reference(tmp_surface->imageSurfaceEquiv);
++ *image_extra = tmp;
++ status = CAIRO_STATUS_SUCCESS;
++ } else {
++ cairo_surface_destroy (tmp);
++ }
++ }
+
+- status = _cairo_quartz_get_image (surface, &image);
+- if (unlikely (status))
+- return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
++ if (status)
++ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+- return &image->base;
++ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static void
+@@ -1915,6 +2120,10 @@ _cairo_quartz_surface_release_source_ima
+ void *image_extra)
+ {
+ cairo_surface_destroy ((cairo_surface_t *) image);
++
++ if (image_extra) {
++ cairo_surface_destroy ((cairo_surface_t *) image_extra);
++ }
+ }
+
+
+@@ -1926,18 +2135,16 @@ _cairo_quartz_surface_acquire_dest_image
+ void **image_extra)
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+- cairo_int_status_t status;
+
+ ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
+
+- status = _cairo_quartz_get_image (surface, image_out);
+- if (status)
+- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+-
+ *image_rect = surface->extents;
+ *image_extra = NULL;
+
+- return CAIRO_STATUS_SUCCESS;
++ _cairo_quartz_surface_will_change (surface);
++
++ return _cairo_quartz_surface_acquire_image (abstract_surface,
++ image_out, image_extra);
+ }
+
+ static void
+@@ -1947,11 +2154,31 @@ _cairo_quartz_surface_release_dest_image
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+ {
+- //cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+-
+- //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
++ /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */
+
+ cairo_surface_destroy ((cairo_surface_t *) image);
++
++ if (image_extra) {
++ /* we need to write the data from the temp surface back to the layer */
++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
++ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra;
++ CGImageRef img;
++ cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img);
++ if (status) {
++ cairo_surface_destroy (&tmp_surface->base);
++ return;
++ }
++
++ CGContextSaveGState (surface->cgContext);
++ CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height);
++ CGContextScaleCTM (surface->cgContext, 1, -1);
++ CGContextDrawImage (surface->cgContext,
++ CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height),
++ img);
++ CGContextRestoreGState (surface->cgContext);
++
++ cairo_surface_destroy (&tmp_surface->base);
++ }
+ }
+
+ static cairo_surface_t *
+@@ -1960,10 +2187,13 @@ _cairo_quartz_surface_create_similar (vo
+ int width,
+ int height)
+ {
+- /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
+-
++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_format_t format;
+
++ if (surface->cgLayer)
++ return cairo_quartz_surface_create_cg_layer (abstract_surface, content,
++ width, height);
++
+ if (content == CAIRO_CONTENT_COLOR_ALPHA)
+ format = CAIRO_FORMAT_ARGB32;
+ else if (content == CAIRO_CONTENT_COLOR)
+@@ -2027,7 +2257,7 @@ _cairo_quartz_surface_clone_similar (voi
+ }
+ }
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image);
++ status = _cairo_surface_to_cgimage (src, &quartz_image);
+ if (status)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+@@ -2087,7 +2317,7 @@ _cairo_quartz_surface_paint_cg (void *ab
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+
+ ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
+
+@@ -2098,31 +2328,24 @@ _cairo_quartz_surface_paint_cg (void *ab
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+
+- action = _cairo_quartz_setup_source (surface, source, NULL);
+-
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+- surface->extents.y,
+- surface->extents.width,
+- surface->extents.height));
+- } else if (action == DO_SHADING) {
+- CGContextSaveGState (surface->cgContext);
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- CGContextRestoreGState (surface->cgContext);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- CGContextSaveGState (surface->cgContext);
+- _cairo_quartz_draw_image (surface, op, action);
+- CGContextRestoreGState (surface->cgContext);
+- } else if (action != DO_NOTHING) {
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
++ CGContextFillRect (state.context, CGRectMake(surface->extents.x,
++ surface->extents.y,
++ surface->extents.width,
++ surface->extents.height));
++ } else if (state.action == DO_SHADING) {
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+- _cairo_quartz_teardown_source (surface, source);
++ _cairo_quartz_teardown_state (&state);
+
+ ND((stderr, "-- paint\n"));
+ return rv;
+@@ -2186,7 +2409,7 @@ _cairo_quartz_surface_fill_cg (void *abs
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ CGPathRef path_for_unbounded = NULL;
+
+ ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
+@@ -2198,14 +2421,6 @@ _cairo_quartz_surface_fill_cg (void *abs
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+-
+- CGContextSaveGState (surface->cgContext);
+-
+- CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
+-
+ if (_cairo_quartz_source_needs_extents (source))
+ {
+ /* We don't need precise extents since these are only used to
+@@ -2213,46 +2428,47 @@ _cairo_quartz_surface_fill_cg (void *abs
+ object. */
+ cairo_rectangle_int_t path_extents;
+ _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
+- action = _cairo_quartz_setup_source (surface, source, &path_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
+ } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+- _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
++ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
++
++ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
+
+ if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
+- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
++ path_for_unbounded = CGContextCopyPathPtr (state.context);
+
+- if (action == DO_SOLID || action == DO_PATTERN) {
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextFillPath (surface->cgContext);
++ CGContextFillPath (state.context);
+ else
+- CGContextEOFillPath (surface->cgContext);
+- } else if (action == DO_SHADING) {
++ CGContextEOFillPath (state.context);
++ } else if (state.action == DO_SHADING) {
+
+ // we have to clip and then paint the shading; we can't fill
+ // with the shading
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextClip (surface->cgContext);
++ CGContextClip (state.context);
+ else
+- CGContextEOClip (surface->cgContext);
++ CGContextEOClip (state.context);
+
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextClip (surface->cgContext);
++ CGContextClip (state.context);
+ else
+- CGContextEOClip (surface->cgContext);
++ CGContextEOClip (state.context);
+
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action != DO_NOTHING) {
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+- _cairo_quartz_teardown_source (surface, source);
+-
+- CGContextRestoreGState (surface->cgContext);
++ _cairo_quartz_teardown_state (&state);
+
+ if (path_for_unbounded) {
+ unbounded_op_data_t ub;
+@@ -2319,7 +2535,7 @@ _cairo_quartz_surface_stroke_cg (void *a
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ CGAffineTransform origCTM, strokeTransform;
+ CGPathRef path_for_unbounded = NULL;
+
+@@ -2336,16 +2552,25 @@ _cairo_quartz_surface_stroke_cg (void *a
+ if (unlikely (rv))
+ return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+
++ if (_cairo_quartz_source_needs_extents (source))
++ {
++ cairo_rectangle_int_t path_extents;
++ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
++ } else {
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
++ }
++
+ // Turning antialiasing off used to cause misrendering with
+ // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
+ // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
+- CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
+- CGContextSetLineWidth (surface->cgContext, style->line_width);
+- CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
+- CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
+- CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
++ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
++ CGContextSetLineWidth (state.context, style->line_width);
++ CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
++ CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
++ CGContextSetMiterLimit (state.context, style->miter_limit);
+
+- origCTM = CGContextGetCTM (surface->cgContext);
++ origCTM = CGContextGetCTM (state.context);
+
+ if (style->dash && style->num_dashes) {
+ #define STATIC_DASH 32
+@@ -2368,72 +2593,62 @@ _cairo_quartz_surface_stroke_cg (void *a
+ if (fdash != sdash)
+ free (fdash);
+ } else
+- CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
++ CGContextSetLineDash (state.context, 0, NULL, 0);
+
+- CGContextSaveGState (surface->cgContext);
+
++ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
+
+- if (_cairo_quartz_source_needs_extents (source))
+- {
+- cairo_rectangle_int_t path_extents;
+- _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
+- action = _cairo_quartz_setup_source (surface, source, &path_extents);
+- } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
+- }
+-
+- _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
++ _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
++ CGContextConcatCTM (state.context, strokeTransform);
+
+ if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
+- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+-
+- _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+- CGContextConcatCTM (surface->cgContext, strokeTransform);
++ path_for_unbounded = CGContextCopyPathPtr (state.context);
+
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextStrokePath (surface->cgContext);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+- CGContextClip (surface->cgContext);
+-
+- CGContextSetCTM (surface->cgContext, origCTM);
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action == DO_SHADING) {
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+- CGContextClip (surface->cgContext);
+-
+- CGContextSetCTM (surface->cgContext, origCTM);
+-
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- } else if (action != DO_NOTHING) {
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
++ CGContextStrokePath (state.context);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
++ CGContextReplacePathWithStrokedPath (state.context);
++ CGContextClip (state.context);
++
++ CGContextSetCTM (state.context, origCTM);
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action == DO_SHADING) {
++ CGContextReplacePathWithStrokedPath (state.context);
++ CGContextClip (state.context);
++
++ CGContextSetCTM (state.context, origCTM);
++
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
++ goto BAIL;
+ }
+
+- _cairo_quartz_teardown_source (surface, source);
+-
+- CGContextRestoreGState (surface->cgContext);
+-
+ if (path_for_unbounded) {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_STROKE_FILL;
+ ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING;
+
+- CGContextBeginPath (surface->cgContext);
+- CGContextAddPath (surface->cgContext, path_for_unbounded);
++ CGContextBeginPath (state.context);
++ CGContextAddPath (state.context, path_for_unbounded);
+ CGPathRelease (path_for_unbounded);
+
+- CGContextSaveGState (surface->cgContext);
+- CGContextConcatCTM (surface->cgContext, strokeTransform);
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+- CGContextRestoreGState (surface->cgContext);
++ CGContextSaveGState (state.context);
++ CGContextConcatCTM (state.context, strokeTransform);
++ CGContextReplacePathWithStrokedPath (state.context);
++ CGContextRestoreGState (state.context);
+
+- ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (surface->cgContext);
++ ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (state.context);
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
+ CGPathRelease (ub.u.stroke_fill.cgPath);
+ }
+
++ BAIL:
++ _cairo_quartz_teardown_state (&state);
++
+ ND((stderr, "-- stroke\n"));
+ return rv;
+ }
+@@ -2490,18 +2705,22 @@ _cairo_quartz_surface_show_glyphs_cg (vo
+ CGGlyph glyphs_static[STATIC_BUF_SIZE];
+ CGSize cg_advances_static[STATIC_BUF_SIZE];
+ CGGlyph *cg_glyphs = &glyphs_static[0];
++ /* We'll use the cg_advances array for either advances or positions,
++ depending which API we're using to actually draw. The types involved
++ have the same size, so this is safe. */
+ CGSize *cg_advances = &cg_advances_static[0];
+
+ cairo_rectangle_int_t glyph_extents;
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ cairo_quartz_float_t xprev, yprev;
+ int i;
+ CGFontRef cgfref = NULL;
+
+ cairo_bool_t isClipping = FALSE;
+ cairo_bool_t didForceFontSmoothing = FALSE;
++ cairo_antialias_t effective_antialiasing;
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+@@ -2516,54 +2735,51 @@ _cairo_quartz_surface_show_glyphs_cg (vo
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+-
+- CGContextSaveGState (surface->cgContext);
+-
+ if (_cairo_quartz_source_needs_extents (source) &&
+ !_cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
+ &glyph_extents, NULL))
+ {
+- action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
+ } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
+- CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
++ CGContextSetTextDrawingMode (state.context, kCGTextFill);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_SHADING || state.action == DO_LAYER) {
++ CGContextSetTextDrawingMode (state.context, kCGTextClip);
+ isClipping = TRUE;
+ } else {
+- if (action != DO_NOTHING)
++ if (state.action != DO_NOTHING)
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto BAIL;
+ }
+
+ /* this doesn't addref */
+ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
+- CGContextSetFont (surface->cgContext, cgfref);
+- CGContextSetFontSize (surface->cgContext, 1.0);
++ CGContextSetFont (state.context, cgfref);
++ CGContextSetFontSize (state.context, 1.0);
++
++ effective_antialiasing = scaled_font->options.antialias;
+
+ switch (scaled_font->options.antialias) {
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+- CGContextSetShouldAntialias (surface->cgContext, TRUE);
+- CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
++ CGContextSetShouldAntialias (state.context, TRUE);
++ CGContextSetShouldSmoothFonts (state.context, TRUE);
+ if (CGContextSetAllowsFontSmoothingPtr &&
+- !CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
++ !CGContextGetAllowsFontSmoothingPtr (state.context))
+ {
+ didForceFontSmoothing = TRUE;
+- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
++ CGContextSetAllowsFontSmoothingPtr (state.context, TRUE);
+ }
+ break;
+ case CAIRO_ANTIALIAS_NONE:
+- CGContextSetShouldAntialias (surface->cgContext, FALSE);
++ CGContextSetShouldAntialias (state.context, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+- CGContextSetShouldAntialias (surface->cgContext, TRUE);
+- CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
++ CGContextSetShouldAntialias (state.context, TRUE);
++ CGContextSetShouldSmoothFonts (state.context, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_DEFAULT:
+ /* Don't do anything */
+@@ -2584,57 +2800,84 @@ _cairo_quartz_surface_show_glyphs_cg (vo
+ }
+ }
+
++ /* scale(1,-1) * scaled_font->scale */
+ textTransform = CGAffineTransformMake (scaled_font->scale.xx,
+ scaled_font->scale.yx,
+ -scaled_font->scale.xy,
+ -scaled_font->scale.yy,
+ 0, 0);
+- _cairo_quartz_cairo_matrix_to_quartz (&scaled_font->scale_inverse, &invTextTransform);
+
+- CGContextSetTextMatrix (surface->cgContext, CGAffineTransformIdentity);
++ /* scaled_font->scale_inverse * scale(1,-1) */
++ invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
++ -scaled_font->scale_inverse.yx,
++ scaled_font->scale_inverse.xy,
++ -scaled_font->scale_inverse.yy,
++ 0.0, 0.0);
+
+- /* Convert our glyph positions to glyph advances. We need n-1 advances,
+- * since the advance at index 0 is applied after glyph 0. */
+- xprev = glyphs[0].x;
+- yprev = glyphs[0].y;
+-
+- cg_glyphs[0] = glyphs[0].index;
+-
+- for (i = 1; i < num_glyphs; i++) {
+- cairo_quartz_float_t xf = glyphs[i].x;
+- cairo_quartz_float_t yf = glyphs[i].y;
+- cg_glyphs[i] = glyphs[i].index;
+- cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
+- xprev = xf;
+- yprev = yf;
+- }
++ CGContextSetTextMatrix (state.context, CGAffineTransformIdentity);
+
+ /* Translate to the first glyph's position before drawing */
+- ctm = CGContextGetCTM (surface->cgContext);
+- CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
+- CGContextConcatCTM (surface->cgContext, textTransform);
+-
+- CGContextShowGlyphsWithAdvances (surface->cgContext,
+- cg_glyphs,
+- cg_advances,
+- num_glyphs);
+-
+- CGContextSetCTM (surface->cgContext, ctm);
++ ctm = CGContextGetCTM (state.context);
++ CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
++ CGContextConcatCTM (state.context, textTransform);
++
++ if (CTFontDrawGlyphsPtr) {
++ /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use
++ * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap
++ * fonts like Apple Color Emoji will render properly.
++ * For this, we need to convert our glyph positions to Core Graphics's CGPoint.
++ * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */
++
++ CGPoint *cg_positions = (CGPoint*) cg_advances;
++ cairo_quartz_float_t origin_x = glyphs[0].x;
++ cairo_quartz_float_t origin_y = glyphs[0].y;
++
++ for (i = 0; i < num_glyphs; i++) {
++ CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y);
++ cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform);
++ cg_glyphs[i] = glyphs[i].index;
++ }
+
+- if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action == DO_SHADING) {
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
++ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
++ cg_glyphs, cg_positions, num_glyphs, state.context);
++ } else {
++ /* Convert our glyph positions to glyph advances. We need n-1 advances,
++ * since the advance at index 0 is applied after glyph 0. */
++ xprev = glyphs[0].x;
++ yprev = glyphs[0].y;
++
++ cg_glyphs[0] = glyphs[0].index;
++
++ for (i = 1; i < num_glyphs; i++) {
++ cairo_quartz_float_t xf = glyphs[i].x;
++ cairo_quartz_float_t yf = glyphs[i].y;
++ cg_glyphs[i] = glyphs[i].index;
++ cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
++ xprev = xf;
++ yprev = yf;
++ }
++
++ CGContextShowGlyphsWithAdvances (state.context,
++ cg_glyphs,
++ cg_advances,
++ num_glyphs);
++ }
++
++ CGContextSetCTM (state.context, ctm);
++
++ if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action == DO_SHADING) {
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
+ }
+
+ BAIL:
+- _cairo_quartz_teardown_source (surface, source);
+-
+ if (didForceFontSmoothing)
+- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
++ CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
+
+- CGContextRestoreGState (surface->cgContext);
++ _cairo_quartz_teardown_state (&state);
+
+ if (rv == CAIRO_STATUS_SUCCESS &&
+ cgfref &&
+@@ -2645,10 +2888,17 @@ BAIL:
+
+ ub.u.show_glyphs.isClipping = isClipping;
+ ub.u.show_glyphs.cg_glyphs = cg_glyphs;
+- ub.u.show_glyphs.cg_advances = cg_advances;
++ if (CTFontDrawGlyphsPtr) {
++ /* we're using Core Text API: the cg_advances array was
++ reused (above) for glyph positions */
++ CGPoint *cg_positions = (CGPoint*) cg_advances;
++ ub.u.show_glyphs.u.cg_positions = cg_positions;
++ } else {
++ ub.u.show_glyphs.u.cg_advances = cg_advances;
++ }
+ ub.u.show_glyphs.nglyphs = num_glyphs;
+ ub.u.show_glyphs.textTransform = textTransform;
+- ub.u.show_glyphs.font = cgfref;
++ ub.u.show_glyphs.scaled_font = scaled_font;
+ ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
+@@ -2717,7 +2967,7 @@ _cairo_quartz_surface_mask_with_surface
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ CGAffineTransform ctm, mask_matrix;
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
++ status = _cairo_surface_to_cgimage (pat_surf, &img);
+ if (status)
+ return status;
+ if (img == NULL) {
+@@ -2820,7 +3070,9 @@ _cairo_quartz_surface_mask_cg (void *abs
+ if (unlikely (rv))
+ return rv;
+
+- if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
++ /* Using CGContextSetAlpha to implement mask alpha doesn't work for all operators. */
++ if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
++ op == CAIRO_OPERATOR_OVER) {
+ /* This is easy; we just need to paint with the alpha. */
+ cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
+
+@@ -2834,8 +3086,11 @@ _cairo_quartz_surface_mask_cg (void *abs
+ /* If we have CGContextClipToMask, we can do more complex masks */
+ if (CGContextClipToMaskPtr) {
+ /* For these, we can skip creating a temporary surface, since we already have one */
+- if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
++ /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */
++ if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
++ mask->extend == CAIRO_EXTEND_NONE) {
+ return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip);
++ }
+
+ return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip);
+ }
+@@ -2920,13 +3175,24 @@ _cairo_quartz_surface_clipper_intersect_
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++static cairo_status_t
++_cairo_quartz_surface_mark_dirty_rectangle (void *abstract_surface,
++ int x, int y,
++ int width, int height)
++{
++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
++ _cairo_quartz_surface_will_change (surface);
++ return CAIRO_STATUS_SUCCESS;
++}
++
++
+ // XXXtodo implement show_page; need to figure out how to handle begin/end
+
+ static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
+ CAIRO_SURFACE_TYPE_QUARTZ,
+ _cairo_quartz_surface_create_similar,
+ _cairo_quartz_surface_finish,
+- _cairo_quartz_surface_acquire_source_image,
++ _cairo_quartz_surface_acquire_image,
+ _cairo_quartz_surface_release_source_image,
+ _cairo_quartz_surface_acquire_dest_image,
+ _cairo_quartz_surface_release_dest_image,
+@@ -2942,7 +3208,7 @@ static const struct _cairo_surface_backe
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+- NULL, /* mark_dirty_rectangle */
++ _cairo_quartz_surface_mark_dirty_rectangle,
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+@@ -2952,7 +3218,7 @@ static const struct _cairo_surface_backe
+ _cairo_quartz_surface_fill,
+ _cairo_quartz_surface_show_glyphs,
+
+- _cairo_quartz_surface_snapshot,
++ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL /* fill_stroke */
+ };
+@@ -3004,6 +3270,9 @@ _cairo_quartz_surface_create_internal (C
+
+ surface->imageData = NULL;
+ surface->imageSurfaceEquiv = NULL;
++ surface->bitmapContextImage = NULL;
++ surface->cgLayer = NULL;
++ surface->ownsData = TRUE;
+
+ return surface;
+ }
+@@ -3056,6 +3325,81 @@ cairo_quartz_surface_create_for_cg_conte
+ }
+
+ /**
++ * cairo_quartz_cglayer_surface_create_similar
++ * @surface: The returned surface can be efficiently drawn into this
++ * destination surface (if tiling is not used)."
++ * @content: the content type of the surface
++ * @width: width of the surface, in pixels
++ * @height: height of the surface, in pixels
++ *
++ * Creates a Quartz surface backed by a CGLayer, if the given surface
++ * is a Quartz surface; the CGLayer is created to match the surface's
++ * Quartz context. Otherwise just calls cairo_surface_create_similar.
++ * The returned surface can be efficiently blitted to the given surface,
++ * but tiling and 'extend' modes other than NONE are not so efficient.
++ *
++ * Return value: the newly created surface.
++ *
++ * Since: 1.10
++ **/
++cairo_surface_t *
++cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
++ cairo_content_t content,
++ unsigned int width,
++ unsigned int height)
++{
++ cairo_quartz_surface_t *surf;
++ CGLayerRef layer;
++ CGContextRef ctx;
++ CGContextRef cgContext;
++
++ cgContext = cairo_quartz_surface_get_cg_context (surface);
++ if (!cgContext)
++ return cairo_surface_create_similar (surface, content,
++ width, height);
++
++
++ if (!_cairo_quartz_verify_surface_size(width, height))
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
++
++ /* If we pass zero width or height into CGLayerCreateWithContext below,
++ * it will fail.
++ */
++ if (width == 0 || height == 0) {
++ return (cairo_surface_t*)
++ _cairo_quartz_surface_create_internal (NULL, content,
++ width, height);
++ }
++
++ layer = CGLayerCreateWithContext (cgContext,
++ CGSizeMake (width, height),
++ NULL);
++ if (!layer)
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
++
++ ctx = CGLayerGetContext (layer);
++ CGContextSetInterpolationQuality (ctx, kCGInterpolationNone);
++ /* Flip it when we draw into it, so that when we finally composite it
++ * to a flipped target, the directions match and Quartz will optimize
++ * the composition properly
++ */
++ CGContextTranslateCTM (ctx, 0, height);
++ CGContextScaleCTM (ctx, 1, -1);
++
++ CGContextRetain (ctx);
++ surf = _cairo_quartz_surface_create_internal (ctx, content,
++ width, height);
++ if (surf->base.status) {
++ CGLayerRelease (layer);
++ // create_internal will have set an error
++ return (cairo_surface_t*) surf;
++ }
++ surf->cgLayer = layer;
++
++ return (cairo_surface_t *) surf;
++}
++
++/**
+ * cairo_quartz_surface_create
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+@@ -3075,13 +3419,93 @@ cairo_quartz_surface_create (cairo_forma
+ unsigned int width,
+ unsigned int height)
+ {
++ int stride;
++ unsigned char *data;
++
++ if (!_cairo_quartz_verify_surface_size(width, height))
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
++
++ if (width == 0 || height == 0) {
++ return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
++ width, height);
++ }
++
++ if (format == CAIRO_FORMAT_ARGB32 ||
++ format == CAIRO_FORMAT_RGB24)
++ {
++ stride = width * 4;
++ } else if (format == CAIRO_FORMAT_A8) {
++ stride = width;
++ } else if (format == CAIRO_FORMAT_A1) {
++ /* I don't think we can usefully support this, as defined by
++ * cairo_format_t -- these are 1-bit pixels stored in 32-bit
++ * quantities.
++ */
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
++ } else {
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
++ }
++
++ /* The Apple docs say that for best performance, the stride and the data
++ * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
++ * so we don't have to anything special on allocation.
++ */
++ stride = (stride + 15) & ~15;
++
++ data = _cairo_malloc_ab (height, stride);
++ if (!data) {
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
++ }
++
++ /* zero the memory to match the image surface behaviour */
++ memset (data, 0, height * stride);
++
++ cairo_quartz_surface_t *surf;
++ surf = (cairo_quartz_surface_t *) cairo_quartz_surface_create_for_data
++ (data, format, width, height, stride);
++ if (surf->base.status) {
++ free (data);
++ return (cairo_surface_t *) surf;
++ }
++
++ // We created this data, so we can delete it.
++ surf->ownsData = TRUE;
++
++ return (cairo_surface_t *) surf;
++}
++
++/**
++ * cairo_quartz_surface_create_for_data
++ * @data: a pointer to a buffer supplied by the application in which
++ * to write contents. This pointer must be suitably aligned for any
++ * kind of variable, (for example, a pointer returned by malloc).
++ * @format: format of pixels in the surface to create
++ * @width: width of the surface, in pixels
++ * @height: height of the surface, in pixels
++ *
++ * Creates a Quartz surface backed by a CGBitmap. The surface is
++ * created using the Device RGB (or Device Gray, for A8) color space.
++ * All Cairo operations, including those that require software
++ * rendering, will succeed on this surface.
++ *
++ * Return value: the newly created surface.
++ *
++ * Since: 1.12
++ **/
++cairo_surface_t *
++cairo_quartz_surface_create_for_data (unsigned char *data,
++ cairo_format_t format,
++ unsigned int width,
++ unsigned int height,
++ unsigned int stride)
++{
+ cairo_quartz_surface_t *surf;
+ CGContextRef cgc;
+ CGColorSpaceRef cgColorspace;
+ CGBitmapInfo bitinfo;
+- void *imageData;
+- int stride;
++ void *imageData = data;
+ int bitsPerComponent;
++ unsigned int i;
+
+ // verify width and height of surface
+ if (!_cairo_quartz_verify_surface_size(width, height))
+@@ -3102,10 +3526,8 @@ cairo_quartz_surface_create (cairo_forma
+ else
+ bitinfo |= kCGImageAlphaNoneSkipFirst;
+ bitsPerComponent = 8;
+- stride = width * 4;
+ } else if (format == CAIRO_FORMAT_A8) {
+ cgColorspace = NULL;
+- stride = width;
+ bitinfo = kCGImageAlphaOnly;
+ bitsPerComponent = 8;
+ } else if (format == CAIRO_FORMAT_A1) {
+@@ -3118,21 +3540,6 @@ cairo_quartz_surface_create (cairo_forma
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+- /* The Apple docs say that for best performance, the stride and the data
+- * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
+- * so we don't have to anything special on allocation.
+- */
+- stride = (stride + 15) & ~15;
+-
+- imageData = _cairo_malloc_ab (height, stride);
+- if (!imageData) {
+- CGColorSpaceRelease (cgColorspace);
+- return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+- }
+-
+- /* zero the memory to match the image surface behaviour */
+- memset (imageData, 0, height * stride);
+-
+ cgc = CGBitmapContextCreate (imageData,
+ width,
+ height,
+@@ -3161,7 +3568,19 @@ cairo_quartz_surface_create (cairo_forma
+ }
+
+ surf->imageData = imageData;
+- surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
++
++ cairo_surface_t* tmpImageSurfaceEquiv =
++ cairo_image_surface_create_for_data (imageData, format,
++ width, height, stride);
++
++ if (cairo_surface_status (tmpImageSurfaceEquiv)) {
++ // Tried & failed to create an imageSurfaceEquiv!
++ cairo_surface_destroy (tmpImageSurfaceEquiv);
++ surf->imageSurfaceEquiv = NULL;
++ } else {
++ surf->imageSurfaceEquiv = tmpImageSurfaceEquiv;
++ surf->ownsData = FALSE;
++ }
+
+ return (cairo_surface_t *) surf;
+ }
+@@ -3193,6 +3612,74 @@ _cairo_surface_is_quartz (const cairo_su
+ return surface->backend == &cairo_quartz_surface_backend;
+ }
+
++CGContextRef
++cairo_quartz_get_cg_context_with_clip (cairo_t *cr)
++{
++
++ cairo_surface_t *surface = cr->gstate->target;
++ cairo_clip_t *clip = &cr->gstate->clip;
++ cairo_status_t status;
++
++ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
++
++ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
++ return NULL;
++
++ if (!clip->path) {
++ if (clip->all_clipped) {
++ /* Save the state before we set an empty clip rect so that
++ * our previous clip will be restored */
++
++ /* _cairo_surface_clipper_set_clip doesn't deal with
++ * clip->all_clipped because drawing is normally discarded earlier */
++ CGRect empty = {{0,0}, {0,0}};
++ CGContextClipToRect (quartz->cgContext, empty);
++ CGContextSaveGState (quartz->cgContext);
++
++ return quartz->cgContext;
++ }
++
++ /* an empty clip is represented by NULL */
++ clip = NULL;
++ }
++
++ status = _cairo_surface_clipper_set_clip (&quartz->clipper, clip);
++
++ /* Save the state after we set the clip so that it persists
++ * after we restore */
++ CGContextSaveGState (quartz->cgContext);
++
++ if (unlikely (status))
++ return NULL;
++
++ return quartz->cgContext;
++}
++
++void
++cairo_quartz_finish_cg_context_with_clip (cairo_t *cr)
++{
++ cairo_surface_t *surface = cr->gstate->target;
++
++ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
++
++ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
++ return;
++
++ CGContextRestoreGState (quartz->cgContext);
++}
++
++cairo_surface_t *
++cairo_quartz_surface_get_image (cairo_surface_t *surface)
++{
++ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *)surface;
++ cairo_image_surface_t *image;
++
++ if (_cairo_quartz_get_image(quartz, &image))
++ return NULL;
++
++ return (cairo_surface_t *)image;
++}
++
+ /* Debug stuff */
+
+ #ifdef QUARTZ_DEBUG
+--- a/src/cairo-quartz.h 2012-11-13 18:20:00.000000000 -0800
++++ b/src/cairo-quartz.h 2012-11-13 18:06:56.000000000 -0800
+@@ -50,6 +50,19 @@ cairo_quartz_surface_create (cairo_forma
+ unsigned int height);
+
+ cairo_public cairo_surface_t *
++cairo_quartz_surface_create_for_data (unsigned char *data,
++ cairo_format_t format,
++ unsigned int width,
++ unsigned int height,
++ unsigned int stride);
++
++cairo_public cairo_surface_t *
++cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
++ cairo_content_t content,
++ unsigned int width,
++ unsigned int height);
++
++cairo_public cairo_surface_t *
+ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
+ unsigned int width,
+ unsigned int height);
+@@ -57,6 +70,15 @@ cairo_quartz_surface_create_for_cg_conte
+ cairo_public CGContextRef
+ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+
++cairo_public CGContextRef
++cairo_quartz_get_cg_context_with_clip (cairo_t *cr);
++
++cairo_public void
++cairo_quartz_finish_cg_context_with_clip (cairo_t *cr);
++
++cairo_public cairo_surface_t *
++cairo_quartz_surface_get_image (cairo_surface_t *surface);
++
+ #if CAIRO_HAS_QUARTZ_FONT
+
+ /*
+@@ -66,8 +88,10 @@ cairo_quartz_surface_get_cg_context (cai
+ cairo_public cairo_font_face_t *
+ cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
+
++#ifndef __LP64__
+ cairo_public cairo_font_face_t *
+ cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
++#endif
+
+ #endif /* CAIRO_HAS_QUARTZ_FONT */
\ No newline at end of file
--- /dev/null
+From 9d460070fca2c0a61aac60ba7cad6f9a6af82309 Mon Sep 17 00:00:00 2001
+From: Andrea Canciani <ranma42@gmail.com>
+Date: Tue, 9 Dec 2014 16:13:00 +0100
+Subject: [PATCH] quartz: Remove call to obsolete CGFontGetGlyphPath
+
+CGFontGetGlyphPath was not public and is not available anymore on
+modern OSX/iOS systems. The same functionality is available through
+the CoreText API since OSX 10.5.
+
+Based on a patch by Simon Cozens.
+
+Fixes https://bugs.freedesktop.org/show_bug.cgi?id=84324
+---
+ src/cairo-quartz-font.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c
+index e6a379a..02f3426 100644
+--- a/src/cairo-quartz-font.c
++++ b/src/cairo-quartz-font.c
+@@ -81,9 +81,6 @@ static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const
+ static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+
+-/* Not public in the least bit */
+-static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL;
+-
+ /* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */
+ typedef struct {
+ int ascent;
+@@ -127,7 +124,6 @@ quartz_font_ensure_symbols(void)
+ /* These have the same name in 10.4 and 10.5 */
+ CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm");
+ CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances");
+- CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath");
+
+ CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics");
+ CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent");
+@@ -144,7 +140,6 @@ quartz_font_ensure_symbols(void)
+ CGFontGetGlyphsForUnicharsPtr &&
+ CGFontGetUnitsPerEmPtr &&
+ CGFontGetGlyphAdvancesPtr &&
+- CGFontGetGlyphPathPtr &&
+ (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
+ _cairo_quartz_font_symbols_present = TRUE;
+
+@@ -550,6 +545,7 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
+ CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
+ CGAffineTransform textMatrix;
+ CGPathRef glyphPath;
++ CTFontRef ctFont;
+ cairo_path_fixed_t *path;
+
+ if (glyph == INVALID_GLYPH) {
+@@ -564,7 +560,9 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
+ -font->base.scale.yy,
+ 0, 0);
+
+- glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph);
++ ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 1.0, NULL, NULL);
++ glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix);
++ CFRelease (ctFont);
+ if (!glyphPath)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+--
+1.9.3 (Apple Git-50)
--- /dev/null
+diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c
+index a9bbbdc..48eb071 100644
+--- a/src/cairo-quartz-font.c
++++ b/src/cairo-quartz-font.c
+@@ -95,6 +95,10 @@ static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL;
+ static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
+ static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
+
++/* CTFontCreateWithGraphicsFont is not public until 10.5. */
++typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
++static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
++
+ /* Not public anymore in 64-bits nor in 10.7 */
+ static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
+
+@@ -137,6 +141,8 @@ quartz_font_ensure_symbols(void)
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+
++ CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
++
+ FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
+
+ if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
+@@ -162,6 +168,7 @@ struct _cairo_quartz_font_face {
+ cairo_font_face_t base;
+
+ CGFontRef cgFont;
++ CTFontRef ctFont;
+ };
+
+ /*
+@@ -246,6 +253,10 @@ _cairo_quartz_font_face_destroy (void *abstract_face)
+ {
+ cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
+
++ if (font_face->ctFont) {
++ CFRelease (font_face->ctFont);
++ }
++
+ CGFontRelease (font_face->cgFont);
+ }
+
+@@ -370,6 +381,12 @@ cairo_quartz_font_face_create_for_cgfont (CGFontRef font)
+
+ font_face->cgFont = CGFontRetain (font);
+
++ if (CTFontCreateWithGraphicsFontPtr) {
++ font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL);
++ } else {
++ font_face->ctFont = NULL;
++ }
++
+ _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
+
+ return &font_face->base;
+@@ -812,6 +829,14 @@ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
+ return ffont->cgFont;
+ }
+
++CTFontRef
++_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
++{
++ cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
++
++ return ffont->ctFont;
++}
++
+ /*
+ * compat with old ATSUI backend
+ */
+diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h
+index f841a49..3c1e5aa 100644
+--- a/src/cairo-quartz-private.h
++++ b/src/cairo-quartz-private.h
+@@ -57,6 +57,9 @@ typedef enum {
+ DO_TILED_IMAGE
+ } cairo_quartz_action_t;
+
++/* define CTFontRef for pre-10.5 SDKs */
++typedef const struct __CTFont *CTFontRef;
++
+ typedef struct cairo_quartz_surface {
+ cairo_surface_t base;
+
+@@ -97,6 +100,9 @@ CairoQuartzCreateCGImage (cairo_format_t format,
+ cairo_private CGFontRef
+ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
+
++CTFontRef
++_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
++
+ #else
+
+ # error Cairo was not compiled with support for the quartz backend
+diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
+index 1e2bbec..417255c 100644
+--- a/src/cairo-quartz-surface.c
++++ b/src/cairo-quartz-surface.c
+@@ -123,6 +123,9 @@ static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+ static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
+ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+
++/* CTFontDrawGlyphs is not available until 10.7 */
++static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
++
+ static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
+
+ /*
+@@ -155,6 +158,8 @@ static void quartz_ensure_symbols (void)
+ CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+
++ CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
++
+ _cairo_quartz_symbol_lookup_done = TRUE;
+ }
+
+@@ -2026,30 +2031,50 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
+ CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
+ CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
+
+- /* Convert our glyph positions to glyph advances. We need n-1 advances,
+- * since the advance at index 0 is applied after glyph 0. */
+- xprev = glyphs[0].x;
+- yprev = glyphs[0].y;
+-
+- cg_glyphs[0] = glyphs[0].index;
+-
+- for (i = 1; i < num_glyphs; i++) {
+- cairo_quartz_float_t xf = glyphs[i].x;
+- cairo_quartz_float_t yf = glyphs[i].y;
+- cg_glyphs[i] = glyphs[i].index;
+- cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
+- xprev = xf;
+- yprev = yf;
+- }
+-
+ /* Translate to the first glyph's position before drawing */
+ CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
+ CGContextConcatCTM (state.cgMaskContext, textTransform);
+
+- CGContextShowGlyphsWithAdvances (state.cgMaskContext,
+- cg_glyphs,
+- cg_advances,
+- num_glyphs);
++ if (CTFontDrawGlyphsPtr) {
++ /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use
++ * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap
++ * fonts like Apple Color Emoji will render properly.
++ * For this, we need to convert our glyph positions to Core Graphics's CGPoint.
++ * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */
++
++ CGPoint *cg_positions = (CGPoint*) cg_advances;
++ cairo_quartz_float_t origin_x = glyphs[0].x;
++ cairo_quartz_float_t origin_y = glyphs[0].y;
++
++ for (i = 0; i < num_glyphs; i++) {
++ CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y);
++ cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform);
++ cg_glyphs[i] = glyphs[i].index;
++ }
++
++ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
++ cg_glyphs, cg_positions, num_glyphs, state.cgMaskContext);
++ } else {
++ /* Convert our glyph positions to glyph advances. We need n-1 advances,
++ * since the advance at index 0 is applied after glyph 0. */
++ xprev = glyphs[0].x;
++ yprev = glyphs[0].y;
++
++ cg_glyphs[0] = glyphs[0].index;
++
++ for (i = 1; i < num_glyphs; i++) {
++ cairo_quartz_float_t xf = glyphs[i].x;
++ cairo_quartz_float_t yf = glyphs[i].y;
++ cg_glyphs[i] = glyphs[i].index;
++ cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
++ xprev = xf;
++ yprev = yf;
++ }
++ CGContextShowGlyphsWithAdvances (state.cgMaskContext,
++ cg_glyphs,
++ cg_advances,
++ num_glyphs);
++ }
+
+ CGContextConcatCTM (state.cgMaskContext, invTextTransform);
+ CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
--- /dev/null
+--- a/src/cairo-quartz-surface.c 2013-03-28 19:19:15.000000000 -0400
++++ b/src/cairo-quartz-surface.c 2013-03-28 19:19:28.000000000 -0400
+@@ -1379,7 +1379,6 @@
+ CGContextDrawLayerInRect (surface->cgContext,
+ state->clipRect,
+ state->layer);
+- CGContextRelease (state->cgDrawContext);
+ CGLayerRelease (state->layer);
+ }
--- /dev/null
+#!/bin/sh
+for f in *.patch; do grep $f ../*.py > /dev/null || echo $f; done
+for f in */*.patch; do grep $f ../*.py > /dev/null || echo $f; done
--- /dev/null
+From b02965711a387b015a6af4b9d26bbdabf10fc5b4 Mon Sep 17 00:00:00 2001
+From: Mikayla Hutchinson <m.j.hutchinson@gmail.com>
+Date: Mon, 1 Aug 2016 13:47:41 -0400
+Subject: [PATCH 1/2] Remove xbuild workarounds
+
+They do not work Mono 4.6+ where the underlying bug is fixed
+---
+ src/FSharpSource.targets | 8 --------
+ 1 file changed, 8 deletions(-)
+
+diff --git a/src/FSharpSource.targets b/src/FSharpSource.targets
+index eb57269..ea3656f 100755
+--- a/src/FSharpSource.targets
++++ b/src/FSharpSource.targets
+@@ -306,8 +306,6 @@ Some other NuGET monikers to support in the future, see http://docs.nuget.org/do
+ <DefineConstants>$(DefineConstants);PUT_TYPE_PROVIDERS_IN_FSCORE</DefineConstants>
+ <DefineConstants>$(DefineConstants);QUERIES_IN_FSLIB</DefineConstants>
+
+- <!-- An explicit search path seems to be needed on Mono 3.4.0 otherwise the reference assemblies for the profile aren't found -->
+- <AssemblySearchPaths Condition="Exists('$(MSBuildExtensionsPath32)\..\xbuild-frameworks\.NETPortable\v4.0\mscorlib.dll')">$(MSBuildExtensionsPath32)\..\xbuild-frameworks\.NETPortable\v4.0</AssemblySearchPaths>
+ </PropertyGroup>
+
+ <!-- Target Portable -->
+@@ -348,8 +346,6 @@ Some other NuGET monikers to support in the future, see http://docs.nuget.org/do
+ <TargetProfile>netcore</TargetProfile>
+ <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+- <!-- An explicit search path seems to be needed on Mono 3.4.0 otherwise the reference assemblies for the profile aren't found -->
+- <AssemblySearchPaths Condition="Exists('$(MSBuildExtensionsPath32)\..\xbuild-frameworks\.NETPortable\v4.5\System.Runtime.dll')">$(MSBuildExtensionsPath32)\..\xbuild-frameworks\.NETPortable\v4.5</AssemblySearchPaths>
+ <OtherFlags>$(OtherFlags) --targetprofile:netcore</OtherFlags>
+ </PropertyGroup>
+
+@@ -389,8 +385,6 @@ Some other NuGET monikers to support in the future, see http://docs.nuget.org/do
+ <TargetProfile>netcore</TargetProfile>
+ <TargetFrameworkProfile>Profile78</TargetFrameworkProfile>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+- <!-- An explicit search path seems to be needed on Mono 3.4.0 otherwise the reference assemblies for the profile aren't found -->
+- <AssemblySearchPaths Condition="Exists('$(MSBuildExtensionsPath32)\..\xbuild-frameworks\.NETPortable\v4.5\System.Runtime.dll')">$(MSBuildExtensionsPath32)\..\xbuild-frameworks\.NETPortable\v4.5</AssemblySearchPaths>
+ <OtherFlags>$(OtherFlags) --targetprofile:netcore</OtherFlags>
+ </PropertyGroup>
+
+@@ -430,8 +424,6 @@ Some other NuGET monikers to support in the future, see http://docs.nuget.org/do
+ <TargetProfile>netcore</TargetProfile>
+ <TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+- <!-- An explicit search path seems to be needed on Mono 3.4.0 otherwise the reference assemblies for the profile aren't found -->
+- <AssemblySearchPaths Condition="Exists('$(MSBuildExtensionsPath32)\..\xbuild-frameworks\.NETPortable\v4.5\System.Runtime.dll')">$(MSBuildExtensionsPath32)\..\xbuild-frameworks\.NETPortable\v4.5</AssemblySearchPaths>
+ <OtherFlags>$(OtherFlags) --targetprofile:netcore</OtherFlags>
+ </PropertyGroup>
+
+--
+2.7.4 (Apple Git-66)
+
+
+From e57cb16ee7927bb31b2c9b63e1b2e394bf9f9932 Mon Sep 17 00:00:00 2001
+From: Mikayla Hutchinson <m.j.hutchinson@gmail.com>
+Date: Mon, 1 Aug 2016 13:48:23 -0400
+Subject: [PATCH 2/2] Remove dead code
+
+Remove Mono workarounds for missing files. Mono has been
+shipping the files for some time now.
+---
+ .../FSharp.Build/Microsoft.Portable.FSharp.Targets | 52 ----------------------
+ 1 file changed, 52 deletions(-)
+
+diff --git a/src/fsharp/FSharp.Build/Microsoft.Portable.FSharp.Targets b/src/fsharp/FSharp.Build/Microsoft.Portable.FSharp.Targets
+index 8c99ab6..ee5f9b1 100644
+--- a/src/fsharp/FSharp.Build/Microsoft.Portable.FSharp.Targets
++++ b/src/fsharp/FSharp.Build/Microsoft.Portable.FSharp.Targets
+@@ -24,60 +24,8 @@ Copyright (C) Microsoft Corporation. All rights reserved.
+ Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\v4.0\Microsoft.Portable.Common.targets') AND
+ !Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\Microsoft.Portable.Core.props')"/>
+
+-
+- <!-- Try to do it ourselves - Explicitly include contents of Microsoft.Portable.Core.props + Microsoft.FSharp.Targets + Microsoft.Portable.Core.targets -->
+- <!-- START MONO 3.2.7 WORKAROUND PART 1 -->
+- <PropertyGroup
+- Condition="(!Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\v4.0\Microsoft.Portable.Common.targets')) AND
+- (!Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\Microsoft.Portable.Core.props'))">
+- <AvailablePlatforms>Any CPU</AvailablePlatforms>
+-
+- <TargetPlatformIdentifier>Portable</TargetPlatformIdentifier>
+- <TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
+- <TargetFrameworkMonikerDisplayName>.NET Portable Subset</TargetFrameworkMonikerDisplayName>
+-
+- <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
+- <NoStdLib>true</NoStdLib>
+-
+- <ImplicitlyExpandTargetFramework Condition="'$(ImplicitlyExpandTargetFramework)' == '' ">true</ImplicitlyExpandTargetFramework>
+- </PropertyGroup>
+- <!-- END MONO 3.2.7 WORKAROUND PART 1 -->
+-
+-
+ <Import Project="$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.1\Framework\v4.0\Microsoft.FSharp.Targets" />
+
+-
+- <!-- START MONO 3.2.7 WORKAROUND PART 2 -->
+- <PropertyGroup
+- Condition="(!Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\v4.0\Microsoft.Portable.Common.targets')) AND
+- (!Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\Microsoft.Portable.Core.props'))" >
+- <ResolveReferencesDependsOn>
+- $(ResolveReferencesDependsOn);
+- ImplicitlyExpandTargetFramework;
+- </ResolveReferencesDependsOn>
+-
+- <ImplicitlyExpandTargetFrameworkDependsOn>
+- $(ImplicitlyExpandTargetFrameworkDependsOn);
+- GetReferenceAssemblyPaths
+- </ImplicitlyExpandTargetFrameworkDependsOn>
+- </PropertyGroup>
+-
+- <Target Name="ImplicitlyExpandTargetFramework"
+- DependsOnTargets="$(ImplicitlyExpandTargetFrameworkDependsOn)"
+- Condition="(!Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\v4.0\Microsoft.Portable.Common.targets')) AND
+- (!Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\Microsoft.Portable.Core.props'))" >
+-
+- <ItemGroup>
+- <ReferenceAssemblyPaths Include="$(_TargetFrameworkDirectories)"/>
+- <ReferencePath Include="%(ReferenceAssemblyPaths.Identity)\*.dll">
+- <CopyLocal>false</CopyLocal>
+- <ResolvedFrom>ImplicitlyExpandTargetFramework</ResolvedFrom>
+- <IsSystemReference>True</IsSystemReference>
+- </ReferencePath>
+- </ItemGroup>
+- </Target>
+- <!-- END MONO 3.2.7 WORKAROUND PART 2 -->
+-
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\Microsoft.Portable.Core.targets"
+ Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\Portable\Microsoft.Portable.Core.props')"/>
+
+--
+2.7.4 (Apple Git-66)
--- /dev/null
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index a8800f7..ec6a893 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -292,10 +292,19 @@ get_keyboard_modifiers_from_ns_flags (NSUInteger nsflags)
+ modifiers |= GDK_SHIFT_MASK;
+ if (nsflags & NSControlKeyMask)
+ modifiers |= GDK_CONTROL_MASK;
+- if (nsflags & NSAlternateKeyMask)
+- modifiers |= GDK_MOD1_MASK;
+- if (nsflags & NSCommandKeyMask)
+- modifiers |= GDK_MOD2_MASK;
++
++ if (gdk_quartz_get_fix_modifiers ())
++ {
++ if (nsflags & NSAlternateKeyMask)
++ modifiers |= GDK_MOD1_MASK;
++ if (nsflags & NSCommandKeyMask)
++ modifiers |= GDK_MOD2_MASK;
++ }
++ else
++ {
++ if (nsflags & NSCommandKeyMask)
++ modifiers |= GDK_MOD1_MASK;
++ }
+
+ return modifiers;
+ }
+@@ -930,7 +939,7 @@ fill_key_event (GdkWindow *window,
+ {
+ case GDK_Meta_R:
+ case GDK_Meta_L:
+- mask = GDK_MOD2_MASK;
++ mask = gdk_quartz_get_fix_modifiers () ? GDK_MOD2_MASK : GDK_MOD1_MASK;
+ break;
+ case GDK_Shift_R:
+ case GDK_Shift_L:
+@@ -941,7 +950,7 @@ fill_key_event (GdkWindow *window,
+ break;
+ case GDK_Alt_R:
+ case GDK_Alt_L:
+- mask = GDK_MOD1_MASK;
++ mask = gdk_quartz_get_fix_modifiers () ? GDK_MOD1_MASK : GDK_MOD2_MASK;
+ break;
+ case GDK_Control_R:
+ case GDK_Control_L:
+@@ -1089,9 +1098,9 @@ _gdk_quartz_events_get_current_keyboard_modifiers (void)
+ if (carbon_modifiers & controlKey)
+ modifiers |= GDK_CONTROL_MASK;
+ if (carbon_modifiers & optionKey)
+- modifiers |= GDK_MOD1_MASK;
++ modifiers |= (gdk_quartz_get_fix_modifiers () ? GDK_MOD1_MASK : GDK_MOD2_MASK);
+ if (carbon_modifiers & cmdKey)
+- modifiers |= GDK_MOD2_MASK;
++ modifiers |= (gdk_quartz_get_fix_modifiers () ? GDK_MOD2_MASK : GDK_MOD1_MASK);
+
+ return modifiers;
+ }
+diff --git a/gdk/quartz/gdkglobals-quartz.c b/gdk/quartz/gdkglobals-quartz.c
+index 53c6d5e..31dbab1 100644
+--- a/gdk/quartz/gdkglobals-quartz.c
++++ b/gdk/quartz/gdkglobals-quartz.c
+@@ -41,3 +41,17 @@ gdk_quartz_osx_version (void)
+ else
+ return minor;
+ }
++
++static gboolean fix_modifiers = FALSE;
++
++void
++gdk_quartz_set_fix_modifiers (gboolean fix)
++{
++ fix_modifiers = fix;
++}
++
++gboolean
++gdk_quartz_get_fix_modifiers (void)
++{
++ return fix_modifiers;
++}
+diff --git a/gdk/quartz/gdkkeys-quartz.c b/gdk/quartz/gdkkeys-quartz.c
+index 19a20f5..c7ceec6 100644
+--- a/gdk/quartz/gdkkeys-quartz.c
++++ b/gdk/quartz/gdkkeys-quartz.c
+@@ -693,11 +693,11 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap,
+ for (bit = GDK_SHIFT_MASK; bit < GDK_BUTTON1_MASK; bit <<= 1)
+ {
+ if (translate_keysym (hardware_keycode,
+- (bit == GDK_MOD1_MASK) ? 0 : group,
++ (bit == (gdk_quartz_get_fix_modifiers () ? GDK_MOD1_MASK : GDK_MOD2_MASK)) ? 0 : group,
+ state & ~bit,
+ NULL, NULL) !=
+ translate_keysym (hardware_keycode,
+- (bit == GDK_MOD1_MASK) ? 1 : group,
++ (bit == (gdk_quartz_get_fix_modifiers () ? GDK_MOD1_MASK : GDK_MOD2_MASK)) ? 1 : group,
+ state | bit,
+ NULL, NULL))
+ tmp_modifiers |= bit;
+@@ -718,16 +718,32 @@ void
+ gdk_keymap_add_virtual_modifiers (GdkKeymap *keymap,
+ GdkModifierType *state)
+ {
+- if (*state & GDK_MOD2_MASK)
+- *state |= GDK_META_MASK;
++ if (gdk_quartz_get_fix_modifiers ())
++ {
++ if (*state & GDK_MOD2_MASK)
++ *state |= GDK_META_MASK;
++ }
++ else
++ {
++ if (*state & GDK_MOD1_MASK)
++ *state |= GDK_META_MASK;
++ }
+ }
+
+ gboolean
+ gdk_keymap_map_virtual_modifiers (GdkKeymap *keymap,
+ GdkModifierType *state)
+ {
+- if (*state & GDK_META_MASK)
+- *state |= GDK_MOD2_MASK;
++ if (gdk_quartz_get_fix_modifiers ())
++ {
++ if (*state & GDK_META_MASK)
++ *state |= GDK_MOD2_MASK;
++ }
++ else
++ {
++ if (*state & GDK_META_MASK)
++ *state |= GDK_MOD1_MASK;
++ }
+
+ return TRUE;
+ }
+diff --git a/gdk/quartz/gdkquartz.h b/gdk/quartz/gdkquartz.h
+index 742d651..ed0ba35 100644
+--- a/gdk/quartz/gdkquartz.h
++++ b/gdk/quartz/gdkquartz.h
+@@ -58,6 +58,9 @@ id gdk_quartz_drag_context_get_dragging_info_libgtk_only (GdkDragContext
+ NSEvent *gdk_quartz_event_get_nsevent (GdkEvent *event);
+ GdkOSXVersion gdk_quartz_osx_version (void);
+
++void gdk_quartz_set_fix_modifiers (gboolean fix);
++gboolean gdk_quartz_get_fix_modifiers (void);
++
+ G_END_DECLS
+
+ #endif /* __GDK_QUARTZ_H__ */
+diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
+index 6386c32..aa1cc74 100644
+--- a/gtk/gtkprivate.h
++++ b/gtk/gtkprivate.h
+@@ -122,7 +122,7 @@ gboolean _gtk_fnmatch (const char *pattern,
+ #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)
++#define GTK_NO_TEXT_INPUT_MOD_MASK (gdk_quartz_get_fix_modifiers () ? (GDK_MOD2_MASK | GDK_CONTROL_MASK) : (GDK_MOD1_MASK | GDK_CONTROL_MASK))
+ #endif
+
+ #ifndef GDK_WINDOWING_QUARTZ
+@@ -130,13 +130,13 @@ gboolean _gtk_fnmatch (const char *pattern,
+ #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
++#define GTK_MODIFY_SELECTION_MOD_MASK (gdk_quartz_get_fix_modifiers () ? GDK_MOD2_MASK : GDK_MOD1_MASK)
+ #endif
+
+ #ifndef GDK_WINDOWING_QUARTZ
+ #define GTK_TOGGLE_GROUP_MOD_MASK 0
+ #else
+-#define GTK_TOGGLE_GROUP_MOD_MASK GDK_MOD1_MASK
++#define GTK_TOGGLE_GROUP_MOD_MASK (gdk_quartz_get_fix_modifiers () ? GDK_MOD1_MASK : 0)
+ #endif
+
+ gboolean _gtk_button_event_triggers_context_menu (GdkEventButton *event);
--- /dev/null
+--- a/gettext-tools/Makefile.in.orig 2010-06-03 16:38:55.000000000 -0500
++++ b/gettext-tools/Makefile.in 2010-06-04 01:16:07.000000000 -0500
+@@ -1248,7 +1248,7 @@
+ top_srcdir = @top_srcdir@
+ AUTOMAKE_OPTIONS = 1.5 gnu no-dependencies
+ ACLOCAL_AMFLAGS = -I m4 -I ../gettext-runtime/m4 -I ../m4 -I gnulib-m4 -I libgrep/gnulib-m4 -I libgettextpo/gnulib-m4
+-SUBDIRS = doc intl gnulib-lib libgrep src libgettextpo po projects styles misc man m4 tests gnulib-tests examples
++SUBDIRS = doc intl gnulib-lib libgrep src libgettextpo po projects styles misc man m4 tests gnulib-tests
+
+ # Allow users to use "gnulib-tool --update".
--- /dev/null
+From 5dfd206b09f91cba45fa8e2b66e1b57aafe30868 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Mon, 8 Jul 2013 12:02:00 +0200
+Subject: [PATCH] Make g_main_context_iterate resilient to recursion in poll
+
+On OS X, main loop recursion may happen during the call the poll.
+As a result, the allocated poll array may be re-allocated (note that
+it is always enlarged, never shrunk). By always using cached_poll_array
+after the poll function, reads from bad memory are avoided.
+---
+ glib/gmain.c | 28 +++++++++++++++-------------
+ 1 file changed, 15 insertions(+), 13 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index 077a935..529f2b6 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -3065,8 +3065,7 @@ g_main_context_iterate (GMainContext *context,
+ gint max_priority;
+ gint timeout;
+ gboolean some_ready;
+- gint nfds, allocated_nfds;
+- GPollFD *fds = NULL;
++ gint nfds;
+
+ UNLOCK_CONTEXT (context);
+
+@@ -3095,29 +3094,32 @@ g_main_context_iterate (GMainContext *context,
+ context->cached_poll_array = g_new (GPollFD, context->n_poll_records);
+ }
+
+- allocated_nfds = context->cached_poll_array_size;
+- fds = context->cached_poll_array;
+-
+ UNLOCK_CONTEXT (context);
+
+ g_main_context_prepare (context, &max_priority);
+
+- while ((nfds = g_main_context_query (context, max_priority, &timeout, fds,
+- allocated_nfds)) > allocated_nfds)
++ while ((nfds = g_main_context_query (context, max_priority, &timeout,
++ context->cached_poll_array,
++ context->cached_poll_array_size))
++ > context->cached_poll_array_size)
+ {
+ LOCK_CONTEXT (context);
+- g_free (fds);
+- context->cached_poll_array_size = allocated_nfds = nfds;
+- context->cached_poll_array = fds = g_new (GPollFD, nfds);
++ g_free (context->cached_poll_array);
++ context->cached_poll_array_size = nfds;
++ context->cached_poll_array = g_new (GPollFD, nfds);
+ UNLOCK_CONTEXT (context);
+ }
+
+ if (!block)
+ timeout = 0;
+
+- g_main_context_poll (context, timeout, max_priority, fds, nfds);
+-
+- some_ready = g_main_context_check (context, max_priority, fds, nfds);
++ g_main_context_poll (context, timeout, max_priority,
++ context->cached_poll_array,
++ nfds);
++
++ some_ready = g_main_context_check (context, max_priority,
++ context->cached_poll_array,
++ nfds);
+
+ if (dispatch)
+ g_main_context_dispatch (context);
+--
+1.7.10
--- /dev/null
+/ AC_APPLE_UNIVERSAL_BUILD /c
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+.
+/ G_VA_COPY_AS_ARRAY /c
+#ifdef __LP64__
+#define G_VA_COPY_AS_ARRAY 1
+#else
+/* #undef G_VA_COPY_AS_ARRAY */
+#endif
+.
+/ SIZEOF_LONG /c
+#ifdef __LP64__
+#define SIZEOF_LONG 8
+#else
+#define SIZEOF_LONG 4
+#endif
+.
+/ SIZEOF_SIZE_T /c
+#ifdef __LP64__
+#define SIZEOF_SIZE_T 8
+#else
+#define SIZEOF_SIZE_T 4
+#endif
+.
+/ SIZEOF_VOID_P /c
+#ifdef __LP64__
+#define SIZEOF_VOID_P 8
+#else
+#define SIZEOF_VOID_P 4
+#endif
+.
+w
--- /dev/null
+--- configure.orig 2012-10-15 19:29:14.000000000 -0500
++++ configure 2012-10-22 17:29:31.000000000 -0500
+@@ -5602,7 +5602,7 @@
+ fi
+
+ GLIB_RUNTIME_LIBDIR="$with_runtime_libdir"
+-ABS_GLIB_RUNTIME_LIBDIR="`readlink -f $libdir/$with_runtime_libdir`"
++ABS_GLIB_RUNTIME_LIBDIR="`readlink $libdir/$with_runtime_libdir`"
+
+
+ if test "x$with_runtime_libdir" != "x"; then
+@@ -30649,10 +30649,10 @@
+ *) glib_vacopy=''
+ esac
+
+-if test x$glib_cv_va_val_copy = xno; then
+ glib_vacopy="\$glib_vacopy
+-#define G_VA_COPY_AS_ARRAY 1"
+-fi
++#ifdef __LP64__
++#define G_VA_COPY_AS_ARRAY 1
++#endif"
+
+ if test x$glib_cv_hasinline = xyes; then
+ glib_inline='#define G_HAVE_INLINE 1'
+@@ -32411,18 +32411,32 @@
+ cat >>$outfile <<_______EOF
+ #define G_HAVE_GINT64 1 /* deprecated, always true */
+
+-${glib_extension}typedef signed $gint64 gint64;
+-${glib_extension}typedef unsigned $gint64 guint64;
++#ifdef __LP64__
++${glib_extension}typedef signed long gint64;
++${glib_extension}typedef unsigned long guint64;
+
+-#define G_GINT64_CONSTANT(val) $gint64_constant
+-#define G_GUINT64_CONSTANT(val) $guint64_constant
++#define G_GINT64_CONSTANT(val) (val##L)
++#define G_GUINT64_CONSTANT(val) (val##UL)
++#else
++${glib_extension}typedef signed long long gint64;
++${glib_extension}typedef unsigned long long guint64;
++
++#define G_GINT64_CONSTANT(val) (G_GNUC_EXTENSION (val##LL))
++#define G_GUINT64_CONSTANT(val) (G_GNUC_EXTENSION (val##ULL))
++#endif
+ _______EOF
+
+ if test x$gint64_format != x ; then
+ cat >>$outfile <<_______EOF
+-#define G_GINT64_MODIFIER $gint64_modifier
+-#define G_GINT64_FORMAT $gint64_format
+-#define G_GUINT64_FORMAT $guint64_format
++#ifdef __LP64__
++#define G_GINT64_MODIFIER "l"
++#define G_GINT64_FORMAT "li"
++#define G_GUINT64_FORMAT "lu"
++#else
++#define G_GINT64_MODIFIER "ll"
++#define G_GINT64_FORMAT "lli"
++#define G_GUINT64_FORMAT "llu"
++#endif
+ _______EOF
+ else
+ cat >>$outfile <<_______EOF
+@@ -32434,9 +32448,15 @@
+
+ cat >>$outfile <<_______EOF
+
+-#define GLIB_SIZEOF_VOID_P $glib_void_p
+-#define GLIB_SIZEOF_LONG $glib_long
+-#define GLIB_SIZEOF_SIZE_T $glib_size_t
++#ifdef __LP64__
++#define GLIB_SIZEOF_VOID_P 8
++#define GLIB_SIZEOF_LONG 8
++#define GLIB_SIZEOF_SIZE_T 8
++#else
++#define GLIB_SIZEOF_VOID_P 4
++#define GLIB_SIZEOF_LONG 4
++#define GLIB_SIZEOF_SIZE_T 4
++#endif
+
+ _______EOF
+
+@@ -32464,18 +32484,33 @@
+ if test -z "$glib_unknown_void_p"; then
+ cat >>$outfile <<_______EOF
+
+-#define GPOINTER_TO_INT(p) ((gint) ${glib_gpi_cast} (p))
+-#define GPOINTER_TO_UINT(p) ((guint) ${glib_gpui_cast} (p))
++#ifdef __LP64__
++#define GPOINTER_TO_INT(p) ((gint) (glong) (p))
++#define GPOINTER_TO_UINT(p) ((guint) (gulong) (p))
++
++#define GINT_TO_POINTER(i) ((gpointer) (glong) (i))
++#define GUINT_TO_POINTER(u) ((gpointer) (gulong) (u))
++
++typedef signed long gintptr;
++typedef unsigned long guintptr;
++
++#define G_GINTPTR_MODIFIER "l"
++#define G_GINTPTR_FORMAT "li"
++#define G_GUINTPTR_FORMAT "lu"
++#else
++#define GPOINTER_TO_INT(p) ((gint) (p))
++#define GPOINTER_TO_UINT(p) ((guint) (p))
+
+-#define GINT_TO_POINTER(i) ((gpointer) ${glib_gpi_cast} (i))
+-#define GUINT_TO_POINTER(u) ((gpointer) ${glib_gpui_cast} (u))
++#define GINT_TO_POINTER(i) ((gpointer) (i))
++#define GUINT_TO_POINTER(u) ((gpointer) (u))
+
+-typedef signed $glib_intptr_type_define gintptr;
+-typedef unsigned $glib_intptr_type_define guintptr;
++typedef signed int gintptr;
++typedef unsigned int guintptr;
+
+-#define G_GINTPTR_MODIFIER $gintptr_modifier
+-#define G_GINTPTR_FORMAT $gintptr_format
+-#define G_GUINTPTR_FORMAT $guintptr_format
++#define G_GINTPTR_MODIFIER ""
++#define G_GINTPTR_FORMAT "i"
++#define G_GUINTPTR_FORMAT "u"
++#endif
+ _______EOF
+ else
+ echo '#error SIZEOF_VOID_P unknown - This should never happen' >>$outfile
+@@ -32588,7 +32623,9 @@
+
+ if test x"$g_memory_barrier_needed" != xno; then
+ echo >>$outfile
++ echo "#ifdef __BIG_ENDIAN__" >>$outfile
+ echo "#define G_ATOMIC_OP_MEMORY_BARRIER_NEEDED 1" >>$outfile
++ echo "#endif" >>$outfile
+ fi
+ if test x"$g_atomic_lock_free" = xyes; then
+ echo >>$outfile
+@@ -32598,27 +32635,52 @@
+ g_bit_sizes="16 32 64"
+ for bits in $g_bit_sizes; do
+ cat >>$outfile <<_______EOF
+-#define GINT${bits}_TO_${g_bs_native}(val) ((gint${bits}) (val))
+-#define GUINT${bits}_TO_${g_bs_native}(val) ((guint${bits}) (val))
+-#define GINT${bits}_TO_${g_bs_alien}(val) ((gint${bits}) GUINT${bits}_SWAP_LE_BE (val))
+-#define GUINT${bits}_TO_${g_bs_alien}(val) (GUINT${bits}_SWAP_LE_BE (val))
++#ifdef __BIG_ENDIAN__
++#define GINT${bits}_TO_BE(val) ((gint${bits}) (val))
++#define GUINT${bits}_TO_BE(val) ((guint${bits}) (val))
++#define GINT${bits}_TO_LE(val) ((gint${bits}) GUINT${bits}_SWAP_LE_BE (val))
++#define GUINT${bits}_TO_LE(val) (GUINT${bits}_SWAP_LE_BE (val))
++#else
++#define GINT${bits}_TO_LE(val) ((gint${bits}) (val))
++#define GUINT${bits}_TO_LE(val) ((guint${bits}) (val))
++#define GINT${bits}_TO_BE(val) ((gint${bits}) GUINT${bits}_SWAP_LE_BE (val))
++#define GUINT${bits}_TO_BE(val) (GUINT${bits}_SWAP_LE_BE (val))
++#endif
+ _______EOF
+ done
+
+ cat >>$outfile <<_______EOF
+-#define GLONG_TO_LE(val) ((glong) GINT${glongbits}_TO_LE (val))
+-#define GULONG_TO_LE(val) ((gulong) GUINT${glongbits}_TO_LE (val))
+-#define GLONG_TO_BE(val) ((glong) GINT${glongbits}_TO_BE (val))
+-#define GULONG_TO_BE(val) ((gulong) GUINT${glongbits}_TO_BE (val))
++#ifdef __LP64__
++#define GLONG_TO_LE(val) ((glong) GINT64_TO_LE (val))
++#define GULONG_TO_LE(val) ((gulong) GUINT64_TO_LE (val))
++#define GLONG_TO_BE(val) ((glong) GINT64_TO_BE (val))
++#define GULONG_TO_BE(val) ((gulong) GUINT64_TO_BE (val))
++#else
++#define GLONG_TO_LE(val) ((glong) GINT32_TO_LE (val))
++#define GULONG_TO_LE(val) ((gulong) GUINT32_TO_LE (val))
++#define GLONG_TO_BE(val) ((glong) GINT32_TO_BE (val))
++#define GULONG_TO_BE(val) ((gulong) GUINT32_TO_BE (val))
++#endif
+ #define GINT_TO_LE(val) ((gint) GINT${gintbits}_TO_LE (val))
+ #define GUINT_TO_LE(val) ((guint) GUINT${gintbits}_TO_LE (val))
+ #define GINT_TO_BE(val) ((gint) GINT${gintbits}_TO_BE (val))
+ #define GUINT_TO_BE(val) ((guint) GUINT${gintbits}_TO_BE (val))
+-#define GSIZE_TO_LE(val) ((gsize) GUINT${gsizebits}_TO_LE (val))
+-#define GSSIZE_TO_LE(val) ((gssize) GINT${gsizebits}_TO_LE (val))
+-#define GSIZE_TO_BE(val) ((gsize) GUINT${gsizebits}_TO_BE (val))
+-#define GSSIZE_TO_BE(val) ((gssize) GINT${gsizebits}_TO_BE (val))
+-#define G_BYTE_ORDER $g_byte_order
++#ifdef __LP64__
++#define GSIZE_TO_LE(val) ((gsize) GUINT64_TO_LE (val))
++#define GSSIZE_TO_LE(val) ((gssize) GINT64_TO_LE (val))
++#define GSIZE_TO_BE(val) ((gsize) GUINT64_TO_BE (val))
++#define GSSIZE_TO_BE(val) ((gssize) GINT64_TO_BE (val))
++#else
++#define GSIZE_TO_LE(val) ((gsize) GUINT32_TO_LE (val))
++#define GSSIZE_TO_LE(val) ((gssize) GINT32_TO_LE (val))
++#define GSIZE_TO_BE(val) ((gsize) GUINT32_TO_BE (val))
++#define GSSIZE_TO_BE(val) ((gssize) GINT32_TO_BE (val))
++#endif
++#ifdef __BIG_ENDIAN__
++#define G_BYTE_ORDER G_BIG_ENDIAN
++#else
++#define G_BYTE_ORDER G_LITTLE_ENDIAN
++#endif
+
+ #define GLIB_SYSDEF_POLLIN =$g_pollin
+ #define GLIB_SYSDEF_POLLOUT =$g_pollout
--- /dev/null
+--- glib/gi18n.h.orig 2008-11-23 23:45:23.000000000 -0600
++++ glib/gi18n.h 2008-11-25 23:59:29.000000000 -0600
+@@ -27,7 +27,9 @@
+
+ #define _(String) gettext (String)
+ #define Q_(String) g_dpgettext (NULL, String, 0)
++#ifndef N_
+ #define N_(String) (String)
++#endif
+ #define C_(Context,String) g_dpgettext (NULL, Context "\004" String, strlen (Context) + 1)
+ #define NC_(Context, String) (String)
--- /dev/null
+--- gio/gdbusprivate.c.orig 2012-04-30 11:24:02.000000000 -0500
++++ gio/gdbusprivate.c 2012-05-02 01:57:47.000000000 -0500
+@@ -2094,7 +2094,7 @@
+ /* TODO: use PACKAGE_LOCALSTATEDIR ? */
+ ret = NULL;
+ first_error = NULL;
+- if (!g_file_get_contents ("/var/lib/dbus/machine-id",
++ if (!g_file_get_contents ("@@PREFIX@@/var/lib/dbus/machine-id",
+ &ret,
+ NULL,
+ &first_error) &&
+@@ -2104,7 +2104,7 @@
+ NULL))
+ {
+ g_propagate_prefixed_error (error, first_error,
+- _("Unable to load /var/lib/dbus/machine-id or /etc/machine-id: "));
++ _("Unable to load @@PREFIX@@/var/lib/dbus/machine-id or /etc/machine-id: "));
+ }
+ else
+ {
--- /dev/null
+--- gio/xdgmime/xdgmime.c.orig 2009-03-12 22:09:52.000000000 -0600
++++ gio/xdgmime/xdgmime.c 2009-04-09 23:41:01.000000000 -0600
+@@ -257,7 +257,7 @@
+
+ xdg_data_dirs = getenv ("XDG_DATA_DIRS");
+ if (xdg_data_dirs == NULL)
+- xdg_data_dirs = "/usr/local/share/:/usr/share/";
++ xdg_data_dirs = "@@PREFIX@@/share/:/usr/share/";
+
+ ptr = xdg_data_dirs;
--- /dev/null
+--- glib-2.0.pc.in.orig 2012-01-15 21:12:06.000000000 -0600
++++ glib-2.0.pc.in 2012-01-19 22:29:43.000000000 -0600
+@@ -13,4 +13,4 @@
+ Requires.private: @PCRE_REQUIRES@
+ Libs: -L${libdir} -lglib-2.0 @INTLLIBS@
+ Libs.private: @G_THREAD_LIBS@ @G_LIBS_EXTRA@ @PCRE_LIBS@ @INTLLIBS@ @ICONV_LIBS@
+-Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include @GLIB_EXTRA_CFLAGS@
++Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include -I${includedir} @GLIB_EXTRA_CFLAGS@
--- /dev/null
+--- glib/gunicollate.c.orig 2009-03-31 18:04:20.000000000 -0500
++++ glib/gunicollate.c 2010-03-06 17:59:08.000000000 -0600
+@@ -26,6 +26,9 @@
+ #include <wchar.h>
+ #endif
+
++/* Carbon is not available on 64-bit */
++#undef HAVE_CARBON
++
+ #ifdef HAVE_CARBON
+ #include <CoreServices/CoreServices.h>
+ #endif
--- /dev/null
+diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
+index e6c516c..f3fa26a 100644
+--- a/gdk/gdkevents.h
++++ b/gdk/gdkevents.h
+@@ -65,6 +65,11 @@ typedef struct _GdkEventWindowState GdkEventWindowState;
+ typedef struct _GdkEventSetting GdkEventSetting;
+ typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
+
++/* OS X specific gesture events */
++typedef struct _GdkEventGestureMagnify GdkEventGestureMagnify;
++typedef struct _GdkEventGestureRotate GdkEventGestureRotate;
++typedef struct _GdkEventGestureSwipe GdkEventGestureSwipe;
++
+ typedef union _GdkEvent GdkEvent;
+
+ typedef void (*GdkEventFunc) (GdkEvent *event,
+@@ -152,6 +157,9 @@ typedef enum
+ GDK_OWNER_CHANGE = 34,
+ GDK_GRAB_BROKEN = 35,
+ GDK_DAMAGE = 36,
++ GDK_GESTURE_MAGNIFY = 37,
++ GDK_GESTURE_ROTATE = 38,
++ GDK_GESTURE_SWIPE = 39,
+ GDK_EVENT_LAST /* helper variable for decls */
+ } GdkEventType;
+
+@@ -488,6 +496,52 @@ struct _GdkEventDND {
+ gshort x_root, y_root;
+ };
+
++/* Event types for OS X gestures */
++
++struct _GdkEventGestureMagnify
++{
++ GdkEventType type;
++ GdkWindow *window;
++ gint8 send_event;
++ guint32 time;
++ gdouble x;
++ gdouble y;
++ guint state;
++ gdouble magnification;
++ GdkDevice *device;
++ gdouble x_root, y_root;
++};
++
++struct _GdkEventGestureRotate
++{
++ GdkEventType type;
++ GdkWindow *window;
++ gint8 send_event;
++ guint32 time;
++ gdouble x;
++ gdouble y;
++ guint state;
++ gdouble rotation;
++ GdkDevice *device;
++ gdouble x_root, y_root;
++};
++
++struct _GdkEventGestureSwipe
++{
++ GdkEventType type;
++ GdkWindow *window;
++ gint8 send_event;
++ guint32 time;
++ gdouble x;
++ gdouble y;
++ guint state;
++ gdouble delta_x;
++ gdouble delta_y;
++ GdkDevice *device;
++ gdouble x_root, y_root;
++};
++
++
+ union _GdkEvent
+ {
+ GdkEventType type;
+@@ -511,6 +565,9 @@ union _GdkEvent
+ GdkEventWindowState window_state;
+ GdkEventSetting setting;
+ GdkEventGrabBroken grab_broken;
++ GdkEventGestureMagnify magnify;
++ GdkEventGestureRotate rotate;
++ GdkEventGestureSwipe swipe;
+ };
+
+ GType gdk_event_get_type (void) G_GNUC_CONST;
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index d20b424..fb89451 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -9813,6 +9813,9 @@ static const guint type_masks[] = {
+ 0, /* GDK_OWNER_CHANGE = 34 */
+ 0, /* GDK_GRAB_BROKEN = 35 */
+ 0, /* GDK_DAMAGE = 36 */
++ 0, /* GDK_GESTURE_MAGNIFY = 37 */
++ 0, /* GDK_GESTURE_ROTATE = 38 */
++ 0, /* GDK_GESTURE_SWIPE = 39 */
+ };
+ G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
+
+diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c
+index 2c897fb..cdae2f1 100644
+--- a/gdk/quartz/GdkQuartzView.c
++++ b/gdk/quartz/GdkQuartzView.c
+@@ -190,4 +190,37 @@
+ [self updateTrackingRect];
+ }
+
++/* Handle OS X gesture events. The Apple documentation is explicit
++ * that these events should not be captured through a tracking loop (which
++ * is how we handle usual event handling), so we fallback to overriding
++ * NSResponder methods.
++ */
++
++-(void)magnifyWithEvent:(NSEvent *)event
++{
++ GdkEvent *gdk_event;
++
++ gdk_event = _gdk_quartz_events_create_magnify_event (event);
++ if (gdk_event)
++ _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
++}
++
++-(void)rotateWithEvent:(NSEvent *)event
++{
++ GdkEvent *gdk_event;
++
++ gdk_event = _gdk_quartz_events_create_rotate_event (event);
++ if (gdk_event)
++ _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
++}
++
++-(void)swipeWithEvent:(NSEvent *)event
++{
++ GdkEvent *gdk_event;
++
++ gdk_event = _gdk_quartz_events_create_swipe_event (event);
++ if (gdk_event)
++ _gdk_event_queue_append (gdk_display_get_default (), gdk_event);
++}
++
+ @end
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 9478c16..128c794 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -374,6 +374,11 @@ get_event_mask_from_ns_event (NSEvent *nsevent)
+ case NSMouseExited:
+ return GDK_LEAVE_NOTIFY_MASK;
+
++ case NSEventTypeMagnify:
++ case NSEventTypeRotate:
++ case NSEventTypeSwipe:
++ return 0;
++
+ default:
+ g_assert_not_reached ();
+ }
+@@ -752,6 +757,11 @@ find_window_for_ns_event (NSEvent *nsevent,
+
+ return toplevel;
+
++ case NSEventTypeMagnify:
++ case NSEventTypeRotate:
++ case NSEventTypeSwipe:
++ return toplevel;
++
+ default:
+ /* Ignore everything else. */
+ break;
+@@ -1011,6 +1021,139 @@ fill_key_event (GdkWindow *window,
+ event->key.keyval));
+ }
+
++
++static GdkWindow *
++find_window_for_ns_gesture_event (NSEvent *nsevent,
++ gint *_x,
++ gint *_y,
++ gint *x_root,
++ gint *y_root)
++{
++ GdkDisplay *display = _gdk_display;
++ GdkWindow *toplevel_window = NULL, *pointer_window = NULL;
++ gdouble found_x, found_y;
++ gint x, y;
++
++ toplevel_window = find_window_for_ns_event (nsevent, &x, &y, x_root, y_root);
++ if (!toplevel_window)
++ return NULL;
++
++ if (toplevel_window == display->pointer_info.toplevel_under_pointer)
++ pointer_window = _gdk_window_find_descendant_at (toplevel_window,
++ x, y,
++ &found_x, &found_y);
++ else
++ pointer_window = NULL;
++
++ *_x = found_x;
++ *_y = found_y;
++
++ return pointer_window;
++}
++
++
++GdkEvent *
++_gdk_quartz_events_create_magnify_event (NSEvent *nsevent)
++{
++ GdkEvent *event;
++ GdkWindow *window = NULL;
++ gint x, y, x_root, y_root;
++
++ window = find_window_for_ns_gesture_event (nsevent,
++ &x, &y,
++ &x_root, &y_root);
++
++ if (!window)
++ return NULL;
++
++ event = gdk_event_new (GDK_GESTURE_MAGNIFY);
++
++ event->any.type = GDK_GESTURE_MAGNIFY;
++ event->magnify.window = window;
++ event->magnify.time = get_time_from_ns_event (nsevent);
++ event->magnify.x = x;
++ event->magnify.y = y;
++ event->magnify.x_root = x_root;
++ event->magnify.y_root = y_root;
++ event->magnify.magnification = [nsevent magnification];
++ event->magnify.state = get_keyboard_modifiers_from_ns_event (nsevent) |
++ _gdk_quartz_events_get_current_mouse_modifiers ();
++ event->magnify.device = _gdk_display->core_pointer;
++
++ fixup_event (event);
++
++ return event;
++}
++
++GdkEvent *
++_gdk_quartz_events_create_rotate_event (NSEvent *nsevent)
++{
++ GdkEvent *event;
++ GdkWindow *window;
++ gint x, y, x_root, y_root;
++
++ window = find_window_for_ns_gesture_event (nsevent,
++ &x, &y,
++ &x_root, &y_root);
++
++ if (!window)
++ return NULL;
++
++ event = gdk_event_new (GDK_GESTURE_ROTATE);
++
++ event->any.type = GDK_GESTURE_ROTATE;
++ event->rotate.window = window;
++ event->rotate.time = get_time_from_ns_event (nsevent);
++ event->rotate.x = x;
++ event->rotate.y = y;
++ event->rotate.x_root = x_root;
++ event->rotate.y_root = y_root;
++ event->rotate.rotation = [nsevent rotation];
++ event->rotate.state = get_keyboard_modifiers_from_ns_event (nsevent) |
++ _gdk_quartz_events_get_current_mouse_modifiers ();
++ event->rotate.device = _gdk_display->core_pointer;
++
++ fixup_event (event);
++
++ return event;
++}
++
++GdkEvent *
++_gdk_quartz_events_create_swipe_event (NSEvent *nsevent)
++{
++ GdkEvent *event;
++ GdkWindow *window;
++ gint x, y, x_root, y_root;
++
++ window = find_window_for_ns_gesture_event (nsevent,
++ &x, &y,
++ &x_root, &y_root);
++
++ if (!window)
++ return NULL;
++
++ event = gdk_event_new (GDK_GESTURE_SWIPE);
++
++ event->any.type = GDK_GESTURE_SWIPE;
++ event->swipe.window = window;
++ event->swipe.time = get_time_from_ns_event (nsevent);
++ event->swipe.x = x;
++ event->swipe.y = y;
++ event->swipe.x_root = x_root;
++ event->swipe.y_root = y_root;
++ event->swipe.delta_x = [nsevent deltaX];
++ event->swipe.delta_y = [nsevent deltaY];
++ event->swipe.state = get_keyboard_modifiers_from_ns_event (nsevent) |
++ _gdk_quartz_events_get_current_mouse_modifiers ();
++ event->swipe.device = _gdk_display->core_pointer;
++
++ fixup_event (event);
++
++ return event;
++}
++
++
++
+ static gboolean
+ synthesize_crossing_event (GdkWindow *window,
+ GdkEvent *event,
+@@ -1392,6 +1535,13 @@ gdk_event_translate (GdkEvent *event,
+ }
+ break;
+
++ /* Gesture events are handled through GdkQuartzView, because the
++ * Apple documentation states very clearly that such events should
++ * not be handled through a tracking loop (like GDK uses).
++ * Experiments show that gesture events do not get through our
++ * tracking loop either.
++ */
++
+ default:
+ /* Ignore everything elsee. */
+ return_val = FALSE;
+diff --git a/gdk/quartz/gdkglobals-quartz.c b/gdk/quartz/gdkglobals-quartz.c
+index 53c6d5e..6d01b59 100644
+--- a/gdk/quartz/gdkglobals-quartz.c
++++ b/gdk/quartz/gdkglobals-quartz.c
+@@ -41,3 +41,9 @@ gdk_quartz_osx_version (void)
+ else
+ return minor;
+ }
++
++gboolean
++gdk_quartz_supports_gesture_events (void)
++{
++ return TRUE;
++}
+diff --git a/gdk/quartz/gdkprivate-quartz.h b/gdk/quartz/gdkprivate-quartz.h
+index c1b3083..03a3857 100644
+--- a/gdk/quartz/gdkprivate-quartz.h
++++ b/gdk/quartz/gdkprivate-quartz.h
+@@ -179,6 +179,10 @@ GdkModifierType _gdk_quartz_events_get_current_mouse_modifiers (void);
+ void _gdk_quartz_events_send_enter_notify_event (GdkWindow *window);
+ void _gdk_quartz_events_break_all_grabs (guint32 time);
+
++GdkEvent *_gdk_quartz_events_create_magnify_event (NSEvent *event);
++GdkEvent *_gdk_quartz_events_create_rotate_event (NSEvent *event);
++GdkEvent *_gdk_quartz_events_create_swipe_event (NSEvent *event);
++
+ /* Event loop */
+ gboolean _gdk_quartz_event_loop_check_pending (void);
+ NSEvent * _gdk_quartz_event_loop_get_pending (void);
+diff --git a/gdk/quartz/gdkquartz.h b/gdk/quartz/gdkquartz.h
+index 742d651..f79d04a 100644
+--- a/gdk/quartz/gdkquartz.h
++++ b/gdk/quartz/gdkquartz.h
+@@ -57,6 +57,7 @@ NSImage *gdk_quartz_pixbuf_to_ns_image_libgtk_only (GdkPixbuf
+ id gdk_quartz_drag_context_get_dragging_info_libgtk_only (GdkDragContext *context);
+ NSEvent *gdk_quartz_event_get_nsevent (GdkEvent *event);
+ GdkOSXVersion gdk_quartz_osx_version (void);
++gboolean gdk_quartz_supports_gesture_events (void);
+
+ G_END_DECLS
+
+diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
+index 5a88679..db77675 100644
+--- a/gtk/gtkmain.c
++++ b/gtk/gtkmain.c
+@@ -1636,6 +1636,9 @@ gtk_main_do_event (GdkEvent *event)
+ case GDK_WINDOW_STATE:
+ case GDK_GRAB_BROKEN:
+ case GDK_DAMAGE:
++ case GDK_GESTURE_MAGNIFY:
++ case GDK_GESTURE_ROTATE:
++ case GDK_GESTURE_SWIPE:
+ if (!_gtk_widget_captured_event (event_widget, event))
+ gtk_widget_event (event_widget, event);
+ break;
+diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
+index 9a43d27..2d11e09 100644
+--- a/gtk/gtkwidget.c
++++ b/gtk/gtkwidget.c
+@@ -197,6 +197,9 @@ enum {
+ KEYNAV_FAILED,
+ DRAG_FAILED,
+ DAMAGE_EVENT,
++ GESTURE_MAGNIFY_EVENT,
++ GESTURE_ROTATE_EVENT,
++ GESTURE_SWIPE_EVENT,
+ LAST_SIGNAL
+ };
+
+@@ -2242,6 +2245,70 @@ gtk_widget_class_init (GtkWidgetClass *klass)
+ _gtk_marshal_BOOLEAN__BOXED,
+ G_TYPE_BOOLEAN, 1,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
++ /**
++ * GtkWidget::gesture-magnify-event:
++ * @widget: the object which received the signal
++ * @event: the #GdkEventGestureMagnify event
++ *
++ * Emitted when a magnify event is received on @widget's window.
++ *
++ * Returns: %TRUE to stop other handlers from being invoked for the event.
++ * %FALSE to propagate the event further.
++ *
++ * Since: 2.24 Xamarin specific.
++ */
++ widget_signals[GESTURE_MAGNIFY_EVENT] =
++ g_signal_new (I_("gesture-magnify-event"),
++ G_TYPE_FROM_CLASS (gobject_class),
++ G_SIGNAL_RUN_LAST,
++ 0,
++ _gtk_boolean_handled_accumulator, NULL,
++ _gtk_marshal_BOOLEAN__BOXED,
++ G_TYPE_BOOLEAN, 1,
++ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
++ /**
++ * GtkWidget::gesture-rotate-event:
++ * @widget: the object which received the signal
++ * @event: the #GdkEventGestureRotate event
++ *
++ * Emitted when a rotation event is received on @widget's window.
++ *
++ * Returns: %TRUE to stop other handlers from being invoked for the event.
++ * %FALSE to propagate the event further.
++ *
++ * Since: 2.24 Xamarin specific.
++ */
++ widget_signals[GESTURE_ROTATE_EVENT] =
++ g_signal_new (I_("gesture-rotate-event"),
++ G_TYPE_FROM_CLASS (gobject_class),
++ G_SIGNAL_RUN_LAST,
++ 0,
++ _gtk_boolean_handled_accumulator, NULL,
++ _gtk_marshal_BOOLEAN__BOXED,
++ G_TYPE_BOOLEAN, 1,
++ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
++ /**
++ * GtkWidget::gesture-swipe-event:
++ * @widget: the object which received the signal
++ * @event: the #GdkEventGestureSwipe event
++ *
++ * Emitted when a swipe event is received on @widget's window.
++ *
++ * Returns: %TRUE to stop other handlers from being invoked for the event.
++ * %FALSE to propagate the event further.
++ *
++ * Since: 2.24 Xamarin specific.
++ */
++ widget_signals[GESTURE_SWIPE_EVENT] =
++ g_signal_new (I_("gesture-swipe-event"),
++ G_TYPE_FROM_CLASS (gobject_class),
++ G_SIGNAL_RUN_LAST,
++ 0,
++ _gtk_boolean_handled_accumulator, NULL,
++ _gtk_marshal_BOOLEAN__BOXED,
++ G_TYPE_BOOLEAN, 1,
++ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
++
+ /**
+ * GtkWidget::grab-broken-event:
+ * @widget: the object which received the signal
+@@ -4975,6 +5042,15 @@ gtk_widget_event_internal (GtkWidget *widget,
+ case GDK_DAMAGE:
+ signal_num = DAMAGE_EVENT;
+ break;
++ case GDK_GESTURE_MAGNIFY:
++ signal_num = GESTURE_MAGNIFY_EVENT;
++ break;
++ case GDK_GESTURE_ROTATE:
++ signal_num = GESTURE_ROTATE_EVENT;
++ break;
++ case GDK_GESTURE_SWIPE:
++ signal_num = GESTURE_SWIPE_EVENT;
++ break;
+ default:
+ g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
+ signal_num = -1;
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 3888826..68a0a79 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -88,7 +88,8 @@ noinst_PROGRAMS = $(TEST_PROGS) \
+ testactions \
+ testgrouping \
+ testtooltips \
+- testvolumebutton
++ testvolumebutton \
++ testgestures
+
+ if HAVE_CXX
+ noinst_PROGRAMS += autotestkeywords
+@@ -165,6 +166,7 @@ testgrouping_DEPENDENCIES = $(TEST_DEPS)
+ testtooltips_DEPENDENCIES = $(TEST_DEPS)
+ testvolumebutton_DEPENDENCIES = $(TEST_DEPS)
+ testwindows_DEPENDENCIES = $(TEST_DEPS)
++testgestures_DEPENDENCIES = $(TEST_DEPS)
+
+ testentrycompletion_SOURCES = \
+ prop-editor.c \
+@@ -273,6 +275,9 @@ testoffscreenwindow_SOURCES = \
+ testwindows_SOURCES = \
+ testwindows.c
+
++testgestures_SOURCES = \
++ testgestures.c
++
+ EXTRA_DIST += \
+ prop-editor.h \
+ testgtk.1 \
+diff --git a/tests/testgestures.c b/tests/testgestures.c
+new file mode 100644
+index 0000000..ae3ab99
+--- /dev/null
++++ b/tests/testgestures.c
+@@ -0,0 +1,214 @@
++#include <gtk/gtk.h>
++#include <math.h>
++
++
++typedef struct
++{
++ gdouble width;
++ gdouble height;
++
++ gdouble angle;
++
++ GtkWidget *widget;
++ gint offset_x;
++ gint offset_y;
++ gdouble progress;
++ gboolean increasing;
++}
++RectangleInfo;
++
++
++
++static gboolean
++handle_expose_event (GtkWidget *widget,
++ GdkEventExpose *expose,
++ gpointer user_data)
++{
++ cairo_t *cr;
++ int center_x, center_y;
++ RectangleInfo *rect = (RectangleInfo *)user_data;
++
++ cr = gdk_cairo_create (widget->window);
++
++ cairo_save (cr);
++
++ /* Background */
++ cairo_rectangle (cr, 0, 0,
++ widget->allocation.width,
++ widget->allocation.height);
++ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
++ cairo_fill (cr);
++
++ cairo_restore (cr);
++
++ /* Rectangle */
++ center_x = (widget->allocation.width - rect->width) / 2;
++ center_y = (widget->allocation.height - rect->height) / 2;
++
++ if (rect->progress != 0.0f)
++ {
++ cairo_translate (cr, rect->offset_x * rect->progress,
++ rect->offset_y * rect->progress);
++ }
++
++ cairo_save (cr);
++
++ cairo_translate (cr, widget->allocation.width / 2,
++ widget->allocation.height / 2);
++ cairo_rotate (cr, rect->angle * M_PI / 180.0);
++ cairo_translate (cr, -widget->allocation.width / 2,
++ -widget->allocation.height / 2);
++
++
++ cairo_rectangle (cr,
++ center_x, center_y,
++ rect->width, rect->height);
++ cairo_set_source_rgb (cr, 0.9, 0.0, 0.0);
++ cairo_stroke (cr);
++
++ cairo_rectangle (cr,
++ center_x, center_y,
++ rect->width, rect->height);
++ cairo_set_source_rgba (cr, 0.9, 0.0, 0.0, 0.3);
++ cairo_fill (cr);
++
++ cairo_restore (cr);
++
++
++ cairo_destroy (cr);
++
++ return FALSE;
++}
++
++static gboolean
++handle_gesture_magnify_event (GtkWidget *widget,
++ GdkEventGestureMagnify *magnify,
++ gpointer user_data)
++{
++ RectangleInfo *rect = (RectangleInfo *)user_data;
++
++ rect->width += rect->width * magnify->magnification;
++ if (rect->width < 5)
++ rect->width = 5;
++
++ rect->height += rect->height * magnify->magnification;
++ if (rect->height < 5)
++ rect->height = 5;
++
++ gtk_widget_queue_draw (widget);
++
++ return TRUE;
++}
++
++static gboolean
++handle_gesture_rotate_event (GtkWidget *widget,
++ GdkEventGestureRotate *rotate,
++ gpointer user_data)
++{
++ RectangleInfo *rect = (RectangleInfo *)user_data;
++
++ rect->angle -= rotate->rotation;
++
++ gtk_widget_queue_draw (widget);
++
++ return TRUE;
++}
++
++
++static gboolean
++bounce_timeout (gpointer user_data)
++{
++ gboolean retval = TRUE;
++ RectangleInfo *rect = (RectangleInfo *)user_data;
++
++ if (rect->increasing)
++ rect->progress += 0.10f;
++ else
++ rect->progress -= 0.10f;
++
++ if (rect->progress > 1.0f)
++ {
++ rect->progress = 0.90f;
++ rect->increasing = FALSE;
++ }
++ else if (rect->progress <= 0.0f)
++ {
++ rect->progress = 0.0f;
++ retval = FALSE;
++ }
++
++ gtk_widget_queue_draw (rect->widget);
++
++ return retval;
++}
++
++static void
++bounce (RectangleInfo *rect,
++ int offset_x,
++ int offset_y)
++{
++ if (rect->progress != 0.0f)
++ return;
++
++ rect->progress = 0.10f;
++ rect->increasing = TRUE;
++ rect->offset_x = offset_x;
++ rect->offset_y = offset_y;
++ gtk_widget_queue_draw (rect->widget);
++
++ gdk_threads_add_timeout (25, bounce_timeout, rect);
++}
++
++static gboolean
++handle_gesture_swipe_event (GtkWidget *widget,
++ GdkEventGestureSwipe *swipe,
++ gpointer user_data)
++{
++ int offset_x = 150, offset_y = 150;
++ RectangleInfo *rect = (RectangleInfo *)user_data;
++
++ offset_x *= -1.0 * swipe->delta_x;
++ offset_y *= -1.0 * swipe->delta_y;
++
++ bounce (rect, offset_x, offset_y);
++
++ return TRUE;
++}
++
++int
++main (int argc, char **argv)
++{
++ GtkWidget *window;
++ GtkWidget *drawing_area;
++ RectangleInfo rect;
++
++ gtk_init (&argc, &argv);
++
++ rect.width = 40.0;
++ rect.height = 40.0;
++ rect.angle = 0.0;
++ rect.progress = 0.0f;
++
++ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
++ gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
++ g_signal_connect (window, "delete-event",
++ G_CALLBACK (gtk_main_quit), NULL);
++
++ drawing_area = gtk_drawing_area_new ();
++ rect.widget = drawing_area;
++ g_signal_connect (drawing_area, "expose-event",
++ G_CALLBACK (handle_expose_event), &rect);
++ g_signal_connect (drawing_area, "gesture-magnify-event",
++ G_CALLBACK (handle_gesture_magnify_event), &rect);
++ g_signal_connect (drawing_area, "gesture-rotate-event",
++ G_CALLBACK (handle_gesture_rotate_event), &rect);
++ g_signal_connect (drawing_area, "gesture-swipe-event",
++ G_CALLBACK (handle_gesture_swipe_event), &rect);
++ gtk_container_add (GTK_CONTAINER (window), drawing_area);
++
++ gtk_widget_show_all (window);
++
++ gtk_main ();
++
++ return 0;
++}
--- /dev/null
+diff --git a/gdk/quartz/GdkQuartzWindow.c b/gdk/quartz/GdkQuartzWindow.c
+index 06d11cf..c11924c 100644
+--- a/gdk/quartz/GdkQuartzWindow.c
++++ b/gdk/quartz/GdkQuartzWindow.c
+@@ -412,7 +412,7 @@
+
+ -(BOOL)isInManualResize
+ {
+- return inManualResize;
++ return inManualResize || inManualMove;
+ }
+
+ -(void)beginManualResize
--- /dev/null
+From fdbef10f94b9e16618b75e7e12184befcd178852 Mon Sep 17 00:00:00 2001
+From: Havoc Pennington <hp@pobox.com>
+Date: Mon, 20 Dec 2010 12:58:04 -0500
+Subject: [PATCH 01/68] Add invariant that a child is unmapped if parent is
+ unmapped
+
+Requires fixes to GtkContainer and GtkWindow to unmap their
+children, rather than just withdrawing or hiding the container
+window.
+
+Requires fix to GtkHandleBox to chain up to GtkContainer unmap.
+
+Historically we avoided these unmaps for efficiency reasons,
+but these days it's a bigger problem that there's no way
+for child widgets to know that one of their ancestors has
+become unmapped.
+(cherry picked from commit b67c5af55bf611c013b2c43e6878281abd773530)
+---
+ docs/widget_system.txt | 2 +-
+ gtk/gtkcontainer.c | 15 ++++++++++-----
+ gtk/gtkhandlebox.c | 2 ++
+ gtk/gtkwindow.c | 7 ++++++-
+ 4 files changed, 19 insertions(+), 7 deletions(-)
+
+diff --git a/docs/widget_system.txt b/docs/widget_system.txt
+index 1c2867c..9463f10 100644
+--- a/docs/widget_system.txt
++++ b/docs/widget_system.txt
+@@ -255,7 +255,7 @@ In the following
+
+ widget->parent && GTK_WIDGET_MAPPED (widget->parent) &&
+ GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_CHILD_VISIBLE
+- => GTK_WIDGET_MAPPED (widget)
++ <=> GTK_WIDGET_MAPPED (widget)
+
+ Note:, the definition
+
+diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
+index 9aaa7d9..7105724 100644
+--- a/gtk/gtkcontainer.c
++++ b/gtk/gtkcontainer.c
+@@ -2694,12 +2694,17 @@ gtk_container_unmap (GtkWidget *widget)
+ {
+ gtk_widget_set_mapped (widget, FALSE);
+
++ /* hide our window first so user doesn't see all the child windows
++ * vanishing one by one. (only matters these days if one of the
++ * children has an actual native window instead of client-side
++ * window, e.g. a GtkSocket would)
++ */
+ if (gtk_widget_get_has_window (widget))
+- gdk_window_hide (widget->window);
+- else
+- gtk_container_forall (GTK_CONTAINER (widget),
+- (GtkCallback)gtk_widget_unmap,
+- NULL);
++ gdk_window_hide (gtk_widget_get_window (widget));
++
++ gtk_container_forall (GTK_CONTAINER (widget),
++ (GtkCallback)gtk_widget_unmap,
++ NULL);
+ }
+
+ /**
+diff --git a/gtk/gtkhandlebox.c b/gtk/gtkhandlebox.c
+index 667371f..557d2de 100644
+--- a/gtk/gtkhandlebox.c
++++ b/gtk/gtkhandlebox.c
+@@ -383,6 +383,8 @@ gtk_handle_box_unmap (GtkWidget *widget)
+ gdk_window_hide (hb->float_window);
+ hb->float_window_mapped = FALSE;
+ }
++
++ GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unmap (widget);
+ }
+
+ static void
+diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
+index 4de3215..8f174c6 100644
+--- a/gtk/gtkwindow.c
++++ b/gtk/gtkwindow.c
+@@ -4690,7 +4690,8 @@ gtk_window_unmap (GtkWidget *widget)
+ {
+ GtkWindow *window = GTK_WINDOW (widget);
+ GtkWindowPrivate *priv = GTK_WINDOW_GET_PRIVATE (widget);
+- GtkWindowGeometryInfo *info;
++ GtkWindowGeometryInfo *info;
++ GtkWidget *child;
+ GdkWindowState state;
+
+ gtk_widget_set_mapped (widget, FALSE);
+@@ -4721,6 +4722,10 @@ gtk_window_unmap (GtkWidget *widget)
+ window->stick_initially = (state & GDK_WINDOW_STATE_STICKY) != 0;
+ priv->above_initially = (state & GDK_WINDOW_STATE_ABOVE) != 0;
+ priv->below_initially = (state & GDK_WINDOW_STATE_BELOW) != 0;
++
++ child = gtk_bin_get_child (&(window->bin));
++ if (child)
++ gtk_widget_unmap (child);
+ }
+
+ static void
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 4f27e87d62b480a49dcffc232e246295c32113ca Mon Sep 17 00:00:00 2001
+From: Kjell Ahlstedt <kjell.ahlstedt@bredband.net>
+Date: Wed, 16 Nov 2011 09:03:12 +0100
+Subject: [PATCH 02/68] Maintain map/unmap invariants in
+ GtkRecentChooserDialog
+
+We used to explicitly map and unmap the child GtkRecentChooserWidget when
+mapping and unmapping the dialog, respectively. Now that GtkContainer actually
+unmaps child widgets (instead of avoiding that), we can assume that the
+child GtkRecentChooserWidget will be unmapped when we want it to be.
+
+This fixes a warning from gtk_widget_verify_invariants(), as we were mapping
+our child widget before calling our parent class' ::map() handler. Bug #659257.
+(cherry picked from commit e8bb2e4545365d83261381a14920b773aba4a678)
+---
+ gtk/gtkrecentchooserdialog.c | 30 ------------------------------
+ 1 file changed, 30 deletions(-)
+
+diff --git a/gtk/gtkrecentchooserdialog.c b/gtk/gtkrecentchooserdialog.c
+index 058439a..1c67b7a 100644
+--- a/gtk/gtkrecentchooserdialog.c
++++ b/gtk/gtkrecentchooserdialog.c
+@@ -55,9 +55,6 @@ static void gtk_recent_chooser_dialog_get_property (GObject *object,
+ GValue *value,
+ GParamSpec *pspec);
+
+-static void gtk_recent_chooser_dialog_map (GtkWidget *widget);
+-static void gtk_recent_chooser_dialog_unmap (GtkWidget *widget);
+-
+ G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserDialog,
+ gtk_recent_chooser_dialog,
+ GTK_TYPE_DIALOG,
+@@ -68,16 +65,12 @@ static void
+ gtk_recent_chooser_dialog_class_init (GtkRecentChooserDialogClass *klass)
+ {
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->set_property = gtk_recent_chooser_dialog_set_property;
+ gobject_class->get_property = gtk_recent_chooser_dialog_get_property;
+ gobject_class->constructor = gtk_recent_chooser_dialog_constructor;
+ gobject_class->finalize = gtk_recent_chooser_dialog_finalize;
+
+- widget_class->map = gtk_recent_chooser_dialog_map;
+- widget_class->unmap = gtk_recent_chooser_dialog_unmap;
+-
+ _gtk_recent_chooser_install_properties (gobject_class);
+
+ g_type_class_add_private (klass, sizeof (GtkRecentChooserDialogPrivate));
+@@ -224,29 +217,6 @@ gtk_recent_chooser_dialog_finalize (GObject *object)
+ G_OBJECT_CLASS (gtk_recent_chooser_dialog_parent_class)->finalize (object);
+ }
+
+-static void
+-gtk_recent_chooser_dialog_map (GtkWidget *widget)
+-{
+- GtkRecentChooserDialog *dialog = GTK_RECENT_CHOOSER_DIALOG (widget);
+- GtkRecentChooserDialogPrivate *priv = dialog->priv;
+-
+- if (!gtk_widget_get_mapped (priv->chooser))
+- gtk_widget_map (priv->chooser);
+-
+- GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->map (widget);
+-}
+-
+-static void
+-gtk_recent_chooser_dialog_unmap (GtkWidget *widget)
+-{
+- GtkRecentChooserDialog *dialog = GTK_RECENT_CHOOSER_DIALOG (widget);
+- GtkRecentChooserDialogPrivate *priv = dialog->priv;
+-
+- GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->unmap (widget);
+-
+- gtk_widget_unmap (priv->chooser);
+-}
+-
+ static GtkWidget *
+ gtk_recent_chooser_dialog_new_valist (const gchar *title,
+ GtkWindow *parent,
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 41c694a884284280c8ac766e636dd344493509e4 Mon Sep 17 00:00:00 2001
+From: Matthias Clasen <mclasen@redhat.com>
+Date: Fri, 2 Sep 2011 21:40:42 -0400
+Subject: [PATCH 03/68] GtkPlug: preserve map/unmap invariants (cherry picked
+ from commit be152f9b6196849c99c54afe5a0f651d08bf4626)
+
+---
+ gtk/gtkplug.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+diff --git a/gtk/gtkplug.c b/gtk/gtkplug.c
+index 99af62a..86dd85e 100644
+--- a/gtk/gtkplug.c
++++ b/gtk/gtkplug.c
+@@ -708,13 +708,15 @@ gtk_plug_map (GtkWidget *widget)
+ {
+ GtkBin *bin = GTK_BIN (widget);
+ GtkPlug *plug = GTK_PLUG (widget);
+-
++ GtkWidget *child;
++
+ gtk_widget_set_mapped (widget, TRUE);
+
+- if (bin->child &&
+- gtk_widget_get_visible (bin->child) &&
+- !gtk_widget_get_mapped (bin->child))
+- gtk_widget_map (bin->child);
++ child = gtk_bin_get_child (bin);
++ if (child != NULL &&
++ gtk_widget_get_visible (child) &&
++ !gtk_widget_get_mapped (child))
++ gtk_widget_map (child);
+
+ _gtk_plug_windowing_map_toplevel (plug);
+
+@@ -732,13 +734,18 @@ gtk_plug_unmap (GtkWidget *widget)
+ if (gtk_widget_is_toplevel (widget))
+ {
+ GtkPlug *plug = GTK_PLUG (widget);
++ GtkWidget *child;
+
+ gtk_widget_set_mapped (widget, FALSE);
+
+ gdk_window_hide (widget->window);
+
++ child = gtk_bin_get_child (GTK_BIN (widget));
++ if (child != NULL)
++ gtk_widget_unmap (child);
++
+ _gtk_plug_windowing_unmap_toplevel (plug);
+-
++
+ gdk_synthesize_window_state (widget->window,
+ 0,
+ GDK_WINDOW_STATE_WITHDRAWN);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 8636b3d980cdef4c12f53779f708cc4a674d1054 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 23 Nov 2012 15:24:36 +0100
+Subject: [PATCH 04/68] Add gdk_screen_get_monitor_workarea() and use it all
+ over the place
+
+---
+ gdk/gdk.symbols | 1 +
+ gdk/gdkscreen.h | 3 +++
+ gdk/quartz/gdkscreen-quartz.c | 30 ++++++++++++++++++++++++++++++
+ gtk/gtkcombobox.c | 4 ++--
+ gtk/gtkentry.c | 2 +-
+ gtk/gtkentrycompletion.c | 2 +-
+ gtk/gtkfilechooserdefault.c | 2 +-
+ gtk/gtklinkbutton.c | 2 +-
+ gtk/gtkmenu.c | 6 +++---
+ gtk/gtkmenuitem.c | 2 +-
+ gtk/gtkmenutoolbutton.c | 2 +-
+ gtk/gtkrecentchooserdefault.c | 4 ++--
+ gtk/gtkscalebutton.c | 2 +-
+ gtk/gtkstatusicon.c | 4 ++--
+ gtk/gtktextview.c | 2 +-
+ gtk/gtktoolbar.c | 2 +-
+ gtk/gtktooltip.c | 2 +-
+ gtk/gtktreeview.c | 2 +-
+ gtk/gtkwindow.c | 6 +++---
+ 19 files changed, 57 insertions(+), 23 deletions(-)
+
+diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols
+index d4f2072..feed787 100644
+--- a/gdk/gdk.symbols
++++ b/gdk/gdk.symbols
+@@ -1169,6 +1169,7 @@ gdk_screen_get_default_colormap
+ gdk_screen_set_default_colormap
+ gdk_screen_get_n_monitors
+ gdk_screen_get_monitor_geometry
++gdk_screen_get_monitor_workarea
+ gdk_screen_get_monitor_width_mm
+ gdk_screen_get_monitor_height_mm
+ gdk_screen_get_monitor_plug_name
+diff --git a/gdk/gdkscreen.h b/gdk/gdkscreen.h
+index 418cecf..d3d4fe9 100644
+--- a/gdk/gdkscreen.h
++++ b/gdk/gdkscreen.h
+@@ -95,6 +95,9 @@ gint gdk_screen_get_primary_monitor (GdkScreen *screen);
+ void gdk_screen_get_monitor_geometry (GdkScreen *screen,
+ gint monitor_num,
+ GdkRectangle *dest);
++void gdk_screen_get_monitor_workarea (GdkScreen *screen,
++ gint monitor_num,
++ GdkRectangle *dest);
+ gint gdk_screen_get_monitor_at_point (GdkScreen *screen,
+ gint x,
+ gint y);
+diff --git a/gdk/quartz/gdkscreen-quartz.c b/gdk/quartz/gdkscreen-quartz.c
+index 796fcb5..4bb573b 100644
+--- a/gdk/quartz/gdkscreen-quartz.c
++++ b/gdk/quartz/gdkscreen-quartz.c
+@@ -464,6 +464,36 @@ gdk_screen_get_monitor_geometry (GdkScreen *screen,
+ *dest = GDK_SCREEN_QUARTZ (screen)->screen_rects[monitor_num];
+ }
+
++void
++gdk_screen_get_monitor_workarea (GdkScreen *screen,
++ gint monitor_num,
++ GdkRectangle *dest)
++{
++ GdkScreenQuartz *quartz_screen;
++ NSArray *array;
++ NSScreen *nsscreen;
++ NSRect rect;
++
++ g_return_if_fail (GDK_IS_SCREEN (screen));
++ g_return_if_fail (monitor_num < gdk_screen_get_n_monitors (screen));
++ g_return_if_fail (monitor_num >= 0);
++
++ quartz_screen = GDK_SCREEN_QUARTZ (screen);
++
++ GDK_QUARTZ_ALLOC_POOL;
++
++ array = [NSScreen screens];
++ nsscreen = [array objectAtIndex:monitor_num];
++ rect = [nsscreen visibleFrame];
++
++ dest->x = rect.origin.x - quartz_screen->min_x;
++ dest->y = quartz_screen->height - (rect.origin.y + rect.size.height) + quartz_screen->min_y;
++ dest->width = rect.size.width;
++ dest->height = rect.size.height;
++
++ GDK_QUARTZ_RELEASE_POOL;
++}
++
+ gchar *
+ gdk_screen_make_display_name (GdkScreen *screen)
+ {
+diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
+index dc912a3..d997d0d 100644
+--- a/gtk/gtkcombobox.c
++++ b/gtk/gtkcombobox.c
+@@ -1667,7 +1667,7 @@ gtk_combo_box_menu_position_below (GtkMenu *menu,
+ screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
+ monitor_num = gdk_screen_get_monitor_at_window (screen,
+ GTK_WIDGET (combo_box)->window);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ if (*x < monitor.x)
+ *x = monitor.x;
+@@ -1836,7 +1836,7 @@ gtk_combo_box_list_position (GtkComboBox *combo_box,
+ screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
+ monitor_num = gdk_screen_get_monitor_at_window (screen,
+ GTK_WIDGET (combo_box)->window);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ if (*x < monitor.x)
+ *x = monitor.x;
+diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
+index e2999a3..0d16d71 100644
+--- a/gtk/gtkentry.c
++++ b/gtk/gtkentry.c
+@@ -8614,7 +8614,7 @@ popup_position_func (GtkMenu *menu,
+ monitor_num = 0;
+ gtk_menu_set_monitor (menu, monitor_num);
+
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+ gtk_widget_size_request (entry->popup_menu, &menu_req);
+ height = gdk_window_get_height (entry->text_area);
+ gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL);
+diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
+index 2fa7b56..a8deace 100644
+--- a/gtk/gtkentrycompletion.c
++++ b/gtk/gtkentrycompletion.c
+@@ -1409,7 +1409,7 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
+ screen = gtk_widget_get_screen (GTK_WIDGET (completion->priv->entry));
+ monitor_num = gdk_screen_get_monitor_at_window (screen,
+ GTK_WIDGET (completion->priv->entry)->window);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+
+
+diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
+index 2a75365..f94f4d6 100644
+--- a/gtk/gtkfilechooserdefault.c
++++ b/gtk/gtkfilechooserdefault.c
+@@ -4100,7 +4100,7 @@ popup_position_func (GtkMenu *menu,
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+ gtk_menu_set_monitor (menu, monitor_num);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
+ *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
+diff --git a/gtk/gtklinkbutton.c b/gtk/gtklinkbutton.c
+index bae8ec3..45d64b2 100644
+--- a/gtk/gtklinkbutton.c
++++ b/gtk/gtklinkbutton.c
+@@ -374,7 +374,7 @@ popup_position_func (GtkMenu *menu,
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+ gtk_menu_set_monitor (menu, monitor_num);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
+ *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
+diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
+index fc25098..44972b2 100644
+--- a/gtk/gtkmenu.c
++++ b/gtk/gtkmenu.c
+@@ -993,7 +993,7 @@ gtk_menu_window_size_request (GtkWidget *window,
+ GdkScreen *screen = gtk_widget_get_screen (window);
+ GdkRectangle monitor;
+
+- gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, private->monitor_num, &monitor);
+
+ if (private->y + requisition->height > monitor.y + monitor.height)
+ requisition->height = monitor.y + monitor.height - private->y;
+@@ -4231,7 +4231,7 @@ gtk_menu_position (GtkMenu *menu)
+ if (private->monitor_num < 0)
+ private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
+
+- gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, private->monitor_num, &monitor);
+ }
+ else
+ {
+@@ -4260,7 +4260,7 @@ gtk_menu_position (GtkMenu *menu)
+ * Positioning in the vertical direction is similar: first try below
+ * mouse cursor, then above.
+ */
+- gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, private->monitor_num, &monitor);
+
+ space_left = x - monitor.x;
+ space_right = monitor.x + monitor.width - x - 1;
+diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c
+index 8f23b75..ffc2db6 100644
+--- a/gtk/gtkmenuitem.c
++++ b/gtk/gtkmenuitem.c
+@@ -1717,7 +1717,7 @@ gtk_menu_item_position_menu (GtkMenu *menu,
+ monitor_num = gdk_screen_get_monitor_at_window (screen, menu_item->event_window);
+ if (monitor_num < 0)
+ monitor_num = 0;
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ if (!gdk_window_get_origin (widget->window, &tx, &ty))
+ {
+diff --git a/gtk/gtkmenutoolbutton.c b/gtk/gtkmenutoolbutton.c
+index 0c464e8..e254e1b 100644
+--- a/gtk/gtkmenutoolbutton.c
++++ b/gtk/gtkmenutoolbutton.c
+@@ -287,7 +287,7 @@ menu_position_func (GtkMenu *menu,
+ monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
+ if (monitor_num < 0)
+ monitor_num = 0;
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+diff --git a/gtk/gtkrecentchooserdefault.c b/gtk/gtkrecentchooserdefault.c
+index 1ab48e2..fe2a772 100644
+--- a/gtk/gtkrecentchooserdefault.c
++++ b/gtk/gtkrecentchooserdefault.c
+@@ -951,7 +951,7 @@ set_default_size (GtkRecentChooserDefault *impl)
+ screen = gtk_widget_get_screen (widget);
+ monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
+
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ width = MIN (width, monitor.width * 3 / 4);
+ height = MIN (height, monitor.height * 3 / 4);
+@@ -1849,7 +1849,7 @@ popup_position_func (GtkMenu *menu,
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+ gtk_menu_set_monitor (menu, monitor_num);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
+ *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
+diff --git a/gtk/gtkscalebutton.c b/gtk/gtkscalebutton.c
+index df9b574..3c6fc85 100644
+--- a/gtk/gtkscalebutton.c
++++ b/gtk/gtkscalebutton.c
+@@ -1000,7 +1000,7 @@ gtk_scale_popup (GtkWidget *widget,
+ monitor = gdk_screen_get_monitor_at_point (screen,
+ button_event->x_root,
+ button_event->y_root);
+- gdk_screen_get_monitor_geometry (screen, monitor, &rect);
++ gdk_screen_get_monitor_workarea (screen, monitor, &rect);
+
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL)
+ y += button_event->y;
+diff --git a/gtk/gtkstatusicon.c b/gtk/gtkstatusicon.c
+index 77d93da..c6d3097 100644
+--- a/gtk/gtkstatusicon.c
++++ b/gtk/gtkstatusicon.c
+@@ -648,7 +648,7 @@ build_button_event (GtkStatusIconPrivate *priv,
+ GdkRectangle monitor0;
+
+ /* We know that gdk/win32 puts the primary monitor at index 0 */
+- gdk_screen_get_monitor_geometry (gdk_screen_get_default (), 0, &monitor0);
++ gdk_screen_get_monitor_workarea (gdk_screen_get_default (), 0, &monitor0);
+ e->window = g_object_ref (gdk_get_default_root_window ());
+ e->send_event = TRUE;
+ e->time = GetTickCount ();
+@@ -2512,7 +2512,7 @@ gtk_status_icon_position_menu (GtkMenu *menu,
+ monitor_num = 0;
+ gtk_menu_set_monitor (menu, monitor_num);
+
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ gdk_window_get_origin (widget->window, x, y);
+
+diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
+index 9ddddec..419cced 100644
+--- a/gtk/gtktextview.c
++++ b/gtk/gtktextview.c
+@@ -7845,7 +7845,7 @@ popup_position_func (GtkMenu *menu,
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+ gtk_menu_set_monitor (menu, monitor_num);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
+ *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
+diff --git a/gtk/gtktoolbar.c b/gtk/gtktoolbar.c
+index b2c4b15..fc1e588 100644
+--- a/gtk/gtktoolbar.c
++++ b/gtk/gtktoolbar.c
+@@ -2618,7 +2618,7 @@ menu_position_func (GtkMenu *menu,
+ monitor_num = gdk_screen_get_monitor_at_window (screen, priv->arrow_button->window);
+ if (monitor_num < 0)
+ monitor_num = 0;
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ gdk_window_get_origin (GTK_BUTTON (priv->arrow_button)->event_window, x, y);
+ if (toolbar->orientation == GTK_ORIENTATION_HORIZONTAL)
+diff --git a/gtk/gtktooltip.c b/gtk/gtktooltip.c
+index 9918165..fb6e869 100644
+--- a/gtk/gtktooltip.c
++++ b/gtk/gtktooltip.c
+@@ -1139,7 +1139,7 @@ gtk_tooltip_position (GtkTooltip *tooltip,
+ &requisition);
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ if (x + requisition.width > monitor.x + monitor.width)
+ x -= x - (monitor.x + monitor.width) + requisition.width;
+diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
+index eccd20c..cca1d3d 100644
+--- a/gtk/gtktreeview.c
++++ b/gtk/gtktreeview.c
+@@ -14251,7 +14251,7 @@ gtk_tree_view_search_position_func (GtkTreeView *tree_view,
+ GdkRectangle monitor;
+
+ monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+
+ gtk_widget_realize (search_dialog);
+
+diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
+index 8f174c6..7094eb6 100644
+--- a/gtk/gtkwindow.c
++++ b/gtk/gtkwindow.c
+@@ -5771,7 +5771,7 @@ center_window_on_monitor (GtkWindow *window,
+ if (monitor_num == -1)
+ monitor_num = get_center_monitor_of_window (window);
+
+- gdk_screen_get_monitor_geometry (gtk_window_check_screen (window),
++ gdk_screen_get_monitor_workarea (gtk_window_check_screen (window),
+ monitor_num, &monitor);
+
+ *x = (monitor.width - w) / 2 + monitor.x;
+@@ -5917,7 +5917,7 @@ gtk_window_compute_configure_request (GtkWindow *window,
+ */
+ if (monitor_num >= 0)
+ {
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+ clamp_window_to_rectangle (&x, &y, w, h, &monitor);
+ }
+ }
+@@ -5952,7 +5952,7 @@ gtk_window_compute_configure_request (GtkWindow *window,
+ */
+ if (monitor_num >= 0)
+ {
+- gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+ clamp_window_to_rectangle (&x, &y, w, h, &monitor);
+ }
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From df766e7dc27c8bc373dd5eaeaa5bddb7702605fd Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 23 Nov 2012 15:28:26 +0100
+Subject: [PATCH 05/68] gtk: don't scroll combo box menus if less than 3
+ itemss are visible
+
+---
+ gtk/gtkcombobox.c | 38 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+
+diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
+index d997d0d..3b58f32 100644
+--- a/gtk/gtkcombobox.c
++++ b/gtk/gtkcombobox.c
+@@ -1781,6 +1781,44 @@ gtk_combo_box_menu_position (GtkMenu *menu,
+ menu_item);
+
+ gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
++
++ if (menu_item)
++ {
++ GdkScreen *screen;
++ GtkWidget *widget = GTK_WIDGET (combo_box);
++ gint monitor_num;
++ GdkRectangle monitor;
++ gint px, py;
++ gint menu_height;
++ gint scroll_offset = 0;
++
++ screen = gtk_widget_get_screen (widget);
++ gdk_display_get_pointer (gdk_screen_get_display (screen),
++ NULL, &px, &py, NULL);
++
++ monitor_num = gdk_screen_get_monitor_at_point (screen, px, py);
++
++ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
++
++ menu_height = GTK_WIDGET (menu)->requisition.height;
++
++ if (*y + menu_height > monitor.y + monitor.height)
++ {
++ scroll_offset -= *y + menu_height - (monitor.y + monitor.height);
++ }
++ else if (*y < monitor.y)
++ {
++ scroll_offset += monitor.y - *y;
++ }
++
++ /* don't scroll the menu if less than 3 items would be visible,
++ * use 4 to roughly take the scroll buttons into account
++ */
++ if (scroll_offset != 0 &&
++ (menu->toplevel->requisition.height - ABS (scroll_offset) <
++ 5 * menu_item->requisition.height))
++ gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
++ }
+ }
+
+ if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->toplevel))
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 37f07504f203aec1345a75abeb07172259bd5973 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Sun, 27 May 2012 22:51:33 +0200
+Subject: [PATCH 06/68] gtk: paint only the exposed region in
+ gdk_window_expose()
+
+---
+ gtk/gtkwindow.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
+index 7094eb6..89c91da 100644
+--- a/gtk/gtkwindow.c
++++ b/gtk/gtkwindow.c
+@@ -6631,13 +6631,28 @@ gtk_window_paint (GtkWidget *widget,
+ GTK_SHADOW_NONE, area, widget, "base", 0, 0, -1, -1);
+ }
+
++static void
++gtk_window_paint_region (GtkWidget *widget,
++ GdkRegion *region)
++{
++ int i, n_rectangles;
++ GdkRectangle *rectangles = NULL;
++
++ gdk_region_get_rectangles (region, &rectangles, &n_rectangles);
++
++ for (i = 0; i < n_rectangles; i++)
++ gtk_window_paint (widget, &rectangles[i]);
++
++ g_free (rectangles);
++}
++
+ static gint
+ gtk_window_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+ {
+ if (!gtk_widget_get_app_paintable (widget))
+- gtk_window_paint (widget, &event->area);
+-
++ gtk_window_paint_region (widget, event->region);
++
+ if (GTK_WIDGET_CLASS (gtk_window_parent_class)->expose_event)
+ return GTK_WIDGET_CLASS (gtk_window_parent_class)->expose_event (widget, event);
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 4c79075b4d396ba009b0f0e70d05c1c253cc3da6 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Fri, 24 Aug 2012 12:12:04 +0200
+Subject: [PATCH 07/68] Implement gtk-enable-overlay-scrollbars GtkSetting
+
+And set this up such that on OS X
+NSPreferredScrollerDidChangeNotification is followed.
+---
+ gdk/quartz/gdkevents-quartz.c | 56 ++++++++++++++++++++++++++++++++++++++++-
+ gtk/gtksettings.c | 20 ++++++++++++++-
+ 2 files changed, 74 insertions(+), 2 deletions(-)
+
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 53f1962..c99a2c9 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -85,6 +85,10 @@ gdk_quartz_ns_notification_callback (CFNotificationCenterRef center,
+ CFSTR("AppleNoRedisplayAppearancePreferenceChanged"),
+ 0) == kCFCompareEqualTo)
+ new_event.setting.name = "gtk-primary-button-warps-slider";
++ else if (CFStringCompare (name,
++ CFSTR("NSPreferredScrollerStyleDidChangeNotification"),
++ 0) == kCFCompareEqualTo)
++ new_event.setting.name = "gtk-enable-overlay-scrollbars";
+
+ if (!new_event.setting.name)
+ return;
+@@ -114,6 +118,20 @@ gdk_quartz_events_init_notifications (void)
+ CFSTR ("AppleNoRedisplayAppearancePreferenceChanged"),
+ NULL,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
++
++ /* The preferred scroller notification and property are only available on Lion
++ * and higher. Also, beware that the notification will only start working after the
++ * property has been queried for the first time!.
++ */
++ if (gdk_quartz_osx_version () >= GDK_OSX_LION)
++ {
++ CFNotificationCenterAddObserver (CFNotificationCenterGetLocalCenter (),
++ NULL,
++ &gdk_quartz_ns_notification_callback,
++ CFSTR ("NSPreferredScrollerStyleDidChangeNotification"),
++ NULL,
++ CFNotificationSuspensionBehaviorDeliverImmediately);
++ }
+ }
+
+ void
+@@ -1686,7 +1704,43 @@ gdk_screen_get_setting (GdkScreen *screen,
+
+ return TRUE;
+ }
+-
++ else if (strcmp (name, "gtk-enable-overlay-scrollbars") == 0)
++ {
++ gboolean enabled = FALSE;
++
++ GDK_QUARTZ_ALLOC_POOL;
++
++ if (gdk_quartz_osx_version () >= GDK_OSX_LION)
++ {
++ /* Use an integer instead of NSScrollerStyle to allow things to be compiled
++ * on < 10.7 systems.
++ */
++ int setting = (int)[NSScroller preferredScrollerStyle];
++
++ if (setting == 1)
++ /* 1 == NSScrollerStyleOverlay */
++ enabled = TRUE;
++ else
++ enabled = FALSE;
++ }
++ else
++ {
++ /* On systems prior to Lion, default to legacy scrolling. */
++ enabled = FALSE;
++ }
++
++ g_value_set_boolean (value, enabled);
++
++ /* Initialize after quering the property for the first theme,
++ * notifications are otherwise not received!
++ */
++ gdk_quartz_events_init_notifications ();
++
++ GDK_QUARTZ_RELEASE_POOL;
++
++ return TRUE;
++ }
++
+ /* FIXME: Add more settings */
+
+ return FALSE;
+diff --git a/gtk/gtksettings.c b/gtk/gtksettings.c
+index 3fbbf00..1455db1 100644
+--- a/gtk/gtksettings.c
++++ b/gtk/gtksettings.c
+@@ -139,7 +139,8 @@ enum {
+ PROP_LABEL_SELECT_ON_FOCUS,
+ PROP_COLOR_PALETTE,
+ PROP_IM_PREEDIT_STYLE,
+- PROP_IM_STATUS_STYLE
++ PROP_IM_STATUS_STYLE,
++ PROP_ENABLE_OVERLAY_SCROLLBARS
+ };
+
+ /* --- prototypes --- */
+@@ -1205,6 +1206,23 @@ gtk_settings_class_init (GtkSettingsClass *class)
+ GTK_PARAM_READWRITE),
+ gtk_rc_property_parse_enum);
+ g_assert (result == PROP_IM_STATUS_STYLE);
++
++ /**
++ * GtkSettings:gtk-enable-overlay-scrollbars:
++ *
++ * Whether overlay scrollbars should be enabled.
++ *
++ * Since: Xamarin specific API.
++ */
++ result = settings_install_property_parser (class,
++ g_param_spec_boolean ("gtk-enable-overlay-scrollbars",
++ P_("Enable Overlay Scrollbars"),
++ P_("Whether to enable overlay scrollbars."),
++ FALSE,
++ GTK_PARAM_READWRITE),
++ NULL);
++
++ g_assert (result == PROP_ENABLE_OVERLAY_SCROLLBARS);
+ }
+
+ static void
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From ab426801023babba7513d9e27a4dbdd44bdbc318 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Tue, 8 May 2012 14:11:50 +0200
+Subject: [PATCH 08/68] Smooth scrolling
+
+---
+ gdk/gdkevents.c | 32 +++++++++++++++++
+ gdk/gdkevents.h | 6 ++++
+ gdk/gdkwindow.c | 3 ++
+ gdk/quartz/gdkevents-quartz.c | 80 +++++++++++++++++++++++++++++++----------
+ gtk/gtkrange.c | 51 +++++++++++++++++++-------
+ gtk/gtkrange.h | 4 +--
+ gtk/gtkscrolledwindow.c | 74 ++++++++++++++++++++++++++++++--------
+ 7 files changed, 203 insertions(+), 47 deletions(-)
+
+diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
+index 53833a0..0f8bba2 100644
+--- a/gdk/gdkevents.c
++++ b/gdk/gdkevents.c
+@@ -392,6 +392,8 @@ gdk_event_new (GdkEventType type)
+ new_event->scroll.y = 0.;
+ new_event->scroll.x_root = 0.;
+ new_event->scroll.y_root = 0.;
++ new_event->scroll.delta_x = 0.;
++ new_event->scroll.delta_y = 0.;
+ break;
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+@@ -845,6 +847,36 @@ gdk_event_get_root_coords (const GdkEvent *event,
+ return fetched;
+ }
+
++gboolean
++gdk_event_get_scroll_deltas (const GdkEvent *event,
++ gdouble *delta_x,
++ gdouble *delta_y)
++{
++ gboolean fetched = TRUE;
++ gdouble dx = 0.0;
++ gdouble dy = 0.0;
++
++ switch (event->type)
++ {
++ case GDK_SCROLL:
++ fetched = event->scroll.has_deltas;
++ dx = event->scroll.delta_x;
++ dy = event->scroll.delta_y;
++ break;
++ default:
++ fetched = FALSE;
++ break;
++ }
++
++ if (delta_x)
++ *delta_x = dx;
++
++ if (delta_y)
++ *delta_y = dy;
++
++ return fetched;
++}
++
+ /**
+ * gdk_event_get_axis:
+ * @event: a #GdkEvent
+diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
+index 0602bd0..f6b4e04 100644
+--- a/gdk/gdkevents.h
++++ b/gdk/gdkevents.h
+@@ -337,6 +337,9 @@ struct _GdkEventScroll
+ GdkScrollDirection direction;
+ GdkDevice *device;
+ gdouble x_root, y_root;
++ gboolean has_deltas;
++ gdouble delta_x;
++ gdouble delta_y;
+ };
+
+ struct _GdkEventKey
+@@ -537,6 +540,9 @@ gboolean gdk_event_get_coords (const GdkEvent *event,
+ gboolean gdk_event_get_root_coords (const GdkEvent *event,
+ gdouble *x_root,
+ gdouble *y_root);
++gboolean gdk_event_get_scroll_deltas (const GdkEvent *event,
++ gdouble *delta_x,
++ gdouble *delta_y);
+ gboolean gdk_event_get_axis (const GdkEvent *event,
+ GdkAxisUse axis_use,
+ gdouble *value);
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index f5f0339..d48751e 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -10800,6 +10800,9 @@ proxy_button_event (GdkEvent *source_event,
+ event->scroll.y_root = source_event->scroll.y_root;
+ event->scroll.state = state;
+ event->scroll.device = source_event->scroll.device;
++ event->scroll.has_deltas = source_event->scroll.has_deltas;
++ event->scroll.delta_x = source_event->scroll.delta_x;
++ event->scroll.delta_y = source_event->scroll.delta_y;
+ return TRUE;
+
+ default:
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index c99a2c9..e7d97dc 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -57,6 +57,13 @@ static GdkWindow *find_toplevel_under_pointer (GdkDisplay *display,
+ gint *x,
+ gint *y);
+
++/* Protocol to build cleanly for OSX < 10.7 */
++@protocol PreciseDeltas
++- (BOOL) hasPreciseScrollingDeltas;
++- (CGFloat) scrollingDeltaX;
++- (CGFloat) scrollingDeltaY;
++@end
++
+
+ NSEvent *
+ gdk_quartz_event_get_nsevent (GdkEvent *event)
+@@ -980,6 +987,9 @@ fill_scroll_event (GdkWindow *window,
+ gint y,
+ gint x_root,
+ gint y_root,
++ gboolean has_deltas,
++ gdouble delta_x,
++ gdouble delta_y,
+ GdkScrollDirection direction)
+ {
+ GdkWindowObject *private;
+@@ -999,6 +1009,9 @@ fill_scroll_event (GdkWindow *window,
+ event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
+ event->scroll.direction = direction;
+ event->scroll.device = _gdk_display->core_pointer;
++ event->scroll.has_deltas = has_deltas;
++ event->scroll.delta_x = delta_x;
++ event->scroll.delta_y = delta_y;
+ }
+
+ static void
+@@ -1471,28 +1484,59 @@ gdk_event_translate (GdkEvent *event,
+
+ case NSScrollWheel:
+ {
+- float dx = [nsevent deltaX];
+- float dy = [nsevent deltaY];
+- GdkScrollDirection direction;
+-
+- if (dy != 0)
+- {
+- if (dy < 0.0)
+- direction = GDK_SCROLL_DOWN;
+- else
+- direction = GDK_SCROLL_UP;
++ GdkScrollDirection direction;
++ float dx;
++ float dy;
+
+- fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
+- }
++ if (gdk_quartz_osx_version() >= GDK_OSX_LION &&
++ [(id <PreciseDeltas>) nsevent hasPreciseScrollingDeltas])
++ {
++ dx = [(id <PreciseDeltas>) nsevent scrollingDeltaX];
++ dy = [(id <PreciseDeltas>) nsevent scrollingDeltaY];
+
+- if (dx != 0)
+- {
+- if (dx < 0.0)
+- direction = GDK_SCROLL_RIGHT;
++ if (fabs (dy) > fabs (dx))
++ {
++ if (dy < 0.0)
++ direction = GDK_SCROLL_DOWN;
++ else
++ direction = GDK_SCROLL_UP;
++ }
+ else
+- direction = GDK_SCROLL_LEFT;
++ {
++ if (dx < 0.0)
++ direction = GDK_SCROLL_RIGHT;
++ else
++ direction = GDK_SCROLL_LEFT;
++ }
+
+- fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
++ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
++ TRUE, -dx, -dy, direction);
++ }
++ else
++ {
++ dx = [nsevent deltaX];
++ dy = [nsevent deltaY];
++
++ if (dy != 0.0)
++ {
++ if (dy < 0.0)
++ direction = GDK_SCROLL_DOWN;
++ else
++ direction = GDK_SCROLL_UP;
++
++ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
++ FALSE, 0.0, fabs (dy), direction);
++ }
++ else if (dx != 0.0)
++ {
++ if (dx < 0.0)
++ direction = GDK_SCROLL_RIGHT;
++ else
++ direction = GDK_SCROLL_LEFT;
++
++ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
++ FALSE, fabs (dx), 0.0, direction);
++ }
+ }
+ }
+ break;
+diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
+index 245fbf6..d39f045 100644
+--- a/gtk/gtkrange.c
++++ b/gtk/gtkrange.c
+@@ -2561,7 +2561,7 @@ gtk_range_button_release (GtkWidget *widget,
+ /**
+ * _gtk_range_get_wheel_delta:
+ * @range: a #GtkRange
+- * @direction: A #GdkScrollDirection
++ * @event: A #GdkEventScroll
+ *
+ * Returns a good step value for the mouse wheel.
+ *
+@@ -2570,27 +2570,52 @@ gtk_range_button_release (GtkWidget *widget,
+ * Since: 2.4
+ **/
+ gdouble
+-_gtk_range_get_wheel_delta (GtkRange *range,
+- GdkScrollDirection direction)
++_gtk_range_get_wheel_delta (GtkRange *range,
++ GdkEventScroll *event)
+ {
+ GtkAdjustment *adj = range->adjustment;
++ gdouble dx, dy;
+ gdouble delta;
+
+- if (GTK_IS_SCROLLBAR (range))
+- delta = pow (adj->page_size, 2.0 / 3.0);
++ if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &dx, &dy))
++ {
++ GtkAllocation allocation;
++
++ gtk_widget_get_allocation (GTK_WIDGET (range), &allocation);
++
++ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) == GTK_ORIENTATION_HORIZONTAL)
++ {
++ if (GTK_IS_SCROLLBAR (range) && adj->page_size > 0)
++ delta = dx * adj->page_size / allocation.width;
++ else
++ delta = dx * (adj->upper - adj->lower) / allocation.width;
++ }
++ else
++ {
++ if (GTK_IS_SCROLLBAR (range) && adj->page_size > 0)
++ delta = dy * adj->page_size / allocation.height;
++ else
++ delta = dy * (adj->upper - adj->lower) / allocation.height;
++ }
++ }
+ else
+- delta = adj->step_increment * 2;
+-
+- if (direction == GDK_SCROLL_UP ||
+- direction == GDK_SCROLL_LEFT)
+- delta = - delta;
+-
++ {
++ if (GTK_IS_SCROLLBAR (range))
++ delta = pow (adj->page_size, 2.0 / 3.0);
++ else
++ delta = adj->step_increment * 2;
++
++ if (event->direction == GDK_SCROLL_UP ||
++ event->direction == GDK_SCROLL_LEFT)
++ delta = - delta;
++ }
++
+ if (range->inverted)
+ delta = - delta;
+
+ return delta;
+ }
+-
++
+ static gboolean
+ gtk_range_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+@@ -2603,7 +2628,7 @@ gtk_range_scroll_event (GtkWidget *widget,
+ gdouble delta;
+ gboolean handled;
+
+- delta = _gtk_range_get_wheel_delta (range, event->direction);
++ delta = _gtk_range_get_wheel_delta (range, event);
+
+ g_signal_emit (range, signals[CHANGE_VALUE], 0,
+ GTK_SCROLL_JUMP, adj->value + delta,
+diff --git a/gtk/gtkrange.h b/gtk/gtkrange.h
+index 5463140..c672acb 100644
+--- a/gtk/gtkrange.h
++++ b/gtk/gtkrange.h
+@@ -199,8 +199,8 @@ gint gtk_range_get_round_digits (GtkRange *range
+
+
+ /* internal API */
+-gdouble _gtk_range_get_wheel_delta (GtkRange *range,
+- GdkScrollDirection direction);
++gdouble _gtk_range_get_wheel_delta (GtkRange *range,
++ GdkEventScroll *event);
+
+ void _gtk_range_set_stop_values (GtkRange *range,
+ gdouble *values,
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 1704d3c..2288b2f 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -1565,31 +1565,77 @@ static gboolean
+ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+ {
+- GtkWidget *range;
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ gboolean handled = FALSE;
++ gdouble delta_x;
++ gdouble delta_y;
+
+ g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+- if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
+- range = GTK_SCROLLED_WINDOW (widget)->vscrollbar;
+- else
+- range = GTK_SCROLLED_WINDOW (widget)->hscrollbar;
++ if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
++ {
++ if (delta_x != 0.0 && scrolled_window->hscrollbar &&
++ gtk_widget_get_visible (scrolled_window->hscrollbar))
++ {
++ GtkAdjustment *adj;
++ gdouble new_value;
++
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++
++ new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x,
++ gtk_adjustment_get_lower (adj),
++ gtk_adjustment_get_upper (adj) -
++ gtk_adjustment_get_page_size (adj));
++
++ gtk_adjustment_set_value (adj, new_value);
++
++ handled = TRUE;
++ }
++
++ if (delta_y != 0.0 && scrolled_window->vscrollbar &&
++ gtk_widget_get_visible (scrolled_window->vscrollbar))
++ {
++ GtkAdjustment *adj;
++ gdouble new_value;
++
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++
++ new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y,
++ gtk_adjustment_get_lower (adj),
++ gtk_adjustment_get_upper (adj) -
++ gtk_adjustment_get_page_size (adj));
+
+- if (range && gtk_widget_get_visible (range))
++ gtk_adjustment_set_value (adj, new_value);
++
++ handled = TRUE;
++ }
++ }
++ else
+ {
+- GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
+- gdouble delta, new_value;
++ GtkWidget *range;
+
+- delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event->direction);
++ if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
++ range = scrolled_window->vscrollbar;
++ else
++ range = scrolled_window->hscrollbar;
+
+- new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
+-
+- gtk_adjustment_set_value (adj, new_value);
++ if (range && gtk_widget_get_visible (range))
++ {
++ GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
++ gdouble delta, new_value;
+
+- return TRUE;
++ delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event);
++
++ new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
++
++ gtk_adjustment_set_value (adj, new_value);
++
++ handled = TRUE;
++ }
+ }
+
+- return FALSE;
++ return handled;
+ }
+
+ static gboolean
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From a484a0af1fc0ab98694c13970a308edeb52f39a6 Mon Sep 17 00:00:00 2001
+From: Carlos Garcia Campos <cgarcia@igalia.com>
+Date: Tue, 8 Feb 2011 14:49:31 +0100
+Subject: [PATCH 09/68] gtk: Add a way to do event capture
+
+This patch adds a capture phase to GTK+'s event propagation
+model. Events are first propagated from the toplevel (or the
+grab widget, if a grab is in place) down to the target widget
+ and then back up. The second phase is using the existing
+::event signal, the new capture phase is using a private
+API instead of a public signal for now.
+
+This mechanism can be used in many places where we currently
+have to prevent child widgets from getting events by putting
+an input-only window over them. It will also be used to implement
+kinetic scrolling in subsequent patches.
+
+http://bugzilla.gnome.org/show_bug.cgi?id=641836
+
+We automatically request more motion events in behalf of
+the original widget if it listens to motion hints. So
+the capturing widget doesn't need to handle such
+implementation details.
+
+We are not making event capture part of the public API for 3.4,
+which is why there is no ::captured-event signal.
+
+Conflicts:
+
+ gtk/gtkmain.c
+ gtk/gtkwidget.c
+ gtk/gtkwidgetprivate.h
+---
+ gtk/gtkmain.c | 268 +++++++++++++++++++++++++++++++++++-------------------
+ gtk/gtkprivate.h | 14 +++
+ gtk/gtkwidget.c | 53 +++++++++++
+ 3 files changed, 241 insertions(+), 94 deletions(-)
+
+diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
+index 56c92db..21345ed 100644
+--- a/gtk/gtkmain.c
++++ b/gtk/gtkmain.c
+@@ -1490,7 +1490,8 @@ void
+ gtk_main_do_event (GdkEvent *event)
+ {
+ GtkWidget *event_widget;
+- GtkWidget *grab_widget;
++ GtkWidget *grab_widget = NULL;
++ GtkWidget *topmost_widget = NULL;
+ GtkWindowGroup *window_group;
+ GdkEvent *rewritten_event = NULL;
+ GList *tmp_list;
+@@ -1552,7 +1553,14 @@ gtk_main_do_event (GdkEvent *event)
+ if (window_group->grabs)
+ {
+ grab_widget = window_group->grabs->data;
+-
++
++ /* Find out the topmost widget where captured event propagation
++ * should start, which is the widget holding the GTK+ grab
++ * if any, otherwise it's left NULL and events are emitted
++ * from the toplevel (or topmost parentless parent).
++ */
++ topmost_widget = grab_widget;
++
+ /* If the grab widget is an ancestor of the event widget
+ * then we send the event to the original event widget.
+ * This is the key to implementing modality.
+@@ -1636,14 +1644,16 @@ gtk_main_do_event (GdkEvent *event)
+ case GDK_WINDOW_STATE:
+ case GDK_GRAB_BROKEN:
+ case GDK_DAMAGE:
+- gtk_widget_event (event_widget, event);
++ if (!_gtk_widget_captured_event (event_widget, event))
++ gtk_widget_event (event_widget, event);
+ break;
+
+ case GDK_SCROLL:
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+- gtk_propagate_event (grab_widget, event);
++ if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
++ gtk_propagate_event (grab_widget, event);
+ break;
+
+ case GDK_KEY_PRESS:
+@@ -1682,19 +1692,22 @@ gtk_main_do_event (GdkEvent *event)
+ case GDK_BUTTON_RELEASE:
+ case GDK_PROXIMITY_IN:
+ case GDK_PROXIMITY_OUT:
+- gtk_propagate_event (grab_widget, event);
++ if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
++ gtk_propagate_event (grab_widget, event);
+ break;
+
+ case GDK_ENTER_NOTIFY:
+ GTK_PRIVATE_SET_FLAG (event_widget, GTK_HAS_POINTER);
+ _gtk_widget_set_pointer_window (event_widget, event->any.window);
+- if (gtk_widget_is_sensitive (grab_widget))
++ if (gtk_widget_is_sensitive (grab_widget) &&
++ !_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
+ gtk_widget_event (grab_widget, event);
+ break;
+
+ case GDK_LEAVE_NOTIFY:
+ GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_HAS_POINTER);
+- if (gtk_widget_is_sensitive (grab_widget))
++ if (gtk_widget_is_sensitive (grab_widget) &&
++ !_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
+ gtk_widget_event (grab_widget, event);
+ break;
+
+@@ -2400,44 +2413,96 @@ gtk_quit_invoke_function (GtkQuitFunction *quitf)
+ }
+ }
+
+-/**
+- * gtk_propagate_event:
+- * @widget: a #GtkWidget
+- * @event: an event
+- *
+- * Sends an event to a widget, propagating the event to parent widgets
+- * if the event remains unhandled. Events received by GTK+ from GDK
+- * normally begin in gtk_main_do_event(). Depending on the type of
+- * event, existence of modal dialogs, grabs, etc., the event may be
+- * propagated; if so, this function is used. gtk_propagate_event()
+- * calls gtk_widget_event() on each widget it decides to send the
+- * event to. So gtk_widget_event() is the lowest-level function; it
+- * simply emits the "event" and possibly an event-specific signal on a
+- * widget. gtk_propagate_event() is a bit higher-level, and
+- * gtk_main_do_event() is the highest level.
+- *
+- * All that said, you most likely don't want to use any of these
+- * functions; synthesizing events is rarely needed. Consider asking on
+- * the mailing list for better ways to achieve your goals. For
+- * example, use gdk_window_invalidate_rect() or
+- * gtk_widget_queue_draw() instead of making up expose events.
+- *
+- **/
+-void
+-gtk_propagate_event (GtkWidget *widget,
+- GdkEvent *event)
++static gboolean
++propagate_event_up (GtkWidget *widget,
++ GdkEvent *event,
++ GtkWidget *topmost)
+ {
+- gint handled_event;
+-
+- g_return_if_fail (GTK_IS_WIDGET (widget));
+- g_return_if_fail (event != NULL);
+-
+- handled_event = FALSE;
++ gboolean handled_event = FALSE;
+
+- g_object_ref (widget);
+-
+- if ((event->type == GDK_KEY_PRESS) ||
+- (event->type == GDK_KEY_RELEASE))
++ /* Propagate event up the widget tree so that
++ * parents can see the button and motion
++ * events of the children.
++ */
++ while (TRUE)
++ {
++ GtkWidget *tmp;
++
++ g_object_ref (widget);
++
++ /* Scroll events are special cased here because it
++ * feels wrong when scrolling a GtkViewport, say,
++ * to have children of the viewport eat the scroll
++ * event
++ */
++ if (!gtk_widget_is_sensitive (widget))
++ handled_event = event->type != GDK_SCROLL;
++ else
++ handled_event = gtk_widget_event (widget, event);
++
++ tmp = gtk_widget_get_parent (widget);
++ g_object_unref (widget);
++
++ if (widget == topmost)
++ break;
++
++ widget = tmp;
++
++ if (handled_event || !widget)
++ break;
++ }
++
++ return handled_event;
++}
++
++static gboolean
++propagate_event_down (GtkWidget *widget,
++ GdkEvent *event,
++ GtkWidget *topmost)
++{
++ gint handled_event = FALSE;
++ GList *widgets = NULL;
++ GList *l;
++
++ widgets = g_list_prepend (widgets, g_object_ref (widget));
++ while (widget && widget != topmost)
++ {
++ widget = gtk_widget_get_parent (widget);
++ if (!widget)
++ break;
++
++ widgets = g_list_prepend (widgets, g_object_ref (widget));
++
++ if (widget == topmost)
++ break;
++ }
++
++ for (l = widgets; l && !handled_event; l = g_list_next (l))
++ {
++ widget = (GtkWidget *)l->data;
++
++ if (!gtk_widget_is_sensitive (widget))
++ handled_event = TRUE;
++ else
++ handled_event = _gtk_widget_captured_event (widget, event);
++ }
++ g_list_free_full (widgets, (GDestroyNotify)g_object_unref);
++
++ return handled_event;
++}
++
++static gboolean
++propagate_event (GtkWidget *widget,
++ GdkEvent *event,
++ gboolean captured,
++ GtkWidget *topmost)
++{
++ gboolean handled_event = FALSE;
++ gboolean (* propagate_func) (GtkWidget *widget, GdkEvent *event);
++
++ propagate_func = captured ? _gtk_widget_captured_event : gtk_widget_event;
++
++ if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
+ {
+ /* Only send key events within Window widgets to the Window
+ * The Window widget will in turn pass the
+@@ -2448,60 +2513,75 @@ gtk_propagate_event (GtkWidget *widget,
+
+ window = gtk_widget_get_toplevel (widget);
+ if (GTK_IS_WINDOW (window))
+- {
+- /* If there is a grab within the window, give the grab widget
+- * a first crack at the key event
+- */
+- if (widget != window && gtk_widget_has_grab (widget))
+- handled_event = gtk_widget_event (widget, event);
+-
+- if (!handled_event)
+- {
+- window = gtk_widget_get_toplevel (widget);
+- if (GTK_IS_WINDOW (window))
+- {
+- if (gtk_widget_is_sensitive (window))
+- gtk_widget_event (window, event);
+- }
+- }
+-
+- handled_event = TRUE; /* don't send to widget */
+- }
++ {
++ g_object_ref (widget);
++ /* If there is a grab within the window, give the grab widget
++ * a first crack at the key event
++ */
++ if (widget != window && gtk_widget_has_grab (widget))
++ handled_event = propagate_func (widget, event);
++
++ if (!handled_event)
++ {
++ window = gtk_widget_get_toplevel (widget);
++ if (GTK_IS_WINDOW (window))
++ {
++ if (gtk_widget_is_sensitive (window))
++ handled_event = propagate_func (window, event);
++ }
++ }
++
++ g_object_unref (widget);
++ return handled_event;
++ }
+ }
+-
+- /* Other events get propagated up the widget tree
+- * so that parents can see the button and motion
+- * events of the children.
+- */
+- if (!handled_event)
+- {
+- while (TRUE)
+- {
+- GtkWidget *tmp;
+
+- /* Scroll events are special cased here because it
+- * feels wrong when scrolling a GtkViewport, say,
+- * to have children of the viewport eat the scroll
+- * event
+- */
+- if (!gtk_widget_is_sensitive (widget))
+- handled_event = event->type != GDK_SCROLL;
+- else
+- handled_event = gtk_widget_event (widget, event);
+-
+- tmp = widget->parent;
+- g_object_unref (widget);
++ /* Other events get propagated up/down the widget tree */
++ return captured ?
++ propagate_event_down (widget, event, topmost) :
++ propagate_event_up (widget, event, topmost);
++}
+
+- widget = tmp;
+-
+- if (!handled_event && widget)
+- g_object_ref (widget);
+- else
+- break;
+- }
+- }
+- else
+- g_object_unref (widget);
++/**
++ * gtk_propagate_event:
++ * @widget: a #GtkWidget
++ * @event: an event
++ *
++ * Sends an event to a widget, propagating the event to parent widgets
++ * if the event remains unhandled.
++ *
++ * Events received by GTK+ from GDK normally begin in gtk_main_do_event().
++ * Depending on the type of event, existence of modal dialogs, grabs, etc.,
++ * the event may be propagated; if so, this function is used.
++ *
++ * gtk_propagate_event() calls gtk_widget_event() on each widget it
++ * decides to send the event to. So gtk_widget_event() is the lowest-level
++ * function; it simply emits the #GtkWidget::event and possibly an
++ * event-specific signal on a widget. gtk_propagate_event() is a bit
++ * higher-level, and gtk_main_do_event() is the highest level.
++ *
++ * All that said, you most likely don't want to use any of these
++ * functions; synthesizing events is rarely needed. There are almost
++ * certainly better ways to achieve your goals. For example, use
++ * gdk_window_invalidate_rect() or gtk_widget_queue_draw() instead
++ * of making up expose events.
++ */
++void
++gtk_propagate_event (GtkWidget *widget,
++ GdkEvent *event)
++{
++ g_return_if_fail (GTK_IS_WIDGET (widget));
++ g_return_if_fail (event != NULL);
++
++ propagate_event (widget, event, FALSE, NULL);
++}
++
++gboolean
++_gtk_propagate_captured_event (GtkWidget *widget,
++ GdkEvent *event,
++ GtkWidget *topmost)
++{
++ return propagate_event (widget, event, TRUE, topmost);
+ }
+
+ #if 0
+diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
+index 6386c32..3865a67 100644
+--- a/gtk/gtkprivate.h
++++ b/gtk/gtkprivate.h
+@@ -152,6 +152,20 @@ gboolean _gtk_translate_keyboard_accel_state (GdkKeymap *keymap,
+ GdkModifierType *consumed_modifiers);
+
+
++gboolean _gtk_propagate_captured_event (GtkWidget *widget,
++ GdkEvent *event,
++ GtkWidget *topmost);
++
++typedef gboolean (*GtkCapturedEventHandler) (GtkWidget *widget, GdkEvent *event);
++
++void _gtk_widget_set_captured_event_handler (GtkWidget *widget,
++ GtkCapturedEventHandler handler);
++
++gboolean _gtk_widget_captured_event (GtkWidget *widget,
++ GdkEvent *event);
++
++
++
+ G_END_DECLS
+
+ #endif /* __GTK_PRIVATE_H__ */
+diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
+index 1d1f6bb..8e38ee1 100644
+--- a/gtk/gtkwidget.c
++++ b/gtk/gtkwidget.c
+@@ -351,6 +351,7 @@ static void gtk_widget_set_usize_internal (GtkWidget *widget,
+ gint height);
+ static void gtk_widget_get_draw_rectangle (GtkWidget *widget,
+ GdkRectangle *rect);
++static gboolean event_window_is_still_viewable (GdkEvent *event);
+
+
+ /* --- variables --- */
+@@ -4807,6 +4808,58 @@ gtk_widget_event (GtkWidget *widget,
+ return gtk_widget_event_internal (widget, event);
+ }
+
++void
++_gtk_widget_set_captured_event_handler (GtkWidget *widget,
++ GtkCapturedEventHandler callback)
++{
++ g_object_set_data (G_OBJECT (widget), "captured-event-handler", callback);
++}
++
++gboolean
++_gtk_widget_captured_event (GtkWidget *widget,
++ GdkEvent *event)
++{
++ gboolean return_val = FALSE;
++ GtkCapturedEventHandler handler;
++
++ g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
++ g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE);
++
++ if (event->type == GDK_EXPOSE)
++ {
++ g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get "
++ "the same effect, call gdk_window_invalidate_rect/region(), "
++ "followed by gdk_window_process_updates().");
++ return TRUE;
++ }
++
++ if (!event_window_is_still_viewable (event))
++ return TRUE;
++
++ handler = g_object_get_data (G_OBJECT (widget), "captured-event-handler");
++ if (!handler)
++ return FALSE;
++
++ g_object_ref (widget);
++
++ return_val = handler (widget, event);
++ return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
++
++ /* The widget that was originally to receive the event
++ * handles motion hints, but the capturing widget might
++ * not, so ensure we get further motion events.
++ */
++ if (return_val &&
++ event->type == GDK_MOTION_NOTIFY &&
++ event->motion.is_hint &&
++ (gdk_window_get_events (event->any.window) &
++ GDK_POINTER_MOTION_HINT_MASK) != 0)
++ gdk_event_request_motions (&event->motion);
++
++ g_object_unref (widget);
++
++ return return_val;
++}
+
+ /**
+ * gtk_widget_send_expose:
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From c0447e2741b3f9c966e337e6c7baf34cb66b0591 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@lanedo.com>
+Date: Thu, 22 Nov 2012 13:37:58 +0100
+Subject: [PATCH 10/68] gtk: don't let insensitive children eat scroll events
+ when bubbling down
+
+When event capturing is enabled, stop propagating scroll events
+at insensitive widgets, but don't handle them (don't return TRUE),
+so they can bubble up again and reach their handling widgets.
+---
+ gtk/gtkmain.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
+index 21345ed..5ca0993 100644
+--- a/gtk/gtkmain.c
++++ b/gtk/gtkmain.c
+@@ -2482,7 +2482,15 @@ propagate_event_down (GtkWidget *widget,
+ widget = (GtkWidget *)l->data;
+
+ if (!gtk_widget_is_sensitive (widget))
+- handled_event = TRUE;
++ {
++ /* stop propagating on SCROLL, but don't handle the event, so it
++ * can propagate up again and reach its handling widget
++ */
++ if (event->type == GDK_SCROLL)
++ break;
++ else
++ handled_event = TRUE;
++ }
+ else
+ handled_event = _gtk_widget_captured_event (widget, event);
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 5001b3d9a9b6445ac199156c0f91f28c2112e262 Mon Sep 17 00:00:00 2001
+From: Carlos Garcia Campos <cgarcia@igalia.com>
+Date: Fri, 11 Feb 2011 13:43:56 +0100
+Subject: [PATCH 11/68] scrolledwindow: Kinetic scrolling support
+
+If the scrolling doesn't start after a long press, the scrolling is
+cancelled and events are handled by child widgets normally.
+
+When clicked again close to the previous button press location
+(assuming it had ~0 movement), the scrolled window will allow
+the child to handle the events immediately.
+
+This is so the user doesn't have to wait to the press-and-hold
+timeout in order to operate on the scrolledwindow child.
+
+The innermost scrolled window always gets to capture the events, all
+scrolled windows above it just let the event go through. Ideally
+reaching a limit on the innermost scrolled window would propagate
+the dragging up the hierarchy in order to keep following the touch
+coords, although that'd involve rather evil hacks just to cater
+for broken UIs.
+
+Conflicts:
+
+ docs/reference/gtk/gtk3-sections.txt
+ gtk/gtk.symbols
+ gtk/gtkscrolledwindow.c
+ gtk/gtkscrolledwindow.h
+---
+ gtk/gtk.symbols | 4 +
+ gtk/gtkscrolledwindow.c | 1061 ++++++++++++++++++++++++++++++++++++++++++++++-
+ gtk/gtkscrolledwindow.h | 8 +
+ 3 files changed, 1058 insertions(+), 15 deletions(-)
+
+diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
+index 6d5d5b6..d13ca41 100644
+--- a/gtk/gtk.symbols
++++ b/gtk/gtk.symbols
+@@ -3730,6 +3730,8 @@ gtk_scrollbar_get_type G_GNUC_CONST
+ gtk_scrolled_window_add_with_viewport
+ gtk_scrolled_window_get_hadjustment
+ gtk_scrolled_window_get_hscrollbar
++gtk_scrolled_window_get_kinetic_scrolling
++gtk_scrolled_window_get_capture_button_press
+ gtk_scrolled_window_get_placement
+ gtk_scrolled_window_get_policy
+ gtk_scrolled_window_get_shadow_type
+@@ -3738,6 +3740,8 @@ gtk_scrolled_window_get_vadjustment
+ gtk_scrolled_window_get_vscrollbar
+ gtk_scrolled_window_new
+ gtk_scrolled_window_set_hadjustment
++gtk_scrolled_window_set_kinetic_scrolling
++gtk_scrolled_window_set_capture_button_press
+ gtk_scrolled_window_set_placement
+ gtk_scrolled_window_set_policy
+ gtk_scrolled_window_set_shadow_type
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 2288b2f..694d20a 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -33,6 +33,8 @@
+ #include "gtkwindow.h"
+ #include "gtkprivate.h"
+ #include "gtkintl.h"
++#include "gtkmain.h"
++#include "gtkdnd.h"
+ #include "gtkalias.h"
+
+
+@@ -72,14 +74,58 @@
+ */
+
+ #define DEFAULT_SCROLLBAR_SPACING 3
++#define TOUCH_BYPASS_CAPTURED_THRESHOLD 30
++
++/* Kinetic scrolling */
++#define FRAME_INTERVAL (1000 / 60)
++#define MAX_OVERSHOOT_DISTANCE 50
++#define FRICTION_DECELERATION 0.003
++#define OVERSHOOT_INVERSE_ACCELERATION 0.003
++#define RELEASE_EVENT_TIMEOUT 1000
+
+ typedef struct {
+- gboolean window_placement_set;
+- GtkCornerType real_window_placement;
++ gboolean window_placement_set;
++ GtkCornerType real_window_placement;
++
++ /* Kinetic scrolling */
++ GdkEvent *button_press_event;
++ GdkWindow *overshoot_window;
++ guint pointer_grabbed : 1;
++ guint kinetic_scrolling : 1;
++ guint capture_button_press : 1;
++ guint in_drag : 1;
++ guint last_button_event_valid : 1;
++
++ guint release_timeout_id;
++ guint deceleration_id;
++
++ gdouble last_button_event_x_root;
++ gdouble last_button_event_y_root;
++
++ gdouble last_motion_event_x_root;
++ gdouble last_motion_event_y_root;
++ guint32 last_motion_event_time;
++
++ gdouble x_velocity;
++ gdouble y_velocity;
++
++ gdouble unclamped_hadj_value;
++ gdouble unclamped_vadj_value;
+ } GtkScrolledWindowPrivate;
+
+ #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate))
+
++typedef struct
++{
++ GtkScrolledWindow *scrolled_window;
++ gint64 last_deceleration_time;
++
++ gdouble x_velocity;
++ gdouble y_velocity;
++ gdouble vel_cosine;
++ gdouble vel_sine;
++} KineticScrollData;
++
+ enum {
+ PROP_0,
+ PROP_HADJUSTMENT,
+@@ -88,7 +134,8 @@ enum {
+ PROP_VSCROLLBAR_POLICY,
+ PROP_WINDOW_PLACEMENT,
+ PROP_WINDOW_PLACEMENT_SET,
+- PROP_SHADOW_TYPE
++ PROP_SHADOW_TYPE,
++ PROP_KINETIC_SCROLLING
+ };
+
+ /* Signals */
+@@ -119,6 +166,8 @@ static void gtk_scrolled_window_size_allocate (GtkWidget *widge
+ GtkAllocation *allocation);
+ static gboolean gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event);
++static gboolean gtk_scrolled_window_captured_event (GtkWidget *widget,
++ GdkEvent *event);
+ static gboolean gtk_scrolled_window_focus (GtkWidget *widget,
+ GtkDirectionType direction);
+ static void gtk_scrolled_window_add (GtkContainer *container,
+@@ -139,9 +188,24 @@ static void gtk_scrolled_window_relative_allocation(GtkWidget *widge
+ GtkAllocation *allocation);
+ static void gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data);
++static void gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
++ gpointer data);
+
+ static void gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window);
+
++static void gtk_scrolled_window_realize (GtkWidget *widget);
++static void gtk_scrolled_window_unrealize (GtkWidget *widget);
++static void gtk_scrolled_window_map (GtkWidget *widget);
++static void gtk_scrolled_window_unmap (GtkWidget *widget);
++static void gtk_scrolled_window_grab_notify (GtkWidget *widget,
++ gboolean was_grabbed);
++
++static gboolean _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
++ GtkAdjustment *adjustment,
++ gdouble value,
++ gboolean allow_overshooting,
++ gboolean snap_to_border);
++
+ static guint signals[LAST_SIGNAL] = {0};
+
+ G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
+@@ -202,6 +266,11 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
+ widget_class->size_allocate = gtk_scrolled_window_size_allocate;
+ widget_class->scroll_event = gtk_scrolled_window_scroll_event;
+ widget_class->focus = gtk_scrolled_window_focus;
++ widget_class->realize = gtk_scrolled_window_realize;
++ widget_class->unrealize = gtk_scrolled_window_unrealize;
++ widget_class->map = gtk_scrolled_window_map;
++ widget_class->unmap = gtk_scrolled_window_unmap;
++ widget_class->grab_notify = gtk_scrolled_window_grab_notify;
+
+ container_class->add = gtk_scrolled_window_add;
+ container_class->remove = gtk_scrolled_window_remove;
+@@ -301,6 +370,22 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
+ GTK_PARAM_READABLE));
+
+ /**
++ * GtkScrolledWindow:kinetic-scrolling:
++ *
++ * The kinetic scrolling behavior flags.
++ *
++ * Since: X.XX
++ */
++ g_object_class_install_property (gobject_class,
++ PROP_KINETIC_SCROLLING,
++ g_param_spec_boolean ("kinetic-scrolling",
++ P_("Kinetic Scrolling"),
++ P_("Kinetic scrolling mode."),
++ TRUE,
++ GTK_PARAM_READABLE |
++ GTK_PARAM_WRITABLE));
++
++ /**
+ * GtkScrolledWindow::scroll-child:
+ * @scrolled_window: a #GtkScrolledWindow
+ * @scroll: a #GtkScrollType describing how much to scroll
+@@ -371,6 +456,12 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ scrolled_window->focus_out = FALSE;
+ scrolled_window->window_placement = GTK_CORNER_TOP_LEFT;
+ gtk_scrolled_window_update_real_placement (scrolled_window);
++
++ if (g_getenv ("GTK2_KINETIC_SCROLLING"))
++ {
++ gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
++ gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
++ }
+ }
+
+ /**
+@@ -458,8 +549,13 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ "changed",
+ G_CALLBACK (gtk_scrolled_window_adjustment_changed),
+ scrolled_window);
++ g_signal_connect (hadjustment,
++ "value-changed",
++ G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
++ scrolled_window);
+ gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
+-
++ gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
++
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+@@ -519,7 +615,12 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ "changed",
+ G_CALLBACK (gtk_scrolled_window_adjustment_changed),
+ scrolled_window);
++ g_signal_connect (vadjustment,
++ "value-changed",
++ G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
++ scrolled_window);
+ gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
++ gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
+
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+@@ -842,10 +943,135 @@ gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
+ return scrolled_window->shadow_type;
+ }
+
++/**
++ * gtk_scrolled_window_set_kinetic_scrolling:
++ * @scrolled_window: a #GtkScrolledWindow
++ * @kinetic_scrolling: %TRUE to enable kinetic scrolling
++ *
++ * Turns kinetic scrolling on or off.
++ * Kinetic scrolling only applies to devices with source
++ * %GDK_SOURCE_TOUCHSCREEN.
++ *
++ * Since: X.XX
++ **/
++void
++gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
++ gboolean kinetic_scrolling)
++{
++ GtkScrolledWindowPrivate *priv;
++
++ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ if (priv->kinetic_scrolling == kinetic_scrolling)
++ return;
++
++ priv->kinetic_scrolling = kinetic_scrolling;
++ if (priv->kinetic_scrolling)
++ {
++ _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window),
++ gtk_scrolled_window_captured_event);
++ }
++ else
++ {
++ _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window), NULL);
++ if (priv->release_timeout_id)
++ {
++ g_source_remove (priv->release_timeout_id);
++ priv->release_timeout_id = 0;
++ }
++ if (priv->deceleration_id)
++ {
++ g_source_remove (priv->deceleration_id);
++ priv->deceleration_id = 0;
++ }
++ }
++ g_object_notify (G_OBJECT (scrolled_window), "kinetic-scrolling");
++}
++
++/**
++ * gtk_scrolled_window_get_kinetic_scrolling:
++ * @scrolled_window: a #GtkScrolledWindow
++ *
++ * Returns the specified kinetic scrolling behavior.
++ *
++ * Return value: the scrolling behavior flags.
++ *
++ * Since: X.XX
++ */
++gboolean
++gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv;
++
++ g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ return priv->kinetic_scrolling;
++}
++
++/**
++ * gtk_scrolled_window_set_capture_button_press:
++ * @scrolled_window: a #GtkScrolledWindow
++ * @capture_button_press: %TRUE to capture button presses
++ *
++ * Changes the behaviour of @scrolled_window wrt. to the initial
++ * event that possibly starts kinetic scrolling. When @capture_button_press
++ * is set to %TRUE, the event is captured by the scrolled window, and
++ * then later replayed if it is meant to go to the child widget.
++ *
++ * This should be enabled if any child widgets perform non-reversible
++ * actions on #GtkWidget::button-press-event. If they don't, and handle
++ * additionally handle #GtkWidget::grab-broken-event, it might be better
++ * to set @capture_button_press to %FALSE.
++ *
++ * This setting only has an effect if kinetic scrolling is enabled.
++ *
++ * Since: X.XX
++ */
++void
++gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
++ gboolean capture_button_press)
++{
++ GtkScrolledWindowPrivate *priv;
++
++ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ priv->capture_button_press = capture_button_press;
++}
++
++/**
++ * gtk_scrolled_window_get_capture_button_press:
++ * @scrolled_window: a #GtkScrolledWindow
++ *
++ * Return whether button presses are captured during kinetic
++ * scrolling. See gtk_scrolled_window_set_capture_button_press().
++ *
++ * Returns: %TRUE if button presses are captured during kinetic scrolling
++ *
++ * Since: X.XX
++ */
++gboolean
++gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv;
++
++ g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ return priv->capture_button_press;
++}
++
++
+ static void
+ gtk_scrolled_window_destroy (GtkObject *object)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+ if (scrolled_window->hscrollbar)
+ {
+@@ -868,6 +1094,23 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ scrolled_window->vscrollbar = NULL;
+ }
+
++ if (priv->release_timeout_id)
++ {
++ g_source_remove (priv->release_timeout_id);
++ priv->release_timeout_id = 0;
++ }
++ if (priv->deceleration_id)
++ {
++ g_source_remove (priv->deceleration_id);
++ priv->deceleration_id = 0;
++ }
++
++ if (priv->button_press_event)
++ {
++ gdk_event_free (priv->button_press_event);
++ priv->button_press_event = NULL;
++ }
++
+ GTK_OBJECT_CLASS (gtk_scrolled_window_parent_class)->destroy (object);
+ }
+
+@@ -912,6 +1155,10 @@ gtk_scrolled_window_set_property (GObject *object,
+ gtk_scrolled_window_set_shadow_type (scrolled_window,
+ g_value_get_enum (value));
+ break;
++ case PROP_KINETIC_SCROLLING:
++ gtk_scrolled_window_set_kinetic_scrolling (scrolled_window,
++ g_value_get_boolean (value));
++ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -952,6 +1199,9 @@ gtk_scrolled_window_get_property (GObject *object,
+ case PROP_SHADOW_TYPE:
+ g_value_set_enum (value, scrolled_window->shadow_type);
+ break;
++ case PROP_KINETIC_SCROLLING:
++ g_value_set_boolean (value, priv->kinetic_scrolling);
++ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -1019,10 +1269,10 @@ gtk_scrolled_window_paint (GtkWidget *widget,
+ GdkRectangle *area)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkAllocation relative_allocation;
+
+ if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
+ {
+- GtkAllocation relative_allocation;
+ gboolean scrollbars_within_bevel;
+
+ gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
+@@ -1378,6 +1628,111 @@ gtk_scrolled_window_relative_allocation (GtkWidget *widget,
+ }
+ }
+
++static gboolean
++_gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
++ gint *overshoot_x,
++ gint *overshoot_y)
++{
++ GtkScrolledWindowPrivate *priv;
++ GtkAdjustment *vadjustment, *hadjustment;
++ gdouble lower, upper, x, y;
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ /* Vertical overshoot */
++ vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++ lower = gtk_adjustment_get_lower (vadjustment);
++ upper = gtk_adjustment_get_upper (vadjustment) -
++ gtk_adjustment_get_page_size (vadjustment);
++
++ if (priv->unclamped_vadj_value < lower)
++ y = priv->unclamped_vadj_value - lower;
++ else if (priv->unclamped_vadj_value > upper)
++ y = priv->unclamped_vadj_value - upper;
++ else
++ y = 0;
++
++ /* Horizontal overshoot */
++ hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++ lower = gtk_adjustment_get_lower (hadjustment);
++ upper = gtk_adjustment_get_upper (hadjustment) -
++ gtk_adjustment_get_page_size (hadjustment);
++
++ if (priv->unclamped_hadj_value < lower)
++ x = priv->unclamped_hadj_value - lower;
++ else if (priv->unclamped_hadj_value > upper)
++ x = priv->unclamped_hadj_value - upper;
++ else
++ x = 0;
++
++ if (overshoot_x)
++ *overshoot_x = x;
++
++ if (overshoot_y)
++ *overshoot_y = y;
++
++ return (x != 0 || y != 0);
++}
++
++static void
++_gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_window)
++{
++ GtkAllocation window_allocation, relative_allocation, allocation;
++ GtkScrolledWindowPrivate *priv;
++ GtkWidget *widget = GTK_WIDGET (scrolled_window);
++ gint overshoot_x, overshoot_y;
++
++ if (!gtk_widget_get_realized (widget))
++ return;
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ gtk_widget_get_allocation (widget, &allocation);
++ gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &overshoot_x, &overshoot_y);
++
++ window_allocation = relative_allocation;
++ window_allocation.x += allocation.x;
++ window_allocation.y += allocation.y;
++
++ if (overshoot_x < 0)
++ window_allocation.x += -overshoot_x;
++
++ if (overshoot_y < 0)
++ window_allocation.y += -overshoot_y;
++
++ window_allocation.width -= ABS (overshoot_x);
++ window_allocation.height -= ABS (overshoot_y);
++
++ gdk_window_move_resize (priv->overshoot_window,
++ window_allocation.x, window_allocation.y,
++ window_allocation.width, window_allocation.height);
++}
++
++static void
++gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
++ GtkAllocation *relative_allocation)
++{
++ GtkWidget *widget = GTK_WIDGET (swindow), *child;
++ GtkAllocation allocation;
++ GtkAllocation child_allocation;
++ gint overshoot_x, overshoot_y;
++
++ child = gtk_bin_get_child (GTK_BIN (widget));
++
++ gtk_widget_get_allocation (widget, &allocation);
++
++ gtk_scrolled_window_relative_allocation (widget, relative_allocation);
++ _gtk_scrolled_window_get_overshoot (swindow, &overshoot_x, &overshoot_y);
++
++ child_allocation.x = child_allocation.y = 0;
++ child_allocation.width = relative_allocation->width;
++ child_allocation.height = relative_allocation->height;
++
++ gtk_widget_size_allocate (child, &child_allocation);
++}
++
+ static void
+ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+@@ -1389,7 +1744,7 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ GtkAllocation child_allocation;
+ gboolean scrollbars_within_bevel;
+ gint scrollbar_spacing;
+-
++
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
+ g_return_if_fail (allocation != NULL);
+
+@@ -1420,17 +1775,9 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+
+ do
+ {
+- gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
+-
+- child_allocation.x = relative_allocation.x + allocation->x;
+- child_allocation.y = relative_allocation.y + allocation->y;
+- child_allocation.width = relative_allocation.width;
+- child_allocation.height = relative_allocation.height;
+-
+ previous_hvis = scrolled_window->hscrollbar_visible;
+ previous_vvis = scrolled_window->vscrollbar_visible;
+-
+- gtk_widget_size_allocate (bin->child, &child_allocation);
++ gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
+
+ /* If, after the first iteration, the hscrollbar and the
+ * vscrollbar flip visiblity, then we need both.
+@@ -1442,6 +1789,8 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ scrolled_window->hscrollbar_visible = TRUE;
+ scrolled_window->vscrollbar_visible = TRUE;
+
++ gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
++
+ /* a new resize is already queued at this point,
+ * so we will immediatedly get reinvoked
+ */
+@@ -1559,6 +1908,8 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ }
+ else if (gtk_widget_get_visible (scrolled_window->vscrollbar))
+ gtk_widget_hide (scrolled_window->vscrollbar);
++
++ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
+ }
+
+ static gboolean
+@@ -1639,6 +1990,555 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ }
+
+ static gboolean
++_gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
++ GtkAdjustment *adjustment,
++ gdouble value,
++ gboolean allow_overshooting,
++ gboolean snap_to_border)
++{
++ GtkScrolledWindowPrivate *priv;
++ gdouble lower, upper, *prev_value;
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ lower = gtk_adjustment_get_lower (adjustment);
++ upper = gtk_adjustment_get_upper (adjustment) -
++ gtk_adjustment_get_page_size (adjustment);
++
++ if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)))
++ prev_value = &priv->unclamped_hadj_value;
++ else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)))
++ prev_value = &priv->unclamped_vadj_value;
++ else
++ return FALSE;
++
++ if (snap_to_border)
++ {
++ if (*prev_value < 0 && value > 0)
++ value = 0;
++ else if (*prev_value > upper && value < upper)
++ value = upper;
++ }
++
++ if (allow_overshooting)
++ {
++ lower -= MAX_OVERSHOOT_DISTANCE;
++ upper += MAX_OVERSHOOT_DISTANCE;
++ }
++
++ *prev_value = CLAMP (value, lower, upper);
++ gtk_adjustment_set_value (adjustment, *prev_value);
++
++ return (*prev_value != value);
++}
++
++static gboolean
++scrolled_window_deceleration_cb (gpointer user_data)
++{
++ KineticScrollData *data = user_data;
++ GtkScrolledWindow *scrolled_window = data->scrolled_window;
++ GtkScrolledWindowPrivate *priv;
++ GtkAdjustment *hadjustment, *vadjustment;
++ gint old_overshoot_x, old_overshoot_y, overshoot_x, overshoot_y;
++ gdouble value;
++ gint64 current_time;
++ guint elapsed;
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++ vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &old_overshoot_x, &old_overshoot_y);
++
++ current_time = g_get_monotonic_time ();
++ elapsed = (current_time - data->last_deceleration_time) / 1000;
++ data->last_deceleration_time = current_time;
++
++ if (hadjustment && scrolled_window->hscrollbar_visible)
++ {
++ value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
++
++ if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
++ hadjustment,
++ value, TRUE, TRUE))
++ data->x_velocity = 0;
++ }
++ else
++ data->x_velocity = 0;
++
++ if (vadjustment && scrolled_window->vscrollbar_visible)
++ {
++ value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
++
++ if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
++ vadjustment,
++ value, TRUE, TRUE))
++ data->y_velocity = 0;
++ }
++ else
++ data->y_velocity = 0;
++
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &overshoot_x, &overshoot_y);
++
++ if (overshoot_x == 0)
++ {
++ if (old_overshoot_x != 0)
++ {
++ /* Overshooting finished snapping back */
++ data->x_velocity = 0;
++ }
++ else if (data->x_velocity > 0)
++ {
++ data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
++ data->x_velocity = MAX (0, data->x_velocity);
++ }
++ else if (data->x_velocity < 0)
++ {
++ data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
++ data->x_velocity = MIN (0, data->x_velocity);
++ }
++ }
++ else if (overshoot_x < 0)
++ data->x_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
++ else if (overshoot_x > 0)
++ data->x_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
++
++ if (overshoot_y == 0)
++ {
++ if (old_overshoot_y != 0)
++ {
++ /* Overshooting finished snapping back */
++ data->y_velocity = 0;
++ }
++ else if (data->y_velocity > 0)
++ {
++ data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
++ data->y_velocity = MAX (0, data->y_velocity);
++ }
++ else if (data->y_velocity < 0)
++ {
++ data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
++ data->y_velocity = MIN (0, data->y_velocity);
++ }
++ }
++ else if (overshoot_y < 0)
++ data->y_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
++ else if (overshoot_y > 0)
++ data->y_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
++
++ if (old_overshoot_x != overshoot_x ||
++ old_overshoot_y != overshoot_y)
++ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
++
++ if (overshoot_x != 0 || overshoot_y != 0 ||
++ data->x_velocity != 0 || data->y_velocity != 0)
++ return TRUE;
++ else
++ {
++ priv->deceleration_id = 0;
++ return FALSE;
++ }
++}
++
++static void
++gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ if (priv->deceleration_id)
++ {
++ g_source_remove (priv->deceleration_id);
++ priv->deceleration_id = 0;
++ }
++}
++
++static void
++gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ KineticScrollData *data;
++ gdouble angle;
++
++ data = g_new0 (KineticScrollData, 1);
++ data->scrolled_window = scrolled_window;
++ data->last_deceleration_time = g_get_monotonic_time ();
++ data->x_velocity = priv->x_velocity;
++ data->y_velocity = priv->y_velocity;
++
++ /* We use sine/cosine as a factor to deceleration x/y components
++ * of the vector, so we care about the sign later.
++ */
++ angle = atan2 (ABS (data->x_velocity), ABS (data->y_velocity));
++ data->vel_cosine = cos (angle);
++ data->vel_sine = sin (angle);
++
++ priv->deceleration_id =
++ gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
++ FRAME_INTERVAL,
++ scrolled_window_deceleration_cb,
++ data, (GDestroyNotify) g_free);
++}
++
++static gboolean
++gtk_scrolled_window_release_captured_event (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ /* Cancel the scrolling and send the button press
++ * event to the child widget
++ */
++ if (!priv->button_press_event)
++ return FALSE;
++
++ if (priv->pointer_grabbed)
++ {
++ gtk_grab_remove (GTK_WIDGET (scrolled_window));
++ priv->pointer_grabbed = FALSE;
++ }
++
++ if (priv->capture_button_press)
++ {
++ GtkWidget *event_widget;
++
++ event_widget = gtk_get_event_widget (priv->button_press_event);
++
++ if (!_gtk_propagate_captured_event (event_widget,
++ priv->button_press_event,
++ gtk_bin_get_child (GTK_BIN (scrolled_window))))
++ gtk_propagate_event (event_widget, priv->button_press_event);
++
++ gdk_event_free (priv->button_press_event);
++ priv->button_press_event = NULL;
++ }
++
++ if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
++ gtk_scrolled_window_start_deceleration (scrolled_window);
++
++ return FALSE;
++}
++
++static gboolean
++gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
++ GdkEvent *event)
++{
++ GtkScrolledWindowPrivate *priv;
++ gdouble x_root, y_root;
++ guint32 _time;
++
++#define STILL_THRESHOLD 40
++
++ if (!gdk_event_get_root_coords (event, &x_root, &y_root))
++ return FALSE;
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ _time = gdk_event_get_time (event);
++
++ if (priv->last_motion_event_x_root != x_root ||
++ priv->last_motion_event_y_root != y_root ||
++ ABS (_time - priv->last_motion_event_time) > STILL_THRESHOLD)
++ {
++ priv->x_velocity = (priv->last_motion_event_x_root - x_root) /
++ (gdouble) (_time - priv->last_motion_event_time);
++ priv->y_velocity = (priv->last_motion_event_y_root - y_root) /
++ (gdouble) (_time - priv->last_motion_event_time);
++ }
++
++ priv->last_motion_event_x_root = x_root;
++ priv->last_motion_event_y_root = y_root;
++ priv->last_motion_event_time = _time;
++
++#undef STILL_THRESHOLD
++
++ return TRUE;
++}
++
++static gboolean
++gtk_scrolled_window_captured_button_release (GtkWidget *widget,
++ GdkEvent *event)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv;
++ GtkWidget *child;
++ gboolean overshoot;
++ gdouble x_root, y_root;
++
++ if (event->button.button != 1)
++ return FALSE;
++
++ child = gtk_bin_get_child (GTK_BIN (widget));
++ if (!child)
++ return FALSE;
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ gtk_grab_remove (widget);
++ priv->pointer_grabbed = FALSE;
++
++ if (priv->release_timeout_id)
++ {
++ g_source_remove (priv->release_timeout_id);
++ priv->release_timeout_id = 0;
++ }
++
++ overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
++
++ if (priv->in_drag)
++ gdk_pointer_ungrab (gdk_event_get_time (event));
++ else
++ {
++ /* There hasn't been scrolling at all, so just let the
++ * child widget handle the button press normally
++ */
++ gtk_scrolled_window_release_captured_event (scrolled_window);
++
++ if (!overshoot)
++ return FALSE;
++ }
++ priv->in_drag = FALSE;
++
++ if (priv->button_press_event)
++ {
++ gdk_event_free (priv->button_press_event);
++ priv->button_press_event = NULL;
++ }
++
++ gtk_scrolled_window_calculate_velocity (scrolled_window, event);
++
++ /* Zero out vector components without a visible scrollbar */
++ if (!scrolled_window->hscrollbar_visible)
++ priv->x_velocity = 0;
++ if (!scrolled_window->vscrollbar_visible)
++ priv->y_velocity = 0;
++
++ if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
++ {
++ gtk_scrolled_window_start_deceleration (scrolled_window);
++ priv->x_velocity = priv->y_velocity = 0;
++ priv->last_button_event_valid = FALSE;
++ }
++ else
++ {
++ gdk_event_get_root_coords (event, &x_root, &y_root);
++ priv->last_button_event_x_root = x_root;
++ priv->last_button_event_y_root = y_root;
++ priv->last_button_event_valid = TRUE;
++ }
++
++ if (priv->capture_button_press)
++ return TRUE;
++ else
++ return FALSE;
++}
++
++static gboolean
++gtk_scrolled_window_captured_motion_notify (GtkWidget *widget,
++ GdkEvent *event)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv;
++ gint old_overshoot_x, old_overshoot_y;
++ gint new_overshoot_x, new_overshoot_y;
++ GtkWidget *child;
++ GtkAdjustment *hadjustment;
++ GtkAdjustment *vadjustment;
++ gdouble dx, dy;
++ GdkModifierType state;
++ gdouble x_root, y_root;
++
++ gdk_event_get_state (event, &state);
++ if (!(state & GDK_BUTTON1_MASK))
++ return FALSE;
++
++ child = gtk_bin_get_child (GTK_BIN (widget));
++ if (!child)
++ return FALSE;
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ /* Check if we've passed the drag threshold */
++ gdk_event_get_root_coords (event, &x_root, &y_root);
++ if (!priv->in_drag)
++ {
++ if (gtk_drag_check_threshold (widget,
++ priv->last_button_event_x_root,
++ priv->last_button_event_y_root,
++ x_root, y_root))
++ {
++ if (priv->release_timeout_id)
++ {
++ g_source_remove (priv->release_timeout_id);
++ priv->release_timeout_id = 0;
++ }
++
++ priv->last_button_event_valid = FALSE;
++ priv->in_drag = TRUE;
++ }
++ else
++ return TRUE;
++ }
++
++ gdk_pointer_grab (gtk_widget_get_window (widget),
++ TRUE,
++ GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK,
++ NULL, NULL,
++ gdk_event_get_time (event));
++
++ priv->last_button_event_valid = FALSE;
++
++ if (priv->button_press_event)
++ {
++ gdk_event_free (priv->button_press_event);
++ priv->button_press_event = NULL;
++ }
++
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &old_overshoot_x, &old_overshoot_y);
++
++ hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++ if (hadjustment && scrolled_window->hscrollbar_visible)
++ {
++ dx = (priv->last_motion_event_x_root - x_root) + priv->unclamped_hadj_value;
++ _gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment,
++ dx, TRUE, FALSE);
++ }
++
++ vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++ if (vadjustment && scrolled_window->vscrollbar_visible)
++ {
++ dy = (priv->last_motion_event_y_root - y_root) + priv->unclamped_vadj_value;
++ _gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment,
++ dy, TRUE, FALSE);
++ }
++
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &new_overshoot_x, &new_overshoot_y);
++
++ if (old_overshoot_x != new_overshoot_x ||
++ old_overshoot_y != new_overshoot_y)
++ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
++
++ gtk_scrolled_window_calculate_velocity (scrolled_window, event);
++
++ return TRUE;
++}
++
++static gboolean
++gtk_scrolled_window_captured_button_press (GtkWidget *widget,
++ GdkEvent *event)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv;
++ GtkWidget *child;
++ GtkWidget *event_widget;
++ gdouble x_root, y_root;
++
++ /* If scrollbars are not visible, we don't do kinetic scrolling */
++ if (!scrolled_window->vscrollbar_visible &&
++ !scrolled_window->hscrollbar_visible)
++ return FALSE;
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ event_widget = gtk_get_event_widget (event);
++
++ /* If there's another scrolled window between the widget
++ * receiving the event and this capturing scrolled window,
++ * let it handle the events.
++ */
++ if (widget != gtk_widget_get_ancestor (event_widget, GTK_TYPE_SCROLLED_WINDOW))
++ return FALSE;
++
++ /* Check whether the button press is close to the previous one,
++ * take that as a shortcut to get the child widget handle events
++ */
++ gdk_event_get_root_coords (event, &x_root, &y_root);
++ if (priv->last_button_event_valid &&
++ ABS (x_root - priv->last_button_event_x_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD &&
++ ABS (y_root - priv->last_button_event_y_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD)
++ {
++ priv->last_button_event_valid = FALSE;
++ return FALSE;
++ }
++
++ priv->last_button_event_x_root = priv->last_motion_event_x_root = x_root;
++ priv->last_button_event_y_root = priv->last_motion_event_y_root = y_root;
++ priv->last_motion_event_time = gdk_event_get_time (event);
++ priv->last_button_event_valid = TRUE;
++
++ if (event->button.button != 1)
++ return FALSE;
++
++ child = gtk_bin_get_child (GTK_BIN (widget));
++ if (!child)
++ return FALSE;
++
++ if (scrolled_window->hscrollbar == event_widget ||
++ scrolled_window->vscrollbar == event_widget)
++ return FALSE;
++
++ priv->pointer_grabbed = TRUE;
++ gtk_grab_add (widget);
++
++ gtk_scrolled_window_cancel_deceleration (scrolled_window);
++
++ /* Only set the timeout if we're going to store an event */
++ if (priv->capture_button_press)
++ priv->release_timeout_id =
++ gdk_threads_add_timeout (RELEASE_EVENT_TIMEOUT,
++ (GSourceFunc) gtk_scrolled_window_release_captured_event,
++ scrolled_window);
++
++ priv->in_drag = FALSE;
++
++ if (priv->capture_button_press)
++ {
++ /* Store the button press event in
++ * case we need to propagate it later
++ */
++ priv->button_press_event = gdk_event_copy (event);
++ return TRUE;
++ }
++ else
++ return FALSE;
++}
++
++static gboolean
++gtk_scrolled_window_captured_event (GtkWidget *widget,
++ GdkEvent *event)
++{
++ gboolean retval = FALSE;
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (widget);
++
++ switch (event->type)
++ {
++ case GDK_BUTTON_PRESS:
++ retval = gtk_scrolled_window_captured_button_press (widget, event);
++ break;
++ case GDK_BUTTON_RELEASE:
++ if (priv->pointer_grabbed)
++ retval = gtk_scrolled_window_captured_button_release (widget, event);
++ else
++ priv->last_button_event_valid = FALSE;
++ break;
++ case GDK_MOTION_NOTIFY:
++ if (priv->pointer_grabbed)
++ retval = gtk_scrolled_window_captured_motion_notify (widget, event);
++ break;
++ case GDK_LEAVE_NOTIFY:
++ case GDK_ENTER_NOTIFY:
++ if (priv->in_drag &&
++ event->crossing.mode != GDK_CROSSING_GRAB)
++ retval = TRUE;
++ break;
++ default:
++ break;
++ }
++
++ return retval;
++}
++
++static gboolean
+ gtk_scrolled_window_focus (GtkWidget *widget,
+ GtkDirectionType direction)
+ {
+@@ -1714,17 +2614,42 @@ gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
+ }
+
+ static void
++gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
++ gpointer user_data)
++{
++ GtkScrolledWindow *scrolled_window = user_data;
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ /* Allow overshooting for kinetic scrolling operations */
++ if (priv->pointer_grabbed || priv->deceleration_id)
++ return;
++
++ /* Ensure GtkAdjustment and unclamped values are in sync */
++ if (scrolled_window->vscrollbar &&
++ adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)))
++ priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
++ else if (scrolled_window->hscrollbar &&
++ adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)))
++ priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
++}
++
++static void
+ gtk_scrolled_window_add (GtkContainer *container,
+ GtkWidget *child)
+ {
+ GtkScrolledWindow *scrolled_window;
++ GtkScrolledWindowPrivate *priv;
+ GtkBin *bin;
+
+ bin = GTK_BIN (container);
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (container);
+ g_return_if_fail (bin->child == NULL);
+
+ scrolled_window = GTK_SCROLLED_WINDOW (container);
+
++ if (gtk_widget_get_realized (GTK_WIDGET (bin)))
++ gtk_widget_set_parent_window (child, priv->overshoot_window);
++
+ bin->child = child;
+ gtk_widget_set_parent (child, GTK_WIDGET (bin));
+
+@@ -1837,5 +2762,111 @@ _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
+ }
+ }
+
++static void
++gtk_scrolled_window_realize (GtkWidget *widget)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GtkAllocation allocation, relative_allocation;
++ GdkWindowAttr attributes;
++ GtkWidget *child_widget;
++ gint attributes_mask;
++
++ gtk_widget_set_realized (widget, TRUE);
++ gtk_widget_get_allocation (widget, &allocation);
++ gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
++
++ attributes.window_type = GDK_WINDOW_CHILD;
++ attributes.x = allocation.x + relative_allocation.x;
++ attributes.y = allocation.y + relative_allocation.y;
++ attributes.width = relative_allocation.width;
++ attributes.height = relative_allocation.height;
++ attributes.wclass = GDK_INPUT_OUTPUT;
++ attributes.visual = gtk_widget_get_visual (widget);
++ attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK |
++ GDK_BUTTON_MOTION_MASK;
++
++ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
++
++ priv->overshoot_window =
++ gdk_window_new (gtk_widget_get_parent_window (widget),
++ &attributes, attributes_mask);
++
++ gdk_window_set_user_data (priv->overshoot_window, widget);
++
++ child_widget = gtk_bin_get_child (GTK_BIN (widget));
++
++ if (child_widget)
++ gtk_widget_set_parent_window (child_widget,
++ priv->overshoot_window);
++
++ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
++}
++
++static void
++gtk_scrolled_window_unrealize (GtkWidget *widget)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ gdk_window_set_user_data (priv->overshoot_window, NULL);
++ gdk_window_destroy (priv->overshoot_window);
++ priv->overshoot_window = NULL;
++
++ gtk_widget_set_realized (widget, FALSE);
++
++ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
++}
++
++static void
++gtk_scrolled_window_map (GtkWidget *widget)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ gdk_window_show (priv->overshoot_window);
++
++ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
++}
++
++static void
++gtk_scrolled_window_unmap (GtkWidget *widget)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ gdk_window_hide (priv->overshoot_window);
++
++ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
++}
++
++static void
++gtk_scrolled_window_grab_notify (GtkWidget *widget,
++ gboolean was_grabbed)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ if (priv->pointer_grabbed && !was_grabbed)
++ {
++ gdk_pointer_ungrab (gtk_get_current_event_time ());
++ priv->pointer_grabbed = FALSE;
++ priv->in_drag = FALSE;
++
++ if (priv->release_timeout_id)
++ {
++ g_source_remove (priv->release_timeout_id);
++ priv->release_timeout_id = 0;
++ }
++
++ if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
++ gtk_scrolled_window_start_deceleration (scrolled_window);
++ else
++ gtk_scrolled_window_cancel_deceleration (scrolled_window);
++
++ priv->last_button_event_valid = FALSE;
++ }
++}
++
+ #define __GTK_SCROLLED_WINDOW_C__
+ #include "gtkaliasdef.c"
+diff --git a/gtk/gtkscrolledwindow.h b/gtk/gtkscrolledwindow.h
+index 5407547..1f555e0 100644
+--- a/gtk/gtkscrolledwindow.h
++++ b/gtk/gtkscrolledwindow.h
+@@ -127,6 +127,14 @@ GtkShadowType gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolle
+ void gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
+ GtkWidget *child);
+
++void gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
++ gboolean kinetic_scrolling);
++gboolean gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window);
++
++void gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
++ gboolean capture_button_press);
++gboolean gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window);
++
+ gint _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window);
+
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From f8b99185c4fe6a281f2075c5780bc71b35b46de9 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 14 Jun 2012 09:27:09 +0200
+Subject: [PATCH 12/68] gtk: paint to the right windows in
+ gtk_scrolled_window_expose()
+
+so we don't paint everything twice.
+---
+ gtk/gtkscrolledwindow.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 694d20a..821981f 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -1310,11 +1310,14 @@ static gboolean
+ gtk_scrolled_window_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+ {
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (widget);
++
+ if (gtk_widget_is_drawable (widget))
+ {
+- gtk_scrolled_window_paint (widget, &event->area);
+-
+- GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->expose_event (widget, event);
++ if (event->window == priv->overshoot_window)
++ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->expose_event (widget, event);
++ else
++ gtk_scrolled_window_paint (widget, &event->area);
+ }
+
+ return FALSE;
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From b7ce8de6d7b964eef99aa46ee90a09605acdd5ed Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 1 Jun 2012 13:08:26 +0200
+Subject: [PATCH 13/68] GtkScrolledWindow: add overlay scrollbars
+
+based on code from gnome-builder by Christian Hergert.
+---
+ gtk/Makefile.am | 4 +
+ gtk/gb-animation.c | 998 +++++++++++++++++++++++++++++++++++++++++++++++
+ gtk/gb-animation.h | 87 +++++
+ gtk/gb-frame-source.c | 134 +++++++
+ gtk/gb-frame-source.h | 32 ++
+ gtk/gtkscrolledwindow.c | 577 +++++++++++++++++++++++++--
+ 6 files changed, 1806 insertions(+), 26 deletions(-)
+ create mode 100644 gtk/gb-animation.c
+ create mode 100644 gtk/gb-animation.h
+ create mode 100644 gtk/gb-frame-source.c
+ create mode 100644 gtk/gb-frame-source.h
+
+diff --git a/gtk/Makefile.am b/gtk/Makefile.am
+index 7fbe429..31dfd19 100644
+--- a/gtk/Makefile.am
++++ b/gtk/Makefile.am
+@@ -361,6 +361,8 @@ gtk_semi_private_h_sources = \
+
+ # GTK+ header files that don't get installed
+ gtk_private_h_sources = \
++ gb-animation.h \
++ gb-frame-source.h \
+ gtkquery.h \
+ gtksearchengine.h \
+ gtksearchenginesimple.h \
+@@ -411,6 +413,8 @@ gtk_private_h_sources = \
+
+ # GTK+ C sources to build the library from
+ gtk_base_c_sources = \
++ gb-animation.c \
++ gb-frame-source.c \
+ gtkquery.c \
+ gtksearchengine.c \
+ gtksearchenginesimple.c \
+diff --git a/gtk/gb-animation.c b/gtk/gb-animation.c
+new file mode 100644
+index 0000000..9452b4a
+--- /dev/null
++++ b/gtk/gb-animation.c
+@@ -0,0 +1,998 @@
++/* gb-animation.c
++ *
++ * Copyright (C) 2010 Christian Hergert <christian@hergert.me>
++ *
++ * 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.1 of the License, or (at your option) any later version.
++ *
++ * This program 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 General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public icense along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <glib/gi18n.h>
++#include <gobject/gvaluecollector.h>
++#include <gtk/gtk.h>
++#include <string.h>
++
++#include "gb-animation.h"
++#include "gb-frame-source.h"
++
++G_DEFINE_TYPE(GbAnimation, _gb_animation, G_TYPE_INITIALLY_UNOWNED)
++
++typedef gdouble (*AlphaFunc) (gdouble offset);
++typedef void (*TweenFunc) (const GValue *begin,
++ const GValue *end,
++ GValue *value,
++ gdouble offset);
++
++typedef struct
++{
++ gboolean is_child; /* Does GParamSpec belong to parent widget */
++ GParamSpec *pspec; /* GParamSpec of target property */
++ GValue begin; /* Begin value in animation */
++ GValue end; /* End value in animation */
++} Tween;
++
++
++struct _GbAnimationPrivate
++{
++ gpointer target; /* Target object to animate */
++ guint64 begin_msec; /* Time in which animation started */
++ guint duration_msec; /* Duration of animation */
++ guint mode; /* Tween mode */
++ guint tween_handler; /* GSource performing tweens */
++ GArray *tweens; /* Array of tweens to perform */
++ guint frame_rate; /* The frame-rate to use */
++ guint frame_count; /* Counter for debugging frames rendered */
++};
++
++
++enum
++{
++ PROP_0,
++ PROP_DURATION,
++ PROP_FRAME_RATE,
++ PROP_MODE,
++ PROP_TARGET,
++ LAST_PROP
++};
++
++
++enum
++{
++ TICK,
++ LAST_SIGNAL
++};
++
++
++/*
++ * Helper macros.
++ */
++#define TIMEVAL_TO_MSEC(t) (((t).tv_sec * 1000UL) + ((t).tv_usec / 1000UL))
++#define LAST_FUNDAMENTAL 64
++#define TWEEN(type) \
++ static void \
++ tween_##type (const GValue *begin, \
++ const GValue *end, \
++ GValue *value, \
++ gdouble offset) \
++ { \
++ g##type x = g_value_get_##type(begin); \
++ g##type y = g_value_get_##type(end); \
++ g_value_set_##type(value, x + ((y - x) * offset)); \
++ }
++
++
++/*
++ * Globals.
++ */
++static AlphaFunc gAlphaFuncs[GB_ANIMATION_LAST];
++static gboolean gDebug;
++static GParamSpec *gParamSpecs[LAST_PROP];
++static guint gSignals[LAST_SIGNAL];
++static TweenFunc gTweenFuncs[LAST_FUNDAMENTAL];
++
++
++/*
++ * Tweeners for basic types.
++ */
++TWEEN(int);
++TWEEN(uint);
++TWEEN(long);
++TWEEN(ulong);
++TWEEN(float);
++TWEEN(double);
++
++
++/**
++ * _gb_animation_alpha_ease_in_cubic:
++ * @offset: (in): The position within the animation; 0.0 to 1.0.
++ *
++ * An alpha function to transform the offset within the animation.
++ * @GB_ANIMATION_CUBIC means the valu ewill be transformed into
++ * cubic acceleration (x * x * x).
++ */
++static gdouble
++_gb_animation_alpha_ease_in_cubic (gdouble offset)
++{
++ return offset * offset * offset;
++}
++
++
++/**
++ * _gb_animation_alpha_linear:
++ * @offset: (in): The position within the animation; 0.0 to 1.0.
++ *
++ * An alpha function to transform the offset within the animation.
++ * @GB_ANIMATION_LINEAR means no tranformation will be made.
++ *
++ * Returns: @offset.
++ * Side effects: None.
++ */
++static gdouble
++_gb_animation_alpha_linear (gdouble offset)
++{
++ return offset;
++}
++
++
++/**
++ * _gb_animation_alpha_ease_in_quad:
++ * @offset: (in): The position within the animation; 0.0 to 1.0.
++ *
++ * An alpha function to transform the offset within the animation.
++ * @GB_ANIMATION_EASE_IN_QUAD means that the value will be transformed
++ * into a quadratic acceleration.
++ *
++ * Returns: A tranformation of @offset.
++ * Side effects: None.
++ */
++static gdouble
++_gb_animation_alpha_ease_in_quad (gdouble offset)
++{
++ return offset * offset;
++}
++
++
++/**
++ * _gb_animation_alpha_ease_out_quad:
++ * @offset: (in): The position within the animation; 0.0 to 1.0.
++ *
++ * An alpha function to transform the offset within the animation.
++ * @GB_ANIMATION_EASE_OUT_QUAD means that the value will be transformed
++ * into a quadratic deceleration.
++ *
++ * Returns: A tranformation of @offset.
++ * Side effects: None.
++ */
++static gdouble
++_gb_animation_alpha_ease_out_quad (gdouble offset)
++{
++ return -1.0 * offset * (offset - 2.0);
++}
++
++
++/**
++ * _gb_animation_alpha_ease_in_out_quad:
++ * @offset: (in): The position within the animation; 0.0 to 1.0.
++ *
++ * An alpha function to transform the offset within the animation.
++ * @GB_ANIMATION_EASE_IN_OUT_QUAD means that the value will be transformed
++ * into a quadratic acceleration for the first half, and quadratic
++ * deceleration the second half.
++ *
++ * Returns: A tranformation of @offset.
++ * Side effects: None.
++ */
++static gdouble
++_gb_animation_alpha_ease_in_out_quad (gdouble offset)
++{
++ offset *= 2.0;
++ if (offset < 1.0) {
++ return 0.5 * offset * offset;
++ }
++ offset -= 1.0;
++ return -0.5 * (offset * (offset - 2.0) - 1.0);
++}
++
++
++/**
++ * _gb_animation_load_begin_values:
++ * @animation: (in): A #GbAnimation.
++ *
++ * Load the begin values for all the properties we are about to
++ * animate.
++ *
++ * Returns: None.
++ * Side effects: None.
++ */
++static void
++_gb_animation_load_begin_values (GbAnimation *animation)
++{
++ GbAnimationPrivate *priv;
++ GtkContainer *container;
++ Tween *tween;
++ gint i;
++
++ g_return_if_fail(GB_IS_ANIMATION(animation));
++
++ priv = animation->priv;
++
++ for (i = 0; i < priv->tweens->len; i++) {
++ tween = &g_array_index(priv->tweens, Tween, i);
++ g_value_reset(&tween->begin);
++ if (tween->is_child) {
++ container = GTK_CONTAINER(gtk_widget_get_parent(priv->target));
++ gtk_container_child_get_property(container, priv->target,
++ tween->pspec->name,
++ &tween->begin);
++ } else {
++ g_object_get_property(priv->target, tween->pspec->name,
++ &tween->begin);
++ }
++ }
++}
++
++
++/**
++ * _gb_animation_unload_begin_values:
++ * @animation: (in): A #GbAnimation.
++ *
++ * Unloads the begin values for the animation. This might be particularly
++ * useful once we support pointer types.
++ *
++ * Returns: None.
++ * Side effects: None.
++ */
++static void
++_gb_animation_unload_begin_values (GbAnimation *animation)
++{
++ GbAnimationPrivate *priv;
++ Tween *tween;
++ gint i;
++
++ g_return_if_fail(GB_IS_ANIMATION(animation));
++
++ priv = animation->priv;
++
++ for (i = 0; i < priv->tweens->len; i++) {
++ tween = &g_array_index(priv->tweens, Tween, i);
++ g_value_reset(&tween->begin);
++ }
++}
++
++
++/**
++ * _gb_animation_get_offset:
++ * @animation: (in): A #GbAnimation.
++ *
++ * Retrieves the position within the animation from 0.0 to 1.0. This
++ * value is calculated using the msec of the beginning of the animation
++ * and the current time.
++ *
++ * Returns: The offset of the animation from 0.0 to 1.0.
++ * Side effects: None.
++ */
++static gdouble
++_gb_animation_get_offset (GbAnimation *animation)
++{
++ GbAnimationPrivate *priv;
++ GTimeVal now;
++ guint64 msec;
++ gdouble offset;
++
++ g_return_val_if_fail(GB_IS_ANIMATION(animation), 0.0);
++
++ priv = animation->priv;
++
++ g_get_current_time(&now);
++ msec = TIMEVAL_TO_MSEC(now);
++ offset = (gdouble)(msec - priv->begin_msec)
++ / (gdouble)priv->duration_msec;
++ return CLAMP(offset, 0.0, 1.0);
++}
++
++
++/**
++ * _gb_animation_update_property:
++ * @animation: (in): A #GbAnimation.
++ * @target: (in): A #GObject.
++ * @tween: (in): a #Tween containing the property.
++ * @value: (in) The new value for the property.
++ *
++ * Updates the value of a property on an object using @value.
++ *
++ * Returns: None.
++ * Side effects: The property of @target is updated.
++ */
++static void
++_gb_animation_update_property (GbAnimation *animation,
++ gpointer target,
++ Tween *tween,
++ const GValue *value)
++{
++ g_object_set_property(target, tween->pspec->name, value);
++}
++
++
++/**
++ * _gb_animation_update_child_property:
++ * @animation: (in): A #GbAnimation.
++ * @target: (in): A #GObject.
++ * @tween: (in): A #Tween containing the property.
++ * @value: (in): The new value for the property.
++ *
++ * Updates the value of the parent widget of the target to @value.
++ *
++ * Returns: None.
++ * Side effects: The property of @target<!-- -->'s parent widget is updated.
++ */
++static void
++_gb_animation_update_child_property (GbAnimation *animation,
++ gpointer target,
++ Tween *tween,
++ const GValue *value)
++{
++ GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(target));
++ gtk_container_child_set_property(GTK_CONTAINER(parent), target,
++ tween->pspec->name, value);
++}
++
++
++/**
++ * _gb_animation_get_value_at_offset:
++ * @animation: (in): A #GbAnimation.
++ * @offset: (in): The offset in the animation from 0.0 to 1.0.
++ * @tween: (in): A #Tween containing the property.
++ * @value: (out): A #GValue in which to store the property.
++ *
++ * Retrieves a value for a particular position within the animation.
++ *
++ * Returns: None.
++ * Side effects: None.
++ */
++static void
++_gb_animation_get_value_at_offset (GbAnimation *animation,
++ gdouble offset,
++ Tween *tween,
++ GValue *value)
++{
++ g_return_if_fail(GB_IS_ANIMATION(animation));
++ g_return_if_fail(offset >= 0.0);
++ g_return_if_fail(offset <= 1.0);
++ g_return_if_fail(tween != NULL);
++ g_return_if_fail(value != NULL);
++ g_return_if_fail(value->g_type == tween->pspec->value_type);
++
++ if (value->g_type < LAST_FUNDAMENTAL) {
++ /*
++ * If you hit the following assertion, you need to add a function
++ * to create the new value at the given offset.
++ */
++ g_assert(gTweenFuncs[value->g_type]);
++ gTweenFuncs[value->g_type](&tween->begin, &tween->end, value, offset);
++ } else {
++ /*
++ * TODO: Support complex transitions.
++ */
++ if (offset >= 1.0) {
++ g_value_copy(&tween->end, value);
++ }
++ }
++}
++
++
++/**
++ * _gb_animation_tick:
++ * @animation: (in): A #GbAnimation.
++ *
++ * Moves the object properties to the next position in the animation.
++ *
++ * Returns: %TRUE if the animation has not completed; otherwise %FALSE.
++ * Side effects: None.
++ */
++static gboolean
++_gb_animation_tick (GbAnimation *animation)
++{
++ GbAnimationPrivate *priv;
++ GdkWindow *window;
++ gdouble offset;
++ gdouble alpha;
++ GValue value = { 0 };
++ Tween *tween;
++ gint i;
++
++ g_return_val_if_fail(GB_IS_ANIMATION(animation), FALSE);
++
++ priv = animation->priv;
++
++ priv->frame_count++;
++ offset = _gb_animation_get_offset(animation);
++ alpha = gAlphaFuncs[priv->mode](offset);
++
++ /*
++ * Update property values.
++ */
++ for (i = 0; i < priv->tweens->len; i++) {
++ tween = &g_array_index(priv->tweens, Tween, i);
++ g_value_init(&value, tween->pspec->value_type);
++ _gb_animation_get_value_at_offset(animation, alpha, tween, &value);
++ if (!tween->is_child) {
++ _gb_animation_update_property(animation, priv->target,
++ tween, &value);
++ } else {
++ _gb_animation_update_child_property(animation, priv->target,
++ tween, &value);
++ }
++ g_value_unset(&value);
++ }
++
++ /*
++ * Notify anyone interested in the tick signal.
++ */
++ g_signal_emit(animation, gSignals[TICK], 0);
++
++ /*
++ * Flush any outstanding events to the graphics server (in the case of X).
++ */
++ if (GTK_IS_WIDGET(priv->target)) {
++ if ((window = gtk_widget_get_window(GTK_WIDGET(priv->target)))) {
++ gdk_window_flush(window);
++ }
++ }
++
++ return (offset < 1.0);
++}
++
++
++/**
++ * _gb_animation_timeout:
++ * @data: (in): A #GbAnimation.
++ *
++ * Timeout from the main loop to move to the next step of the animation.
++ *
++ * Returns: %TRUE until the animation has completed; otherwise %FALSE.
++ * Side effects: None.
++ */
++static gboolean
++_gb_animation_timeout (gpointer data)
++{
++ GbAnimation *animation = (GbAnimation *)data;
++ gboolean ret;
++
++ if (!(ret = _gb_animation_tick(animation))) {
++ _gb_animation_stop(animation);
++ }
++
++ return ret;
++}
++
++
++/**
++ * _gb_animation_start:
++ * @animation: (in): A #GbAnimation.
++ *
++ * Start the animation. When the animation stops, the internal reference will
++ * be dropped and the animation may be finalized.
++ *
++ * Returns: None.
++ * Side effects: None.
++ */
++void
++_gb_animation_start (GbAnimation *animation)
++{
++ GbAnimationPrivate *priv;
++ GTimeVal now;
++
++ g_return_if_fail(GB_IS_ANIMATION(animation));
++ g_return_if_fail(!animation->priv->tween_handler);
++
++ priv = animation->priv;
++
++ g_get_current_time(&now);
++ g_object_ref_sink(animation);
++ _gb_animation_load_begin_values(animation);
++
++ priv->begin_msec = TIMEVAL_TO_MSEC(now);
++ priv->tween_handler = _gb_frame_source_add(priv->frame_rate,
++ _gb_animation_timeout,
++ animation);
++}
++
++
++/**
++ * _gb_animation_stop:
++ * @animation: (in): A #GbAnimation.
++ *
++ * Stops a running animation. The internal reference to the animation is
++ * dropped and therefore may cause the object to finalize.
++ *
++ * Returns: None.
++ * Side effects: None.
++ */
++void
++_gb_animation_stop (GbAnimation *animation)
++{
++ GbAnimationPrivate *priv;
++
++ g_return_if_fail(GB_IS_ANIMATION(animation));
++
++ priv = animation->priv;
++
++ if (priv->tween_handler) {
++ g_source_remove(priv->tween_handler);
++ priv->tween_handler = 0;
++ _gb_animation_unload_begin_values(animation);
++ g_object_unref(animation);
++ }
++}
++
++
++/**
++ * _gb_animation_add_property:
++ * @animation: (in): A #GbAnimation.
++ * @pspec: (in): A #ParamSpec of @target or a #GtkWidget<!-- -->'s parent.
++ * @value: (in): The new value for the property at the end of the animation.
++ *
++ * Adds a new property to the set of properties to be animated during the
++ * lifetime of the animation.
++ *
++ * Returns: None.
++ * Side effects: None.
++ */
++void
++_gb_animation_add_property (GbAnimation *animation,
++ GParamSpec *pspec,
++ const GValue *value)
++{
++ GbAnimationPrivate *priv;
++ Tween tween = { 0 };
++ GType type;
++
++ g_return_if_fail(GB_IS_ANIMATION(animation));
++ g_return_if_fail(pspec != NULL);
++ g_return_if_fail(value != NULL);
++ g_return_if_fail(value->g_type);
++ g_return_if_fail(animation->priv->target);
++ g_return_if_fail(!animation->priv->tween_handler);
++
++ priv = animation->priv;
++
++ type = G_TYPE_FROM_INSTANCE(priv->target);
++ tween.is_child = !g_type_is_a(type, pspec->owner_type);
++ if (tween.is_child) {
++ if (!GTK_IS_WIDGET(priv->target)) {
++ g_critical("Cannot locate property %s in class %s",
++ pspec->name, g_type_name(type));
++ return;
++ }
++ }
++
++ tween.pspec = g_param_spec_ref(pspec);
++ g_value_init(&tween.begin, pspec->value_type);
++ g_value_init(&tween.end, pspec->value_type);
++ g_value_copy(value, &tween.end);
++ g_array_append_val(priv->tweens, tween);
++}
++
++
++/**
++ * _gb_animation_dispose:
++ * @object: (in): A #GbAnimation.
++ *
++ * Releases any object references the animation contains.
++ *
++ * Returns: None.
++ * Side effects: None.
++ */
++static void
++_gb_animation_dispose (GObject *object)
++{
++ GbAnimationPrivate *priv = GB_ANIMATION(object)->priv;
++ gpointer instance;
++
++ if ((instance = priv->target)) {
++ priv->target = NULL;
++ g_object_unref(instance);
++ }
++
++ G_OBJECT_CLASS(_gb_animation_parent_class)->dispose(object);
++}
++
++
++/**
++ * _gb_animation_finalize:
++ * @object: (in): A #GbAnimation.
++ *
++ * Finalizes the object and releases any resources allocated.
++ *
++ * Returns: None.
++ * Side effects: None.
++ */
++static void
++_gb_animation_finalize (GObject *object)
++{
++ GbAnimationPrivate *priv = GB_ANIMATION(object)->priv;
++ Tween *tween;
++ gint i;
++
++ for (i = 0; i < priv->tweens->len; i++) {
++ tween = &g_array_index(priv->tweens, Tween, i);
++ g_value_unset(&tween->begin);
++ g_value_unset(&tween->end);
++ g_param_spec_unref(tween->pspec);
++ }
++
++ g_array_unref(priv->tweens);
++
++ if (gDebug) {
++ g_print("Rendered %d frames in %d msec animation.\n",
++ priv->frame_count, priv->duration_msec);
++ }
++
++ G_OBJECT_CLASS(_gb_animation_parent_class)->finalize(object);
++}
++
++
++/**
++ * _gb_animation_set_property:
++ * @object: (in): A #GObject.
++ * @prop_id: (in): The property identifier.
++ * @value: (in): The given property.
++ * @pspec: (in): A #ParamSpec.
++ *
++ * Set a given #GObject property.
++ */
++static void
++_gb_animation_set_property (GObject *object,
++ guint prop_id,
++ const GValue *value,
++ GParamSpec *pspec)
++{
++ GbAnimation *animation = GB_ANIMATION(object);
++
++ switch (prop_id) {
++ case PROP_DURATION:
++ animation->priv->duration_msec = g_value_get_uint(value);
++ break;
++ case PROP_FRAME_RATE:
++ animation->priv->frame_rate = g_value_get_uint(value);
++ break;
++ case PROP_MODE:
++ animation->priv->mode = g_value_get_enum(value);
++ break;
++ case PROP_TARGET:
++ animation->priv->target = g_value_dup_object(value);
++ break;
++ default:
++ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
++ }
++}
++
++
++/**
++ * _gb_animation_class_init:
++ * @klass: (in): A #GbAnimationClass.
++ *
++ * Initializes the GObjectClass.
++ *
++ * Returns: None.
++ * Side effects: Properties, signals, and vtables are initialized.
++ */
++static void
++_gb_animation_class_init (GbAnimationClass *klass)
++{
++ GObjectClass *object_class;
++
++ gDebug = !!g_getenv("GB_ANIMATION_DEBUG");
++
++ object_class = G_OBJECT_CLASS(klass);
++ object_class->dispose = _gb_animation_dispose;
++ object_class->finalize = _gb_animation_finalize;
++ object_class->set_property = _gb_animation_set_property;
++ g_type_class_add_private(object_class, sizeof(GbAnimationPrivate));
++
++ /**
++ * GbAnimation:duration:
++ *
++ * The "duration" property is the total number of milliseconds that the
++ * animation should run before being completed.
++ */
++ gParamSpecs[PROP_DURATION] =
++ g_param_spec_uint("duration",
++ _("Duration"),
++ _("The duration of the animation"),
++ 0,
++ G_MAXUINT,
++ 250,
++ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
++ g_object_class_install_property(object_class, PROP_DURATION,
++ gParamSpecs[PROP_DURATION]);
++
++ /**
++ * GbAnimation:mode:
++ *
++ * The "mode" property is the Alpha function that should be used to
++ * determine the offset within the animation based on the current
++ * offset in the animations duration.
++ */
++ gParamSpecs[PROP_MODE] =
++ g_param_spec_enum("mode",
++ _("Mode"),
++ _("The animation mode"),
++ GB_TYPE_ANIMATION_MODE,
++ GB_ANIMATION_LINEAR,
++ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
++ g_object_class_install_property(object_class, PROP_MODE,
++ gParamSpecs[PROP_MODE]);
++
++ /**
++ * GbAnimation:target:
++ *
++ * The "target" property is the #GObject that should have it's properties
++ * animated.
++ */
++ gParamSpecs[PROP_TARGET] =
++ g_param_spec_object("target",
++ _("Target"),
++ _("The target of the animation"),
++ G_TYPE_OBJECT,
++ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
++ g_object_class_install_property(object_class, PROP_TARGET,
++ gParamSpecs[PROP_TARGET]);
++
++ /**
++ * GbAnimation:frame-rate:
++ *
++ * The "frame-rate" is the number of frames that the animation should
++ * try to perform per-second. The default is 60 frames-per-second.
++ */
++ gParamSpecs[PROP_FRAME_RATE] =
++ g_param_spec_uint("frame-rate",
++ _("Frame Rate"),
++ _("The number of frames per second."),
++ 1,
++ G_MAXUINT,
++ 60,
++ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
++ g_object_class_install_property(object_class, PROP_FRAME_RATE,
++ gParamSpecs[PROP_FRAME_RATE]);
++
++ /**
++ * GbAnimation::tick:
++ *
++ * The "tick" signal is emitted on each frame in the animation.
++ */
++ gSignals[TICK] = g_signal_new("tick",
++ GB_TYPE_ANIMATION,
++ G_SIGNAL_RUN_FIRST,
++ 0,
++ NULL,
++ NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE,
++ 0);
++
++#define SET_ALPHA(_T, _t) \
++ gAlphaFuncs[GB_ANIMATION_##_T] = _gb_animation_alpha_##_t
++
++ SET_ALPHA(LINEAR, linear);
++ SET_ALPHA(EASE_IN_QUAD, ease_in_quad);
++ SET_ALPHA(EASE_OUT_QUAD, ease_out_quad);
++ SET_ALPHA(EASE_IN_OUT_QUAD, ease_in_out_quad);
++ SET_ALPHA(EASE_IN_CUBIC, ease_in_cubic);
++
++#define SET_TWEEN(_T, _t) \
++ G_STMT_START { \
++ guint idx = G_TYPE_##_T; \
++ gTweenFuncs[idx] = tween_##_t; \
++ } G_STMT_END
++
++ SET_TWEEN(INT, int);
++ SET_TWEEN(UINT, uint);
++ SET_TWEEN(LONG, long);
++ SET_TWEEN(ULONG, ulong);
++ SET_TWEEN(FLOAT, float);
++ SET_TWEEN(DOUBLE, double);
++}
++
++
++/**
++ * _gb_animation_init:
++ * @animation: (in): A #GbAnimation.
++ *
++ * Initializes the #GbAnimation instance.
++ *
++ * Returns: None.
++ * Side effects: Everything.
++ */
++static void
++_gb_animation_init (GbAnimation *animation)
++{
++ GbAnimationPrivate *priv;
++
++ priv = G_TYPE_INSTANCE_GET_PRIVATE(animation, GB_TYPE_ANIMATION,
++ GbAnimationPrivate);
++ animation->priv = priv;
++
++ priv->duration_msec = 250;
++ priv->frame_rate = 60;
++ priv->mode = GB_ANIMATION_LINEAR;
++ priv->tweens = g_array_new(FALSE, FALSE, sizeof(Tween));
++}
++
++
++/**
++ * _gb_animation_mode_get_type:
++ *
++ * Retrieves the GType for #GbAnimationMode.
++ *
++ * Returns: A GType.
++ * Side effects: GType registered on first call.
++ */
++GType
++_gb_animation_mode_get_type (void)
++{
++ static GType type_id = 0;
++ static const GEnumValue values[] = {
++ { GB_ANIMATION_LINEAR, "GB_ANIMATION_LINEAR", "LINEAR" },
++ { GB_ANIMATION_EASE_IN_QUAD, "GB_ANIMATION_EASE_IN_QUAD", "EASE_IN_QUAD" },
++ { GB_ANIMATION_EASE_IN_OUT_QUAD, "GB_ANIMATION_EASE_IN_OUT_QUAD", "EASE_IN_OUT_QUAD" },
++ { GB_ANIMATION_EASE_OUT_QUAD, "GB_ANIMATION_EASE_OUT_QUAD", "EASE_OUT_QUAD" },
++ { GB_ANIMATION_EASE_IN_CUBIC, "GB_ANIMATION_EASE_IN_CUBIC", "EASE_IN_CUBIC" },
++ { 0 }
++ };
++
++ if (G_UNLIKELY(!type_id)) {
++ type_id = g_enum_register_static("GbAnimationMode", values);
++ }
++ return type_id;
++}
++
++/**
++ * _gb_object_animatev:
++ * Returns: (transfer none): A #GbAnimation.
++ */
++GbAnimation*
++_gb_object_animatev (gpointer object,
++ GbAnimationMode mode,
++ guint duration_msec,
++ guint frame_rate,
++ const gchar *first_property,
++ va_list args)
++{
++ GbAnimation *animation;
++ GObjectClass *klass;
++ GObjectClass *pklass;
++ const gchar *name;
++ GParamSpec *pspec;
++ GtkWidget *parent;
++ GValue value = { 0 };
++ gchar *error = NULL;
++ GType type;
++ GType ptype;
++
++ g_return_val_if_fail(first_property != NULL, NULL);
++ g_return_val_if_fail(mode < GB_ANIMATION_LAST, NULL);
++
++ name = first_property;
++ type = G_TYPE_FROM_INSTANCE(object);
++ klass = G_OBJECT_GET_CLASS(object);
++ animation = g_object_new(GB_TYPE_ANIMATION,
++ "duration", duration_msec,
++ "frame-rate", frame_rate ? frame_rate : 60,
++ "mode", mode,
++ "target", object,
++ NULL);
++
++ do {
++ /*
++ * First check for the property on the object. If that does not exist
++ * then check if the object has a parent and look at its child
++ * properties (if its a GtkWidget).
++ */
++ if (!(pspec = g_object_class_find_property(klass, name))) {
++ if (!g_type_is_a(type, GTK_TYPE_WIDGET)) {
++ g_critical("Failed to find property %s in %s",
++ name, g_type_name(type));
++ goto failure;
++ }
++ if (!(parent = gtk_widget_get_parent(object))) {
++ g_critical("Failed to find property %s in %s",
++ name, g_type_name(type));
++ goto failure;
++ }
++ pklass = G_OBJECT_GET_CLASS(parent);
++ ptype = G_TYPE_FROM_INSTANCE(parent);
++ if (!(pspec = gtk_container_class_find_child_property(pklass, name))) {
++ g_critical("Failed to find property %s in %s or parent %s",
++ name, g_type_name(type), g_type_name(ptype));
++ goto failure;
++ }
++ }
++
++ g_value_init(&value, pspec->value_type);
++ G_VALUE_COLLECT(&value, args, 0, &error);
++ if (error != NULL) {
++ g_critical("Failed to retrieve va_list value: %s", error);
++ g_free(error);
++ goto failure;
++ }
++
++ _gb_animation_add_property(animation, pspec, &value);
++ g_value_unset(&value);
++ } while ((name = va_arg(args, const gchar *)));
++
++ _gb_animation_start(animation);
++
++ return animation;
++
++failure:
++ g_object_ref_sink(animation);
++ g_object_unref(animation);
++ return NULL;
++}
++
++/**
++ * _gb_object_animate:
++ * @object: (in): A #GObject.
++ * @mode: (in): The animation mode.
++ * @duration_msec: (in): The duration in milliseconds.
++ * @first_property: (in): The first property to animate.
++ *
++ * Animates the properties of @object. The can be set in a similar
++ * manner to g_object_set(). They will be animated from their current
++ * value to the target value over the time period.
++ *
++ * Return value: (transfer none): A #GbAnimation.
++ * Side effects: None.
++ */
++GbAnimation*
++_gb_object_animate (gpointer object,
++ GbAnimationMode mode,
++ guint duration_msec,
++ const gchar *first_property,
++ ...)
++{
++ GbAnimation *animation;
++ va_list args;
++
++ va_start(args, first_property);
++ animation = _gb_object_animatev(object, mode, duration_msec, 0,
++ first_property, args);
++ va_end(args);
++ return animation;
++}
++
++/**
++ * _gb_object_animate_full:
++ *
++ * Return value: (transfer none): A #GbAnimation.
++ */
++GbAnimation*
++_gb_object_animate_full (gpointer object,
++ GbAnimationMode mode,
++ guint duration_msec,
++ guint frame_rate,
++ GDestroyNotify notify,
++ gpointer notify_data,
++ const gchar *first_property,
++ ...)
++{
++ GbAnimation *animation;
++ va_list args;
++
++ va_start(args, first_property);
++ animation = _gb_object_animatev(object, mode, duration_msec,
++ frame_rate, first_property, args);
++ va_end(args);
++ g_object_weak_ref(G_OBJECT(animation), (GWeakNotify)notify, notify_data);
++ return animation;
++}
+diff --git a/gtk/gb-animation.h b/gtk/gb-animation.h
+new file mode 100644
+index 0000000..bf9268d
+--- /dev/null
++++ b/gtk/gb-animation.h
+@@ -0,0 +1,87 @@
++/* gb-animation.h
++ *
++ * Copyright (C) 2010 Christian Hergert <chris@dronelabs.com>
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program 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 General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef GB_ANIMATION_H
++#define GB_ANIMATION_H
++
++#include <glib-object.h>
++
++G_BEGIN_DECLS
++
++#define GB_TYPE_ANIMATION (_gb_animation_get_type())
++#define GB_TYPE_ANIMATION_MODE (_gb_animation_mode_get_type())
++#define GB_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_ANIMATION, GbAnimation))
++#define GB_ANIMATION_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_ANIMATION, GbAnimation const))
++#define GB_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GB_TYPE_ANIMATION, GbAnimationClass))
++#define GB_IS_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GB_TYPE_ANIMATION))
++#define GB_IS_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GB_TYPE_ANIMATION))
++#define GB_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GB_TYPE_ANIMATION, GbAnimationClass))
++
++typedef struct _GbAnimation GbAnimation;
++typedef struct _GbAnimationClass GbAnimationClass;
++typedef struct _GbAnimationPrivate GbAnimationPrivate;
++typedef enum _GbAnimationMode GbAnimationMode;
++
++enum _GbAnimationMode
++{
++ GB_ANIMATION_LINEAR,
++ GB_ANIMATION_EASE_IN_QUAD,
++ GB_ANIMATION_EASE_OUT_QUAD,
++ GB_ANIMATION_EASE_IN_OUT_QUAD,
++ GB_ANIMATION_EASE_IN_CUBIC,
++
++ GB_ANIMATION_LAST
++};
++
++struct _GbAnimation
++{
++ GInitiallyUnowned parent;
++
++ /*< private >*/
++ GbAnimationPrivate *priv;
++};
++
++struct _GbAnimationClass
++{
++ GInitiallyUnownedClass parent_class;
++};
++
++GType _gb_animation_get_type (void) G_GNUC_CONST;
++GType _gb_animation_mode_get_type (void) G_GNUC_CONST;
++void _gb_animation_start (GbAnimation *animation);
++void _gb_animation_stop (GbAnimation *animation);
++void _gb_animation_add_property (GbAnimation *animation,
++ GParamSpec *pspec,
++ const GValue *value);
++GbAnimation* _gb_object_animate (gpointer object,
++ GbAnimationMode mode,
++ guint duration_msec,
++ const gchar *first_property,
++ ...) G_GNUC_NULL_TERMINATED;
++GbAnimation* _gb_object_animate_full (gpointer object,
++ GbAnimationMode mode,
++ guint duration_msec,
++ guint frame_rate,
++ GDestroyNotify notify,
++ gpointer notify_data,
++ const gchar *first_property,
++ ...) G_GNUC_NULL_TERMINATED;
++
++G_END_DECLS
++
++#endif /* GB_ANIMATION_H */
+diff --git a/gtk/gb-frame-source.c b/gtk/gb-frame-source.c
+new file mode 100644
+index 0000000..be04c1b
+--- /dev/null
++++ b/gtk/gb-frame-source.c
+@@ -0,0 +1,134 @@
++/*
++ * Based upon code from Clutter:
++ *
++ * Authored By Neil Roberts <neil@linux.intel.com>
++ *
++ * Copyright (C) 2009 Intel Corporation.
++ * Copyright (C) 2012 Christian Hergert.
++ *
++ * 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, see <http://www.gnu.org/licenses/>.
++ */
++
++#include "gb-frame-source.h"
++
++typedef struct
++{
++ GSource parent;
++ guint fps;
++ guint frame_count;
++ gint64 start_time;
++} GbFrameSource;
++
++static gboolean
++gb_frame_source_prepare (GSource *source,
++ gint *timeout_)
++{
++ GbFrameSource *fsource = (GbFrameSource *)source;
++ gint64 current_time;
++ guint elapsed_time;
++ guint new_frame_num;
++ guint frame_time;
++
++ current_time = g_source_get_time(source) / 1000;
++ elapsed_time = current_time - fsource->start_time;
++ new_frame_num = elapsed_time * fsource->fps / 1000;
++
++ /* If time has gone backwards or the time since the last frame is
++ * greater than the two frames worth then reset the time and do a
++ * frame now */
++ if (new_frame_num < fsource->frame_count ||
++ new_frame_num - fsource->frame_count > 2) {
++ /* Get the frame time rounded up to the nearest ms */
++ frame_time = (1000 + fsource->fps - 1) / fsource->fps;
++
++ /* Reset the start time */
++ fsource->start_time = current_time;
++
++ /* Move the start time as if one whole frame has elapsed */
++ fsource->start_time -= frame_time;
++ fsource->frame_count = 0;
++ *timeout_ = 0;
++ return TRUE;
++ } else if (new_frame_num > fsource->frame_count) {
++ *timeout_ = 0;
++ return TRUE;
++ } else {
++ *timeout_ = (fsource->frame_count + 1) * 1000 / fsource->fps - elapsed_time;
++ return FALSE;
++ }
++}
++
++static gboolean
++gb_frame_source_check (GSource *source)
++{
++ gint timeout_;
++ return gb_frame_source_prepare(source, &timeout_);
++}
++
++static gboolean
++gb_frame_source_dispatch (GSource *source,
++ GSourceFunc source_func,
++ gpointer user_data)
++{
++ GbFrameSource *fsource = (GbFrameSource *)source;
++ gboolean ret;
++
++ if ((ret = source_func(user_data)))
++ fsource->frame_count++;
++ return ret;
++}
++
++static GSourceFuncs source_funcs = {
++ gb_frame_source_prepare,
++ gb_frame_source_check,
++ gb_frame_source_dispatch,
++};
++
++/**
++ * gb_frame_source_add:
++ * @frames_per_sec: (in): Target frames per second.
++ * @callback: (in) (scope notified): A #GSourceFunc to execute.
++ * @user_data: (in): User data for @callback.
++ *
++ * Creates a new frame source that will execute when the timeout interval
++ * for the source has elapsed. The timing will try to synchronize based
++ * on the end time of the animation.
++ *
++ * Returns: A source id that can be removed with g_source_remove().
++ */
++guint
++_gb_frame_source_add (guint frames_per_sec,
++ GSourceFunc callback,
++ gpointer user_data)
++{
++ GbFrameSource *fsource;
++ GSource *source;
++ guint ret;
++
++ g_return_val_if_fail(frames_per_sec > 0, 0);
++ g_return_val_if_fail(frames_per_sec < 120, 0);
++
++ source = g_source_new(&source_funcs, sizeof(GbFrameSource));
++ fsource = (GbFrameSource *)source;
++ fsource->fps = frames_per_sec;
++ fsource->frame_count = 0;
++ fsource->start_time = g_get_monotonic_time() / 1000;
++ g_source_set_callback(source, callback, user_data, NULL);
++ g_source_set_name(source, "GbFrameSource");
++
++ ret = g_source_attach(source, NULL);
++ g_source_unref(source);
++
++ return ret;
++}
+diff --git a/gtk/gb-frame-source.h b/gtk/gb-frame-source.h
+new file mode 100644
+index 0000000..502d86e
+--- /dev/null
++++ b/gtk/gb-frame-source.h
+@@ -0,0 +1,32 @@
++/* gb-frame-source.h
++ *
++ * Copyright (C) 2012 Christian Hergert <chris@dronelabs.com>
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program 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 General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef GB_FRAME_SOURCE_H
++#define GB_FRAME_SOURCE_H
++
++#include <glib.h>
++
++G_BEGIN_DECLS
++
++guint _gb_frame_source_add (guint frames_per_sec,
++ GSourceFunc callback,
++ gpointer user_data);
++
++G_END_DECLS
++
++#endif /* GB_FRAME_SOURCE_H */
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 821981f..77d485f 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -32,6 +32,7 @@
+ #include "gtkscrolledwindow.h"
+ #include "gtkwindow.h"
+ #include "gtkprivate.h"
++#include "gb-animation.h"
+ #include "gtkintl.h"
+ #include "gtkmain.h"
+ #include "gtkdnd.h"
+@@ -111,6 +112,17 @@ typedef struct {
+
+ gdouble unclamped_hadj_value;
+ gdouble unclamped_vadj_value;
++
++ GtkAdjustment *opacity;
++ GbAnimation *opacity_anim;
++
++ gint sb_min_height;
++ gint sb_padding;
++ gint sb_radius;
++ gint sb_width;
++ gboolean sb_fading_in;
++ gint sb_fade_out_delay;
++ guint sb_fade_out_id;
+ } GtkScrolledWindowPrivate;
+
+ #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate))
+@@ -206,10 +218,21 @@ static gboolean _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindo
+ gboolean allow_overshooting,
+ gboolean snap_to_border);
+
++static void gtk_scrolled_window_cancel_animation (GtkScrolledWindow *scrolled_window);
++static void gtk_scrolled_window_start_fade_in_animation (GtkScrolledWindow *scrolled_window);
++static void gtk_scrolled_window_start_fade_out_animation (GtkScrolledWindow *scrolled_window);
++static gboolean gtk_scrolled_window_child_expose (GtkWidget *widget,
++ GdkEventExpose *eevent,
++ GtkScrolledWindow *scrolled_window);
++static void gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj,
++ GtkScrolledWindow *scrolled_window);
++
+ static guint signals[LAST_SIGNAL] = {0};
+
+ G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
+
++static gboolean overlay_scrollbars = TRUE;
++
+ static void
+ add_scroll_binding (GtkBindingSet *binding_set,
+ guint keyval,
+@@ -444,6 +467,8 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
+ static void
+ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ {
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
+ gtk_widget_set_has_window (GTK_WIDGET (scrolled_window), FALSE);
+ gtk_widget_set_can_focus (GTK_WIDGET (scrolled_window), TRUE);
+
+@@ -462,6 +487,24 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
+ gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
+ }
++
++ if (overlay_scrollbars)
++ {
++ priv->opacity = g_object_new (GTK_TYPE_ADJUSTMENT,
++ "lower", 0.0,
++ "upper", 0.5,
++ "value", 0.0,
++ NULL);
++ priv->sb_min_height = 20;
++ priv->sb_padding = 2;
++ priv->sb_radius = 3;
++ priv->sb_width = 6;
++ priv->sb_fade_out_delay = 1000;
++
++ g_signal_connect (priv->opacity, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
++ }
+ }
+
+ /**
+@@ -541,6 +584,17 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
++
++ if (overlay_scrollbars)
++ {
++ g_signal_handlers_disconnect_by_func (old_adjustment,
++ gtk_scrolled_window_adjustment_value_changed,
++ scrolled_window);
++ g_signal_handlers_disconnect_by_func (old_adjustment,
++ gtk_scrolled_window_expose_scrollbars,
++ scrolled_window);
++ }
++
+ gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
+ hadjustment);
+ }
+@@ -556,10 +610,24 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
+
++ if (overlay_scrollbars)
++ {
++ g_signal_connect (hadjustment, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
++ scrolled_window);
++
++ g_signal_connect (hadjustment, "changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
++ g_signal_connect (hadjustment, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
++ }
++
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+- gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+- gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
++ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
++ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
+
+ g_object_notify (G_OBJECT (scrolled_window), "hadjustment");
+ }
+@@ -607,6 +675,17 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
++
++ if (overlay_scrollbars)
++ {
++ g_signal_handlers_disconnect_by_func (old_adjustment,
++ gtk_scrolled_window_adjustment_value_changed,
++ scrolled_window);
++ g_signal_handlers_disconnect_by_func (old_adjustment,
++ gtk_scrolled_window_expose_scrollbars,
++ scrolled_window);
++ }
++
+ gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
+ vadjustment);
+ }
+@@ -622,10 +701,25 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
+
++ if (overlay_scrollbars)
++ {
++ g_signal_connect (vadjustment,
++ "value-changed",
++ G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
++ scrolled_window);
++
++ g_signal_connect (vadjustment, "changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
++ g_signal_connect (vadjustment, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
++ }
++
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+- gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+- gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
++ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
++ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
+
+ g_object_notify (G_OBJECT (scrolled_window), "vadjustment");
+ }
+@@ -1073,11 +1167,24 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
++ gtk_scrolled_window_cancel_animation (scrolled_window);
++
+ if (scrolled_window->hscrollbar)
+ {
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
++
++ if (overlay_scrollbars)
++ {
++ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
++ gtk_scrolled_window_adjustment_value_changed,
++ scrolled_window);
++ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
++ gtk_scrolled_window_expose_scrollbars,
++ scrolled_window);
++ }
++
+ gtk_widget_unparent (scrolled_window->hscrollbar);
+ gtk_widget_destroy (scrolled_window->hscrollbar);
+ g_object_unref (scrolled_window->hscrollbar);
+@@ -1088,6 +1195,17 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
++
++ if (overlay_scrollbars)
++ {
++ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
++ gtk_scrolled_window_adjustment_value_changed,
++ scrolled_window);
++ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
++ gtk_scrolled_window_expose_scrollbars,
++ scrolled_window);
++ }
++
+ gtk_widget_unparent (scrolled_window->vscrollbar);
+ gtk_widget_destroy (scrolled_window->vscrollbar);
+ g_object_unref (scrolled_window->vscrollbar);
+@@ -1515,7 +1633,7 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+
+ if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
+ requisition->width += child_requisition.width;
+- else
++ else if (! overlay_scrollbars)
+ {
+ GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
+
+@@ -1530,7 +1648,7 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+
+ if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
+ requisition->height += child_requisition.height;
+- else
++ else if (! overlay_scrollbars)
+ {
+ GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
+
+@@ -1544,20 +1662,23 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+ }
+ }
+
+- if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
+- scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
++ if (! overlay_scrollbars)
+ {
+- requisition->width = MAX (requisition->width, hscrollbar_requisition.width);
+- if (!extra_height || scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
+- extra_height = scrollbar_spacing + hscrollbar_requisition.height;
+- }
++ if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
++ scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
++ {
++ requisition->width = MAX (requisition->width, hscrollbar_requisition.width);
++ if (!extra_height || scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
++ extra_height = scrollbar_spacing + hscrollbar_requisition.height;
++ }
+
+- if (scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
+- scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
+- {
+- requisition->height = MAX (requisition->height, vscrollbar_requisition.height);
+- if (!extra_height || scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
+- extra_width = scrollbar_spacing + vscrollbar_requisition.width;
++ if (scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
++ scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
++ {
++ requisition->height = MAX (requisition->height, vscrollbar_requisition.height);
++ if (!extra_height || scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
++ extra_width = scrollbar_spacing + vscrollbar_requisition.width;
++ }
+ }
+
+ requisition->width += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_width);
+@@ -1598,6 +1719,9 @@ gtk_scrolled_window_relative_allocation (GtkWidget *widget,
+ allocation->width = MAX (1, (gint)widget->allocation.width - allocation->x * 2);
+ allocation->height = MAX (1, (gint)widget->allocation.height - allocation->y * 2);
+
++ if (overlay_scrollbars)
++ return;
++
+ if (scrolled_window->vscrollbar_visible)
+ {
+ GtkRequisition vscrollbar_requisition;
+@@ -1754,6 +1878,9 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ bin = GTK_BIN (scrolled_window);
+
++ if (overlay_scrollbars)
++ gtk_scrolled_window_expose_scrollbars (NULL, scrolled_window);
++
+ scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+ gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
+
+@@ -1812,7 +1939,7 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
+ }
+
+- if (scrolled_window->hscrollbar_visible)
++ if (!overlay_scrollbars && scrolled_window->hscrollbar_visible)
+ {
+ GtkRequisition hscrollbar_requisition;
+ gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
+@@ -1860,7 +1987,7 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ else if (gtk_widget_get_visible (scrolled_window->hscrollbar))
+ gtk_widget_hide (scrolled_window->hscrollbar);
+
+- if (scrolled_window->vscrollbar_visible)
++ if (!overlay_scrollbars && scrolled_window->vscrollbar_visible)
+ {
+ GtkRequisition vscrollbar_requisition;
+ if (!gtk_widget_get_visible (scrolled_window->vscrollbar))
+@@ -1930,7 +2057,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
+ {
+ if (delta_x != 0.0 && scrolled_window->hscrollbar &&
+- gtk_widget_get_visible (scrolled_window->hscrollbar))
++ (overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar)))
+ {
+ GtkAdjustment *adj;
+ gdouble new_value;
+@@ -1948,7 +2075,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ }
+
+ if (delta_y != 0.0 && scrolled_window->vscrollbar &&
+- gtk_widget_get_visible (scrolled_window->vscrollbar))
++ (overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar)))
+ {
+ GtkAdjustment *adj;
+ gdouble new_value;
+@@ -1974,7 +2101,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ else
+ range = scrolled_window->hscrollbar;
+
+- if (range && gtk_widget_get_visible (range))
++ if (range && (overlay_scrollbars || gtk_widget_get_visible (range)))
+ {
+ GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
+ gdouble delta, new_value;
+@@ -2614,6 +2741,9 @@ gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
+ gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
+ }
+ }
++
++ if (overlay_scrollbars)
++ gtk_scrolled_window_start_fade_in_animation (scrolled_win);
+ }
+
+ static void
+@@ -2634,6 +2764,9 @@ gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
+ else if (scrolled_window->hscrollbar &&
+ adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)))
+ priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
++
++ if (overlay_scrollbars)
++ gtk_scrolled_window_start_fade_in_animation (scrolled_window);
+ }
+
+ static void
+@@ -2658,10 +2791,17 @@ gtk_scrolled_window_add (GtkContainer *container,
+
+ /* this is a temporary message */
+ if (!gtk_widget_set_scroll_adjustments (child,
+- gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+- gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar))))
++ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
++ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar))))
+ g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
+ "use gtk_scrolled_window_add_with_viewport() instead");
++
++ if (overlay_scrollbars)
++ {
++ g_signal_connect_after (child, "expose-event",
++ G_CALLBACK (gtk_scrolled_window_child_expose),
++ container);
++ }
+ }
+
+ static void
+@@ -2671,7 +2811,14 @@ gtk_scrolled_window_remove (GtkContainer *container,
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (GTK_BIN (container)->child == child);
+-
++
++ if (overlay_scrollbars)
++ {
++ g_signal_handlers_disconnect_by_func (child,
++ gtk_scrolled_window_child_expose,
++ container);
++ }
++
+ gtk_widget_set_scroll_adjustments (child, NULL, NULL);
+
+ /* chain parent class handler to remove child */
+@@ -2871,5 +3018,383 @@ gtk_scrolled_window_grab_notify (GtkWidget *widget,
+ }
+ }
+
++static void
++gtk_scrolled_window_rounded_rectangle (cairo_t *cr,
++ gint x,
++ gint y,
++ gint width,
++ gint height,
++ gint x_radius,
++ gint y_radius)
++{
++ gint x1, x2;
++ gint y1, y2;
++ gint xr1, xr2;
++ gint yr1, yr2;
++
++ x1 = x;
++ x2 = x1 + width;
++ y1 = y;
++ y2 = y1 + height;
++
++ x_radius = MIN (x_radius, width / 2.0);
++ y_radius = MIN (y_radius, height / 2.0);
++
++ xr1 = x_radius;
++ xr2 = x_radius / 2.0;
++ yr1 = y_radius;
++ yr2 = y_radius / 2.0;
++
++ cairo_move_to (cr, x1 + xr1, y1);
++ cairo_line_to (cr, x2 - xr1, y1);
++ cairo_curve_to (cr, x2 - xr2, y1, x2, y1 + yr2, x2, y1 + yr1);
++ cairo_line_to (cr, x2, y2 - yr1);
++ cairo_curve_to (cr, x2, y2 - yr2, x2 - xr2, y2, x2 - xr1, y2);
++ cairo_line_to (cr, x1 + xr1, y2);
++ cairo_curve_to (cr, x1 + xr2, y2, x1, y2 - yr2, x1, y2 - yr1);
++ cairo_line_to (cr, x1, y1 + yr1);
++ cairo_curve_to (cr, x1, y1 + yr2, x1 + xr2, y1, x1 + xr1, y1);
++ cairo_close_path (cr);
++}
++
++static void
++gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
++ GtkWidget *child,
++ GdkWindow *child_window,
++ GdkRectangle *vbar_rect,
++ GdkRectangle *vslider_rect,
++ GdkRectangle *hbar_rect,
++ GdkRectangle *hslider_rect)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GtkAdjustment *adj;
++ GtkAllocation allocation;
++ gdouble lower;
++ gdouble page_size;
++ gdouble upper;
++ gdouble value_h = 0.0;
++ gdouble value_v = 0.0;
++ gdouble ratio;
++ gdouble width;
++ gdouble height;
++ gdouble x;
++ gdouble y;
++ gint window_width;
++ gint window_height;
++ gint viewport_width;
++ gint viewport_height;
++
++ window_width = gdk_window_get_width (child_window);
++ window_height = gdk_window_get_height (child_window);
++
++ gtk_widget_get_allocation (child, &allocation);
++
++ viewport_width = MIN (window_width, allocation.width);
++ viewport_height = MIN (window_height, allocation.height);
++
++ if (scrolled_window->vscrollbar)
++ {
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++
++ value_v = gtk_adjustment_get_value (adj);
++ }
++
++ if (scrolled_window->hscrollbar)
++ {
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++
++ value_h = gtk_adjustment_get_value (adj);
++ }
++
++ if ((vbar_rect || vslider_rect) && scrolled_window->vscrollbar)
++ {
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++
++ g_object_get (adj,
++ "lower", &lower,
++ "upper", &upper,
++ "page-size", &page_size,
++ NULL);
++
++ ratio = page_size / (upper - lower);
++ if (ratio < 1.0)
++ {
++ height = ratio * (viewport_height - (2 * priv->sb_padding));
++ height = MAX (height, 20);
++ height = MIN (height, viewport_height - (2 * priv->sb_padding));
++
++ ratio = (value_v - lower) / (upper - page_size - lower);
++ y = ratio * (viewport_height - (2 * priv->sb_padding) - height) + priv->sb_padding;
++ x = viewport_width - priv->sb_width - priv->sb_padding;
++
++ if (window_width > allocation.width)
++ x += value_h;
++
++ if (window_height > allocation.height)
++ y += value_v;
++
++ if (vbar_rect)
++ {
++ vbar_rect->x = x - priv->sb_padding;
++ vbar_rect->y = 0;
++ vbar_rect->width = priv->sb_width + 2 * priv->sb_padding;
++ vbar_rect->height = viewport_height;
++ }
++
++ if (vslider_rect)
++ {
++ vslider_rect->x = x;
++ vslider_rect->y = y;
++ vslider_rect->width = priv->sb_width;
++ vslider_rect->height = height;
++ }
++ }
++ else
++ {
++ if (vbar_rect)
++ {
++ vbar_rect->x = 0;
++ vbar_rect->y = 0;
++ vbar_rect->width = 0;
++ vbar_rect->height = 0;
++ }
++
++ if (vslider_rect)
++ {
++ vslider_rect->x = 0;
++ vslider_rect->y = 0;
++ vslider_rect->width = 0;
++ vslider_rect->height = 0;
++ }
++ }
++ }
++
++ if ((hbar_rect || hslider_rect) && scrolled_window->hscrollbar)
++ {
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++
++ g_object_get (adj,
++ "lower", &lower,
++ "upper", &upper,
++ "page-size", &page_size,
++ NULL);
++
++ ratio = page_size / (upper - lower);
++ if (ratio < 1.0)
++ {
++ width = ratio * (viewport_width - (2 * priv->sb_padding));
++ width = MAX (width, 20);
++ width = MIN (width, viewport_width - (2 * priv->sb_padding));
++
++ ratio = (value_h - lower) / (upper - page_size - lower);
++ x = ratio * (viewport_width - (2 * priv->sb_padding) - width) + priv->sb_padding;
++ y = viewport_height - priv->sb_width - priv->sb_padding;
++
++ if (window_width > allocation.width)
++ x += value_h;
++
++ if (window_height > allocation.height)
++ y += value_v;
++
++ if (hbar_rect)
++ {
++ hbar_rect->x = 0;
++ hbar_rect->y = y - priv->sb_padding;
++ hbar_rect->width = viewport_width;
++ hbar_rect->height = priv->sb_width + 2 * priv->sb_padding;
++ }
++
++ if (hslider_rect)
++ {
++ hslider_rect->x = x;
++ hslider_rect->y = y;
++ hslider_rect->width = width;
++ hslider_rect->height = priv->sb_width;
++ }
++ }
++ else
++ {
++ if (hbar_rect)
++ {
++ hbar_rect->x = 0;
++ hbar_rect->y = 0;
++ hbar_rect->width = 0;
++ hbar_rect->height = 0;
++ }
++
++ if (hslider_rect)
++ {
++ hslider_rect->x = 0;
++ hslider_rect->y = 0;
++ hslider_rect->width = 0;
++ hslider_rect->height = 0;
++ }
++ }
++ }
++}
++
++static gboolean
++gtk_scrolled_window_child_expose (GtkWidget *widget,
++ GdkEventExpose *eevent,
++ GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GdkRectangle vbar_rect;
++ GdkRectangle vslider_rect;
++ GdkRectangle hbar_rect;
++ GdkRectangle hslider_rect;
++ cairo_t *cr;
++
++ cr = gdk_cairo_create (eevent->window);
++ gdk_cairo_region (cr, eevent->region);
++ cairo_clip (cr);
++
++ gtk_scrolled_window_get_child_scroll_areas (scrolled_window,
++ gtk_bin_get_child (GTK_BIN (scrolled_window)),
++ eevent->window,
++ &vbar_rect, &vslider_rect,
++ &hbar_rect, &hslider_rect);
++
++ if (TRUE)
++ {
++ if (scrolled_window->vscrollbar && vbar_rect.width > 0)
++ gdk_cairo_rectangle (cr, &vbar_rect);
++
++ if (scrolled_window->hscrollbar && hbar_rect.width > 0)
++ gdk_cairo_rectangle (cr, &hbar_rect);
++
++ cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
++ cairo_fill (cr);
++ }
++
++ if (scrolled_window->vscrollbar && vslider_rect.width > 0)
++ gtk_scrolled_window_rounded_rectangle (cr,
++ vslider_rect.x,
++ vslider_rect.y,
++ vslider_rect.width,
++ vslider_rect.height,
++ priv->sb_radius,
++ priv->sb_radius);
++
++ if (scrolled_window->hscrollbar && hslider_rect.width > 0)
++ gtk_scrolled_window_rounded_rectangle (cr,
++ hslider_rect.x,
++ hslider_rect.y,
++ hslider_rect.width,
++ hslider_rect.height,
++ priv->sb_radius,
++ priv->sb_radius);
++
++ cairo_set_source_rgba (cr, 0, 0, 0, gtk_adjustment_get_value (priv->opacity));
++ cairo_fill (cr);
++
++ cairo_destroy (cr);
++
++ return FALSE;
++}
++
++static void
++gtk_scrolled_window_cancel_animation (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GbAnimation *anim = priv->opacity_anim;
++
++ if (anim)
++ {
++ priv->opacity_anim = NULL;
++ g_object_remove_weak_pointer (G_OBJECT (anim),
++ (gpointer *) &priv->opacity_anim);
++ _gb_animation_stop (anim);
++ }
++
++ if (priv->sb_fade_out_id)
++ {
++ g_source_remove (priv->sb_fade_out_id);
++ priv->sb_fade_out_id = 0;
++ }
++
++ priv->sb_fading_in = FALSE;
++}
++
++static gboolean
++gtk_scrolled_window_fade_out_timeout (GtkScrolledWindow *scrolled_window)
++{
++ gtk_scrolled_window_start_fade_out_animation (scrolled_window);
++
++ return FALSE;
++}
++
++static void
++gtk_scrolled_window_start_fade_in_animation (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ gdouble upper;
++
++ if (priv->sb_fading_in)
++ return;
++
++ gtk_scrolled_window_cancel_animation (scrolled_window);
++
++ priv->sb_fading_in = TRUE;
++
++ upper = gtk_adjustment_get_upper (priv->opacity);
++ priv->opacity_anim = _gb_object_animate (priv->opacity,
++ GB_ANIMATION_EASE_OUT_QUAD,
++ 100,
++ "value", upper,
++ NULL);
++ g_object_add_weak_pointer (G_OBJECT (priv->opacity_anim),
++ (gpointer *) &priv->opacity_anim);
++
++ priv->sb_fade_out_id =
++ gdk_threads_add_timeout (priv->sb_fade_out_delay,
++ (GSourceFunc) gtk_scrolled_window_fade_out_timeout,
++ scrolled_window);
++}
++
++static void
++gtk_scrolled_window_start_fade_out_animation (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ gtk_scrolled_window_cancel_animation (scrolled_window);
++
++ priv->opacity_anim = _gb_object_animate (priv->opacity,
++ GB_ANIMATION_EASE_IN_QUAD,
++ 300,
++ "value", 0.0,
++ NULL);
++ g_object_add_weak_pointer (G_OBJECT (priv->opacity_anim),
++ (gpointer *) &priv->opacity_anim);
++}
++
++static void
++gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj,
++ GtkScrolledWindow *scrolled_window)
++{
++ GtkWidget *child = gtk_bin_get_child (GTK_BIN (scrolled_window));
++
++ if (child && gtk_widget_get_visible (child))
++ {
++ GtkAllocation alloc;
++
++ gtk_widget_get_allocation (child, &alloc);
++
++ if (scrolled_window->vscrollbar)
++ gtk_widget_queue_draw_area (child,
++ alloc.width - 20,
++ 0,
++ 20,
++ alloc.height);
++
++ if (scrolled_window->hscrollbar)
++ gtk_widget_queue_draw_area (child,
++ 0,
++ alloc.height - 20,
++ alloc.width,
++ 20);
++ }
++}
++
+ #define __GTK_SCROLLED_WINDOW_C__
+ #include "gtkaliasdef.c"
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 713b51c19644653815b8ff1cd629d706f1074043 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Tue, 10 Jul 2012 14:33:45 +0200
+Subject: [PATCH 14/68] gtk: add event handling to GtkScrolledWindow's overlay
+ scrollbars
+
+---
+ gtk/gtkscrolledwindow.c | 438 +++++++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 403 insertions(+), 35 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 77d485f..70de6ec 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -84,6 +84,10 @@
+ #define OVERSHOOT_INVERSE_ACCELERATION 0.003
+ #define RELEASE_EVENT_TIMEOUT 1000
+
++/* Overlay scrollbars */
++#define SCROLL_INTERVAL_INITIAL 300
++#define SCROLL_INTERVAL_REPEAT 100
++
+ typedef struct {
+ gboolean window_placement_set;
+ GtkCornerType real_window_placement;
+@@ -123,6 +127,19 @@ typedef struct {
+ gboolean sb_fading_in;
+ gint sb_fade_out_delay;
+ guint sb_fade_out_id;
++
++ gboolean sb_hovering;
++ gboolean sb_pointer_grabbed;
++ gboolean sb_grab_vscroll;
++ gboolean sb_grab_hscroll;
++ gboolean sb_drag_slider;
++ gboolean sb_visible;
++
++ gint sb_grab_offset_x;
++ gint sb_grab_offset_y;
++
++ gint sb_scroll_direction;
++ guint sb_scroll_timeout_id;
+ } GtkScrolledWindowPrivate;
+
+ #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate))
+@@ -219,8 +236,24 @@ static gboolean _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindo
+ gboolean snap_to_border);
+
+ static void gtk_scrolled_window_cancel_animation (GtkScrolledWindow *scrolled_window);
++static void gtk_scrolled_window_start_fade_out_timeout (GtkScrolledWindow *scrolled_window);
++static void gtk_scrolled_window_stop_fade_out_timeout (GtkScrolledWindow *scrolled_window);
+ static void gtk_scrolled_window_start_fade_in_animation (GtkScrolledWindow *scrolled_window);
+ static void gtk_scrolled_window_start_fade_out_animation (GtkScrolledWindow *scrolled_window);
++static gboolean
++ gtk_scrolled_window_over_child_scroll_areas (GtkScrolledWindow *scrolled_window,
++ GdkEvent *event,
++ gint x,
++ gint y,
++ gboolean *over_vscroll,
++ gboolean *over_hscroll);
++static void gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
++ GtkWidget *child,
++ GdkWindow *child_window,
++ GdkRectangle *vbar_rect,
++ GdkRectangle *vslider_rect,
++ GdkRectangle *hbar_rect,
++ GdkRectangle *hslider_rect);
+ static gboolean gtk_scrolled_window_child_expose (GtkWidget *widget,
+ GdkEventExpose *eevent,
+ GtkScrolledWindow *scrolled_window);
+@@ -2385,8 +2418,8 @@ gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
+ }
+
+ static gboolean
+-gtk_scrolled_window_captured_button_release (GtkWidget *widget,
+- GdkEvent *event)
++gtk_scrolled_window_captured_button_release_kinetic (GtkWidget *widget,
++ GdkEvent *event)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv;
+@@ -2462,8 +2495,8 @@ gtk_scrolled_window_captured_button_release (GtkWidget *widget,
+ }
+
+ static gboolean
+-gtk_scrolled_window_captured_motion_notify (GtkWidget *widget,
+- GdkEvent *event)
++gtk_scrolled_window_captured_motion_notify_kinetic (GtkWidget *widget,
++ GdkEvent *event)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv;
+@@ -2554,8 +2587,8 @@ gtk_scrolled_window_captured_motion_notify (GtkWidget *widget,
+ }
+
+ static gboolean
+-gtk_scrolled_window_captured_button_press (GtkWidget *widget,
+- GdkEvent *event)
++gtk_scrolled_window_captured_button_press_kinetic (GtkWidget *widget,
++ GdkEvent *event)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv;
+@@ -2633,29 +2666,283 @@ gtk_scrolled_window_captured_button_press (GtkWidget *widget,
+ return FALSE;
+ }
+
++static void
++gtk_scrolled_window_scroll_step (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GtkAdjustment *adj;
++ gdouble value;
++
++ if (priv->sb_grab_vscroll)
++ {
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++ }
++ else if (priv->sb_grab_hscroll)
++ {
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++ }
++
++ value = adj->value + (priv->sb_scroll_direction * adj->page_size);
++ value = CLAMP (value, adj->lower, adj->upper - adj->page_size);
++
++ gtk_adjustment_set_value (adj, value);
++}
++
++static gboolean
++gtk_scrolled_window_scroll_step_timeout (gpointer data)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (data);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ gtk_scrolled_window_scroll_step (scrolled_window);
++
++ g_source_remove (priv->sb_scroll_timeout_id);
++
++ priv->sb_scroll_timeout_id =
++ gdk_threads_add_timeout (SCROLL_INTERVAL_REPEAT,
++ gtk_scrolled_window_scroll_step_timeout,
++ scrolled_window);
++
++ return FALSE;
++}
++
++static gboolean
++gtk_scrolled_window_captured_button_press_scrollbar (GtkWidget *widget,
++ GdkEvent *event)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GdkEventButton *bevent = (GdkEventButton *) event;
++
++ if (bevent->button != 1)
++ return FALSE;
++
++ if (gtk_scrolled_window_over_child_scroll_areas (scrolled_window, event,
++ bevent->x, bevent->y,
++ &priv->sb_grab_vscroll,
++ &priv->sb_grab_hscroll))
++ {
++ GdkRectangle vbar_rect;
++ GdkRectangle vslider_rect;
++ GdkRectangle hbar_rect;
++ GdkRectangle hslider_rect;
++
++ priv->sb_pointer_grabbed = TRUE;
++ gtk_grab_add (widget);
++
++ gtk_scrolled_window_get_child_scroll_areas (scrolled_window,
++ gtk_bin_get_child (GTK_BIN (widget)),
++ bevent->window,
++ &vbar_rect, &vslider_rect,
++ &hbar_rect, &hslider_rect);
++
++ if (priv->sb_grab_vscroll)
++ {
++ /* we consider the entire width of the scrollbar clickable */
++ vslider_rect.x = vbar_rect.x;
++ vslider_rect.width = vbar_rect.width;
++
++ if (bevent->x >= vslider_rect.x &&
++ bevent->x < (vslider_rect.x + vslider_rect.width) &&
++ bevent->y >= vslider_rect.y &&
++ bevent->y < (vslider_rect.y + vslider_rect.height))
++ {
++ priv->sb_drag_slider = TRUE;
++ priv->sb_grab_offset_y = bevent->y - vslider_rect.y;
++ }
++ else
++ {
++ priv->sb_drag_slider = FALSE;
++ priv->sb_grab_offset_y = bevent->y - vbar_rect.y;
++
++ if (bevent->y < vslider_rect.y)
++ priv->sb_scroll_direction = -1;
++ else
++ priv->sb_scroll_direction = 1;
++ }
++ }
++ else if (priv->sb_grab_hscroll)
++ {
++ /* we consider the entire height of the scrollbar clickable */
++ hslider_rect.y = hbar_rect.y;
++ hslider_rect.height = hbar_rect.height;
++
++ if (bevent->x >= hslider_rect.x &&
++ bevent->x < (hslider_rect.x + hslider_rect.width) &&
++ bevent->y >= hslider_rect.y &&
++ bevent->y < (hslider_rect.y + hslider_rect.height))
++ {
++ priv->sb_drag_slider = TRUE;
++ priv->sb_grab_offset_x = bevent->x - hslider_rect.x;
++ }
++ else
++ {
++ priv->sb_drag_slider = FALSE;
++ priv->sb_grab_offset_x = bevent->x - hbar_rect.x;
++
++ if (bevent->x < hslider_rect.x)
++ priv->sb_scroll_direction = -1;
++ else
++ priv->sb_scroll_direction = 1;
++ }
++ }
++
++ if ((priv->sb_grab_vscroll || priv->sb_grab_hscroll) &&
++ !priv->sb_drag_slider)
++ {
++ gtk_scrolled_window_scroll_step (scrolled_window);
++
++ priv->sb_scroll_timeout_id =
++ gdk_threads_add_timeout (SCROLL_INTERVAL_INITIAL,
++ gtk_scrolled_window_scroll_step_timeout,
++ scrolled_window);
++ }
++
++ return TRUE;
++ }
++
++ return FALSE;
++}
++
++static gboolean
++gtk_scrolled_window_captured_button_release_scrollbar (GtkWidget *widget,
++ GdkEvent *event)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GdkEventButton *bevent = (GdkEventButton *) event;
++
++ if (bevent->button != 1)
++ return FALSE;
++
++ gtk_grab_remove (widget);
++ priv->sb_pointer_grabbed = FALSE;
++
++ if (priv->sb_scroll_timeout_id)
++ {
++ g_source_remove (priv->sb_scroll_timeout_id);
++ priv->sb_scroll_timeout_id = 0;
++ }
++
++ return TRUE;
++}
++
++static gboolean
++gtk_scrolled_window_captured_motion_notify_scrollbar (GtkWidget *widget,
++ GdkEvent *event)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GdkEventMotion *mevent = (GdkEventMotion *) event;
++
++ if (priv->sb_pointer_grabbed)
++ {
++ if (priv->sb_drag_slider)
++ {
++ GdkRectangle vbar_rect;
++ GdkRectangle vslider_rect;
++ GdkRectangle hbar_rect;
++ GdkRectangle hslider_rect;
++ GtkAdjustment *adj;
++ gint pos;
++ gint visible_range;
++ gdouble value;
++
++ gtk_scrolled_window_get_child_scroll_areas (scrolled_window,
++ gtk_bin_get_child (GTK_BIN (widget)),
++ mevent->window,
++ &vbar_rect, &vslider_rect,
++ &hbar_rect, &hslider_rect);
++
++ if (priv->sb_grab_vscroll)
++ {
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++ pos = mevent->y - priv->sb_grab_offset_y - vbar_rect.y;
++ visible_range = vbar_rect.height - vslider_rect.height;
++ }
++ else if (priv->sb_grab_hscroll)
++ {
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++ pos = mevent->x - priv->sb_grab_offset_x - hbar_rect.x;
++ visible_range = hbar_rect.width - hslider_rect.width;
++ }
++
++ pos = CLAMP (pos, 0, visible_range);
++
++ value = (adj->upper - adj->page_size - adj->lower) * pos / visible_range;
++
++ gtk_adjustment_set_value (adj, value);
++ }
++
++ return TRUE;
++ }
++ else
++ {
++ if (gtk_scrolled_window_over_child_scroll_areas (scrolled_window, event,
++ mevent->x, mevent->y,
++ NULL, NULL))
++ {
++ priv->sb_hovering = TRUE;
++ priv->sb_visible = TRUE;
++
++ gtk_scrolled_window_start_fade_in_animation (scrolled_window);
++ gtk_scrolled_window_stop_fade_out_timeout (scrolled_window);
++
++ /* needed when entering the scrollbar */
++ gtk_scrolled_window_expose_scrollbars (NULL, scrolled_window);
++
++ return TRUE;
++ }
++
++ priv->sb_hovering = FALSE;
++
++ if (priv->sb_visible || gtk_adjustment_get_value (priv->opacity) > 0.0)
++ {
++ /* keep visible scrollbars visible while the mouse is moving */
++ gtk_scrolled_window_start_fade_in_animation (scrolled_window);
++ gtk_scrolled_window_stop_fade_out_timeout (scrolled_window);
++ gtk_scrolled_window_start_fade_out_timeout (scrolled_window);
++ }
++
++ return FALSE;
++ }
++}
++
+ static gboolean
+ gtk_scrolled_window_captured_event (GtkWidget *widget,
+ GdkEvent *event)
+ {
+- gboolean retval = FALSE;
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (widget);
++ gboolean retval = FALSE;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+- retval = gtk_scrolled_window_captured_button_press (widget, event);
++ retval = gtk_scrolled_window_captured_button_press_scrollbar (widget, event);
++ if (!retval)
++ retval = gtk_scrolled_window_captured_button_press_kinetic (widget, event);
+ break;
+ case GDK_BUTTON_RELEASE:
+- if (priv->pointer_grabbed)
+- retval = gtk_scrolled_window_captured_button_release (widget, event);
++ if (priv->sb_pointer_grabbed)
++ retval = gtk_scrolled_window_captured_button_release_scrollbar (widget, event);
++ else if (priv->pointer_grabbed)
++ retval = gtk_scrolled_window_captured_button_release_kinetic (widget, event);
+ else
+ priv->last_button_event_valid = FALSE;
+ break;
+ case GDK_MOTION_NOTIFY:
+- if (priv->pointer_grabbed)
+- retval = gtk_scrolled_window_captured_motion_notify (widget, event);
++ if (priv->sb_pointer_grabbed || !priv->pointer_grabbed)
++ retval = gtk_scrolled_window_captured_motion_notify_scrollbar (widget, event);
++ else if (priv->pointer_grabbed)
++ retval = gtk_scrolled_window_captured_motion_notify_kinetic (widget, event);
+ break;
+ case GDK_LEAVE_NOTIFY:
++ if (!priv->in_drag && !priv->sb_pointer_grabbed)
++ {
++ gtk_scrolled_window_start_fade_out_timeout (scrolled_window);
++ priv->sb_hovering = FALSE;
++ }
+ case GDK_ENTER_NOTIFY:
+ if (priv->in_drag &&
+ event->crossing.mode != GDK_CROSSING_GRAB)
+@@ -3016,6 +3303,17 @@ gtk_scrolled_window_grab_notify (GtkWidget *widget,
+
+ priv->last_button_event_valid = FALSE;
+ }
++
++ if (priv->sb_pointer_grabbed && !was_grabbed)
++ {
++ priv->sb_pointer_grabbed = FALSE;
++
++ if (priv->sb_scroll_timeout_id)
++ {
++ g_source_remove (priv->sb_scroll_timeout_id);
++ priv->sb_scroll_timeout_id = 0;
++ }
++ }
+ }
+
+ static void
+@@ -3057,6 +3355,56 @@ gtk_scrolled_window_rounded_rectangle (cairo_t *cr,
+ cairo_close_path (cr);
+ }
+
++static gboolean
++gtk_scrolled_window_over_child_scroll_areas (GtkScrolledWindow *scrolled_window,
++ GdkEvent *event,
++ gint x,
++ gint y,
++ gboolean *over_vscroll,
++ gboolean *over_hscroll)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GtkWidget *child;
++ GdkRectangle vbar_rect;
++ GdkRectangle hbar_rect;
++ gboolean over_v = FALSE;
++ gboolean over_h = FALSE;
++
++ child = gtk_bin_get_child (GTK_BIN (scrolled_window));
++ if (!child)
++ return FALSE;
++
++ if (gtk_get_event_widget (event) != child)
++ return FALSE;
++
++ if (gtk_adjustment_get_value (priv->opacity) == 0.0)
++ return FALSE;
++
++ gtk_scrolled_window_get_child_scroll_areas (scrolled_window,
++ child,
++ ((GdkEventAny *) event)->window,
++ &vbar_rect, NULL,
++ &hbar_rect, NULL);
++
++ if (vbar_rect.width > 0 &&
++ x >= vbar_rect.x && x < (vbar_rect.x + vbar_rect.width) &&
++ y >= vbar_rect.y && y < (vbar_rect.y + vbar_rect.height))
++ {
++ over_v = TRUE;
++ }
++ else if (hbar_rect.width > 0 &&
++ x >= hbar_rect.x && x < (hbar_rect.x + hbar_rect.width) &&
++ y >= hbar_rect.y && y < (hbar_rect.y + hbar_rect.height))
++ {
++ over_h = TRUE;
++ }
++
++ if (over_vscroll) *over_vscroll = over_v;
++ if (over_hscroll) *over_hscroll = over_h;
++
++ return over_v || over_h;
++}
++
+ static void
+ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ GtkWidget *child,
+@@ -3083,6 +3431,8 @@ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ gint window_height;
+ gint viewport_width;
+ gint viewport_height;
++ gint offset_x = 0;
++ gint offset_y = 0;
+
+ window_width = gdk_window_get_width (child_window);
+ window_height = gdk_window_get_height (child_window);
+@@ -3106,6 +3456,12 @@ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ value_h = gtk_adjustment_get_value (adj);
+ }
+
++ if (window_width > allocation.width)
++ offset_x = value_h;
++
++ if (window_height > allocation.height)
++ offset_y = value_v;
++
+ if ((vbar_rect || vslider_rect) && scrolled_window->vscrollbar)
+ {
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
+@@ -3127,16 +3483,13 @@ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ y = ratio * (viewport_height - (2 * priv->sb_padding) - height) + priv->sb_padding;
+ x = viewport_width - priv->sb_width - priv->sb_padding;
+
+- if (window_width > allocation.width)
+- x += value_h;
+-
+- if (window_height > allocation.height)
+- y += value_v;
++ x += offset_x;
++ y += offset_y;
+
+ if (vbar_rect)
+ {
+ vbar_rect->x = x - priv->sb_padding;
+- vbar_rect->y = 0;
++ vbar_rect->y = offset_y;
+ vbar_rect->width = priv->sb_width + 2 * priv->sb_padding;
+ vbar_rect->height = viewport_height;
+ }
+@@ -3190,15 +3543,12 @@ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ x = ratio * (viewport_width - (2 * priv->sb_padding) - width) + priv->sb_padding;
+ y = viewport_height - priv->sb_width - priv->sb_padding;
+
+- if (window_width > allocation.width)
+- x += value_h;
+-
+- if (window_height > allocation.height)
+- y += value_v;
++ x += offset_x;
++ y += offset_y;
+
+ if (hbar_rect)
+ {
+- hbar_rect->x = 0;
++ hbar_rect->x = offset_x;
+ hbar_rect->y = y - priv->sb_padding;
+ hbar_rect->width = viewport_width;
+ hbar_rect->height = priv->sb_width + 2 * priv->sb_padding;
+@@ -3255,7 +3605,7 @@ gtk_scrolled_window_child_expose (GtkWidget *widget,
+ &vbar_rect, &vslider_rect,
+ &hbar_rect, &hslider_rect);
+
+- if (TRUE)
++ if (priv->sb_visible)
+ {
+ if (scrolled_window->vscrollbar && vbar_rect.width > 0)
+ gdk_cairo_rectangle (cr, &vbar_rect);
+@@ -3263,7 +3613,7 @@ gtk_scrolled_window_child_expose (GtkWidget *widget,
+ if (scrolled_window->hscrollbar && hbar_rect.width > 0)
+ gdk_cairo_rectangle (cr, &hbar_rect);
+
+- cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
++ cairo_set_source_rgba (cr, 0, 0, 0, gtk_adjustment_get_value (priv->opacity) / 2.0);
+ cairo_fill (cr);
+ }
+
+@@ -3307,11 +3657,7 @@ gtk_scrolled_window_cancel_animation (GtkScrolledWindow *scrolled_window)
+ _gb_animation_stop (anim);
+ }
+
+- if (priv->sb_fade_out_id)
+- {
+- g_source_remove (priv->sb_fade_out_id);
+- priv->sb_fade_out_id = 0;
+- }
++ gtk_scrolled_window_stop_fade_out_timeout (scrolled_window);
+
+ priv->sb_fading_in = FALSE;
+ }
+@@ -3325,6 +3671,30 @@ gtk_scrolled_window_fade_out_timeout (GtkScrolledWindow *scrolled_window)
+ }
+
+ static void
++gtk_scrolled_window_start_fade_out_timeout (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ if (! priv->sb_fade_out_id)
++ priv->sb_fade_out_id =
++ gdk_threads_add_timeout (priv->sb_fade_out_delay,
++ (GSourceFunc) gtk_scrolled_window_fade_out_timeout,
++ scrolled_window);
++}
++
++static void
++gtk_scrolled_window_stop_fade_out_timeout (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ if (priv->sb_fade_out_id)
++ {
++ g_source_remove (priv->sb_fade_out_id);
++ priv->sb_fade_out_id = 0;
++ }
++}
++
++static void
+ gtk_scrolled_window_start_fade_in_animation (GtkScrolledWindow *scrolled_window)
+ {
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+@@ -3336,6 +3706,7 @@ gtk_scrolled_window_start_fade_in_animation (GtkScrolledWindow *scrolled_window)
+ gtk_scrolled_window_cancel_animation (scrolled_window);
+
+ priv->sb_fading_in = TRUE;
++ priv->sb_visible = priv->sb_hovering;
+
+ upper = gtk_adjustment_get_upper (priv->opacity);
+ priv->opacity_anim = _gb_object_animate (priv->opacity,
+@@ -3346,10 +3717,7 @@ gtk_scrolled_window_start_fade_in_animation (GtkScrolledWindow *scrolled_window)
+ g_object_add_weak_pointer (G_OBJECT (priv->opacity_anim),
+ (gpointer *) &priv->opacity_anim);
+
+- priv->sb_fade_out_id =
+- gdk_threads_add_timeout (priv->sb_fade_out_delay,
+- (GSourceFunc) gtk_scrolled_window_fade_out_timeout,
+- scrolled_window);
++ gtk_scrolled_window_start_fade_out_timeout (scrolled_window);
+ }
+
+ static void
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From eea6eef69858ff65ff8ccf46bb640bcc0580f592 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Sun, 29 Jul 2012 16:14:09 +0200
+Subject: [PATCH 15/68] Use gtk-enable-overlay-scrollbars in GtkScrolledWindow
+
+And listen for changes to this setting. Move overlay_scrollbars variable
+to GtkScrolledWindowPrivate, because we are going to have to monitor for
+each scrolled window whether it has transformed itself in response to the
+signal.
+---
+ gtk/gtkscrolledwindow.c | 97 +++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 72 insertions(+), 25 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 70de6ec..3220e91 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -140,6 +140,8 @@ typedef struct {
+
+ gint sb_scroll_direction;
+ guint sb_scroll_timeout_id;
++
++ gboolean overlay_scrollbars;
+ } GtkScrolledWindowPrivate;
+
+ #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate))
+@@ -260,12 +262,14 @@ static gboolean gtk_scrolled_window_child_expose (GtkWidget *widget,
+ static void gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj,
+ GtkScrolledWindow *scrolled_window);
+
++static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *settings,
++ GParamSpec *arg,
++ gpointer user_data);
++
+ static guint signals[LAST_SIGNAL] = {0};
+
+ G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
+
+-static gboolean overlay_scrollbars = TRUE;
+-
+ static void
+ add_scroll_binding (GtkBindingSet *binding_set,
+ guint keyval,
+@@ -501,6 +505,7 @@ static void
+ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ {
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GtkSettings *settings;
+
+ gtk_widget_set_has_window (GTK_WIDGET (scrolled_window), FALSE);
+ gtk_widget_set_can_focus (GTK_WIDGET (scrolled_window), TRUE);
+@@ -515,13 +520,22 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ scrolled_window->window_placement = GTK_CORNER_TOP_LEFT;
+ gtk_scrolled_window_update_real_placement (scrolled_window);
+
++ settings = gtk_widget_get_settings (GTK_WIDGET (scrolled_window));
++ g_object_get (settings,
++ "gtk-enable-overlay-scrollbars",
++ &priv->overlay_scrollbars,
++ NULL);
++ g_signal_connect (settings, "notify::gtk-enable-overlay-scrollbars",
++ G_CALLBACK (gtk_scrolled_window_overlay_scrollbars_changed),
++ scrolled_window);
++
+ if (g_getenv ("GTK2_KINETIC_SCROLLING"))
+ {
+ gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
+ gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
+ }
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ {
+ priv->opacity = g_object_new (GTK_TYPE_ADJUSTMENT,
+ "lower", 0.0,
+@@ -586,6 +600,7 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ GtkAdjustment *hadjustment)
+ {
+ GtkBin *bin;
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
+ if (hadjustment)
+@@ -618,7 +633,7 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ {
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+ gtk_scrolled_window_adjustment_value_changed,
+@@ -643,7 +658,7 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ {
+ g_signal_connect (hadjustment, "value-changed",
+ G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
+@@ -677,6 +692,7 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ GtkAdjustment *vadjustment)
+ {
+ GtkBin *bin;
++ GtkScrolledWindowPrivate *priv;
+
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
+ if (vadjustment)
+@@ -685,6 +701,7 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
+
+ bin = GTK_BIN (scrolled_window);
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+ if (!scrolled_window->vscrollbar)
+ {
+@@ -709,7 +726,7 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ {
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+ gtk_scrolled_window_adjustment_value_changed,
+@@ -734,7 +751,7 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ {
+ g_signal_connect (vadjustment,
+ "value-changed",
+@@ -1208,7 +1225,7 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ {
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+ gtk_scrolled_window_adjustment_value_changed,
+@@ -1229,7 +1246,7 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ {
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
+ gtk_scrolled_window_adjustment_value_changed,
+@@ -1245,6 +1262,10 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ scrolled_window->vscrollbar = NULL;
+ }
+
++ g_signal_handlers_disconnect_by_func (gtk_widget_get_settings (GTK_WIDGET (scrolled_window)),
++ G_CALLBACK (gtk_scrolled_window_overlay_scrollbars_changed),
++ scrolled_window);
++
+ if (priv->release_timeout_id)
+ {
+ g_source_remove (priv->release_timeout_id);
+@@ -1641,12 +1662,14 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+ GtkRequisition hscrollbar_requisition;
+ GtkRequisition vscrollbar_requisition;
+ GtkRequisition child_requisition;
++ GtkScrolledWindowPrivate *priv;
+
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
+ g_return_if_fail (requisition != NULL);
+
+ scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ bin = GTK_BIN (scrolled_window);
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+ scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+
+@@ -1666,7 +1689,7 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+
+ if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
+ requisition->width += child_requisition.width;
+- else if (! overlay_scrollbars)
++ else if (! priv->overlay_scrollbars)
+ {
+ GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
+
+@@ -1681,7 +1704,7 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+
+ if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
+ requisition->height += child_requisition.height;
+- else if (! overlay_scrollbars)
++ else if (! priv->overlay_scrollbars)
+ {
+ GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
+
+@@ -1695,7 +1718,7 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+ }
+ }
+
+- if (! overlay_scrollbars)
++ if (! priv->overlay_scrollbars)
+ {
+ if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
+ scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
+@@ -1752,7 +1775,7 @@ gtk_scrolled_window_relative_allocation (GtkWidget *widget,
+ allocation->width = MAX (1, (gint)widget->allocation.width - allocation->x * 2);
+ allocation->height = MAX (1, (gint)widget->allocation.height - allocation->y * 2);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ return;
+
+ if (scrolled_window->vscrollbar_visible)
+@@ -1909,16 +1932,15 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ g_return_if_fail (allocation != NULL);
+
+ scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ bin = GTK_BIN (scrolled_window);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ gtk_scrolled_window_expose_scrollbars (NULL, scrolled_window);
+
+ scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+ gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
+
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+ widget->allocation = *allocation;
+
+ if (scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
+@@ -1972,7 +1994,7 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
+ }
+
+- if (!overlay_scrollbars && scrolled_window->hscrollbar_visible)
++ if (!priv->overlay_scrollbars && scrolled_window->hscrollbar_visible)
+ {
+ GtkRequisition hscrollbar_requisition;
+ gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
+@@ -2020,7 +2042,7 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ else if (gtk_widget_get_visible (scrolled_window->hscrollbar))
+ gtk_widget_hide (scrolled_window->hscrollbar);
+
+- if (!overlay_scrollbars && scrolled_window->vscrollbar_visible)
++ if (!priv->overlay_scrollbars && scrolled_window->vscrollbar_visible)
+ {
+ GtkRequisition vscrollbar_requisition;
+ if (!gtk_widget_get_visible (scrolled_window->vscrollbar))
+@@ -2080,6 +2102,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ gboolean handled = FALSE;
+ gdouble delta_x;
+ gdouble delta_y;
+@@ -2090,7 +2113,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
+ {
+ if (delta_x != 0.0 && scrolled_window->hscrollbar &&
+- (overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar)))
++ (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar)))
+ {
+ GtkAdjustment *adj;
+ gdouble new_value;
+@@ -2108,7 +2131,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ }
+
+ if (delta_y != 0.0 && scrolled_window->vscrollbar &&
+- (overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar)))
++ (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar)))
+ {
+ GtkAdjustment *adj;
+ gdouble new_value;
+@@ -2134,7 +2157,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ else
+ range = scrolled_window->hscrollbar;
+
+- if (range && (overlay_scrollbars || gtk_widget_get_visible (range)))
++ if (range && (priv->overlay_scrollbars || gtk_widget_get_visible (range)))
+ {
+ GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
+ gdouble delta, new_value;
+@@ -2994,11 +3017,13 @@ gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+ {
+ GtkScrolledWindow *scrolled_win;
++ GtkScrolledWindowPrivate *priv;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ scrolled_win = GTK_SCROLLED_WINDOW (data);
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (data);
+
+ if (scrolled_win->hscrollbar &&
+ adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->hscrollbar)))
+@@ -3029,7 +3054,7 @@ gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
+ }
+ }
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ gtk_scrolled_window_start_fade_in_animation (scrolled_win);
+ }
+
+@@ -3052,7 +3077,7 @@ gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
+ adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)))
+ priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ gtk_scrolled_window_start_fade_in_animation (scrolled_window);
+ }
+
+@@ -3083,7 +3108,7 @@ gtk_scrolled_window_add (GtkContainer *container,
+ g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
+ "use gtk_scrolled_window_add_with_viewport() instead");
+
+- if (overlay_scrollbars)
++ if (priv->overlay_scrollbars)
+ {
+ g_signal_connect_after (child, "expose-event",
+ G_CALLBACK (gtk_scrolled_window_child_expose),
+@@ -3095,11 +3120,15 @@ static void
+ gtk_scrolled_window_remove (GtkContainer *container,
+ GtkWidget *child)
+ {
++ GtkScrolledWindowPrivate *priv;
++
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (GTK_BIN (container)->child == child);
+
+- if (overlay_scrollbars)
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (container);
++
++ if (priv->overlay_scrollbars)
+ {
+ g_signal_handlers_disconnect_by_func (child,
+ gtk_scrolled_window_child_expose,
+@@ -3764,5 +3793,23 @@ gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj,
+ }
+ }
+
++static void
++gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *settings,
++ GParamSpec *arg,
++ gpointer user_data)
++{
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (user_data);
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (user_data);
++
++ /* FIXME: tear down/set up things to make the switch */
++
++ g_object_get (settings,
++ "gtk-enable-overlay-scrollbars",
++ &priv->overlay_scrollbars,
++ NULL);
++
++ g_print ("enable-overlay-scrollbar is now: %d\n", priv->overlay_scrollbars);
++}
++
+ #define __GTK_SCROLLED_WINDOW_C__
+ #include "gtkaliasdef.c"
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 8d10b09aff0bc03c54c2f1899900cc062f36ad4b Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 16 Aug 2012 09:35:53 +0200
+Subject: [PATCH 16/68] gtk: correctly handle toggling of the scrollbar
+ visibility setting
+
+By doing most things unconditionally, like connecting to signals
+and creating the opacity adjustment. Queue a resize when the
+setting changes so things get recalculated properly, and make
+sure the scrollbars get expose events if they are visible.
+Unrelated: don't leak the priv->opacity adjustment.
+---
+ gtk/gtkscrolledwindow.c | 244 +++++++++++++++++++++++------------------------
+ 1 file changed, 119 insertions(+), 125 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 3220e91..7680f5d 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -529,29 +529,25 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ G_CALLBACK (gtk_scrolled_window_overlay_scrollbars_changed),
+ scrolled_window);
+
+- if (g_getenv ("GTK2_KINETIC_SCROLLING"))
+- {
+- gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
+- gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
+- }
+-
+- if (priv->overlay_scrollbars)
+- {
+- priv->opacity = g_object_new (GTK_TYPE_ADJUSTMENT,
+- "lower", 0.0,
+- "upper", 0.5,
+- "value", 0.0,
+- NULL);
+- priv->sb_min_height = 20;
+- priv->sb_padding = 2;
+- priv->sb_radius = 3;
+- priv->sb_width = 6;
+- priv->sb_fade_out_delay = 1000;
+-
+- g_signal_connect (priv->opacity, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
+- }
++ gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
++ gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
++
++ priv->opacity = g_object_new (GTK_TYPE_ADJUSTMENT,
++ "lower", 0.0,
++ "upper", 0.5,
++ "value", 0.0,
++ NULL);
++ g_object_ref_sink (priv->opacity);
++
++ priv->sb_min_height = 20;
++ priv->sb_padding = 2;
++ priv->sb_radius = 3;
++ priv->sb_width = 6;
++ priv->sb_fade_out_delay = 1000;
++
++ g_signal_connect (priv->opacity, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
+ }
+
+ /**
+@@ -632,16 +628,12 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
+-
+- if (priv->overlay_scrollbars)
+- {
+- g_signal_handlers_disconnect_by_func (old_adjustment,
+- gtk_scrolled_window_adjustment_value_changed,
+- scrolled_window);
+- g_signal_handlers_disconnect_by_func (old_adjustment,
+- gtk_scrolled_window_expose_scrollbars,
+- scrolled_window);
+- }
++ g_signal_handlers_disconnect_by_func (old_adjustment,
++ gtk_scrolled_window_adjustment_value_changed,
++ scrolled_window);
++ g_signal_handlers_disconnect_by_func (old_adjustment,
++ gtk_scrolled_window_expose_scrollbars,
++ scrolled_window);
+
+ gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
+ hadjustment);
+@@ -658,19 +650,18 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
+
+- if (priv->overlay_scrollbars)
+- {
+- g_signal_connect (hadjustment, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
+- scrolled_window);
++#if 0
++ g_signal_connect (hadjustment, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
++ scrolled_window);
++#endif
+
+- g_signal_connect (hadjustment, "changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
+- g_signal_connect (hadjustment, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
+- }
++ g_signal_connect (hadjustment, "changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
++ g_signal_connect (hadjustment, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
+
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+@@ -725,16 +716,12 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
+-
+- if (priv->overlay_scrollbars)
+- {
+- g_signal_handlers_disconnect_by_func (old_adjustment,
+- gtk_scrolled_window_adjustment_value_changed,
+- scrolled_window);
+- g_signal_handlers_disconnect_by_func (old_adjustment,
+- gtk_scrolled_window_expose_scrollbars,
+- scrolled_window);
+- }
++ g_signal_handlers_disconnect_by_func (old_adjustment,
++ gtk_scrolled_window_adjustment_value_changed,
++ scrolled_window);
++ g_signal_handlers_disconnect_by_func (old_adjustment,
++ gtk_scrolled_window_expose_scrollbars,
++ scrolled_window);
+
+ gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
+ vadjustment);
+@@ -751,20 +738,19 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
+
+- if (priv->overlay_scrollbars)
+- {
+- g_signal_connect (vadjustment,
+- "value-changed",
+- G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
+- scrolled_window);
++#if 0
++ g_signal_connect (vadjustment,
++ "value-changed",
++ G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
++ scrolled_window);
++#endif
+
+- g_signal_connect (vadjustment, "changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
+- g_signal_connect (vadjustment, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
+- }
++ g_signal_connect (vadjustment, "changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
++ g_signal_connect (vadjustment, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
++ scrolled_window);
+
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+@@ -1224,16 +1210,12 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
+-
+- if (priv->overlay_scrollbars)
+- {
+- g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+- gtk_scrolled_window_adjustment_value_changed,
+- scrolled_window);
+- g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+- gtk_scrolled_window_expose_scrollbars,
+- scrolled_window);
+- }
++ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
++ gtk_scrolled_window_adjustment_value_changed,
++ scrolled_window);
++ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
++ gtk_scrolled_window_expose_scrollbars,
++ scrolled_window);
+
+ gtk_widget_unparent (scrolled_window->hscrollbar);
+ gtk_widget_destroy (scrolled_window->hscrollbar);
+@@ -1245,16 +1227,12 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
+ gtk_scrolled_window_adjustment_changed,
+ scrolled_window);
+-
+- if (priv->overlay_scrollbars)
+- {
+- g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
+- gtk_scrolled_window_adjustment_value_changed,
+- scrolled_window);
+- g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
+- gtk_scrolled_window_expose_scrollbars,
+- scrolled_window);
+- }
++ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
++ gtk_scrolled_window_adjustment_value_changed,
++ scrolled_window);
++ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
++ gtk_scrolled_window_expose_scrollbars,
++ scrolled_window);
+
+ gtk_widget_unparent (scrolled_window->vscrollbar);
+ gtk_widget_destroy (scrolled_window->vscrollbar);
+@@ -1283,6 +1261,12 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ priv->button_press_event = NULL;
+ }
+
++ if (priv->opacity)
++ {
++ g_object_unref (priv->opacity);
++ priv->opacity = NULL;
++ }
++
+ GTK_OBJECT_CLASS (gtk_scrolled_window_parent_class)->destroy (object);
+ }
+
+@@ -1482,11 +1466,23 @@ static gboolean
+ gtk_scrolled_window_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+ {
++ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (widget);
+
+ if (gtk_widget_is_drawable (widget))
+ {
+- if (event->window == priv->overshoot_window)
++ GdkWindow *hscrollbar_window = NULL;
++ GdkWindow *vscrollbar_window = NULL;
++
++ if (scrolled_window->hscrollbar)
++ hscrollbar_window = gtk_widget_get_window (scrolled_window->hscrollbar);
++
++ if (scrolled_window->vscrollbar)
++ vscrollbar_window = gtk_widget_get_window (scrolled_window->vscrollbar);
++
++ if (event->window == priv->overshoot_window ||
++ event->window == hscrollbar_window ||
++ event->window == vscrollbar_window)
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->expose_event (widget, event);
+ else
+ gtk_scrolled_window_paint (widget, &event->area);
+@@ -1935,8 +1931,7 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ bin = GTK_BIN (scrolled_window);
+
+- if (priv->overlay_scrollbars)
+- gtk_scrolled_window_expose_scrollbars (NULL, scrolled_window);
++ gtk_scrolled_window_expose_scrollbars (NULL, scrolled_window);
+
+ scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+ gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
+@@ -3108,12 +3103,9 @@ gtk_scrolled_window_add (GtkContainer *container,
+ g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
+ "use gtk_scrolled_window_add_with_viewport() instead");
+
+- if (priv->overlay_scrollbars)
+- {
+- g_signal_connect_after (child, "expose-event",
+- G_CALLBACK (gtk_scrolled_window_child_expose),
+- container);
+- }
++ g_signal_connect_after (child, "expose-event",
++ G_CALLBACK (gtk_scrolled_window_child_expose),
++ container);
+ }
+
+ static void
+@@ -3128,12 +3120,9 @@ gtk_scrolled_window_remove (GtkContainer *container,
+
+ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (container);
+
+- if (priv->overlay_scrollbars)
+- {
+- g_signal_handlers_disconnect_by_func (child,
+- gtk_scrolled_window_child_expose,
+- container);
+- }
++ g_signal_handlers_disconnect_by_func (child,
++ gtk_scrolled_window_child_expose,
++ container);
+
+ gtk_widget_set_scroll_adjustments (child, NULL, NULL);
+
+@@ -3624,6 +3613,9 @@ gtk_scrolled_window_child_expose (GtkWidget *widget,
+ GdkRectangle hslider_rect;
+ cairo_t *cr;
+
++ if (!priv->overlay_scrollbars)
++ return FALSE;
++
+ cr = gdk_cairo_create (eevent->window);
+ gdk_cairo_region (cr, eevent->region);
+ cairo_clip (cr);
+@@ -3769,27 +3761,32 @@ static void
+ gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj,
+ GtkScrolledWindow *scrolled_window)
+ {
+- GtkWidget *child = gtk_bin_get_child (GTK_BIN (scrolled_window));
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+- if (child && gtk_widget_get_visible (child))
++ if (priv->overlay_scrollbars)
+ {
+- GtkAllocation alloc;
+-
+- gtk_widget_get_allocation (child, &alloc);
+-
+- if (scrolled_window->vscrollbar)
+- gtk_widget_queue_draw_area (child,
+- alloc.width - 20,
+- 0,
+- 20,
+- alloc.height);
++ GtkWidget *child = gtk_bin_get_child (GTK_BIN (scrolled_window));
+
+- if (scrolled_window->hscrollbar)
+- gtk_widget_queue_draw_area (child,
+- 0,
+- alloc.height - 20,
+- alloc.width,
+- 20);
++ if (child && gtk_widget_get_visible (child))
++ {
++ GtkAllocation alloc;
++
++ gtk_widget_get_allocation (child, &alloc);
++
++ if (scrolled_window->vscrollbar)
++ gtk_widget_queue_draw_area (child,
++ alloc.width - 20,
++ 0,
++ 20,
++ alloc.height);
++
++ if (scrolled_window->hscrollbar)
++ gtk_widget_queue_draw_area (child,
++ 0,
++ alloc.height - 20,
++ alloc.width,
++ 20);
++ }
+ }
+ }
+
+@@ -3798,17 +3795,14 @@ gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *settings,
+ GParamSpec *arg,
+ gpointer user_data)
+ {
+- GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (user_data);
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (user_data);
+
+- /* FIXME: tear down/set up things to make the switch */
+-
+ g_object_get (settings,
+ "gtk-enable-overlay-scrollbars",
+ &priv->overlay_scrollbars,
+ NULL);
+
+- g_print ("enable-overlay-scrollbar is now: %d\n", priv->overlay_scrollbars);
++ gtk_widget_queue_resize (user_data);
+ }
+
+ #define __GTK_SCROLLED_WINDOW_C__
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From f2c762968b02b73323a75a723ad664ff1645abde Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 31 Aug 2012 16:24:07 +0200
+Subject: [PATCH 17/68] gtk: handle gtk-primary-button-warps-slider for the
+ overlay scrollbars
+
+---
+ gtk/gtkscrolledwindow.c | 38 +++++++++++++++++++++++++++++++++-----
+ 1 file changed, 33 insertions(+), 5 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 7680f5d..02745b1 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -2725,6 +2725,10 @@ gtk_scrolled_window_scroll_step_timeout (gpointer data)
+ }
+
+ static gboolean
++gtk_scrolled_window_captured_motion_notify_scrollbar (GtkWidget *widget,
++ GdkEvent *event);
++
++static gboolean
+ gtk_scrolled_window_captured_button_press_scrollbar (GtkWidget *widget,
+ GdkEvent *event)
+ {
+@@ -2808,12 +2812,36 @@ gtk_scrolled_window_captured_button_press_scrollbar (GtkWidget *widget,
+ if ((priv->sb_grab_vscroll || priv->sb_grab_hscroll) &&
+ !priv->sb_drag_slider)
+ {
+- gtk_scrolled_window_scroll_step (scrolled_window);
++ gboolean primary_warps;
++
++ g_object_get (gtk_widget_get_settings (widget),
++ "gtk-primary-button-warps-slider", &primary_warps,
++ NULL);
++
++ if (primary_warps)
++ {
++ GdkEventMotion mevent = { 0, };
++
++ priv->sb_drag_slider = TRUE;
++ priv->sb_grab_offset_x = hslider_rect.width / 2;
++ priv->sb_grab_offset_y = vslider_rect.height / 2;
++
++ mevent.window = bevent->window;
++ mevent.x = bevent->x;
++ mevent.y = bevent->y;
+
+- priv->sb_scroll_timeout_id =
+- gdk_threads_add_timeout (SCROLL_INTERVAL_INITIAL,
+- gtk_scrolled_window_scroll_step_timeout,
+- scrolled_window);
++ gtk_scrolled_window_captured_motion_notify_scrollbar (widget,
++ (GdkEvent *) &mevent);
++ }
++ else
++ {
++ gtk_scrolled_window_scroll_step (scrolled_window);
++
++ priv->sb_scroll_timeout_id =
++ gdk_threads_add_timeout (SCROLL_INTERVAL_INITIAL,
++ gtk_scrolled_window_scroll_step_timeout,
++ scrolled_window);
++ }
+ }
+
+ return TRUE;
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 4ef417938760f98e530c152ee50071da2af31b06 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Sun, 2 Sep 2012 14:16:45 +0200
+Subject: [PATCH 18/68] Introduce phase field in GdkEventScroll
+
+Using the phase field it is possible to distinguish between
+events generated while the user is performing a gesture and
+momentum events that are generated after the gesture has been
+finished.
+---
+ gdk/gdkevents.c | 1 +
+ gdk/gdkevents.h | 9 +++++++++
+ gdk/gdkwindow.c | 1 +
+ gdk/quartz/gdkevents-quartz.c | 36 +++++++++++++++++++++++++++++++++---
+ 4 files changed, 44 insertions(+), 3 deletions(-)
+
+diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
+index 0f8bba2..d3d67db 100644
+--- a/gdk/gdkevents.c
++++ b/gdk/gdkevents.c
+@@ -394,6 +394,7 @@ gdk_event_new (GdkEventType type)
+ new_event->scroll.y_root = 0.;
+ new_event->scroll.delta_x = 0.;
+ new_event->scroll.delta_y = 0.;
++ new_event->scroll.phase = GDK_EVENT_SCROLL_PHASE_NONE;
+ break;
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
+index f6b4e04..765b520 100644
+--- a/gdk/gdkevents.h
++++ b/gdk/gdkevents.h
+@@ -263,6 +263,14 @@ typedef enum
+ GDK_OWNER_CHANGE_CLOSE
+ } GdkOwnerChange;
+
++typedef enum
++{
++ GDK_EVENT_SCROLL_PHASE_NONE,
++ GDK_EVENT_SCROLL_PHASE_START,
++ GDK_EVENT_SCROLL_PHASE_ACTIVE,
++ GDK_EVENT_SCROLL_PHASE_END
++} GdkEventScrollPhase;
++
+ struct _GdkEventAny
+ {
+ GdkEventType type;
+@@ -340,6 +348,7 @@ struct _GdkEventScroll
+ gboolean has_deltas;
+ gdouble delta_x;
+ gdouble delta_y;
++ GdkEventScrollPhase phase;
+ };
+
+ struct _GdkEventKey
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index d48751e..1843873 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -10803,6 +10803,7 @@ proxy_button_event (GdkEvent *source_event,
+ event->scroll.has_deltas = source_event->scroll.has_deltas;
+ event->scroll.delta_x = source_event->scroll.delta_x;
+ event->scroll.delta_y = source_event->scroll.delta_y;
++ event->scroll.phase = source_event->scroll.phase;
+ return TRUE;
+
+ default:
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index e7d97dc..bb4da70 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -62,6 +62,7 @@ static GdkWindow *find_toplevel_under_pointer (GdkDisplay *display,
+ - (BOOL) hasPreciseScrollingDeltas;
+ - (CGFloat) scrollingDeltaX;
+ - (CGFloat) scrollingDeltaY;
++- (int) phase;
+ @end
+
+
+@@ -990,6 +991,7 @@ fill_scroll_event (GdkWindow *window,
+ gboolean has_deltas,
+ gdouble delta_x,
+ gdouble delta_y,
++ GdkEventScrollPhase phase,
+ GdkScrollDirection direction)
+ {
+ GdkWindowObject *private;
+@@ -1012,6 +1014,7 @@ fill_scroll_event (GdkWindow *window,
+ event->scroll.has_deltas = has_deltas;
+ event->scroll.delta_x = delta_x;
+ event->scroll.delta_y = delta_y;
++ event->scroll.phase = phase;
+ }
+
+ static void
+@@ -1300,6 +1303,28 @@ test_resize (NSEvent *event, GdkWindow *toplevel, gint x, gint y)
+ return FALSE;
+ }
+
++static GdkEventScrollPhase
++gdk_event_scroll_phase_from_ns_event_phase (NSUInteger phase)
++{
++ switch (phase)
++ {
++ case 0:
++ return GDK_EVENT_SCROLL_PHASE_NONE;
++
++ case 1 << 0:
++ return GDK_EVENT_SCROLL_PHASE_START;
++
++ case 1 << 1:
++ case 1 << 2:
++ return GDK_EVENT_SCROLL_PHASE_ACTIVE;
++
++ case 1 << 3:
++ return GDK_EVENT_SCROLL_PHASE_END;
++ }
++
++ return GDK_EVENT_SCROLL_PHASE_NONE;
++}
++
+ static gboolean
+ gdk_event_translate (GdkEvent *event,
+ NSEvent *nsevent)
+@@ -1491,6 +1516,7 @@ gdk_event_translate (GdkEvent *event,
+ if (gdk_quartz_osx_version() >= GDK_OSX_LION &&
+ [(id <PreciseDeltas>) nsevent hasPreciseScrollingDeltas])
+ {
++ GdkEventScrollPhase phase;
+ dx = [(id <PreciseDeltas>) nsevent scrollingDeltaX];
+ dy = [(id <PreciseDeltas>) nsevent scrollingDeltaY];
+
+@@ -1509,8 +1535,10 @@ gdk_event_translate (GdkEvent *event,
+ direction = GDK_SCROLL_LEFT;
+ }
+
++ phase = gdk_event_scroll_phase_from_ns_event_phase ([(id <PreciseDeltas>) nsevent phase]);
++
+ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+- TRUE, -dx, -dy, direction);
++ TRUE, -dx, -dy, phase, direction);
+ }
+ else
+ {
+@@ -1525,7 +1553,8 @@ gdk_event_translate (GdkEvent *event,
+ direction = GDK_SCROLL_UP;
+
+ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+- FALSE, 0.0, fabs (dy), direction);
++ FALSE, 0.0, fabs (dy), GDK_EVENT_SCROLL_PHASE_NONE,
++ direction);
+ }
+ else if (dx != 0.0)
+ {
+@@ -1535,7 +1564,8 @@ gdk_event_translate (GdkEvent *event,
+ direction = GDK_SCROLL_LEFT;
+
+ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+- FALSE, fabs (dx), 0.0, direction);
++ FALSE, fabs (dx), 0.0, GDK_EVENT_SCROLL_PHASE_NONE,
++ direction);
+ }
+ }
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 026f9bc1846eae8fa98a307975fedce1f61d9cd5 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Fri, 28 Sep 2012 08:24:05 +0200
+Subject: [PATCH 19/68] Add hack to lock flow of scroll events to window where
+ scroll started
+
+A bit evil but there is probably no other way, because this is very
+different from GDK's usual behavior.
+
+There's one quirk left, if you start a scroll and move to another GTK+
+window in the same process and click, the click won't be processed until
+the folow of momentum events has ended.
+---
+ gdk/gdkwindow.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 94 insertions(+)
+
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index 1843873..1dac543 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -434,6 +434,10 @@ accumulate_get_window (GSignalInvocationHint *ihint,
+ }
+
+ static GQuark quark_pointer_window = 0;
++#ifdef GDK_WINDOWING_QUARTZ
++static GQuark quark_last_scroll_pointer_window = 0;
++static GQuark quark_last_scroll_event_window = 0;
++#endif /* GDK_WINDOWING_QUARTZ */
+
+ static void
+ gdk_window_class_init (GdkWindowObjectClass *klass)
+@@ -478,6 +482,12 @@ gdk_window_class_init (GdkWindowObjectClass *klass)
+ drawable_class->get_source_drawable = gdk_window_get_source_drawable;
+
+ quark_pointer_window = g_quark_from_static_string ("gtk-pointer-window");
++#ifdef GDK_WINDOWING_QUARTZ
++ quark_last_scroll_pointer_window =
++ g_quark_from_static_string ("gtk-last-scroll-pointer-window");
++ quark_last_scroll_event_window =
++ g_quark_from_static_string ("gtk-last-scroll-event-window");
++#endif /* GDK_WINDOWING_QUARTZ */
+
+
+ /* Properties */
+@@ -10702,6 +10712,64 @@ proxy_pointer_event (GdkDisplay *display,
+ GDK_BUTTON4_MASK | \
+ GDK_BUTTON5_MASK)
+
++#ifdef GDK_WINDOWING_QUARTZ
++static void
++last_scroll_event_weak_ref_notify (gpointer data,
++ GObject *where_the_object_was)
++{
++ /* If any of pointer_window or event_window is destroyed, we unset
++ * both values in the display's qdata.
++ */
++ GdkDisplay *display = data;
++
++ g_object_set_qdata (G_OBJECT (display), quark_last_scroll_pointer_window,
++ NULL);
++ g_object_set_qdata (G_OBJECT (display), quark_last_scroll_event_window,
++ NULL);
++}
++
++static void
++set_last_scroll_event_windows (GdkDisplay *display,
++ GdkWindow *pointer_window,
++ GdkWindow *event_window)
++{
++ GdkWindow *old_window;
++
++ /* Check whether the values are still set from a previous scroll,
++ * if so we need to release the weak references. (If they are no
++ * longer set, we assume the weak ref notify callback was called).
++ */
++ old_window = g_object_get_qdata (G_OBJECT (display),
++ quark_last_scroll_pointer_window);
++ if (old_window)
++ g_object_weak_unref (G_OBJECT (old_window),
++ last_scroll_event_weak_ref_notify, display);
++
++ old_window = g_object_get_qdata (G_OBJECT (display),
++ quark_last_scroll_event_window);
++ if (old_window)
++ g_object_weak_unref (G_OBJECT (old_window),
++ last_scroll_event_weak_ref_notify, display);
++
++ /* Set new values and setup weak references. Note that pointer_window
++ * and event_window can be NULL, in which case GDK won't proxy the
++ * event. In this case we store NULL into the qdata so that we won't
++ * store the scroll event.
++ */
++ g_object_set_qdata (G_OBJECT (display), quark_last_scroll_pointer_window,
++ pointer_window);
++ if (pointer_window)
++ g_object_weak_ref (G_OBJECT (pointer_window),
++ last_scroll_event_weak_ref_notify, display);
++
++ g_object_set_qdata (G_OBJECT (display), quark_last_scroll_event_window,
++ event_window);
++ if (event_window)
++ g_object_weak_ref (G_OBJECT (event_window),
++ last_scroll_event_weak_ref_notify, display);
++}
++#endif /* GDK_WINDOWING_QUARTZ */
++
+ static gboolean
+ proxy_button_event (GdkEvent *source_event,
+ gulong serial)
+@@ -10769,6 +10837,32 @@ proxy_button_event (GdkEvent *source_event,
+ type, state,
+ NULL, serial);
+
++#ifdef GDK_WINDOWING_QUARTZ
++ /* A Quartz-specific hack we cannot handle from within the backend
++ * unfortunately. For scroll events with precise deltas (i.e. these
++ * generated by the Mac touchpad or Magic Mouse, we want to lock the
++ * flow of events belonging to a single gesture to the window the
++ * gesture was started on. The default behavior of GDK, which insists
++ * to send events to the window under the pointer or discard the
++ * events when there's no GDK window under the pointer, makes it
++ * impossible to implement this differently.
++ */
++ if (type == GDK_SCROLL && source_event->scroll.has_deltas)
++ {
++ if (source_event->scroll.phase == GDK_EVENT_SCROLL_PHASE_START)
++ {
++ set_last_scroll_event_windows (display, pointer_window, event_win);
++ }
++ else
++ {
++ pointer_window = g_object_get_qdata (G_OBJECT (display),
++ quark_last_scroll_pointer_window);
++ event_win = g_object_get_qdata (G_OBJECT (display),
++ quark_last_scroll_event_window);
++ }
++ }
++#endif /* GDK_WINDOWING_QUARTZ */
++
+ if (event_win == NULL || display->ignore_core_events)
+ return TRUE;
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 69e85305f2d455c8943514edde215ce69791076a Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Sun, 2 Sep 2012 21:21:51 +0200
+Subject: [PATCH 20/68] Introduce a background window
+
+The background window covers the part left uncovered by the overshoot
+window. Two background windows are used to be able to cover an "L shape"
+that appears when is scrolled in the horizontal and vertical direction
+at the same time. These background windows are used to capture events,
+such as scroll events. In the future, it could also be used to draw a
+specific background pattern/gradient (if the window is configured as
+INPUT/OUTPUT window).
+---
+ gtk/gtkscrolledwindow.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 104 insertions(+)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 02745b1..9d0d87a 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -95,6 +95,8 @@ typedef struct {
+ /* Kinetic scrolling */
+ GdkEvent *button_press_event;
+ GdkWindow *overshoot_window;
++ GdkWindow *vbackground_window;
++ GdkWindow *hbackground_window;
+ guint pointer_grabbed : 1;
+ guint kinetic_scrolling : 1;
+ guint capture_button_press : 1;
+@@ -1481,6 +1483,8 @@ gtk_scrolled_window_expose (GtkWidget *widget,
+ vscrollbar_window = gtk_widget_get_window (scrolled_window->vscrollbar);
+
+ if (event->window == priv->overshoot_window ||
++ event->window == priv->vbackground_window ||
++ event->window == priv->hbackground_window ||
+ event->window == hscrollbar_window ||
+ event->window == vscrollbar_window)
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->expose_event (widget, event);
+@@ -1871,6 +1875,7 @@ _gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_wind
+ _gtk_scrolled_window_get_overshoot (scrolled_window,
+ &overshoot_x, &overshoot_y);
+
++ /* Overshoot window */
+ window_allocation = relative_allocation;
+ window_allocation.x += allocation.x;
+ window_allocation.y += allocation.y;
+@@ -1887,6 +1892,46 @@ _gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_wind
+ gdk_window_move_resize (priv->overshoot_window,
+ window_allocation.x, window_allocation.y,
+ window_allocation.width, window_allocation.height);
++
++ /* Vertical background window */
++ window_allocation = relative_allocation;
++ window_allocation.x += allocation.x;
++ window_allocation.y += allocation.y;
++
++ if (ABS (overshoot_x) > 0)
++ {
++ window_allocation.width = ABS (overshoot_x);
++ if (overshoot_x > 0)
++ window_allocation.x += relative_allocation.width - overshoot_x;
++
++ gdk_window_move_resize (priv->vbackground_window,
++ window_allocation.x, window_allocation.y,
++ window_allocation.width,
++ window_allocation.height);
++ gdk_window_show (priv->vbackground_window);
++ }
++ else
++ gdk_window_hide (priv->vbackground_window);
++
++ /* Horizontal background window */
++ window_allocation = relative_allocation;
++ window_allocation.x += allocation.x;
++ window_allocation.y += allocation.y;
++
++ if (ABS (overshoot_y) > 0)
++ {
++ window_allocation.height = ABS (overshoot_y);
++ if (overshoot_y > 0)
++ window_allocation.y += relative_allocation.height - overshoot_y;
++
++ gdk_window_move_resize (priv->hbackground_window,
++ window_allocation.x, window_allocation.y,
++ window_allocation.width,
++ window_allocation.height);
++ gdk_window_show (priv->hbackground_window);
++ }
++ else
++ gdk_window_hide (priv->hbackground_window);
+ }
+
+ static void
+@@ -3259,6 +3304,7 @@ gtk_scrolled_window_realize (GtkWidget *widget)
+ gtk_widget_get_allocation (widget, &allocation);
+ gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
+
++ /* Overshoot window */
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = allocation.x + relative_allocation.x;
+ attributes.y = allocation.y + relative_allocation.y;
+@@ -3277,6 +3323,45 @@ gtk_scrolled_window_realize (GtkWidget *widget)
+
+ gdk_window_set_user_data (priv->overshoot_window, widget);
+
++ /* Vertical background window */
++ attributes.window_type = GDK_WINDOW_CHILD;
++ attributes.x = allocation.x + relative_allocation.x;
++ attributes.y = allocation.y + relative_allocation.y;
++ attributes.width = 0;
++ attributes.height = 0;
++ attributes.wclass = GDK_INPUT_ONLY;
++ attributes.visual = gtk_widget_get_visual (widget);
++ attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK |
++ GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK;
++
++ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
++
++ priv->vbackground_window =
++ gdk_window_new (gtk_widget_get_parent_window (widget),
++ &attributes, attributes_mask);
++
++ gdk_window_set_user_data (priv->vbackground_window, widget);
++
++ /* Horizontal background window */
++ attributes.window_type = GDK_WINDOW_CHILD;
++ attributes.x = allocation.x + relative_allocation.x;
++ attributes.y = allocation.y + relative_allocation.y;
++ attributes.width = 0;
++ attributes.height = 0;
++ attributes.wclass = GDK_INPUT_ONLY;
++ attributes.visual = gtk_widget_get_visual (widget);
++ attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK |
++ GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK;
++
++ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
++
++ priv->hbackground_window =
++ gdk_window_new (gtk_widget_get_parent_window (widget),
++ &attributes, attributes_mask);
++
++ gdk_window_set_user_data (priv->hbackground_window, widget);
++
++
+ child_widget = gtk_bin_get_child (GTK_BIN (widget));
+
+ if (child_widget)
+@@ -3296,6 +3381,14 @@ gtk_scrolled_window_unrealize (GtkWidget *widget)
+ gdk_window_destroy (priv->overshoot_window);
+ priv->overshoot_window = NULL;
+
++ gdk_window_set_user_data (priv->vbackground_window, NULL);
++ gdk_window_destroy (priv->vbackground_window);
++ priv->vbackground_window = NULL;
++
++ gdk_window_set_user_data (priv->hbackground_window, NULL);
++ gdk_window_destroy (priv->hbackground_window);
++ priv->hbackground_window = NULL;
++
+ gtk_widget_set_realized (widget, FALSE);
+
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
+@@ -3308,6 +3401,15 @@ gtk_scrolled_window_map (GtkWidget *widget)
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+ gdk_window_show (priv->overshoot_window);
++ if (gdk_window_get_width (priv->vbackground_window) > 1)
++ gdk_window_show (priv->vbackground_window);
++ else
++ gdk_window_hide (priv->vbackground_window);
++
++ if (gdk_window_get_height (priv->hbackground_window) > 1)
++ gdk_window_show (priv->hbackground_window);
++ else
++ gdk_window_hide (priv->hbackground_window);
+
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
+ }
+@@ -3319,6 +3421,8 @@ gtk_scrolled_window_unmap (GtkWidget *widget)
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+ gdk_window_hide (priv->overshoot_window);
++ gdk_window_hide (priv->vbackground_window);
++ gdk_window_hide (priv->hbackground_window);
+
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 79c1e789097b4e46b9f3fdcbc96a2aae69e24144 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Mon, 3 Sep 2012 10:45:13 +0200
+Subject: [PATCH 21/68] Make scrolled window work well with Mac touchpad
+
+Modify the scrolled window code to work with a Mac touchpad as you
+would expect. When precise deltas are received from the Mac touchpad,
+overshooting and snapping back will work. The existing kinetic scrolling
+code is only used for snapping back. The amount of pixels to overshoot
+is determined by the incoming scroll events from the touchpad. We use
+the deceleration mechanism that was already in place to snap back.
+
+Other changes made are:
+ - Increased the kinetic scrolling distance, this makes things feel
+ nicer IMHO.
+ - Removed everything related to handling kinetic scrolling by making
+ drag gestures. We don't need this code.
+---
+ gtk/gtkscrolledwindow.c | 680 +++++++++++------------------------------------
+ 1 file changed, 157 insertions(+), 523 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 9d0d87a..5341b50 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -79,7 +79,7 @@
+
+ /* Kinetic scrolling */
+ #define FRAME_INTERVAL (1000 / 60)
+-#define MAX_OVERSHOOT_DISTANCE 50
++#define MAX_OVERSHOOT_DISTANCE 100
+ #define FRICTION_DECELERATION 0.003
+ #define OVERSHOOT_INVERSE_ACCELERATION 0.003
+ #define RELEASE_EVENT_TIMEOUT 1000
+@@ -98,20 +98,11 @@ typedef struct {
+ GdkWindow *vbackground_window;
+ GdkWindow *hbackground_window;
+ guint pointer_grabbed : 1;
+- guint kinetic_scrolling : 1;
+- guint capture_button_press : 1;
+ guint in_drag : 1;
+- guint last_button_event_valid : 1;
+
+- guint release_timeout_id;
+ guint deceleration_id;
+
+- gdouble last_button_event_x_root;
+- gdouble last_button_event_y_root;
+-
+- gdouble last_motion_event_x_root;
+- gdouble last_motion_event_y_root;
+- guint32 last_motion_event_time;
++ guint32 last_scroll_event_time;
+
+ gdouble x_velocity;
+ gdouble y_velocity;
+@@ -144,6 +135,7 @@ typedef struct {
+ guint sb_scroll_timeout_id;
+
+ gboolean overlay_scrollbars;
++ gboolean is_snapping_back;
+ } GtkScrolledWindowPrivate;
+
+ #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate))
+@@ -167,8 +159,7 @@ enum {
+ PROP_VSCROLLBAR_POLICY,
+ PROP_WINDOW_PLACEMENT,
+ PROP_WINDOW_PLACEMENT_SET,
+- PROP_SHADOW_TYPE,
+- PROP_KINETIC_SCROLLING
++ PROP_SHADOW_TYPE
+ };
+
+ /* Signals */
+@@ -268,6 +259,13 @@ static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *setting
+ GParamSpec *arg,
+ gpointer user_data);
+
++
++static void gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window);
++static gboolean gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
++ GdkEvent *event);
++static void gtk_scrolled_window_init_overlay_scrollbars (GtkScrolledWindow *window);
++
++
+ static guint signals[LAST_SIGNAL] = {0};
+
+ G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
+@@ -432,22 +430,6 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
+ GTK_PARAM_READABLE));
+
+ /**
+- * GtkScrolledWindow:kinetic-scrolling:
+- *
+- * The kinetic scrolling behavior flags.
+- *
+- * Since: X.XX
+- */
+- g_object_class_install_property (gobject_class,
+- PROP_KINETIC_SCROLLING,
+- g_param_spec_boolean ("kinetic-scrolling",
+- P_("Kinetic Scrolling"),
+- P_("Kinetic scrolling mode."),
+- TRUE,
+- GTK_PARAM_READABLE |
+- GTK_PARAM_WRITABLE));
+-
+- /**
+ * GtkScrolledWindow::scroll-child:
+ * @scrolled_window: a #GtkScrolledWindow
+ * @scroll: a #GtkScrollType describing how much to scroll
+@@ -531,8 +513,7 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ G_CALLBACK (gtk_scrolled_window_overlay_scrollbars_changed),
+ scrolled_window);
+
+- gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
+- gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
++ gtk_scrolled_window_init_overlay_scrollbars (scrolled_window);
+
+ priv->opacity = g_object_new (GTK_TYPE_ADJUSTMENT,
+ "lower", 0.0,
+@@ -1075,130 +1056,6 @@ gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
+ return scrolled_window->shadow_type;
+ }
+
+-/**
+- * gtk_scrolled_window_set_kinetic_scrolling:
+- * @scrolled_window: a #GtkScrolledWindow
+- * @kinetic_scrolling: %TRUE to enable kinetic scrolling
+- *
+- * Turns kinetic scrolling on or off.
+- * Kinetic scrolling only applies to devices with source
+- * %GDK_SOURCE_TOUCHSCREEN.
+- *
+- * Since: X.XX
+- **/
+-void
+-gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
+- gboolean kinetic_scrolling)
+-{
+- GtkScrolledWindowPrivate *priv;
+-
+- g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
+-
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+- if (priv->kinetic_scrolling == kinetic_scrolling)
+- return;
+-
+- priv->kinetic_scrolling = kinetic_scrolling;
+- if (priv->kinetic_scrolling)
+- {
+- _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window),
+- gtk_scrolled_window_captured_event);
+- }
+- else
+- {
+- _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window), NULL);
+- if (priv->release_timeout_id)
+- {
+- g_source_remove (priv->release_timeout_id);
+- priv->release_timeout_id = 0;
+- }
+- if (priv->deceleration_id)
+- {
+- g_source_remove (priv->deceleration_id);
+- priv->deceleration_id = 0;
+- }
+- }
+- g_object_notify (G_OBJECT (scrolled_window), "kinetic-scrolling");
+-}
+-
+-/**
+- * gtk_scrolled_window_get_kinetic_scrolling:
+- * @scrolled_window: a #GtkScrolledWindow
+- *
+- * Returns the specified kinetic scrolling behavior.
+- *
+- * Return value: the scrolling behavior flags.
+- *
+- * Since: X.XX
+- */
+-gboolean
+-gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window)
+-{
+- GtkScrolledWindowPrivate *priv;
+-
+- g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
+-
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+- return priv->kinetic_scrolling;
+-}
+-
+-/**
+- * gtk_scrolled_window_set_capture_button_press:
+- * @scrolled_window: a #GtkScrolledWindow
+- * @capture_button_press: %TRUE to capture button presses
+- *
+- * Changes the behaviour of @scrolled_window wrt. to the initial
+- * event that possibly starts kinetic scrolling. When @capture_button_press
+- * is set to %TRUE, the event is captured by the scrolled window, and
+- * then later replayed if it is meant to go to the child widget.
+- *
+- * This should be enabled if any child widgets perform non-reversible
+- * actions on #GtkWidget::button-press-event. If they don't, and handle
+- * additionally handle #GtkWidget::grab-broken-event, it might be better
+- * to set @capture_button_press to %FALSE.
+- *
+- * This setting only has an effect if kinetic scrolling is enabled.
+- *
+- * Since: X.XX
+- */
+-void
+-gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
+- gboolean capture_button_press)
+-{
+- GtkScrolledWindowPrivate *priv;
+-
+- g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
+-
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+- priv->capture_button_press = capture_button_press;
+-}
+-
+-/**
+- * gtk_scrolled_window_get_capture_button_press:
+- * @scrolled_window: a #GtkScrolledWindow
+- *
+- * Return whether button presses are captured during kinetic
+- * scrolling. See gtk_scrolled_window_set_capture_button_press().
+- *
+- * Returns: %TRUE if button presses are captured during kinetic scrolling
+- *
+- * Since: X.XX
+- */
+-gboolean
+-gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window)
+-{
+- GtkScrolledWindowPrivate *priv;
+-
+- g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
+-
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+- return priv->capture_button_press;
+-}
+-
+-
+ static void
+ gtk_scrolled_window_destroy (GtkObject *object)
+ {
+@@ -1246,11 +1103,6 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ G_CALLBACK (gtk_scrolled_window_overlay_scrollbars_changed),
+ scrolled_window);
+
+- if (priv->release_timeout_id)
+- {
+- g_source_remove (priv->release_timeout_id);
+- priv->release_timeout_id = 0;
+- }
+ if (priv->deceleration_id)
+ {
+ g_source_remove (priv->deceleration_id);
+@@ -1313,10 +1165,6 @@ gtk_scrolled_window_set_property (GObject *object,
+ gtk_scrolled_window_set_shadow_type (scrolled_window,
+ g_value_get_enum (value));
+ break;
+- case PROP_KINETIC_SCROLLING:
+- gtk_scrolled_window_set_kinetic_scrolling (scrolled_window,
+- g_value_get_boolean (value));
+- break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -1357,9 +1205,6 @@ gtk_scrolled_window_get_property (GObject *object,
+ case PROP_SHADOW_TYPE:
+ g_value_set_enum (value, scrolled_window->shadow_type);
+ break;
+- case PROP_KINETIC_SCROLLING:
+- g_value_set_boolean (value, priv->kinetic_scrolling);
+- break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -2152,40 +1997,146 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+
+ if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
+ {
+- if (delta_x != 0.0 && scrolled_window->hscrollbar &&
+- (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar)))
++ gint new_overshoot_x, new_overshoot_y;
++ gint old_overshoot_x, old_overshoot_y;
++ gboolean start_snap_back = FALSE;
++ gboolean is_overshot = FALSE;
++ gboolean is_momentum_event = event->phase == GDK_EVENT_SCROLL_PHASE_NONE;
++
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &old_overshoot_x, &old_overshoot_y);
++
++ /* Check if the view is currently overshot. */
++ if (old_overshoot_x != 0 || old_overshoot_y != 0)
++ is_overshot = TRUE;
++
++ /* In case the view is not overshot, no snap back is active
++ * and this event is not a momentum event, then a new scrolling
++ * gesture has started. In case we are still in snapping back
++ * state we can reset this (because the snapback has ended).
++ */
++ if (!is_overshot && priv->deceleration_id == 0 && !is_momentum_event)
++ priv->is_snapping_back = FALSE;
++
++ /* Scroll events are handled in two cases:
++ * 1) We are not overshot and not snapping back, so scroll as
++ * usual and also handle momentum events.
++ * 2) If the view is currently overshot, then do not handle
++ * momentum events but handle non-momentum events as usual.
++ *
++ * For both cases we only allow overshooting in a direction if
++ * that particular scrollbar is actually visible.
++ */
++ if ((!is_overshot && !priv->is_snapping_back) ||
++ (is_overshot && !is_momentum_event))
+ {
+- GtkAdjustment *adj;
+- gdouble new_value;
++ if (delta_x != 0.0 && scrolled_window->hscrollbar &&
++ (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar)))
++ {
++ GtkAdjustment *adj;
+
+- adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
+
+- new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x,
+- gtk_adjustment_get_lower (adj),
+- gtk_adjustment_get_upper (adj) -
+- gtk_adjustment_get_page_size (adj));
++ if (scrolled_window->hscrollbar_visible)
++ {
++ _gtk_scrolled_window_set_adjustment_value (scrolled_window,
++ adj,
++ priv->unclamped_hadj_value + delta_x,
++ TRUE,
++ FALSE);
++ }
++ else
++ {
++ gdouble new_value;
+
+- gtk_adjustment_set_value (adj, new_value);
++ /* If the scrollbar is not visible, clamp the value and
++ * don't trigger overshooting.
++ */
++ new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x,
++ gtk_adjustment_get_lower (adj),
++ gtk_adjustment_get_upper (adj) -
++ gtk_adjustment_get_page_size (adj));
+
+- handled = TRUE;
++ gtk_adjustment_set_value (adj, new_value);
++ }
++
++ handled = TRUE;
++ }
++
++ if (delta_y != 0.0 && scrolled_window->vscrollbar &&
++ (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar)))
++ {
++ GtkAdjustment *adj;
++
++ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++
++ if (scrolled_window->vscrollbar_visible)
++ {
++ _gtk_scrolled_window_set_adjustment_value (scrolled_window,
++ adj,
++ priv->unclamped_vadj_value + delta_y,
++ TRUE,
++ FALSE);
++ }
++ else
++ {
++ gdouble new_value;
++
++ /* If the scrollbar is not visible, clamp the value and
++ * don't trigger overshooting.
++ */
++ new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y,
++ gtk_adjustment_get_lower (adj),
++ gtk_adjustment_get_upper (adj) -
++ gtk_adjustment_get_page_size (adj));
++
++ gtk_adjustment_set_value (adj, new_value);
++ }
++
++ handled = TRUE;
++ }
++
++
++ priv->last_scroll_event_time = gdk_event_get_time ((GdkEvent *)event);
++ gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
+ }
+
+- if (delta_y != 0.0 && scrolled_window->vscrollbar &&
+- (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar)))
+- {
+- GtkAdjustment *adj;
+- gdouble new_value;
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &new_overshoot_x, &new_overshoot_y);
+
+- adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++ if (old_overshoot_x != new_overshoot_x ||
++ old_overshoot_y != new_overshoot_y)
++ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
+
+- new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y,
+- gtk_adjustment_get_lower (adj),
+- gtk_adjustment_get_upper (adj) -
+- gtk_adjustment_get_page_size (adj));
++ /* In two cases we want to start snapping back:
++ * 1) The view is overshot and the gesture has ended (signalled
++ * by an event with both deltas set to zero.
++ * 2) The view is overshot and we receive a momentum event, which
++ * also signals that the user's gesture has ended.
++ */
++ if (is_overshot &&
++ ((delta_x == 0.0 && delta_y == 0.0) || is_momentum_event))
++ start_snap_back = TRUE;
+
+- gtk_adjustment_set_value (adj, new_value);
++ /* If we should start a snap back and no current deceleration
++ * is active, start the snap back.
++ */
++ if (start_snap_back && priv->deceleration_id == 0)
++ {
++ /* Zero out vector components without a visible scrollbar */
++ if (!scrolled_window->hscrollbar_visible)
++ priv->x_velocity = 0;
++ if (!scrolled_window->vscrollbar_visible)
++ priv->y_velocity = 0;
+
+- handled = TRUE;
++ priv->is_snapping_back = TRUE;
++
++ if (new_overshoot_x != 0 || new_overshoot_y != 0)
++ {
++ gtk_scrolled_window_start_deceleration (scrolled_window);
++ priv->x_velocity = 0.0;
++ priv->y_velocity = 0.0;
++ }
+ }
+ }
+ else
+@@ -2369,18 +2320,6 @@ scrolled_window_deceleration_cb (gpointer user_data)
+ }
+
+ static void
+-gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
+-{
+- GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+- if (priv->deceleration_id)
+- {
+- g_source_remove (priv->deceleration_id);
+- priv->deceleration_id = 0;
+- }
+-}
+-
+-static void
+ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
+ {
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+@@ -2408,327 +2347,36 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
+ }
+
+ static gboolean
+-gtk_scrolled_window_release_captured_event (GtkScrolledWindow *scrolled_window)
+-{
+- GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+- /* Cancel the scrolling and send the button press
+- * event to the child widget
+- */
+- if (!priv->button_press_event)
+- return FALSE;
+-
+- if (priv->pointer_grabbed)
+- {
+- gtk_grab_remove (GTK_WIDGET (scrolled_window));
+- priv->pointer_grabbed = FALSE;
+- }
+-
+- if (priv->capture_button_press)
+- {
+- GtkWidget *event_widget;
+-
+- event_widget = gtk_get_event_widget (priv->button_press_event);
+-
+- if (!_gtk_propagate_captured_event (event_widget,
+- priv->button_press_event,
+- gtk_bin_get_child (GTK_BIN (scrolled_window))))
+- gtk_propagate_event (event_widget, priv->button_press_event);
+-
+- gdk_event_free (priv->button_press_event);
+- priv->button_press_event = NULL;
+- }
+-
+- if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
+- gtk_scrolled_window_start_deceleration (scrolled_window);
+-
+- return FALSE;
+-}
+-
+-static gboolean
+ gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
+ GdkEvent *event)
+ {
+ GtkScrolledWindowPrivate *priv;
+- gdouble x_root, y_root;
+ guint32 _time;
+
+-#define STILL_THRESHOLD 40
+-
+- if (!gdk_event_get_root_coords (event, &x_root, &y_root))
+- return FALSE;
+-
+ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ _time = gdk_event_get_time (event);
+
+- if (priv->last_motion_event_x_root != x_root ||
+- priv->last_motion_event_y_root != y_root ||
+- ABS (_time - priv->last_motion_event_time) > STILL_THRESHOLD)
+- {
+- priv->x_velocity = (priv->last_motion_event_x_root - x_root) /
+- (gdouble) (_time - priv->last_motion_event_time);
+- priv->y_velocity = (priv->last_motion_event_y_root - y_root) /
+- (gdouble) (_time - priv->last_motion_event_time);
+- }
+-
+- priv->last_motion_event_x_root = x_root;
+- priv->last_motion_event_y_root = y_root;
+- priv->last_motion_event_time = _time;
+-
+-#undef STILL_THRESHOLD
+-
+- return TRUE;
+-}
+-
+-static gboolean
+-gtk_scrolled_window_captured_button_release_kinetic (GtkWidget *widget,
+- GdkEvent *event)
+-{
+- GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+- GtkScrolledWindowPrivate *priv;
+- GtkWidget *child;
+- gboolean overshoot;
+- gdouble x_root, y_root;
+-
+- if (event->button.button != 1)
+- return FALSE;
+-
+- child = gtk_bin_get_child (GTK_BIN (widget));
+- if (!child)
+- return FALSE;
+-
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+- gtk_grab_remove (widget);
+- priv->pointer_grabbed = FALSE;
+-
+- if (priv->release_timeout_id)
+- {
+- g_source_remove (priv->release_timeout_id);
+- priv->release_timeout_id = 0;
+- }
+-
+- overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
+-
+- if (priv->in_drag)
+- gdk_pointer_ungrab (gdk_event_get_time (event));
+- else
+- {
+- /* There hasn't been scrolling at all, so just let the
+- * child widget handle the button press normally
+- */
+- gtk_scrolled_window_release_captured_event (scrolled_window);
+-
+- if (!overshoot)
+- return FALSE;
+- }
+- priv->in_drag = FALSE;
+-
+- if (priv->button_press_event)
+- {
+- gdk_event_free (priv->button_press_event);
+- priv->button_press_event = NULL;
+- }
+-
+- gtk_scrolled_window_calculate_velocity (scrolled_window, event);
+-
+- /* Zero out vector components without a visible scrollbar */
+- if (!scrolled_window->hscrollbar_visible)
+- priv->x_velocity = 0;
+- if (!scrolled_window->vscrollbar_visible)
+- priv->y_velocity = 0;
++#define STILL_THRESHOLD 40
+
+- if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
++ if (event->type == GDK_SCROLL)
+ {
+- gtk_scrolled_window_start_deceleration (scrolled_window);
+- priv->x_velocity = priv->y_velocity = 0;
+- priv->last_button_event_valid = FALSE;
+- }
+- else
+- {
+- gdk_event_get_root_coords (event, &x_root, &y_root);
+- priv->last_button_event_x_root = x_root;
+- priv->last_button_event_y_root = y_root;
+- priv->last_button_event_valid = TRUE;
+- }
+-
+- if (priv->capture_button_press)
+- return TRUE;
+- else
+- return FALSE;
+-}
++ gdouble delta_x, delta_y;
+
+-static gboolean
+-gtk_scrolled_window_captured_motion_notify_kinetic (GtkWidget *widget,
+- GdkEvent *event)
+-{
+- GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+- GtkScrolledWindowPrivate *priv;
+- gint old_overshoot_x, old_overshoot_y;
+- gint new_overshoot_x, new_overshoot_y;
+- GtkWidget *child;
+- GtkAdjustment *hadjustment;
+- GtkAdjustment *vadjustment;
+- gdouble dx, dy;
+- GdkModifierType state;
+- gdouble x_root, y_root;
+-
+- gdk_event_get_state (event, &state);
+- if (!(state & GDK_BUTTON1_MASK))
+- return FALSE;
+-
+- child = gtk_bin_get_child (GTK_BIN (widget));
+- if (!child)
+- return FALSE;
+-
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+- /* Check if we've passed the drag threshold */
+- gdk_event_get_root_coords (event, &x_root, &y_root);
+- if (!priv->in_drag)
+- {
+- if (gtk_drag_check_threshold (widget,
+- priv->last_button_event_x_root,
+- priv->last_button_event_y_root,
+- x_root, y_root))
++ if (gdk_event_get_scroll_deltas (event, &delta_x, &delta_y) &&
++ ABS (_time - priv->last_scroll_event_time) > STILL_THRESHOLD)
+ {
+- if (priv->release_timeout_id)
+- {
+- g_source_remove (priv->release_timeout_id);
+- priv->release_timeout_id = 0;
+- }
++ priv->x_velocity = delta_x / (gdouble) (_time - priv->last_scroll_event_time);
++ priv->y_velocity = delta_y / (gdouble) (_time - priv->last_scroll_event_time);
+
+- priv->last_button_event_valid = FALSE;
+- priv->in_drag = TRUE;
++ priv->last_scroll_event_time = _time;
+ }
+- else
+- return TRUE;
+- }
+-
+- gdk_pointer_grab (gtk_widget_get_window (widget),
+- TRUE,
+- GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK,
+- NULL, NULL,
+- gdk_event_get_time (event));
+-
+- priv->last_button_event_valid = FALSE;
+-
+- if (priv->button_press_event)
+- {
+- gdk_event_free (priv->button_press_event);
+- priv->button_press_event = NULL;
+- }
+-
+- _gtk_scrolled_window_get_overshoot (scrolled_window,
+- &old_overshoot_x, &old_overshoot_y);
+-
+- hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
+- if (hadjustment && scrolled_window->hscrollbar_visible)
+- {
+- dx = (priv->last_motion_event_x_root - x_root) + priv->unclamped_hadj_value;
+- _gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment,
+- dx, TRUE, FALSE);
+- }
+-
+- vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
+- if (vadjustment && scrolled_window->vscrollbar_visible)
+- {
+- dy = (priv->last_motion_event_y_root - y_root) + priv->unclamped_vadj_value;
+- _gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment,
+- dy, TRUE, FALSE);
+ }
+
+- _gtk_scrolled_window_get_overshoot (scrolled_window,
+- &new_overshoot_x, &new_overshoot_y);
+-
+- if (old_overshoot_x != new_overshoot_x ||
+- old_overshoot_y != new_overshoot_y)
+- _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
+-
+- gtk_scrolled_window_calculate_velocity (scrolled_window, event);
++#undef STILL_THRESHOLD
+
+ return TRUE;
+ }
+
+-static gboolean
+-gtk_scrolled_window_captured_button_press_kinetic (GtkWidget *widget,
+- GdkEvent *event)
+-{
+- GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+- GtkScrolledWindowPrivate *priv;
+- GtkWidget *child;
+- GtkWidget *event_widget;
+- gdouble x_root, y_root;
+-
+- /* If scrollbars are not visible, we don't do kinetic scrolling */
+- if (!scrolled_window->vscrollbar_visible &&
+- !scrolled_window->hscrollbar_visible)
+- return FALSE;
+-
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+- event_widget = gtk_get_event_widget (event);
+-
+- /* If there's another scrolled window between the widget
+- * receiving the event and this capturing scrolled window,
+- * let it handle the events.
+- */
+- if (widget != gtk_widget_get_ancestor (event_widget, GTK_TYPE_SCROLLED_WINDOW))
+- return FALSE;
+-
+- /* Check whether the button press is close to the previous one,
+- * take that as a shortcut to get the child widget handle events
+- */
+- gdk_event_get_root_coords (event, &x_root, &y_root);
+- if (priv->last_button_event_valid &&
+- ABS (x_root - priv->last_button_event_x_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD &&
+- ABS (y_root - priv->last_button_event_y_root) < TOUCH_BYPASS_CAPTURED_THRESHOLD)
+- {
+- priv->last_button_event_valid = FALSE;
+- return FALSE;
+- }
+-
+- priv->last_button_event_x_root = priv->last_motion_event_x_root = x_root;
+- priv->last_button_event_y_root = priv->last_motion_event_y_root = y_root;
+- priv->last_motion_event_time = gdk_event_get_time (event);
+- priv->last_button_event_valid = TRUE;
+-
+- if (event->button.button != 1)
+- return FALSE;
+-
+- child = gtk_bin_get_child (GTK_BIN (widget));
+- if (!child)
+- return FALSE;
+-
+- if (scrolled_window->hscrollbar == event_widget ||
+- scrolled_window->vscrollbar == event_widget)
+- return FALSE;
+-
+- priv->pointer_grabbed = TRUE;
+- gtk_grab_add (widget);
+-
+- gtk_scrolled_window_cancel_deceleration (scrolled_window);
+-
+- /* Only set the timeout if we're going to store an event */
+- if (priv->capture_button_press)
+- priv->release_timeout_id =
+- gdk_threads_add_timeout (RELEASE_EVENT_TIMEOUT,
+- (GSourceFunc) gtk_scrolled_window_release_captured_event,
+- scrolled_window);
+-
+- priv->in_drag = FALSE;
+-
+- if (priv->capture_button_press)
+- {
+- /* Store the button press event in
+- * case we need to propagate it later
+- */
+- priv->button_press_event = gdk_event_copy (event);
+- return TRUE;
+- }
+- else
+- return FALSE;
+-}
+-
+ static void
+ gtk_scrolled_window_scroll_step (GtkScrolledWindow *scrolled_window)
+ {
+@@ -3011,22 +2659,14 @@ gtk_scrolled_window_captured_event (GtkWidget *widget,
+ {
+ case GDK_BUTTON_PRESS:
+ retval = gtk_scrolled_window_captured_button_press_scrollbar (widget, event);
+- if (!retval)
+- retval = gtk_scrolled_window_captured_button_press_kinetic (widget, event);
+ break;
+ case GDK_BUTTON_RELEASE:
+ if (priv->sb_pointer_grabbed)
+ retval = gtk_scrolled_window_captured_button_release_scrollbar (widget, event);
+- else if (priv->pointer_grabbed)
+- retval = gtk_scrolled_window_captured_button_release_kinetic (widget, event);
+- else
+- priv->last_button_event_valid = FALSE;
+ break;
+ case GDK_MOTION_NOTIFY:
+ if (priv->sb_pointer_grabbed || !priv->pointer_grabbed)
+ retval = gtk_scrolled_window_captured_motion_notify_scrollbar (widget, event);
+- else if (priv->pointer_grabbed)
+- retval = gtk_scrolled_window_captured_motion_notify_kinetic (widget, event);
+ break;
+ case GDK_LEAVE_NOTIFY:
+ if (!priv->in_drag && !priv->sb_pointer_grabbed)
+@@ -3439,19 +3079,6 @@ gtk_scrolled_window_grab_notify (GtkWidget *widget,
+ gdk_pointer_ungrab (gtk_get_current_event_time ());
+ priv->pointer_grabbed = FALSE;
+ priv->in_drag = FALSE;
+-
+- if (priv->release_timeout_id)
+- {
+- g_source_remove (priv->release_timeout_id);
+- priv->release_timeout_id = 0;
+- }
+-
+- if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
+- gtk_scrolled_window_start_deceleration (scrolled_window);
+- else
+- gtk_scrolled_window_cancel_deceleration (scrolled_window);
+-
+- priv->last_button_event_valid = FALSE;
+ }
+
+ if (priv->sb_pointer_grabbed && !was_grabbed)
+@@ -3923,6 +3550,13 @@ gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj,
+ }
+
+ static void
++gtk_scrolled_window_init_overlay_scrollbars (GtkScrolledWindow *scrolled_window)
++{
++ _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window),
++ gtk_scrolled_window_captured_event);
++}
++
++static void
+ gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *settings,
+ GParamSpec *arg,
+ gpointer user_data)
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From a4e5fe549e5497a7e5ae7eecae4fc654330500f2 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Fri, 28 Sep 2012 06:18:49 +0200
+Subject: [PATCH 22/68] Use start/end phase in event handling
+
+Should make things work better on Mountain Lion.
+---
+ gtk/gtkscrolledwindow.c | 13 +++++--------
+ 1 file changed, 5 insertions(+), 8 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 5341b50..92e75a0 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -2010,12 +2010,10 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ if (old_overshoot_x != 0 || old_overshoot_y != 0)
+ is_overshot = TRUE;
+
+- /* In case the view is not overshot, no snap back is active
+- * and this event is not a momentum event, then a new scrolling
+- * gesture has started. In case we are still in snapping back
+- * state we can reset this (because the snapback has ended).
++ /* If a new gesture has started, reset snap back state.
++ * FIXME: check if overshoot has really ended.
+ */
+- if (!is_overshot && priv->deceleration_id == 0 && !is_momentum_event)
++ if (event->phase == GDK_EVENT_SCROLL_PHASE_START)
+ priv->is_snapping_back = FALSE;
+
+ /* Scroll events are handled in two cases:
+@@ -2109,13 +2107,12 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
+
+ /* In two cases we want to start snapping back:
+- * 1) The view is overshot and the gesture has ended (signalled
+- * by an event with both deltas set to zero.
++ * 1) The view is overshot and the gesture has ended.
+ * 2) The view is overshot and we receive a momentum event, which
+ * also signals that the user's gesture has ended.
+ */
+ if (is_overshot &&
+- ((delta_x == 0.0 && delta_y == 0.0) || is_momentum_event))
++ (event->phase == GDK_EVENT_SCROLL_PHASE_END || is_momentum_event))
+ start_snap_back = TRUE;
+
+ /* If we should start a snap back and no current deceleration
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From ced3c2c63872021885cf91f134dde818096bae42 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Wed, 19 Sep 2012 07:22:39 +0200
+Subject: [PATCH 23/68] Improve overshooting behavior
+
+This is done by taking the stiffness calculations as found in the WebKit
+source code. These calculations are now used to compute the overshooting
+distance as well as handling the snap back animation. Also, we only
+start overshooting after we have hit a border in the container (i.e. an
+adjustment has reached its upper/lower bound).
+
+Also rename the timeout functions from deceleration to snap back, since
+these are not handling kinetic deceleration in this code.
+---
+ gtk/gtkscrolledwindow.c | 179 ++++++++++++++++++++++++++---------------------
+ 1 file changed, 99 insertions(+), 80 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 92e75a0..f3be303 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -84,6 +84,10 @@
+ #define OVERSHOOT_INVERSE_ACCELERATION 0.003
+ #define RELEASE_EVENT_TIMEOUT 1000
+
++#define BAND_STIFFNESS 20.0f
++#define BAND_AMPLITUDE 0.31f
++#define BAND_PERIOD 1.6f
++
+ /* Overlay scrollbars */
+ #define SCROLL_INTERVAL_INITIAL 300
+ #define SCROLL_INTERVAL_REPEAT 100
+@@ -107,6 +111,9 @@ typedef struct {
+ gdouble x_velocity;
+ gdouble y_velocity;
+
++ gdouble x_force;
++ gdouble y_force;
++
+ gdouble unclamped_hadj_value;
+ gdouble unclamped_vadj_value;
+
+@@ -143,12 +150,13 @@ typedef struct {
+ typedef struct
+ {
+ GtkScrolledWindow *scrolled_window;
+- gint64 last_deceleration_time;
++ gint64 start_snap_back_time;
+
+ gdouble x_velocity;
+ gdouble y_velocity;
+- gdouble vel_cosine;
+- gdouble vel_sine;
++
++ gint x_overshoot;
++ gint y_overshoot;
+ } KineticScrollData;
+
+ enum {
+@@ -260,7 +268,7 @@ static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *setting
+ gpointer user_data);
+
+
+-static void gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window);
++static void gtk_scrolled_window_start_snap_back (GtkScrolledWindow *scrolled_window);
+ static gboolean gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
+ GdkEvent *event);
+ static void gtk_scrolled_window_init_overlay_scrollbars (GtkScrolledWindow *window);
+@@ -2016,6 +2024,9 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ if (event->phase == GDK_EVENT_SCROLL_PHASE_START)
+ priv->is_snapping_back = FALSE;
+
++ if (is_momentum_event && !is_overshot)
++ gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
++
+ /* Scroll events are handled in two cases:
+ * 1) We are not overshot and not snapping back, so scroll as
+ * usual and also handle momentum events.
+@@ -2031,15 +2042,29 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ if (delta_x != 0.0 && scrolled_window->hscrollbar &&
+ (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->hscrollbar)))
+ {
++ gboolean may_overshoot = FALSE;
+ GtkAdjustment *adj;
+
++ /* Overshooting is allowed once the adjustment has reached
++ * the left/right.
++ */
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++ gdouble max_adj = gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj);
++ if (gtk_adjustment_get_value (adj) < 1.0 ||
++ gtk_adjustment_get_value (adj) > max_adj - 1.0)
++ may_overshoot = TRUE;
+
+- if (scrolled_window->hscrollbar_visible)
++ if (scrolled_window->hscrollbar_visible && (is_overshot || may_overshoot))
+ {
++ gdouble damped_delta;
++
++ priv->x_force += delta_x;
++ damped_delta = ceil(priv->x_force / BAND_STIFFNESS);
++ damped_delta = damped_delta - old_overshoot_x;
++
+ _gtk_scrolled_window_set_adjustment_value (scrolled_window,
+ adj,
+- priv->unclamped_hadj_value + delta_x,
++ priv->unclamped_hadj_value + damped_delta,
+ TRUE,
+ FALSE);
+ }
+@@ -2064,15 +2089,29 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ if (delta_y != 0.0 && scrolled_window->vscrollbar &&
+ (priv->overlay_scrollbars || gtk_widget_get_visible (scrolled_window->vscrollbar)))
+ {
++ gboolean may_overshoot = FALSE;
+ GtkAdjustment *adj;
+
++ /* Overshooting is allowed once the adjustment has reached
++ * the top/bottom.
++ */
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++ gdouble max_adj = gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj);
++ if (gtk_adjustment_get_value (adj) < 1.0 ||
++ gtk_adjustment_get_value (adj) > max_adj - 1.0)
++ may_overshoot = TRUE;
+
+- if (scrolled_window->vscrollbar_visible)
++ if (scrolled_window->vscrollbar_visible && (is_overshot || may_overshoot))
+ {
++ gdouble damped_delta;
++
++ priv->y_force += delta_y;
++ damped_delta = ceil(priv->y_force / BAND_STIFFNESS);
++ damped_delta = damped_delta - old_overshoot_y;
++
+ _gtk_scrolled_window_set_adjustment_value (scrolled_window,
+ adj,
+- priv->unclamped_vadj_value + delta_y,
++ priv->unclamped_vadj_value + damped_delta,
+ TRUE,
+ FALSE);
+ }
+@@ -2093,10 +2132,6 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+
+ handled = TRUE;
+ }
+-
+-
+- priv->last_scroll_event_time = gdk_event_get_time ((GdkEvent *)event);
+- gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
+ }
+
+ _gtk_scrolled_window_get_overshoot (scrolled_window,
+@@ -2112,9 +2147,17 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ * also signals that the user's gesture has ended.
+ */
+ if (is_overshot &&
+- (event->phase == GDK_EVENT_SCROLL_PHASE_END || is_momentum_event))
++ ((priv->last_scroll_event_time > 0 && is_momentum_event) ||
++ event->phase == GDK_EVENT_SCROLL_PHASE_END))
+ start_snap_back = TRUE;
+
++ /* Reset force if gesture has ended. */
++ if (event->phase == GDK_EVENT_SCROLL_PHASE_END)
++ {
++ priv->x_force = 0.0;
++ priv->y_force = 0.0;
++ }
++
+ /* If we should start a snap back and no current deceleration
+ * is active, start the snap back.
+ */
+@@ -2130,9 +2173,10 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+
+ if (new_overshoot_x != 0 || new_overshoot_y != 0)
+ {
+- gtk_scrolled_window_start_deceleration (scrolled_window);
++ gtk_scrolled_window_start_snap_back (scrolled_window);
+ priv->x_velocity = 0.0;
+ priv->y_velocity = 0.0;
++ priv->last_scroll_event_time = 0;
+ }
+ }
+ }
+@@ -2207,16 +2251,16 @@ _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
+ }
+
+ static gboolean
+-scrolled_window_deceleration_cb (gpointer user_data)
++scrolled_window_snap_back_cb (gpointer user_data)
+ {
+ KineticScrollData *data = user_data;
+ GtkScrolledWindow *scrolled_window = data->scrolled_window;
+ GtkScrolledWindowPrivate *priv;
+ GtkAdjustment *hadjustment, *vadjustment;
+ gint old_overshoot_x, old_overshoot_y, overshoot_x, overshoot_y;
+- gdouble value;
+ gint64 current_time;
+- guint elapsed;
++ gdouble elapsed;
++ gdouble damp_factor;
+
+ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
+@@ -2226,12 +2270,22 @@ scrolled_window_deceleration_cb (gpointer user_data)
+ &old_overshoot_x, &old_overshoot_y);
+
+ current_time = g_get_monotonic_time ();
+- elapsed = (current_time - data->last_deceleration_time) / 1000;
+- data->last_deceleration_time = current_time;
++ elapsed = (current_time - data->start_snap_back_time) / 1000000.0;
++ damp_factor = exp((((double)-elapsed) * BAND_STIFFNESS) / BAND_PERIOD);
+
+ if (hadjustment && scrolled_window->hscrollbar_visible)
+ {
+- value = priv->unclamped_hadj_value + (data->x_velocity * elapsed);
++ gdouble delta_x, value;
++
++ delta_x = (data->x_overshoot + (data->x_velocity * elapsed * BAND_AMPLITUDE)) * damp_factor;
++
++ if (fabs (delta_x) >= 1.0)
++ value = priv->unclamped_hadj_value + (delta_x - old_overshoot_x);
++ else
++ value = CLAMP (priv->unclamped_hadj_value,
++ gtk_adjustment_get_lower (hadjustment),
++ gtk_adjustment_get_upper (hadjustment) -
++ gtk_adjustment_get_page_size (hadjustment));
+
+ if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
+ hadjustment,
+@@ -2243,7 +2297,17 @@ scrolled_window_deceleration_cb (gpointer user_data)
+
+ if (vadjustment && scrolled_window->vscrollbar_visible)
+ {
+- value = priv->unclamped_vadj_value + (data->y_velocity * elapsed);
++ gdouble delta_y, value;
++
++ delta_y = (data->y_overshoot + (data->y_velocity * elapsed * BAND_AMPLITUDE)) * damp_factor;
++
++ if (fabs (delta_y) >= 1.0)
++ value = priv->unclamped_vadj_value + (delta_y - old_overshoot_y);
++ else
++ value = CLAMP (priv->unclamped_vadj_value,
++ gtk_adjustment_get_lower (vadjustment),
++ gtk_adjustment_get_upper (vadjustment) -
++ gtk_adjustment_get_page_size (vadjustment));
+
+ if (_gtk_scrolled_window_set_adjustment_value (scrolled_window,
+ vadjustment,
+@@ -2256,58 +2320,17 @@ scrolled_window_deceleration_cb (gpointer user_data)
+ _gtk_scrolled_window_get_overshoot (scrolled_window,
+ &overshoot_x, &overshoot_y);
+
+- if (overshoot_x == 0)
+- {
+- if (old_overshoot_x != 0)
+- {
+- /* Overshooting finished snapping back */
+- data->x_velocity = 0;
+- }
+- else if (data->x_velocity > 0)
+- {
+- data->x_velocity -= FRICTION_DECELERATION * elapsed * data->vel_sine;
+- data->x_velocity = MAX (0, data->x_velocity);
+- }
+- else if (data->x_velocity < 0)
+- {
+- data->x_velocity += FRICTION_DECELERATION * elapsed * data->vel_sine;
+- data->x_velocity = MIN (0, data->x_velocity);
+- }
+- }
+- else if (overshoot_x < 0)
+- data->x_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
+- else if (overshoot_x > 0)
+- data->x_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
++ if (overshoot_x != 0)
++ priv->x_force = overshoot_x * BAND_STIFFNESS;
+
+- if (overshoot_y == 0)
+- {
+- if (old_overshoot_y != 0)
+- {
+- /* Overshooting finished snapping back */
+- data->y_velocity = 0;
+- }
+- else if (data->y_velocity > 0)
+- {
+- data->y_velocity -= FRICTION_DECELERATION * elapsed * data->vel_cosine;
+- data->y_velocity = MAX (0, data->y_velocity);
+- }
+- else if (data->y_velocity < 0)
+- {
+- data->y_velocity += FRICTION_DECELERATION * elapsed * data->vel_cosine;
+- data->y_velocity = MIN (0, data->y_velocity);
+- }
+- }
+- else if (overshoot_y < 0)
+- data->y_velocity += OVERSHOOT_INVERSE_ACCELERATION * elapsed;
+- else if (overshoot_y > 0)
+- data->y_velocity -= OVERSHOOT_INVERSE_ACCELERATION * elapsed;
++ if (overshoot_y != 0)
++ priv->y_force = overshoot_y * BAND_STIFFNESS;
+
+ if (old_overshoot_x != overshoot_x ||
+ old_overshoot_y != overshoot_y)
+ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
+
+- if (overshoot_x != 0 || overshoot_y != 0 ||
+- data->x_velocity != 0 || data->y_velocity != 0)
++ if (overshoot_x != 0 || overshoot_y != 0)
+ return TRUE;
+ else
+ {
+@@ -2317,29 +2340,24 @@ scrolled_window_deceleration_cb (gpointer user_data)
+ }
+
+ static void
+-gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
++gtk_scrolled_window_start_snap_back (GtkScrolledWindow *scrolled_window)
+ {
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ KineticScrollData *data;
+- gdouble angle;
+
+ data = g_new0 (KineticScrollData, 1);
+ data->scrolled_window = scrolled_window;
+- data->last_deceleration_time = g_get_monotonic_time ();
++ data->start_snap_back_time = g_get_monotonic_time ();
+ data->x_velocity = priv->x_velocity;
+ data->y_velocity = priv->y_velocity;
+-
+- /* We use sine/cosine as a factor to deceleration x/y components
+- * of the vector, so we care about the sign later.
+- */
+- angle = atan2 (ABS (data->x_velocity), ABS (data->y_velocity));
+- data->vel_cosine = cos (angle);
+- data->vel_sine = sin (angle);
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &data->x_overshoot,
++ &data->y_overshoot);
+
+ priv->deceleration_id =
+ gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
+ FRAME_INTERVAL,
+- scrolled_window_deceleration_cb,
++ scrolled_window_snap_back_cb,
+ data, (GDestroyNotify) g_free);
+ }
+
+@@ -2360,13 +2378,14 @@ gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scrolled_window,
+ gdouble delta_x, delta_y;
+
+ if (gdk_event_get_scroll_deltas (event, &delta_x, &delta_y) &&
++ priv->last_scroll_event_time > 0 &&
+ ABS (_time - priv->last_scroll_event_time) > STILL_THRESHOLD)
+ {
+ priv->x_velocity = delta_x / (gdouble) (_time - priv->last_scroll_event_time);
+ priv->y_velocity = delta_y / (gdouble) (_time - priv->last_scroll_event_time);
+-
+- priv->last_scroll_event_time = _time;
+ }
++
++ priv->last_scroll_event_time = _time;
+ }
+
+ #undef STILL_THRESHOLD
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 649890483c012837251571c0042b9149743be9e7 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Wed, 19 Sep 2012 07:28:20 +0200
+Subject: [PATCH 24/68] Cancel out smaller delta component.
+
+---
+ gtk/gtkscrolledwindow.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index f3be303..ab3df16 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -2027,6 +2027,14 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ if (is_momentum_event && !is_overshot)
+ gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
+
++ /* Cancel out smaller component, makes it easier to scroll when the
++ * gestures are not fully straight.
++ */
++ if (fabs (delta_y) >= fabs (delta_x))
++ delta_x = 0.0;
++ else
++ delta_y = 0.0;
++
+ /* Scroll events are handled in two cases:
+ * 1) We are not overshot and not snapping back, so scroll as
+ * usual and also handle momentum events.
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 3c7e6438bb2420c8fd31ba164c400b66ae474f5d Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 5 Oct 2012 12:10:15 +0200
+Subject: [PATCH 25/68] quartz: Add a dummy NSView serving as layer view
+
+By doing this, the GdkQuartzView does not have to be marked as layer
+backed, such that the disadvantages that come with that (not being able
+to copy pixels and large redraw areas) no longer apply.
+---
+ gdk/quartz/GdkQuartzView.c | 6 ++++++
+ gdk/quartz/gdkquartz.h | 1 +
+ gdk/quartz/gdkwindow-quartz.c | 16 ++++++++++++++++
+ gdk/quartz/gdkwindow-quartz.h | 1 +
+ 4 files changed, 24 insertions(+)
+
+diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c
+index 7f0ec40..c6ccf06 100644
+--- a/gdk/quartz/GdkQuartzView.c
++++ b/gdk/quartz/GdkQuartzView.c
+@@ -632,6 +632,8 @@
+ [[self window] invalidateShadow];
+ needsInvalidateShadow = NO;
+ }
++
++ [[self layer] removeAllAnimations];
+ }
+
+ -(void)setNeedsInvalidateShadow: (BOOL)invalidate
+@@ -690,7 +692,11 @@
+
+ -(void)setFrame: (NSRect)frame
+ {
++ GdkWindowObject *private = GDK_WINDOW_OBJECT (gdk_window);
++ GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
++
+ [super setFrame: frame];
++ [impl->layer_view setFrame: frame];
+
+ if ([self window])
+ [self updateTrackingRect];
+diff --git a/gdk/quartz/gdkquartz.h b/gdk/quartz/gdkquartz.h
+index 3c42983..8b0085c 100644
+--- a/gdk/quartz/gdkquartz.h
++++ b/gdk/quartz/gdkquartz.h
+@@ -54,6 +54,7 @@ typedef enum
+
+ NSWindow *gdk_quartz_window_get_nswindow (GdkWindow *window);
+ NSView *gdk_quartz_window_get_nsview (GdkWindow *window);
++NSView *gdk_quartz_window_get_layer_view (GdkWindow *window);
+ NSImage *gdk_quartz_pixbuf_to_ns_image_libgtk_only (GdkPixbuf *pixbuf);
+ id gdk_quartz_drag_context_get_dragging_info_libgtk_only (GdkDragContext *context);
+ NSEvent *gdk_quartz_event_get_nsevent (GdkEvent *event);
+diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c
+index 318a171..500776d 100644
+--- a/gdk/quartz/gdkwindow-quartz.c
++++ b/gdk/quartz/gdkwindow-quartz.c
+@@ -68,6 +68,17 @@ gdk_quartz_window_get_nsview (GdkWindow *window)
+ return ((GdkWindowImplQuartz *)private->impl)->view;
+ }
+
++NSView *
++gdk_quartz_window_get_layer_view (GdkWindow *window)
++{
++ GdkWindowObject *private = (GdkWindowObject *)window;
++
++ if (GDK_WINDOW_DESTROYED (window))
++ return NULL;
++
++ return ((GdkWindowImplQuartz *)private->impl)->layer_view;
++}
++
+ NSWindow *
+ gdk_quartz_window_get_nswindow (GdkWindow *window)
+ {
+@@ -1024,6 +1035,11 @@ _gdk_window_impl_new (GdkWindow *window,
+ [impl->view setGdkWindow:window];
+ [impl->toplevel setContentView:impl->view];
+ [impl->view release];
++
++ impl->layer_view = [[NSView alloc] initWithFrame:content_rect];
++ [impl->view addSubview:impl->layer_view];
++ [impl->layer_view setWantsLayer:YES];
++ [impl->layer_view release];
+ }
+ break;
+
+diff --git a/gdk/quartz/gdkwindow-quartz.h b/gdk/quartz/gdkwindow-quartz.h
+index 4a0e27a..f35238b 100644
+--- a/gdk/quartz/gdkwindow-quartz.h
++++ b/gdk/quartz/gdkwindow-quartz.h
+@@ -47,6 +47,7 @@ struct _GdkWindowImplQuartz
+ NSWindow *toplevel;
+ NSTrackingRectTag tracking_rect;
+ GdkQuartzView *view;
++ NSView *layer_view;
+
+ GdkWindowTypeHint type_hint;
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 756b650315bed2426bd7f1e0525a10b707c868e6 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 27 Sep 2012 17:03:47 +0200
+Subject: [PATCH 26/68] gtk: port overlay scrollbars to native CALayers
+
+---
+ gtk/gtkscrolledwindow.c | 679 ++++++++++++++++++++++++++---------------------
+ 1 file changed, 379 insertions(+), 300 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index ab3df16..1fba87b 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -36,8 +36,12 @@
+ #include "gtkintl.h"
+ #include "gtkmain.h"
+ #include "gtkdnd.h"
++#include "gtktreeview.h"
+ #include "gtkalias.h"
+
++#include "gdk/quartz/gdkquartz.h"
++#include <Cocoa/Cocoa.h>
++
+
+ /* scrolled window policy and size requisition handling:
+ *
+@@ -117,6 +121,12 @@ typedef struct {
+ gdouble unclamped_hadj_value;
+ gdouble unclamped_vadj_value;
+
++ GtkAllocation viewport_allocation;
++ CALayer *vbar_layer;
++ CALayer *hbar_layer;
++ CALayer *vslider_layer;
++ CALayer *hslider_layer;
++
+ GtkAdjustment *opacity;
+ GbAnimation *opacity_anim;
+
+@@ -243,29 +253,23 @@ static void gtk_scrolled_window_start_fade_out_timeout (GtkScrolledWindow *scrol
+ static void gtk_scrolled_window_stop_fade_out_timeout (GtkScrolledWindow *scrolled_window);
+ static void gtk_scrolled_window_start_fade_in_animation (GtkScrolledWindow *scrolled_window);
+ static void gtk_scrolled_window_start_fade_out_animation (GtkScrolledWindow *scrolled_window);
+-static gboolean
+- gtk_scrolled_window_over_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+- GdkEvent *event,
++static gboolean gtk_scrolled_window_over_scroll_areas (GtkScrolledWindow *scrolled_window,
+ gint x,
+ gint y,
+ gboolean *over_vscroll,
+ gboolean *over_hscroll);
+-static void gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+- GtkWidget *child,
+- GdkWindow *child_window,
++static void gtk_scrolled_window_get_scroll_areas (GtkScrolledWindow *scrolled_window,
+ GdkRectangle *vbar_rect,
+ GdkRectangle *vslider_rect,
+ GdkRectangle *hbar_rect,
+ GdkRectangle *hslider_rect);
+-static gboolean gtk_scrolled_window_child_expose (GtkWidget *widget,
+- GdkEventExpose *eevent,
+- GtkScrolledWindow *scrolled_window);
+-static void gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj,
+- GtkScrolledWindow *scrolled_window);
++static void gtk_scrolled_window_update_scrollbars (GtkScrolledWindow *scrolled_window);
+
+ static void gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *settings,
+ GParamSpec *arg,
+ gpointer user_data);
++static void gtk_scrolled_window_map_layers (GtkScrolledWindow *scrolled_window);
++static void gtk_scrolled_window_unmap_layers (GtkScrolledWindow *scrolled_window);
+
+
+ static void gtk_scrolled_window_start_snap_back (GtkScrolledWindow *scrolled_window);
+@@ -536,9 +540,9 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ priv->sb_width = 6;
+ priv->sb_fade_out_delay = 1000;
+
+- g_signal_connect (priv->opacity, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
++ g_signal_connect_swapped (priv->opacity, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_update_scrollbars),
++ scrolled_window);
+ }
+
+ /**
+@@ -587,7 +591,6 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ GtkAdjustment *hadjustment)
+ {
+ GtkBin *bin;
+- GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
+ if (hadjustment)
+@@ -623,7 +626,7 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_value_changed,
+ scrolled_window);
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+- gtk_scrolled_window_expose_scrollbars,
++ gtk_scrolled_window_update_scrollbars,
+ scrolled_window);
+
+ gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
+@@ -641,18 +644,12 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
+
+-#if 0
+- g_signal_connect (hadjustment, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
+- scrolled_window);
+-#endif
+-
+- g_signal_connect (hadjustment, "changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
+- g_signal_connect (hadjustment, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
++ g_signal_connect_swapped (hadjustment, "changed",
++ G_CALLBACK (gtk_scrolled_window_update_scrollbars),
++ scrolled_window);
++ g_signal_connect_swapped (hadjustment, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_update_scrollbars),
++ scrolled_window);
+
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+@@ -711,7 +708,7 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_value_changed,
+ scrolled_window);
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+- gtk_scrolled_window_expose_scrollbars,
++ gtk_scrolled_window_update_scrollbars,
+ scrolled_window);
+
+ gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
+@@ -729,19 +726,12 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
+
+-#if 0
+- g_signal_connect (vadjustment,
+- "value-changed",
+- G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
+- scrolled_window);
+-#endif
+-
+- g_signal_connect (vadjustment, "changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
+- g_signal_connect (vadjustment, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_expose_scrollbars),
+- scrolled_window);
++ g_signal_connect_swapped (vadjustment, "changed",
++ G_CALLBACK (gtk_scrolled_window_update_scrollbars),
++ scrolled_window);
++ g_signal_connect_swapped (vadjustment, "value-changed",
++ G_CALLBACK (gtk_scrolled_window_update_scrollbars),
++ scrolled_window);
+
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+@@ -1081,7 +1071,7 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ gtk_scrolled_window_adjustment_value_changed,
+ scrolled_window);
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+- gtk_scrolled_window_expose_scrollbars,
++ gtk_scrolled_window_update_scrollbars,
+ scrolled_window);
+
+ gtk_widget_unparent (scrolled_window->hscrollbar);
+@@ -1098,7 +1088,7 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ gtk_scrolled_window_adjustment_value_changed,
+ scrolled_window);
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
+- gtk_scrolled_window_expose_scrollbars,
++ gtk_scrolled_window_update_scrollbars,
+ scrolled_window);
+
+ gtk_widget_unparent (scrolled_window->vscrollbar);
+@@ -1276,10 +1266,11 @@ gtk_scrolled_window_screen_changed (GtkWidget *widget,
+ }
+
+ static void
+-gtk_scrolled_window_paint (GtkWidget *widget,
+- GdkRectangle *area)
++gtk_scrolled_window_paint (GtkWidget *widget,
++ GdkEventExpose *event)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
++ GdkRectangle *area = &event->area;
+ GtkAllocation relative_allocation;
+
+ if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
+@@ -1317,6 +1308,116 @@ gtk_scrolled_window_paint (GtkWidget *widget,
+ }
+ }
+
++static void
++gtk_scrolled_window_update_scrollbars (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GtkWidget *widget = GTK_WIDGET (scrolled_window);
++ GdkWindow *window;
++ gint window_height;
++ GdkRectangle vbar_rect;
++ GdkRectangle vslider_rect;
++ GdkRectangle hbar_rect;
++ GdkRectangle hslider_rect;
++ CGRect rect;
++
++ if (!priv->overlay_scrollbars || !gtk_widget_get_realized (widget))
++ return;
++
++ window = gtk_widget_get_window (gtk_widget_get_toplevel (widget));
++ window_height = gdk_window_get_height (window);
++
++ gtk_scrolled_window_get_scroll_areas (scrolled_window,
++ &vbar_rect, &vslider_rect,
++ &hbar_rect, &hslider_rect);
++
++ if (priv->sb_visible && scrolled_window->vscrollbar && vbar_rect.width > 0)
++ {
++ rect.origin.x = priv->viewport_allocation.x + vbar_rect.x;
++ rect.origin.y = priv->viewport_allocation.y + vbar_rect.y;
++ rect.size.width = vbar_rect.width;
++ rect.size.height = vbar_rect.height;
++
++ rect.origin.y = window_height - rect.origin.y - rect.size.height;
++
++ priv->vbar_layer.frame = rect;
++ priv->vbar_layer.opacity = gtk_adjustment_get_value (priv->opacity) / 2.0;
++ }
++ else
++ {
++ priv->vbar_layer.opacity = 0.0;
++ }
++
++ if (priv->sb_visible && scrolled_window->hscrollbar && hbar_rect.width > 0)
++ {
++ rect.origin.x = priv->viewport_allocation.x + hbar_rect.x;
++ rect.origin.y = priv->viewport_allocation.y + hbar_rect.y;
++ rect.size.width = hbar_rect.width;
++ rect.size.height = hbar_rect.height;
++
++ /* don't overlap in the corner */
++ if (scrolled_window->vscrollbar && vbar_rect.width > 0)
++ rect.size.width -= vbar_rect.width;
++
++ rect.origin.y = window_height - rect.origin.y - rect.size.height;
++
++ priv->hbar_layer.frame = rect;
++ priv->hbar_layer.opacity = gtk_adjustment_get_value (priv->opacity) / 2.0;
++ }
++ else
++ {
++ priv->hbar_layer.opacity = 0.0;
++ }
++
++ if (scrolled_window->vscrollbar && vslider_rect.width > 0)
++ {
++ rect.origin.x = priv->viewport_allocation.x + vslider_rect.x;
++ rect.origin.y = priv->viewport_allocation.y + vslider_rect.y;
++ rect.size.width = vslider_rect.width;
++ rect.size.height = vslider_rect.height;
++
++ rect.origin.y = window_height - rect.origin.y - rect.size.height;
++
++ priv->vslider_layer.frame = rect;
++ priv->vslider_layer.cornerRadius = priv->sb_radius;
++ priv->vslider_layer.opacity = gtk_adjustment_get_value (priv->opacity);
++ }
++ else
++ {
++ priv->vslider_layer.opacity = 0.0;
++ }
++
++ if (scrolled_window->hscrollbar && hslider_rect.width > 0)
++ {
++ rect.origin.x = priv->viewport_allocation.x + hslider_rect.x;
++ rect.origin.y = priv->viewport_allocation.y + hslider_rect.y;
++ rect.size.width = hslider_rect.width;
++ rect.size.height = hslider_rect.height;
++
++ rect.origin.y = window_height - rect.origin.y - rect.size.height;
++
++ priv->hslider_layer.frame = rect;
++ priv->hslider_layer.cornerRadius = priv->sb_radius;
++ priv->hslider_layer.opacity = gtk_adjustment_get_value (priv->opacity);
++ }
++ else
++ {
++ priv->hslider_layer.opacity = 0.0;
++ }
++
++ [priv->vbar_layer removeAllAnimations];
++ [priv->vbar_layer setNeedsDisplay];
++
++ [priv->vslider_layer removeAllAnimations];
++ [priv->vslider_layer setNeedsDisplay];
++
++ [priv->hbar_layer removeAllAnimations];
++ [priv->hbar_layer setNeedsDisplay];
++
++ [priv->hslider_layer removeAllAnimations];
++ [priv->hslider_layer setNeedsDisplay];
++}
++
+ static gboolean
+ gtk_scrolled_window_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+@@ -1326,23 +1427,11 @@ gtk_scrolled_window_expose (GtkWidget *widget,
+
+ if (gtk_widget_is_drawable (widget))
+ {
+- GdkWindow *hscrollbar_window = NULL;
+- GdkWindow *vscrollbar_window = NULL;
++ gtk_scrolled_window_paint (widget, event);
+
+- if (scrolled_window->hscrollbar)
+- hscrollbar_window = gtk_widget_get_window (scrolled_window->hscrollbar);
++ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->expose_event (widget, event);
+
+- if (scrolled_window->vscrollbar)
+- vscrollbar_window = gtk_widget_get_window (scrolled_window->vscrollbar);
+-
+- if (event->window == priv->overshoot_window ||
+- event->window == priv->vbackground_window ||
+- event->window == priv->hbackground_window ||
+- event->window == hscrollbar_window ||
+- event->window == vscrollbar_window)
+- GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->expose_event (widget, event);
+- else
+- gtk_scrolled_window_paint (widget, &event->area);
++ gtk_scrolled_window_update_scrollbars (scrolled_window);
+ }
+
+ return FALSE;
+@@ -1554,6 +1643,8 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+ else
+ requisition->width += vscrollbar_requisition.width;
+ }
++ else
++ requisition->width += priv->sb_width + 2 * priv->sb_padding;
+
+ if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
+ requisition->height += child_requisition.height;
+@@ -1569,6 +1660,8 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+ else
+ requisition->height += hscrollbar_requisition.height;
+ }
++ else
++ requisition->height += priv->sb_width + 2 * priv->sb_padding;
+ }
+
+ if (! priv->overlay_scrollbars)
+@@ -1785,6 +1878,12 @@ _gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_wind
+ }
+ else
+ gdk_window_hide (priv->hbackground_window);
++
++ if (priv->overlay_scrollbars)
++ {
++ gtk_scrolled_window_start_fade_in_animation (scrolled_window);
++ gtk_scrolled_window_update_scrollbars (scrolled_window);
++ }
+ }
+
+ static void
+@@ -1811,6 +1910,35 @@ gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
+ }
+
+ static void
++gtk_scrolled_window_compute_viewport_allocation (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++ GtkWidget *widget = GTK_WIDGET (scrolled_window);
++ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
++ gint toplevel_alloc_x, toplevel_alloc_y;
++
++ /* Translate the viewport_allocation coordinates to coordinates relative to
++ * the toplevel. Then set these coordinates as viewport_allocation, which
++ * will be used to draw the scrollbars to the CALayer.
++ */
++ gtk_scrolled_window_relative_allocation (widget, &priv->viewport_allocation);
++ if (gtk_widget_translate_coordinates (widget, toplevel,
++ priv->viewport_allocation.x,
++ priv->viewport_allocation.y,
++ &toplevel_alloc_x, &toplevel_alloc_y))
++ {
++ priv->viewport_allocation.x = toplevel_alloc_x;
++ priv->viewport_allocation.y = toplevel_alloc_y;
++ }
++ else
++ {
++ /* Fallback using only the widget's allocation. */
++ priv->viewport_allocation.x += widget->allocation.x;
++ priv->viewport_allocation.y += widget->allocation.y;
++ }
++}
++
++static void
+ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+ {
+@@ -1829,8 +1957,6 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ bin = GTK_BIN (scrolled_window);
+
+- gtk_scrolled_window_expose_scrollbars (NULL, scrolled_window);
+-
+ scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+ gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
+
+@@ -1879,6 +2005,9 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ }
+ while (previous_hvis != scrolled_window->hscrollbar_visible ||
+ previous_vvis != scrolled_window->vscrollbar_visible);
++
++ if (gtk_widget_get_realized (widget))
++ gtk_scrolled_window_compute_viewport_allocation (scrolled_window);
+ }
+ else
+ {
+@@ -2166,6 +2295,10 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ priv->y_force = 0.0;
+ }
+
++ /* Stop fade out timeout while we're overshot */
++ if (new_overshoot_x != 0 || new_overshoot_y != 0)
++ gtk_scrolled_window_stop_fade_out_timeout (scrolled_window);
++
+ /* If we should start a snap back and no current deceleration
+ * is active, start the snap back.
+ */
+@@ -2343,6 +2476,10 @@ scrolled_window_snap_back_cb (gpointer user_data)
+ else
+ {
+ priv->deceleration_id = 0;
++
++ /* Snap back has finished, make sure the scrollbars will fade out */
++ gtk_scrolled_window_start_fade_out_timeout (scrolled_window);
++
+ return FALSE;
+ }
+ }
+@@ -2445,6 +2582,25 @@ static gboolean
+ gtk_scrolled_window_captured_motion_notify_scrollbar (GtkWidget *widget,
+ GdkEvent *event);
+
++static void
++gtk_scrolled_window_translate_coordinates (GtkWidget *src_widget,
++ GtkWidget *dest_widget,
++ gint src_x,
++ gint src_y,
++ gint *dest_x,
++ gint *dest_y)
++{
++ if (GTK_IS_TREE_VIEW (src_widget))
++ {
++ gtk_tree_view_convert_bin_window_to_widget_coords (GTK_TREE_VIEW (src_widget),
++ src_x, src_y,
++ &src_x, &src_y);
++ }
++
++ gtk_widget_translate_coordinates (src_widget, dest_widget,
++ src_x, src_y, dest_x, dest_y);
++}
++
+ static gboolean
+ gtk_scrolled_window_captured_button_press_scrollbar (GtkWidget *widget,
+ GdkEvent *event)
+@@ -2452,14 +2608,19 @@ gtk_scrolled_window_captured_button_press_scrollbar (GtkWidget *widget,
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ GdkEventButton *bevent = (GdkEventButton *) event;
++ GtkWidget *event_widget = gtk_get_event_widget (event);
++ gint x, y;
+
+ if (bevent->button != 1)
+ return FALSE;
+
+- if (gtk_scrolled_window_over_child_scroll_areas (scrolled_window, event,
+- bevent->x, bevent->y,
+- &priv->sb_grab_vscroll,
+- &priv->sb_grab_hscroll))
++ gtk_scrolled_window_translate_coordinates (event_widget, widget,
++ bevent->x, bevent->y, &x, &y);
++
++ if (gtk_scrolled_window_over_scroll_areas (scrolled_window,
++ x, y,
++ &priv->sb_grab_vscroll,
++ &priv->sb_grab_hscroll))
+ {
+ GdkRectangle vbar_rect;
+ GdkRectangle vslider_rect;
+@@ -2469,11 +2630,9 @@ gtk_scrolled_window_captured_button_press_scrollbar (GtkWidget *widget,
+ priv->sb_pointer_grabbed = TRUE;
+ gtk_grab_add (widget);
+
+- gtk_scrolled_window_get_child_scroll_areas (scrolled_window,
+- gtk_bin_get_child (GTK_BIN (widget)),
+- bevent->window,
+- &vbar_rect, &vslider_rect,
+- &hbar_rect, &hslider_rect);
++ gtk_scrolled_window_get_scroll_areas (scrolled_window,
++ &vbar_rect, &vslider_rect,
++ &hbar_rect, &hslider_rect);
+
+ if (priv->sb_grab_vscroll)
+ {
+@@ -2481,20 +2640,20 @@ gtk_scrolled_window_captured_button_press_scrollbar (GtkWidget *widget,
+ vslider_rect.x = vbar_rect.x;
+ vslider_rect.width = vbar_rect.width;
+
+- if (bevent->x >= vslider_rect.x &&
+- bevent->x < (vslider_rect.x + vslider_rect.width) &&
+- bevent->y >= vslider_rect.y &&
+- bevent->y < (vslider_rect.y + vslider_rect.height))
++ if (x >= vslider_rect.x &&
++ x < (vslider_rect.x + vslider_rect.width) &&
++ y >= vslider_rect.y &&
++ y < (vslider_rect.y + vslider_rect.height))
+ {
+ priv->sb_drag_slider = TRUE;
+- priv->sb_grab_offset_y = bevent->y - vslider_rect.y;
++ priv->sb_grab_offset_y = y - vslider_rect.y;
+ }
+ else
+ {
+ priv->sb_drag_slider = FALSE;
+- priv->sb_grab_offset_y = bevent->y - vbar_rect.y;
++ priv->sb_grab_offset_y = y - vbar_rect.y;
+
+- if (bevent->y < vslider_rect.y)
++ if (y < vslider_rect.y)
+ priv->sb_scroll_direction = -1;
+ else
+ priv->sb_scroll_direction = 1;
+@@ -2506,20 +2665,20 @@ gtk_scrolled_window_captured_button_press_scrollbar (GtkWidget *widget,
+ hslider_rect.y = hbar_rect.y;
+ hslider_rect.height = hbar_rect.height;
+
+- if (bevent->x >= hslider_rect.x &&
+- bevent->x < (hslider_rect.x + hslider_rect.width) &&
+- bevent->y >= hslider_rect.y &&
+- bevent->y < (hslider_rect.y + hslider_rect.height))
++ if (x >= hslider_rect.x &&
++ x < (hslider_rect.x + hslider_rect.width) &&
++ y >= hslider_rect.y &&
++ y < (hslider_rect.y + hslider_rect.height))
+ {
+ priv->sb_drag_slider = TRUE;
+- priv->sb_grab_offset_x = bevent->x - hslider_rect.x;
++ priv->sb_grab_offset_x = x - hslider_rect.x;
+ }
+ else
+ {
+ priv->sb_drag_slider = FALSE;
+- priv->sb_grab_offset_x = bevent->x - hbar_rect.x;
++ priv->sb_grab_offset_x = x - hbar_rect.x;
+
+- if (bevent->x < hslider_rect.x)
++ if (x < hslider_rect.x)
+ priv->sb_scroll_direction = -1;
+ else
+ priv->sb_scroll_direction = 1;
+@@ -2597,6 +2756,11 @@ gtk_scrolled_window_captured_motion_notify_scrollbar (GtkWidget *widget,
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ GdkEventMotion *mevent = (GdkEventMotion *) event;
++ GtkWidget *event_widget = gtk_get_event_widget (event);
++ gint x, y;
++
++ gtk_scrolled_window_translate_coordinates (event_widget, widget,
++ mevent->x, mevent->y, &x, &y);
+
+ if (priv->sb_pointer_grabbed)
+ {
+@@ -2611,22 +2775,20 @@ gtk_scrolled_window_captured_motion_notify_scrollbar (GtkWidget *widget,
+ gint visible_range;
+ gdouble value;
+
+- gtk_scrolled_window_get_child_scroll_areas (scrolled_window,
+- gtk_bin_get_child (GTK_BIN (widget)),
+- mevent->window,
+- &vbar_rect, &vslider_rect,
+- &hbar_rect, &hslider_rect);
++ gtk_scrolled_window_get_scroll_areas (scrolled_window,
++ &vbar_rect, &vslider_rect,
++ &hbar_rect, &hslider_rect);
+
+ if (priv->sb_grab_vscroll)
+ {
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
+- pos = mevent->y - priv->sb_grab_offset_y - vbar_rect.y;
++ pos = y - priv->sb_grab_offset_y - vbar_rect.y;
+ visible_range = vbar_rect.height - vslider_rect.height;
+ }
+ else if (priv->sb_grab_hscroll)
+ {
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
+- pos = mevent->x - priv->sb_grab_offset_x - hbar_rect.x;
++ pos = x - priv->sb_grab_offset_x - hbar_rect.x;
+ visible_range = hbar_rect.width - hslider_rect.width;
+ }
+
+@@ -2641,9 +2803,9 @@ gtk_scrolled_window_captured_motion_notify_scrollbar (GtkWidget *widget,
+ }
+ else
+ {
+- if (gtk_scrolled_window_over_child_scroll_areas (scrolled_window, event,
+- mevent->x, mevent->y,
+- NULL, NULL))
++ if (gtk_scrolled_window_over_scroll_areas (scrolled_window,
++ x, y,
++ NULL, NULL))
+ {
+ priv->sb_hovering = TRUE;
+ priv->sb_visible = TRUE;
+@@ -2652,7 +2814,7 @@ gtk_scrolled_window_captured_motion_notify_scrollbar (GtkWidget *widget,
+ gtk_scrolled_window_stop_fade_out_timeout (scrolled_window);
+
+ /* needed when entering the scrollbar */
+- gtk_scrolled_window_expose_scrollbars (NULL, scrolled_window);
++ gtk_scrolled_window_update_scrollbars (scrolled_window);
+
+ return TRUE;
+ }
+@@ -2839,10 +3001,6 @@ gtk_scrolled_window_add (GtkContainer *container,
+ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar))))
+ g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
+ "use gtk_scrolled_window_add_with_viewport() instead");
+-
+- g_signal_connect_after (child, "expose-event",
+- G_CALLBACK (gtk_scrolled_window_child_expose),
+- container);
+ }
+
+ static void
+@@ -2857,10 +3015,6 @@ gtk_scrolled_window_remove (GtkContainer *container,
+
+ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (container);
+
+- g_signal_handlers_disconnect_by_func (child,
+- gtk_scrolled_window_child_expose,
+- container);
+-
+ gtk_widget_set_scroll_adjustments (child, NULL, NULL);
+
+ /* chain parent class handler to remove child */
+@@ -3033,6 +3187,37 @@ gtk_scrolled_window_realize (GtkWidget *widget)
+ priv->overshoot_window);
+
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
++
++ gtk_scrolled_window_compute_viewport_allocation (scrolled_window);
++
++ GdkWindow *parent_window = gtk_widget_get_window (gtk_widget_get_toplevel (widget));
++ NSView *layer_view;
++ CALayer *parent_layer;
++
++ layer_view = gdk_quartz_window_get_layer_view (parent_window);
++ parent_layer = [layer_view layer];
++
++ priv->vbar_layer = [[CALayer layer] retain];
++ priv->vbar_layer.backgroundColor = CGColorCreateGenericRGB (0.0, 0.0, 0.0, 0.5);
++ priv->vbar_layer.hidden = YES;
++
++ priv->vslider_layer = [[CALayer layer] retain];
++ priv->vslider_layer.backgroundColor = CGColorCreateGenericRGB (0.0, 0.0, 0.0, 1.0);
++ priv->vslider_layer.hidden = YES;
++
++ priv->hbar_layer = [[CALayer layer] retain];
++ priv->hbar_layer.backgroundColor = CGColorCreateGenericRGB (0.0, 0.0, 0.0, 0.5);
++ priv->hbar_layer.hidden = YES;
++
++ priv->hslider_layer = [[CALayer layer] retain];
++ priv->hslider_layer.backgroundColor = CGColorCreateGenericRGB (0.0, 0.0, 0.0, 1.0);
++ priv->hslider_layer.hidden = YES;
++
++ [parent_layer addSublayer:priv->vbar_layer];
++ [parent_layer addSublayer:priv->vslider_layer];
++
++ [parent_layer addSublayer:priv->hbar_layer];
++ [parent_layer addSublayer:priv->hslider_layer];
+ }
+
+ static void
+@@ -3053,6 +3238,22 @@ gtk_scrolled_window_unrealize (GtkWidget *widget)
+ gdk_window_destroy (priv->hbackground_window);
+ priv->hbackground_window = NULL;
+
++ [priv->vbar_layer removeFromSuperlayer];
++ [priv->vbar_layer release];
++ priv->vbar_layer = NULL;
++
++ [priv->vslider_layer removeFromSuperlayer];
++ [priv->vslider_layer release];
++ priv->vslider_layer = NULL;
++
++ [priv->hbar_layer removeFromSuperlayer];
++ [priv->hbar_layer release];
++ priv->hbar_layer = NULL;
++
++ [priv->hslider_layer removeFromSuperlayer];
++ [priv->hslider_layer release];
++ priv->hslider_layer = NULL;
++
+ gtk_widget_set_realized (widget, FALSE);
+
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
+@@ -3076,6 +3277,9 @@ gtk_scrolled_window_map (GtkWidget *widget)
+ gdk_window_hide (priv->hbackground_window);
+
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
++
++ if (priv->overlay_scrollbars)
++ gtk_scrolled_window_map_layers (scrolled_window);
+ }
+
+ static void
+@@ -3088,6 +3292,9 @@ gtk_scrolled_window_unmap (GtkWidget *widget)
+ gdk_window_hide (priv->vbackground_window);
+ gdk_window_hide (priv->hbackground_window);
+
++ /* Always unmap the layers, regardless of overlay state */
++ gtk_scrolled_window_unmap_layers (scrolled_window);
++
+ GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
+ }
+
+@@ -3117,75 +3324,25 @@ gtk_scrolled_window_grab_notify (GtkWidget *widget,
+ }
+ }
+
+-static void
+-gtk_scrolled_window_rounded_rectangle (cairo_t *cr,
+- gint x,
+- gint y,
+- gint width,
+- gint height,
+- gint x_radius,
+- gint y_radius)
+-{
+- gint x1, x2;
+- gint y1, y2;
+- gint xr1, xr2;
+- gint yr1, yr2;
+-
+- x1 = x;
+- x2 = x1 + width;
+- y1 = y;
+- y2 = y1 + height;
+-
+- x_radius = MIN (x_radius, width / 2.0);
+- y_radius = MIN (y_radius, height / 2.0);
+-
+- xr1 = x_radius;
+- xr2 = x_radius / 2.0;
+- yr1 = y_radius;
+- yr2 = y_radius / 2.0;
+-
+- cairo_move_to (cr, x1 + xr1, y1);
+- cairo_line_to (cr, x2 - xr1, y1);
+- cairo_curve_to (cr, x2 - xr2, y1, x2, y1 + yr2, x2, y1 + yr1);
+- cairo_line_to (cr, x2, y2 - yr1);
+- cairo_curve_to (cr, x2, y2 - yr2, x2 - xr2, y2, x2 - xr1, y2);
+- cairo_line_to (cr, x1 + xr1, y2);
+- cairo_curve_to (cr, x1 + xr2, y2, x1, y2 - yr2, x1, y2 - yr1);
+- cairo_line_to (cr, x1, y1 + yr1);
+- cairo_curve_to (cr, x1, y1 + yr2, x1 + xr2, y1, x1 + xr1, y1);
+- cairo_close_path (cr);
+-}
+-
+ static gboolean
+-gtk_scrolled_window_over_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+- GdkEvent *event,
+- gint x,
+- gint y,
+- gboolean *over_vscroll,
+- gboolean *over_hscroll)
++gtk_scrolled_window_over_scroll_areas (GtkScrolledWindow *scrolled_window,
++ gint x,
++ gint y,
++ gboolean *over_vscroll,
++ gboolean *over_hscroll)
+ {
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+- GtkWidget *child;
+ GdkRectangle vbar_rect;
+ GdkRectangle hbar_rect;
+ gboolean over_v = FALSE;
+ gboolean over_h = FALSE;
+
+- child = gtk_bin_get_child (GTK_BIN (scrolled_window));
+- if (!child)
+- return FALSE;
+-
+- if (gtk_get_event_widget (event) != child)
+- return FALSE;
+-
+ if (gtk_adjustment_get_value (priv->opacity) == 0.0)
+ return FALSE;
+
+- gtk_scrolled_window_get_child_scroll_areas (scrolled_window,
+- child,
+- ((GdkEventAny *) event)->window,
+- &vbar_rect, NULL,
+- &hbar_rect, NULL);
++ gtk_scrolled_window_get_scroll_areas (scrolled_window,
++ &vbar_rect, NULL,
++ &hbar_rect, NULL);
+
+ if (vbar_rect.width > 0 &&
+ x >= vbar_rect.x && x < (vbar_rect.x + vbar_rect.width) &&
+@@ -3207,72 +3364,48 @@ gtk_scrolled_window_over_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ }
+
+ static void
+-gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+- GtkWidget *child,
+- GdkWindow *child_window,
+- GdkRectangle *vbar_rect,
+- GdkRectangle *vslider_rect,
+- GdkRectangle *hbar_rect,
+- GdkRectangle *hslider_rect)
++gtk_scrolled_window_get_scroll_areas (GtkScrolledWindow *scrolled_window,
++ GdkRectangle *vbar_rect,
++ GdkRectangle *vslider_rect,
++ GdkRectangle *hbar_rect,
++ GdkRectangle *hslider_rect)
+ {
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ GtkAdjustment *adj;
+- GtkAllocation allocation;
++ gdouble value;
+ gdouble lower;
+ gdouble page_size;
+ gdouble upper;
+- gdouble value_h = 0.0;
+- gdouble value_v = 0.0;
+ gdouble ratio;
+ gdouble width;
+ gdouble height;
+ gdouble x;
+ gdouble y;
+- gint window_width;
+- gint window_height;
+ gint viewport_width;
+ gint viewport_height;
+ gint offset_x = 0;
+ gint offset_y = 0;
+
+- window_width = gdk_window_get_width (child_window);
+- window_height = gdk_window_get_height (child_window);
+-
+- gtk_widget_get_allocation (child, &allocation);
+-
+- viewport_width = MIN (window_width, allocation.width);
+- viewport_height = MIN (window_height, allocation.height);
+-
+- if (scrolled_window->vscrollbar)
+- {
+- adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
+-
+- value_v = gtk_adjustment_get_value (adj);
+- }
+-
+- if (scrolled_window->hscrollbar)
+- {
+- adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
+-
+- value_h = gtk_adjustment_get_value (adj);
+- }
+-
+- if (window_width > allocation.width)
+- offset_x = value_h;
+-
+- if (window_height > allocation.height)
+- offset_y = value_v;
++ viewport_width = priv->viewport_allocation.width;
++ viewport_height = priv->viewport_allocation.height;
+
+ if ((vbar_rect || vslider_rect) && scrolled_window->vscrollbar)
+ {
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
+
+ g_object_get (adj,
++ "value", &value,
+ "lower", &lower,
+ "upper", &upper,
+ "page-size", &page_size,
+ NULL);
+
++ /* take overshooting into account */
++ if (priv->unclamped_vadj_value + page_size > upper)
++ page_size = upper - priv->unclamped_vadj_value;
++ else if (priv->unclamped_vadj_value < 0.0)
++ page_size += priv->unclamped_vadj_value;
++
+ ratio = page_size / (upper - lower);
+ if (ratio < 1.0)
+ {
+@@ -3280,7 +3413,7 @@ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ height = MAX (height, 20);
+ height = MIN (height, viewport_height - (2 * priv->sb_padding));
+
+- ratio = (value_v - lower) / (upper - page_size - lower);
++ ratio = (value - lower) / (upper - page_size - lower);
+ y = ratio * (viewport_height - (2 * priv->sb_padding) - height) + priv->sb_padding;
+ x = viewport_width - priv->sb_width - priv->sb_padding;
+
+@@ -3328,11 +3461,18 @@ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
+
+ g_object_get (adj,
++ "value", &value,
+ "lower", &lower,
+ "upper", &upper,
+ "page-size", &page_size,
+ NULL);
+
++ /* take overshooting into account */
++ if (priv->unclamped_hadj_value + page_size > upper)
++ page_size = upper - priv->unclamped_hadj_value;
++ else if (priv->unclamped_hadj_value < 0.0)
++ page_size += priv->unclamped_hadj_value;
++
+ ratio = page_size / (upper - lower);
+ if (ratio < 1.0)
+ {
+@@ -3340,7 +3480,7 @@ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ width = MAX (width, 20);
+ width = MIN (width, viewport_width - (2 * priv->sb_padding));
+
+- ratio = (value_h - lower) / (upper - page_size - lower);
++ ratio = (value - lower) / (upper - page_size - lower);
+ x = ratio * (viewport_width - (2 * priv->sb_padding) - width) + priv->sb_padding;
+ y = viewport_height - priv->sb_width - priv->sb_padding;
+
+@@ -3384,69 +3524,6 @@ gtk_scrolled_window_get_child_scroll_areas (GtkScrolledWindow *scrolled_window,
+ }
+ }
+
+-static gboolean
+-gtk_scrolled_window_child_expose (GtkWidget *widget,
+- GdkEventExpose *eevent,
+- GtkScrolledWindow *scrolled_window)
+-{
+- GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+- GdkRectangle vbar_rect;
+- GdkRectangle vslider_rect;
+- GdkRectangle hbar_rect;
+- GdkRectangle hslider_rect;
+- cairo_t *cr;
+-
+- if (!priv->overlay_scrollbars)
+- return FALSE;
+-
+- cr = gdk_cairo_create (eevent->window);
+- gdk_cairo_region (cr, eevent->region);
+- cairo_clip (cr);
+-
+- gtk_scrolled_window_get_child_scroll_areas (scrolled_window,
+- gtk_bin_get_child (GTK_BIN (scrolled_window)),
+- eevent->window,
+- &vbar_rect, &vslider_rect,
+- &hbar_rect, &hslider_rect);
+-
+- if (priv->sb_visible)
+- {
+- if (scrolled_window->vscrollbar && vbar_rect.width > 0)
+- gdk_cairo_rectangle (cr, &vbar_rect);
+-
+- if (scrolled_window->hscrollbar && hbar_rect.width > 0)
+- gdk_cairo_rectangle (cr, &hbar_rect);
+-
+- cairo_set_source_rgba (cr, 0, 0, 0, gtk_adjustment_get_value (priv->opacity) / 2.0);
+- cairo_fill (cr);
+- }
+-
+- if (scrolled_window->vscrollbar && vslider_rect.width > 0)
+- gtk_scrolled_window_rounded_rectangle (cr,
+- vslider_rect.x,
+- vslider_rect.y,
+- vslider_rect.width,
+- vslider_rect.height,
+- priv->sb_radius,
+- priv->sb_radius);
+-
+- if (scrolled_window->hscrollbar && hslider_rect.width > 0)
+- gtk_scrolled_window_rounded_rectangle (cr,
+- hslider_rect.x,
+- hslider_rect.y,
+- hslider_rect.width,
+- hslider_rect.height,
+- priv->sb_radius,
+- priv->sb_radius);
+-
+- cairo_set_source_rgba (cr, 0, 0, 0, gtk_adjustment_get_value (priv->opacity));
+- cairo_fill (cr);
+-
+- cairo_destroy (cr);
+-
+- return FALSE;
+-}
+-
+ static void
+ gtk_scrolled_window_cancel_animation (GtkScrolledWindow *scrolled_window)
+ {
+@@ -3541,39 +3618,6 @@ gtk_scrolled_window_start_fade_out_animation (GtkScrolledWindow *scrolled_window
+ }
+
+ static void
+-gtk_scrolled_window_expose_scrollbars (GtkAdjustment *adj,
+- GtkScrolledWindow *scrolled_window)
+-{
+- GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+-
+- if (priv->overlay_scrollbars)
+- {
+- GtkWidget *child = gtk_bin_get_child (GTK_BIN (scrolled_window));
+-
+- if (child && gtk_widget_get_visible (child))
+- {
+- GtkAllocation alloc;
+-
+- gtk_widget_get_allocation (child, &alloc);
+-
+- if (scrolled_window->vscrollbar)
+- gtk_widget_queue_draw_area (child,
+- alloc.width - 20,
+- 0,
+- 20,
+- alloc.height);
+-
+- if (scrolled_window->hscrollbar)
+- gtk_widget_queue_draw_area (child,
+- 0,
+- alloc.height - 20,
+- alloc.width,
+- 20);
+- }
+- }
+-}
+-
+-static void
+ gtk_scrolled_window_init_overlay_scrollbars (GtkScrolledWindow *scrolled_window)
+ {
+ _gtk_widget_set_captured_event_handler (GTK_WIDGET (scrolled_window),
+@@ -3592,8 +3636,43 @@ gtk_scrolled_window_overlay_scrollbars_changed (GtkSettings *settings,
+ &priv->overlay_scrollbars,
+ NULL);
+
++ if (priv->overlay_scrollbars)
++ gtk_scrolled_window_map_layers (GTK_SCROLLED_WINDOW (user_data));
++ else
++ gtk_scrolled_window_unmap_layers (GTK_SCROLLED_WINDOW (user_data));
++
+ gtk_widget_queue_resize (user_data);
+ }
+
++static void
++gtk_scrolled_window_map_layers (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ priv->vbar_layer.hidden = NO;
++ priv->vslider_layer.hidden = NO;
++
++ priv->hbar_layer.hidden = NO;
++ priv->hslider_layer.hidden = NO;
++}
++
++static void
++gtk_scrolled_window_unmap_layers (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ priv->vbar_layer.hidden = YES;
++ [priv->vbar_layer setNeedsDisplay];
++
++ priv->vslider_layer.hidden = YES;
++ [priv->vslider_layer setNeedsDisplay];
++
++ priv->hbar_layer.hidden = YES;
++ [priv->hbar_layer setNeedsDisplay];
++
++ priv->hslider_layer.hidden = YES;
++ [priv->hslider_layer setNeedsDisplay];
++}
++
+ #define __GTK_SCROLLED_WINDOW_C__
+ #include "gtkaliasdef.c"
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From d35ff9d66c8d76d509819dabc7b97972b76d7438 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Mon, 8 Oct 2012 11:48:38 +0200
+Subject: [PATCH 27/68] Refrain from starting fading out while a gesture is in
+ progress
+
+---
+ gtk/gtkscrolledwindow.c | 20 ++++++++++++++++----
+ 1 file changed, 16 insertions(+), 4 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 1fba87b..a04436b 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -153,6 +153,7 @@ typedef struct {
+
+ gboolean overlay_scrollbars;
+ gboolean is_snapping_back;
++ gboolean gesture_in_progress;
+ } GtkScrolledWindowPrivate;
+
+ #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate))
+@@ -2151,7 +2152,10 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ * FIXME: check if overshoot has really ended.
+ */
+ if (event->phase == GDK_EVENT_SCROLL_PHASE_START)
+- priv->is_snapping_back = FALSE;
++ {
++ priv->is_snapping_back = FALSE;
++ priv->gesture_in_progress = TRUE;
++ }
+
+ if (is_momentum_event && !is_overshot)
+ gtk_scrolled_window_calculate_velocity (scrolled_window, (GdkEvent *)event);
+@@ -2291,13 +2295,20 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ /* Reset force if gesture has ended. */
+ if (event->phase == GDK_EVENT_SCROLL_PHASE_END)
+ {
++ priv->gesture_in_progress = FALSE;
++
+ priv->x_force = 0.0;
+ priv->y_force = 0.0;
+ }
+
+- /* Stop fade out timeout while we're overshot */
+- if (new_overshoot_x != 0 || new_overshoot_y != 0)
++ /* Stop fade out timeout while we're overshot or while
++ * a gesture is in progress.
++ */
++ if (new_overshoot_x != 0 || new_overshoot_y != 0 ||
++ priv->gesture_in_progress)
+ gtk_scrolled_window_stop_fade_out_timeout (scrolled_window);
++ else
++ gtk_scrolled_window_start_fade_out_timeout (scrolled_window);
+
+ /* If we should start a snap back and no current deceleration
+ * is active, start the snap back.
+@@ -3598,7 +3609,8 @@ gtk_scrolled_window_start_fade_in_animation (GtkScrolledWindow *scrolled_window)
+ g_object_add_weak_pointer (G_OBJECT (priv->opacity_anim),
+ (gpointer *) &priv->opacity_anim);
+
+- gtk_scrolled_window_start_fade_out_timeout (scrolled_window);
++ if (!priv->gesture_in_progress)
++ gtk_scrolled_window_start_fade_out_timeout (scrolled_window);
+ }
+
+ static void
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 133655d890a4513783e4cb13e25770a84d5c41d8 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 2 Nov 2012 16:02:32 +0100
+Subject: [PATCH 28/68] gtk: don't show the olverlay scrollbars if only the
+ size of the content changes
+
+This commit also optimizes away many signal connections and thus
+double and triple updates.
+---
+ gtk/gtkscrolledwindow.c | 57 ++++++++++++++++++++---------------------------
+ 1 file changed, 24 insertions(+), 33 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index a04436b..7f56793 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -626,9 +626,6 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+ gtk_scrolled_window_adjustment_value_changed,
+ scrolled_window);
+- g_signal_handlers_disconnect_by_func (old_adjustment,
+- gtk_scrolled_window_update_scrollbars,
+- scrolled_window);
+
+ gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
+ hadjustment);
+@@ -645,13 +642,6 @@ gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
+
+- g_signal_connect_swapped (hadjustment, "changed",
+- G_CALLBACK (gtk_scrolled_window_update_scrollbars),
+- scrolled_window);
+- g_signal_connect_swapped (hadjustment, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_update_scrollbars),
+- scrolled_window);
+-
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+@@ -708,9 +698,6 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ g_signal_handlers_disconnect_by_func (old_adjustment,
+ gtk_scrolled_window_adjustment_value_changed,
+ scrolled_window);
+- g_signal_handlers_disconnect_by_func (old_adjustment,
+- gtk_scrolled_window_update_scrollbars,
+- scrolled_window);
+
+ gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
+ vadjustment);
+@@ -727,13 +714,6 @@ gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
+ gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
+ gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
+
+- g_signal_connect_swapped (vadjustment, "changed",
+- G_CALLBACK (gtk_scrolled_window_update_scrollbars),
+- scrolled_window);
+- g_signal_connect_swapped (vadjustment, "value-changed",
+- G_CALLBACK (gtk_scrolled_window_update_scrollbars),
+- scrolled_window);
+-
+ if (bin->child)
+ gtk_widget_set_scroll_adjustments (bin->child,
+ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+@@ -1071,9 +1051,6 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+ gtk_scrolled_window_adjustment_value_changed,
+ scrolled_window);
+- g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+- gtk_scrolled_window_update_scrollbars,
+- scrolled_window);
+
+ gtk_widget_unparent (scrolled_window->hscrollbar);
+ gtk_widget_destroy (scrolled_window->hscrollbar);
+@@ -1088,9 +1065,6 @@ gtk_scrolled_window_destroy (GtkObject *object)
+ g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
+ gtk_scrolled_window_adjustment_value_changed,
+ scrolled_window);
+- g_signal_handlers_disconnect_by_func (gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)),
+- gtk_scrolled_window_update_scrollbars,
+- scrolled_window);
+
+ gtk_widget_unparent (scrolled_window->vscrollbar);
+ gtk_widget_destroy (scrolled_window->vscrollbar);
+@@ -1805,7 +1779,8 @@ _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
+ }
+
+ static void
+-_gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_window)
++_gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_window,
++ gboolean update_scrollbars)
+ {
+ GtkAllocation window_allocation, relative_allocation, allocation;
+ GtkScrolledWindowPrivate *priv;
+@@ -1880,7 +1855,7 @@ _gtk_scrolled_window_allocate_overshoot_window (GtkScrolledWindow *scrolled_wind
+ else
+ gdk_window_hide (priv->hbackground_window);
+
+- if (priv->overlay_scrollbars)
++ if (priv->overlay_scrollbars && update_scrollbars)
+ {
+ gtk_scrolled_window_start_fade_in_animation (scrolled_window);
+ gtk_scrolled_window_update_scrollbars (scrolled_window);
+@@ -1948,12 +1923,15 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ GtkBin *bin;
+ GtkAllocation relative_allocation;
+ GtkAllocation child_allocation;
++ GtkAllocation old_allocation;
+ gboolean scrollbars_within_bevel;
+ gint scrollbar_spacing;
+
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
+ g_return_if_fail (allocation != NULL);
+
++ old_allocation = widget->allocation;
++
+ scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ bin = GTK_BIN (scrolled_window);
+@@ -2117,7 +2095,12 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ else if (gtk_widget_get_visible (scrolled_window->vscrollbar))
+ gtk_widget_hide (scrolled_window->vscrollbar);
+
+- _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
++ /* need to update the overlay scrollbars only if the allocation has
++ * actually changed, not if only the content changed
++ */
++ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window,
++ allocation->x != old_allocation.x ||
++ allocation->y != old_allocation.y);
+ }
+
+ static gboolean
+@@ -2280,7 +2263,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+
+ if (old_overshoot_x != new_overshoot_x ||
+ old_overshoot_y != new_overshoot_y)
+- _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
++ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window, TRUE);
+
+ /* In two cases we want to start snapping back:
+ * 1) The view is overshot and the gesture has ended.
+@@ -2480,7 +2463,7 @@ scrolled_window_snap_back_cb (gpointer user_data)
+
+ if (old_overshoot_x != overshoot_x ||
+ old_overshoot_y != overshoot_y)
+- _gtk_scrolled_window_allocate_overshoot_window (scrolled_window);
++ _gtk_scrolled_window_allocate_overshoot_window (scrolled_window, TRUE);
+
+ if (overshoot_x != 0 || overshoot_y != 0)
+ return TRUE;
+@@ -2960,7 +2943,12 @@ gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
+ }
+
+ if (priv->overlay_scrollbars)
+- gtk_scrolled_window_start_fade_in_animation (scrolled_win);
++ {
++ /* dont't fade in if the extent of the content changes, but update
++ * the scrollbar's dimensions anyway.
++ */
++ gtk_scrolled_window_update_scrollbars (scrolled_win);
++ }
+ }
+
+ static void
+@@ -2983,7 +2971,10 @@ gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
+ priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
+
+ if (priv->overlay_scrollbars)
+- gtk_scrolled_window_start_fade_in_animation (scrolled_window);
++ {
++ gtk_scrolled_window_start_fade_in_animation (scrolled_window);
++ gtk_scrolled_window_update_scrollbars (scrolled_window);
++ }
+ }
+
+ static void
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 5d691b32e0792cce518643f2e650909101c1095d Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 8 Nov 2012 15:54:13 +0100
+Subject: [PATCH 29/68] Reclamp unclamped adjustments after resize
+
+---
+ gtk/gtkscrolledwindow.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 7f56793..09f19d9 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -1926,6 +1926,7 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ GtkAllocation old_allocation;
+ gboolean scrollbars_within_bevel;
+ gint scrollbar_spacing;
++ gint overshoot_x, overshoot_y;
+
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
+ g_return_if_fail (allocation != NULL);
+@@ -1936,6 +1937,10 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+ bin = GTK_BIN (scrolled_window);
+
++ /* Save overshoot state from before resizing the child. */
++ _gtk_scrolled_window_get_overshoot (scrolled_window,
++ &overshoot_x, &overshoot_y);
++
+ scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+ gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
+
+@@ -2095,6 +2100,29 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
+ else if (gtk_widget_get_visible (scrolled_window->vscrollbar))
+ gtk_widget_hide (scrolled_window->vscrollbar);
+
++ /* We have to reclamp the unclamped adjustments, otherwise the content
++ * widget might be stuck in overshot state after resizing.
++ */
++ if (overshoot_x == 0.0)
++ {
++ GtkAdjustment *hadj;
++ hadj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
++ priv->unclamped_hadj_value = CLAMP (priv->unclamped_hadj_value,
++ gtk_adjustment_get_lower (hadj),
++ gtk_adjustment_get_upper (hadj) -
++ gtk_adjustment_get_page_size (hadj));
++ }
++
++ if (overshoot_y == 0.0)
++ {
++ GtkAdjustment *vadj;
++ vadj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
++ priv->unclamped_vadj_value = CLAMP (priv->unclamped_vadj_value,
++ gtk_adjustment_get_lower (vadj),
++ gtk_adjustment_get_upper (vadj) -
++ gtk_adjustment_get_page_size (vadj));
++ }
++
+ /* need to update the overlay scrollbars only if the allocation has
+ * actually changed, not if only the content changed
+ */
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 438b85d3fe83286a5ecf9c83049e5db045097507 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 22 Nov 2012 19:49:20 +0100
+Subject: [PATCH 30/68] gtk: fix size_request() of scrolled window
+
+---
+ gtk/gtkscrolledwindow.c | 41 ++++++++++++++++-------------------------
+ 1 file changed, 16 insertions(+), 25 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 09f19d9..77bb5af 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -1579,14 +1579,12 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+ GtkRequisition hscrollbar_requisition;
+ GtkRequisition vscrollbar_requisition;
+ GtkRequisition child_requisition;
+- GtkScrolledWindowPrivate *priv;
+
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
+ g_return_if_fail (requisition != NULL);
+
+ scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ bin = GTK_BIN (scrolled_window);
+- priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
+ scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+
+@@ -1606,7 +1604,7 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+
+ if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
+ requisition->width += child_requisition.width;
+- else if (! priv->overlay_scrollbars)
++ else
+ {
+ GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
+
+@@ -1617,13 +1615,11 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+ }
+ else
+ requisition->width += vscrollbar_requisition.width;
+- }
+- else
+- requisition->width += priv->sb_width + 2 * priv->sb_padding;
++ }
+
+ if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
+ requisition->height += child_requisition.height;
+- else if (! priv->overlay_scrollbars)
++ else
+ {
+ GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (bin->child, FALSE);
+
+@@ -1634,28 +1630,23 @@ gtk_scrolled_window_size_request (GtkWidget *widget,
+ }
+ else
+ requisition->height += hscrollbar_requisition.height;
+- }
+- else
+- requisition->height += priv->sb_width + 2 * priv->sb_padding;
++ }
+ }
+
+- if (! priv->overlay_scrollbars)
++ if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
++ scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
+ {
+- if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
+- scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
+- {
+- requisition->width = MAX (requisition->width, hscrollbar_requisition.width);
+- if (!extra_height || scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
+- extra_height = scrollbar_spacing + hscrollbar_requisition.height;
+- }
++ requisition->width = MAX (requisition->width, hscrollbar_requisition.width);
++ if (!extra_height || scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
++ extra_height = scrollbar_spacing + hscrollbar_requisition.height;
++ }
+
+- if (scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
+- scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
+- {
+- requisition->height = MAX (requisition->height, vscrollbar_requisition.height);
+- if (!extra_height || scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
+- extra_width = scrollbar_spacing + vscrollbar_requisition.width;
+- }
++ if (scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
++ scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
++ {
++ requisition->height = MAX (requisition->height, vscrollbar_requisition.height);
++ if (!extra_height || scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
++ extra_width = scrollbar_spacing + vscrollbar_requisition.width;
+ }
+
+ requisition->width += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_width);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From fd8e0fe0f9c630a56d415604c3c80dce5cd24648 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Tue, 15 Jan 2013 14:56:29 +0100
+Subject: [PATCH 31/68] Hackish fix for bug 8493 - Min size of
+ GtkScrolledWindow is too small
+
+In gtk_scrollbar_size_allocate(), pretend the scrollbar
+always has scroll arrows so the parent impl makes it a bit
+larger.
+---
+ gtk/gtkscrollbar.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/gtk/gtkscrollbar.c b/gtk/gtkscrollbar.c
+index 634b337..bab062d 100644
+--- a/gtk/gtkscrollbar.c
++++ b/gtk/gtkscrollbar.c
+@@ -32,6 +32,8 @@
+ #include "gtkprivate.h"
+ #include "gtkalias.h"
+
++static void gtk_scrollbar_size_request (GtkWidget *widget,
++ GtkRequisition *requisition);
+ static void gtk_scrollbar_style_set (GtkWidget *widget,
+ GtkStyle *previous);
+
+@@ -42,6 +44,7 @@ gtk_scrollbar_class_init (GtkScrollbarClass *class)
+ {
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
++ widget_class->size_request = gtk_scrollbar_size_request;
+ widget_class->style_set = gtk_scrollbar_style_set;
+
+ GTK_RANGE_CLASS (class)->stepper_detail = "Xscrollbar";
+@@ -97,6 +100,31 @@ gtk_scrollbar_init (GtkScrollbar *scrollbar)
+ }
+
+ static void
++gtk_scrollbar_size_request (GtkWidget *widget,
++ GtkRequisition *requisition)
++{
++ GtkRange *range = GTK_RANGE (widget);
++ gboolean saved_a, saved_b, saved_c, saved_d;
++
++ saved_a = range->has_stepper_a;
++ saved_b = range->has_stepper_b;
++ saved_c = range->has_stepper_c;
++ saved_d = range->has_stepper_d;
++
++ range->has_stepper_a = TRUE;
++ range->has_stepper_b = FALSE;
++ range->has_stepper_c = FALSE;
++ range->has_stepper_d = TRUE;
++
++ GTK_WIDGET_CLASS (gtk_scrollbar_parent_class)->size_request (widget, requisition);
++
++ range->has_stepper_a = saved_a;
++ range->has_stepper_b = saved_b;
++ range->has_stepper_c = saved_c;
++ range->has_stepper_d = saved_d;
++}
++
++static void
+ gtk_scrollbar_style_set (GtkWidget *widget,
+ GtkStyle *previous)
+ {
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 7e06599edaf97a91943e992f412116e6513c0c7e Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Sat, 23 Feb 2013 00:52:43 +0100
+Subject: [PATCH 32/68] Add momentum_phase to GdkEventScroll
+
+We need this for the detection of legacy mice that do emit precise
+deltas.
+---
+ gdk/gdkevents.h | 1 +
+ gdk/gdkwindow.c | 1 +
+ gdk/quartz/gdkevents-quartz.c | 16 ++++++++++++----
+ 3 files changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
+index 765b520..1cbfc58 100644
+--- a/gdk/gdkevents.h
++++ b/gdk/gdkevents.h
+@@ -349,6 +349,7 @@ struct _GdkEventScroll
+ gdouble delta_x;
+ gdouble delta_y;
+ GdkEventScrollPhase phase;
++ GdkEventScrollPhase momentum_phase;
+ };
+
+ struct _GdkEventKey
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index 1dac543..7a3732c 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -10898,6 +10898,7 @@ proxy_button_event (GdkEvent *source_event,
+ event->scroll.delta_x = source_event->scroll.delta_x;
+ event->scroll.delta_y = source_event->scroll.delta_y;
+ event->scroll.phase = source_event->scroll.phase;
++ event->scroll.momentum_phase = source_event->scroll.momentum_phase;
+ return TRUE;
+
+ default:
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index bb4da70..f3ba2c8 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -63,6 +63,7 @@ static GdkWindow *find_toplevel_under_pointer (GdkDisplay *display,
+ - (CGFloat) scrollingDeltaX;
+ - (CGFloat) scrollingDeltaY;
+ - (int) phase;
++- (int) momentumPhase;
+ @end
+
+
+@@ -992,6 +993,7 @@ fill_scroll_event (GdkWindow *window,
+ gdouble delta_x,
+ gdouble delta_y,
+ GdkEventScrollPhase phase,
++ GdkEventScrollPhase momentum_phase,
+ GdkScrollDirection direction)
+ {
+ GdkWindowObject *private;
+@@ -1015,6 +1017,7 @@ fill_scroll_event (GdkWindow *window,
+ event->scroll.delta_x = delta_x;
+ event->scroll.delta_y = delta_y;
+ event->scroll.phase = phase;
++ event->scroll.momentum_phase = momentum_phase;
+ }
+
+ static void
+@@ -1516,7 +1519,7 @@ gdk_event_translate (GdkEvent *event,
+ if (gdk_quartz_osx_version() >= GDK_OSX_LION &&
+ [(id <PreciseDeltas>) nsevent hasPreciseScrollingDeltas])
+ {
+- GdkEventScrollPhase phase;
++ GdkEventScrollPhase phase, momentum_phase;
+ dx = [(id <PreciseDeltas>) nsevent scrollingDeltaX];
+ dy = [(id <PreciseDeltas>) nsevent scrollingDeltaY];
+
+@@ -1536,9 +1539,10 @@ gdk_event_translate (GdkEvent *event,
+ }
+
+ phase = gdk_event_scroll_phase_from_ns_event_phase ([(id <PreciseDeltas>) nsevent phase]);
++ momentum_phase = gdk_event_scroll_phase_from_ns_event_phase ([(id <PreciseDeltas>) nsevent momentumPhase]);
+
+ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+- TRUE, -dx, -dy, phase, direction);
++ TRUE, -dx, -dy, phase, momentum_phase, direction);
+ }
+ else
+ {
+@@ -1553,7 +1557,9 @@ gdk_event_translate (GdkEvent *event,
+ direction = GDK_SCROLL_UP;
+
+ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+- FALSE, 0.0, fabs (dy), GDK_EVENT_SCROLL_PHASE_NONE,
++ FALSE, 0.0, fabs (dy),
++ GDK_EVENT_SCROLL_PHASE_NONE,
++ GDK_EVENT_SCROLL_PHASE_NONE,
+ direction);
+ }
+ else if (dx != 0.0)
+@@ -1564,7 +1570,9 @@ gdk_event_translate (GdkEvent *event,
+ direction = GDK_SCROLL_LEFT;
+
+ fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+- FALSE, fabs (dx), 0.0, GDK_EVENT_SCROLL_PHASE_NONE,
++ FALSE, fabs (dx), 0.0,
++ GDK_EVENT_SCROLL_PHASE_NONE,
++ GDK_EVENT_SCROLL_PHASE_NONE,
+ direction);
+ }
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 4eab270368617383e2e287c434f71e037e686047 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Sun, 17 Feb 2013 13:06:59 +0100
+Subject: [PATCH 33/68] Never intervene in the event stream for legacy mice
+
+This is necessary for devices (e.g. Mighty Mouse) which do emit
+precise deltas but no phase.
+---
+ gdk/gdkwindow.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index 7a3732c..2db0c0b 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -10849,12 +10849,20 @@ proxy_button_event (GdkEvent *source_event,
+ */
+ if (type == GDK_SCROLL && source_event->scroll.has_deltas)
+ {
++ gboolean legacy_mouse =
++ source_event->scroll.phase == GDK_EVENT_SCROLL_PHASE_NONE &&
++ source_event->scroll.momentum_phase == GDK_EVENT_SCROLL_PHASE_NONE;
++
+ if (source_event->scroll.phase == GDK_EVENT_SCROLL_PHASE_START)
+ {
+ set_last_scroll_event_windows (display, pointer_window, event_win);
+ }
+- else
++ else if (!legacy_mouse)
+ {
++ /* Never override pointer and event windows for legacy devices
++ * which are not capable of momentum scrolling. (The windows
++ * will be NULL, because they have never been set).
++ */
+ pointer_window = g_object_get_qdata (G_OBJECT (display),
+ quark_last_scroll_pointer_window);
+ event_win = g_object_get_qdata (G_OBJECT (display),
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 06fe34a85d02df941da86e34cd1d88ef9da52a18 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Sun, 17 Feb 2013 13:08:14 +0100
+Subject: [PATCH 34/68] Do not start overshooting for legacy mouse scroll
+ events
+
+Doing overshooting properly relies on the detection of gestures.
+For legacy devices that do emit precise deltas but no (gesture) phase,
+simply disallow overshooting so that the state machine does not get
+stuck.
+---
+ gtk/gtkscrolledwindow.c | 23 ++++++++++++++++-------
+ 1 file changed, 16 insertions(+), 7 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 77bb5af..4159af8 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -2141,7 +2141,12 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ gint old_overshoot_x, old_overshoot_y;
+ gboolean start_snap_back = FALSE;
+ gboolean is_overshot = FALSE;
+- gboolean is_momentum_event = event->phase == GDK_EVENT_SCROLL_PHASE_NONE;
++ gboolean is_momentum_event = event->momentum_phase != GDK_EVENT_SCROLL_PHASE_NONE;
++ gboolean legacy_mouse = FALSE;
++
++ if (event->phase == GDK_EVENT_SCROLL_PHASE_NONE &&
++ event->momentum_phase == GDK_EVENT_SCROLL_PHASE_NONE)
++ legacy_mouse = TRUE;
+
+ _gtk_scrolled_window_get_overshoot (scrolled_window,
+ &old_overshoot_x, &old_overshoot_y);
+@@ -2150,10 +2155,12 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ if (old_overshoot_x != 0 || old_overshoot_y != 0)
+ is_overshot = TRUE;
+
+- /* If a new gesture has started, reset snap back state.
++ /* If a new gesture has started or we detect the end of a momentum
++ * phase, reset snap back state.
+ * FIXME: check if overshoot has really ended.
+ */
+- if (event->phase == GDK_EVENT_SCROLL_PHASE_START)
++ if (event->momentum_phase == GDK_EVENT_SCROLL_PHASE_END ||
++ event->phase == GDK_EVENT_SCROLL_PHASE_START)
+ {
+ priv->is_snapping_back = FALSE;
+ priv->gesture_in_progress = TRUE;
+@@ -2193,8 +2200,9 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ */
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
+ gdouble max_adj = gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj);
+- if (gtk_adjustment_get_value (adj) < 1.0 ||
+- gtk_adjustment_get_value (adj) > max_adj - 1.0)
++ if (!legacy_mouse &&
++ (gtk_adjustment_get_value (adj) < 1.0 ||
++ gtk_adjustment_get_value (adj) > max_adj - 1.0))
+ may_overshoot = TRUE;
+
+ if (scrolled_window->hscrollbar_visible && (is_overshot || may_overshoot))
+@@ -2240,8 +2248,9 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ */
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
+ gdouble max_adj = gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj);
+- if (gtk_adjustment_get_value (adj) < 1.0 ||
+- gtk_adjustment_get_value (adj) > max_adj - 1.0)
++ if (!legacy_mouse &&
++ (gtk_adjustment_get_value (adj) < 1.0 ||
++ gtk_adjustment_get_value (adj) > max_adj - 1.0))
+ may_overshoot = TRUE;
+
+ if (scrolled_window->vscrollbar_visible && (is_overshot || may_overshoot))
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 388e70cee0897fb743531ec001c1e6f796e2c27f Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 1 Mar 2013 15:06:20 +0100
+Subject: [PATCH 35/68] gtk: remove the overlay scrollbar grab on unrealize()
+
+---
+ gtk/gtkscrolledwindow.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 4159af8..7200462 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -3256,6 +3256,12 @@ gtk_scrolled_window_unrealize (GtkWidget *widget)
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
+
++ if (priv->sb_pointer_grabbed)
++ {
++ gtk_grab_remove (widget);
++ priv->sb_pointer_grabbed = FALSE;
++ }
++
+ gdk_window_set_user_data (priv->overshoot_window, NULL);
+ gdk_window_destroy (priv->overshoot_window);
+ priv->overshoot_window = NULL;
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 2b8a48086aee90fe60753e5b6f0c415036469f48 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 5 Apr 2013 15:15:08 +0200
+Subject: [PATCH 36/68] gtk: add GtkScrolledWindow API to disable overshoot
+ per widget
+
+And remove some forgotten declarations in the header.
+---
+ gtk/gtkscrolledwindow.c | 33 +++++++++++++++++++++++++++++++--
+ gtk/gtkscrolledwindow.h | 10 +++-------
+ 2 files changed, 34 insertions(+), 9 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 7200462..9def3aa 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -154,6 +154,7 @@ typedef struct {
+ gboolean overlay_scrollbars;
+ gboolean is_snapping_back;
+ gboolean gesture_in_progress;
++ gboolean enable_overshoot;
+ } GtkScrolledWindowPrivate;
+
+ #define GTK_SCROLLED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCROLLED_WINDOW, GtkScrolledWindowPrivate))
+@@ -541,6 +542,8 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ priv->sb_width = 6;
+ priv->sb_fade_out_delay = 1000;
+
++ priv->enable_overshoot = TRUE;
++
+ g_signal_connect_swapped (priv->opacity, "value-changed",
+ G_CALLBACK (gtk_scrolled_window_update_scrollbars),
+ scrolled_window);
+@@ -2144,8 +2147,9 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
+ gboolean is_momentum_event = event->momentum_phase != GDK_EVENT_SCROLL_PHASE_NONE;
+ gboolean legacy_mouse = FALSE;
+
+- if (event->phase == GDK_EVENT_SCROLL_PHASE_NONE &&
+- event->momentum_phase == GDK_EVENT_SCROLL_PHASE_NONE)
++ if (!priv->enable_overshoot ||
++ (event->phase == GDK_EVENT_SCROLL_PHASE_NONE &&
++ event->momentum_phase == GDK_EVENT_SCROLL_PHASE_NONE))
+ legacy_mouse = TRUE;
+
+ _gtk_scrolled_window_get_overshoot (scrolled_window,
+@@ -3106,6 +3110,31 @@ gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
+ gtk_container_add (GTK_CONTAINER (viewport), child);
+ }
+
++void
++gtk_scrolled_window_set_enable_overshoot (GtkScrolledWindow *scrolled_window,
++ gboolean enable_overshoot)
++{
++ GtkScrolledWindowPrivate *priv;
++
++ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ priv->enable_overshoot = enable_overshoot ? TRUE : FALSE;
++}
++
++gboolean
++gtk_scrolled_window_get_enable_overshoot (GtkScrolledWindow *scrolled_window)
++{
++ GtkScrolledWindowPrivate *priv;
++
++ g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ return priv->enable_overshoot;
++}
++
+ /*
+ * _gtk_scrolled_window_get_spacing:
+ * @scrolled_window: a scrolled window
+diff --git a/gtk/gtkscrolledwindow.h b/gtk/gtkscrolledwindow.h
+index 1f555e0..f75596d 100644
+--- a/gtk/gtkscrolledwindow.h
++++ b/gtk/gtkscrolledwindow.h
+@@ -127,13 +127,9 @@ GtkShadowType gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolle
+ void gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
+ GtkWidget *child);
+
+-void gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
+- gboolean kinetic_scrolling);
+-gboolean gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window);
+-
+-void gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
+- gboolean capture_button_press);
+-gboolean gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window);
++void gtk_scrolled_window_set_enable_overshoot (GtkScrolledWindow *scrolled_window,
++ gboolean enable_overshoot);
++gboolean gtk_scrolled_window_get_enable_overshoot (GtkScrolledWindow *scrolled_window);
+
+ gint _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window);
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 6437422a8153939d06563d02fc938b90f36204d0 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 23 Mar 2012 12:22:31 +0100
+Subject: [PATCH 37/68] quartz: return events on embedded foreign NSViews back
+ to Cocoa
+
+---
+ gdk/gdkmarshalers.list | 1 +
+ gdk/gdkwindow.c | 14 ++++++++++++++
+ gdk/quartz/gdkevents-quartz.c | 20 ++++++++++++++++++++
+ 3 files changed, 35 insertions(+)
+
+diff --git a/gdk/gdkmarshalers.list b/gdk/gdkmarshalers.list
+index ea36bae..7b37163 100644
+--- a/gdk/gdkmarshalers.list
++++ b/gdk/gdkmarshalers.list
+@@ -4,3 +4,4 @@ VOID:POINTER,POINTER,POINTER
+ OBJECT:VOID
+ OBJECT:DOUBLE,DOUBLE
+ VOID:DOUBLE,DOUBLE,POINTER,POINTER
++VOID:POINTER,POINTER
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index 2db0c0b..a6b582c 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -126,6 +126,7 @@ enum {
+ PICK_EMBEDDED_CHILD, /* only called if children are embedded */
+ TO_EMBEDDER,
+ FROM_EMBEDDER,
++ NATIVE_CHILD_EVENT,
+ LAST_SIGNAL
+ };
+
+@@ -595,6 +596,19 @@ gdk_window_class_init (GdkWindowObjectClass *klass)
+ G_TYPE_DOUBLE,
+ G_TYPE_POINTER,
+ G_TYPE_POINTER);
++
++ signals[NATIVE_CHILD_EVENT] =
++ g_signal_new (g_intern_static_string ("native-child-event"),
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ 0,
++ NULL, NULL,
++ _gdk_marshal_VOID__POINTER_POINTER,
++ G_TYPE_NONE,
++ 2,
++ G_TYPE_POINTER,
++ G_TYPE_POINTER);
++
+ }
+
+ static void
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index f3ba2c8..f6742f0 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -841,12 +841,32 @@ find_window_for_ns_event (NSEvent *nsevent,
+ {
+ GdkWindowObject *toplevel_private;
+ GdkWindowImplQuartz *toplevel_impl;
++ guint n_subviews;
++ guint i;
+
+ toplevel = toplevel_under_pointer;
+
+ toplevel_private = (GdkWindowObject *)toplevel;
+ toplevel_impl = (GdkWindowImplQuartz *)toplevel_private->impl;
+
++ n_subviews = [[toplevel_impl->view subviews] count];
++
++ for (i = 0; i < n_subviews; ++i)
++ {
++ NSView* sv = [[toplevel_impl->view subviews] objectAtIndex:i];
++ NSRect r = [sv frame];
++
++ if (r.origin.x <= *x && r.origin.x + r.size.width >= *x &&
++ r.origin.y <= *y && r.origin.y + r.size.height >= *y)
++ {
++ g_signal_emit_by_name (toplevel, "native-child-event",
++ sv, nsevent);
++
++ /* event is within subview, forward back to Cocoa */
++ return NULL;
++ }
++ }
++
+ *x = x_tmp;
+ *y = y_tmp;
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 08000a6f3a45742baa6649271f41408047dcf61c Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 28 Feb 2013 11:08:13 +0100
+Subject: [PATCH 38/68] quartz: don't forward events to the toplevel
+ nswindow's layer_view
+
+---
+ gdk/quartz/gdkevents-quartz.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index f6742f0..6271085 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -856,6 +856,9 @@ find_window_for_ns_event (NSEvent *nsevent,
+ NSView* sv = [[toplevel_impl->view subviews] objectAtIndex:i];
+ NSRect r = [sv frame];
+
++ if (sv == toplevel_impl->layer_view)
++ continue;
++
+ if (r.origin.x <= *x && r.origin.x + r.size.width >= *x &&
+ r.origin.y <= *y && r.origin.y + r.size.height >= *y)
+ {
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From a790bc42e0fdaf73fbc7561f7497e5b1fbdc1f81 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 22 Feb 2013 11:05:17 +0100
+Subject: [PATCH 39/68] gdk: add a "move-native-children" signal to GdkWindow
+
+and emit it from move_native_children(), so GtkNSView has
+a chance to do the right thing on scrolling.
+---
+ gdk/gdkwindow.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index a6b582c..3040321 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -127,6 +127,7 @@ enum {
+ TO_EMBEDDER,
+ FROM_EMBEDDER,
+ NATIVE_CHILD_EVENT,
++ MOVE_NATIVE_CHILDREN,
+ LAST_SIGNAL
+ };
+
+@@ -609,6 +610,15 @@ gdk_window_class_init (GdkWindowObjectClass *klass)
+ G_TYPE_POINTER,
+ G_TYPE_POINTER);
+
++ signals[MOVE_NATIVE_CHILDREN] =
++ g_signal_new (g_intern_static_string ("move-native-children"),
++ G_OBJECT_CLASS_TYPE (object_class),
++ G_SIGNAL_RUN_LAST,
++ 0,
++ NULL, NULL,
++ g_cclosure_marshal_VOID__VOID,
++ G_TYPE_NONE,
++ 0);
+ }
+
+ static void
+@@ -7474,6 +7484,8 @@ move_native_children (GdkWindowObject *private)
+ else
+ move_native_children (child);
+ }
++
++ g_signal_emit_by_name (private, "move-native-children");
+ }
+
+ static gboolean
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 99be0cbaccfc53b374055085f778a971a5758566 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@lanedo.com>
+Date: Tue, 29 Nov 2011 16:09:31 +0100
+Subject: [PATCH 40/68] gtk: add new widget GtkNSView which alows to embed an
+ NSView
+
+---
+ gtk/Makefile.am | 2 +
+ gtk/gtk.h | 4 +
+ gtk/gtknsview.c | 470 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ gtk/gtknsview.h | 62 +++++++
+ tests/Makefile.am | 10 +-
+ tests/testnsview.c | 190 +++++++++++++++++++++
+ 6 files changed, 737 insertions(+), 1 deletion(-)
+ create mode 100644 gtk/gtknsview.c
+ create mode 100644 gtk/gtknsview.h
+ create mode 100644 tests/testnsview.c
+
+diff --git a/gtk/Makefile.am b/gtk/Makefile.am
+index 31dfd19..59a6742 100644
+--- a/gtk/Makefile.am
++++ b/gtk/Makefile.am
+@@ -737,6 +737,7 @@ gtk_use_win32_c_sources = \
+ gtkwin32embedwidget.c \
+ gtkmountoperation-stub.c
+ gtk_use_quartz_c_sources = \
++ gtknsview.c \
+ gtksearchenginequartz.c \
+ gtkplug-stub.c \
+ gtksocket-stub.c \
+@@ -757,6 +758,7 @@ else
+ if USE_QUARTZ
+ libgtk_quartz_2_0_la_CFLAGS = "-xobjective-c"
+ gtk_private_h_sources += gtksearchenginequartz.h
++gtk_public_h_sources += gtknsview.h
+ gtk_c_sources += $(gtk_use_quartz_c_sources)
+ else
+ gtk_c_sources += $(gtk_use_stub_c_sources)
+diff --git a/gtk/gtk.h b/gtk/gtk.h
+index 94e0b61..cc5834a 100644
+--- a/gtk/gtk.h
++++ b/gtk/gtk.h
+@@ -218,6 +218,10 @@
+ #include <gtk/gtkwidget.h>
+ #include <gtk/gtkwindow.h>
+
++#ifdef GDK_WINDOWING_QUARTZ
++#include <gtk/gtknsview.h>
++#endif
++
+ /* Broken */
+ #include <gtk/gtktext.h>
+ #include <gtk/gtktree.h>
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+new file mode 100644
+index 0000000..40c6a9b
+--- /dev/null
++++ b/gtk/gtknsview.c
+@@ -0,0 +1,470 @@
++/* GTK - The GIMP Toolkit
++ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
++ *
++ * GtkNSView - Native NSView embedding widget
++ * Copyright (C) 2011 Michael Natterer <mitch@lanedo.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 02111-1307, USA.
++ */
++
++#include "config.h"
++
++#include <gdk/gdk.h>
++#include <gdk/gdkkeysyms.h>
++#include <gdk/quartz/gdkquartz.h>
++#include <objc/runtime.h>
++
++#include "gtknsview.h"
++#include "gtkprivate.h"
++#include "gtkintl.h"
++#include "gtkalias.h"
++
++
++/* #define DEBUG_FOCUS 1 */
++
++
++enum
++{
++ PROP_0,
++ PROP_VIEW
++};
++
++
++struct _GtkNSViewPrivate
++{
++ NSView *view;
++};
++
++#define GTK_NS_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
++ GTK_TYPE_NS_VIEW, GtkNSViewPrivate))
++
++
++static void gtk_ns_view_finalize (GObject *object);
++static void gtk_ns_view_set_property (GObject *object,
++ guint prop_id,
++ const GValue *value,
++ GParamSpec *pspec);
++static void gtk_ns_view_get_property (GObject *object,
++ guint prop_id,
++ GValue *value,
++ GParamSpec *pspec);
++static void gtk_ns_view_notify (GObject *object,
++ GParamSpec *pspec);
++
++static void gtk_ns_view_unrealize (GtkWidget *widget);
++static void gtk_ns_view_map (GtkWidget *widget);
++static void gtk_ns_view_unmap (GtkWidget *widget);
++static void gtk_ns_view_size_request (GtkWidget *widget,
++ GtkRequisition *requisition);
++static void gtk_ns_view_size_allocate (GtkWidget *widget,
++ GtkAllocation *allocation);
++static void gtk_ns_view_grab_focus (GtkWidget *widget);
++static gboolean gtk_ns_view_key_press (GtkWidget *widget,
++ GdkEventKey *event);
++static gboolean gtk_ns_view_key_release (GtkWidget *widget,
++ GdkEventKey *event);
++
++static void gtk_ns_view_native_child_event (GdkWindow *window,
++ NSView *view,
++ NSEvent *event,
++ GtkNSView *ns_view);
++static gboolean gtk_ns_view_forward_event (GtkWidget *widget,
++ GdkEventKey *event);
++
++
++G_DEFINE_TYPE (GtkNSView, gtk_ns_view, GTK_TYPE_WIDGET)
++
++
++static void
++gtk_ns_view_class_init (GtkNSViewClass *klass)
++{
++ GObjectClass *object_class = G_OBJECT_CLASS (klass);
++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
++
++ g_type_class_add_private (klass, sizeof (GtkNSViewPrivate));
++
++ object_class->finalize = gtk_ns_view_finalize;
++ object_class->set_property = gtk_ns_view_set_property;
++ object_class->get_property = gtk_ns_view_get_property;
++ object_class->notify = gtk_ns_view_notify;
++
++ widget_class->unrealize = gtk_ns_view_unrealize;
++ widget_class->map = gtk_ns_view_map;
++ widget_class->unmap = gtk_ns_view_unmap;
++ widget_class->size_request = gtk_ns_view_size_request;
++ widget_class->size_allocate = gtk_ns_view_size_allocate;
++ widget_class->grab_focus = gtk_ns_view_grab_focus;
++ widget_class->key_press_event = gtk_ns_view_key_press;
++ widget_class->key_release_event = gtk_ns_view_key_release;
++
++ /**
++ * GtkNSView:view:
++ *
++ * The widget's NSView.
++ *
++ * Since: 2.24
++ */
++ g_object_class_install_property (object_class,
++ PROP_VIEW,
++ g_param_spec_pointer ("view",
++ P_("View"),
++ P_("The NSView"),
++ GTK_PARAM_READWRITE |
++ G_PARAM_CONSTRUCT_ONLY));
++}
++
++static void
++gtk_ns_view_init (GtkNSView *ns_view)
++{
++ ns_view->priv = GTK_NS_VIEW_GET_PRIVATE (ns_view);
++
++ gtk_widget_set_has_window (GTK_WIDGET (ns_view), FALSE);
++}
++
++static void
++gtk_ns_view_finalize (GObject *object)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (object);
++
++ if (ns_view->priv->view)
++ {
++ [ns_view->priv->view release];
++ ns_view->priv->view = NULL;
++ }
++
++ G_OBJECT_CLASS (gtk_ns_view_parent_class)->finalize (object);
++}
++
++static void
++gtk_ns_view_set_property (GObject *object,
++ guint prop_id,
++ const GValue *value,
++ GParamSpec *pspec)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (object);
++
++ switch (prop_id)
++ {
++ case PROP_VIEW:
++ ns_view->priv->view = g_value_get_pointer (value);
++ if (ns_view->priv->view)
++ {
++ [ns_view->priv->view retain];
++ gtk_widget_set_can_focus (GTK_WIDGET (ns_view),
++ [ns_view->priv->view acceptsFirstResponder]);
++
++#if DEBUG_FOCUS
++ g_printerr ("%s can focus: %d\n",
++ class_getName ([ns_view->priv->view class]),
++ gtk_widget_get_can_focus (GTK_WIDGET (ns_view)));
++#endif
++ }
++ break;
++
++ default:
++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++ break;
++ }
++}
++
++static void
++gtk_ns_view_get_property (GObject *object,
++ guint prop_id,
++ GValue *value,
++ GParamSpec *pspec)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (object);
++
++ switch (prop_id)
++ {
++ case PROP_VIEW:
++ g_value_set_pointer (value, ns_view->priv->view);
++ break;
++
++ default:
++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++ break;
++ }
++}
++
++static void
++gtk_ns_view_notify (GObject *object,
++ GParamSpec *pspec)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (object);
++
++ if (G_OBJECT_CLASS (gtk_ns_view_parent_class)->notify)
++ G_OBJECT_CLASS (gtk_ns_view_parent_class)->notify (object, pspec);
++
++ if (!strcmp (pspec->name, "has-focus"))
++ {
++ NSWindow *ns_window = [ns_view->priv->view window];
++
++#if DEBUG_FOCUS
++ g_printerr ("%s has-focus: %d\n",
++ class_getName ([ns_view->priv->view class]),
++ gtk_widget_has_focus (GTK_WIDGET (object)));
++#endif
++
++ if (gtk_widget_has_focus (GTK_WIDGET (object)))
++ [ns_window makeFirstResponder:ns_view->priv->view];
++ else
++ [ns_window makeFirstResponder:nil];
++ }
++}
++
++static void
++gtk_ns_view_position_view (GtkNSView *ns_view,
++ GtkAllocation *allocation)
++{
++ GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (ns_view));
++ GdkWindow *native;
++ gdouble x, y;
++ NSSize size;
++ NSPoint origin;
++
++ x = allocation->x;
++ y = allocation->y;
++
++ /* convert to the coordinate system of the innermost parent window
++ * that has an NSView
++ */
++ native = window;
++ while (! gdk_window_has_native (native))
++ {
++ gdk_window_coords_to_parent (native, x, y, &x, &y);
++ native = gdk_window_get_parent (native);
++ }
++
++ size.width = allocation->width;
++ size.height = allocation->height;
++ [ns_view->priv->view setFrameSize:size];
++
++ origin.x = x;
++ origin.y = y;
++ [ns_view->priv->view setFrameOrigin:origin];
++}
++
++static void
++gtk_ns_view_unrealize (GtkWidget *widget)
++{
++ if (gtk_widget_get_mapped (widget))
++ gtk_widget_unmap (widget);
++
++ GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->unrealize (widget);
++}
++
++static void
++gtk_ns_view_map (GtkWidget *widget)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
++ GtkAllocation allocation;
++ NSView *parent_view;
++
++ gtk_widget_get_allocation (widget, &allocation);
++ gtk_ns_view_position_view (ns_view, &allocation);
++
++ parent_view = gdk_quartz_window_get_nsview (gtk_widget_get_window (widget));
++ [parent_view addSubview:ns_view->priv->view];
++
++ [ns_view->priv->view setNextKeyView:nil];
++
++ g_signal_connect_object (gtk_widget_get_window (toplevel), "native-child-event",
++ G_CALLBACK (gtk_ns_view_native_child_event),
++ G_OBJECT (widget), 0);
++
++ GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->map (widget);
++}
++
++static void
++gtk_ns_view_unmap (GtkWidget *widget)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
++
++ g_signal_handlers_disconnect_by_func (gtk_widget_get_window (toplevel),
++ gtk_ns_view_native_child_event,
++ widget);
++
++ [ns_view->priv->view removeFromSuperview];
++
++ GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->unmap (widget);
++}
++
++static void
++gtk_ns_view_size_request (GtkWidget *widget,
++ GtkRequisition *requisition)
++{
++ requisition->width = 1;
++ requisition->height = 1;
++}
++
++static void
++gtk_ns_view_size_allocate (GtkWidget *widget,
++ GtkAllocation *allocation)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++
++ widget->allocation = *allocation;
++
++ if (gtk_widget_get_mapped (widget))
++ gtk_ns_view_position_view (ns_view, allocation);
++}
++
++static void
++gtk_ns_view_grab_focus (GtkWidget *widget)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++ NSWindow *ns_window;
++
++ GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->grab_focus (widget);
++
++ ns_window = [ns_view->priv->view window];
++ [ns_window makeFirstResponder:ns_view->priv->view];
++}
++
++static gboolean
++gtk_ns_view_key_press (GtkWidget *widget,
++ GdkEventKey *event)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++ NSEvent *nsevent = gdk_quartz_event_get_nsevent ((GdkEvent *) event);
++ NSWindow *ns_window;
++
++ if (gtk_ns_view_forward_event (widget, event))
++ {
++ ns_window = [ns_view->priv->view window];
++ [ns_window sendEvent:nsevent];
++
++ return TRUE;
++ }
++
++ return GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->key_press_event (widget, event);
++}
++
++static gboolean
++gtk_ns_view_key_release (GtkWidget *widget,
++ GdkEventKey *event)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++ NSEvent *nsevent = gdk_quartz_event_get_nsevent ((GdkEvent *) event);
++ NSWindow *ns_window;
++
++ if (gtk_ns_view_forward_event (widget, event))
++ {
++ ns_window = [ns_view->priv->view window];
++ [ns_window sendEvent:nsevent];
++
++ return TRUE;
++ }
++
++ return GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->key_release_event (widget, event);
++}
++
++static void
++gtk_ns_view_native_child_event (GdkWindow *window,
++ NSView *view,
++ NSEvent *event,
++ GtkNSView *ns_view)
++{
++ if (view == ns_view->priv->view)
++ {
++#if 0
++ g_printerr ("native child event on %s\n",
++ class_getName ([ns_view->priv->view class]));
++#endif
++
++ switch ([event type])
++ {
++ case NSLeftMouseDown:
++ if (! gtk_widget_has_focus (GTK_WIDGET (ns_view)) &&
++
++ /* other code can set can-focus, so check for both */
++ gtk_widget_get_can_focus (GTK_WIDGET (ns_view)) &&
++ [ns_view->priv->view acceptsFirstResponder])
++ {
++#if DEBUG_FOCUS
++ g_printerr ("grabbing focus on %s\n",
++ class_getName ([ns_view->priv->view class]));
++#endif
++
++ gtk_widget_grab_focus (GTK_WIDGET (ns_view));
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++}
++
++static gboolean
++gtk_ns_view_forward_event (GtkWidget *widget,
++ GdkEventKey *event)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++ NSWindow *ns_window;
++ NSResponder *first_responder;
++ NSView *next_key_view;
++
++ if (event->type != GDK_KEY_PRESS ||
++ (event->keyval != GDK_KEY_Tab &&
++ event->keyval != GDK_KEY_ISO_Left_Tab))
++ {
++ return TRUE;
++ }
++
++ ns_window = [ns_view->priv->view window];
++ first_responder = [ns_window firstResponder];
++
++#if DEBUG_FOCUS
++ g_printerr ("first reponder: %p %s\n", first_responder,
++ class_getName ([first_responder class]));
++#endif
++
++ if (event->keyval == GDK_KEY_Tab)
++ next_key_view = [first_responder nextValidKeyView];
++ else
++ next_key_view = [first_responder previousValidKeyView];
++
++#if DEBUG_FOCUS
++ g_printerr ("next key view: %p %s\n", next_key_view,
++ class_getName ([next_key_view class]));
++#endif
++
++ if (next_key_view &&
++ next_key_view != ns_view->priv->view &&
++ [next_key_view isDescendantOf:ns_view->priv->view])
++ {
++ return TRUE;
++ }
++
++ return FALSE;
++}
++
++GtkWidget *
++gtk_ns_view_new (gpointer nsview)
++{
++ g_return_val_if_fail (nsview != NULL, NULL);
++
++ return g_object_new (GTK_TYPE_NS_VIEW,
++ "view", nsview,
++ NULL);
++}
++
++#define __GTK_NS_VIEW_C__
++#include "gtkaliasdef.c"
+diff --git a/gtk/gtknsview.h b/gtk/gtknsview.h
+new file mode 100644
+index 0000000..2c0aab7
+--- /dev/null
++++ b/gtk/gtknsview.h
+@@ -0,0 +1,62 @@
++/* GTK - The GIMP Toolkit
++ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
++ *
++ * GtkNSView - Native NSView embedding widget
++ * Copyright (C) 2011 Michael Natterer <mitch@lanedo.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 02111-1307, USA.
++ */
++
++#ifndef __GTK_NS_VIEW_H__
++#define __GTK_NS_VIEW_H__
++
++#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
++#error "Only <gtk/gtk.h> can be included directly."
++#endif
++
++#include <gtk/gtkwidget.h>
++
++G_BEGIN_DECLS
++
++#define GTK_TYPE_NS_VIEW (gtk_ns_view_get_type ())
++#define GTK_NS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_NS_VIEW, GtkNSView))
++#define GTK_NS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_NS_VIEW, GtkNSViewClass))
++#define GTK_IS_NS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_NS_VIEW))
++#define GTK_IS_NS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_NS_VIEW))
++#define GTK_NS_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_NS_VIEW, GtkNSViewClass))
++
++typedef struct _GtkNSView GtkNSView;
++typedef struct _GtkNSViewClass GtkNSViewClass;
++typedef struct _GtkNSViewPrivate GtkNSViewPrivate;
++
++struct _GtkNSView
++{
++ GtkWidget parent_instance;
++
++ GtkNSViewPrivate *priv;
++};
++
++struct _GtkNSViewClass
++{
++ GtkWidgetClass parent_class;
++};
++
++GType gtk_ns_view_get_type (void) G_GNUC_CONST;
++GtkWidget * gtk_ns_view_new (gpointer nsview);
++
++G_END_DECLS
++
++#endif /* __GTK_NS_VIEW_H__ */
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 3888826..af098f6 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -8,7 +8,8 @@ INCLUDES = \
+ -DGDK_DISABLE_DEPRECATED \
+ -DGTK_DISABLE_DEPRECATED \
+ $(GTK_DEBUG_FLAGS) \
+- $(GTK_DEP_CFLAGS)
++ $(GTK_DEP_CFLAGS) \
++ -xobjective-c
+
+ DEPS = \
+ $(top_builddir)/gdk/$(gdktargetlib) \
+@@ -57,6 +58,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
+ testmultiscreen \
+ testnotebookdnd \
+ testnouiprint \
++ testnsview \
+ testoffscreen \
+ testoffscreenwindow \
+ testorientable \
+@@ -134,6 +136,7 @@ testmultidisplay_DEPENDENCIES = $(TEST_DEPS)
+ testmultiscreen_DEPENDENCIES = $(TEST_DEPS)
+ testnotebookdnd_DEPENDENCIES = $(TEST_DEPS)
+ testnouiprint_DEPENDENCIES = $(TEST_DEPS)
++testnsview_DEPENDENCIES = $(TEST_DEPS)
+ testoffscreen_DEPENDENCIES = $(TEST_DEPS)
+ testoffscreenwindow_DEPENDENCIES = $(TEST_DEPS)
+ testorientable_DEPENDENCIES = $(TEST_DEPS)
+@@ -166,6 +169,8 @@ testtooltips_DEPENDENCIES = $(TEST_DEPS)
+ testvolumebutton_DEPENDENCIES = $(TEST_DEPS)
+ testwindows_DEPENDENCIES = $(TEST_DEPS)
+
++testnsview_LDFLAGS = -framework WebKit
++
+ testentrycompletion_SOURCES = \
+ prop-editor.c \
+ testentrycompletion.c
+@@ -262,6 +267,9 @@ testrecentchoosermenu_SOURCES = \
+ testvolumebutton_SOURCES = \
+ testvolumebutton.c
+
++testnsview_SOURCES = \
++ testnsview.c
++
+ testoffscreen_SOURCES = \
+ gtkoffscreenbox.c \
+ gtkoffscreenbox.h \
+diff --git a/tests/testnsview.c b/tests/testnsview.c
+new file mode 100644
+index 0000000..7c9ccb7
+--- /dev/null
++++ b/tests/testnsview.c
+@@ -0,0 +1,190 @@
++/* testnsview.c
++ * Copyright (C) 2011 Michael Natterer <mitch@lanedo.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 02111-1307, USA.
++ */
++
++#include "config.h"
++
++#include <WebKit/WebKit.h>
++#include <gtk/gtk.h>
++
++
++static void
++back_clicked (GtkToolItem *item,
++ WebView *webview)
++{
++ [webview goBack];
++}
++
++static void
++forward_clicked (GtkToolItem *item,
++ WebView *webview)
++{
++ [webview goForward];
++}
++
++gint
++main (gint argc,
++ gchar *argv[])
++{
++ GtkWidget *window;
++ GtkWidget *vbox;
++ GtkWidget *toolbar;
++ GtkToolItem *item;
++ WebView *webview;
++ NSRect web_rect = { { 0.0, 0.0 }, { 100.0, 100.0 } };
++ NSURL *url;
++ NSURLRequest *request;
++ GtkWidget *ns_view;
++
++ gtk_init (&argc, &argv);
++
++ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
++ gtk_window_set_title (GTK_WINDOW (window), "GtkNSView featuring WebView");
++
++ g_signal_connect (window, "destroy",
++ G_CALLBACK (gtk_main_quit),
++ NULL);
++
++ vbox = gtk_vbox_new (FALSE, 0);
++ gtk_container_add (GTK_CONTAINER (window), vbox);
++ gtk_widget_show (vbox);
++
++ toolbar = gtk_toolbar_new ();
++ gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
++ gtk_widget_show (toolbar);
++
++ webview = [WebView alloc];
++
++ item = gtk_tool_button_new_from_stock (GTK_STOCK_GO_BACK);
++ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
++ gtk_widget_show (GTK_WIDGET (item));
++
++ g_signal_connect (item, "clicked",
++ G_CALLBACK (back_clicked),
++ webview);
++
++ item = gtk_tool_button_new_from_stock (GTK_STOCK_GO_FORWARD);
++ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
++ gtk_widget_show (GTK_WIDGET (item));
++
++ g_signal_connect (item, "clicked",
++ G_CALLBACK (forward_clicked),
++ webview);
++
++ [webview initWithFrame:web_rect
++ frameName:@"foo"
++ groupName:@"bar"];
++
++ url = [NSURL URLWithString:@"http://www.gimp.org/"];
++ request = [NSURLRequest requestWithURL:url];
++
++ [[webview mainFrame] loadRequest:request];
++
++ ns_view = gtk_ns_view_new ((NSView *) webview);
++ gtk_widget_set_size_request (ns_view, 300, 200);
++ gtk_box_pack_end (GTK_BOX (vbox), ns_view, TRUE, TRUE, 0);
++ gtk_widget_show (ns_view);
++
++ [webview release];
++
++ {
++ GtkWidget *button;
++
++ button = gtk_button_new_with_label ("hide webview");
++ gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0);
++ gtk_widget_show (button);
++
++ g_signal_connect_swapped (button, "clicked",
++ G_CALLBACK (gtk_widget_hide),
++ ns_view);
++
++ button = gtk_button_new_with_label ("show webview");
++ gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0);
++ gtk_widget_show (button);
++
++ g_signal_connect_swapped (button, "clicked",
++ G_CALLBACK (gtk_widget_show),
++ ns_view);
++ }
++
++ /* add an entry in an event box to test living inside another gdkwindow */
++ {
++ GtkWidget *event_box;
++ GtkWidget *abox;
++ GtkWidget *hbox;
++ NSRect label_rect = { { 0.0, 0.0 }, { 100.0, 12.0 } };
++ NSRect text_rect = { { 0.0, 0.0 }, { 100.0, 12.0 } };
++ NSTextField *text_field;
++
++ event_box = gtk_event_box_new ();
++ gtk_widget_set_state (event_box, GTK_STATE_ACTIVE);
++ gtk_box_pack_start (GTK_BOX (vbox), event_box, FALSE, FALSE, 0);
++ gtk_widget_show (event_box);
++
++ abox = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
++ gtk_container_set_border_width (GTK_CONTAINER (abox), 10);
++ gtk_container_add (GTK_CONTAINER (event_box), abox);
++ gtk_widget_show (abox);
++
++ hbox = gtk_hbox_new (FALSE, 10);
++ gtk_container_add (GTK_CONTAINER (abox), hbox);
++ gtk_widget_show (hbox);
++
++ /* a non-editable text label */
++ text_field = [[NSTextField alloc] initWithFrame:label_rect];
++ [text_field setEditable:NO];
++ [text_field setDrawsBackground:NO];
++ [text_field setBordered:NO];
++ [text_field setStringValue:@"A Text Label"];
++
++ ns_view = gtk_ns_view_new ((NSView *) text_field);
++ gtk_widget_set_size_request (ns_view, 100, 20);
++ gtk_box_pack_start (GTK_BOX (hbox), ns_view, FALSE, FALSE, 0);
++ gtk_widget_show (ns_view);
++
++ [text_field release];
++
++ /* an editable text field */
++ text_field = [[NSTextField alloc] initWithFrame:text_rect];
++ [text_field setEditable:YES];
++ [text_field setStringValue:@"An editable text entry"];
++
++ ns_view = gtk_ns_view_new ((NSView *) text_field);
++ gtk_widget_set_size_request (ns_view, 100, 20);
++ gtk_box_pack_start (GTK_BOX (hbox), ns_view, TRUE, TRUE, 0);
++ gtk_widget_show (ns_view);
++
++ [text_field release];
++ }
++
++ /* and a normal GtkEntry to check focus */
++ {
++ GtkWidget *entry;
++
++ entry = gtk_entry_new ();
++ gtk_entry_set_text (GTK_ENTRY (entry), "Normal GTK+ entry");
++ gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
++ gtk_widget_show (entry);
++ }
++
++ gtk_widget_show (window);
++
++ gtk_main ();
++
++ return 0;
++}
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 9e267bd60da4235a0facb6bab98fef5884bc0ff0 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Tue, 12 Feb 2013 13:22:39 +0100
+Subject: [PATCH 41/68] tests: add a notebook to testnsview.c
+
+---
+ tests/testnsview.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/tests/testnsview.c b/tests/testnsview.c
+index 7c9ccb7..45b1d60 100644
+--- a/tests/testnsview.c
++++ b/tests/testnsview.c
+@@ -45,6 +45,7 @@ main (gint argc,
+ GtkWidget *vbox;
+ GtkWidget *toolbar;
+ GtkToolItem *item;
++ GtkWidget *notebook;
+ WebView *webview;
+ NSRect web_rect = { { 0.0, 0.0 }, { 100.0, 100.0 } };
+ NSURL *url;
+@@ -86,6 +87,10 @@ main (gint argc,
+ G_CALLBACK (forward_clicked),
+ webview);
+
++ notebook = gtk_notebook_new ();
++ gtk_box_pack_end (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
++ gtk_widget_show (notebook);
++
+ [webview initWithFrame:web_rect
+ frameName:@"foo"
+ groupName:@"bar"];
+@@ -97,12 +102,24 @@ main (gint argc,
+
+ ns_view = gtk_ns_view_new ((NSView *) webview);
+ gtk_widget_set_size_request (ns_view, 300, 200);
++#if 0
+ gtk_box_pack_end (GTK_BOX (vbox), ns_view, TRUE, TRUE, 0);
++#else
++ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), ns_view,
++ gtk_label_new ("WebView"));
++#endif
+ gtk_widget_show (ns_view);
+
+ [webview release];
+
+ {
++ GtkWidget *useless = gtk_label_new ("Useless Label");
++ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), useless,
++ gtk_label_new ("Useless"));
++ gtk_widget_show (useless);
++ }
++
++ {
+ GtkWidget *button;
+
+ button = gtk_button_new_with_label ("hide webview");
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 87d285a9e186e7745d4f3a2b2e6a489b1b803b94 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 22 Feb 2013 11:06:26 +0100
+Subject: [PATCH 42/68] gtk: connect GtkNSView to "move-native-children" and
+ reposition
+
+---
+ gtk/gtknsview.c | 32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index 40c6a9b..a19a94e 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -77,12 +77,14 @@ static gboolean gtk_ns_view_key_press (GtkWidget *widget,
+ static gboolean gtk_ns_view_key_release (GtkWidget *widget,
+ GdkEventKey *event);
+
+-static void gtk_ns_view_native_child_event (GdkWindow *window,
+- NSView *view,
+- NSEvent *event,
+- GtkNSView *ns_view);
+-static gboolean gtk_ns_view_forward_event (GtkWidget *widget,
+- GdkEventKey *event);
++static void gtk_ns_view_native_child_event (GdkWindow *window,
++ NSView *view,
++ NSEvent *event,
++ GtkNSView *ns_view);
++static void gtk_ns_view_move_native_children (GdkWindow *window,
++ GtkNSView *ns_view);
++static gboolean gtk_ns_view_forward_event (GtkWidget *widget,
++ GdkEventKey *event);
+
+
+ G_DEFINE_TYPE (GtkNSView, gtk_ns_view, GTK_TYPE_WIDGET)
+@@ -287,6 +289,10 @@ gtk_ns_view_map (GtkWidget *widget)
+ G_CALLBACK (gtk_ns_view_native_child_event),
+ G_OBJECT (widget), 0);
+
++ g_signal_connect_object (gtk_widget_get_window (widget), "move-native-children",
++ G_CALLBACK (gtk_ns_view_move_native_children),
++ G_OBJECT (widget), 0);
++
+ GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->map (widget);
+ }
+
+@@ -296,6 +302,10 @@ gtk_ns_view_unmap (GtkWidget *widget)
+ GtkNSView *ns_view = GTK_NS_VIEW (widget);
+ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+
++ g_signal_handlers_disconnect_by_func (gtk_widget_get_window (widget),
++ gtk_ns_view_move_native_children,
++ widget);
++
+ g_signal_handlers_disconnect_by_func (gtk_widget_get_window (toplevel),
+ gtk_ns_view_native_child_event,
+ widget);
+@@ -412,6 +422,16 @@ gtk_ns_view_native_child_event (GdkWindow *window,
+ }
+ }
+
++static void
++gtk_ns_view_move_native_children (GdkWindow *window,
++ GtkNSView *ns_view)
++{
++ GtkAllocation allocation;
++
++ gtk_widget_get_allocation (GTK_WIDGET (ns_view), &allocation);
++ gtk_ns_view_position_view (ns_view, &allocation);
++}
++
+ static gboolean
+ gtk_ns_view_forward_event (GtkWidget *widget,
+ GdkEventKey *event)
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From aa3e5322000437f02e04916abf278dbbc407a403 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 22 Feb 2013 11:06:58 +0100
+Subject: [PATCH 43/68] tests: add a scrolled window test widget to
+ testnsview.c
+
+---
+ tests/testnsview.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 51 insertions(+)
+
+diff --git a/tests/testnsview.c b/tests/testnsview.c
+index 45b1d60..1a523e6 100644
+--- a/tests/testnsview.c
++++ b/tests/testnsview.c
+@@ -139,6 +139,57 @@ main (gint argc,
+ ns_view);
+ }
+
++ /* add an entry in a scrolled window to test scrolling / clipping */
++ {
++ GtkWidget *sw;
++ GtkWidget *abox;
++ GtkWidget *hbox;
++ NSRect label_rect = { { 0.0, 0.0 }, { 100.0, 12.0 } };
++ NSRect text_rect = { { 0.0, 0.0 }, { 100.0, 12.0 } };
++ NSTextField *text_field;
++
++ sw = gtk_scrolled_window_new (NULL, NULL);
++ gtk_widget_set_size_request (sw, -1, 100);
++ gtk_box_pack_start (GTK_BOX (vbox), sw, FALSE, FALSE, 0);
++ gtk_widget_show (sw);
++
++ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
++ gtk_widget_set_size_request (abox, -1, 200);
++ gtk_container_set_border_width (GTK_CONTAINER (abox), 10);
++ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), abox);
++ gtk_widget_show (abox);
++
++ hbox = gtk_hbox_new (FALSE, 10);
++ gtk_container_add (GTK_CONTAINER (abox), hbox);
++ gtk_widget_show (hbox);
++
++ /* a non-editable text label */
++ text_field = [[NSTextField alloc] initWithFrame:label_rect];
++ [text_field setEditable:NO];
++ [text_field setDrawsBackground:NO];
++ [text_field setBordered:NO];
++ [text_field setStringValue:@"A Text Label"];
++
++ ns_view = gtk_ns_view_new ((NSView *) text_field);
++ gtk_widget_set_size_request (ns_view, 100, 20);
++ gtk_box_pack_start (GTK_BOX (hbox), ns_view, FALSE, FALSE, 0);
++ gtk_widget_show (ns_view);
++
++ [text_field release];
++
++ /* an editable text field */
++ text_field = [[NSTextField alloc] initWithFrame:text_rect];
++ [text_field setEditable:YES];
++ [text_field setStringValue:@"An editable text entry"];
++
++ ns_view = gtk_ns_view_new ((NSView *) text_field);
++ gtk_widget_set_size_request (ns_view, 100, 20);
++ gtk_box_pack_start (GTK_BOX (hbox), ns_view, TRUE, TRUE, 0);
++ gtk_widget_show (ns_view);
++
++ [text_field release];
++ }
++
+ /* add an entry in an event box to test living inside another gdkwindow */
+ {
+ GtkWidget *event_box;
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 08f7cca62c0a810d1bd5eaf52813b9ecb776478e Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 1 Mar 2013 12:39:41 +0100
+Subject: [PATCH 44/68] gtknsview: clip drawRect to a parent GtkViewport's
+ window
+
+not quite perfect yet, but getting there...
+---
+ gtk/gtknsview.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 109 insertions(+), 12 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index a19a94e..84d758f 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -28,6 +28,7 @@
+ #include <objc/runtime.h>
+
+ #include "gtknsview.h"
++#include "gtkviewport.h"
+ #include "gtkprivate.h"
+ #include "gtkintl.h"
+ #include "gtkalias.h"
+@@ -52,6 +53,7 @@ struct _GtkNSViewPrivate
+ GTK_TYPE_NS_VIEW, GtkNSViewPrivate))
+
+
++static void gtk_ns_view_constructed (GObject *object);
+ static void gtk_ns_view_finalize (GObject *object);
+ static void gtk_ns_view_set_property (GObject *object,
+ guint prop_id,
+@@ -89,7 +91,6 @@ static gboolean gtk_ns_view_forward_event (GtkWidget *widget,
+
+ G_DEFINE_TYPE (GtkNSView, gtk_ns_view, GTK_TYPE_WIDGET)
+
+-
+ static void
+ gtk_ns_view_class_init (GtkNSViewClass *klass)
+ {
+@@ -98,6 +99,7 @@ gtk_ns_view_class_init (GtkNSViewClass *klass)
+
+ g_type_class_add_private (klass, sizeof (GtkNSViewPrivate));
+
++ object_class->constructed = gtk_ns_view_constructed;
+ object_class->finalize = gtk_ns_view_finalize;
+ object_class->set_property = gtk_ns_view_set_property;
+ object_class->get_property = gtk_ns_view_get_property;
+@@ -136,6 +138,111 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ gtk_widget_set_has_window (GTK_WIDGET (ns_view), FALSE);
+ }
+
++@implementation NSView (myDrawRect)
++- (void) myDrawRect: (NSRect) dirtyRect
++{
++ GtkNSView *ns_view;
++ GtkWidget *viewport;
++
++#if 0
++ g_printerr ("drawRect called\n");
++#endif
++
++ ns_view = (GtkNSView *) objc_getAssociatedObject (self, "gtknsview");
++
++ if (! ns_view)
++ {
++ [self myDrawRect: dirtyRect];
++ return;
++ }
++
++ viewport = gtk_widget_get_ancestor (GTK_WIDGET (ns_view), GTK_TYPE_VIEWPORT);
++
++ if (viewport)
++ {
++ CGContextRef cg_context = [[NSGraphicsContext currentContext] graphicsPort];
++ GtkAllocation viewport_allocation;
++ CGRect rect;
++
++#if 0
++ g_printerr ("drawRect called on gtknsview in gtkviewport\n");
++#endif
++
++ gtk_widget_get_allocation (viewport, &viewport_allocation);
++
++ if (gtk_viewport_get_shadow_type (GTK_VIEWPORT (viewport)) != GTK_SHADOW_NONE)
++ {
++ GtkStyle *style = gtk_widget_get_style (viewport);
++
++ viewport_allocation.x += style->xthickness;
++ viewport_allocation.y += style->ythickness;
++ viewport_allocation.width -= 2 * style->xthickness;
++ viewport_allocation.height -= 2 * style->ythickness;
++ }
++
++ gtk_widget_translate_coordinates (viewport, GTK_WIDGET (ns_view),
++ viewport_allocation.x,
++ viewport_allocation.y,
++ &viewport_allocation.x,
++ &viewport_allocation.y);
++
++ rect.origin.x = viewport_allocation.x;
++ rect.origin.y = viewport_allocation.y;
++ rect.size.width = viewport_allocation.width;
++ rect.size.height = viewport_allocation.height;
++
++ CGContextSaveGState (cg_context);
++ CGContextClipToRect (cg_context, rect);
++
++ [self myDrawRect: dirtyRect];
++
++ CGContextRestoreGState (cg_context);
++ }
++ else
++ {
++ [self myDrawRect: dirtyRect];
++ }
++}
++@end
++
++static void
++gtk_ns_view_constructed (GObject *object)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (object);
++ Method original_drawRect;
++ Method my_drawRect;
++
++ G_OBJECT_CLASS (gtk_ns_view_parent_class)->constructed (object);
++
++ gtk_widget_set_can_focus (GTK_WIDGET (ns_view),
++ [ns_view->priv->view acceptsFirstResponder]);
++
++#if DEBUG_FOCUS
++ g_printerr ("%s can focus: %d\n",
++ class_getName ([ns_view->priv->view class]),
++ gtk_widget_get_can_focus (GTK_WIDGET (ns_view)));
++#endif
++
++ original_drawRect = class_getInstanceMethod ([ns_view->priv->view class],
++ @selector (drawRect:));
++ my_drawRect = class_getInstanceMethod ([ns_view->priv->view class],
++ @selector (myDrawRect:));
++
++ if (class_addMethod ([ns_view->priv->view class],
++ @selector (myDrawRect:),
++ method_getImplementation (original_drawRect),
++ method_getTypeEncoding (original_drawRect)))
++ {
++ class_replaceMethod ([ns_view->priv->view class],
++ @selector (drawRect:),
++ method_getImplementation (my_drawRect),
++ method_getTypeEncoding (my_drawRect));
++ }
++
++ objc_setAssociatedObject (ns_view->priv->view, "gtknsview", (id) ns_view,
++ OBJC_ASSOCIATION_ASSIGN);
++}
++
+ static void
+ gtk_ns_view_finalize (GObject *object)
+ {
+@@ -163,17 +270,7 @@ gtk_ns_view_set_property (GObject *object,
+ case PROP_VIEW:
+ ns_view->priv->view = g_value_get_pointer (value);
+ if (ns_view->priv->view)
+- {
+- [ns_view->priv->view retain];
+- gtk_widget_set_can_focus (GTK_WIDGET (ns_view),
+- [ns_view->priv->view acceptsFirstResponder]);
+-
+-#if DEBUG_FOCUS
+- g_printerr ("%s can focus: %d\n",
+- class_getName ([ns_view->priv->view class]),
+- gtk_widget_get_can_focus (GTK_WIDGET (ns_view)));
+-#endif
+- }
++ [ns_view->priv->view retain];
+ break;
+
+ default:
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 24caf0db2af4d43a2d8bf1cd6dff7e54d5268502 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 14 Mar 2013 16:54:38 +0100
+Subject: [PATCH 45/68] gtk: clip NSViews to the scrolled window's
+ overshoot_window
+
+not to the viewport's allocation. This is ugly but by far the
+easiest way to get clipping right.
+---
+ gtk/gtknsview.c | 31 +++++++++++++++++++++++++++++++
+ tests/testnsview.c | 3 ++-
+ 2 files changed, 33 insertions(+), 1 deletion(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index 84d758f..db1da93 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -160,6 +160,7 @@ gtk_ns_view_init (GtkNSView *ns_view)
+
+ if (viewport)
+ {
++ GdkWindow *window;
+ CGContextRef cg_context = [[NSGraphicsContext currentContext] graphicsPort];
+ GtkAllocation viewport_allocation;
+ CGRect rect;
+@@ -170,6 +171,36 @@ gtk_ns_view_init (GtkNSView *ns_view)
+
+ gtk_widget_get_allocation (viewport, &viewport_allocation);
+
++#if 0
++ g_printerr ("viewport allocation: %d, %d (%d x %x)\n",
++ viewport_allocation.x,
++ viewport_allocation.y,
++ viewport_allocation.width,
++ viewport_allocation.height);
++#endif
++
++ /* evil: don't clip to the viewport's width/height but to that
++ * of its parent window, because we know we hacked an
++ * overshoot_window into GtkScrolledWindow and need to restrict
++ * rendering in its area
++ */
++ window = gtk_widget_get_parent_window (viewport);
++
++ viewport_allocation.width = gdk_window_get_width (window);
++ viewport_allocation.height = gdk_window_get_height (window);
++
++#if 0
++ {
++ gint x, y;
++
++ gdk_window_get_position (window, &x, &y);
++ g_printerr ("viewport parent window at %d, %d (%x x %x)\n",
++ x, y,
++ gdk_window_get_width (window),
++ gdk_window_get_height (window));
++ }
++#endif
++
+ if (gtk_viewport_get_shadow_type (GTK_VIEWPORT (viewport)) != GTK_SHADOW_NONE)
+ {
+ GtkStyle *style = gtk_widget_get_style (viewport);
+diff --git a/tests/testnsview.c b/tests/testnsview.c
+index 1a523e6..24d1d41 100644
+--- a/tests/testnsview.c
++++ b/tests/testnsview.c
+@@ -149,7 +149,8 @@ main (gint argc,
+ NSTextField *text_field;
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+- gtk_widget_set_size_request (sw, -1, 100);
++ gtk_widget_set_size_request (sw, -1, 130);
++ gtk_container_set_border_width (GTK_CONTAINER (sw), 20);
+ gtk_box_pack_start (GTK_BOX (vbox), sw, FALSE, FALSE, 0);
+ gtk_widget_show (sw);
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From ab0bb7104783da5155361e6fbd89ee14f25e2544 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 14 Mar 2013 20:10:51 +0100
+Subject: [PATCH 46/68] gtk: implement clipping to multiple parent viewports
+ in GtkNSView
+
+---
+ gtk/gtknsview.c | 51 +++++++++++----------------------------------------
+ 1 file changed, 11 insertions(+), 40 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index db1da93..1e0b7e6 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -143,10 +143,7 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ {
+ GtkNSView *ns_view;
+ GtkWidget *viewport;
+-
+-#if 0
+- g_printerr ("drawRect called\n");
+-#endif
++ CGContextRef cg_context;
+
+ ns_view = (GtkNSView *) objc_getAssociatedObject (self, "gtknsview");
+
+@@ -156,51 +153,30 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ return;
+ }
+
+- viewport = gtk_widget_get_ancestor (GTK_WIDGET (ns_view), GTK_TYPE_VIEWPORT);
++ cg_context = [[NSGraphicsContext currentContext] graphicsPort];
++ CGContextSaveGState (cg_context);
+
+- if (viewport)
++ for (viewport = gtk_widget_get_ancestor (GTK_WIDGET (ns_view), GTK_TYPE_VIEWPORT);
++ viewport;
++ viewport = gtk_widget_get_ancestor (gtk_widget_get_parent (viewport),
++ GTK_TYPE_VIEWPORT))
+ {
+ GdkWindow *window;
+- CGContextRef cg_context = [[NSGraphicsContext currentContext] graphicsPort];
+ GtkAllocation viewport_allocation;
+ CGRect rect;
+
+-#if 0
+- g_printerr ("drawRect called on gtknsview in gtkviewport\n");
+-#endif
+-
+ gtk_widget_get_allocation (viewport, &viewport_allocation);
+
+-#if 0
+- g_printerr ("viewport allocation: %d, %d (%d x %x)\n",
+- viewport_allocation.x,
+- viewport_allocation.y,
+- viewport_allocation.width,
+- viewport_allocation.height);
+-#endif
+-
+ /* evil: don't clip to the viewport's width/height but to that
+ * of its parent window, because we know we hacked an
+ * overshoot_window into GtkScrolledWindow and need to restrict
+- * rendering in its area
++ * rendering to its area
+ */
+ window = gtk_widget_get_parent_window (viewport);
+
+ viewport_allocation.width = gdk_window_get_width (window);
+ viewport_allocation.height = gdk_window_get_height (window);
+
+-#if 0
+- {
+- gint x, y;
+-
+- gdk_window_get_position (window, &x, &y);
+- g_printerr ("viewport parent window at %d, %d (%x x %x)\n",
+- x, y,
+- gdk_window_get_width (window),
+- gdk_window_get_height (window));
+- }
+-#endif
+-
+ if (gtk_viewport_get_shadow_type (GTK_VIEWPORT (viewport)) != GTK_SHADOW_NONE)
+ {
+ GtkStyle *style = gtk_widget_get_style (viewport);
+@@ -222,17 +198,12 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ rect.size.width = viewport_allocation.width;
+ rect.size.height = viewport_allocation.height;
+
+- CGContextSaveGState (cg_context);
+ CGContextClipToRect (cg_context, rect);
++ }
+
+- [self myDrawRect: dirtyRect];
++ [self myDrawRect: dirtyRect];
+
+- CGContextRestoreGState (cg_context);
+- }
+- else
+- {
+- [self myDrawRect: dirtyRect];
+- }
++ CGContextRestoreGState (cg_context);
+ }
+ @end
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 56e863ef4b425c1ac79e53d35f9b8b9649cec7d3 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 15 Mar 2013 14:49:59 +0100
+Subject: [PATCH 47/68] gtk: first attempt to also clip NSWindow's field
+ editor
+
+Also, factor out the method swizzling to a utility function.
+---
+ gtk/gtknsview.c | 75 ++++++++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 58 insertions(+), 17 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index 1e0b7e6..b58a2c8 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -29,6 +29,7 @@
+
+ #include "gtknsview.h"
+ #include "gtkviewport.h"
++#include "gtkwindow.h"
+ #include "gtkprivate.h"
+ #include "gtkintl.h"
+ #include "gtkalias.h"
+@@ -147,6 +148,22 @@ gtk_ns_view_init (GtkNSView *ns_view)
+
+ ns_view = (GtkNSView *) objc_getAssociatedObject (self, "gtknsview");
+
++ if (! ns_view && ([self class] == [NSTextView class]))
++ {
++ /* if it's not a GtkNSView, check if it's the NSWindow's cell
++ * editor editing an NSTextField managed by a GtkNSView
++ */
++ GtkWindow *window = (GtkWindow *) objc_getAssociatedObject (self, "gtkwindow");
++
++ if (GTK_IS_WINDOW (window))
++ {
++ GtkWidget *focus = gtk_window_get_focus (window);
++
++ if (GTK_IS_NS_VIEW (focus))
++ ns_view = GTK_NS_VIEW (focus);
++ }
++ }
++
+ if (! ns_view)
+ {
+ [self myDrawRect: dirtyRect];
+@@ -208,38 +225,45 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ @end
+
+ static void
+-gtk_ns_view_constructed (GObject *object)
++gtk_ns_view_swizzle_draw_rect (NSView *view)
+ {
+- GtkNSView *ns_view = GTK_NS_VIEW (object);
+ Method original_drawRect;
+ Method my_drawRect;
+
+- G_OBJECT_CLASS (gtk_ns_view_parent_class)->constructed (object);
+-
+- gtk_widget_set_can_focus (GTK_WIDGET (ns_view),
+- [ns_view->priv->view acceptsFirstResponder]);
+-
+-#if DEBUG_FOCUS
+- g_printerr ("%s can focus: %d\n",
+- class_getName ([ns_view->priv->view class]),
+- gtk_widget_get_can_focus (GTK_WIDGET (ns_view)));
+-#endif
+-
+- original_drawRect = class_getInstanceMethod ([ns_view->priv->view class],
++ original_drawRect = class_getInstanceMethod ([view class],
+ @selector (drawRect:));
+- my_drawRect = class_getInstanceMethod ([ns_view->priv->view class],
++ my_drawRect = class_getInstanceMethod ([view class],
+ @selector (myDrawRect:));
+
+- if (class_addMethod ([ns_view->priv->view class],
++ if (class_addMethod ([view class],
+ @selector (myDrawRect:),
+ method_getImplementation (original_drawRect),
+ method_getTypeEncoding (original_drawRect)))
+ {
+- class_replaceMethod ([ns_view->priv->view class],
++ class_replaceMethod ([view class],
+ @selector (drawRect:),
+ method_getImplementation (my_drawRect),
+ method_getTypeEncoding (my_drawRect));
+ }
++}
++
++static void
++gtk_ns_view_constructed (GObject *object)
++{
++ GtkNSView *ns_view = GTK_NS_VIEW (object);
++
++ G_OBJECT_CLASS (gtk_ns_view_parent_class)->constructed (object);
++
++ gtk_widget_set_can_focus (GTK_WIDGET (ns_view),
++ [ns_view->priv->view acceptsFirstResponder]);
++
++#if DEBUG_FOCUS
++ g_printerr ("%s can focus: %d\n",
++ class_getName ([ns_view->priv->view class]),
++ gtk_widget_get_can_focus (GTK_WIDGET (ns_view)));
++#endif
++
++ gtk_ns_view_swizzle_draw_rect (ns_view->priv->view);
+
+ objc_setAssociatedObject (ns_view->priv->view, "gtknsview", (id) ns_view,
+ OBJC_ASSOCIATION_ASSIGN);
+@@ -375,6 +399,7 @@ gtk_ns_view_map (GtkWidget *widget)
+ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+ GtkAllocation allocation;
+ NSView *parent_view;
++ NSWindow *window;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ gtk_ns_view_position_view (ns_view, &allocation);
+@@ -393,6 +418,22 @@ gtk_ns_view_map (GtkWidget *widget)
+ G_OBJECT (widget), 0);
+
+ GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->map (widget);
++
++ window = [ns_view->priv->view window];
++
++ if (window)
++ {
++ NSText *text = [window fieldEditor: YES
++ forObject: nil];
++
++ if (text)
++ {
++ gtk_ns_view_swizzle_draw_rect (text);
++
++ objc_setAssociatedObject (text, "gtkwindow", (id) toplevel,
++ OBJC_ASSOCIATION_ASSIGN);
++ }
++ }
+ }
+
+ static void
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 20bf51d57fecdc6783aa3edee50a657243aa872a Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 15 Mar 2013 16:56:59 +0100
+Subject: [PATCH 48/68] gtk: also clip the NSView's subviews
+
+---
+ gtk/gtknsview.c | 29 ++++++++++++++++++++---------
+ 1 file changed, 20 insertions(+), 9 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index b58a2c8..e2d8f96 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -225,10 +225,14 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ @end
+
+ static void
+-gtk_ns_view_swizzle_draw_rect (NSView *view)
++gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
++ const gchar *associated_key,
++ gpointer associated_object)
+ {
+ Method original_drawRect;
+ Method my_drawRect;
++ NSArray *subviews;
++ gint i;
+
+ original_drawRect = class_getInstanceMethod ([view class],
+ @selector (drawRect:));
+@@ -245,6 +249,18 @@ gtk_ns_view_swizzle_draw_rect (NSView *view)
+ method_getImplementation (my_drawRect),
+ method_getTypeEncoding (my_drawRect));
+ }
++
++ objc_setAssociatedObject (view, associated_key, (id) associated_object,
++ OBJC_ASSOCIATION_ASSIGN);
++
++ subviews = [view subviews];
++
++ for (i = 0; i < [subviews count]; i++)
++ {
++ gtk_ns_view_swizzle_draw_rect_recursive ([subviews objectAtIndex: i],
++ associated_key,
++ associated_object);
++ }
+ }
+
+ static void
+@@ -263,10 +279,8 @@ gtk_ns_view_constructed (GObject *object)
+ gtk_widget_get_can_focus (GTK_WIDGET (ns_view)));
+ #endif
+
+- gtk_ns_view_swizzle_draw_rect (ns_view->priv->view);
+-
+- objc_setAssociatedObject (ns_view->priv->view, "gtknsview", (id) ns_view,
+- OBJC_ASSOCIATION_ASSIGN);
++ gtk_ns_view_swizzle_draw_rect_recursive (ns_view->priv->view,
++ "gtknsview", ns_view);
+ }
+
+ static void
+@@ -428,10 +442,7 @@ gtk_ns_view_map (GtkWidget *widget)
+
+ if (text)
+ {
+- gtk_ns_view_swizzle_draw_rect (text);
+-
+- objc_setAssociatedObject (text, "gtkwindow", (id) toplevel,
+- OBJC_ASSOCIATION_ASSIGN);
++ gtk_ns_view_swizzle_draw_rect_recursive (text, "gtkwindow", toplevel);
+ }
+ }
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From ef95f0fc2917e98ea48ea5a07031b6e4b4152634 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 25 Apr 2013 12:22:28 +0200
+Subject: [PATCH 49/68] nsview: also swizzle DidAddSubview and clip all
+ subviews that are added
+
+Finally makes textviews being clipped correctly. Original patch
+from Kris, I fixed a minor clipping off-by-something.
+---
+ gtk/gtknsview.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 62 insertions(+), 1 deletion(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index e2d8f96..bc3cbfb 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -139,6 +139,25 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ gtk_widget_set_has_window (GTK_WIDGET (ns_view), FALSE);
+ }
+
++
++static void gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
++ const gchar *associated_key,
++ gpointer associated_object);
++
++@implementation NSView (myDidAddSubview)
++- (void) myDidAddSubview:(NSView *)aView
++{
++ void *associated_object;
++
++ associated_object = objc_getAssociatedObject (self, "gtknsview");
++
++ if (associated_object)
++ gtk_ns_view_swizzle_draw_rect_recursive (aView, "gtknsview", associated_object);
++
++ [self myDidAddSubview:aView];
++}
++@end
++
+ @implementation NSView (myDrawRect)
+ - (void) myDrawRect: (NSRect) dirtyRect
+ {
+@@ -164,7 +183,8 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ }
+ }
+
+- if (! ns_view)
++ if (! ns_view ||
++ ns_view->priv->view != [ns_view->priv->view ancestorSharedWithView: self])
+ {
+ [self myDrawRect: dirtyRect];
+ return;
+@@ -215,6 +235,18 @@ gtk_ns_view_init (GtkNSView *ns_view)
+ rect.size.width = viewport_allocation.width;
+ rect.size.height = viewport_allocation.height;
+
++ /* need to translate rect if this is not the view itself but a subview */
++ if (ns_view->priv->view != self)
++ {
++ NSRect offset = NSMakeRect (0, 0, 0, 0);
++
++ offset = [ns_view->priv->view convertRect: offset
++ fromView: self];
++
++ rect.origin.x -= offset.origin.x;
++ rect.origin.y -= offset.origin.y;
++ }
++
+ CGContextClipToRect (cg_context, rect);
+ }
+
+@@ -231,9 +263,20 @@ gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ {
+ Method original_drawRect;
+ Method my_drawRect;
++ Method original_didAddSubview;
++ Method my_didAddSubview;
+ NSArray *subviews;
+ gint i;
+
++ /* This is a private method that disable automatic focus ring handling.
++ * It looks like automatic focus ring drawing is handled by a toplevel
++ * NSView, so it cannot be caught by our clipping. If we disable this
++ * automatic handling, the focus ring appears to be drawn locally and
++ * is affected by our clipping.
++ */
++ [view _setAutomaticFocusRingDisabled:YES];
++
++ /* Swizzle drawRect */
+ original_drawRect = class_getInstanceMethod ([view class],
+ @selector (drawRect:));
+ my_drawRect = class_getInstanceMethod ([view class],
+@@ -250,6 +293,24 @@ gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ method_getTypeEncoding (my_drawRect));
+ }
+
++ /* Swizzle didAddSubView */
++ original_didAddSubview = class_getInstanceMethod ([view class],
++ @selector (didAddSubview:));
++ my_didAddSubview = class_getInstanceMethod ([view class],
++ @selector (myDidAddSubview:));
++
++ if (class_addMethod ([view class],
++ @selector (myDidAddSubview:),
++ method_getImplementation (original_didAddSubview),
++ method_getTypeEncoding (original_didAddSubview)))
++ {
++ class_replaceMethod ([view class],
++ @selector (didAddSubview:),
++ method_getImplementation (my_didAddSubview),
++ method_getTypeEncoding (my_didAddSubview));
++ }
++
++
+ objc_setAssociatedObject (view, associated_key, (id) associated_object,
+ OBJC_ASSOCIATION_ASSIGN);
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From daf0f5ecb7b9a71d93d7e41b000369d977ea4816 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Thu, 25 Apr 2013 14:52:18 +0200
+Subject: [PATCH 50/68] nsview: clip text field cursor drawing
+
+by replacing (not "normal" swizzling) drawInsertionPointInRect. This
+fixes cursor clipping for the *blinking* cursor, but not if the
+cusor moves. Patch patially from Kris..
+---
+ gtk/gtknsview.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 159 insertions(+), 3 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index bc3cbfb..5e3aa59 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -145,14 +145,23 @@ static void gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ gpointer associated_object);
+
+ @implementation NSView (myDidAddSubview)
+-- (void) myDidAddSubview:(NSView *)aView
++- (void) myDidAddSubview: (NSView *) aView
+ {
+ void *associated_object;
+
+ associated_object = objc_getAssociatedObject (self, "gtknsview");
+
+ if (associated_object)
+- gtk_ns_view_swizzle_draw_rect_recursive (aView, "gtknsview", associated_object);
++ {
++ gtk_ns_view_swizzle_draw_rect_recursive (aView, "gtknsview", associated_object);
++ }
++ else
++ {
++ associated_object = objc_getAssociatedObject (self, "gtkwindow");
++
++ if (associated_object)
++ gtk_ns_view_swizzle_draw_rect_recursive (aView, "gtkwindow", associated_object);
++ }
+
+ [self myDidAddSubview:aView];
+ }
+@@ -256,6 +265,110 @@ static void gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ }
+ @end
+
++@implementation NSTextView (myDrawInsertionPointInRect)
++- (void) myDrawInsertionPointInRect: (NSRect) aRect
++ color: (NSColor *) aColor
++ turnedOn: (BOOL) flag
++{
++ GtkNSView *ns_view;
++ GtkWidget *viewport;
++ CGContextRef cg_context;
++
++ ns_view = (GtkNSView *) objc_getAssociatedObject (self, "gtknsview");
++
++ if (! ns_view && ([self class] == [NSTextView class]))
++ {
++ /* if it's not a GtkNSView, check if it's the NSWindow's cell
++ * editor editing an NSTextField managed by a GtkNSView
++ */
++ GtkWindow *window = (GtkWindow *) objc_getAssociatedObject (self, "gtkwindow");
++
++ if (GTK_IS_WINDOW (window))
++ {
++ GtkWidget *focus = gtk_window_get_focus (window);
++
++ if (GTK_IS_NS_VIEW (focus))
++ ns_view = GTK_NS_VIEW (focus);
++ }
++ }
++
++ if (! ns_view ||
++ ns_view->priv->view != [ns_view->priv->view ancestorSharedWithView: self])
++ {
++ [self myDrawInsertionPointInRect: aRect
++ color: aColor
++ turnedOn: flag];
++ return;
++ }
++
++ cg_context = [[NSGraphicsContext currentContext] graphicsPort];
++ CGContextSaveGState (cg_context);
++
++ for (viewport = gtk_widget_get_ancestor (GTK_WIDGET (ns_view), GTK_TYPE_VIEWPORT);
++ viewport;
++ viewport = gtk_widget_get_ancestor (gtk_widget_get_parent (viewport),
++ GTK_TYPE_VIEWPORT))
++ {
++ GdkWindow *window;
++ GtkAllocation viewport_allocation;
++ CGRect rect;
++
++ gtk_widget_get_allocation (viewport, &viewport_allocation);
++
++ /* evil: don't clip to the viewport's width/height but to that
++ * of its parent window, because we know we hacked an
++ * overshoot_window into GtkScrolledWindow and need to restrict
++ * rendering to its area
++ */
++ window = gtk_widget_get_parent_window (viewport);
++
++ viewport_allocation.width = gdk_window_get_width (window);
++ viewport_allocation.height = gdk_window_get_height (window);
++
++ if (gtk_viewport_get_shadow_type (GTK_VIEWPORT (viewport)) != GTK_SHADOW_NONE)
++ {
++ GtkStyle *style = gtk_widget_get_style (viewport);
++
++ viewport_allocation.x += style->xthickness;
++ viewport_allocation.y += style->ythickness;
++ viewport_allocation.width -= 2 * style->xthickness;
++ viewport_allocation.height -= 2 * style->ythickness;
++ }
++
++ gtk_widget_translate_coordinates (viewport, GTK_WIDGET (ns_view),
++ viewport_allocation.x,
++ viewport_allocation.y,
++ &viewport_allocation.x,
++ &viewport_allocation.y);
++
++ rect.origin.x = viewport_allocation.x;
++ rect.origin.y = viewport_allocation.y;
++ rect.size.width = viewport_allocation.width;
++ rect.size.height = viewport_allocation.height;
++
++ /* need to translate rect if this is not the view itself but a subview */
++ if (ns_view->priv->view != self)
++ {
++ NSRect offset = NSMakeRect (0, 0, 0, 0);
++
++ offset = [ns_view->priv->view convertRect: offset
++ fromView: self];
++
++ rect.origin.x -= offset.origin.x;
++ rect.origin.y -= offset.origin.y;
++ }
++
++ CGContextClipToRect (cg_context, rect);
++ }
++
++ [self myDrawInsertionPointInRect: aRect
++ color: aColor
++ turnedOn: flag];
++
++ CGContextRestoreGState (cg_context);
++}
++@end
++
+ static void
+ gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ const gchar *associated_key,
+@@ -310,7 +423,6 @@ gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ method_getTypeEncoding (my_didAddSubview));
+ }
+
+-
+ objc_setAssociatedObject (view, associated_key, (id) associated_object,
+ OBJC_ASSOCIATION_ASSIGN);
+
+@@ -325,6 +437,49 @@ gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ }
+
+ static void
++gtk_ns_view_replace_draw_insertion_point (void)
++{
++ static volatile gsize draw_insertion_point_replaced = 0;
++
++ /* We replace the drawInsertPointInRect:color:turnedOn: method of the
++ * NSTextView class. This should only be done once per application
++ * instance.
++ */
++
++ if (g_once_init_enter (&draw_insertion_point_replaced))
++ {
++ Method original_drawInsertionPointInRect;
++ Method my_drawInsertionPointInRect;
++ IMP original_implementation;
++ Class text_view_class = [NSTextView class];
++
++ /* Get the original method and an explicit reference to the
++ * implementation behind this method. This because after the
++ * first replace method call, the method will point at a different
++ * implementation.
++ */
++ original_drawInsertionPointInRect = class_getInstanceMethod (text_view_class,
++ @selector (drawInsertionPointInRect:color:turnedOn:));
++ original_implementation = method_getImplementation (original_drawInsertionPointInRect);
++
++ my_drawInsertionPointInRect = class_getInstanceMethod (text_view_class,
++ @selector (myDrawInsertionPointInRect:color:turnedOn:));
++
++ class_replaceMethod (text_view_class,
++ @selector (drawInsertionPointInRect:color:turnedOn:),
++ method_getImplementation (my_drawInsertionPointInRect),
++ method_getTypeEncoding (my_drawInsertionPointInRect));
++
++ class_replaceMethod (text_view_class,
++ @selector (myDrawInsertionPointInRect:color:turnedOn:),
++ original_implementation,
++ method_getTypeEncoding (original_drawInsertionPointInRect));
++
++ g_once_init_leave (&draw_insertion_point_replaced, TRUE);
++ }
++}
++
++static void
+ gtk_ns_view_constructed (GObject *object)
+ {
+ GtkNSView *ns_view = GTK_NS_VIEW (object);
+@@ -340,6 +495,7 @@ gtk_ns_view_constructed (GObject *object)
+ gtk_widget_get_can_focus (GTK_WIDGET (ns_view)));
+ #endif
+
++ gtk_ns_view_replace_draw_insertion_point ();
+ gtk_ns_view_swizzle_draw_rect_recursive (ns_view->priv->view,
+ "gtknsview", ns_view);
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 478d022cd553d33de59ec8ce22605b14a3837264 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 26 Apr 2013 15:50:14 +0200
+Subject: [PATCH 51/68] nsview: factor out almost all code from the overridden
+ draw functions
+
+---
+ gtk/gtknsview.c | 151 +++++++++++++++++--------------------------------------
+ 1 file changed, 47 insertions(+), 104 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index 5e3aa59..b37b2fa 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -167,21 +167,19 @@ static void gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ }
+ @end
+
+-@implementation NSView (myDrawRect)
+-- (void) myDrawRect: (NSRect) dirtyRect
++static GtkNSView *
++get_associated_gtknsview (NSView *view)
+ {
+ GtkNSView *ns_view;
+- GtkWidget *viewport;
+- CGContextRef cg_context;
+
+- ns_view = (GtkNSView *) objc_getAssociatedObject (self, "gtknsview");
++ ns_view = (GtkNSView *) objc_getAssociatedObject (view, "gtknsview");
+
+- if (! ns_view && ([self class] == [NSTextView class]))
++ if (! ns_view && ([view class] == [NSTextView class]))
+ {
+ /* if it's not a GtkNSView, check if it's the NSWindow's cell
+ * editor editing an NSTextField managed by a GtkNSView
+ */
+- GtkWindow *window = (GtkWindow *) objc_getAssociatedObject (self, "gtkwindow");
++ GtkWindow *window = (GtkWindow *) objc_getAssociatedObject (view, "gtkwindow");
+
+ if (GTK_IS_WINDOW (window))
+ {
+@@ -192,15 +190,21 @@ static void gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ }
+ }
+
+- if (! ns_view ||
+- ns_view->priv->view != [ns_view->priv->view ancestorSharedWithView: self])
++ if (ns_view &&
++ ns_view->priv->view != [ns_view->priv->view ancestorSharedWithView: view])
+ {
+- [self myDrawRect: dirtyRect];
+- return;
++ return NULL;
+ }
+
+- cg_context = [[NSGraphicsContext currentContext] graphicsPort];
+- CGContextSaveGState (cg_context);
++ return ns_view;
++}
++
++static CGContextRef
++clip_to_parent_viewports (GtkNSView *ns_view,
++ NSView *view)
++{
++ CGContextRef cg_context = nil;
++ GtkWidget *viewport;
+
+ for (viewport = gtk_widget_get_ancestor (GTK_WIDGET (ns_view), GTK_TYPE_VIEWPORT);
+ viewport;
+@@ -245,23 +249,44 @@ static void gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ rect.size.height = viewport_allocation.height;
+
+ /* need to translate rect if this is not the view itself but a subview */
+- if (ns_view->priv->view != self)
++ if (ns_view->priv->view != view)
+ {
+ NSRect offset = NSMakeRect (0, 0, 0, 0);
+
+ offset = [ns_view->priv->view convertRect: offset
+- fromView: self];
++ fromView: view];
+
+ rect.origin.x -= offset.origin.x;
+ rect.origin.y -= offset.origin.y;
+ }
+
++ if (! cg_context)
++ {
++ cg_context = [[NSGraphicsContext currentContext] graphicsPort];
++ CGContextSaveGState (cg_context);
++ }
++
+ CGContextClipToRect (cg_context, rect);
+ }
+
++ return cg_context;
++}
++
++@implementation NSView (myDrawRect)
++- (void) myDrawRect: (NSRect) dirtyRect
++{
++ GtkNSView *ns_view;
++ CGContextRef cg_context = nil;
++
++ ns_view = get_associated_gtknsview (self);
++
++ if (ns_view)
++ cg_context = clip_to_parent_viewports (ns_view, self);
++
+ [self myDrawRect: dirtyRect];
+
+- CGContextRestoreGState (cg_context);
++ if (cg_context)
++ CGContextRestoreGState (cg_context);
+ }
+ @end
+
+@@ -271,101 +296,19 @@ static void gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ turnedOn: (BOOL) flag
+ {
+ GtkNSView *ns_view;
+- GtkWidget *viewport;
+- CGContextRef cg_context;
+-
+- ns_view = (GtkNSView *) objc_getAssociatedObject (self, "gtknsview");
+-
+- if (! ns_view && ([self class] == [NSTextView class]))
+- {
+- /* if it's not a GtkNSView, check if it's the NSWindow's cell
+- * editor editing an NSTextField managed by a GtkNSView
+- */
+- GtkWindow *window = (GtkWindow *) objc_getAssociatedObject (self, "gtkwindow");
+-
+- if (GTK_IS_WINDOW (window))
+- {
+- GtkWidget *focus = gtk_window_get_focus (window);
+-
+- if (GTK_IS_NS_VIEW (focus))
+- ns_view = GTK_NS_VIEW (focus);
+- }
+- }
+-
+- if (! ns_view ||
+- ns_view->priv->view != [ns_view->priv->view ancestorSharedWithView: self])
+- {
+- [self myDrawInsertionPointInRect: aRect
+- color: aColor
+- turnedOn: flag];
+- return;
+- }
+-
+- cg_context = [[NSGraphicsContext currentContext] graphicsPort];
+- CGContextSaveGState (cg_context);
+-
+- for (viewport = gtk_widget_get_ancestor (GTK_WIDGET (ns_view), GTK_TYPE_VIEWPORT);
+- viewport;
+- viewport = gtk_widget_get_ancestor (gtk_widget_get_parent (viewport),
+- GTK_TYPE_VIEWPORT))
+- {
+- GdkWindow *window;
+- GtkAllocation viewport_allocation;
+- CGRect rect;
+-
+- gtk_widget_get_allocation (viewport, &viewport_allocation);
+-
+- /* evil: don't clip to the viewport's width/height but to that
+- * of its parent window, because we know we hacked an
+- * overshoot_window into GtkScrolledWindow and need to restrict
+- * rendering to its area
+- */
+- window = gtk_widget_get_parent_window (viewport);
+-
+- viewport_allocation.width = gdk_window_get_width (window);
+- viewport_allocation.height = gdk_window_get_height (window);
+-
+- if (gtk_viewport_get_shadow_type (GTK_VIEWPORT (viewport)) != GTK_SHADOW_NONE)
+- {
+- GtkStyle *style = gtk_widget_get_style (viewport);
+-
+- viewport_allocation.x += style->xthickness;
+- viewport_allocation.y += style->ythickness;
+- viewport_allocation.width -= 2 * style->xthickness;
+- viewport_allocation.height -= 2 * style->ythickness;
+- }
+-
+- gtk_widget_translate_coordinates (viewport, GTK_WIDGET (ns_view),
+- viewport_allocation.x,
+- viewport_allocation.y,
+- &viewport_allocation.x,
+- &viewport_allocation.y);
++ CGContextRef cg_context = nil;;
+
+- rect.origin.x = viewport_allocation.x;
+- rect.origin.y = viewport_allocation.y;
+- rect.size.width = viewport_allocation.width;
+- rect.size.height = viewport_allocation.height;
++ ns_view = get_associated_gtknsview (self);
+
+- /* need to translate rect if this is not the view itself but a subview */
+- if (ns_view->priv->view != self)
+- {
+- NSRect offset = NSMakeRect (0, 0, 0, 0);
+-
+- offset = [ns_view->priv->view convertRect: offset
+- fromView: self];
+-
+- rect.origin.x -= offset.origin.x;
+- rect.origin.y -= offset.origin.y;
+- }
+-
+- CGContextClipToRect (cg_context, rect);
+- }
++ if (ns_view)
++ cg_context = clip_to_parent_viewports (ns_view, self);
+
+ [self myDrawInsertionPointInRect: aRect
+ color: aColor
+ turnedOn: flag];
+
+- CGContextRestoreGState (cg_context);
++ if (cg_context)
++ CGContextRestoreGState (cg_context);
+ }
+ @end
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 6bf17cdaffe648ce53dd39619c3ff9a65d272831 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 5 Jul 2013 11:49:22 +0200
+Subject: [PATCH 52/68] nsview: also focus the GtkNSView if a focussable
+ subview was clicked
+
+---
+ gtk/gtknsview.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index b37b2fa..3b30d3b 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -713,17 +713,25 @@ gtk_ns_view_native_child_event (GdkWindow *window,
+ {
+ case NSLeftMouseDown:
+ if (! gtk_widget_has_focus (GTK_WIDGET (ns_view)) &&
+-
+- /* other code can set can-focus, so check for both */
+- gtk_widget_get_can_focus (GTK_WIDGET (ns_view)) &&
+- [ns_view->priv->view acceptsFirstResponder])
++ gtk_widget_get_can_focus (GTK_WIDGET (ns_view)))
+ {
++ NSPoint point = [[view superview ] convertPoint: [event locationInWindow]
++ fromView: nil];
++ NSView *hit = [view hitTest: point];
++
++ if (hit &&
++ (hit == view ||
++ [hit ancestorSharedWithView: view] == view) &&
++ ([hit acceptsFirstResponder] ||
++ [view acceptsFirstResponder]))
++ {
+ #if DEBUG_FOCUS
+- g_printerr ("grabbing focus on %s\n",
+- class_getName ([ns_view->priv->view class]));
++ g_printerr ("grabbing focus on %s\n",
++ class_getName ([ns_view->priv->view class]));
+ #endif
+
+- gtk_widget_grab_focus (GTK_WIDGET (ns_view));
++ gtk_widget_grab_focus (GTK_WIDGET (ns_view));
++ }
+ }
+ break;
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 3e4fba4264957d704d9c01496eef57e897b7d691 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 3 May 2013 11:55:51 +0200
+Subject: [PATCH 53/68] gtk: add an "overlay policy" API to GtkScrolledWindow
+
+which allows to turn off drawing of the overlay scrollbars,
+while keeping scrolling enabled.
+---
+ gtk/gtkscrolledwindow.c | 68 ++++++++++++++++++++++++++++++++++++++++++-----
+ gtk/gtkscrolledwindow.h | 6 +++++
+ 2 files changed, 68 insertions(+), 6 deletions(-)
+
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 9def3aa..a643b7d 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -121,6 +121,9 @@ typedef struct {
+ gdouble unclamped_hadj_value;
+ gdouble unclamped_vadj_value;
+
++ GtkPolicyType hoverlay_policy;
++ GtkPolicyType voverlay_policy;
++
+ GtkAllocation viewport_allocation;
+ CALayer *vbar_layer;
+ CALayer *hbar_layer;
+@@ -536,6 +539,9 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
+ NULL);
+ g_object_ref_sink (priv->opacity);
+
++ priv->hoverlay_policy = GTK_POLICY_AUTOMATIC;
++ priv->voverlay_policy = GTK_POLICY_AUTOMATIC;
++
+ priv->sb_min_height = 20;
+ priv->sb_padding = 2;
+ priv->sb_radius = 3;
+@@ -865,6 +871,42 @@ gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
+ *vscrollbar_policy = scrolled_window->vscrollbar_policy;
+ }
+
++void
++gtk_scrolled_window_set_overlay_policy (GtkScrolledWindow *scrolled_window,
++ GtkPolicyType hoverlay_policy,
++ GtkPolicyType voverlay_policy)
++{
++ GtkScrolledWindowPrivate *priv;
++
++ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
++ g_return_if_fail (hoverlay_policy == GTK_POLICY_AUTOMATIC ||
++ hoverlay_policy == GTK_POLICY_NEVER);
++ g_return_if_fail (voverlay_policy == GTK_POLICY_AUTOMATIC ||
++ voverlay_policy == GTK_POLICY_NEVER);
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ priv->hoverlay_policy = hoverlay_policy;
++ priv->voverlay_policy = voverlay_policy;
++}
++
++void
++gtk_scrolled_window_get_overlay_policy (GtkScrolledWindow *scrolled_window,
++ GtkPolicyType *hoverlay_policy,
++ GtkPolicyType *voverlay_policy)
++{
++ GtkScrolledWindowPrivate *priv;
++
++ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
++
++ priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (scrolled_window);
++
++ if (hoverlay_policy)
++ *hoverlay_policy = priv->hoverlay_policy;
++ if (voverlay_policy)
++ *voverlay_policy = priv->voverlay_policy;
++}
++
+ static void
+ gtk_scrolled_window_update_real_placement (GtkScrolledWindow *scrolled_window)
+ {
+@@ -1309,7 +1351,10 @@ gtk_scrolled_window_update_scrollbars (GtkScrolledWindow *scrolled_window)
+ &vbar_rect, &vslider_rect,
+ &hbar_rect, &hslider_rect);
+
+- if (priv->sb_visible && scrolled_window->vscrollbar && vbar_rect.width > 0)
++ if (priv->sb_visible &&
++ scrolled_window->vscrollbar &&
++ priv->voverlay_policy == GTK_POLICY_AUTOMATIC &&
++ vbar_rect.width > 0)
+ {
+ rect.origin.x = priv->viewport_allocation.x + vbar_rect.x;
+ rect.origin.y = priv->viewport_allocation.y + vbar_rect.y;
+@@ -1326,7 +1371,10 @@ gtk_scrolled_window_update_scrollbars (GtkScrolledWindow *scrolled_window)
+ priv->vbar_layer.opacity = 0.0;
+ }
+
+- if (priv->sb_visible && scrolled_window->hscrollbar && hbar_rect.width > 0)
++ if (priv->sb_visible &&
++ scrolled_window->hscrollbar &&
++ priv->hoverlay_policy == GTK_POLICY_AUTOMATIC &&
++ hbar_rect.width > 0)
+ {
+ rect.origin.x = priv->viewport_allocation.x + hbar_rect.x;
+ rect.origin.y = priv->viewport_allocation.y + hbar_rect.y;
+@@ -1334,8 +1382,12 @@ gtk_scrolled_window_update_scrollbars (GtkScrolledWindow *scrolled_window)
+ rect.size.height = hbar_rect.height;
+
+ /* don't overlap in the corner */
+- if (scrolled_window->vscrollbar && vbar_rect.width > 0)
+- rect.size.width -= vbar_rect.width;
++ if (scrolled_window->vscrollbar &&
++ priv->voverlay_policy == GTK_POLICY_AUTOMATIC &&
++ vbar_rect.width > 0)
++ {
++ rect.size.width -= vbar_rect.width;
++ }
+
+ rect.origin.y = window_height - rect.origin.y - rect.size.height;
+
+@@ -1347,7 +1399,9 @@ gtk_scrolled_window_update_scrollbars (GtkScrolledWindow *scrolled_window)
+ priv->hbar_layer.opacity = 0.0;
+ }
+
+- if (scrolled_window->vscrollbar && vslider_rect.width > 0)
++ if (scrolled_window->vscrollbar &&
++ priv->voverlay_policy == GTK_POLICY_AUTOMATIC &&
++ vslider_rect.width > 0)
+ {
+ rect.origin.x = priv->viewport_allocation.x + vslider_rect.x;
+ rect.origin.y = priv->viewport_allocation.y + vslider_rect.y;
+@@ -1365,7 +1419,9 @@ gtk_scrolled_window_update_scrollbars (GtkScrolledWindow *scrolled_window)
+ priv->vslider_layer.opacity = 0.0;
+ }
+
+- if (scrolled_window->hscrollbar && hslider_rect.width > 0)
++ if (scrolled_window->hscrollbar &&
++ priv->hoverlay_policy == GTK_POLICY_AUTOMATIC &&
++ hslider_rect.width > 0)
+ {
+ rect.origin.x = priv->viewport_allocation.x + hslider_rect.x;
+ rect.origin.y = priv->viewport_allocation.y + hslider_rect.y;
+diff --git a/gtk/gtkscrolledwindow.h b/gtk/gtkscrolledwindow.h
+index f75596d..69dcb43 100644
+--- a/gtk/gtkscrolledwindow.h
++++ b/gtk/gtkscrolledwindow.h
+@@ -116,6 +116,12 @@ void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolle
+ void gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
+ GtkPolicyType *hscrollbar_policy,
+ GtkPolicyType *vscrollbar_policy);
++void gtk_scrolled_window_set_overlay_policy (GtkScrolledWindow *scrolled_window,
++ GtkPolicyType hoverlay_policy,
++ GtkPolicyType voverlay_policy);
++void gtk_scrolled_window_get_overlay_policy (GtkScrolledWindow *scrolled_window,
++ GtkPolicyType *hoverlay_policy,
++ GtkPolicyType *voverlay_policy);
+ void gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
+ GtkCornerType window_placement);
+ void gtk_scrolled_window_unset_placement (GtkScrolledWindow *scrolled_window);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 13bba88188cc3880d4eba9a93f9109154308d51a Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 18 Jan 2013 15:47:29 +0100
+Subject: [PATCH 54/68] quartz: add gdk_screen_ and
+ gdk_window_get_scale_factor()
+
+which return 1.0 normally and 2.0 on retina displays.
+---
+ gdk/gdkscreen.h | 2 ++
+ gdk/gdkwindow.c | 24 ++++++++++++++++++++++++
+ gdk/gdkwindow.h | 1 +
+ gdk/gdkwindowimpl.h | 3 +++
+ gdk/quartz/gdkscreen-quartz.c | 33 +++++++++++++++++++++++++++++++++
+ gdk/quartz/gdkwindow-quartz.c | 22 ++++++++++++++++++++++
+ gdk/x11/gdkscreen-x11.c | 7 +++++++
+ 7 files changed, 92 insertions(+)
+
+diff --git a/gdk/gdkscreen.h b/gdk/gdkscreen.h
+index d3d4fe9..0c1e895 100644
+--- a/gdk/gdkscreen.h
++++ b/gdk/gdkscreen.h
+@@ -109,6 +109,8 @@ gint gdk_screen_get_monitor_height_mm (GdkScreen *screen,
+ gint monitor_num);
+ gchar * gdk_screen_get_monitor_plug_name (GdkScreen *screen,
+ gint monitor_num);
++gdouble gdk_screen_get_monitor_scale_factor (GdkScreen *screen,
++ gint monitor_num);
+
+ void gdk_screen_broadcast_client_message (GdkScreen *screen,
+ GdkEvent *event);
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index 3040321..29b96e2 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -11475,6 +11475,30 @@ gdk_window_get_height (GdkWindow *window)
+ return height;
+ }
+
++gdouble
++gdk_window_get_scale_factor (GdkWindow *window)
++{
++ GdkWindowObject *private;
++ GdkWindowImplIface *impl_iface;
++
++ g_return_val_if_fail (GDK_IS_WINDOW (window), 1.0);
++
++ private = (GdkWindowObject *) window;
++ if (private->destroyed)
++ return 1.0;
++
++ window = gdk_window_get_toplevel (window);
++
++ if (gdk_window_has_impl (private))
++ {
++ impl_iface = GDK_WINDOW_IMPL_GET_IFACE (private->impl);
++
++ if (impl_iface->get_scale_factor)
++ return impl_iface->get_scale_factor (window);
++ }
++
++ return 1.0;
++}
+
+ #define __GDK_WINDOW_C__
+ #include "gdkaliasdef.c"
+diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
+index 572797b..95a3665 100644
+--- a/gdk/gdkwindow.h
++++ b/gdk/gdkwindow.h
+@@ -341,6 +341,7 @@ GdkDisplay* gdk_window_get_display (GdkWindow *window);
+ GdkVisual* gdk_window_get_visual (GdkWindow *window);
+ int gdk_window_get_width (GdkWindow *window);
+ int gdk_window_get_height (GdkWindow *window);
++gdouble gdk_window_get_scale_factor (GdkWindow *window);
+
+ GdkWindow* gdk_window_at_pointer (gint *win_x,
+ gint *win_y);
+diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h
+index 3a5029b..d6a0c89 100644
+--- a/gdk/gdkwindowimpl.h
++++ b/gdk/gdkwindowimpl.h
+@@ -146,6 +146,9 @@ struct _GdkWindowImplIface
+ void (* input_window_destroy) (GdkWindow *window);
+ void (* input_window_crossing)(GdkWindow *window,
+ gboolean enter);
++
++ gdouble (* get_scale_factor) (GdkWindow *window);
++
+ gboolean supports_native_bg;
+ };
+
+diff --git a/gdk/quartz/gdkscreen-quartz.c b/gdk/quartz/gdkscreen-quartz.c
+index 4bb573b..e6f0c44 100644
+--- a/gdk/quartz/gdkscreen-quartz.c
++++ b/gdk/quartz/gdkscreen-quartz.c
+@@ -494,6 +494,39 @@ gdk_screen_get_monitor_workarea (GdkScreen *screen,
+ GDK_QUARTZ_RELEASE_POOL;
+ }
+
++/* Protocol to build cleanly for OSX < 10.7 */
++@protocol ScaleFactor
++- (CGFloat) backingScaleFactor;
++@end
++
++gdouble
++gdk_screen_get_monitor_scale_factor (GdkScreen *screen,
++ gint monitor_num)
++{
++ GdkScreenQuartz *quartz_screen;
++ NSArray *array;
++ NSScreen *nsscreen;
++ gdouble scale_factor = 1.0;
++
++ g_return_val_if_fail (GDK_IS_SCREEN (screen), 1.0);
++ g_return_val_if_fail (monitor_num < gdk_screen_get_n_monitors (screen), 1.0);
++ g_return_val_if_fail (monitor_num >= 0, 1.0);
++
++ quartz_screen = GDK_SCREEN_QUARTZ (screen);
++
++ GDK_QUARTZ_ALLOC_POOL;
++
++ array = [NSScreen screens];
++ nsscreen = [array objectAtIndex:monitor_num];
++
++ if (gdk_quartz_osx_version() >= GDK_OSX_LION)
++ scale_factor = [(id <ScaleFactor>) nsscreen backingScaleFactor];
++
++ GDK_QUARTZ_RELEASE_POOL;
++
++ return scale_factor;
++}
++
+ gchar *
+ gdk_screen_make_display_name (GdkScreen *screen)
+ {
+diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c
+index 500776d..5b9ceef 100644
+--- a/gdk/quartz/gdkwindow-quartz.c
++++ b/gdk/quartz/gdkwindow-quartz.c
+@@ -3201,6 +3201,27 @@ _gdk_windowing_window_get_input_shape (GdkWindow *window)
+ return NULL;
+ }
+
++/* Protocol to build cleanly for OSX < 10.7 */
++@protocol ScaleFactor
++- (CGFloat) backingScaleFactor;
++@end
++
++static gdouble
++gdk_window_quartz_get_scale_factor (GdkWindow *window)
++{
++ GdkWindowImplQuartz *impl;
++
++ if (GDK_WINDOW_DESTROYED (window))
++ return 1.0;
++
++ impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (window)->impl);
++
++ if (gdk_quartz_osx_version() >= GDK_OSX_LION)
++ return [(id <ScaleFactor>) impl->toplevel backingScaleFactor];
++
++ return 1.0;
++}
++
+ static void
+ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
+ {
+@@ -3229,4 +3250,5 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
+ iface->destroy = _gdk_quartz_window_destroy;
+ iface->input_window_destroy = _gdk_input_window_destroy;
+ iface->input_window_crossing = _gdk_input_window_crossing;
++ iface->get_scale_factor = gdk_window_quartz_get_scale_factor;
+ }
+diff --git a/gdk/x11/gdkscreen-x11.c b/gdk/x11/gdkscreen-x11.c
+index 66a0d96..d09d0b6 100644
+--- a/gdk/x11/gdkscreen-x11.c
++++ b/gdk/x11/gdkscreen-x11.c
+@@ -454,6 +454,13 @@ gdk_screen_get_monitor_plug_name (GdkScreen *screen,
+ return g_strdup (screen_x11->monitors[monitor_num].output_name);
+ }
+
++gdouble
++gdk_screen_get_monitor_scale_factor (GdkScreen *screen,
++ gint monitor_num)
++{
++ return 1.0;
++}
++
+ /**
+ * gdk_x11_screen_get_monitor_output:
+ * @screen: a #GdkScreen
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 82dbf415e822d1bb687db09b9dfa0d1e43528e99 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 15 Feb 2013 15:35:13 +0100
+Subject: [PATCH 55/68] gtk: add gtk_widget_get_scale_factor()
+
+---
+ gtk/gtkwidget.c | 20 ++++++++++++++++++++
+ gtk/gtkwidget.h | 1 +
+ 2 files changed, 21 insertions(+)
+
+diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
+index 8e38ee1..c812bb9 100644
+--- a/gtk/gtkwidget.c
++++ b/gtk/gtkwidget.c
+@@ -7713,6 +7713,26 @@ gtk_widget_has_screen (GtkWidget *widget)
+ return (gtk_widget_get_screen_unchecked (widget) != NULL);
+ }
+
++gdouble
++gtk_widget_get_scale_factor (GtkWidget *widget)
++{
++ GtkWidget *toplevel;
++
++ g_return_val_if_fail (GTK_IS_WIDGET (widget), 1.0);
++
++ toplevel = gtk_widget_get_toplevel (widget);
++ if (toplevel && toplevel != widget)
++ return gtk_widget_get_scale_factor (toplevel);
++
++ if (widget->window)
++ return gdk_window_get_scale_factor (widget->window);
++
++ /* else fall back to something that is more likely to be right than
++ * just returning 1.0:
++ */
++ return gdk_screen_get_monitor_scale_factor (gtk_widget_get_screen (widget), 0);
++}
++
+ /**
+ * gtk_widget_get_display:
+ * @widget: a #GtkWidget
+diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
+index 1fcf21a..cee16cd 100644
+--- a/gtk/gtkwidget.h
++++ b/gtk/gtkwidget.h
+@@ -1055,6 +1055,7 @@ GdkVisual* gtk_widget_get_visual (GtkWidget *widget);
+
+ GdkScreen * gtk_widget_get_screen (GtkWidget *widget);
+ gboolean gtk_widget_has_screen (GtkWidget *widget);
++gdouble gtk_widget_get_scale_factor (GtkWidget *widget);
+ GdkDisplay * gtk_widget_get_display (GtkWidget *widget);
+ GdkWindow * gtk_widget_get_root_window (GtkWidget *widget);
+ GtkSettings* gtk_widget_get_settings (GtkWidget *widget);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From e9dfba023f756801f8931a08644c23809cb38412 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Tue, 12 Feb 2013 13:59:09 +0100
+Subject: [PATCH 56/68] iconfactory: Add _scaled() variants
+
+These functions can be used to deal with stock icons
+at sizes that are suitable for gdk_window_get_scale_factor()
+---
+ gtk/gtkiconfactory.c | 173 +++++++++++++++++++++++++++++++++++++++-----------
+ gtk/gtkiconfactory.h | 14 +++-
+ 2 files changed, 150 insertions(+), 37 deletions(-)
+
+diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c
+index c4b6c9c..0dc31e6 100644
+--- a/gtk/gtkiconfactory.c
++++ b/gtk/gtkiconfactory.c
+@@ -801,6 +801,7 @@ icon_sizes_init_for_settings (GtkSettings *settings)
+ static gboolean
+ icon_size_lookup_intern (GtkSettings *settings,
+ GtkIconSize size,
++ gdouble scale,
+ gint *widthp,
+ gint *heightp)
+ {
+@@ -840,10 +841,16 @@ icon_size_lookup_intern (GtkSettings *settings,
+ }
+
+ if (widthp)
+- *widthp = width_for_settings >= 0 ? width_for_settings : icon_sizes[size].width;
++ {
++ *widthp = width_for_settings >= 0 ? width_for_settings : icon_sizes[size].width;
++ (*widthp) *= scale;
++ }
+
+ if (heightp)
+- *heightp = height_for_settings >= 0 ? height_for_settings : icon_sizes[size].height;
++ {
++ *heightp = height_for_settings >= 0 ? height_for_settings : icon_sizes[size].height;
++ (*heightp) *= scale;
++ }
+
+ return TRUE;
+ }
+@@ -879,7 +886,7 @@ gtk_icon_size_lookup_for_settings (GtkSettings *settings,
+ {
+ g_return_val_if_fail (GTK_IS_SETTINGS (settings), FALSE);
+
+- return icon_size_lookup_intern (settings, size, width, height);
++ return icon_size_lookup_intern (settings, size, 1, width, height);
+ }
+
+ /**
+@@ -914,6 +921,18 @@ gtk_icon_size_lookup (GtkIconSize size,
+ size, widthp, heightp);
+ }
+
++gboolean
++gtk_icon_size_lookup_scaled (GtkSettings *settings,
++ GtkIconSize size,
++ gdouble scale,
++ gint *width,
++ gint *height)
++{
++ g_return_val_if_fail (GTK_IS_SETTINGS (settings), FALSE);
++
++ return icon_size_lookup_intern (settings, size, scale, width, height);
++}
++
+ static GtkIconSize
+ icon_size_register_intern (const gchar *name,
+ gint width,
+@@ -1000,7 +1019,7 @@ gtk_icon_size_register_alias (const gchar *alias,
+
+ init_icon_sizes ();
+
+- if (!icon_size_lookup_intern (NULL, target, NULL, NULL))
++ if (!icon_size_lookup_intern (NULL, target, 1, NULL, NULL))
+ g_warning ("gtk_icon_size_register_alias: Icon size %u does not exist", target);
+
+ ia = g_hash_table_lookup (icon_aliases, alias);
+@@ -1288,8 +1307,8 @@ sizes_equivalent (GtkIconSize lhs,
+
+ gint r_w, r_h, l_w, l_h;
+
+- icon_size_lookup_intern (NULL, rhs, &r_w, &r_h);
+- icon_size_lookup_intern (NULL, lhs, &l_w, &l_h);
++ icon_size_lookup_intern (NULL, rhs, 1, &r_w, &r_h);
++ icon_size_lookup_intern (NULL, lhs, 1, &l_w, &l_h);
+
+ return r_w == l_w && r_h == l_h;
+ #endif
+@@ -1372,7 +1391,8 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ GtkStateType state,
+ GtkIconSize size,
+ GtkWidget *widget,
+- const char *detail)
++ const char *detail,
++ gboolean scale_requested)
+ {
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *tmp_pixbuf;
+@@ -1383,6 +1403,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ gint width, height, pixel_size;
+ gint *sizes, *s, dist;
+ GError *error = NULL;
++ gdouble scale = 1;
+
+ if (widget && gtk_widget_has_screen (widget))
+ screen = gtk_widget_get_screen (widget);
+@@ -1398,6 +1419,14 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ icon_theme = gtk_icon_theme_get_for_screen (screen);
+ settings = gtk_settings_get_for_screen (screen);
+
++ if (scale_requested && widget)
++ {
++ if (!widget->window)
++ gtk_widget_realize (widget);
++
++ scale = gdk_window_get_scale_factor (widget->window);
++ }
++
+ if (!gtk_icon_size_lookup_for_settings (settings, size, &width, &height))
+ {
+ if (size == (GtkIconSize)-1)
+@@ -1440,7 +1469,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ }
+ }
+
+- pixel_size = MIN (width, height);
++ pixel_size = MIN (width, height) * scale;
+
+ if (icon_source->direction != GTK_TEXT_DIR_NONE)
+ {
+@@ -1504,7 +1533,8 @@ find_and_render_icon_source (GtkIconSet *icon_set,
+ GtkStateType state,
+ GtkIconSize size,
+ GtkWidget *widget,
+- const char *detail)
++ const char *detail,
++ gboolean scale_requested)
+ {
+ GSList *failed = NULL;
+ GdkPixbuf *pixbuf = NULL;
+@@ -1546,7 +1576,7 @@ find_and_render_icon_source (GtkIconSet *icon_set,
+ case GTK_ICON_SOURCE_STATIC_ICON_NAME:
+ pixbuf = render_icon_name_pixbuf (source, style,
+ direction, state, size,
+- widget, detail);
++ widget, detail, scale_requested);
+ if (!pixbuf)
+ failed = g_slist_prepend (failed, source);
+ break;
+@@ -1598,6 +1628,84 @@ render_fallback_image (GtkStyle *style,
+ detail);
+ }
+
++static gdouble
++_get_real_scale (GtkWidget *widget,
++ GtkStyle *style,
++ GtkIconSize size,
++ GdkPixbuf *icon)
++{
++ GtkSettings *settings;
++ GdkScreen *screen;
++ gint icon_width;
++
++ if (widget && gtk_widget_has_screen (widget))
++ screen = gtk_widget_get_screen (widget);
++ else if (style && style->colormap)
++ screen = gdk_colormap_get_screen (style->colormap);
++ else
++ {
++ screen = gdk_screen_get_default ();
++ GTK_NOTE (MULTIHEAD,
++ g_warning ("Using the default screen for gtk_icon_set_render_icon()"));
++ }
++
++ settings = gtk_settings_get_for_screen (screen);
++ gtk_icon_size_lookup_for_settings (settings, size, &icon_width, NULL);
++
++ return (gdouble) gdk_pixbuf_get_width (icon) / icon_width;
++}
++
++GdkPixbuf*
++gtk_icon_set_render_icon_internal (GtkIconSet *icon_set,
++ GtkStyle *style,
++ GtkTextDirection direction,
++ GtkStateType state,
++ GtkIconSize size,
++ GtkWidget *widget,
++ const char *detail,
++ gboolean scale_requested,
++ gdouble *real_scale)
++{
++ GdkPixbuf *icon;
++
++ if (real_scale)
++ *real_scale = 1;
++
++ if (icon_set->sources == NULL)
++ return render_fallback_image (style, direction, state, size, widget, detail);
++
++ if (detail == NULL)
++ {
++ icon = find_in_cache (icon_set, style, direction,
++ state, size);
++
++ if (icon)
++ {
++ g_object_ref (icon);
++
++ if (scale_requested && real_scale)
++ *real_scale = _get_real_scale (widget, style, size, icon);
++
++ return icon;
++ }
++ }
++
++
++ icon = find_and_render_icon_source (icon_set, style, direction, state, size,
++ widget, detail, scale_requested);
++
++ if (icon == NULL)
++ icon = render_fallback_image (style, direction, state, size, widget, detail);
++
++ if (detail == NULL)
++ add_to_cache (icon_set, style, direction, state, size, icon);
++
++ if (scale_requested && real_scale)
++ *real_scale = _get_real_scale (widget, style, size, icon);
++
++ return icon;
++}
++
+ /**
+ * gtk_icon_set_render_icon:
+ * @icon_set: a #GtkIconSet
+@@ -1631,37 +1739,30 @@ gtk_icon_set_render_icon (GtkIconSet *icon_set,
+ GtkWidget *widget,
+ const char *detail)
+ {
+- GdkPixbuf *icon;
+-
+ g_return_val_if_fail (icon_set != NULL, NULL);
+ g_return_val_if_fail (style == NULL || GTK_IS_STYLE (style), NULL);
+
+- if (icon_set->sources == NULL)
+- return render_fallback_image (style, direction, state, size, widget, detail);
+-
+- if (detail == NULL)
+- {
+- icon = find_in_cache (icon_set, style, direction,
+- state, size);
+-
+- if (icon)
+- {
+- g_object_ref (icon);
+- return icon;
+- }
+- }
+-
+-
+- icon = find_and_render_icon_source (icon_set, style, direction, state, size,
+- widget, detail);
+-
+- if (icon == NULL)
+- icon = render_fallback_image (style, direction, state, size, widget, detail);
++ return gtk_icon_set_render_icon_internal (icon_set, style, direction,
++ state, size, widget, detail,
++ FALSE, NULL);
++}
+
+- if (detail == NULL)
+- add_to_cache (icon_set, style, direction, state, size, icon);
++GdkPixbuf*
++gtk_icon_set_render_icon_scaled (GtkIconSet *icon_set,
++ GtkStyle *style,
++ GtkTextDirection direction,
++ GtkStateType state,
++ GtkIconSize size,
++ GtkWidget *widget,
++ const char *detail,
++ gdouble *real_scale)
++{
++ g_return_val_if_fail (icon_set != NULL, NULL);
++ g_return_val_if_fail (style == NULL || GTK_IS_STYLE (style), NULL);
+
+- return icon;
++ return gtk_icon_set_render_icon_internal (icon_set, style, direction,
++ state, size, widget, detail,
++ TRUE, real_scale);
+ }
+
+ /* Order sources by their "wildness", so that "wilder" sources are
+diff --git a/gtk/gtkiconfactory.h b/gtk/gtkiconfactory.h
+index ea97123..e38f8e6 100644
+--- a/gtk/gtkiconfactory.h
++++ b/gtk/gtkiconfactory.h
+@@ -105,6 +105,11 @@ gboolean gtk_icon_size_lookup_for_settings (GtkSettings *settings,
+ GtkIconSize size,
+ gint *width,
+ gint *height);
++gboolean gtk_icon_size_lookup_scaled (GtkSettings *settings,
++ GtkIconSize size,
++ gdouble scale,
++ gint *width,
++ gint *height);
+
+ GtkIconSize gtk_icon_size_register (const gchar *name,
+ gint width,
+@@ -134,7 +139,14 @@ GdkPixbuf* gtk_icon_set_render_icon (GtkIconSet *icon_set,
+ GtkIconSize size,
+ GtkWidget *widget,
+ const char *detail);
+-
++GdkPixbuf* gtk_icon_set_render_icon_scaled (GtkIconSet *icon_set,
++ GtkStyle *style,
++ GtkTextDirection direction,
++ GtkStateType state,
++ GtkIconSize size,
++ GtkWidget *widget,
++ const char *detail,
++ gdouble *real_scale);
+
+ void gtk_icon_set_add_source (GtkIconSet *icon_set,
+ const GtkIconSource *source);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From de10316fb1045f610072ef7c2addbe969e0fe617 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Tue, 12 Feb 2013 14:01:15 +0100
+Subject: [PATCH 57/68] widget: Add _scaled() variants for icon rendering
+
+Likewise to the iconfactory changes, this API allows to
+deal with stock icons at sizes that are suitable for
+gdk_window_get_scale_factor()
+---
+ gtk/gtkwidget.c | 27 +++++++++++++++++++++++++++
+ gtk/gtkwidget.h | 5 +++++
+ 2 files changed, 32 insertions(+)
+
+diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
+index c812bb9..f093c39 100644
+--- a/gtk/gtkwidget.c
++++ b/gtk/gtkwidget.c
+@@ -7502,6 +7502,33 @@ gtk_widget_render_icon (GtkWidget *widget,
+ return retval;
+ }
+
++GdkPixbuf*
++gtk_widget_render_icon_scaled (GtkWidget *widget,
++ const gchar *stock_id,
++ GtkIconSize size,
++ const gchar *detail,
++ gdouble *real_scale)
++{
++ GtkIconSet *icon_set;
++ GdkPixbuf *retval;
++
++ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
++ g_return_val_if_fail (stock_id != NULL, NULL);
++ g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
++
++ gtk_widget_ensure_style (widget);
++
++ icon_set = gtk_style_lookup_icon_set (widget->style, stock_id);
++
++ if (icon_set == NULL)
++ return NULL;
++
++ return gtk_icon_set_render_icon_scaled (icon_set, widget->style,
++ gtk_widget_get_direction (widget),
++ gtk_widget_get_state (widget),
++ size, widget, detail, real_scale);
++}
++
+ /**
+ * gtk_widget_set_parent_window:
+ * @widget: a #GtkWidget.
+diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
+index cee16cd..f7ebf9b 100644
+--- a/gtk/gtkwidget.h
++++ b/gtk/gtkwidget.h
+@@ -1194,6 +1194,11 @@ GdkPixbuf *gtk_widget_render_icon (GtkWidget *widget,
+ const gchar *stock_id,
+ GtkIconSize size,
+ const gchar *detail);
++GdkPixbuf *gtk_widget_render_icon_scaled (GtkWidget *widget,
++ const gchar *stock_id,
++ GtkIconSize size,
++ const gchar *detail,
++ gdouble *real_scale);
+
+ /* handle composite names for GTK_COMPOSITE_CHILD widgets,
+ * the returned name is newly allocated.
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 75299b2806b1bed9e14081f2e108afb39e31896e Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlos@lanedo.com>
+Date: Fri, 10 May 2013 18:06:00 +0200
+Subject: [PATCH 58/68] icontheme: Add support for high resolution icons
+
+An optional OutputScale integer key has been added to index.theme
+subdirs description, so icon themes may provide icons that are
+more suitable to render at a (typically 2x) integer upscaled
+resolution. This way it is possible to make eg. a 16x16@2x icon has a
+real size of 32x32, but contains a similar level of detail to the
+16x16 icon so things don't look any more cluttered on high-dpi
+screens.
+
+The pixbuf lookup has changed so it prefers a minimal scale change
+that yields the minimal real size difference, so if looking up for
+a 16x16 icon at 2x, it would first prefer 16x16@2x, then 32x32, and
+then any other icon that's closest to match
+
+There is now *_for_scale() variants for all GtkIconTheme ways
+to directly or indirectly fetch a GdkPixbuf.
+---
+ gtk/gtkicontheme.c | 152 ++++++++++++++++++++++++++++++++++++++++------------
+ gtk/gtkicontheme.h | 17 +++++-
+ 2 files changed, 135 insertions(+), 34 deletions(-)
+
+diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c
+index bf81546..500f0ab 100644
+--- a/gtk/gtkicontheme.c
++++ b/gtk/gtkicontheme.c
+@@ -168,6 +168,7 @@ typedef struct
+ int min_size;
+ int max_size;
+ int threshold;
++ int scale;
+
+ char *dir;
+ char *subdir;
+@@ -206,6 +207,7 @@ static void theme_destroy (IconTheme *theme);
+ static GtkIconInfo *theme_lookup_icon (IconTheme *theme,
+ const char *icon_name,
+ int size,
++ gdouble scale,
+ gboolean allow_svg,
+ gboolean use_default_icons);
+ static void theme_list_icons (IconTheme *theme,
+@@ -1161,11 +1163,11 @@ _gtk_icon_theme_ensure_builtin_cache (void)
+ IconThemeDir *dir;
+ static IconThemeDir dirs[5] =
+ {
+- { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL },
+- { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1, NULL, NULL, NULL },
+- { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL },
+- { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL },
+- { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL }
++ { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, 1, NULL, "16", -1, NULL, NULL, NULL },
++ { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, 1, NULL, "20", -1, NULL, NULL, NULL },
++ { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, 1, NULL, "24", -1, NULL, NULL, NULL },
++ { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, 1, NULL, "32", -1, NULL, NULL, NULL },
++ { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, 1, NULL, "48", -1, NULL, NULL, NULL }
+ };
+ gint i;
+
+@@ -1242,6 +1244,7 @@ static GtkIconInfo *
+ choose_icon (GtkIconTheme *icon_theme,
+ const gchar *icon_names[],
+ gint size,
++ gdouble scale,
+ GtkIconLookupFlags flags)
+ {
+ GtkIconThemePrivate *priv;
+@@ -1271,7 +1274,7 @@ choose_icon (GtkIconTheme *icon_theme,
+
+ for (i = 0; icon_names[i]; i++)
+ {
+- icon_info = theme_lookup_icon (theme, icon_names[i], size, allow_svg, use_builtin);
++ icon_info = theme_lookup_icon (theme, icon_names[i], size, scale, allow_svg, use_builtin);
+ if (icon_info)
+ goto out;
+ }
+@@ -1400,12 +1403,32 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
+ gint size,
+ GtkIconLookupFlags flags)
+ {
++ g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
++ g_return_val_if_fail (icon_name != NULL, NULL);
++ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
++ (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
++
++ GTK_NOTE (ICONTHEME,
++ g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
++
++ return gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name,
++ size, 1, flags);
++}
++
++GtkIconInfo *
++gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme *icon_theme,
++ const gchar *icon_name,
++ gint size,
++ gdouble scale,
++ GtkIconLookupFlags flags)
++{
+ GtkIconInfo *info;
+
+ g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+ g_return_val_if_fail (icon_name != NULL, NULL);
+ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+ (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
++ g_return_val_if_fail (scale >= 1, NULL);
+
+ GTK_NOTE (ICONTHEME,
+ g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
+@@ -1431,7 +1454,7 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
+ }
+ names[dashes + 1] = NULL;
+
+- info = choose_icon (icon_theme, (const gchar **) names, size, flags);
++ info = choose_icon (icon_theme, (const gchar **) names, size, scale, flags);
+
+ g_strfreev (names);
+ }
+@@ -1442,7 +1465,7 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
+ names[0] = icon_name;
+ names[1] = NULL;
+
+- info = choose_icon (icon_theme, names, size, flags);
++ info = choose_icon (icon_theme, names, size, scale, flags);
+ }
+
+ return info;
+@@ -1483,9 +1506,26 @@ gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme,
+ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+ (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
+
+- return choose_icon (icon_theme, icon_names, size, flags);
++ return choose_icon (icon_theme, icon_names, size, 1, flags);
++}
++
++GtkIconInfo *
++gtk_icon_theme_choose_icon_for_scale (GtkIconTheme *icon_theme,
++ const gchar *icon_names[],
++ gint size,
++ gdouble scale,
++ GtkIconLookupFlags flags)
++{
++ g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
++ g_return_val_if_fail (icon_names != NULL, NULL);
++ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
++ (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
++ g_return_val_if_fail (scale >= 1, NULL);
++
++ return choose_icon (icon_theme, icon_names, size, scale, flags);
+ }
+
++
+ /* Error quark */
+ GQuark
+ gtk_icon_theme_error_quark (void)
+@@ -1529,6 +1569,24 @@ gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
+ GtkIconLookupFlags flags,
+ GError **error)
+ {
++ g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
++ g_return_val_if_fail (icon_name != NULL, NULL);
++ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
++ (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
++ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++ return gtk_icon_theme_load_icon_for_scale (icon_theme, icon_name,
++ size, 1, flags, error);
++}
++
++GdkPixbuf *
++gtk_icon_theme_load_icon_for_scale (GtkIconTheme *icon_theme,
++ const gchar *icon_name,
++ gint size,
++ gdouble scale,
++ GtkIconLookupFlags flags,
++ GError **error)
++{
+ GtkIconInfo *icon_info;
+ GdkPixbuf *pixbuf = NULL;
+
+@@ -1537,9 +1595,10 @@ gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
+ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+ (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+-
+- icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
+- flags | GTK_ICON_LOOKUP_USE_BUILTIN);
++ g_return_val_if_fail (scale >= 1, NULL);
++
++ icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name, size, scale,
++ flags | GTK_ICON_LOOKUP_USE_BUILTIN);
+ if (!icon_info)
+ {
+ g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND,
+@@ -1976,31 +2035,42 @@ theme_dir_destroy (IconThemeDir *dir)
+ }
+
+ static int
+-theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
++theme_dir_size_difference (IconThemeDir *dir,
++ int size,
++ gdouble scale,
++ gboolean *smaller,
++ gint *scale_diff)
+ {
++ int scaled_size, scaled_dir_size;
+ int min, max;
++
++ scaled_size = size * scale;
++ scaled_dir_size = dir->size * dir->scale;
++ *scale_diff = abs (scale - dir->scale);
++
+ switch (dir->type)
+ {
+ case ICON_THEME_DIR_FIXED:
+- *smaller = size < dir->size;
+- return abs (size - dir->size);
++ *smaller = scaled_size < scaled_dir_size;
++ return abs (scaled_size - scaled_dir_size);
+ break;
+ case ICON_THEME_DIR_SCALABLE:
+- *smaller = size < dir->min_size;
+- if (size < dir->min_size)
+- return dir->min_size - size;
+- if (size > dir->max_size)
+- return size - dir->max_size;
++ *scale_diff = 0;
++ *smaller = scaled_size < (dir->min_size * dir->scale);
++ if (scaled_size < (dir->min_size * dir->scale))
++ return (dir->min_size * dir->scale) - scaled_size;
++ if (size > (dir->max_size * dir->scale))
++ return scaled_size - (dir->max_size * dir->scale);
+ return 0;
+ break;
+ case ICON_THEME_DIR_THRESHOLD:
+- min = dir->size - dir->threshold;
+- max = dir->size + dir->threshold;
+- *smaller = size < min;
+- if (size < min)
+- return min - size;
+- if (size > max)
+- return size - max;
++ min = (dir->size - dir->threshold) * dir->scale;
++ max = (dir->size + dir->threshold) * dir->scale;
++ *smaller = scaled_size < min;
++ if (scaled_size < min)
++ return min - scaled_size;
++ if (scaled_size > max)
++ return scaled_size - max;
+ return 0;
+ break;
+ case ICON_THEME_DIR_UNTHEMED:
+@@ -2091,6 +2161,7 @@ static GtkIconInfo *
+ theme_lookup_icon (IconTheme *theme,
+ const char *icon_name,
+ int size,
++ gdouble scale,
+ gboolean allow_svg,
+ gboolean use_builtin)
+ {
+@@ -2098,11 +2169,13 @@ theme_lookup_icon (IconTheme *theme,
+ IconThemeDir *dir, *min_dir;
+ char *file;
+ int min_difference, difference;
++ int min_scale_diff, scale_diff;
+ BuiltinIcon *closest_builtin = NULL;
+ gboolean smaller, has_larger, match;
+ IconSuffix suffix;
+
+ min_difference = G_MAXINT;
++ min_scale_diff = G_MAXINT;
+ min_dir = NULL;
+ has_larger = FALSE;
+ match = FALSE;
+@@ -2135,9 +2208,10 @@ theme_lookup_icon (IconTheme *theme,
+ suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
+ if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
+ {
+- difference = theme_dir_size_difference (dir, size, &smaller);
++ difference = theme_dir_size_difference (dir, size, scale,
++ &smaller, &scale_diff);
+
+- if (difference == 0)
++ if (difference == 0 && scale_diff == 0)
+ {
+ if (dir->type == ICON_THEME_DIR_SCALABLE)
+ {
+@@ -2156,13 +2230,15 @@ theme_lookup_icon (IconTheme *theme,
+ * going and look for a closer match
+ */
+ difference = abs (size - dir->size);
+- if (!match || difference < min_difference)
++ if (!match ||
++ (scale_diff <= min_scale_diff && difference < min_difference))
+ {
+ match = TRUE;
+ min_difference = difference;
++ min_scale_diff = scale_diff;
+ min_dir = dir;
+ }
+- if (difference == 0)
++ if (difference == 0 && scale_diff == 0)
+ break;
+ }
+ }
+@@ -2171,18 +2247,20 @@ theme_lookup_icon (IconTheme *theme,
+ {
+ if (!has_larger)
+ {
+- if (difference < min_difference || smaller)
++ if ((scale_diff <= min_scale_diff && difference < min_difference) || (scale_diff == 0 && smaller))
+ {
+ min_difference = difference;
++ min_scale_diff = scale_diff;
+ min_dir = dir;
+ has_larger = smaller;
+ }
+ }
+ else
+ {
+- if (difference < min_difference && smaller)
++ if ((scale_diff <= min_scale_diff && difference < min_difference) && (scale_diff == 0 && smaller))
+ {
+ min_difference = difference;
++ min_scale_diff = scale_diff;
+ min_dir = dir;
+ }
+ }
+@@ -2484,6 +2562,7 @@ theme_subdir_load (GtkIconTheme *icon_theme,
+ char *full_dir;
+ GError *error = NULL;
+ IconThemeDirMtime *dir_mtime;
++ int scale;
+
+ size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
+ if (error)
+@@ -2543,6 +2622,11 @@ theme_subdir_load (GtkIconTheme *icon_theme,
+ error = NULL;
+ }
+
++ if (g_key_file_has_key (theme_file, subdir, "OutputScale", NULL))
++ scale = g_key_file_get_integer (theme_file, subdir, "OutputScale", NULL);
++ else
++ scale = 1;
++
+ for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
+ {
+ dir_mtime = (IconThemeDirMtime *)d->data;
+@@ -2571,6 +2655,8 @@ theme_subdir_load (GtkIconTheme *icon_theme,
+ dir->dir = full_dir;
+ dir->icon_data = NULL;
+ dir->subdir = g_strdup (subdir);
++ dir->scale = scale;
++
+ if (dir_mtime->cache != NULL)
+ {
+ dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
+diff --git a/gtk/gtkicontheme.h b/gtk/gtkicontheme.h
+index 3611c74..9b29f96 100644
+--- a/gtk/gtkicontheme.h
++++ b/gtk/gtkicontheme.h
+@@ -141,16 +141,31 @@ GtkIconInfo * gtk_icon_theme_lookup_icon (GtkIconTheme
+ const gchar *icon_name,
+ gint size,
+ GtkIconLookupFlags flags);
++GtkIconInfo * gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme *icon_theme,
++ const gchar *icon_name,
++ gint size,
++ gdouble scale,
++ GtkIconLookupFlags flags);
+ GtkIconInfo * gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme,
+ const gchar *icon_names[],
+ gint size,
+ GtkIconLookupFlags flags);
++GtkIconInfo * gtk_icon_theme_choose_icon_for_scale (GtkIconTheme *icon_theme,
++ const gchar *icon_names[],
++ gint size,
++ gdouble scale,
++ GtkIconLookupFlags flags);
+ GdkPixbuf * gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
+ const gchar *icon_name,
+ gint size,
+ GtkIconLookupFlags flags,
+ GError **error);
+-
++GdkPixbuf * gtk_icon_theme_load_icon_for_scale (GtkIconTheme *icon_theme,
++ const gchar *icon_name,
++ gint size,
++ gdouble scale,
++ GtkIconLookupFlags flags,
++ GError **error);
+ GtkIconInfo * gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme,
+ GIcon *icon,
+ gint size,
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 431d1225b153c1a1389686920aed1d26ff3218b2 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlos@lanedo.com>
+Date: Fri, 10 May 2013 18:24:26 +0200
+Subject: [PATCH 59/68] iconfactory: Add scale info to GtkIconSource
+
+GtkIconSource now has notions knows about scale, so it can correctly
+fetch the icon at the right scale for ICON_NAME source types.
+
+All default stock icons have been made to have a wildcarded scale,
+so it's up to the GtkIconTheme to do the scaling business.
+---
+ gtk/gtkiconfactory.c | 140 ++++++++++++++++++++++++++++++++------------------
+ gtk/gtkiconfactory.h | 7 ++-
+ 2 files changed, 97 insertions(+), 50 deletions(-)
+
+diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c
+index 0dc31e6..41d1ee7 100644
+--- a/gtk/gtkiconfactory.c
++++ b/gtk/gtkiconfactory.c
+@@ -66,6 +66,7 @@ struct _GtkIconSource
+ GtkTextDirection direction;
+ GtkStateType state;
+ GtkIconSize size;
++ gdouble scale;
+
+ /* If TRUE, then the parameter is wildcarded, and the above
+ * fields should be ignored. If FALSE, the parameter is
+@@ -74,6 +75,7 @@ struct _GtkIconSource
+ guint any_direction : 1;
+ guint any_state : 1;
+ guint any_size : 1;
++ guint any_scale : 1;
+
+ #if defined (G_OS_WIN32) && !defined (_WIN64)
+ /* System codepage version of filename, for DLL ABI backward
+@@ -106,10 +108,10 @@ static GtkIconSize icon_size_register_intern (const gchar *name,
+ gint width,
+ gint height);
+
+-#define GTK_ICON_SOURCE_INIT(any_direction, any_state, any_size) \
++#define GTK_ICON_SOURCE_INIT(any_direction, any_state, any_size, any_scale) \
+ { GTK_ICON_SOURCE_EMPTY, { NULL }, NULL, \
+- 0, 0, 0, \
+- any_direction, any_state, any_size }
++ 0, 0, 0, 1, \
++ any_direction, any_state, any_size, any_scale }
+
+ G_DEFINE_TYPE_WITH_CODE (GtkIconFactory, gtk_icon_factory, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+@@ -349,7 +351,7 @@ register_stock_icon (GtkIconFactory *factory,
+ const gchar *icon_name)
+ {
+ GtkIconSet *set = gtk_icon_set_new ();
+- GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
++ GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE, TRUE);
+
+ source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME;
+ source.source.icon_name = (gchar *)icon_name;
+@@ -366,7 +368,7 @@ register_bidi_stock_icon (GtkIconFactory *factory,
+ const gchar *icon_name)
+ {
+ GtkIconSet *set = gtk_icon_set_new ();
+- GtkIconSource source = GTK_ICON_SOURCE_INIT (FALSE, TRUE, TRUE);
++ GtkIconSource source = GTK_ICON_SOURCE_INIT (FALSE, TRUE, TRUE, TRUE);
+
+ source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME;
+ source.source.icon_name = (gchar *)icon_name;
+@@ -1094,12 +1096,14 @@ static GdkPixbuf *find_in_cache (GtkIconSet *icon_set,
+ GtkStyle *style,
+ GtkTextDirection direction,
+ GtkStateType state,
+- GtkIconSize size);
++ GtkIconSize size,
++ gdouble scale);
+ static void add_to_cache (GtkIconSet *icon_set,
+ GtkStyle *style,
+ GtkTextDirection direction,
+ GtkStateType state,
+ GtkIconSize size,
++ gdouble scale,
+ GdkPixbuf *pixbuf);
+ /* Clear icon set contents, drop references to all contained
+ * GdkPixbuf objects and forget all GtkIconSources. Used to
+@@ -1179,7 +1183,7 @@ gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf)
+ {
+ GtkIconSet *set;
+
+- GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
++ GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE, TRUE);
+
+ g_return_val_if_fail (pixbuf != NULL, NULL);
+
+@@ -1319,6 +1323,7 @@ find_best_matching_source (GtkIconSet *icon_set,
+ GtkTextDirection direction,
+ GtkStateType state,
+ GtkIconSize size,
++ gdouble scale,
+ GSList *failed)
+ {
+ GtkIconSource *source;
+@@ -1340,7 +1345,8 @@ find_best_matching_source (GtkIconSet *icon_set,
+
+ if ((s->any_direction || (s->direction == direction)) &&
+ (s->any_state || (s->state == state)) &&
+- (s->any_size || size == (GtkIconSize)-1 || (sizes_equivalent (size, s->size))))
++ (s->any_size || size == (GtkIconSize)-1 || (sizes_equivalent (size, s->size))) &&
++ (s->any_scale || (s->scale == scale)))
+ {
+ if (!g_slist_find (failed, s))
+ {
+@@ -1392,7 +1398,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ GtkIconSize size,
+ GtkWidget *widget,
+ const char *detail,
+- gboolean scale_requested)
++ gdouble scale)
+ {
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *tmp_pixbuf;
+@@ -1403,7 +1409,6 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ gint width, height, pixel_size;
+ gint *sizes, *s, dist;
+ GError *error = NULL;
+- gdouble scale = 1;
+
+ if (widget && gtk_widget_has_screen (widget))
+ screen = gtk_widget_get_screen (widget);
+@@ -1419,14 +1424,6 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ icon_theme = gtk_icon_theme_get_for_screen (screen);
+ settings = gtk_settings_get_for_screen (screen);
+
+- if (scale_requested && widget)
+- {
+- if (!widget->window)
+- gtk_widget_realize (widget);
+-
+- scale = gdk_window_get_scale_factor (widget->window);
+- }
+-
+ if (!gtk_icon_size_lookup_for_settings (settings, size, &width, &height))
+ {
+ if (size == (GtkIconSize)-1)
+@@ -1469,7 +1466,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ }
+ }
+
+- pixel_size = MIN (width, height) * scale;
++ pixel_size = MIN (width, height);
+
+ if (icon_source->direction != GTK_TEXT_DIR_NONE)
+ {
+@@ -1483,9 +1480,10 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ names[1] = icon_source->source.icon_name;
+ names[2] = NULL;
+
+- info = gtk_icon_theme_choose_icon (icon_theme,
+- names,
+- pixel_size, GTK_ICON_LOOKUP_USE_BUILTIN);
++ info = gtk_icon_theme_choose_icon_for_scale (icon_theme,
++ names,
++ pixel_size, scale,
++ GTK_ICON_LOOKUP_USE_BUILTIN);
+ g_free (name_with_dir);
+ if (info)
+ {
+@@ -1495,10 +1493,10 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
+ }
+ else
+ {
+- tmp_pixbuf = gtk_icon_theme_load_icon (icon_theme,
+- icon_source->source.icon_name,
+- pixel_size, 0,
+- &error);
++ tmp_pixbuf = gtk_icon_theme_load_icon_for_scale (icon_theme,
++ icon_source->source.icon_name,
++ pixel_size, scale, 0,
++ &error);
+ }
+
+ if (!tmp_pixbuf)
+@@ -1534,7 +1532,7 @@ find_and_render_icon_source (GtkIconSet *icon_set,
+ GtkIconSize size,
+ GtkWidget *widget,
+ const char *detail,
+- gboolean scale_requested)
++ gdouble scale)
+ {
+ GSList *failed = NULL;
+ GdkPixbuf *pixbuf = NULL;
+@@ -1551,7 +1549,7 @@ find_and_render_icon_source (GtkIconSet *icon_set,
+ */
+ while (pixbuf == NULL)
+ {
+- GtkIconSource *source = find_best_matching_source (icon_set, direction, state, size, failed);
++ GtkIconSource *source = find_best_matching_source (icon_set, direction, state, size, scale, failed);
+
+ if (source == NULL)
+ break;
+@@ -1576,7 +1574,7 @@ find_and_render_icon_source (GtkIconSet *icon_set,
+ case GTK_ICON_SOURCE_STATIC_ICON_NAME:
+ pixbuf = render_icon_name_pixbuf (source, style,
+ direction, state, size,
+- widget, detail, scale_requested);
++ widget, detail, scale);
+ if (!pixbuf)
+ failed = g_slist_prepend (failed, source);
+ break;
+@@ -1601,7 +1599,7 @@ render_fallback_image (GtkStyle *style,
+ const char *detail)
+ {
+ /* This icon can be used for any direction/state/size */
+- static GtkIconSource fallback_source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
++ static GtkIconSource fallback_source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE, TRUE);
+
+ if (fallback_source.type == GTK_ICON_SOURCE_EMPTY)
+ {
+@@ -1652,7 +1650,7 @@ _get_real_scale (GtkWidget *widget,
+ settings = gtk_settings_get_for_screen (screen);
+ gtk_icon_size_lookup_for_settings (settings, size, &icon_width, NULL);
+
+- return (gdouble) gdk_pixbuf_get_width (icon) / icon_width;
++ return round ((gdouble) gdk_pixbuf_get_width (icon) / icon_width);
+ }
+
+ GdkPixbuf*
+@@ -1663,28 +1661,23 @@ gtk_icon_set_render_icon_internal (GtkIconSet *icon_set,
+ GtkIconSize size,
+ GtkWidget *widget,
+ const char *detail,
+- gboolean scale_requested,
+- gdouble *real_scale)
++ gdouble *scale)
+ {
+ GdkPixbuf *icon;
+
+- if (real_scale)
+- *real_scale = 1;
+-
+ if (icon_set->sources == NULL)
+ return render_fallback_image (style, direction, state, size, widget, detail);
+
+ if (detail == NULL)
+ {
+ icon = find_in_cache (icon_set, style, direction,
+- state, size);
++ state, size, *scale);
+
+ if (icon)
+ {
+ g_object_ref (icon);
+
+- if (scale_requested && real_scale)
+- *real_scale = _get_real_scale (widget, style, size, icon);
++ *scale = _get_real_scale (widget, style, size, icon);
+
+ return icon;
+ }
+@@ -1692,16 +1685,15 @@ gtk_icon_set_render_icon_internal (GtkIconSet *icon_set,
+
+
+ icon = find_and_render_icon_source (icon_set, style, direction, state, size,
+- widget, detail, scale_requested);
++ widget, detail, *scale);
+
+ if (icon == NULL)
+ icon = render_fallback_image (style, direction, state, size, widget, detail);
+
+- if (detail == NULL)
+- add_to_cache (icon_set, style, direction, state, size, icon);
++ *scale = _get_real_scale (widget, style, size, icon);
+
+- if (scale_requested && real_scale)
+- *real_scale = _get_real_scale (widget, style, size, icon);
++ if (detail == NULL)
++ add_to_cache (icon_set, style, direction, state, size, *scale, icon);
+
+ return icon;
+ }
+@@ -1739,12 +1731,14 @@ gtk_icon_set_render_icon (GtkIconSet *icon_set,
+ GtkWidget *widget,
+ const char *detail)
+ {
++ gdouble scale = 1;
++
+ g_return_val_if_fail (icon_set != NULL, NULL);
+ g_return_val_if_fail (style == NULL || GTK_IS_STYLE (style), NULL);
+
+ return gtk_icon_set_render_icon_internal (icon_set, style, direction,
+ state, size, widget, detail,
+- FALSE, NULL);
++ &scale);
+ }
+
+ GdkPixbuf*
+@@ -1755,19 +1749,22 @@ gtk_icon_set_render_icon_scaled (GtkIconSet *icon_set,
+ GtkIconSize size,
+ GtkWidget *widget,
+ const char *detail,
+- gdouble *real_scale)
++ gdouble *scale)
+ {
+ g_return_val_if_fail (icon_set != NULL, NULL);
+ g_return_val_if_fail (style == NULL || GTK_IS_STYLE (style), NULL);
++ g_return_val_if_fail (scale != NULL, NULL);
++
++ *scale = MAX (*scale, 1);
+
+ return gtk_icon_set_render_icon_internal (icon_set, style, direction,
+ state, size, widget, detail,
+- TRUE, real_scale);
++ scale);
+ }
+
+ /* Order sources by their "wildness", so that "wilder" sources are
+ * greater than "specific" sources; for determining ordering,
+- * direction beats state beats size.
++ * direction beats state beats size beats scale.
+ */
+
+ static int
+@@ -1788,6 +1785,10 @@ icon_source_compare (gconstpointer ap, gconstpointer bp)
+ return -1;
+ else if (a->any_size && !b->any_size)
+ return 1;
++ else if (!a->any_scale && b->any_scale)
++ return -1;
++ else if (a->any_scale && !b->any_scale)
++ return 1;
+ else
+ return 0;
+ }
+@@ -1965,10 +1966,12 @@ gtk_icon_source_new (void)
+ src->direction = GTK_TEXT_DIR_NONE;
+ src->size = GTK_ICON_SIZE_INVALID;
+ src->state = GTK_STATE_NORMAL;
++ src->scale = 1;
+
+ src->any_direction = TRUE;
+ src->any_state = TRUE;
+ src->any_size = TRUE;
++ src->any_scale = TRUE;
+
+ return src;
+ }
+@@ -2319,6 +2322,15 @@ gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
+ source->any_size = setting != FALSE;
+ }
+
++void
++gtk_icon_source_set_scale_wildcarded (GtkIconSource *source,
++ gboolean setting)
++{
++ g_return_if_fail (source != NULL);
++
++ source->any_scale = setting != FALSE;
++}
++
+ /**
+ * gtk_icon_source_get_size_wildcarded:
+ * @source: a #GtkIconSource
+@@ -2367,6 +2379,14 @@ gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source)
+ return source->any_direction;
+ }
+
++gboolean
++gtk_icon_source_get_scale_wildcarded (const GtkIconSource *source)
++{
++ g_return_val_if_fail (source != NULL, TRUE);
++
++ return source->any_scale;
++}
++
+ /**
+ * gtk_icon_source_set_direction:
+ * @source: a #GtkIconSource
+@@ -2433,6 +2453,15 @@ gtk_icon_source_set_size (GtkIconSource *source,
+ source->size = size;
+ }
+
++void
++gtk_icon_source_set_scale (GtkIconSource *source,
++ gdouble scale)
++{
++ g_return_if_fail (source != NULL);
++
++ source->scale = scale;
++}
++
+ /**
+ * gtk_icon_source_get_direction:
+ * @source: a #GtkIconSource
+@@ -2486,6 +2515,14 @@ gtk_icon_source_get_size (const GtkIconSource *source)
+ return source->size;
+ }
+
++gdouble
++gtk_icon_source_get_scale (const GtkIconSource *source)
++{
++ g_return_val_if_fail (source != NULL, 0);
++
++ return source->scale;
++}
++
+ #define NUM_CACHED_ICONS 8
+
+ typedef struct _CachedIcon CachedIcon;
+@@ -2499,6 +2536,7 @@ struct _CachedIcon
+ GtkTextDirection direction;
+ GtkStateType state;
+ GtkIconSize size;
++ gdouble scale;
+
+ GdkPixbuf *pixbuf;
+ };
+@@ -2529,7 +2567,8 @@ find_in_cache (GtkIconSet *icon_set,
+ GtkStyle *style,
+ GtkTextDirection direction,
+ GtkStateType state,
+- GtkIconSize size)
++ GtkIconSize size,
++ gdouble scale)
+ {
+ GSList *tmp_list;
+ GSList *prev;
+@@ -2545,6 +2584,7 @@ find_in_cache (GtkIconSet *icon_set,
+ if (icon->style == style &&
+ icon->direction == direction &&
+ icon->state == state &&
++ icon->scale == scale &&
+ (size == (GtkIconSize)-1 || icon->size == size))
+ {
+ if (prev)
+@@ -2571,6 +2611,7 @@ add_to_cache (GtkIconSet *icon_set,
+ GtkTextDirection direction,
+ GtkStateType state,
+ GtkIconSize size,
++ gdouble scale,
+ GdkPixbuf *pixbuf)
+ {
+ CachedIcon *icon;
+@@ -2595,6 +2636,7 @@ add_to_cache (GtkIconSet *icon_set,
+ icon->direction = direction;
+ icon->state = state;
+ icon->size = size;
++ icon->scale = scale;
+ icon->pixbuf = pixbuf;
+
+ if (icon->style)
+diff --git a/gtk/gtkiconfactory.h b/gtk/gtkiconfactory.h
+index e38f8e6..d646ed9 100644
+--- a/gtk/gtkiconfactory.h
++++ b/gtk/gtkiconfactory.h
+@@ -177,19 +177,24 @@ void gtk_icon_source_set_state_wildcarded (GtkIconSource *
+ gboolean setting);
+ void gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
+ gboolean setting);
++void gtk_icon_source_set_scale_wildcarded (GtkIconSource *source,
++ gboolean setting);
+ gboolean gtk_icon_source_get_size_wildcarded (const GtkIconSource *source);
+ gboolean gtk_icon_source_get_state_wildcarded (const GtkIconSource *source);
+ gboolean gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source);
++gboolean gtk_icon_source_get_scale_wildcarded (const GtkIconSource *source);
+ void gtk_icon_source_set_direction (GtkIconSource *source,
+ GtkTextDirection direction);
+ void gtk_icon_source_set_state (GtkIconSource *source,
+ GtkStateType state);
+ void gtk_icon_source_set_size (GtkIconSource *source,
+ GtkIconSize size);
++void gtk_icon_source_set_scale (GtkIconSource *source,
++ gdouble scale);
+ GtkTextDirection gtk_icon_source_get_direction (const GtkIconSource *source);
+ GtkStateType gtk_icon_source_get_state (const GtkIconSource *source);
+ GtkIconSize gtk_icon_source_get_size (const GtkIconSource *source);
+-
++gdouble gtk_icon_source_get_scale (const GtkIconSource *source);
+
+ /* ignore this */
+ void _gtk_icon_set_invalidate_caches (void);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 2c894a76c7fb002ed276d1d82552fb4e91bb3a78 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlos@lanedo.com>
+Date: Fri, 10 May 2013 18:29:05 +0200
+Subject: [PATCH 60/68] iconfactory: Add gtk_cairo_set_source_icon_set()
+
+As GtkIconSet remains a good candidate to delay pixbuf lookup until
+the moment of rendering, a cairo helper function has been added
+to easy enabling support for high resolution icons.
+---
+ gtk/gtkiconfactory.c | 32 ++++++++++++++++++++++++++++++++
+ gtk/gtkiconfactory.h | 9 +++++++++
+ 2 files changed, 41 insertions(+)
+
+diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c
+index 41d1ee7..ab9a212 100644
+--- a/gtk/gtkiconfactory.c
++++ b/gtk/gtkiconfactory.c
+@@ -3127,5 +3127,37 @@ gtk_icon_source_get_filename (const GtkIconSource *source)
+
+ #endif
+
++void
++gtk_cairo_set_source_icon_set (cairo_t *cr,
++ GtkWidget *widget,
++ GtkIconSet *icon_set,
++ GtkIconSize size,
++ gdouble scale,
++ gdouble icon_x,
++ gdouble icon_y)
++{
++ cairo_pattern_t *pattern;
++ cairo_matrix_t matrix;
++ GdkPixbuf *pixbuf;
++
++ g_return_if_fail (GTK_IS_WIDGET (widget));
++ g_return_if_fail (cr != NULL);
++ g_return_if_fail (icon_set != NULL);
++
++ pixbuf = gtk_icon_set_render_icon_scaled (icon_set,
++ gtk_widget_get_style (widget),
++ gtk_widget_get_direction (widget),
++ gtk_widget_get_state (widget),
++ size, widget, NULL, &scale);
++ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
++
++ pattern = cairo_get_source (cr);
++ cairo_matrix_init_scale (&matrix, scale, scale);
++ cairo_matrix_translate (&matrix, -icon_x, -icon_y);
++ cairo_pattern_set_matrix (pattern, &matrix);
++
++ g_object_unref (pixbuf);
++}
++
+ #define __GTK_ICON_FACTORY_C__
+ #include "gtkaliasdef.c"
+diff --git a/gtk/gtkiconfactory.h b/gtk/gtkiconfactory.h
+index d646ed9..67e318a 100644
+--- a/gtk/gtkiconfactory.h
++++ b/gtk/gtkiconfactory.h
+@@ -196,6 +196,15 @@ GtkStateType gtk_icon_source_get_state (const GtkIconSource *
+ GtkIconSize gtk_icon_source_get_size (const GtkIconSource *source);
+ gdouble gtk_icon_source_get_scale (const GtkIconSource *source);
+
++/* Cairo helper */
++void gtk_cairo_set_source_icon_set (cairo_t *cr,
++ GtkWidget *widget,
++ GtkIconSet *icon_set,
++ GtkIconSize size,
++ gdouble scale,
++ gdouble icon_x,
++ gdouble icon_y);
++
+ /* ignore this */
+ void _gtk_icon_set_invalidate_caches (void);
+ GList* _gtk_icon_factory_list_ids (void);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From a7d250bd2126a146d0e5d77c1083a47285d5dfb7 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Tue, 12 Feb 2013 14:03:40 +0100
+Subject: [PATCH 61/68] image: Use scaled icons on windows with a scaling
+ factor
+
+---
+ gtk/gtkimage.c | 112 ++++++++++++++++++++++++++++++++++----------------------
+ 1 file changed, 68 insertions(+), 44 deletions(-)
+
+diff --git a/gtk/gtkimage.c b/gtk/gtkimage.c
+index c35d962..b492d73 100644
+--- a/gtk/gtkimage.c
++++ b/gtk/gtkimage.c
+@@ -1717,9 +1717,11 @@ ensure_pixbuf_for_icon_name (GtkImage *image)
+ }
+ }
+ image->data.name.pixbuf =
+- gtk_icon_theme_load_icon (icon_theme,
+- image->data.name.icon_name,
+- MIN (width, height), flags, &error);
++ gtk_icon_theme_load_icon_for_scale (icon_theme,
++ image->data.name.icon_name,
++ MIN (width, height),
++ gtk_widget_get_scale_factor (GTK_WIDGET (image)),
++ flags, &error);
+ if (image->data.name.pixbuf == NULL)
+ {
+ g_error_free (error);
+@@ -1757,9 +1759,10 @@ ensure_pixbuf_for_gicon (GtkImage *image)
+ width = height = priv->pixel_size;
+ flags |= GTK_ICON_LOOKUP_FORCE_SIZE;
+ }
+- else if (!gtk_icon_size_lookup_for_settings (settings,
+- image->icon_size,
+- &width, &height))
++ else if (!gtk_icon_size_lookup_scaled (settings,
++ image->icon_size,
++ gtk_widget_get_scale_factor (GTK_WIDGET (image)),
++ &width, &height))
+ {
+ if (image->icon_size == -1)
+ width = height = 48;
+@@ -1848,11 +1851,11 @@ gtk_image_expose (GtkWidget *widget,
+ GdkBitmap *mask;
+ GdkPixbuf *pixbuf;
+ gboolean needs_state_transform;
++ gdouble render_scale = 1.0;
+
+ image = GTK_IMAGE (widget);
+ misc = GTK_MISC (widget);
+ priv = GTK_IMAGE_GET_PRIVATE (image);
+-
+ area = event->area;
+
+ /* For stock items and icon sets, we lazily calculate
+@@ -1954,12 +1957,13 @@ gtk_image_expose (GtkWidget *widget,
+ break;
+
+ case GTK_IMAGE_STOCK:
+- pixbuf = gtk_widget_render_icon (widget,
+- image->data.stock.stock_id,
+- image->icon_size,
+- NULL);
++ render_scale = gtk_widget_get_scale_factor (widget);
++ pixbuf = gtk_widget_render_icon_scaled (widget,
++ image->data.stock.stock_id,
++ image->icon_size,
++ NULL, &render_scale);
+ if (pixbuf)
+- {
++ {
+ image_bound.width = gdk_pixbuf_get_width (pixbuf);
+ image_bound.height = gdk_pixbuf_get_height (pixbuf);
+ }
+@@ -1969,14 +1973,15 @@ gtk_image_expose (GtkWidget *widget,
+ break;
+
+ case GTK_IMAGE_ICON_SET:
++ render_scale = gtk_widget_get_scale_factor (widget);
+ pixbuf =
+- gtk_icon_set_render_icon (image->data.icon_set.icon_set,
+- widget->style,
+- gtk_widget_get_direction (widget),
+- gtk_widget_get_state (widget),
+- image->icon_size,
+- widget,
+- NULL);
++ gtk_icon_set_render_icon_scaled (image->data.icon_set.icon_set,
++ widget->style,
++ gtk_widget_get_direction (widget),
++ gtk_widget_get_state (widget),
++ image->icon_size,
++ widget,
++ NULL, &render_scale);
+
+ if (pixbuf)
+ {
+@@ -2082,17 +2087,35 @@ gtk_image_expose (GtkWidget *widget,
+
+ if (pixbuf)
+ {
+- gdk_draw_pixbuf (widget->window,
+- widget->style->black_gc,
+- pixbuf,
+- image_bound.x - x,
+- image_bound.y - y,
+- image_bound.x,
+- image_bound.y,
+- image_bound.width,
+- image_bound.height,
+- GDK_RGB_DITHER_NORMAL,
+- 0, 0);
++ if (render_scale == 1)
++ {
++ gdk_draw_pixbuf (widget->window,
++ widget->style->black_gc,
++ pixbuf,
++ image_bound.x - x,
++ image_bound.y - y,
++ image_bound.x,
++ image_bound.y,
++ image_bound.width,
++ image_bound.height,
++ GDK_RGB_DITHER_NORMAL,
++ 0, 0);
++ }
++ else
++ {
++ cairo_t *cr = gdk_cairo_create (widget->window);
++
++ gdk_cairo_region (cr, event->region);
++ cairo_clip (cr);
++
++ cairo_scale (cr, 1.0 / render_scale, 1.0 / render_scale);
++ gdk_cairo_set_source_pixbuf (cr, pixbuf,
++ image_bound.x * render_scale,
++ image_bound.y * render_scale);
++
++ cairo_paint (cr);
++ cairo_destroy (cr);
++ }
+ }
+ }
+ else
+@@ -2298,8 +2321,10 @@ gtk_image_calc_size (GtkImage *image)
+ GtkWidget *widget = GTK_WIDGET (image);
+ GdkPixbuf *pixbuf = NULL;
+ GtkImagePrivate *priv;
++ gdouble render_scale;
+
+ priv = GTK_IMAGE_GET_PRIVATE (image);
++ render_scale = gtk_widget_get_scale_factor (widget);
+
+ priv->need_calc_size = 0;
+
+@@ -2311,20 +2336,20 @@ gtk_image_calc_size (GtkImage *image)
+ switch (image->storage_type)
+ {
+ case GTK_IMAGE_STOCK:
+- pixbuf = gtk_widget_render_icon (widget,
+- image->data.stock.stock_id,
+- image->icon_size,
+- NULL);
++ pixbuf = gtk_widget_render_icon_scaled (widget,
++ image->data.stock.stock_id,
++ image->icon_size,
++ NULL, &render_scale);
+ break;
+
+ case GTK_IMAGE_ICON_SET:
+- pixbuf = gtk_icon_set_render_icon (image->data.icon_set.icon_set,
+- widget->style,
+- gtk_widget_get_direction (widget),
+- gtk_widget_get_state (widget),
+- image->icon_size,
+- widget,
+- NULL);
++ pixbuf = gtk_icon_set_render_icon_scaled (image->data.icon_set.icon_set,
++ widget->style,
++ gtk_widget_get_direction (widget),
++ gtk_widget_get_state (widget),
++ image->icon_size,
++ widget,
++ NULL, &render_scale);
+ break;
+ case GTK_IMAGE_ICON_NAME:
+ ensure_pixbuf_for_icon_name (image);
+@@ -2343,9 +2368,8 @@ gtk_image_calc_size (GtkImage *image)
+
+ if (pixbuf)
+ {
+- widget->requisition.width = gdk_pixbuf_get_width (pixbuf) + GTK_MISC (image)->xpad * 2;
+- widget->requisition.height = gdk_pixbuf_get_height (pixbuf) + GTK_MISC (image)->ypad * 2;
+-
++ widget->requisition.width = (gdk_pixbuf_get_width (pixbuf) / render_scale) + GTK_MISC (image)->xpad * 2;
++ widget->requisition.height = (gdk_pixbuf_get_height (pixbuf) / render_scale) + GTK_MISC (image)->ypad * 2;
+ g_object_unref (pixbuf);
+ }
+ }
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 52cba71be7d226b628731ea92c2064bf41f08b54 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Tue, 12 Feb 2013 14:04:09 +0100
+Subject: [PATCH 62/68] cellrendererpixbuf: Use scaled icons on windows with a
+ scale factor
+
+---
+ gtk/gtkcellrendererpixbuf.c | 115 ++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 102 insertions(+), 13 deletions(-)
+
+diff --git a/gtk/gtkcellrendererpixbuf.c b/gtk/gtkcellrendererpixbuf.c
+index f689784..7c767b6 100644
+--- a/gtk/gtkcellrendererpixbuf.c
++++ b/gtk/gtkcellrendererpixbuf.c
+@@ -63,7 +63,8 @@ enum {
+ PROP_STOCK_DETAIL,
+ PROP_FOLLOW_STATE,
+ PROP_ICON_NAME,
+- PROP_GICON
++ PROP_GICON,
++ PROP_ICON_SET
+ };
+
+
+@@ -78,6 +79,8 @@ struct _GtkCellRendererPixbufPrivate
+ gboolean follow_state;
+ gchar *icon_name;
+ GIcon *gicon;
++ GtkIconSet *icon_set;
++ gdouble render_scale;
+ };
+
+ G_DEFINE_TYPE (GtkCellRendererPixbuf, gtk_cell_renderer_pixbuf, GTK_TYPE_CELL_RENDERER)
+@@ -173,6 +176,14 @@ gtk_cell_renderer_pixbuf_class_init (GtkCellRendererPixbufClass *class)
+ NULL,
+ GTK_PARAM_READWRITE));
+
++ g_object_class_install_property (object_class,
++ PROP_ICON_SET,
++ g_param_spec_boxed ("icon-set",
++ P_("Icon set"),
++ P_("Icon set to render the image from"),
++ GTK_TYPE_ICON_SET,
++ GTK_PARAM_READWRITE));
++
+ /**
+ * GtkCellRendererPixbuf:follow-state:
+ *
+@@ -277,6 +288,9 @@ gtk_cell_renderer_pixbuf_get_property (GObject *object,
+ case PROP_GICON:
+ g_value_set_object (value, priv->gicon);
+ break;
++ case PROP_ICON_SET:
++ g_value_set_boxed (value, priv->icon_set);
++ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+@@ -300,6 +314,7 @@ gtk_cell_renderer_pixbuf_set_property (GObject *object,
+ if (cellpixbuf->pixbuf)
+ g_object_unref (cellpixbuf->pixbuf);
+ cellpixbuf->pixbuf = (GdkPixbuf*) g_value_dup_object (value);
++ priv->render_scale = 1;
+ if (cellpixbuf->pixbuf)
+ {
+ if (priv->stock_id)
+@@ -422,6 +437,7 @@ gtk_cell_renderer_pixbuf_set_property (GObject *object,
+ g_object_unref (priv->gicon);
+ }
+ priv->gicon = (GIcon *) g_value_dup_object (value);
++ priv->render_scale = 1;
+ if (priv->gicon)
+ {
+ if (cellpixbuf->pixbuf)
+@@ -444,6 +460,49 @@ gtk_cell_renderer_pixbuf_set_property (GObject *object,
+ }
+ }
+ break;
++ case PROP_ICON_SET:
++ if (priv->icon_set)
++ {
++ if (cellpixbuf->pixbuf)
++ {
++ g_object_unref (cellpixbuf->pixbuf);
++ cellpixbuf->pixbuf = NULL;
++ g_object_notify (object, "pixbuf");
++ }
++
++ gtk_icon_set_unref (priv->icon_set);
++ }
++
++ priv->icon_set = g_value_dup_boxed (value);
++
++ if (priv->icon_set)
++ {
++ if (cellpixbuf->pixbuf)
++ {
++ g_object_unref (cellpixbuf->pixbuf);
++ cellpixbuf->pixbuf = NULL;
++ g_object_notify (object, "pixbuf");
++ }
++ if (priv->stock_id)
++ {
++ g_free (priv->stock_id);
++ priv->stock_id = NULL;
++ g_object_notify (object, "stock-id");
++ }
++ if (priv->icon_name)
++ {
++ g_free (priv->icon_name);
++ priv->icon_name = NULL;
++ g_object_notify (object, "icon-name");
++ }
++ if (priv->gicon)
++ {
++ g_object_unref (priv->gicon);
++ priv->gicon = NULL;
++ g_object_notify (object, "gicon");
++ }
++ }
++ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+@@ -474,16 +533,19 @@ gtk_cell_renderer_pixbuf_create_stock_pixbuf (GtkCellRendererPixbuf *cellpixbuf,
+ GtkWidget *widget)
+ {
+ GtkCellRendererPixbufPrivate *priv;
++ GtkIconSet *icon_set;
+
+ priv = GTK_CELL_RENDERER_PIXBUF_GET_PRIVATE (cellpixbuf);
+
+ if (cellpixbuf->pixbuf)
+ g_object_unref (cellpixbuf->pixbuf);
+
+- cellpixbuf->pixbuf = gtk_widget_render_icon (widget,
+- priv->stock_id,
+- priv->stock_size,
+- priv->stock_detail);
++ priv->render_scale = gtk_widget_get_scale_factor (widget);
++ cellpixbuf->pixbuf = gtk_widget_render_icon_scaled (widget,
++ priv->stock_id,
++ priv->stock_size,
++ priv->stock_detail,
++ &priv->render_scale);
+
+ g_object_notify (G_OBJECT (cellpixbuf), "pixbuf");
+ }
+@@ -510,15 +572,24 @@ gtk_cell_renderer_pixbuf_create_themed_pixbuf (GtkCellRendererPixbuf *cellpixbuf
+ icon_theme = gtk_icon_theme_get_for_screen (screen);
+ settings = gtk_settings_get_for_screen (screen);
+
+- if (!gtk_icon_size_lookup_for_settings (settings,
+- priv->stock_size,
+- &width, &height))
++ if (!gtk_icon_size_lookup_scaled (settings,
++ priv->stock_size,
++ gdk_window_get_scale_factor (GTK_WIDGET (widget)->window),
++ &width, &height))
+ {
+ g_warning ("Invalid icon size %u\n", priv->stock_size);
+ width = height = 24;
+ }
+
+- if (priv->icon_name)
++ if (priv->icon_set)
++ cellpixbuf->pixbuf =
++ gtk_icon_set_render_icon_scaled (priv->icon_set,
++ widget->style,
++ gtk_widget_get_direction (widget),
++ gtk_widget_get_state (widget),
++ priv->stock_size, widget,
++ NULL, &priv->render_scale);
++ else if (priv->icon_name)
+ cellpixbuf->pixbuf = gtk_icon_theme_load_icon (icon_theme,
+ priv->icon_name,
+ MIN (width, height),
+@@ -611,14 +682,28 @@ gtk_cell_renderer_pixbuf_get_size (GtkCellRenderer *cell,
+ {
+ if (priv->stock_id)
+ gtk_cell_renderer_pixbuf_create_stock_pixbuf (cellpixbuf, widget);
++ else if (priv->icon_set)
++ {
++ gdouble scale;
++
++ scale = gtk_widget_get_scale_factor (widget);
++ cellpixbuf->pixbuf =
++ gtk_icon_set_render_icon_scaled (priv->icon_set,
++ widget->style,
++ gtk_widget_get_direction (widget),
++ gtk_widget_get_state (widget),
++ priv->stock_size,
++ widget, priv->stock_detail,
++ &scale);
++ }
+ else if (priv->icon_name || priv->gicon)
+ gtk_cell_renderer_pixbuf_create_themed_pixbuf (cellpixbuf, widget);
+ }
+
+ if (cellpixbuf->pixbuf)
+ {
+- pixbuf_width = gdk_pixbuf_get_width (cellpixbuf->pixbuf);
+- pixbuf_height = gdk_pixbuf_get_height (cellpixbuf->pixbuf);
++ pixbuf_width = gdk_pixbuf_get_width (cellpixbuf->pixbuf) / priv->render_scale;
++ pixbuf_height = gdk_pixbuf_get_height (cellpixbuf->pixbuf) / priv->render_scale;
+ }
+ if (cellpixbuf->pixbuf_expander_open)
+ {
+@@ -761,10 +846,14 @@ gtk_cell_renderer_pixbuf_render (GtkCellRenderer *cell,
+ pixbuf = colorized;
+ }
+
++ draw_rect.x -= pix_rect.x;
++ draw_rect.y -= pix_rect.y;
++
+ cr = gdk_cairo_create (window);
+-
+- gdk_cairo_set_source_pixbuf (cr, pixbuf, pix_rect.x, pix_rect.y);
++ cairo_translate (cr, pix_rect.x, pix_rect.y);
+ gdk_cairo_rectangle (cr, &draw_rect);
++ cairo_scale (cr, 1 / priv->render_scale, 1 / priv->render_scale);
++ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From aba260146c099864e69379e31bd3decbf3681817 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Tue, 12 Feb 2013 14:04:37 +0100
+Subject: [PATCH 63/68] entry: Use scaled icons on windows with a scale factor
+
+---
+ gtk/gtkentry.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++------
+ gtk/gtkentry.h | 7 +++
+ 2 files changed, 171 insertions(+), 20 deletions(-)
+
+diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
+index 0d16d71..313804e 100644
+--- a/gtk/gtkentry.c
++++ b/gtk/gtkentry.c
+@@ -105,9 +105,11 @@ typedef struct
+ gchar *stock_id;
+ gchar *icon_name;
+ GIcon *gicon;
++ GtkIconSet *icon_set;
+
+ GtkTargetList *target_list;
+ GdkDragAction actions;
++ gdouble render_scale;
+ } EntryIconInfo;
+
+ struct _GtkEntryPrivate
+@@ -207,6 +209,8 @@ enum {
+ PROP_ICON_NAME_SECONDARY,
+ PROP_GICON_PRIMARY,
+ PROP_GICON_SECONDARY,
++ PROP_ICON_SET_PRIMARY,
++ PROP_ICON_SET_SECONDARY,
+ PROP_STORAGE_TYPE_PRIMARY,
+ PROP_STORAGE_TYPE_SECONDARY,
+ PROP_ACTIVATABLE_PRIMARY,
+@@ -1009,7 +1013,21 @@ gtk_entry_class_init (GtkEntryClass *class)
+ P_("GIcon for secondary icon"),
+ G_TYPE_ICON,
+ GTK_PARAM_READWRITE));
+-
++ g_object_class_install_property (gobject_class,
++ PROP_ICON_SET_PRIMARY,
++ g_param_spec_boxed ("primary-icon-set",
++ P_("Primary icon set"),
++ P_("GtkIconSet for the primary icon"),
++ GTK_TYPE_ICON_SET,
++ GTK_PARAM_READWRITE));
++ g_object_class_install_property (gobject_class,
++ PROP_ICON_SET_SECONDARY,
++ g_param_spec_boxed ("secondary-icon-set",
++ P_("Secondary icon set"),
++ P_("GtkIconSet for the secondary icon"),
++ GTK_TYPE_ICON_SET,
++ GTK_PARAM_READWRITE));
++
+ /**
+ * GtkEntry:primary-icon-storage-type:
+ *
+@@ -1940,6 +1958,18 @@ gtk_entry_set_property (GObject *object,
+ g_value_get_object (value));
+ break;
+
++ case PROP_ICON_SET_PRIMARY:
++ gtk_entry_set_icon_from_icon_set (entry,
++ GTK_ENTRY_ICON_PRIMARY,
++ g_value_get_boxed (value));
++ break;
++
++ case PROP_ICON_SET_SECONDARY:
++ gtk_entry_set_icon_from_icon_set (entry,
++ GTK_ENTRY_ICON_SECONDARY,
++ g_value_get_boxed (value));
++ break;
++
+ case PROP_ACTIVATABLE_PRIMARY:
+ gtk_entry_set_icon_activatable (entry,
+ GTK_ENTRY_ICON_PRIMARY,
+@@ -2158,6 +2188,18 @@ gtk_entry_get_property (GObject *object,
+ GTK_ENTRY_ICON_SECONDARY));
+ break;
+
++ case PROP_ICON_SET_PRIMARY:
++ g_value_set_boxed (value,
++ gtk_entry_get_icon_set (entry,
++ GTK_ENTRY_ICON_PRIMARY));
++ break;
++
++ case PROP_ICON_SET_SECONDARY:
++ g_value_set_boxed (value,
++ gtk_entry_get_icon_set (entry,
++ GTK_ENTRY_ICON_SECONDARY));
++ break;
++
+ case PROP_STORAGE_TYPE_PRIMARY:
+ g_value_set_enum (value,
+ gtk_entry_get_icon_storage_type (entry,
+@@ -2334,7 +2376,9 @@ get_icon_width (GtkEntry *entry,
+ gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
+ &menu_icon_width, NULL);
+
+- return MAX (gdk_pixbuf_get_width (icon_info->pixbuf), menu_icon_width);
++ return MAX (gdk_pixbuf_get_width (icon_info->pixbuf) /
++ gtk_widget_get_scale_factor (GTK_WIDGET (entry)),
++ menu_icon_width);
+ }
+
+ static void
+@@ -3188,6 +3232,7 @@ draw_icon (GtkWidget *widget,
+ EntryIconInfo *icon_info = priv->icons[icon_pos];
+ GdkPixbuf *pixbuf;
+ gint x, y, width, height;
++ gdouble window_scale;
+ cairo_t *cr;
+
+ if (!icon_info)
+@@ -3198,8 +3243,9 @@ draw_icon (GtkWidget *widget,
+ if (icon_info->pixbuf == NULL)
+ return;
+
+- width = gdk_window_get_width (icon_info->window);
+- height = gdk_window_get_height (icon_info->window);
++ window_scale = gdk_window_get_scale_factor (widget->window);
++ width = gdk_window_get_width (icon_info->window) / window_scale;
++ height = gdk_window_get_height (icon_info->window) / window_scale;
+
+ /* size_allocate hasn't been called yet. These are the default values.
+ */
+@@ -3209,20 +3255,20 @@ draw_icon (GtkWidget *widget,
+ pixbuf = icon_info->pixbuf;
+ g_object_ref (pixbuf);
+
+- if (gdk_pixbuf_get_height (pixbuf) > height)
++ if (gdk_pixbuf_get_height (pixbuf) > (height * window_scale))
+ {
+ GdkPixbuf *temp_pixbuf;
+ gint scale;
+
+- scale = height - 2 * priv->icon_margin;
++ scale = (height - 2 * priv->icon_margin) * window_scale;
+ temp_pixbuf = gdk_pixbuf_scale_simple (pixbuf, scale, scale,
+ GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+ pixbuf = temp_pixbuf;
+ }
+
+- x = (width - gdk_pixbuf_get_width (pixbuf)) / 2;
+- y = (height - gdk_pixbuf_get_height (pixbuf)) / 2;
++ x = (width - (gdk_pixbuf_get_width (pixbuf) / window_scale)) / 2;
++ y = (height - (gdk_pixbuf_get_height (pixbuf) / window_scale)) / 2;
+
+ if (!gtk_widget_is_sensitive (widget) ||
+ icon_info->insensitive)
+@@ -6455,6 +6501,17 @@ gtk_entry_clear (GtkEntry *entry,
+ icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-gicon" : "secondary-icon-gicon");
+ break;
+
++ case GTK_IMAGE_ICON_SET:
++ if (icon_info->icon_set)
++ {
++ gtk_icon_set_unref (icon_info->icon_set);
++ icon_info->icon_set = NULL;
++ }
++
++ g_object_notify (G_OBJECT (entry),
++ icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-set" : "secondary-icon-set");
++ break;
++
+ default:
+ g_assert_not_reached ();
+ break;
+@@ -6494,15 +6551,18 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
+ case GTK_IMAGE_STOCK:
+ state = gtk_widget_get_state (widget);
+ gtk_widget_set_state (widget, GTK_STATE_NORMAL);
+- icon_info->pixbuf = gtk_widget_render_icon (widget,
+- icon_info->stock_id,
+- GTK_ICON_SIZE_MENU,
+- NULL);
++ icon_info->render_scale = gtk_widget_get_scale_factor (widget);
++ icon_info->pixbuf = gtk_widget_render_icon_scaled (widget,
++ icon_info->stock_id,
++ GTK_ICON_SIZE_MENU,
++ NULL,
++ &icon_info->render_scale);
+ if (!icon_info->pixbuf)
+- icon_info->pixbuf = gtk_widget_render_icon (widget,
+- GTK_STOCK_MISSING_IMAGE,
+- GTK_ICON_SIZE_MENU,
+- NULL);
++ icon_info->pixbuf = gtk_widget_render_icon_scaled (widget,
++ GTK_STOCK_MISSING_IMAGE,
++ GTK_ICON_SIZE_MENU,
++ NULL,
++ &icon_info->render_scale);
+ gtk_widget_set_state (widget, state);
+ break;
+
+@@ -6514,8 +6574,9 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
+ settings = gtk_settings_get_for_screen (screen);
+
+ gtk_icon_size_lookup_for_settings (settings,
+- GTK_ICON_SIZE_MENU,
+- &width, &height);
++ GTK_ICON_SIZE_MENU,
++ // gdk_window_get_scale_factor (widget->window),
++ &width, &height);
+
+ icon_info->pixbuf = gtk_icon_theme_load_icon (icon_theme,
+ icon_info->icon_name,
+@@ -6543,8 +6604,9 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
+ settings = gtk_settings_get_for_screen (screen);
+
+ gtk_icon_size_lookup_for_settings (settings,
+- GTK_ICON_SIZE_MENU,
+- &width, &height);
++ GTK_ICON_SIZE_MENU,
++ //gdk_window_get_scale_factor (widget->window),
++ &width, &height);
+
+ info = gtk_icon_theme_lookup_by_gicon (icon_theme,
+ icon_info->gicon,
+@@ -6569,6 +6631,17 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
+ }
+ break;
+
++ case GTK_IMAGE_ICON_SET:
++ icon_info->render_scale = gtk_widget_get_scale_factor (widget);
++ icon_info->pixbuf =
++ gtk_icon_set_render_icon_scaled (icon_info->icon_set,
++ widget->style,
++ gtk_widget_get_direction (widget),
++ gtk_widget_get_state (widget),
++ GTK_ICON_SIZE_MENU, widget,
++ NULL, &icon_info->render_scale);
++ break;
++
+ default:
+ g_assert_not_reached ();
+ break;
+@@ -7847,6 +7920,58 @@ gtk_entry_set_icon_from_gicon (GtkEntry *entry,
+ g_object_thaw_notify (G_OBJECT (entry));
+ }
+
++void
++gtk_entry_set_icon_from_icon_set (GtkEntry *entry,
++ GtkEntryIconPosition icon_pos,
++ GtkIconSet *icon_set)
++{
++ GtkEntryPrivate *priv;
++ EntryIconInfo *icon_info;
++
++ g_return_if_fail (GTK_IS_ENTRY (entry));
++ g_return_if_fail (IS_VALID_ICON_POSITION (icon_pos));
++
++ priv = GTK_ENTRY_GET_PRIVATE (entry);
++
++ if ((icon_info = priv->icons[icon_pos]) == NULL)
++ icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
++
++ g_object_freeze_notify (G_OBJECT (entry));
++
++ /* need to ref before clearing */
++ if (icon_set)
++ gtk_icon_set_ref (icon_set);
++
++ gtk_entry_clear (entry, icon_pos);
++
++ if (icon_set)
++ {
++ icon_info->storage_type = GTK_IMAGE_ICON_SET;
++ icon_info->icon_set = icon_set;
++
++ if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
++ {
++ g_object_notify (G_OBJECT (entry), "primary-icon-set");
++ g_object_notify (G_OBJECT (entry), "primary-icon-storage-type");
++ }
++ else
++ {
++ g_object_notify (G_OBJECT (entry), "secondary-icon-set");
++ g_object_notify (G_OBJECT (entry), "secondary-icon-storage-type");
++ }
++
++ if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
++ gdk_window_show_unraised (icon_info->window);
++ }
++
++ gtk_entry_ensure_pixbuf (entry, icon_pos);
++
++ if (gtk_widget_get_visible (GTK_WIDGET (entry)))
++ gtk_widget_queue_resize (GTK_WIDGET (entry));
++
++ g_object_thaw_notify (G_OBJECT (entry));
++}
++
+ /**
+ * gtk_entry_set_icon_activatable:
+ * @entry: A #GtkEntry
+@@ -8050,6 +8175,25 @@ gtk_entry_get_icon_name (GtkEntry *entry,
+ return icon_info->storage_type == GTK_IMAGE_ICON_NAME ? icon_info->icon_name : NULL;
+ }
+
++const GtkIconSet *
++gtk_entry_get_icon_set (GtkEntry *entry,
++ GtkEntryIconPosition icon_pos)
++{
++ GtkEntryPrivate *priv;
++ EntryIconInfo *icon_info;
++
++ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
++ g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), NULL);
++
++ priv = GTK_ENTRY_GET_PRIVATE (entry);
++ icon_info = priv->icons[icon_pos];
++
++ if (!icon_info)
++ return NULL;
++
++ return icon_info->storage_type == GTK_IMAGE_ICON_SET ? icon_info->icon_set : NULL;
++}
++
+ /**
+ * gtk_entry_set_icon_sensitive:
+ * @entry: A #GtkEntry
+diff --git a/gtk/gtkentry.h b/gtk/gtkentry.h
+index f771e17..0153f49 100644
+--- a/gtk/gtkentry.h
++++ b/gtk/gtkentry.h
+@@ -264,6 +264,9 @@ void gtk_entry_set_icon_from_icon_name (GtkEntry *
+ void gtk_entry_set_icon_from_gicon (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GIcon *icon);
++void gtk_entry_set_icon_from_icon_set (GtkEntry *entry,
++ GtkEntryIconPosition icon_pos,
++ GtkIconSet *icon_set);
+ GtkImageType gtk_entry_get_icon_storage_type (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos);
+ GdkPixbuf* gtk_entry_get_icon_pixbuf (GtkEntry *entry,
+@@ -274,6 +277,10 @@ const gchar* gtk_entry_get_icon_name (GtkEntry *
+ GtkEntryIconPosition icon_pos);
+ GIcon* gtk_entry_get_icon_gicon (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos);
++const GtkIconSet *
++ gtk_entry_get_icon_set (GtkEntry *entry,
++ GtkEntryIconPosition icon_pos);
++
+ void gtk_entry_set_icon_activatable (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ gboolean activatable);
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 67197e562dbdfdbd518d1e8ffb4746af41c3f731 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Fri, 17 May 2013 15:58:15 +0200
+Subject: [PATCH 64/68] gdk: Lookup double scaled variants on pixbufs
+
+if the pixbuf contains its @2x variant as gobject data and
+the display has a doubled scale factor, use such pixbuf
+for rendering
+---
+ gdk/gdkcairo.c | 36 ++++++++++++++++++++++++++++++++++--
+ gdk/gdkdraw.c | 18 ++++++++++++++++++
+ 2 files changed, 52 insertions(+), 2 deletions(-)
+
+diff --git a/gdk/gdkcairo.c b/gdk/gdkcairo.c
+index c423871..9aab5e1 100644
+--- a/gdk/gdkcairo.c
++++ b/gdk/gdkcairo.c
+@@ -23,6 +23,8 @@
+ #include "gdkregion-generic.h"
+ #include "gdkalias.h"
+
++static const cairo_user_data_key_t gdk_cairo_drawable_pointer;
++
+ static void
+ gdk_ensure_surface_flush (gpointer surface)
+ {
+@@ -58,6 +60,7 @@ gdk_cairo_create (GdkDrawable *drawable)
+
+ surface = _gdk_drawable_ref_cairo_surface (drawable);
+ cr = cairo_create (surface);
++ cairo_set_user_data (cr, &gdk_cairo_drawable_pointer, drawable, NULL);
+
+ if (GDK_DRAWABLE_GET_CLASS (drawable)->set_cairo_clip)
+ GDK_DRAWABLE_GET_CLASS (drawable)->set_cairo_clip (drawable, cr);
+@@ -202,7 +205,32 @@ gdk_cairo_set_source_pixbuf (cairo_t *cr,
+ cairo_format_t format;
+ cairo_surface_t *surface;
+ static const cairo_user_data_key_t key;
+- int j;
++ GdkWindow *window;
++ cairo_pattern_t *pattern;
++ cairo_matrix_t matrix;
++ int j, scale = 1;
++
++ window = cairo_get_user_data (cr, &gdk_cairo_drawable_pointer);
++
++ if (window &&
++ (int) gdk_window_get_scale_factor (window) == 2)
++ {
++ GdkPixbuf *scaled_pixbuf;
++
++ scaled_pixbuf = g_object_get_data (G_OBJECT (pixbuf),
++ "gdk-pixbuf-2x-variant");
++ if (scaled_pixbuf)
++ {
++ scale = 2;
++ pixbuf = scaled_pixbuf;
++ }
++ }
++
++ width = gdk_pixbuf_get_width (pixbuf);
++ height = gdk_pixbuf_get_height (pixbuf);
++ gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
++ gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
++ n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+ if (n_channels == 3)
+ format = CAIRO_FORMAT_RGB24;
+@@ -274,7 +302,11 @@ gdk_cairo_set_source_pixbuf (cairo_t *cr,
+ cairo_pixels += cairo_stride;
+ }
+
+- cairo_set_source_surface (cr, surface, pixbuf_x, pixbuf_y);
++ cairo_set_source_surface (cr, surface, 0, 0);
++ pattern = cairo_get_source (cr);
++ cairo_matrix_init_scale (&matrix, scale, scale);
++ cairo_matrix_translate (&matrix, -pixbuf_x, -pixbuf_y);
++ cairo_pattern_set_matrix (pattern, &matrix);
+ cairo_surface_destroy (surface);
+ }
+
+diff --git a/gdk/gdkdraw.c b/gdk/gdkdraw.c
+index 932de97..6ebd8c7 100644
+--- a/gdk/gdkdraw.c
++++ b/gdk/gdkdraw.c
+@@ -819,6 +819,8 @@ gdk_draw_pixbuf (GdkDrawable *drawable,
+ gint x_dither,
+ gint y_dither)
+ {
++ GdkPixbuf *scaled_pixbuf;
++
+ g_return_if_fail (GDK_IS_DRAWABLE (drawable));
+ g_return_if_fail (gc == NULL || GDK_IS_GC (gc));
+ g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+@@ -826,6 +828,22 @@ gdk_draw_pixbuf (GdkDrawable *drawable,
+ if (width == 0 || height == 0)
+ return;
+
++ scaled_pixbuf = g_object_get_data (G_OBJECT (pixbuf),
++ "gdk-pixbuf-2x-variant");
++
++ if (scaled_pixbuf && GDK_IS_WINDOW (drawable) &&
++ (int) gdk_window_get_scale_factor (GDK_WINDOW (drawable)) == 2)
++ {
++ cairo_t *cr;
++
++ cr = gdk_cairo_create (GDK_WINDOW (drawable));
++ gdk_cairo_set_source_pixbuf (cr, pixbuf, dest_x, dest_y);
++ cairo_paint (cr);
++
++ cairo_destroy (cr);
++ return;
++ }
++
+ if (width == -1)
+ width = gdk_pixbuf_get_width (pixbuf);
+ if (height == -1)
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 318b7822a8a737a826aab50e1a829a6b59c9704f Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Fri, 14 Jun 2013 15:47:44 +0200
+Subject: [PATCH 65/68] Make usual calls to get a GdkPixbuf attach a 2x
+ variant if available
+
+---
+ gtk/gtkiconfactory.c | 24 ++++++++++++++++++---
+ gtk/gtkicontheme.c | 57 +++++++++++++++++++++++++++++++++++++++++++++-----
+ gtk/gtkwidget.c | 20 ++++++++++++++----
+ 3 files changed, 89 insertions(+), 12 deletions(-)
+
+diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c
+index ab9a212..291c05e 100644
+--- a/gtk/gtkiconfactory.c
++++ b/gtk/gtkiconfactory.c
+@@ -1731,14 +1731,32 @@ gtk_icon_set_render_icon (GtkIconSet *icon_set,
+ GtkWidget *widget,
+ const char *detail)
+ {
++ GdkPixbuf *pixbuf, *variant;
+ gdouble scale = 1;
+
+ g_return_val_if_fail (icon_set != NULL, NULL);
+ g_return_val_if_fail (style == NULL || GTK_IS_STYLE (style), NULL);
+
+- return gtk_icon_set_render_icon_internal (icon_set, style, direction,
+- state, size, widget, detail,
+- &scale);
++ pixbuf = gtk_icon_set_render_icon_internal (icon_set, style, direction,
++ state, size, widget, detail,
++ &scale);
++ if (pixbuf && scale == 1)
++ {
++ scale = 2;
++ variant = gtk_icon_set_render_icon_internal (icon_set, style, direction,
++ state, size, widget, detail,
++ &scale);
++ if (variant &&
++ gdk_pixbuf_get_width (variant) > gdk_pixbuf_get_width (pixbuf))
++ g_object_set_data_full (G_OBJECT (pixbuf),
++ "gdk-pixbuf-2x-variant",
++ variant,
++ (GDestroyNotify) g_object_unref);
++ else if (variant)
++ g_object_unref (variant);
++ }
++
++ return pixbuf;
+ }
+
+ GdkPixbuf*
+diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c
+index 500f0ab..0e42adc 100644
+--- a/gtk/gtkicontheme.c
++++ b/gtk/gtkicontheme.c
+@@ -1403,6 +1403,8 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
+ gint size,
+ GtkIconLookupFlags flags)
+ {
++ GtkIconInfo *retval, *variant;
++
+ g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+ g_return_val_if_fail (icon_name != NULL, NULL);
+ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+@@ -1411,8 +1413,22 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
+ GTK_NOTE (ICONTHEME,
+ g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
+
+- return gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name,
+- size, 1, flags);
++ retval = gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name,
++ size, 1, flags);
++
++ variant = gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name,
++ size, 2, flags);
++ if (retval && variant &&
++ retval->pixbuf && variant->pixbuf &&
++ gdk_pixbuf_get_width (variant->pixbuf) > gdk_pixbuf_get_width (retval->pixbuf))
++ g_object_set_data_full (G_OBJECT (retval->pixbuf),
++ "gdk-pixbuf-2x-variant",
++ g_object_ref (variant->pixbuf),
++ (GDestroyNotify) g_object_unref);
++ if (variant)
++ gtk_icon_info_free (variant);
++
++ return retval;
+ }
+
+ GtkIconInfo *
+@@ -1501,12 +1517,27 @@ gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme,
+ gint size,
+ GtkIconLookupFlags flags)
+ {
++ GtkIconInfo *retval, *variant;
++
+ g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+ g_return_val_if_fail (icon_names != NULL, NULL);
+ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+ (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
+
+- return choose_icon (icon_theme, icon_names, size, 1, flags);
++ retval = choose_icon (icon_theme, icon_names, size, 1, flags);
++ variant = choose_icon (icon_theme, icon_names, size, 2, flags);
++
++ if (retval && variant &&
++ retval->pixbuf && variant->pixbuf &&
++ gdk_pixbuf_get_width (variant->pixbuf) > gdk_pixbuf_get_width (retval->pixbuf))
++ g_object_set_data_full (G_OBJECT (retval->pixbuf),
++ "gdk-pixbuf-2x-variant",
++ g_object_ref (variant->pixbuf),
++ (GDestroyNotify) g_object_unref);
++ if (variant)
++ gtk_icon_info_free (variant);
++
++ return retval;
+ }
+
+ GtkIconInfo *
+@@ -1569,14 +1600,30 @@ gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
+ GtkIconLookupFlags flags,
+ GError **error)
+ {
++ GdkPixbuf *pixbuf, *variant;
++
+ g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+ g_return_val_if_fail (icon_name != NULL, NULL);
+ g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+ (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+- return gtk_icon_theme_load_icon_for_scale (icon_theme, icon_name,
+- size, 1, flags, error);
++ pixbuf = gtk_icon_theme_load_icon_for_scale (icon_theme, icon_name,
++ size, 1, flags, error);
++
++ variant = gtk_icon_theme_load_icon_for_scale (icon_theme, icon_name,
++ size, 2, flags, NULL);
++
++ if (pixbuf && variant &&
++ gdk_pixbuf_get_width (variant) > gdk_pixbuf_get_width (pixbuf))
++ g_object_set_data_full (G_OBJECT (pixbuf),
++ "gdk-pixbuf-2x-variant",
++ g_object_ref (variant),
++ (GDestroyNotify) g_object_unref);
++ if (variant)
++ g_object_unref (variant);
++
++ return pixbuf;
+ }
+
+ GdkPixbuf *
+diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
+index f093c39..464cb59 100644
+--- a/gtk/gtkwidget.c
++++ b/gtk/gtkwidget.c
+@@ -7477,15 +7477,16 @@ gtk_widget_render_icon (GtkWidget *widget,
+ GtkIconSize size,
+ const gchar *detail)
+ {
++ GdkPixbuf *retval, *variant;
+ GtkIconSet *icon_set;
+- GdkPixbuf *retval;
+-
++ gdouble scale = 2;
++
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+ g_return_val_if_fail (stock_id != NULL, NULL);
+ g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
+-
++
+ gtk_widget_ensure_style (widget);
+-
++
+ icon_set = gtk_style_lookup_icon_set (widget->style, stock_id);
+
+ if (icon_set == NULL)
+@@ -7499,6 +7500,17 @@ gtk_widget_render_icon (GtkWidget *widget,
+ widget,
+ detail);
+
++ variant = gtk_icon_set_render_icon_scaled (icon_set, widget->style,
++ gtk_widget_get_direction (widget),
++ gtk_widget_get_state (widget),
++ size, widget, detail, &scale);
++
++ if (variant)
++ g_object_set_data_full (G_OBJECT (retval),
++ "gdk-pixbuf-2x-variant",
++ variant,
++ (GDestroyNotify) g_object_unref);
++
+ return retval;
+ }
+
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 0f14c053a91185715f276dffe286698e0c3b43ba Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho <carlosg@gnome.org>
+Date: Fri, 28 Jun 2013 19:35:02 +0200
+Subject: [PATCH 66/68] cellrendererpixbuf: let 2x variants go through
+ pixel-mangling code
+
+---
+ gtk/gtkcellrendererpixbuf.c | 26 ++++++++++++++++++++++++--
+ 1 file changed, 24 insertions(+), 2 deletions(-)
+
+diff --git a/gtk/gtkcellrendererpixbuf.c b/gtk/gtkcellrendererpixbuf.c
+index 7c767b6..5969792 100644
+--- a/gtk/gtkcellrendererpixbuf.c
++++ b/gtk/gtkcellrendererpixbuf.c
+@@ -614,8 +614,8 @@ gtk_cell_renderer_pixbuf_create_themed_pixbuf (GtkCellRendererPixbuf *cellpixbuf
+ }
+
+ static GdkPixbuf *
+-create_colorized_pixbuf (GdkPixbuf *src,
+- GdkColor *new_color)
++create_colorized_pixbuf_single (GdkPixbuf *src,
++ GdkColor *new_color)
+ {
+ gint i, j;
+ gint width, height, has_alpha, src_row_stride, dst_row_stride;
+@@ -659,6 +659,28 @@ create_colorized_pixbuf (GdkPixbuf *src,
+ return dest;
+ }
+
++static GdkPixbuf *
++create_colorized_pixbuf (GdkPixbuf *src,
++ GdkColor *new_color)
++{
++ GdkPixbuf *colorized, *variant, *colorized_variant;
++
++ colorized = create_colorized_pixbuf_single (src, new_color);
++
++ variant = g_object_get_data (G_OBJECT (src),
++ "gdk-pixbuf-2x-variant");
++
++ if (colorized && variant)
++ {
++ colorized_variant = create_colorized_pixbuf_single (variant, new_color);
++ g_object_set_data_full (G_OBJECT (colorized),
++ "gdk-pixbuf-2x-variant",
++ colorized_variant,
++ (GDestroyNotify) g_object_unref);
++ }
++
++ return colorized;
++}
+
+ static void
+ gtk_cell_renderer_pixbuf_get_size (GtkCellRenderer *cell,
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From 5d0dfb203a4945eff28ad1696647b19dd0ef10e5 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Mon, 8 Jul 2013 12:04:04 +0200
+Subject: [PATCH 67/68] quartz: Make event loop deal with recursive poll
+ invocations
+
+This patch addresses a number of problems with the recursion in
+poll_func() that happens on OS X. Firstly, autorelease pool refreshes
+are done from prepare and only if we are at the base level of *both*
+main loops and not recursing in poll. This first part incorporates
+suggestions from Daniel Sabo and John Ralls.
+
+Secondly, a mechanism is implemented to detect if poll has been
+called recursively. This is used to discard file descriptor state
+at the base level, retrieved before possibly recursing (through the
+NSApp get next event).
+---
+ gdk/quartz/gdkeventloop-quartz.c | 106 ++++++++++++++++++++++++++++----------
+ 1 file changed, 78 insertions(+), 28 deletions(-)
+
+diff --git a/gdk/quartz/gdkeventloop-quartz.c b/gdk/quartz/gdkeventloop-quartz.c
+index 224d84c..36c716a 100644
+--- a/gdk/quartz/gdkeventloop-quartz.c
++++ b/gdk/quartz/gdkeventloop-quartz.c
+@@ -168,6 +168,12 @@ static pthread_cond_t select_thread_cond = PTHREAD_COND_INITIALIZER;
+ static GPollFD *current_pollfds;
+ static guint current_n_pollfds;
+
++/* We maintain serial numbers for calls to the start function. Because we
++ * only test for equality of this number (and not smaller/greater than)
++ * no special handling is needed for wrap around.
++ */
++static guint select_thread_start_serial = 0;
++
+ /* These are the file descriptors that the select thread should pick
+ * up and start polling when it has a chance.
+ */
+@@ -399,6 +405,8 @@ select_thread_start_poll (GPollFD *ufds,
+ gint poll_fd_index = -1;
+ gint i;
+
++ select_thread_start_serial++;
++
+ for (i = 0; i < nfds; i++)
+ if (ufds[i].fd == -1)
+ {
+@@ -703,15 +721,33 @@ poll_func (GPollFD *ufds,
+ NSEvent *event;
+ NSDate *limit_date;
+ gint n_ready;
++ guint current_select_thread_serial;
+
+- static GPollFD *last_ufds;
++ /* Maintain current poll parameters as static parameters. On recursive
++ * calls to poll, we update the values. This way, if the poll array
++ * is updated in an recursive call, we can still access the new array
++ * when the recursion is wound down.
++ *
++ * The ufds value that is pushed in is allocated in g_main_context_iterate()
++ * and is only reallocated for enlargement of this array. The array thus
++ * never shrinks.
++ */
++ static GPollFD *current_ufds = NULL;
++ static int current_nfds = 0;
+
+- last_ufds = ufds;
++ current_ufds = ufds;
++ current_nfds = nfds;
+
+ n_ready = select_thread_start_poll (ufds, nfds, timeout_);
+ if (n_ready > 0)
+ timeout_ = 0;
+
++ /* Using this serial number, we can check below whether the select
++ * thread was started/collected in a recursive invocation of this
++ * function.
++ */
++ current_select_thread_serial = select_thread_start_serial;
++
+ if (timeout_ == -1)
+ limit_date = [NSDate distantFuture];
+ else if (timeout_ == 0)
+@@ -726,17 +762,31 @@ poll_func (GPollFD *ufds,
+ dequeue: YES];
+ getting_events--;
+
+- /* We check if last_ufds did not change since the time this function was
+- * called. It is possible that a recursive main loop (and thus recursive
+- * invocation of this poll function) is triggered while in
+- * nextEventMatchingMask:. If during that time new fds are added,
+- * the cached fds array might be replaced in g_main_context_iterate().
+- * So, we should avoid accessing the old fd array (still pointed at by
+- * ufds) here in that case, since it might have been freed. We avoid this
+- * by not calling the collect stage.
+- */
+- if (last_ufds == ufds && n_ready < 0)
+- n_ready = select_thread_collect_poll (ufds, nfds);
++ /* Note that, from this point onwards, ufds might point to freed memory! */
++
++ if (current_select_thread_serial == select_thread_start_serial)
++ {
++ if (n_ready < 0)
++ n_ready = select_thread_collect_poll (current_ufds, nfds);
++
++ /* Nothing should be done in case no poll func recursion occurred
++ * and n_ready > 0.
++ */
++ }
++ else
++ {
++ /* In this case, a recursive invocation of the poll function has
++ * collected results from the select thread. We clear the ufds
++ * values so that stale poll results are not returned. In case
++ * we did not receive results from start poll above, we also
++ * set n_ready to zero.
++ */
++ int i;
++
++ n_ready = 0;
++ for (i = 0; i < nfds; i++)
++ current_ufds[i].revents = 0;
++ }
+
+ if (event &&
+ [event type] == NSApplicationDefined &&
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+From da200e6664615a5ef9e89d0a295aa435f60522c9 Mon Sep 17 00:00:00 2001
+From: Michael Natterer <mitch@gimp.org>
+Date: Fri, 26 Jul 2013 14:48:57 +0200
+Subject: [PATCH 68/68] nsview: implement a few text view command accelerators
+ manually
+
+so cut, copy, paste and select all work.
+---
+ gtk/gtknsview.c | 50 ++++++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 44 insertions(+), 6 deletions(-)
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index 3b30d3b..d5eb307 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -664,14 +664,52 @@ gtk_ns_view_key_press (GtkWidget *widget,
+ {
+ GtkNSView *ns_view = GTK_NS_VIEW (widget);
+ NSEvent *nsevent = gdk_quartz_event_get_nsevent ((GdkEvent *) event);
+- NSWindow *ns_window;
+
+ if (gtk_ns_view_forward_event (widget, event))
+ {
+- ns_window = [ns_view->priv->view window];
+- [ns_window sendEvent:nsevent];
++ NSWindow *ns_window = [ns_view->priv->view window];
++ NSResponder *responder = [ns_window firstResponder];
+
+- return TRUE;
++ if ([responder isKindOfClass: [NSTextView class]] &&
++ (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK |
++ GDK_MOD1_MASK | GDK_MOD2_MASK)) == GDK_MOD2_MASK)
++ {
++ NSTextView *text_view = (NSTextView *) responder;
++ NSRange range = [text_view selectedRange];
++ gboolean has_selection = range.length > 0;
++
++ switch (event->keyval)
++ {
++ case GDK_KEY_c: /* copy */
++ if (has_selection)
++ [text_view copy: text_view];
++ return TRUE;
++
++ case GDK_KEY_x: /* cut */
++ if (has_selection)
++ [text_view cut: text_view];
++ return TRUE;
++
++ case GDK_KEY_v: /* paste */
++ [text_view paste: text_view];
++ return TRUE;
++
++ case GDK_KEY_a: /* all */
++ range.location = 0;
++ range.length = [[text_view string] length];
++ [text_view setSelectedRange: range];
++ return TRUE;
++
++ default:
++ break;
++ }
++ }
++ else
++ {
++ [ns_window sendEvent:nsevent];
++
++ return TRUE;
++ }
+ }
+
+ return GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->key_press_event (widget, event);
+@@ -683,11 +721,11 @@ gtk_ns_view_key_release (GtkWidget *widget,
+ {
+ GtkNSView *ns_view = GTK_NS_VIEW (widget);
+ NSEvent *nsevent = gdk_quartz_event_get_nsevent ((GdkEvent *) event);
+- NSWindow *ns_window;
+
+ if (gtk_ns_view_forward_event (widget, event))
+ {
+- ns_window = [ns_view->priv->view window];
++ NSWindow *ns_window = [ns_view->priv->view window];
++
+ [ns_window sendEvent:nsevent];
+
+ return TRUE;
+--
+1.7.10.2 (Apple Git-33)
--- /dev/null
+diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
+index fc25098..de9a4b0 100644
+--- a/gtk/gtkmenu.c
++++ b/gtk/gtkmenu.c
+@@ -55,6 +55,7 @@
+ #define MENU_SCROLL_FAST_ZONE 8
+ #define MENU_SCROLL_TIMEOUT1 50
+ #define MENU_SCROLL_TIMEOUT2 20
++#define GTK_SCROLL_STEP_SMOOTH 2
+
+ #define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
+ #define ATTACHED_MENUS "gtk-attached-menus"
+@@ -3504,17 +3505,25 @@ gtk_menu_scroll (GtkWidget *widget,
+ GdkEventScroll *event)
+ {
+ GtkMenu *menu = GTK_MENU (widget);
++ gdouble dx, dy;
+
+- switch (event->direction)
++ if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &dx, &dy))
+ {
+- case GDK_SCROLL_RIGHT:
+- case GDK_SCROLL_DOWN:
+- gtk_menu_scroll_by (menu, MENU_SCROLL_STEP2);
+- break;
+- case GDK_SCROLL_LEFT:
+- case GDK_SCROLL_UP:
+- gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2);
+- break;
++ gtk_menu_scroll_by (menu, GTK_SCROLL_STEP_SMOOTH * dy);
++ }
++ else
++ {
++ switch (event->direction)
++ {
++ case GDK_SCROLL_RIGHT:
++ case GDK_SCROLL_DOWN:
++ gtk_menu_scroll_by (menu, MENU_SCROLL_STEP2);
++ break;
++ case GDK_SCROLL_LEFT:
++ case GDK_SCROLL_UP:
++ gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2);
++ break;
++ }
+ }
+
+ return TRUE;
--- /dev/null
+diff --git a/gtk/gtktooltip.c b/gtk/gtktooltip.c
+index 9918165..0efe4ff 100644
+--- a/gtk/gtktooltip.c
++++ b/gtk/gtktooltip.c
+@@ -98,6 +98,9 @@ static void gtk_tooltip_display_closed (GdkDisplay *display,
+ static void gtk_tooltip_set_last_window (GtkTooltip *tooltip,
+ GdkWindow *window);
+ static void update_shape (GtkTooltip *tooltip);
++static void toplevel_focus_out (GtkWidget *widget,
++ GdkEventFocus *event,
++ GtkTooltip *tooltip);
+
+
+ G_DEFINE_TYPE (GtkTooltip, gtk_tooltip, G_TYPE_OBJECT);
+@@ -1169,6 +1172,7 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
+ GdkScreen *screen;
+
+ GdkWindow *window;
++ GtkWidget *toplevel;
+ GtkWidget *tooltip_widget;
+ GtkWidget *pointer_widget;
+ GtkTooltip *tooltip;
+@@ -1220,6 +1224,11 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
+ tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
+ }
+
++ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tooltip_widget));
++ g_signal_connect (toplevel, "focus-out-event",
++ G_CALLBACK (toplevel_focus_out),
++ tooltip);
++
+ screen = gtk_widget_get_screen (tooltip_widget);
+
+ /* FIXME: should use tooltip->current_window iso tooltip->window */
+@@ -1251,6 +1260,8 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
+ static void
+ gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
+ {
++ GtkWidget *toplevel;
++
+ if (!tooltip)
+ return;
+
+@@ -1263,6 +1274,10 @@ gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
+ if (!GTK_TOOLTIP_VISIBLE (tooltip))
+ return;
+
++ toplevel = gtk_widget_get_toplevel (tooltip->tooltip_widget);
++ if (toplevel)
++ g_signal_handlers_disconnect_by_func (toplevel, G_CALLBACK (toplevel_focus_out), tooltip);
++
+ tooltip->tooltip_widget = NULL;
+
+ if (!tooltip->keyboard_mode_enabled)
+@@ -1302,6 +1317,14 @@ gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
+ }
+ }
+
++static void
++toplevel_focus_out (GtkWidget *widget,
++ GdkEventFocus *event,
++ GtkTooltip *tooltip)
++{
++ gtk_tooltip_hide_tooltip (tooltip);
++}
++
+ static gint
+ tooltip_popup_timeout (gpointer data)
+ {
--- /dev/null
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index a643b7d..02920ce 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -282,6 +282,10 @@ static gboolean gtk_scrolled_window_calculate_velocity (GtkScrolledWindow *scro
+ GdkEvent *event);
+ static void gtk_scrolled_window_init_overlay_scrollbars (GtkScrolledWindow *window);
+
++static void gtk_scrolled_window_style_changed (GtkWidget *widget,
++ GParamSpec *property,
++ gpointer user_data);
++static void update_overlay_scrollbar_color (GtkWidget *widget);
+
+ static guint signals[LAST_SIGNAL] = {0};
+
+@@ -1149,7 +1153,7 @@ gtk_scrolled_window_set_property (GObject *object,
+ GParamSpec *pspec)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
+-
++
+ switch (prop_id)
+ {
+ case PROP_HADJUSTMENT:
+@@ -1457,7 +1461,6 @@ gtk_scrolled_window_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+- GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (widget);
+
+ if (gtk_widget_is_drawable (widget))
+ {
+@@ -3085,6 +3088,11 @@ gtk_scrolled_window_add (GtkContainer *container,
+ bin->child = child;
+ gtk_widget_set_parent (child, GTK_WIDGET (bin));
+
++ g_signal_connect (scrolled_window,
++ "notify::style",
++ G_CALLBACK (gtk_scrolled_window_style_changed),
++ NULL);
++
+ /* this is a temporary message */
+ if (!gtk_widget_set_scroll_adjustments (child,
+ gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
+@@ -3224,6 +3232,43 @@ _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
+ }
+
+ static void
++gtk_scrolled_window_style_changed (GtkWidget *widget,
++ GParamSpec *property,
++ gpointer user_data)
++{
++ update_overlay_scrollbar_color (widget);
++}
++
++static void
++update_overlay_scrollbar_color (GtkWidget *widget)
++{
++ GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW_GET_PRIVATE (widget);
++ GtkStyle *style;
++ GdkColor c;
++ gdouble r, g, b;
++ gfloat cc;
++ gint brightness;
++
++ style = gtk_widget_get_style (widget);
++ c = style->bg[GTK_STATE_NORMAL];
++
++ r = (gdouble)c.red;
++ g = (gdouble)c.green;
++ b = (gdouble)c.blue;
++
++ brightness = (int)(sqrt (r * r * 0.241 +
++ g * g * 0.691 +
++ b * b * 0.068) / 256.0);
++
++ cc = brightness < 130 ? 1.0 : 0.0;
++
++ [priv->vbar_layer setBackgroundColor: CGColorCreateGenericRGB (cc, cc, cc, 0.5)];
++ [priv->vslider_layer setBackgroundColor: CGColorCreateGenericRGB (cc, cc, cc, 1.0)];
++ [priv->hbar_layer setBackgroundColor: CGColorCreateGenericRGB (cc, cc, cc, 0.5)];
++ [priv->hslider_layer setBackgroundColor: CGColorCreateGenericRGB (cc, cc, cc, 1.0)];
++}
++
++static void
+ gtk_scrolled_window_realize (GtkWidget *widget)
+ {
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+@@ -3313,21 +3358,22 @@ gtk_scrolled_window_realize (GtkWidget *widget)
+ parent_layer = [layer_view layer];
+
+ priv->vbar_layer = [[CALayer layer] retain];
+- priv->vbar_layer.backgroundColor = CGColorCreateGenericRGB (0.0, 0.0, 0.0, 0.5);
+ priv->vbar_layer.hidden = YES;
+
+ priv->vslider_layer = [[CALayer layer] retain];
+- priv->vslider_layer.backgroundColor = CGColorCreateGenericRGB (0.0, 0.0, 0.0, 1.0);
+ priv->vslider_layer.hidden = YES;
+
+ priv->hbar_layer = [[CALayer layer] retain];
+- priv->hbar_layer.backgroundColor = CGColorCreateGenericRGB (0.0, 0.0, 0.0, 0.5);
+ priv->hbar_layer.hidden = YES;
+
+ priv->hslider_layer = [[CALayer layer] retain];
+- priv->hslider_layer.backgroundColor = CGColorCreateGenericRGB (0.0, 0.0, 0.0, 1.0);
+ priv->hslider_layer.hidden = YES;
+
++ priv->vbar_layer.backgroundColor = CGColorCreateGenericRGB (0, 0, 0, 0.5);
++ priv->vslider_layer.backgroundColor = CGColorCreateGenericRGB (0, 0, 0, 1.0);
++ priv->hbar_layer.backgroundColor = CGColorCreateGenericRGB (0, 0, 0, 0.5);
++ priv->hslider_layer.backgroundColor = CGColorCreateGenericRGB (0, 0, 0, 1.0);
++
+ [parent_layer addSublayer:priv->vbar_layer];
+ [parent_layer addSublayer:priv->vslider_layer];
--- /dev/null
+diff --git a/gdk/quartz/gdkkeys-quartz.c b/gdk/quartz/gdkkeys-quartz.c
+index a034bbd..e81ea4d 100644
+--- a/gdk/quartz/gdkkeys-quartz.c
++++ b/gdk/quartz/gdkkeys-quartz.c
+@@ -812,9 +812,41 @@ gdk_keymap_map_virtual_modifiers (GdkKeymap *keymap,
+ GdkEventType
+ _gdk_quartz_keys_event_type (NSEvent *event)
+ {
+- unsigned short keycode;
+- unsigned int flags;
++ unsigned short keycode = [event keyCode];
++ unsigned int flags = [event modifierFlags];
++ CFArrayRef global_keys = NULL;
++ unsigned int eventmods = (flags & NSCommandKeyMask ? cmdKey : 0) |
++ (flags & NSAlternateKeyMask ? optionKey : 0) |
++ (flags & NSControlKeyMask ? controlKey : 0) |
++ (flags & NSShiftKeyMask ? shiftKey : 0);
+ int i;
++
++ if (CopySymbolicHotKeys (&global_keys) == noErr && global_keys != NULL)
++ {
++ CFIndex length = CFArrayGetCount (global_keys);
++
++ for (i = 0; i < length; i++)
++ {
++ CFDictionaryRef key_info = CFArrayGetValueAtIndex (global_keys, i);
++
++ CFNumberRef code = CFDictionaryGetValue (key_info, kHISymbolicHotKeyCode);
++ CFNumberRef mods = CFDictionaryGetValue (key_info, kHISymbolicHotKeyModifiers);
++ CFBooleanRef enabled = CFDictionaryGetValue (key_info, kHISymbolicHotKeyEnabled);
++
++ gint32 mod_value;
++ gushort tmp_keycode;
++
++ CFNumberGetValue (mods, kCFNumberSInt32Type, &mod_value);
++ CFNumberGetValue (code, kCFNumberShortType, &tmp_keycode);
++
++ if (CFBooleanGetValue (enabled) && keycode == tmp_keycode && mod_value == eventmods)
++ {
++ return GDK_NOTHING;
++ }
++ }
++ }
++
++ CFRelease (global_keys);
+
+ switch ([event type])
+ {
+@@ -830,9 +862,6 @@ _gdk_quartz_keys_event_type (NSEvent *event)
+
+ /* For flags-changed events, we have to find the special key that caused the
+ * event, and see if it's in the modifier mask. */
+- keycode = [event keyCode];
+- flags = [event modifierFlags];
+-
+ for (i = 0; i < G_N_ELEMENTS (modifier_keys); i++)
+ {
+ if (modifier_keys[i].keycode == keycode)
--- /dev/null
+diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
+index 39514fd..bd83a1e 100644
+--- a/gtk/gtkcombobox.c
++++ b/gtk/gtkcombobox.c
+@@ -330,8 +330,6 @@ static void gtk_combo_box_forall (GtkContainer *container,
+ gpointer callback_data);
+ static gboolean gtk_combo_box_expose_event (GtkWidget *widget,
+ GdkEventExpose *event);
+-static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
+- GdkEventScroll *event);
+ static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
+ GtkTreePath *path);
+
+@@ -551,7 +549,6 @@ gtk_combo_box_class_init (GtkComboBoxClass *klass)
+ widget_class->size_allocate = gtk_combo_box_size_allocate;
+ widget_class->size_request = gtk_combo_box_size_request;
+ widget_class->expose_event = gtk_combo_box_expose_event;
+- widget_class->scroll_event = gtk_combo_box_scroll_event;
+ widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
+ widget_class->grab_focus = gtk_combo_box_grab_focus;
+ widget_class->style_set = gtk_combo_box_style_set;
+@@ -2963,31 +2960,6 @@ tree_first (GtkComboBox *combo,
+ return search_data.set;
+ }
+
+-static gboolean
+-gtk_combo_box_scroll_event (GtkWidget *widget,
+- GdkEventScroll *event)
+-{
+- GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
+- gboolean found;
+- GtkTreeIter iter;
+- GtkTreeIter new_iter;
+-
+- if (!gtk_combo_box_get_active_iter (combo_box, &iter))
+- return TRUE;
+-
+- if (event->direction == GDK_SCROLL_UP)
+- found = tree_prev (combo_box, combo_box->priv->model,
+- &iter, &new_iter, FALSE);
+- else
+- found = tree_next (combo_box, combo_box->priv->model,
+- &iter, &new_iter, FALSE);
+-
+- if (found)
+- gtk_combo_box_set_active_iter (combo_box, &new_iter);
+-
+- return TRUE;
+-}
+-
+ /*
+ * menu style
+ */
--- /dev/null
+commit 124e814f478936e017e58359240c540ad6678f3b
+Author: John Ralls <jralls@ceridwen.us>
+Date: Sat Sep 28 10:55:22 2013 -0700
+
+ Bug 651224 - Potential NULL display ptr from quartz gtk_clipboard_wait_for_contents
+
+diff --git a/gtk/gtkclipboard-quartz.c b/gtk/gtkclipboard-quartz.c
+index ab7732b..a68d1cf 100644
+--- a/gtk/gtkclipboard-quartz.c
++++ b/gtk/gtkclipboard-quartz.c
+@@ -701,10 +701,12 @@ gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
+ GdkAtom *atoms;
+
+ length = [types count] * sizeof (GdkAtom);
+-
++
+ selection_data = g_slice_new0 (GtkSelectionData);
+ selection_data->selection = clipboard->selection;
+ selection_data->target = target;
++ if (!selection_data->display)
++ selection_data->display = gdk_display_get_default ();
+
+ atoms = g_malloc (length);
--- /dev/null
+diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
+index f94f4d6..eeb501b 100644
+--- a/gtk/gtkfilechooserdefault.c
++++ b/gtk/gtkfilechooserdefault.c
+@@ -4409,17 +4409,26 @@ file_pane_create (GtkFileChooserDefault *impl,
+ }
+
+ static void
++on_folder_loaded (GtkFileChooserEntry *entry, GtkFileChooserDefault *chooser)
++{
++ gtk_file_chooser_default_should_respond (GTK_FILE_CHOOSER_EMBED (chooser));
++ g_signal_emit_by_name (chooser, "response-requested");
++}
++
++static void
+ location_entry_create (GtkFileChooserDefault *impl)
+ {
+ if (!impl->location_entry)
+- impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
++ {
++ impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
++ g_signal_connect (impl->location_entry, "xam-load-folder", G_CALLBACK (on_folder_loaded), impl);
++ }
+
+ _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 */
+diff --git a/gtk/gtkfilechooserdialog.c b/gtk/gtkfilechooserdialog.c
+index c06fe3f..a0548a6 100644
+--- a/gtk/gtkfilechooserdialog.c
++++ b/gtk/gtkfilechooserdialog.c
+@@ -254,7 +254,7 @@ file_chooser_widget_response_requested (GtkWidget *widget,
+
+ g_list_free (children);
+ }
+-
++
+ static GObject*
+ gtk_file_chooser_dialog_constructor (GType type,
+ guint n_construct_properties,
+diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c
+index 3caa7b8..44cf665 100644
+--- a/gtk/gtkfilechooserentry.c
++++ b/gtk/gtkfilechooserentry.c
+@@ -28,6 +28,7 @@
+ #include "gtkfilechooserentry.h"
+ #include "gtklabel.h"
+ #include "gtkmain.h"
++#include "gtkmarshalers.h"
+ #include "gtkwindow.h"
+ #include "gtkintl.h"
+ #include "gtkalias.h"
+@@ -45,6 +46,11 @@ struct _GtkFileChooserEntryClass
+ GtkEntryClass parent_class;
+ };
+
++enum {
++ XAM_LOAD_FOLDER,
++ LAST_SIGNAL
++};
++
+ /* Action to take when the current folder finishes loading (for explicit or automatic completion) */
+ typedef enum {
+ LOAD_COMPLETE_NOTHING,
+@@ -168,6 +174,8 @@ static void pop_up_completion_feedback (GtkFileChooserEntry *chooser_entry,
+
+ static GtkEditableClass *parent_editable_iface;
+
++static guint signals[LAST_SIGNAL] = { 0 };
++
+ G_DEFINE_TYPE_WITH_CODE (GtkFileChooserEntry, _gtk_file_chooser_entry, GTK_TYPE_ENTRY,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
+ gtk_file_chooser_entry_iface_init))
+@@ -188,6 +196,14 @@ _gtk_file_chooser_entry_class_init (GtkFileChooserEntryClass *class)
+ widget_class->focus_out_event = gtk_file_chooser_entry_focus_out_event;
+
+ entry_class->activate = gtk_file_chooser_entry_activate;
++
++ signals[XAM_LOAD_FOLDER] = g_signal_new (I_("xam-load-folder"),
++ G_OBJECT_CLASS_TYPE (gobject_class),
++ G_SIGNAL_RUN_LAST,
++ NULL,
++ NULL, NULL,
++ _gtk_marshal_VOID__VOID,
++ G_TYPE_NONE, 0);
+ }
+
+ static void
+@@ -1301,6 +1317,7 @@ gtk_file_chooser_entry_activate (GtkEntry *entry)
+ GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (entry);
+
+ commit_completion_and_refresh (chooser_entry);
++ g_signal_emit (chooser_entry, signals[XAM_LOAD_FOLDER], 0);
+ GTK_ENTRY_CLASS (_gtk_file_chooser_entry_parent_class)->activate (entry);
+ }
+
+diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c
+index d69d854..3ad8a19 100644
+--- a/gtk/gtkfilechooserwidget.c
++++ b/gtk/gtkfilechooserwidget.c
+@@ -24,6 +24,7 @@
+ #include "gtkfilechooserwidget.h"
+ #include "gtkfilechooserdefault.h"
+ #include "gtkfilechooserutils.h"
++#include "gtkmarshalers.h"
+ #include "gtktypebuiltins.h"
+ #include "gtkfilechooserembed.h"
+ #include "gtkintl.h"
+@@ -101,7 +102,6 @@ gtk_file_chooser_widget_constructor (GType type,
+ gtk_widget_push_composite_child ();
+
+ priv->impl = _gtk_file_chooser_default_new ();
+-
+ gtk_box_pack_start (GTK_BOX (object), priv->impl, TRUE, TRUE, 0);
+ gtk_widget_show (priv->impl);
--- /dev/null
+commit 072cd971ff3c9f48bd43b1e6f1e771dcb46f822a
+Author: Cody Russell <cody@jhu.edu>
+Date: Thu Apr 24 16:37:14 2014 -0500
+
+ When finding matching icon sources or cached icons, any scale greater than
+ 1.0 should be treated as 2.0. This is important on Windows where scales can
+ be things like 1.25 or 1.5.
+
+diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c
+index 291c05e..5e356b7 100644
+--- a/gtk/gtkiconfactory.c
++++ b/gtk/gtkiconfactory.c
+@@ -1323,11 +1323,12 @@ find_best_matching_source (GtkIconSet *icon_set,
+ GtkTextDirection direction,
+ GtkStateType state,
+ GtkIconSize size,
+- gdouble scale,
++ gdouble _scale,
+ GSList *failed)
+ {
+ GtkIconSource *source;
+ GSList *tmp_list;
++ gdouble scale = (_scale > 1.0 ? 2.0 : 1.0);
+
+ /* We need to find the best icon source. Direction matters more
+ * than state, state matters more than size. icon_set->sources
+@@ -2586,10 +2587,11 @@ find_in_cache (GtkIconSet *icon_set,
+ GtkTextDirection direction,
+ GtkStateType state,
+ GtkIconSize size,
+- gdouble scale)
++ gdouble _scale)
+ {
+ GSList *tmp_list;
+ GSList *prev;
++ gdouble scale = (_scale > 1.0 ? 2.0 : 1.0);
+
+ ensure_cache_up_to_date (icon_set);
--- /dev/null
+From b93535cc5d60149edc3a45d5b7f31f62a80234bf Mon Sep 17 00:00:00 2001
+From: Takuro Ashie <ashie@clear-code.com>
+Date: Fri, 21 Jun 2013 21:29:14 +0900
+Subject: [PATCH] Quartz: translate JIS Hiragana & Eisu keys.
+
+Since UCKeyTranslate() converts these keys to Space key unexpectedly,
+applications can't distinguish these keys by keysyms.
+To solve it, this fix translates these keys by same way with function
+keys & keypad keys.
+---
+ gdk/quartz/gdkkeys-quartz.c | 31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+diff --git a/gdk/quartz/gdkkeys-quartz.c b/gdk/quartz/gdkkeys-quartz.c
+index a4df2cf..fb11c33 100644
+--- a/gdk/quartz/gdkkeys-quartz.c
++++ b/gdk/quartz/gdkkeys-quartz.c
+@@ -191,6 +191,29 @@ const static struct {
+ { 92, GDK_KEY_9, GDK_KEY_KP_9 }
+ };
+
++/* Keys only in JIS layout.
++ * The rationale of these key codes is <HIToolbox/Events.h> in Carbon.
++ */
++const static struct {
++ guint keycode;
++ guint keyval;
++} jis_keys[] = {
++#if 0
++ /* Although These keys are also defined in <HIToolbox/Events.h>, they can be
++ * translated by UCKeyTranslate correctly.
++ */
++ { 0x5D, GDK_KEY_yen },
++ { 0x5E, GDK_KEY_underscore },
++ { 0x5F, GDK_KEY_comma },
++#endif
++ /* These keys are unexpectedly translated to Space key by UCKeyTranslate,
++ * and there is no suitable ucs value for them to add to special_ucs_table.
++ * So we should translate them particularly.
++ */
++ { 0x66 /* 102 */, GDK_KEY_Eisu_toggle },
++ { 0x68 /* 104 */, GDK_KEY_Hiragana }
++};
++
+ /* These values aren't covered by gdk_unicode_to_keyval */
+ const static struct {
+ gunichar ucs_value;
+@@ -498,6 +521,14 @@ maybe_update_keymap (void)
+ if (p[0] == known_numeric_keys[i].normal_keyval)
+ p[0] = known_numeric_keys[i].keypad_keyval;
+ }
++
++ for (i = 0; i < G_N_ELEMENTS (jis_keys); i++)
++ {
++ p = keyval_array + jis_keys[i].keycode * KEYVALS_PER_KEYCODE;
++
++ p[0] = jis_keys[i].keyval;
++ p[1] = p[2] = p[3] = 0;
++ }
+
+ if (current_layout)
+ g_signal_emit_by_name (default_keymap, "keys_changed");
+--
+1.7.12.4 (Apple Git-37)
--- /dev/null
+From ccd93c714859737d9b40b547e433a690ad6a9e55 Mon Sep 17 00:00:00 2001
+From: iain holmes <iain@xamarin.com>
+Date: Thu, 30 Jun 2016 12:34:56 +0100
+Subject: [PATCH] [Gdk] Don't crash when turning screen off
+
+Check the length of characters in the NSFlagsChanged event before accessing
+the individual characters because cmd+alt+power key doesn't have any characters
+so an ObjC will throw an exception.
+
+Fixes BXC #41657
+---
+ gdk/quartz/gdkevents-quartz.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 8cb05c0..0fa63be 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -1847,7 +1847,7 @@ gdk_event_translate (GdkEvent *event,
+ NSView *tmp_view = [nswindow firstResponder];
+ gboolean gtk_child = FALSE;
+
+- if (event_type == NSKeyDown && ([nsevent modifierFlags] & NSCommandKeyMask) != 0 && [[nsevent characters] characterAtIndex:0] == 'z')
++ if (event_type == NSKeyDown && ([nsevent modifierFlags] & NSCommandKeyMask) != 0 && [[nsevent characters] length] > 0 && [[nsevent characters] characterAtIndex:0] == 'z')
+ {
+ if ([tmp_view respondsToSelector:@selector(undoManager)])
+ {
--- /dev/null
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index a8800f7..a7d3f75 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -682,11 +682,25 @@ find_window_for_ns_event (NSEvent *nsevent,
+ else
+ {
+ /* Finally check the grab window. */
+- GdkWindow *grab_toplevel;
++ GdkWindow *grab_toplevel = NULL;
+
+- grab_toplevel = gdk_window_get_effective_toplevel (grab->window);
+- get_window_point_from_screen_point (grab_toplevel,
+- screen_point, x, y);
++ if (grab->window == NULL)
++ {
++ g_warning ("grab->window == NULL");
++ }
++ else
++ {
++ grab_toplevel = gdk_window_get_effective_toplevel (grab->window);
++ if (grab_toplevel == NULL)
++ {
++ g_warning ("grab_toplevel == NULL");
++ }
++ else
++ {
++ get_window_point_from_screen_point (grab_toplevel,
++ screen_point, x, y);
++ }
++ }
+
+ return grab_toplevel;
+ }
--- /dev/null
+diff --git a/gtk/gtkrc.key.mac b/gtk/gtkrc.key.mac
+index 980f3e4..91033a6 100644
+--- a/gtk/gtkrc.key.mac
++++ b/gtk/gtkrc.key.mac
+@@ -10,6 +10,8 @@ binding "gtk-mac-alt-arrows"
+ bind "<shift><alt>KP_Right" { "move-cursor" (words, 1, 1) }
+ bind "<shift><alt>Left" { "move-cursor" (words, -1, 1) }
+ bind "<shift><alt>KP_Left" { "move-cursor" (words, -1, 1) }
++ bind "<ctrl>p" { "move-cursor" (display-lines, -1, 0) }
++ bind "<ctrl>n" { "move-cursor" (display-lines, 1, 0) }
+ }
+
+ class "GtkTextView" binding "gtk-mac-alt-arrows"
+@@ -19,6 +21,8 @@ class "GtkEntry" binding "gtk-mac-alt-arrows"
+
+ binding "gtk-mac-alt-delete"
+ {
++ bind "<ctrl>d" { "delete-from-cursor" (chars, 1) }
++ bind "<ctrl>k" { "delete-from-cursor" (paragraph-ends, 1) }
+ bind "<alt>Delete" { "delete-from-cursor" (word-ends, 1) }
+ bind "<alt>KP_Delete" { "delete-from-cursor" (word-ends, 1) }
+ bind "<alt>BackSpace" { "delete-from-cursor" (word-ends, -1) }
--- /dev/null
+From c7143a37ec4d802710442380bc4cfd0d521350bd Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Sat, 23 Feb 2013 13:51:18 +0100
+Subject: [PATCH] Call NSApp finishLaunching before polling the first event
+ from Cocoa
+
+---
+ gdk/quartz/gdkeventloop-quartz.c | 20 ++++++++++++++++++++
+ 1 files changed, 20 insertions(+), 0 deletions(-)
+
+diff --git a/gdk/quartz/gdkeventloop-quartz.c b/gdk/quartz/gdkeventloop-quartz.c
+index 224d84c..80ef87f 100644
+--- a/gdk/quartz/gdkeventloop-quartz.c
++++ b/gdk/quartz/gdkeventloop-quartz.c
+@@ -695,6 +695,23 @@ static GSourceFuncs event_funcs = {
+ ********* Our Poll Function *********
+ ************************************************************/
+
++static inline void
++ensure_finish_launching_called (void)
++{
++ static volatile gsize finish_launched_called = 0;
++
++ if (g_once_init_enter (&finish_launched_called))
++ {
++ /* This call finished application start up and enables for example
++ * accessibility support. This function is called from poll_func
++ * and an autorelease pool is set up at that point.
++ */
++ [NSApp finishLaunching];
++
++ g_once_init_leave (&finish_launched_called, TRUE);
++ }
++}
++
+ static gint
+ poll_func (GPollFD *ufds,
+ guint nfds,
+@@ -706,6 +723,9 @@ poll_func (GPollFD *ufds,
+ static GPollFD *current_ufds = NULL;
+ static int current_nfds = 0;
+
++ /* This is performed *once* before we poll Cocoa for the first event. */
++ ensure_finish_launching_called ();
++
+ current_ufds = ufds;
+ current_nfds = nfds;
+
+--
+1.7.4.4
--- /dev/null
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 9e57edd..e8c28ad 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -46,6 +46,8 @@
+ /* This is the window corresponding to the key window */
+ static GdkWindow *current_keyboard_window;
+
++static GdkWindow *crossing_current_window = NULL;
++
+ /* This is the event mask from the last event */
+ static GdkEventMask current_event_mask;
+
+@@ -1129,12 +1131,18 @@ synthesize_crossing_event (GdkWindow *window,
+ if (!(private->event_mask & GDK_ENTER_NOTIFY_MASK))
+ return FALSE;
+
++ if (crossing_current_window == window)
++ return FALSE;
++
+ fill_crossing_event (window, event, nsevent,
+ x, y,
+ x_root, y_root,
+ GDK_ENTER_NOTIFY,
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
++
++ crossing_current_window = window;
++
+ return TRUE;
+
+ case NSMouseExited:
+@@ -1142,6 +1150,11 @@ synthesize_crossing_event (GdkWindow *window,
+ if (!(private->event_mask & GDK_LEAVE_NOTIFY_MASK))
+ return FALSE;
+
++ if (crossing_current_window != NULL && crossing_current_window != window)
++ return FALSE;
++
++ crossing_current_window = NULL;
++
+ fill_crossing_event (window, event, nsevent,
+ x, y,
+ x_root, y_root,
--- /dev/null
+commit 6ed9fd7a27d85b5abecc0f727282586bc616b79d
+Author: iain holmes <iain@xamarin.com>
+Date: Mon Jul 25 17:00:40 2016 +0100
+
+ [GtkWidget] Always create an accessibilityy object for every widget
+
+ Create an accessibility once the widget has been fully constructed.
+
+diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
+index facc0db..55d1fbc 100644
+--- a/gtk/gtkwidget.c
++++ b/gtk/gtkwidget.c
+@@ -255,6 +255,7 @@ static void gtk_widget_get_property (GObject *object,
+ static void gtk_widget_dispose (GObject *object);
+ static void gtk_widget_real_destroy (GtkObject *object);
+ static void gtk_widget_finalize (GObject *object);
++static void gtk_widget_constructed (GObject *object);
+ static void gtk_widget_real_show (GtkWidget *widget);
+ static void gtk_widget_real_hide (GtkWidget *widget);
+ static void gtk_widget_real_map (GtkWidget *widget);
+@@ -481,6 +482,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
+
+ gobject_class->dispose = gtk_widget_dispose;
+ gobject_class->finalize = gtk_widget_finalize;
++ gobject_class->constructed = gtk_widget_constructed;
+ gobject_class->set_property = gtk_widget_set_property;
+ gobject_class->get_property = gtk_widget_get_property;
+
+@@ -2961,6 +2963,13 @@ gtk_widget_init (GtkWidget *widget)
+ g_object_ref (widget->style);
+ }
+
++static void
++gtk_widget_constructed (GObject *object)
++{
++ // Always create an accessible for widgets
++ gtk_widget_get_accessible (GTK_WIDGET (object));
++ G_OBJECT_CLASS (gtk_widget_parent_class)->constructed (object);
++}
+
+ static void
+ gtk_widget_dispatch_child_properties_changed (GtkWidget *widget,
--- /dev/null
+diff --git a/gtk/gtkcolorsel.c b/gtk/gtkcolorsel.c
+index 78a483d..06b9d0e 100644
+--- a/gtk/gtkcolorsel.c
++++ b/gtk/gtkcolorsel.c
+@@ -367,6 +367,7 @@ gtk_color_selection_init (GtkColorSelection *colorsel)
+ gtk_container_add (GTK_CONTAINER (frame), priv->sample_area);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+
++ /*
+ button = gtk_button_new ();
+
+ gtk_widget_set_events (button, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
+@@ -380,6 +381,7 @@ gtk_color_selection_init (GtkColorSelection *colorsel)
+
+ gtk_widget_set_tooltip_text (button,
+ _("Click the eyedropper, then click a color anywhere on your screen to select that color."));
++ */
+
+ top_right_vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (top_hbox), top_right_vbox, FALSE, FALSE, 0);
--- /dev/null
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index f4ae401..d601ee3 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -680,6 +680,8 @@ _gdk_quartz_events_update_focus_window (GdkWindow *window,
+ * everything in the window is set to correct state.
+ */
+ generate_motion_event (window);
++
++ _gdk_quartz_update_symbolic_hotkeys ();
+ }
+ }
+
+diff --git a/gdk/quartz/gdkinput.c b/gdk/quartz/gdkinput.c
+index 6f5f8ea..8d5698f 100644
+--- a/gdk/quartz/gdkinput.c
++++ b/gdk/quartz/gdkinput.c
+@@ -568,6 +568,8 @@ _gdk_input_exit (void)
+ g_free (tmp_list->data);
+ }
+ g_list_free (_gdk_input_windows);
++
++ _gdk_quartz_release_symbolic_hotkeys ();
+ }
+
+ gboolean
+diff --git a/gdk/quartz/gdkkeys-quartz.c b/gdk/quartz/gdkkeys-quartz.c
+index e81ea4d..746881b 100644
+--- a/gdk/quartz/gdkkeys-quartz.c
++++ b/gdk/quartz/gdkkeys-quartz.c
+@@ -55,6 +55,7 @@
+ #include <AppKit/NSEvent.h>
+ #include "gdk.h"
+ #include "gdkkeysyms.h"
++#include "gdkprivate-quartz.h"
+
+ #define NUM_KEYCODES 128
+ #define KEYVALS_PER_KEYCODE 4
+@@ -65,6 +66,7 @@ static GdkKeymap *default_keymap = NULL;
+ * TThere is 1 keyval per modifier (Nothing, Shift, Alt, Shift+Alt);
+ */
+ static guint *keyval_array = NULL;
++static CFArrayRef global_hotkeys = NULL;
+
+ static inline UniChar
+ macroman2ucs (unsigned char c)
+@@ -806,6 +808,24 @@ gdk_keymap_map_virtual_modifiers (GdkKeymap *keymap,
+ return TRUE;
+ }
+
++void
++_gdk_quartz_update_symbolic_hotkeys ()
++{
++ _gdk_quartz_release_symbolic_hotkeys ();
++
++ CopySymbolicHotKeys (&global_hotkeys);
++}
++
++void
++_gdk_quartz_release_symbolic_hotkeys ()
++{
++ if (global_hotkeys != NULL)
++ {
++ CFRelease (global_hotkeys);
++ global_hotkeys = NULL;
++ }
++}
++
+ /* What sort of key event is this? Returns one of
+ * GDK_KEY_PRESS, GDK_KEY_RELEASE, GDK_NOTHING (should be ignored)
+ */
+@@ -814,20 +834,19 @@ _gdk_quartz_keys_event_type (NSEvent *event)
+ {
+ unsigned short keycode = [event keyCode];
+ unsigned int flags = [event modifierFlags];
+- CFArrayRef global_keys = NULL;
+ unsigned int eventmods = (flags & NSCommandKeyMask ? cmdKey : 0) |
+ (flags & NSAlternateKeyMask ? optionKey : 0) |
+ (flags & NSControlKeyMask ? controlKey : 0) |
+ (flags & NSShiftKeyMask ? shiftKey : 0);
+ int i;
+
+- if (CopySymbolicHotKeys (&global_keys) == noErr && global_keys != NULL)
++ if (global_hotkeys != NULL)
+ {
+- CFIndex length = CFArrayGetCount (global_keys);
++ CFIndex length = CFArrayGetCount (global_hotkeys);
+
+ for (i = 0; i < length; i++)
+ {
+- CFDictionaryRef key_info = CFArrayGetValueAtIndex (global_keys, i);
++ CFDictionaryRef key_info = CFArrayGetValueAtIndex (global_hotkeys, i);
+
+ CFNumberRef code = CFDictionaryGetValue (key_info, kHISymbolicHotKeyCode);
+ CFNumberRef mods = CFDictionaryGetValue (key_info, kHISymbolicHotKeyModifiers);
+@@ -846,8 +865,6 @@ _gdk_quartz_keys_event_type (NSEvent *event)
+ }
+ }
+
+- CFRelease (global_keys);
+-
+ switch ([event type])
+ {
+ case NSKeyDown:
+diff --git a/gdk/quartz/gdkprivate-quartz.h b/gdk/quartz/gdkprivate-quartz.h
+index f8b382d..0b0f66b 100644
+--- a/gdk/quartz/gdkprivate-quartz.h
++++ b/gdk/quartz/gdkprivate-quartz.h
+@@ -200,6 +200,8 @@ GdkImage *_gdk_quartz_image_copy_to_image (GdkDrawable *drawable,
+ gint height);
+
+ /* Keys */
++void _gdk_quartz_update_symbolic_hotkeys ();
++void _gdk_quartz_release_symbolic_hotkeys ();
+ GdkEventType _gdk_quartz_keys_event_type (NSEvent *event);
+ gboolean _gdk_quartz_keys_is_modifier (guint keycode);
+ void _gdk_quartz_synthesize_null_key_event (GdkWindow *window);
--- /dev/null
+diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
+index e9428d0..8bd02d7 100644
+--- a/gdk/gdkwindow.h
++++ b/gdk/gdkwindow.h
+@@ -724,6 +724,8 @@ void gdk_window_remove_redirection (GdkWindow *window);
+
+ /* NSView embedding */
+ gboolean gdk_window_supports_nsview_embedding ();
++gboolean gdk_window_has_embedded_nsview_focus (GdkWindow *window);
++void gdk_window_set_has_embedded_nsview_focus (GdkWindow *window, gboolean value);
+
+ #ifndef GDK_DISABLE_DEPRECATED
+ #ifndef GDK_MULTIHEAD_SAFE
+diff --git a/gdk/quartz/GdkQuartzWindow.c b/gdk/quartz/GdkQuartzWindow.c
+index b6b794e..124427e 100644
+--- a/gdk/quartz/GdkQuartzWindow.c
++++ b/gdk/quartz/GdkQuartzWindow.c
+@@ -247,6 +247,15 @@
+ }
+ }
+
++ if (responder != NULL && [responder isKindOfClass: [NSTextView class]])
++ {
++ gdk_window_set_has_embedded_nsview_focus (window, TRUE);
++ }
++ else
++ {
++ gdk_window_set_has_embedded_nsview_focus (window, FALSE);
++ }
++
+ return [super makeFirstResponder:responder];
+ }
+
+diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c
+index 050139a..cd5cc26 100644
+--- a/gdk/quartz/gdkwindow-quartz.c
++++ b/gdk/quartz/gdkwindow-quartz.c
+@@ -895,6 +895,24 @@ gdk_window_supports_nsview_embedding ()
+ return TRUE;
+ }
+
++gboolean
++gdk_window_has_embedded_nsview_focus (GdkWindow *window)
++{
++ GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
++ GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
++
++ return impl->has_nsview_focus;
++}
++
++void
++gdk_window_set_has_embedded_nsview_focus (GdkWindow *window, gboolean value)
++{
++ GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
++ GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
++
++ impl->has_nsview_focus = value;
++}
++
+ void
+ _gdk_window_impl_new (GdkWindow *window,
+ GdkWindow *real_parent,
+diff --git a/gdk/quartz/gdkwindow-quartz.h b/gdk/quartz/gdkwindow-quartz.h
+index f35238b..b619914 100644
+--- a/gdk/quartz/gdkwindow-quartz.h
++++ b/gdk/quartz/gdkwindow-quartz.h
+@@ -61,6 +61,7 @@ struct _GdkWindowImplQuartz
+ GList *sorted_children;
+
+ GdkRegion *needs_display_region;
++ gboolean has_nsview_focus;
+ };
+
+ struct _GdkWindowImplQuartzClass
--- /dev/null
+From 74a81cbb03a4b6a27b0e9c58099d54bc1537865d Mon Sep 17 00:00:00 2001
+From: iain holmes <iain@xamarin.com>
+Date: Fri, 22 Jul 2016 14:39:28 +0100
+Subject: [PATCH] [GtkBox] Emit GtkContainer's add signal
+
+Even though GtkBox is a subclass of GtkContainer, it won't emit
+GtkContainer::add when a widget is added via gtk_box_pack functions.
+
+We emit that signal, and guard against cycles caused by the signal triggering
+another gtk_box_add call.
+---
+ gtk/gtkbox.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/gtk/gtkbox.c b/gtk/gtkbox.c
+index 1ebcbcf..26c66cf 100644
+--- a/gtk/gtkbox.c
++++ b/gtk/gtkbox.c
+@@ -760,6 +760,8 @@ gtk_box_pack (GtkBox *box,
+ gtk_widget_child_notify (child, "pack-type");
+ gtk_widget_child_notify (child, "position");
+ gtk_widget_thaw_child_notify (child);
++
++ g_signal_emit_by_name (G_OBJECT (box), "add", child);
+ }
+
+ /**
+@@ -1188,6 +1190,11 @@ gtk_box_add (GtkContainer *container,
+ {
+ GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (container);
+
++ if (widget->parent == container) {
++ // Break the add signal cycle
++ return;
++ }
++
+ gtk_box_pack_start (GTK_BOX (container), widget,
+ private->default_expand,
+ private->default_expand,
--- /dev/null
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index ad003d9..483f105 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -41,7 +41,8 @@
+ enum
+ {
+ PROP_0,
+- PROP_VIEW
++ PROP_VIEW,
++ PROP_ENABLE_SWIZZLE
+ };
+
+
+@@ -49,6 +50,7 @@ struct _GtkNSViewPrivate
+ {
+ NSView *view;
+ guint map_timeout;
++ gboolean enable_swizzle;
+ };
+
+ #define GTK_NS_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+@@ -130,6 +132,14 @@ gtk_ns_view_class_init (GtkNSViewClass *klass)
+ P_("The NSView"),
+ GTK_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
++
++ g_object_class_install_property (object_class,
++ PROP_ENABLE_SWIZZLE,
++ g_param_spec_boolean ("enable-swizzle",
++ P_("Enable swizzle"),
++ P_("Enable swizzle"),
++ FALSE,
++ GTK_PARAM_READWRITE));
+ }
+
+ static void
+@@ -149,12 +159,18 @@ static void gtk_ns_view_swizzle_draw_rect_recursive (NSView *view,
+ - (void) myDidAddSubview: (NSView *) aView
+ {
+ void *associated_object;
++ GtkNSView *gtknsview;
+
+ associated_object = objc_getAssociatedObject (self, "gtknsview");
+
+ if (associated_object)
+ {
+- gtk_ns_view_swizzle_draw_rect_recursive (aView, "gtknsview", associated_object);
++ gtknsview = (GtkNSView *)associated_object;
++
++ if (gtknsview->priv->enable_swizzle)
++ {
++ gtk_ns_view_swizzle_draw_rect_recursive (aView, "gtknsview", associated_object);
++ }
+ }
+ else
+ {
+@@ -443,8 +459,11 @@ gtk_ns_view_constructed (GObject *object)
+ #endif
+
+ gtk_ns_view_replace_draw_insertion_point ();
+- gtk_ns_view_swizzle_draw_rect_recursive (ns_view->priv->view,
+- "gtknsview", ns_view);
++ if (ns_view->priv->enable_swizzle)
++ {
++ gtk_ns_view_swizzle_draw_rect_recursive (ns_view->priv->view,
++ "gtknsview", ns_view);
++ }
+ }
+
+ static void
+@@ -477,6 +496,10 @@ gtk_ns_view_set_property (GObject *object,
+ [ns_view->priv->view retain];
+ break;
+
++ case PROP_ENABLE_SWIZZLE:
++ ns_view->priv->enable_swizzle = g_value_get_boolean (value);
++ break;
++
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -497,6 +520,9 @@ gtk_ns_view_get_property (GObject *object,
+ g_value_set_pointer (value, ns_view->priv->view);
+ break;
+
++ case PROP_ENABLE_SWIZZLE:
++ g_value_set_boolean (value, ns_view->priv->enable_swizzle);
++
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+@@ -617,7 +643,7 @@ really_map (GtkWidget *widget) {
+ NSText *text = [window fieldEditor: YES
+ forObject: nil];
+
+- if (text)
++ if (text && ns_view->priv->enable_swizzle)
+ {
+ gtk_ns_view_swizzle_draw_rect_recursive (text, "gtkwindow", toplevel);
+ }
--- /dev/null
+From 4ba1fbfdb15fdf31643b7c88ca457b0eb71a0c00 Mon Sep 17 00:00:00 2001
+From: David Lechner <david@lechnology.com>
+Date: Fri, 2 Oct 2015 13:45:00 +0000
+Subject: [PATCH] Bug 753992 - im-quartz discard_preedit segmentation fault
+
+Replace checking if the NSView is really a GdkWindow, which will crash
+in the likely event it's not a GObject, with ensuring that the parent
+GdkWindow is really a GdkWindowQuartz.
+---
+ modules/input/imquartz.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/modules/input/imquartz.c b/modules/input/imquartz.c
+index 67c7d6c..f6d8f0f 100644
+--- a/modules/input/imquartz.c
++++ b/modules/input/imquartz.c
+@@ -195,11 +195,7 @@ quartz_filter_keypress (GtkIMContext *context,
+ return FALSE;
+
+ nsview = gdk_quartz_window_get_nsview (qc->client_window);
+- if (GDK_IS_WINDOW (nsview))
+- /* it gets GDK_WINDOW in some cases */
+- return gtk_im_context_filter_keypress (qc->slave, event);
+- else
+- win = (GdkWindow *)[ (GdkQuartzView *)nsview gdkWindow];
++ win = (GdkWindow *)[ (GdkQuartzView *)nsview gdkWindow];
+ GTK_NOTE (MISC, g_print ("client_window: %p, win: %p, nsview: %p\n",
+ qc->client_window, win, nsview));
+
+@@ -251,9 +247,6 @@ discard_preedit (GtkIMContext *context)
+ if (!nsview)
+ return;
+
+- if (GDK_IS_WINDOW (nsview))
+- return;
+-
+ /* reset any partial input for this NSView */
+ [(GdkQuartzView *)nsview unmarkText];
+ NSInputManager *currentInputManager = [NSInputManager currentInputManager];
+@@ -334,9 +327,6 @@ quartz_set_cursor_location (GtkIMContext *context, GdkRectangle *area)
+ qc->cursor_rect->y = area->y + y;
+
+ nsview = gdk_quartz_window_get_nsview (qc->client_window);
+- if (GDK_IS_WINDOW (nsview))
+- /* it returns GDK_WINDOW in some cases */
+- return;
+
+ win = (GdkWindow *)[ (GdkQuartzView*)nsview gdkWindow];
+ g_object_set_data (G_OBJECT (win), GIC_CURSOR_RECT, qc->cursor_rect);
--- /dev/null
+commit d15d466cc479630ac27925b0edaa769c5ab0bead
+Author: Cody Russell <cody@jhu.edu>
+Date: Tue Jan 13 16:44:46 2015 -0600
+
+ [GtkNSView] Flip command mask between MOD1 and MOD2 depending on gdk_quartz_get_fix_modifiers
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index ad003d9..d5f8c76 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -700,9 +700,11 @@ gtk_ns_view_key_press (GtkWidget *widget,
+ NSWindow *ns_window = [ns_view->priv->view window];
+ NSResponder *responder = [ns_window firstResponder];
+
++ gint command_mask = gdk_quartz_get_fix_modifiers () ? GDK_MOD2_MASK : GDK_MOD1_MASK;
++
+ if ([responder isKindOfClass: [NSTextView class]] &&
+ (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK |
+- GDK_MOD1_MASK | GDK_MOD2_MASK)) == GDK_MOD2_MASK)
++ GDK_MOD1_MASK | GDK_MOD2_MASK)) == command_mask)
+ {
+ NSTextView *text_view = (NSTextView *) responder;
+ NSRange range = [text_view selectedRange];
--- /dev/null
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 23e879c..b7b2665 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -831,7 +831,7 @@ find_window_for_ns_event (NSEvent *nsevent,
+ {
+ g_warning ("grab_toplevel == NULL");
+ }
+- else
++ else if (!gdk_window_is_destroyed (grab_toplevel))
+ {
+ get_window_point_from_screen_point (grab_toplevel,
+ screen_point, x, y);
--- /dev/null
+diff --git a/modules/input/imquartz.c b/modules/input/imquartz.c
+index 67c7d6c..a8ebd02 100644
+--- a/modules/input/imquartz.c
++++ b/modules/input/imquartz.c
+@@ -132,6 +132,8 @@ output_result (GtkIMContext *context,
+ GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
+ gboolean retval = FALSE;
+ gchar *fixed_str, *marked_str;
++ gboolean needs_commit = FALSE;
++ gboolean needs_preedit_change = FALSE;
+
+ fixed_str = g_object_get_data (G_OBJECT (win), TIC_INSERT_TEXT);
+ marked_str = g_object_get_data (G_OBJECT (win), TIC_MARKED_TEXT);
+@@ -141,8 +143,8 @@ output_result (GtkIMContext *context,
+ g_free (qc->preedit_str);
+ qc->preedit_str = NULL;
+ g_object_set_data (G_OBJECT (win), TIC_INSERT_TEXT, NULL);
+- g_signal_emit_by_name (context, "commit", fixed_str);
+- g_signal_emit_by_name (context, "preedit_changed");
++ needs_commit = TRUE;
++ needs_preedit_change = TRUE;
+
+ unsigned int filtered =
+ GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (win),
+@@ -162,10 +164,11 @@ output_result (GtkIMContext *context,
+ qc->selected_len =
+ GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (win),
+ TIC_SELECTED_LEN));
+- g_free (qc->preedit_str);
++ if (qc->preedit_str)
++ g_free (qc->preedit_str);
+ qc->preedit_str = g_strdup (marked_str);
+ g_object_set_data (G_OBJECT (win), TIC_MARKED_TEXT, NULL);
+- g_signal_emit_by_name (context, "preedit_changed");
++ needs_preedit_change = TRUE;
+ retval = TRUE;
+ }
+ if (!fixed_str && !marked_str)
+@@ -174,8 +177,15 @@ output_result (GtkIMContext *context,
+ retval = TRUE;
+ }
+
+- g_free (fixed_str);
+- g_free (marked_str);
++ if (needs_commit)
++ g_signal_emit_by_name (context, "commit", fixed_str);
++ if (needs_preedit_change)
++ g_signal_emit_by_name (context, "preedit-changed");
++
++ if (fixed_str)
++ g_free (fixed_str);
++ if (marked_str)
++ g_free (marked_str);
+
+ return retval;
+ }
+@@ -261,8 +271,6 @@ discard_preedit (GtkIMContext *context)
+
+ if (qc->preedit_str && strlen (qc->preedit_str) > 0)
+ {
+- g_signal_emit_by_name (context, "commit", qc->preedit_str);
+-
+ g_free (qc->preedit_str);
+ qc->preedit_str = NULL;
+ g_signal_emit_by_name (context, "preedit_changed");
--- /dev/null
+commit 33da74a9ab13bba4f46b2455cc858c7ae02bd923
+Author: Cody Russell <cody@jhu.edu>
+Date: Mon Mar 31 08:51:25 2014 -0500
+
+ This is an attempt to solve an issue in GtkNSView that occurs in
+ Xamarin Studio when we try to use an animating NSProgressIndicator.
+ The error only occurred if the indicator was in animating mode,
+ and it seems likely to be a threading race condition somewhere.
+
+ This patch does two things. I've found the issue to be reproducible
+ if we do one of these things but not the other, but I have not been
+ able to reproduce it with both of these.
+
+ First, we slightly defer the mapping of the widget.
+ Second, we lock the view during clip_to_parent_viewports().
+
+ https://bugzilla.xamarin.com/show_bug.cgi?id=17401
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index d5eb307..31c4970 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -280,6 +280,7 @@ clip_to_parent_viewports (GtkNSView *ns_view,
+
+ ns_view = get_associated_gtknsview (self);
+
++ [self lockFocusIfCanDraw];
+ if (ns_view)
+ cg_context = clip_to_parent_viewports (ns_view, self);
+
+@@ -287,6 +288,8 @@ clip_to_parent_viewports (GtkNSView *ns_view,
+
+ if (cg_context)
+ CGContextRestoreGState (cg_context);
++
++ [self unlockFocus];
+ }
+ @end
+
+@@ -566,15 +569,17 @@ gtk_ns_view_unrealize (GtkWidget *widget)
+ GTK_WIDGET_CLASS (gtk_ns_view_parent_class)->unrealize (widget);
+ }
+
+-static void
+-gtk_ns_view_map (GtkWidget *widget)
+-{
++static gboolean
++really_map (GtkWidget *widget) {
+ GtkNSView *ns_view = GTK_NS_VIEW (widget);
+ GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+ GtkAllocation allocation;
+ NSView *parent_view;
+ NSWindow *window;
+
++ if (gtk_widget_get_mapped (widget))
++ return FALSE;
++
+ gtk_widget_get_allocation (widget, &allocation);
+ gtk_ns_view_position_view (ns_view, &allocation);
+
+@@ -605,6 +610,14 @@ gtk_ns_view_map (GtkWidget *widget)
+ gtk_ns_view_swizzle_draw_rect_recursive (text, "gtkwindow", toplevel);
+ }
+ }
++
++ return FALSE;
++}
++
++static void
++gtk_ns_view_map (GtkWidget *widget)
++{
++ g_timeout_add (50, (GSourceFunc)really_map, widget);
+ }
+
+ static void
--- /dev/null
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index bb9ae06..6820113 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -534,7 +534,7 @@ gtk_ns_view_notify (GObject *object,
+ GParamSpec *pspec)
+ {
+ GtkNSView *ns_view = GTK_NS_VIEW (object);
+- GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (object)));
++ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object));
+
+ if (G_OBJECT_CLASS (gtk_ns_view_parent_class)->notify)
+ G_OBJECT_CLASS (gtk_ns_view_parent_class)->notify (object, pspec);
+@@ -551,7 +551,7 @@ gtk_ns_view_notify (GObject *object,
+
+ if (gtk_widget_has_focus (GTK_WIDGET (object)))
+ [ns_window makeFirstResponder:ns_view->priv->view];
+- else if ([ns_window firstResponder] == ns_view->priv->view || !gtk_window_is_active (window))
++ else if ([ns_window firstResponder] == ns_view->priv->view || (GTK_IS_WINDOW (toplevel) && !gtk_window_is_active (GTK_WINDOW (toplevel))))
+ [ns_window makeFirstResponder:nil];
+ }
+ }
--- /dev/null
+commit ea748772ee7fd99d70a1ce119bc377c3e2083954
+Author: Cody Russell <cody@jhu.edu>
+Date: Tue Jan 13 16:46:03 2015 -0600
+
+ [GtkNSView] Add support for forwarding Cmd-Z to an NSTextView's undo manager
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index d5f8c76..9ba080d 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -712,6 +712,11 @@ gtk_ns_view_key_press (GtkWidget *widget,
+
+ switch (event->keyval)
+ {
++ case GDK_KEY_z: /* undo */
++ if ([[text_view undoManager] canUndo])
++ [[text_view undoManager] undo];
++ return TRUE;
++
+ case GDK_KEY_c: /* copy */
+ if (has_selection)
+ [text_view copy: text_view];
--- /dev/null
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index 2540a81..e5c48c3 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -914,5 +914,13 @@ gtk_ns_view_new (gpointer nsview)
+ NULL);
+ }
+
++gpointer
++gtk_ns_view_get_nsview (GtkNSView *gtknsview)
++{
++ g_return_val_if_fail (GTK_IS_NS_VIEW (gtknsview), NULL);
++
++ return gtknsview->priv->view;
++}
++
+ #define __GTK_NS_VIEW_C__
+ #include "gtkaliasdef.c"
+diff --git a/gtk/gtknsview.h b/gtk/gtknsview.h
+index 2c0aab7..a923827 100644
+--- a/gtk/gtknsview.h
++++ b/gtk/gtknsview.h
+@@ -56,6 +56,7 @@ struct _GtkNSViewClass
+
+ GType gtk_ns_view_get_type (void) G_GNUC_CONST;
+ GtkWidget * gtk_ns_view_new (gpointer nsview);
++gpointer gtk_ns_view_get_nsview (GtkNSView *gtknsview);
+
+ G_END_DECLS
--- /dev/null
+commit f35387716481b79c353f098cb80ded665bd7d399
+Author: Cody Russell <cody@jhu.edu>
+Date: Sat Feb 28 11:27:07 2015 -0600
+
+ GtkNSView should only unset the first responder if either it is
+ our embedded view or if our toplevel window is no longer focused.
+
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index b325bda..645231d 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -534,6 +534,7 @@ gtk_ns_view_notify (GObject *object,
+ GParamSpec *pspec)
+ {
+ GtkNSView *ns_view = GTK_NS_VIEW (object);
++ GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (object)));
+
+ if (G_OBJECT_CLASS (gtk_ns_view_parent_class)->notify)
+ G_OBJECT_CLASS (gtk_ns_view_parent_class)->notify (object, pspec);
+@@ -550,7 +551,7 @@ gtk_ns_view_notify (GObject *object,
+
+ if (gtk_widget_has_focus (GTK_WIDGET (object)))
+ [ns_window makeFirstResponder:ns_view->priv->view];
+- else
++ else if ([ns_window firstResponder] == ns_view->priv->view || !gtk_window_is_active (window))
+ [ns_window makeFirstResponder:nil];
+ }
+ }
--- /dev/null
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index 31c4970..ad003d9 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -48,6 +48,7 @@ enum
+ struct _GtkNSViewPrivate
+ {
+ NSView *view;
++ guint map_timeout;
+ };
+
+ #define GTK_NS_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+@@ -563,6 +564,15 @@ gtk_ns_view_position_view (GtkNSView *ns_view,
+ static void
+ gtk_ns_view_unrealize (GtkWidget *widget)
+ {
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++
++ if (ns_view->priv->map_timeout != 0)
++ {
++ g_source_remove (ns_view->priv->map_timeout);
++ ns_view->priv->map_timeout = 0;
++ g_object_unref (widget);
++ }
++
+ if (gtk_widget_get_mapped (widget))
+ gtk_widget_unmap (widget);
+
+@@ -577,6 +587,8 @@ really_map (GtkWidget *widget) {
+ NSView *parent_view;
+ NSWindow *window;
+
++ g_object_unref (widget);
++
+ if (gtk_widget_get_mapped (widget))
+ return FALSE;
+
+@@ -611,13 +623,18 @@ really_map (GtkWidget *widget) {
+ }
+ }
+
++ ns_view->priv->map_timeout = 0;
++
+ return FALSE;
+ }
+
+ static void
+ gtk_ns_view_map (GtkWidget *widget)
+ {
+- g_timeout_add (50, (GSourceFunc)really_map, widget);
++ GtkNSView *ns_view = GTK_NS_VIEW (widget);
++
++ g_object_ref (widget);
++ ns_view->priv->map_timeout = g_timeout_add (50, (GSourceFunc)really_map, widget);
+ }
+
+ static void
--- /dev/null
+From 1646b8a78f00cbddc00f6138d80fbc42dec82dac Mon Sep 17 00:00:00 2001
+From: iain holmes <iain@xamarin.com>
+Date: Tue, 13 Sep 2016 16:06:39 +0100
+Subject: [PATCH] [a11y] Make the GtkPaned widget emit GtkContainer::add
+ signals
+
+---
+ gtk/gtkpaned.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c
+index f6225f0..0e3afd6 100644
+--- a/gtk/gtkpaned.c
++++ b/gtk/gtkpaned.c
+@@ -1375,6 +1375,10 @@ gtk_paned_pack1 (GtkPaned *paned,
+ paned->child1_shrink = shrink;
+
+ gtk_widget_set_parent (child, GTK_WIDGET (paned));
++
++ /* We need to emit this signal so that the accessibility system knows
++ that a widget has been added to the pane */
++ g_signal_emit_by_name (G_OBJECT (paned), "add", child);
+ }
+ }
+
+@@ -1394,6 +1398,10 @@ gtk_paned_pack2 (GtkPaned *paned,
+ paned->child2_shrink = shrink;
+
+ gtk_widget_set_parent (child, GTK_WIDGET (paned));
++
++ /* We need to emit this signal so that the accessibility system knows
++ that a widget has been added to the pane */
++ g_signal_emit_by_name (G_OBJECT (paned), "add", child);
+ }
+ }
+
+@@ -1406,6 +1414,11 @@ gtk_paned_add (GtkContainer *container,
+
+ g_return_if_fail (GTK_IS_PANED (container));
+
++ /* Break the add signal cycle */
++ if (widget->parent == container) {
++ return;
++ }
++
+ paned = GTK_PANED (container);
+
+ if (!paned->child1)
--- /dev/null
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 183537f..ee7dc96 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -1831,7 +1831,10 @@ gdk_event_translate (GdkEvent *event,
+ if (tmp_view && [tmp_view respondsToSelector:@selector(isGtkView)])
+ gtk_child = TRUE;
+
+- tmp_view = [tmp_view superview];
++ if ([tmp_view respondsToSelector:@selector(superview)])
++ tmp_view = [tmp_view superview];
++ else
++ tmp_view = NULL;
+ }
+
+ if (!gtk_child && ![[nswindow firstResponder] respondsToSelector:@selector(isGtkView)])
--- /dev/null
+commit 291c4626867885c74aa3a544eb9f74cd974895a0
+Author: Cody Russell <cody@jhu.edu>
+Date: Wed Jan 14 14:56:34 2015 -0600
+
+ When determining if the event is destined for an NSView outside our
+ view hierarchy, we can't just check if it's not a gtkview, we need to
+ also ensure that none of its superviews are gtkviews.
+
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 31c5d33..183537f 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -1823,7 +1823,18 @@ gdk_event_translate (GdkEvent *event,
+ GdkQuartzWindow *nswindow = ((GdkWindowImplQuartz *)private->impl)->toplevel;
+ GdkQuartzView *nsview = ((GdkWindowImplQuartz *)private->impl)->view;
+
+- if (![[nswindow firstResponder] respondsToSelector:@selector(isGtkView)])
++ NSView *tmp_view = [nswindow firstResponder];
++ gboolean gtk_child = FALSE;
++
++ while (tmp_view != NULL)
++ {
++ if (tmp_view && [tmp_view respondsToSelector:@selector(isGtkView)])
++ gtk_child = TRUE;
++
++ tmp_view = [tmp_view superview];
++ }
++
++ if (!gtk_child && ![[nswindow firstResponder] respondsToSelector:@selector(isGtkView)])
+ {
+ return_val = FALSE;
+ break;
--- /dev/null
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index 6d4abc0..cf8b626 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -1468,6 +1468,8 @@ gdk_window_new (GdkWindow *parent,
+ private->parent->children = g_list_prepend (private->parent->children, window);
+
+ native = _gdk_native_windows; /* Default */
++ if (attributes->type_hint == 100)
++ native = TRUE;
+ if (private->parent->window_type == GDK_WINDOW_ROOT)
+ native = TRUE; /* Always use native windows for toplevels */
+ else if (!private->input_only &&
+diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
+index 95a3665..e9428d0 100644
+--- a/gdk/gdkwindow.h
++++ b/gdk/gdkwindow.h
+@@ -722,6 +722,9 @@ void gdk_window_redirect_to_drawable (GdkWindow *window,
+ gint height);
+ void gdk_window_remove_redirection (GdkWindow *window);
+
++/* NSView embedding */
++gboolean gdk_window_supports_nsview_embedding ();
++
+ #ifndef GDK_DISABLE_DEPRECATED
+ #ifndef GDK_MULTIHEAD_SAFE
+ GdkPointerHooks *gdk_set_pointer_hooks (const GdkPointerHooks *new_hooks);
+diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c
+index 1c92714..3c7ba37 100644
+--- a/gdk/quartz/GdkQuartzView.c
++++ b/gdk/quartz/GdkQuartzView.c
+@@ -122,6 +122,11 @@
+ return selectedRange;
+ }
+
++-(BOOL)isGtkView
++{
++ return YES;
++}
++
+ -(void)unmarkText
+ {
+ GDK_NOTE (EVENTS, g_print ("unmarkText\n"));
+diff --git a/gdk/quartz/GdkQuartzView.h b/gdk/quartz/GdkQuartzView.h
+index 2f86de7..04acb62 100644
+--- a/gdk/quartz/GdkQuartzView.h
++++ b/gdk/quartz/GdkQuartzView.h
+@@ -47,5 +47,6 @@
+ - (GdkWindow *)gdkWindow;
+ - (NSTrackingRectTag)trackingRect;
+ - (void)setNeedsInvalidateShadow: (BOOL)invalidate;
++- (void)mouseDown:(NSEvent *)theEvent;
+
+ @end
+diff --git a/gdk/quartz/GdkQuartzWindow.c b/gdk/quartz/GdkQuartzWindow.c
+index 8face0c..b6b794e 100644
+--- a/gdk/quartz/GdkQuartzWindow.c
++++ b/gdk/quartz/GdkQuartzWindow.c
+@@ -228,6 +228,28 @@
+ [self checkSendEnterNotify];
+ }
+
++-(BOOL)makeFirstResponder:(NSResponder *)responder
++{
++ GdkWindow *window = [[self contentView] gdkWindow];
++ GdkWindowObject *private = (GdkWindowObject *)window;
++ GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
++
++ if (impl->type_hint == GDK_WINDOW_TYPE_HINT_TOOLBAR)
++ {
++ if ([responder respondsToSelector:@selector(isGtkView)] ||
++ [responder isKindOfClass:[GdkQuartzView class]])
++ {
++ _gdk_quartz_events_update_focus_window (window, TRUE);
++ }
++ else
++ {
++ _gdk_quartz_events_update_focus_window (window, FALSE);
++ }
++ }
++
++ return [super makeFirstResponder:responder];
++}
++
+ -(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag screen:(NSScreen *)screen
+ {
+ self = [super initWithContentRect:contentRect
+diff --git a/gdk/quartz/GdkQuartzWindow.h b/gdk/quartz/GdkQuartzWindow.h
+index 9cdee6b..928f961 100644
+--- a/gdk/quartz/GdkQuartzWindow.h
++++ b/gdk/quartz/GdkQuartzWindow.h
+@@ -44,6 +44,7 @@
+ -(BOOL)trackManualResize;
+ -(void)showAndMakeKey:(BOOL)makeKey;
+ -(void)hide;
++-(BOOL)makeFirstResponder:(NSResponder *)responder;
+
+ @end
+
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index b7b2665..f5bf65a 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -703,6 +703,37 @@ _gdk_quartz_events_send_map_event (GdkWindow *window)
+ }
+ }
+
++static NSView *
++find_nsview_at_pos (GdkWindowImplQuartz *impl, gint x, gint y)
++{
++ NSView *view = impl->view;
++ guint n_subviews;
++ guint i;
++
++ n_subviews = [[view subviews] count];
++
++ for (i = 0; i < n_subviews; ++i)
++ {
++ NSView* sv = [[view subviews] objectAtIndex:i];
++ NSRect r = [sv frame];
++
++ if (sv == impl->layer_view)
++ continue;
++
++ if (r.origin.x <= x && r.origin.x + r.size.width >= x &&
++ r.origin.y <= y && r.origin.y + r.size.height >= y)
++ {
++ NSView* child = find_nsview_at_pos (impl, x - r.origin.x, y - r.origin.y);
++ if (child != NULL)
++ return child;
++ else
++ return sv;
++ }
++ }
++
++ return NULL;
++}
++
+ static GdkWindow *
+ find_toplevel_under_pointer (GdkDisplay *display,
+ NSPoint screen_point,
+@@ -869,34 +900,21 @@ find_window_for_ns_event (NSEvent *nsevent,
+ {
+ GdkWindowObject *toplevel_private;
+ GdkWindowImplQuartz *toplevel_impl;
+- guint n_subviews;
+- guint i;
++ NSView *subview;
+
+ toplevel = toplevel_under_pointer;
+
+ toplevel_private = (GdkWindowObject *)toplevel;
+ toplevel_impl = (GdkWindowImplQuartz *)toplevel_private->impl;
+
+- n_subviews = [[toplevel_impl->view subviews] count];
+-
+- for (i = 0; i < n_subviews; ++i)
+- {
+- NSView* sv = [[toplevel_impl->view subviews] objectAtIndex:i];
+- NSRect r = [sv frame];
+-
+- if (sv == toplevel_impl->layer_view)
+- continue;
+-
+- if (r.origin.x <= *x && r.origin.x + r.size.width >= *x &&
+- r.origin.y <= *y && r.origin.y + r.size.height >= *y)
+- {
+- g_signal_emit_by_name (toplevel, "native-child-event",
+- sv, nsevent);
++ subview = find_nsview_at_pos (toplevel_impl, *x, *y);
++ if (subview != NULL && ![subview isKindOfClass:[GdkQuartzView class]]) {
++ g_signal_emit_by_name (toplevel, "native-child-event",
++ subview, nsevent);
+
+- /* event is within subview, forward back to Cocoa */
+- return NULL;
+- }
+- }
++ /* event is within subview, forward back to Cocoa */
++ return NULL;
++ }
+
+ *x = x_tmp;
+ *y = y_tmp;
+@@ -1665,6 +1683,11 @@ gdk_event_translate (GdkEvent *event,
+ GdkWindowObject *private = (GdkWindowObject *)window;
+ GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
+
++ GdkQuartzWindow *nswindow = ((GdkWindowImplQuartz *)private->impl)->toplevel;
++ GdkQuartzView *nsview = ((GdkWindowImplQuartz *)private->impl)->view;
++
++ [nswindow makeFirstResponder:nsview];
++
+ if (![NSApp isActive])
+ {
+ [NSApp activateIgnoringOtherApps:YES];
+@@ -1796,6 +1819,15 @@ gdk_event_translate (GdkEvent *event,
+ case NSFlagsChanged:
+ {
+ GdkEventType type;
++ GdkWindowObject *private = (GdkWindowObject *)window;
++ GdkQuartzWindow *nswindow = ((GdkWindowImplQuartz *)private->impl)->toplevel;
++ GdkQuartzView *nsview = ((GdkWindowImplQuartz *)private->impl)->view;
++
++ if (![[nswindow firstResponder] respondsToSelector:@selector(isGtkView)])
++ {
++ return_val = FALSE;
++ break;
++ }
+
+ type = _gdk_quartz_keys_event_type (nsevent);
+ if (type == GDK_NOTHING)
+diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c
+index 5b9ceef..050139a 100644
+--- a/gdk/quartz/gdkwindow-quartz.c
++++ b/gdk/quartz/gdkwindow-quartz.c
+@@ -889,6 +889,12 @@ get_nsscreen_for_point (gint x, gint y)
+ return screen;
+ }
+
++gboolean
++gdk_window_supports_nsview_embedding ()
++{
++ return TRUE;
++}
++
+ void
+ _gdk_window_impl_new (GdkWindow *window,
+ GdkWindow *real_parent,
--- /dev/null
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index ee7dc96..f4ae401 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -1826,6 +1826,33 @@ gdk_event_translate (GdkEvent *event,
+ NSView *tmp_view = [nswindow firstResponder];
+ gboolean gtk_child = FALSE;
+
++ if (event_type == NSKeyDown && ([nsevent modifierFlags] & NSCommandKeyMask) != 0 && [[nsevent characters] characterAtIndex:0] == 'z')
++ {
++ if ([tmp_view respondsToSelector:@selector(undoManager)])
++ {
++ NSUndoManager *undo_manager = [tmp_view undoManager];
++
++ if (([nsevent modifierFlags] & NSShiftKeyMask) != 0)
++ {
++ if ([undo_manager canRedo])
++ {
++ [undo_manager redo];
++ return_val = FALSE;
++ break;
++ }
++ }
++ else
++ {
++ if ([undo_manager canUndo])
++ {
++ [undo_manager undo];
++ return_val = FALSE;
++ break;
++ }
++ }
++ }
++ }
++
+ while (tmp_view != NULL)
+ {
+ if (tmp_view && [tmp_view respondsToSelector:@selector(isGtkView)])
+diff --git a/gtk/gtknsview.c b/gtk/gtknsview.c
+index 71fd917..182c82d 100644
+--- a/gtk/gtknsview.c
++++ b/gtk/gtknsview.c
+@@ -742,11 +742,6 @@ gtk_ns_view_key_press (GtkWidget *widget,
+
+ switch (event->keyval)
+ {
+- case GDK_KEY_z: /* undo */
+- if ([[text_view undoManager] canUndo])
+- [[text_view undoManager] undo];
+- return TRUE;
+-
+ case GDK_KEY_c: /* copy */
+ if (has_selection)
+ [text_view copy: text_view];
--- /dev/null
+commit d91f9583f6a3b0d40912ba35a71534255500b371
+Author: Alexis Christoforides <alexis@thenull.net>
+Date: Fri Mar 6 15:30:34 2015 -0500
+
+ Remove demos from build, they fail when using a gdk-pixbuf that is staged.
+
+diff --git a/Makefile.am b/Makefile.am
+index 8e3a2f1..7288db2 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1,7 +1,7 @@
+ ## Makefile.am for GTK+
+ include $(top_srcdir)/Makefile.decl
+
+-SRC_SUBDIRS = gdk gtk modules demos tests perf
++SRC_SUBDIRS = gdk gtk modules tests perf
+ SUBDIRS = po po-properties $(SRC_SUBDIRS) docs m4macros build
+
+ # require automake 1.4
--- /dev/null
+commit ad48f4d52bbac6139dd829fcc421ad16441f34d2
+Author: Cody Russell <bratsche@gnome.org>
+Date: Tue Sep 21 16:18:22 2010 -0500
+
+ Remove mouse scrolling from GtkNotebook tabs. Bug #630226.
+
+diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
+index 0770de6..9931b4c 100644
+--- a/gtk/gtknotebook.c
++++ b/gtk/gtknotebook.c
+@@ -257,8 +257,6 @@ static void gtk_notebook_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+ static gint gtk_notebook_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+-static gboolean gtk_notebook_scroll (GtkWidget *widget,
+- GdkEventScroll *event);
+ static gint gtk_notebook_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+ static gint gtk_notebook_button_release (GtkWidget *widget,
+@@ -542,7 +540,6 @@ gtk_notebook_class_init (GtkNotebookClass *class)
+ widget_class->size_request = gtk_notebook_size_request;
+ widget_class->size_allocate = gtk_notebook_size_allocate;
+ widget_class->expose_event = gtk_notebook_expose;
+- widget_class->scroll_event = gtk_notebook_scroll;
+ widget_class->button_press_event = gtk_notebook_button_press;
+ widget_class->button_release_event = gtk_notebook_button_release;
+ widget_class->popup_menu = gtk_notebook_popup_menu;
+@@ -1658,7 +1655,6 @@ gtk_notebook_get_property (GObject *object,
+ * gtk_notebook_size_request
+ * gtk_notebook_size_allocate
+ * gtk_notebook_expose
+- * gtk_notebook_scroll
+ * gtk_notebook_button_press
+ * gtk_notebook_button_release
+ * gtk_notebook_popup_menu
+@@ -1852,8 +1848,7 @@ gtk_notebook_realize (GtkWidget *widget)
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
+- GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK |
+- GDK_SCROLL_MASK);
++ GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
+ attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+ notebook->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
+@@ -2604,49 +2599,6 @@ get_widget_coordinates (GtkWidget *widget,
+ return FALSE;
+ }
+
+-static gboolean
+-gtk_notebook_scroll (GtkWidget *widget,
+- GdkEventScroll *event)
+-{
+- GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
+- GtkNotebook *notebook = GTK_NOTEBOOK (widget);
+- GtkWidget *child, *event_widget;
+- gint i;
+-
+- if (!notebook->cur_page)
+- return FALSE;
+-
+- child = notebook->cur_page->child;
+- event_widget = gtk_get_event_widget ((GdkEvent *)event);
+-
+- /* ignore scroll events from the content of the page */
+- if (!event_widget || gtk_widget_is_ancestor (event_widget, child) || event_widget == child)
+- return FALSE;
+-
+- /* nor from the action area */
+- for (i = 0; i < 2; i++)
+- {
+- if (event_widget == priv->action_widget[i] ||
+- (priv->action_widget[i] &&
+- gtk_widget_is_ancestor (event_widget, priv->action_widget[i])))
+- return FALSE;
+- }
+-
+- switch (event->direction)
+- {
+- case GDK_SCROLL_RIGHT:
+- case GDK_SCROLL_DOWN:
+- gtk_notebook_next_page (notebook);
+- break;
+- case GDK_SCROLL_LEFT:
+- case GDK_SCROLL_UP:
+- gtk_notebook_prev_page (notebook);
+- break;
+- }
+-
+- return TRUE;
+-}
+-
+ static GList*
+ get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
+ {
--- /dev/null
+diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
+index 02920ce..3999d7d 100644
+--- a/gtk/gtkscrolledwindow.c
++++ b/gtk/gtkscrolledwindow.c
+@@ -442,6 +442,13 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
+ GTK_PARAM_READABLE));
+
+ gtk_widget_class_install_style_property (widget_class,
++ g_param_spec_boolean ("draw-child-bg",
++ P_("Draw Child Background"),
++ P_("Fill window with child background color"),
++ FALSE,
++ GTK_PARAM_READABLE));
++
++ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_int ("scrollbar-spacing",
+ P_("Scrollbar spacing"),
+ P_("Number of pixels between the scrollbars and the scrolled window"),
+@@ -1296,8 +1303,10 @@ gtk_scrolled_window_paint (GtkWidget *widget,
+ GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+ GdkRectangle *area = &event->area;
+ GtkAllocation relative_allocation;
++ gboolean draw_child_bg;
++ gtk_widget_style_get (widget, "draw-child-bg", &draw_child_bg, NULL);
+
+- if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
++ if (draw_child_bg || scrolled_window->shadow_type != GTK_SHADOW_NONE)
+ {
+ gboolean scrollbars_within_bevel;
+
+@@ -1322,13 +1331,47 @@ gtk_scrolled_window_paint (GtkWidget *widget,
+ relative_allocation.height = widget->allocation.height - 2 * container->border_width;
+ }
+
+- gtk_paint_shadow (widget->style, widget->window,
+- GTK_STATE_NORMAL, scrolled_window->shadow_type,
+- area, widget, "scrolled_window",
+- widget->allocation.x + relative_allocation.x,
+- widget->allocation.y + relative_allocation.y,
+- relative_allocation.width,
+- relative_allocation.height);
++ if (draw_child_bg)
++ {
++ GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
++ if (child != NULL)
++ {
++ GdkColor *bgcolor = NULL;
++ if (GTK_IS_TREE_VIEW (child))
++ {
++ gtk_widget_style_get (child, "even-row-color", &bgcolor, NULL);
++ if (!bgcolor)
++ bgcolor = &child->style->base[child->state];
++ } else
++ bgcolor = &child->style->bg[child->state];
++
++ if (bgcolor)
++ {
++ cairo_t *cr = gdk_cairo_create (widget->window);
++ cairo_rectangle (cr,
++ widget->allocation.x + relative_allocation.x,
++ widget->allocation.y + relative_allocation.y,
++ relative_allocation.width,
++ relative_allocation.height);
++ cairo_clip_preserve (cr);
++ gdk_cairo_set_source_color (cr, bgcolor);
++ cairo_fill (cr);
++ cairo_destroy (cr);
++ }
++ }
++ }
++
++ if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
++ {
++
++ gtk_paint_shadow (widget->style, widget->window,
++ GTK_STATE_NORMAL, scrolled_window->shadow_type,
++ area, widget, "scrolled_window",
++ widget->allocation.x + relative_allocation.x,
++ widget->allocation.y + relative_allocation.y,
++ relative_allocation.width,
++ relative_allocation.height);
++ }
+ }
+ }
--- /dev/null
+diff --git a/scripts/Makefile.am b/scripts/Makefile.am
+index 73b9cea..98b8cfb 100644
+--- a/scripts/Makefile.am
++++ b/scripts/Makefile.am
+@@ -179,7 +179,8 @@ REWRITE_COMMON = sed \
+ -e 's,@''bindir@,$(bindir),g' \
+ -e 's,@''plat_bindir@,$(plat_bindir),g' \
+ -e 's,@''mono_instdir@,$(mono_instdir),g' \
+- -e 's,@''gtkdir@,$(gtkdir),g'
++ -e 's,@''gtkdir@,$(gtkdir),g' \
++ -e 's,@''mono_version@,$(VERSION),g'
+
+ REWRITE = $(REWRITE_COMMON) -e 's,@''mono_interp@,$(mono_interp),g'
+ REWRITE_DEBUG = $(REWRITE_COMMON) -e 's,@''mono_interp@,$(mono_interp) --debug,g'
+diff --git a/scripts/mcs.in b/scripts/mcs.in
+index 32498fa..c15087e 100644
+--- a/scripts/mcs.in
++++ b/scripts/mcs.in
+@@ -1,2 +1,4 @@
+ #!/bin/sh
++export PATH=$PATH:/Library/Frameworks/Mono.framework/Versions/@mono_version@/bin
++export PKG_CONFIG_PATH=/Library/Frameworks/Mono.framework/External/pkgconfig:/Library/Frameworks/Mono.framework/Versions/@mono_version@/lib/pkgconfig:/Library/Frameworks/Mono.framework/Versions/@mono_version@/share/pkgconfig:$PKG_CONFIG_PATH
+ exec @bindir@/mono $MONO_OPTIONS @mono_instdir@/2.0/mcs.exe -lib:@mono_instdir@/2.0 -lib:@mono_instdir@/3.5 "$@"
--- /dev/null
+diff --git a/src/murrine_draw.c b/src/murrine_draw.c
+index 4cab20f..06ed9b1 100644
+--- a/src/murrine_draw.c
++++ b/src/murrine_draw.c
+@@ -1625,10 +1625,6 @@ murrine_draw_scrollbar_slider (cairo_t *cr,
+ }
+
+ cairo_restore (cr);
+-
+- murrine_set_color_rgb (cr, &border);
+- murrine_rounded_rectangle (cr, 0.5, 0.5, width-1, height-1, widget->roundness, widget->corners);
+- cairo_stroke (cr);
+ }
+
+ static void
+@@ -1791,21 +1787,15 @@ murrine_draw_normal_arrow (cairo_t *cr,
+ {
+ double arrow_width;
+ double arrow_height;
+- double line_width_2;
+
+- cairo_save (cr);
+-
+- arrow_width = MIN (height*2.0 + MAX (1.0, ceil (height*2.0/6.0*2.0)/2.0)/2.0, width);
+- line_width_2 = MAX (1.0, ceil (arrow_width/6.0*2.0)/2.0)/2.0;
+- arrow_height = arrow_width/2.0+line_width_2;
++ arrow_width = CLAMP (width, 3, 8);
++ arrow_height = CLAMP (height, 3, 8);
+
+- cairo_translate (cr, x, y-arrow_height/2.0);
++ cairo_save (cr);
+
+- cairo_move_to (cr, -arrow_width/2.0, line_width_2);
+- cairo_line_to (cr, -arrow_width/2.0 + line_width_2, 0);
+- cairo_arc_negative (cr, 0, arrow_height-2*line_width_2-2*line_width_2*sqrt(2), 2*line_width_2, M_PI_2+M_PI_4, M_PI_4);
+- cairo_line_to (cr, arrow_width/2.0-line_width_2, 0);
+- cairo_line_to (cr, arrow_width/2.0, line_width_2);
++ cairo_translate (cr, x, y - arrow_height / 2.0);
++ cairo_move_to (cr, -arrow_width / 2.0, 0);
++ cairo_line_to (cr, arrow_width / 2.0, 0);
+ cairo_line_to (cr, 0, arrow_height);
+ cairo_close_path (cr);
--- /dev/null
+From 3cab26a0468bab855ed2eb13e4f334176e109483 Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Tue, 19 Mar 2013 11:23:09 +0100
+Subject: [PATCH 1/2] Simply process entire bitmap obtained from the core text
+ font
+
+---
+ pango/pangocoretext.c | 3 +--
+ 1 files changed, 1 insertions(+), 2 deletions(-)
+
+diff --git a/pango/pangocoretext.c b/pango/pangocoretext.c
+index 36bcd82..1d46271 100644
+--- a/pango/pangocoretext.c
++++ b/pango/pangocoretext.c
+@@ -89,8 +89,7 @@ ct_font_descriptor_get_coverage (CTFontDescriptorRef desc)
+ bitmap = CFCharacterSetCreateBitmapRepresentation (kCFAllocatorDefault,
+ charset);
+
+- /* We only handle the BMP plane */
+- length = MIN (CFDataGetLength (bitmap), 8192);
++ length = CFDataGetLength (bitmap);
+ ptr = CFDataGetBytePtr (bitmap);
+
+ /* FIXME: can and should this be done more efficiently? */
+--
+1.7.4.4
--- /dev/null
+661f8c0b920f5da Mon Sep 17 00:00:00 2001
+From: Kristian Rietveld <kris@lanedo.com>
+Date: Tue, 19 Mar 2013 11:23:49 +0100
+Subject: [PATCH 2/2] Detect and handle characters encoded in two UTF16 code
+ points
+
+Another important change: gi->index should point at the current
+character, not the current into the string. Before this change,
+the current character equaled the current index into the string.
+---
+ modules/basic/basic-coretext.c | 55 ++++++++++++++++++++++++++++-----------
+ 1 files changed, 39 insertions(+), 16 deletions(-)
+
+diff --git a/modules/basic/basic-coretext.c b/modules/basic/basic-coretext.c
+index 33ce479..06b648e 100644
+--- a/modules/basic/basic-coretext.c
++++ b/modules/basic/basic-coretext.c
+@@ -166,7 +166,42 @@ run_iterator_run_is_non_monotonic (struct RunIterator *iter)
+ static gunichar
+ run_iterator_get_character (struct RunIterator *iter)
+ {
+- return CFStringGetCharacterAtIndex (iter->cstr, iter->current_indices[iter->ct_i]);
++ int lower, upper;
++
++ lower = iter->current_indices[iter->ct_i];
++ if (iter->ct_i + 1 < CTRunGetGlyphCount (iter->current_run))
++ upper = iter->current_indices[iter->ct_i + 1];
++ else
++ {
++ CFRange range = CTRunGetStringRange (iter->current_run);
++ upper = range.location + range.length;
++ }
++
++ if (upper - lower == 1)
++ return CFStringGetCharacterAtIndex (iter->cstr, lower);
++ if (upper - lower == 2)
++ {
++ /* Character is encoded in two UTF16 code points. */
++ gunichar *ch;
++ gunichar retval;
++ gunichar2 orig[2];
++
++ orig[0] = CFStringGetCharacterAtIndex (iter->cstr, lower);
++ orig[1] = CFStringGetCharacterAtIndex (iter->cstr, lower + 1);
++
++ ch = g_utf16_to_ucs4 (orig, 2, NULL, NULL, NULL);
++ retval = *ch;
++ g_free (ch);
++
++ return retval;
++ }
++
++ /* This should not be reached, because other cases cannot occur. Instead
++ * of crashing, return the first character which will likely be displayed
++ * as unknown glyph.
++ */
++
++ return CFStringGetCharacterAtIndex (iter->cstr, lower);
+ }
+
+ static CGGlyph
+@@ -175,12 +210,6 @@ run_iterator_get_cgglyph (struct RunIterator *iter)
+ return iter->current_cgglyphs[iter->ct_i];
+ }
+
+-static CFIndex
+-run_iterator_get_index (struct RunIterator *iter)
+-{
+- return iter->current_indices[iter->ct_i];
+-}
+-
+ static gboolean
+ run_iterator_create (struct RunIterator *iter,
+ const char *text,
+@@ -336,7 +365,7 @@ create_core_text_glyph_list (const char *text,
+ struct GlyphInfo *gi;
+
+ gi = g_slice_new (struct GlyphInfo);
+- gi->index = run_iterator_get_index (&riter);
++ gi->index = riter.total_ct_i;
+ gi->cgglyph = run_iterator_get_cgglyph (&riter);
+ gi->wc = run_iterator_get_character (&riter);
+
+@@ -376,9 +405,8 @@ basic_engine_shape (PangoEngineShape *engine,
+ * glyph sequence generated by the CoreText typesetter:
+ * # E.g. zero-width spaces do not end up in the CoreText glyph sequence. We have
+ * to manually account for the gap in the character indices.
+- * # Sometimes, CoreText generates two glyph for the same character index. We
+- * currently handle this "properly" as in we do not crash or corrupt memory,
+- * but that's about it.
++ * # Sometimes, CoreText generates two glyph for the same character index. These
++ * are properly composed into a single 32-bit gunichar.
+ * # Due to mismatches in size, the CoreText glyph sequence can either be longer or
+ * shorter than the PangoGlyphString. Note that the size of the PangoGlyphString
+ * should match the number of characters in "text".
+@@ -390,11 +418,6 @@ basic_engine_shape (PangoEngineShape *engine,
+ * increasing/decreasing.
+ *
+ * FIXME items for future fixing:
+- * # CoreText strings are UTF16, and the indices *often* refer to characters,
+- * but not *always*. Notable exception is when a character is encoded using
+- * two UTF16 code points. This are two characters in a CFString. At this point
+- * advancing a single character in the CFString and advancing a single character
+- * using g_utf8_next_char in the const char string goes out of sync.
+ * # We currently don't bother about LTR, Pango core appears to fix this up for us.
+ * (Even when we cared warnings were generated that strings were in the wrong
+ * order, this should be investigated).
+--
+1.7.4.4
--- /dev/null
+diff --git a/pango/pangocoretext-fontmap.c b/pango/pangocoretext-fontmap.c
+index 53b2676..2b30a07 100644
+--- a/pango/pangocoretext-fontmap.c
++++ b/pango/pangocoretext-fontmap.c
+@@ -411,6 +411,9 @@ _pango_core_text_font_description_from_ct_font_descriptor (CTFontDescriptorRef d
+ else
+ pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL);
+
++ if ((font_traits & kCTFontCondensedTrait) == kCTFontCondensedTrait)
++ pango_font_description_set_stretch (font_desc, PANGO_STRETCH_CONDENSED);
++
+ if (ct_font_descriptor_is_small_caps (desc))
+ pango_font_description_set_variant (font_desc, PANGO_VARIANT_SMALL_CAPS);
+ else
--- /dev/null
+diff --git a/modules/basic/.libs/basic-coretext.o b/modules/basic/.libs/basic-coretext.o
+index 13cce67..80c3268 100644
+Binary files a/modules/basic/.libs/basic-coretext.o and b/modules/basic/.libs/basic-coretext.o differ
+diff --git a/modules/basic/.libs/pango-basic-coretext.so b/modules/basic/.libs/pango-basic-coretext.so
+index 70bb117..d0940c4 100755
+Binary files a/modules/basic/.libs/pango-basic-coretext.so and b/modules/basic/.libs/pango-basic-coretext.so differ
+diff --git a/modules/basic/basic-coretext.c b/modules/basic/basic-coretext.c
+index c34460a..46d83ff 100644
+--- a/modules/basic/basic-coretext.c
++++ b/modules/basic/basic-coretext.c
+@@ -92,6 +92,7 @@ struct RunIterator
+ CTRunRef current_run;
+ CFIndex *current_indices;
+ const CGGlyph *current_cgglyphs;
++ CGGlyph *current_cgglyphs_buffer;
+ CTRunStatus current_run_status;
+ };
+
+@@ -101,6 +102,9 @@ run_iterator_free_current_run (struct RunIterator *iter)
+ iter->current_run_number = -1;
+ iter->current_run = NULL;
+ iter->current_cgglyphs = NULL;
++ if (iter->current_cgglyphs_buffer)
++ free (iter->current_cgglyphs_buffer);
++ iter->current_cgglyphs_buffer = NULL;
+ if (iter->current_indices)
+ free (iter->current_indices);
+ iter->current_indices = NULL;
+@@ -116,10 +120,18 @@ run_iterator_set_current_run (struct RunIterator *iter,
+
+ iter->current_run_number = run_number;
+ iter->current_run = CFArrayGetValueAtIndex (iter->runs, run_number);
++ ct_glyph_count = CTRunGetGlyphCount (iter->current_run);
++
+ iter->current_run_status = CTRunGetStatus (iter->current_run);
+ iter->current_cgglyphs = CTRunGetGlyphsPtr (iter->current_run);
++ if (!iter->current_cgglyphs)
++ {
++ iter->current_cgglyphs_buffer = (CGGlyph *)malloc (sizeof (CGGlyph) * ct_glyph_count);
++ CTRunGetGlyphs (iter->current_run, CFRangeMake (0, ct_glyph_count),
++ iter->current_cgglyphs_buffer);
++ iter->current_cgglyphs = iter->current_cgglyphs_buffer;
++ }
+
+- ct_glyph_count = CTRunGetGlyphCount (iter->current_run);
+ iter->current_indices = malloc (sizeof (CFIndex *) * ct_glyph_count);
+ CTRunGetStringIndices (iter->current_run, CFRangeMake (0, ct_glyph_count),
+ iter->current_indices);
+@@ -237,6 +249,7 @@ run_iterator_create (struct RunIterator *iter,
+ iter->current_run = NULL;
+ iter->current_indices = NULL;
+ iter->current_cgglyphs = NULL;
++ iter->current_cgglyphs_buffer = NULL;
+
+ /* Create CTLine */
+ attributes = CFDictionaryCreate (kCFAllocatorDefault,
+diff --git a/modules/basic/basic-coretext.c.orig b/modules/basic/basic-coretext.c.orig
+index 0a2c27f..c34460a 100644
+--- a/modules/basic/basic-coretext.c.orig
++++ b/modules/basic/basic-coretext.c.orig
+@@ -166,7 +166,42 @@ run_iterator_run_is_non_monotonic (struct RunIterator *iter)
+ static gunichar
+ run_iterator_get_character (struct RunIterator *iter)
+ {
+- return CFStringGetCharacterAtIndex (iter->cstr, iter->current_indices[iter->ct_i]);
++ int lower, upper;
++
++ lower = iter->current_indices[iter->ct_i];
++ if (iter->ct_i + 1 < CTRunGetGlyphCount (iter->current_run))
++ upper = iter->current_indices[iter->ct_i + 1];
++ else
++ {
++ CFRange range = CTRunGetStringRange (iter->current_run);
++ upper = range.location + range.length;
++ }
++
++ if (upper - lower == 1)
++ return CFStringGetCharacterAtIndex (iter->cstr, lower);
++ if (upper - lower == 2)
++ {
++ /* Character is encoded in two UTF16 code points. */
++ gunichar *ch;
++ gunichar retval;
++ gunichar2 orig[2];
++
++ orig[0] = CFStringGetCharacterAtIndex (iter->cstr, lower);
++ orig[1] = CFStringGetCharacterAtIndex (iter->cstr, lower + 1);
++
++ ch = g_utf16_to_ucs4 (orig, 2, NULL, NULL, NULL);
++ retval = *ch;
++ g_free (ch);
++
++ return retval;
++ }
++
++ /* This should not be reached, because other cases cannot occur. Instead
++ * of crashing, return the first character which will likely be displayed
++ * as unknown glyph.
++ */
++
++ return CFStringGetCharacterAtIndex (iter->cstr, lower);
+ }
+
+ static CGGlyph
+@@ -175,12 +210,6 @@ run_iterator_get_cgglyph (struct RunIterator *iter)
+ return iter->current_cgglyphs[iter->ct_i];
+ }
+
+-static CFIndex
+-run_iterator_get_index (struct RunIterator *iter)
+-{
+- return iter->current_indices[iter->ct_i];
+-}
+-
+ static gboolean
+ run_iterator_create (struct RunIterator *iter,
+ const char *text,
+@@ -190,13 +219,17 @@ run_iterator_create (struct RunIterator *iter,
+ char *copy;
+ CFDictionaryRef attributes;
+ CFAttributedStringRef attstr;
++ int val = 0;
++ CFNumberRef number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &val);
+
+ CFTypeRef keys[] = {
+- (CFTypeRef) kCTFontAttributeName
++ (CFTypeRef) kCTFontAttributeName,
++ kCTLigatureAttributeName
+ };
+
+ CFTypeRef values[] = {
+- ctfont
++ ctfont,
++ number
+ };
+
+ /* Initialize RunIterator structure */
+@@ -209,7 +242,7 @@ run_iterator_create (struct RunIterator *iter,
+ attributes = CFDictionaryCreate (kCFAllocatorDefault,
+ (const void **)keys,
+ (const void **)values,
+- 1,
++ sizeof (keys) / sizeof (keys[0]),
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+@@ -233,6 +266,7 @@ run_iterator_create (struct RunIterator *iter,
+ iter->line = CTLineCreateWithAttributedString (attstr);
+ iter->runs = CTLineGetGlyphRuns (iter->line);
+
++ CFRelease (number);
+ CFRelease (attstr);
+ CFRelease (attributes);
+
+@@ -336,7 +370,7 @@ create_core_text_glyph_list (const char *text,
+ struct GlyphInfo *gi;
+
+ gi = g_slice_new (struct GlyphInfo);
+- gi->index = run_iterator_get_index (&riter);
++ gi->index = riter.total_ct_i;
+ gi->cgglyph = run_iterator_get_cgglyph (&riter);
+ gi->wc = run_iterator_get_character (&riter);
+
+@@ -378,9 +412,8 @@ basic_engine_shape (PangoEngineShape *engine,
+ * glyph sequence generated by the CoreText typesetter:
+ * # E.g. zero-width spaces do not end up in the CoreText glyph sequence. We have
+ * to manually account for the gap in the character indices.
+- * # Sometimes, CoreText generates two glyph for the same character index. We
+- * currently handle this "properly" as in we do not crash or corrupt memory,
+- * but that's about it.
++ * # Sometimes, CoreText generates two glyph for the same character index. These
++ * are properly composed into a single 32-bit gunichar.
+ * # Due to mismatches in size, the CoreText glyph sequence can either be longer or
+ * shorter than the PangoGlyphString. Note that the size of the PangoGlyphString
+ * should match the number of characters in "text".
+@@ -392,11 +425,6 @@ basic_engine_shape (PangoEngineShape *engine,
+ * increasing/decreasing.
+ *
+ * FIXME items for future fixing:
+- * # CoreText strings are UTF16, and the indices *often* refer to characters,
+- * but not *always*. Notable exception is when a character is encoded using
+- * two UTF16 code points. This are two characters in a CFString. At this point
+- * advancing a single character in the CFString and advancing a single character
+- * using g_utf8_next_char in the const char string goes out of sync.
+ * # We currently don't bother about LTR, Pango core appears to fix this up for us.
+ * (Even when we cared warnings were generated that strings were in the wrong
+ * order, this should be investigated).
--- /dev/null
+diff --git a/modules/basic/.libs/basic-coretext.o b/modules/basic/.libs/basic-coretext.o
+index f013cbc..270a4f4 100644
+Binary files a/modules/basic/.libs/basic-coretext.o and b/modules/basic/.libs/basic-coretext.o differ
+diff --git a/modules/basic/.libs/pango-basic-coretext.so b/modules/basic/.libs/pango-basic-coretext.so
+index 2cbf3ef..fec3e0f 100755
+Binary files a/modules/basic/.libs/pango-basic-coretext.so and b/modules/basic/.libs/pango-basic-coretext.so differ
+diff --git a/modules/basic/basic-coretext.c b/modules/basic/basic-coretext.c
+index 82c2b48..c34460a 100644
+--- a/modules/basic/basic-coretext.c
++++ b/modules/basic/basic-coretext.c
+@@ -219,13 +219,17 @@ run_iterator_create (struct RunIterator *iter,
+ char *copy;
+ CFDictionaryRef attributes;
+ CFAttributedStringRef attstr;
++ int val = 0;
++ CFNumberRef number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &val);
+
+ CFTypeRef keys[] = {
+- (CFTypeRef) kCTFontAttributeName
++ (CFTypeRef) kCTFontAttributeName,
++ kCTLigatureAttributeName
+ };
+
+ CFTypeRef values[] = {
+- ctfont
++ ctfont,
++ number
+ };
+
+ /* Initialize RunIterator structure */
+@@ -238,7 +242,7 @@ run_iterator_create (struct RunIterator *iter,
+ attributes = CFDictionaryCreate (kCFAllocatorDefault,
+ (const void **)keys,
+ (const void **)values,
+- 1,
++ sizeof (keys) / sizeof (keys[0]),
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+@@ -262,6 +266,7 @@ run_iterator_create (struct RunIterator *iter,
+ iter->line = CTLineCreateWithAttributedString (attstr);
+ iter->runs = CTLineGetGlyphRuns (iter->line);
+
++ CFRelease (number);
+ CFRelease (attstr);
+ CFRelease (attributes);
--- /dev/null
+diff --git a/pango/pangocoretext-fontmap.c b/pango/pangocoretext-fontmap.c
+index bcbb173..53b2676 100644
+--- a/pango/pangocoretext-fontmap.c
++++ b/pango/pangocoretext-fontmap.c
+@@ -297,7 +297,7 @@ ct_font_descriptor_get_weight (CTFontDescriptorRef desc)
+ cf_number = (CFNumberRef)CFDictionaryGetValue (dict,
+ kCTFontWeightTrait);
+
+- if (CFNumberGetValue (cf_number, kCFNumberCGFloatType, &value))
++ if (cf_number != NULL && CFNumberGetValue (cf_number, kCFNumberCGFloatType, &value))
+ {
+ if (value < ct_weight_min || value > ct_weight_max)
+ {
--- /dev/null
+Index: pango/pango-utils.c
+===================================================================
+--- a/pango/pango-utils.c (revision 2425)
++++ b/pango/pango-utils.c (working copy)
+@@ -567,6 +567,20 @@ read_config_file (const char *filename,
+ gchar *value = g_key_file_get_value(key_file, group, key, &key_error);
+ if (value != NULL)
+ {
++ if (!g_path_is_absolute (value))
++ {
++ gchar *dirname;
++ gchar *absolute_path;
++
++ dirname = g_path_get_dirname (filename);
++ absolute_path = g_build_filename (dirname, value, NULL);
++
++ g_free (dirname);
++ g_free (value);
++
++ value = absolute_path;
++ }
++
+ g_hash_table_insert (ht,
+ g_strdup_printf ("%s/%s", group, key),
+ value);
--- /dev/null
+diff --git a/pango/pangocairo-coretextfontmap.c b/pango/pangocairo-coretextfontmap.c
+index 6cb3809..c75737c 100644
+--- a/pango/pangocairo-coretextfontmap.c
++++ b/pango/pangocairo-coretextfontmap.c
+@@ -185,9 +185,13 @@ pango_cairo_core_text_font_map_load_font (PangoCoreTextFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *description)
+ {
+- char *descname = pango_font_description_get_family (description);
++ const char *descname = pango_font_description_get_family (description);
++ SInt32 major, minor;
+
+- if (strcmp (descname, "-apple-system-font") == 0)
++ Gestalt (gestaltSystemVersionMajor, &major);
++ Gestalt (gestaltSystemVersionMinor, &minor);
++
++ if (major == 10 && minor>= 11 && strcmp (descname, "-apple-system-font") == 0)
+ {
+ PangoCoreTextFont *cfont;
+ NSFont *sysfont;
+@@ -195,7 +199,6 @@ pango_cairo_core_text_font_map_load_font (PangoCoreTextFontMap *fontmap,
+ cairo_matrix_t font_matrix;
+ PangoCoreTextFontsetKey fontset_key;
+ PangoCoreTextFontKey key;
+- PangoLanguage *language;
+ PangoFontDescription *tmp_desc;
+ gboolean synthetic_italic = FALSE;
+ double abs_size = get_scaled_size (fontmap, context, description);
+@@ -207,7 +210,7 @@ pango_cairo_core_text_font_map_load_font (PangoCoreTextFontMap *fontmap,
+ PangoCairoCoreTextFont *cafont = g_object_new (PANGO_TYPE_CAIRO_CORE_TEXT_FONT, NULL);
+ cfont = PANGO_CORE_TEXT_FONT (cafont);
+ cafont->abs_size = abs_size;
+- _pango_core_text_font_set_ctfont (cfont, CFRetain (sysfont));
++ _pango_core_text_font_set_ctfont (cfont, (CTFontRef)CFRetain (sysfont));
+
+ tmp_desc = pango_font_description_copy_static (description);
+
+@@ -261,7 +264,7 @@ pango_cairo_core_text_font_map_load_font (PangoCoreTextFontMap *fontmap,
+ }
+ }
+
+- return PANGO_FONT_MAP_CLASS (pango_cairo_core_text_font_map_parent_class)->load_font (fontmap, context, description);
++ return (PangoCoreTextFont*) PANGO_FONT_MAP_CLASS (pango_cairo_core_text_font_map_parent_class)->load_font (PANGO_FONT_MAP (fontmap), context, description);
+ }
+
+ static void
+diff --git a/pango/pangocoretext-fontmap.c b/pango/pangocoretext-fontmap.c
+index 4234bf1..c395c91 100644
+--- a/pango/pangocoretext-fontmap.c
++++ b/pango/pangocoretext-fontmap.c
+@@ -1367,6 +1367,7 @@ pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap)
+ CTFontCollectionRef collection;
+ CFArrayRef ctfaces;
+ CFIndex i, count;
++ SInt32 major, minor;
+
+ ctfontmap->serial = 1;
+ ctfontmap->families = g_hash_table_new_full (g_str_hash, g_str_equal,
+@@ -1430,7 +1431,10 @@ pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap)
+ CFRelease (dict);
+ }
+
+- if (true)
++ Gestalt (gestaltSystemVersionMajor, &major);
++ Gestalt (gestaltSystemVersionMinor, &minor);
++
++ if (major == 10 && minor >= 11)
+ {
+ NSFont *sysfont = [NSFont systemFontOfSize: 0.0];
+ NSString *name = [[NSFont fontWithName: [[sysfont fontDescriptor] postscriptName] size: 0.0] familyName];
--- /dev/null
+diff --git a/configure.ac b/configure.ac
+index 42c04ef..1260b8a 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -185,7 +185,7 @@ changequote(,)dnl
+ if test "x$GCC" = "xyes"; then
+ case " $CFLAGS " in
+ *[\ \ ]-Wall[\ \ ]*) ;;
+- *) CFLAGS="$CFLAGS -Wall" ;;
++ *) CFLAGS="$CFLAGS -Wall -ObjC" ;;
+ esac
+ fi
+ changequote([,])dnl
+diff --git a/pango/pangocairo-coretext.h b/pango/pangocairo-coretext.h
+index 31faf9b..f562587 100644
+--- a/pango/pangocairo-coretext.h
++++ b/pango/pangocairo-coretext.h
+@@ -43,6 +43,19 @@ struct _PangoCairoCoreTextFontMap
+ gdouble dpi;
+ };
+
++struct _PangoCairoCoreTextFont
++{
++ PangoCoreTextFont font;
++ PangoCairoFontPrivate cf_priv;
++
++ int abs_size;
++};
++
++struct _PangoCairoCoreTextFontClass
++{
++ PangoCoreTextFontClass parent_class;
++};
++
+ GType pango_cairo_core_text_font_map_get_type (void) G_GNUC_CONST;
+
+ PangoCoreTextFont *
+diff --git a/pango/pangocairo-coretextfont.c b/pango/pangocairo-coretextfont.c
+index 0f2a9ef..463b6e2 100644
+--- a/pango/pangocairo-coretextfont.c
++++ b/pango/pangocairo-coretextfont.c
+@@ -23,6 +23,7 @@
+
+ #include "config.h"
+
++#import <AppKit/AppKit.h>
+ #include <Carbon/Carbon.h>
+
+ #include "pango-impl-utils.h"
+@@ -32,21 +33,6 @@
+ #include "pangocairo-coretext.h"
+ #include "pangocairo-coretextfont.h"
+
+-struct _PangoCairoCoreTextFont
+-{
+- PangoCoreTextFont font;
+- PangoCairoFontPrivate cf_priv;
+-
+- int abs_size;
+-};
+-
+-struct _PangoCairoCoreTextFontClass
+-{
+- PangoCoreTextFontClass parent_class;
+-};
+-
+-
+-
+ static cairo_font_face_t *pango_cairo_core_text_font_create_font_face (PangoCairoFont *font);
+ static PangoFontMetrics *pango_cairo_core_text_font_create_base_metrics_for_context (PangoCairoFont *font,
+ PangoContext *context);
+@@ -204,6 +190,75 @@ pango_cairo_core_text_font_init (PangoCairoCoreTextFont *cafont G_GNUC_UNUSED)
+ {
+ }
+
++static gchar *
++gchar_from_cf_string (CFStringRef str)
++{
++ CFIndex len;
++ gchar *buffer;
++
++ /* GetLength returns the number of UTF-16 pairs, so this number
++ * times 2 should definitely give us enough space for UTF8.
++ * We add one for the terminating zero.
++ */
++ len = CFStringGetLength (str) * 2 + 1;
++ buffer = g_new0 (char, len);
++ CFStringGetCString (str, buffer, len, kCFStringEncodingUTF8);
++
++ return buffer;
++}
++
++static gchar *
++ct_font_descriptor_get_family_name (CTFontDescriptorRef desc,
++ gboolean may_fail)
++{
++ CFStringRef cf_str;
++ char *buffer;
++
++ cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontFamilyNameAttribute);
++ if (!cf_str)
++ {
++ int i;
++
++ /* No font family name is set, try to retrieve font name and deduce
++ * the family name from that instead.
++ */
++ cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontNameAttribute);
++ if (!cf_str)
++ {
++ if (may_fail)
++ return NULL;
++
++ /* This font is likely broken, return a default family name ... */
++ return g_strdup ("Sans");
++ }
++
++ buffer = gchar_from_cf_string (cf_str);
++ CFRelease (cf_str);
++
++ for (i = 0; i < strlen (buffer); i++)
++ if (buffer[i] == '-')
++ break;
++
++ if (i < strlen (buffer))
++ {
++ char *ret;
++
++ ret = g_strndup (buffer, i);
++ g_free (buffer);
++
++ return ret;
++ }
++ else
++ return buffer;
++ }
++ /* else */
++
++ buffer = gchar_from_cf_string (cf_str);
++ CFRelease (cf_str);
++
++ return buffer;
++}
++
+ PangoCoreTextFont *
+ _pango_cairo_core_text_font_new (PangoCairoCoreTextFontMap *cafontmap,
+ PangoCoreTextFontKey *key)
+diff --git a/pango/pangocairo-coretextfontmap.c b/pango/pangocairo-coretextfontmap.c
+index ba5dcec..6cb3809 100644
+--- a/pango/pangocairo-coretextfontmap.c
++++ b/pango/pangocairo-coretextfontmap.c
+@@ -26,6 +26,9 @@
+ #include "pangocairo.h"
+ #include "pangocairo-private.h"
+ #include "pangocairo-coretext.h"
++#include "pangocairo-coretextfont.h"
++
++#import <AppKit/Appkit.h>
+
+ typedef struct _PangoCairoCoreTextFontMapClass PangoCairoCoreTextFontMapClass;
+
+@@ -161,6 +164,106 @@ pango_cairo_core_text_font_map_context_key_equal (PangoCoreTextFontMap *fontmap
+ return cairo_font_options_equal (key_a, key_b);
+ }
+
++static int
++get_scaled_size (PangoCoreTextFontMap *fontmap,
++ PangoContext *context,
++ const PangoFontDescription *desc)
++{
++ double size = pango_font_description_get_size (desc);
++
++ if (!pango_font_description_get_size_is_absolute (desc))
++ {
++ double dpi = pango_cairo_core_text_font_map_get_resolution_core_text (fontmap, context);
++ size = size * dpi / 72.;
++ }
++
++ return .5 + pango_matrix_get_font_scale_factor (pango_context_get_matrix (context)) * size;
++}
++
++static PangoCoreTextFont *
++pango_cairo_core_text_font_map_load_font (PangoCoreTextFontMap *fontmap,
++ PangoContext *context,
++ const PangoFontDescription *description)
++{
++ char *descname = pango_font_description_get_family (description);
++
++ if (strcmp (descname, "-apple-system-font") == 0)
++ {
++ PangoCoreTextFont *cfont;
++ NSFont *sysfont;
++ CTFontDescriptorRef ctfontdescriptor;
++ cairo_matrix_t font_matrix;
++ PangoCoreTextFontsetKey fontset_key;
++ PangoCoreTextFontKey key;
++ PangoLanguage *language;
++ PangoFontDescription *tmp_desc;
++ gboolean synthetic_italic = FALSE;
++ double abs_size = get_scaled_size (fontmap, context, description);
++ double size = pango_units_to_double (abs_size) / pango_matrix_get_font_scale_factor (pango_context_get_matrix (context));
++
++ sysfont = [NSFont systemFontOfSize: size];
++ ctfontdescriptor = (CTFontDescriptorRef)[sysfont fontDescriptor];
++
++ PangoCairoCoreTextFont *cafont = g_object_new (PANGO_TYPE_CAIRO_CORE_TEXT_FONT, NULL);
++ cfont = PANGO_CORE_TEXT_FONT (cafont);
++ cafont->abs_size = abs_size;
++ _pango_core_text_font_set_ctfont (cfont, CFRetain (sysfont));
++
++ tmp_desc = pango_font_description_copy_static (description);
++
++ _pango_core_text_fontset_key_init (&fontset_key,
++ fontmap,
++ context,
++ tmp_desc,
++ pango_context_get_language (context));
++ _pango_core_text_font_key_init (&key,
++ fontmap,
++ &fontset_key,
++ ctfontdescriptor,
++ synthetic_italic);
++
++ if (pango_core_text_font_key_get_synthetic_italic (&key))
++ synthetic_italic = TRUE;
++
++ if (synthetic_italic)
++ {
++ cairo_matrix_init (&font_matrix,
++ 1, 0,
++ -0.25, 1,
++ 0, 0);
++ }
++ else
++ {
++ cairo_matrix_init_identity (&font_matrix);
++ }
++
++ cairo_matrix_scale (&font_matrix, size, size);
++
++ _pango_cairo_font_private_initialize (&cafont->cf_priv,
++ (PangoCairoFont *)cafont,
++ pango_core_text_font_key_get_gravity (&key),
++ pango_core_text_font_key_get_context_key (&key),
++ pango_core_text_font_key_get_matrix (&key),
++ &font_matrix);
++
++ PangoCoreTextFont *tmp_font = _pango_core_text_font_map_lookup_font (fontmap, &key);
++ if (tmp_font)
++ {
++ g_object_ref (tmp_font);
++ return tmp_font;
++ }
++ else
++ {
++ g_object_ref (cfont);
++ _pango_core_text_font_map_add (PANGO_CORE_TEXT_FONT_MAP (fontmap), &key, cfont);
++
++ return cfont;
++ }
++ }
++
++ return PANGO_FONT_MAP_CLASS (pango_cairo_core_text_font_map_parent_class)->load_font (fontmap, context, description);
++}
++
+ static void
+ pango_cairo_core_text_font_map_class_init (PangoCairoCoreTextFontMapClass *class)
+ {
+@@ -170,6 +273,7 @@ pango_cairo_core_text_font_map_class_init (PangoCairoCoreTextFontMapClass *class
+
+ object_class->finalize = pango_cairo_core_text_font_map_finalize;
+
++ fontmap_class->load_font = pango_cairo_core_text_font_map_load_font;
+ fontmap_class->get_serial = pango_cairo_core_text_font_map_get_serial;
+ fontmap_class->changed = pango_cairo_core_text_font_map_changed;
+
+diff --git a/pango/pangocoretext-fontmap.c b/pango/pangocoretext-fontmap.c
+index bcbb173..4234bf1 100644
+--- a/pango/pangocoretext-fontmap.c
++++ b/pango/pangocoretext-fontmap.c
+@@ -28,6 +28,7 @@
+ #include "pango-impl-utils.h"
+ #include "modules.h"
+
++#import <AppKit/Appkit.h>
+ #include <Carbon/Carbon.h>
+
+ typedef struct _FontHashKey FontHashKey;
+@@ -826,28 +827,12 @@ get_scaled_size (PangoCoreTextFontMap *fontmap,
+ return .5 + pango_matrix_get_font_scale_factor (pango_context_get_matrix (context)) * size;
+ }
+
+-
+-/*
+- * PangoCoreTextFontsetKey
+- */
+-struct _PangoCoreTextFontsetKey
+-{
+- PangoCoreTextFontMap *fontmap;
+- PangoLanguage *language;
+- PangoFontDescription *desc;
+- PangoMatrix matrix;
+- int pixelsize;
+- double resolution;
+- PangoGravity gravity;
+- gpointer context_key;
+-};
+-
+-static void
+-pango_core_text_fontset_key_init (PangoCoreTextFontsetKey *key,
+- PangoCoreTextFontMap *fontmap,
+- PangoContext *context,
+- const PangoFontDescription *desc,
+- PangoLanguage *language)
++void
++_pango_core_text_fontset_key_init (PangoCoreTextFontsetKey *key,
++ PangoCoreTextFontMap *fontmap,
++ PangoContext *context,
++ const PangoFontDescription *desc,
++ PangoLanguage *language)
+ {
+ if (!language && context)
+ language = pango_context_get_language (context);
+@@ -960,27 +945,12 @@ pango_core_text_fontset_key_get_context_key (const PangoCoreTextFontsetKey *key)
+ return key->context_key;
+ }
+
+-/*
+- * PangoCoreTextFontKey
+- */
+-struct _PangoCoreTextFontKey
+-{
+- PangoCoreTextFontMap *fontmap;
+- CTFontDescriptorRef ctfontdescriptor;
+- PangoMatrix matrix;
+- PangoGravity gravity;
+- int pixelsize;
+- double resolution;
+- gboolean synthetic_italic;
+- gpointer context_key;
+-};
+-
+-static void
+-pango_core_text_font_key_init (PangoCoreTextFontKey *key,
+- PangoCoreTextFontMap *ctfontmap,
+- PangoCoreTextFontsetKey *fontset_key,
+- CTFontDescriptorRef ctdescriptor,
+- gboolean synthetic_italic)
++void
++_pango_core_text_font_key_init (PangoCoreTextFontKey *key,
++ PangoCoreTextFontMap *ctfontmap,
++ PangoCoreTextFontsetKey *fontset_key,
++ CTFontDescriptorRef ctdescriptor,
++ gboolean synthetic_italic)
+ {
+ key->fontmap = ctfontmap;
+ key->ctfontdescriptor = ctdescriptor;
+@@ -1104,14 +1074,24 @@ pango_core_text_font_key_get_ctfontdescriptor (const PangoCoreTextFontKey *key)
+ return key->ctfontdescriptor;
+ }
+
++PangoCoreTextFont *
++_pango_core_text_font_map_lookup_font (PangoCoreTextFontMap *fontmap,
++ PangoCoreTextFontKey *key)
++{
++ return g_hash_table_lookup (fontmap->font_hash, key);
++}
+
+-
+-static void
+-pango_core_text_font_map_add (PangoCoreTextFontMap *ctfontmap,
+- PangoCoreTextFontKey *key,
+- PangoCoreTextFont *ctfont)
++void
++_pango_core_text_font_map_add (PangoCoreTextFontMap *ctfontmap,
++ PangoCoreTextFontKey *key,
++ PangoCoreTextFont *ctfont)
+ {
+ PangoCoreTextFontKey *key_copy;
++ PangoCoreTextFont *tmp;
++
++ tmp = g_hash_table_lookup (ctfontmap->font_hash, key);
++ if (tmp)
++ return;
+
+ _pango_core_text_font_set_font_map (ctfont, ctfontmap);
+
+@@ -1130,8 +1110,8 @@ pango_core_text_font_map_new_font (PangoCoreTextFontMap *fontmap,
+ PangoCoreTextFont *font;
+ PangoCoreTextFontKey key;
+
+- pango_core_text_font_key_init (&key, fontmap, fontset_key, ctfontdescriptor,
+- synthetic_italic);
++ _pango_core_text_font_key_init (&key, fontmap, fontset_key, ctfontdescriptor,
++ synthetic_italic);
+
+ font = g_hash_table_lookup (fontmap->font_hash, &key);
+ if (font)
+@@ -1144,7 +1124,7 @@ pango_core_text_font_map_new_font (PangoCoreTextFontMap *fontmap,
+ if (!font)
+ return NULL;
+
+- pango_core_text_font_map_add (fontmap, &key, font);
++ _pango_core_text_font_map_add (fontmap, &key, font);
+
+ return font;
+ }
+@@ -1295,8 +1275,8 @@ pango_core_text_font_map_load_fontset (PangoFontMap *fontmap,
+ PangoCoreTextFontMap *ctfontmap = PANGO_CORE_TEXT_FONT_MAP (fontmap);
+ static gboolean warned_full_fallback = FALSE; /* MT-safe */
+
+- pango_core_text_fontset_key_init (&key, ctfontmap,
+- context, desc, language);
++ _pango_core_text_fontset_key_init (&key, ctfontmap,
++ context, desc, language);
+
+ fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key);
+
+@@ -1320,8 +1300,8 @@ pango_core_text_font_map_load_fontset (PangoFontMap *fontmap,
+ tmp_desc = pango_font_description_copy_static (desc);
+ pango_font_description_set_family_static (tmp_desc, "Sans");
+
+- pango_core_text_fontset_key_init (&key, ctfontmap, context, tmp_desc,
+- language);
++ _pango_core_text_fontset_key_init (&key, ctfontmap, context, tmp_desc,
++ language);
+
+ fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key);
+ if (G_LIKELY (fontset))
+@@ -1450,6 +1430,53 @@ pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap)
+ CFRelease (dict);
+ }
+
++ if (true)
++ {
++ NSFont *sysfont = [NSFont systemFontOfSize: 0.0];
++ NSString *name = [[NSFont fontWithName: [[sysfont fontDescriptor] postscriptName] size: 0.0] familyName];
++
++ NSArray *fontfaces = [[NSFontManager sharedFontManager] availableMembersOfFontFamily: name];
++ int num_faces = [fontfaces count];
++ CFRelease (name);
++
++ for (int faceindex = 0; faceindex < num_faces; faceindex++)
++ {
++ SInt64 font_traits;
++ char *buffer;
++ char *family_name;
++ CFNumberRef number;
++ CFDictionaryRef dict;
++ CTFontDescriptorRef desc = [sysfont fontDescriptor];
++
++ buffer = ct_font_descriptor_get_family_name (desc, TRUE);
++ if (!buffer)
++ continue;
++
++ family_name = g_utf8_casefold (buffer, -1);
++ family = g_hash_table_lookup (ctfontmap->families, family_name);
++ if (!family)
++ {
++ family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL);
++ g_hash_table_insert (ctfontmap->families, g_strdup ("-apple-system-font"), family);
++ family->family_name = g_strdup (buffer);
++ }
++
++ g_free (buffer);
++ g_free (family_name);
++
++ dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute);
++ number = (CFNumberRef)CFDictionaryGetValue (dict, kCTFontSymbolicTrait);
++
++ if (CFNumberGetValue (number, kCFNumberSInt64Type, &font_traits))
++ {
++ if ((font_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait)
++ family->is_monospace = TRUE;
++ }
++
++ CFRelease (dict);
++ }
++ }
++
+ /* Insert aliases */
+ family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL);
+ family->family_name = g_strdup ("Sans");
+diff --git a/pango/pangocoretext-private.h b/pango/pangocoretext-private.h
+index 8076eae..5c6ce41 100644
+--- a/pango/pangocoretext-private.h
++++ b/pango/pangocoretext-private.h
+@@ -81,6 +81,30 @@ struct _PangoCoreTextFontMapClass
+ PangoContext *context);
+ };
+
++struct _PangoCoreTextFontKey
++{
++ PangoCoreTextFontMap *fontmap;
++ CTFontDescriptorRef ctfontdescriptor;
++ PangoMatrix matrix;
++ PangoGravity gravity;
++ int pixelsize;
++ double resolution;
++ gboolean synthetic_italic;
++ gpointer context_key;
++};
++
++struct _PangoCoreTextFontsetKey
++{
++ PangoCoreTextFontMap *fontmap;
++ PangoLanguage *language;
++ PangoFontDescription *desc;
++ PangoMatrix matrix;
++ int pixelsize;
++ double resolution;
++ PangoGravity gravity;
++ gpointer context_key;
++};
++
+
+ GType pango_core_text_font_map_get_type (void) G_GNUC_CONST;
+
+@@ -97,6 +121,24 @@ void _pango_core_text_font_set_font_key (PangoCoreTextF
+ void _pango_core_text_font_set_ctfont (PangoCoreTextFont *font,
+ CTFontRef font_ref);
+
++void _pango_core_text_fontset_key_init (PangoCoreTextFontsetKey *key,
++ PangoCoreTextFontMap *fontmap,
++ PangoContext *context,
++ const PangoFontDescription *desc,
++ PangoLanguage *language);
++void _pango_core_text_font_key_init (PangoCoreTextFontKey *key,
++ PangoCoreTextFontMap *ctfontmap,
++ PangoCoreTextFontsetKey *fontset_key,
++ CTFontDescriptorRef ctdescriptor,
++ gboolean synthetic_italic);
++
++void _pango_core_text_font_map_add (PangoCoreTextFontMap *ctfontmap,
++ PangoCoreTextFontKey *key,
++ PangoCoreTextFont *ctfont);
++
++PangoCoreTextFont *_pango_core_text_font_map_lookup_font (PangoCoreTextFontMap *fontmap,
++ PangoCoreTextFontKey *key);
++
+ PangoFontDescription *_pango_core_text_font_description_from_ct_font_descriptor (CTFontDescriptorRef desc);
+
+ int pango_core_text_font_key_get_absolute_size (const PangoCoreTextFontKey *key);
--- /dev/null
+diff --git a/libs/tiff-4.0.2/tools/tiffgt.c b/libs/tiff-4.0.2/tools/tiffgt.c
+index de42039..4c8e509 100644
+--- a/tools/tiffgt.c
++++ b/tools/tiffgt.c
+@@ -31,11 +31,16 @@
+ #include <string.h>
+ #include <unistd.h>
+
+-#if HAVE_APPLE_OPENGL_FRAMEWORK
++#if HAVE_OPENGL_GL_H
+ # include <OpenGL/gl.h>
++#endif
++#if HAVE_GLUT_GLUT_H
+ # include <GLUT/glut.h>
+-#else
++#endif
++#if HAVE_GL_GL_H
+ # include <GL/gl.h>
++#endif
++#if HAVE_GL_GLUT_H
+ # include <GL/glut.h>
+ #endif
--- /dev/null
+import glob
+import os
+import shutil
+
+
+class PCLReferenceAssembliesPackage(Package):
+
+ def __init__(self):
+ Package.__init__(self,
+ name='PortableReferenceAssemblies',
+ version='2014-04-14',
+ sources=['http://storage.bos.xamarin.com/bot-provisioning/PortableReferenceAssemblies-2014-04-14.zip'])
+
+ def build(self):
+ pass
+
+ # A bunch of shell script written inside python literals ;(
+ def install(self):
+ dest = os.path.join(
+ self.staged_prefix,
+ "lib",
+ "mono",
+ "xbuild-frameworks",
+ ".NETPortable")
+ if not os.path.exists(dest):
+ os.makedirs(dest)
+
+ shutil.rmtree(dest, ignore_errors=True)
+
+ self.sh("rsync -abv -q %s/* %s" % (self.workspace, dest))
+
+ for f in glob.glob("%s/*/Profile/*/SupportedFrameworks" % dest):
+ self.write_xml(f)
+
+ def write_xml(self, directory):
+ # print "Writing iOS/Android/Mac listings for " + directory
+ data = {
+ os.path.join(directory, "MonoTouch.xml"):
+ """<Framework Identifier="MonoTouch" MinimumVersion="1.0" Profile="*" DisplayName="Xamarin.iOS Classic"/>""",
+ os.path.join(directory, "Xamarin.iOS.xml"):
+ """<Framework Identifier="Xamarin.iOS" MinimumVersion="1.0" Profile="*" DisplayName="Xamarin.iOS Unified"/>""",
+ os.path.join(directory, "Xamarin.Android.xml"):
+ """<Framework Identifier="MonoAndroid" MinimumVersion="1.0" Profile="*" DisplayName="Xamarin.Android"/>""",
+ os.path.join(directory, "Xamarin.Mac.xml"):
+ """<Framework Identifier="Xamarin.Mac" MinimumVersion="2.0" Profile="*" DisplayName="Xamarin.Mac Unified"/>""",
+ os.path.join(directory, "Xamarin.TVOS.xml"):
+ """<Framework Identifier="Xamarin.TVOS" MinimumVersion="1.0" Profile="*" DisplayName="Xamarin.TVOS"/>""",
+ os.path.join(directory, "Xamarin.WatchOS.xml"):
+ """<Framework Identifier="Xamarin.WatchOS" MinimumVersion="1.0" Profile="*" DisplayName="Xamarin.WatchOS"/>""",
+ }
+ for filename, content in data.iteritems():
+ f = open(filename, "w")
+ f.write(content + "\n")
+ f.close()
+
+
+PCLReferenceAssembliesPackage()
--- /dev/null
+class PixmanPackage (CairoGraphicsPackage):
+
+ def __init__(self):
+ CairoGraphicsPackage.__init__(self, 'pixman', '0.30.0')
+
+PixmanPackage()
--- /dev/null
+package = FreeDesktopPackage('%{name}', 'pkg-config', '0.27',
+ configure_flags=["--with-internal-glib"])
+
+package.needs_lipo = True
--- /dev/null
+import itertools
+import os
+import re
+import shutil
+import string
+import sys
+import tempfile
+import subprocess
+import stat
+
+from bockbuild.darwinprofile import DarwinProfile
+from bockbuild.util.util import *
+from glob import glob
+
+class MonoReleaseProfile(DarwinProfile):
+
+ packages = [
+ 'autoconf',
+ 'automake',
+ 'gettext',
+ 'pkg-config',
+
+ # Base Libraries
+ 'libpng',
+ 'libjpeg',
+ 'libtiff',
+ 'libgif',
+ 'libxml2',
+ 'freetype',
+ 'fontconfig',
+ 'pixman',
+ 'cairo',
+ 'libffi',
+ 'glib',
+ 'pango',
+ 'atk',
+ 'intltool',
+ 'gdk-pixbuf',
+ 'gtk+',
+ 'libglade',
+ 'sqlite',
+ 'expat',
+ 'ige-mac-integration',
+
+ # Theme
+ 'libcroco',
+ 'librsvg',
+ 'hicolor-icon-theme',
+ 'gtk-engines',
+ 'murrine',
+ 'xamarin-gtk-theme',
+ 'gtk-quartz-engine',
+
+ # Mono
+ 'mono-llvm',
+ 'mono',
+ 'msbuild',
+ 'pcl-reference-assemblies',
+ 'libgdiplus',
+ 'xsp',
+ 'gtk-sharp',
+ 'ironlangs',
+ 'fsharp',
+ 'mono-basic',
+ 'nuget'
+ ]
+
+ def attach (self, bockbuild):
+ self.min_version = 7
+ DarwinProfile.attach (self, bockbuild)
+
+ # quick disk space check (http://stackoverflow.com/questions/787776/)
+ s = os.statvfs(bockbuild.root)
+ free_space = (s.f_bavail * s.f_frsize) / (1024 * 1024 * 1024) # in GB
+
+ if free_space < 10:
+ error('Low disk space (less than 10GB), aborting')
+
+ # check for XQuartz installation (needed for libgdiplus)
+ if not os.path.exists('/opt/X11/include/X11/Xlib.h'):
+ error(
+ 'XQuartz is required to be installed (download from http://xquartz.macosforge.org/) ')
+
+ self.MONO_ROOT = "/Library/Frameworks/Mono.framework"
+ self.BUILD_NUMBER = "0"
+ self.MDK_GUID = "964ebddd-1ffe-47e7-8128-5ce17ffffb05"
+
+ self.self_dir = os.path.realpath(os.path.dirname(sys.argv[0]))
+ self.packaging_dir = os.path.join(self.self_dir, "packaging")
+
+ system_mono_dir = '/Library/Frameworks/Mono.framework/Versions/Current'
+ self.env.set('system_mono', os.path.join(
+ system_mono_dir, 'bin', 'mono'))
+ self.env.set('system_mcs', os.path.join(system_mono_dir, 'bin', 'mcs'))
+
+ self.env.set('system_mono_version', backtick(
+ '%s --version' % self.env.system_mono)[0])
+
+ # config overrides for some programs to be functional while staged
+
+ self.env.set('GDK_PIXBUF_MODULE_FILE',
+ '%{staged_prefix}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache')
+ self.env.set('GDK_PIXBUF_MODULEDIR',
+ '%{staged_prefix}/lib/gdk-pixbuf-2.0/2.10.0/loaders')
+ self.env.set('PANGO_SYSCONFDIR', '%{staged_prefix}/etc')
+ self.env.set('PANGO_LIBDIR', '%{staged_prefix}/lib')
+ # self.env.set ('MONO_PATH', '%{staged_prefix}/lib/mono/4.0')
+ self.debug_info = ['gtk+', 'cairo',
+ 'pango', 'mono', 'llvm', 'libgdiplus']
+ self.cache_host = None
+
+ def setup_release(self):
+ self.mono_package = self.release_packages['mono']
+ self.mono_package.fetch()
+
+ verbose('Mono version: %s' % self.mono_package.version)
+ self.RELEASE_VERSION = self.mono_package.version
+ self.prefix = os.path.join(
+ self.MONO_ROOT, "Versions", self.RELEASE_VERSION)
+
+ if os.path.exists(self.prefix):
+ error('Prefix %s exists, and may interfere with the staged build. Please remove and try again.' % self.prefix)
+
+ self.calculate_updateid()
+ trace(self.package_info('MDK'))
+
+ self.dont_optimize = ['pixman']
+
+ for p in self.release_packages.values():
+ if p.name in self.dont_optimize:
+ continue
+ self.gcc_flags.extend(['-O2'])
+
+ # THIS IS THE MAIN METHOD FOR MAKING A PACKAGE
+ def package(self):
+ self.fix_gtksharp_configs()
+ self.verify_binaries()
+
+ working = self.setup_working_dir()
+ uninstall_script = os.path.join(working, "uninstallMono.sh")
+
+ # make the MDK
+ self.apply_blacklist(working, 'mdk_blacklist.sh')
+ self.make_updateinfo(working, self.MDK_GUID)
+ mdk_pkg = self.run_pkgbuild(working, "MDK")
+ title(mdk_pkg)
+ # self.make_dmg(mdk_dmg, title, mdk_pkg, uninstall_script)
+
+ shutil.rmtree(working)
+
+ def calculate_updateid(self):
+ # Create the updateid
+ pwd = os.getcwd()
+ git_bin = self.bockbuild.git_bin
+ trace("cur path is %s and git is %s" % (pwd, git_bin))
+ blame_rev_str = 'cd %s; %s blame configure.ac HEAD | grep AC_INIT | sed \'s/ .*//\' ' % (
+ self.mono_package.workspace, git_bin)
+ blame_rev = backtick(blame_rev_str)[0]
+ trace("Last commit to the version string %s" % (blame_rev))
+ version_number_str = 'cd %s; %s log %s..HEAD --oneline | wc -l | sed \'s/ //g\'' % (
+ self.mono_package.workspace, git_bin, blame_rev)
+ self.BUILD_NUMBER = backtick(version_number_str)[0]
+ trace("Calculating commit distance, %s" % (self.BUILD_NUMBER))
+ self.FULL_VERSION = self.RELEASE_VERSION + "." + self.BUILD_NUMBER
+ os.chdir(pwd)
+
+ parts = self.RELEASE_VERSION.split(".")
+ version_list = (parts + ["0"] * (3 - len(parts)))[:4]
+ for i in range(1, 3):
+ version_list[i] = version_list[i].zfill(2)
+ self.updateid = "".join(version_list)
+ self.updateid += self.BUILD_NUMBER.replace(
+ ".", "").zfill(9 - len(self.updateid))
+ trace(self.updateid)
+
+ # creates and returns the path to a working directory containing:
+ # PKGROOT/ - this root will be bundled into the .pkg and extracted at /
+ # uninstallMono.sh - copied onto the DMG
+ # Info{_sdk}.plist - used by packagemaker to make the installer
+ # resources/ - other resources used by packagemaker for the installer
+ def setup_working_dir(self):
+ def make_package_symlinks(root):
+ os.symlink(self.prefix, os.path.join(root, "Versions", "Current"))
+ currentlink = os.path.join(self.MONO_ROOT, "Versions", "Current")
+ links = [
+ ("bin", "Commands"),
+ ("include", "Headers"),
+ ("lib", "Libraries"),
+ ("", "Home"),
+ (os.path.join("lib", "libmono-2.0.dylib"), "Mono")
+ ]
+ for srcname, destname in links:
+ src = os.path.join(currentlink, srcname)
+ dest = os.path.join(root, destname)
+ # If the symlink exists, we remove it so we can create a fresh
+ # one
+ if os.path.exists(dest):
+ os.unlink(dest)
+ os.symlink(src, dest)
+
+ tmpdir = tempfile.mkdtemp()
+ monoroot = os.path.join(tmpdir, "PKGROOT", self.MONO_ROOT[1:])
+ versions = os.path.join(monoroot, "Versions")
+ os.makedirs(versions)
+
+ print "Setting up temporary package directory:", tmpdir
+
+ # setup metadata
+ run_shell('rsync -aPq %s/* %s' % (self.packaging_dir, tmpdir), False)
+
+ packages_list = string.join(
+ [pkg.desc for pkg in self.release_packages.values()], "\\\n")
+ deps_list = 'bockbuild (rev. %s)\\\n' % self.bockbuild_rev + string.join(
+ [pkg.desc for pkg in self.toolchain_packages.values()], "\\\n")
+
+ parameter_map = {
+ '@@MONO_VERSION@@': self.RELEASE_VERSION,
+ '@@MONO_RELEASE@@': self.BUILD_NUMBER,
+ '@@MONO_VERSION_RELEASE@@': self.RELEASE_VERSION + '_' + self.BUILD_NUMBER,
+ '@@MONO_CSDK_GUID@@': self.MDK_GUID,
+ '@@MONO_VERSION_RELEASE_INT@@': self.updateid,
+ '@@PACKAGES@@': packages_list,
+ '@@DEP_PACKAGES@@': deps_list
+ }
+ for dirpath, d, files in os.walk(tmpdir):
+ for name in files:
+ if not name.startswith('.'):
+ replace_in_file(os.path.join(dirpath, name), parameter_map)
+
+ make_package_symlinks(monoroot)
+
+ # copy to package root
+ run_shell('rsync -aPq "%s"/* "%s/%s"' %
+ (self.package_root, versions, self.RELEASE_VERSION), False)
+
+ return tmpdir
+
+ def apply_blacklist(self, working_dir, blacklist_name):
+ print "Applying blacklist script:", blacklist_name
+ blacklist = os.path.join(self.packaging_dir, blacklist_name)
+ root = os.path.join(working_dir, "PKGROOT", self.prefix[1:])
+ run_shell('%s "%s" > /dev/null' % (blacklist, root), print_cmd=False)
+
+ def run_pkgbuild(self, working_dir, package_type):
+ print 'Running pkgbuild & productbuild...',
+ info = self.package_info(package_type)
+ output = os.path.join(self.self_dir, info["filename"])
+ identifier = "com.xamarin.mono-" + info["type"] + ".pkg"
+ resources_dir = os.path.join(working_dir, "resources")
+ distribution_xml = os.path.join(resources_dir, "distribution.xml")
+
+ old_cwd = os.getcwd()
+ os.chdir(working_dir)
+ pkgbuild = "/usr/bin/pkgbuild"
+ pkgbuild_cmd = ' '.join([pkgbuild,
+ "--identifier " + identifier,
+ "--root '%s/PKGROOT'" % working_dir,
+ "--version '%s'" % self.RELEASE_VERSION,
+ "--install-location '/'",
+ "--scripts '%s'" % resources_dir,
+ "--quiet",
+ os.path.join(working_dir, "mono.pkg")])
+
+ run_shell(pkgbuild_cmd)
+
+ productbuild = "/usr/bin/productbuild"
+ productbuild_cmd = ' '.join([productbuild,
+ "--resources %s" % resources_dir,
+ "--distribution %s" % distribution_xml,
+ "--package-path %s" % working_dir,
+ "--quiet",
+ output])
+
+ run_shell(productbuild_cmd)
+
+ assert_exists(output)
+ os.chdir(old_cwd)
+ print output
+ return output
+
+ def make_updateinfo(self, working_dir, guid):
+ updateinfo = os.path.join(
+ working_dir, "PKGROOT", self.prefix[1:], "updateinfo")
+ with open(updateinfo, "w") as updateinfo:
+ updateinfo.write(guid + ' ' + self.updateid + "\n")
+
+ def package_info(self, pkg_type):
+ arch = self.bockbuild.cmd_options.arch
+ arch_str = None
+ if arch == "darwin-32":
+ arch_str = "x86"
+ elif arch == "darwin-64":
+ arch_str = "x64"
+ elif arch == "darwin-universal":
+ arch_str = "universal"
+ else:
+ error ("Unknown architecture")
+
+ if self.bockbuild.cmd_options.release_build:
+ info = (pkg_type, self.FULL_VERSION, arch_str)
+ else:
+ info = (pkg_type, '%s-%s' % (git_shortid(self.bockbuild,
+ self.mono_package.workspace), self.FULL_VERSION), arch_str)
+
+ filename = "MonoFramework-%s-%s.macos10.xamarin.%s.pkg" % info
+ return {
+ "type": pkg_type,
+ "filename": filename
+ }
+
+ def fix_line(self, line, matcher):
+ def insert_install_root(matches):
+ root = self.prefix
+ captures = matches.groupdict()
+ return 'target="%s"' % os.path.join(root, "lib", captures["lib"])
+
+ if matcher(line):
+ pattern = r'target="(?P<lib>.+\.dylib)"'
+ result = re.sub(pattern, insert_install_root, line)
+ return result
+ else:
+ return line
+
+ def fix_dllmap(self, config, matcher):
+ handle, temp = tempfile.mkstemp()
+ with open(config) as c:
+ with open(temp, "w") as output:
+ for line in c:
+ output.write(self.fix_line(line, matcher))
+ os.rename(temp, config)
+ os.system('chmod a+r %s' % config)
+
+ def fix_gtksharp_configs(self):
+ print 'Fixing GTK# configuration files...',
+ count = 0
+ libs = [
+ 'atk-sharp',
+ 'gdk-sharp',
+ 'glade-sharp',
+ 'glib-sharp',
+ 'gtk-dotnet',
+ 'gtk-sharp',
+ 'pango-sharp'
+ ]
+ gac = os.path.join(self.package_root, "lib", "mono", "gac")
+ confs = [glob(os.path.join(gac, x, "*", "*.dll.config")) for x in libs]
+ for c in itertools.chain(*confs):
+ count = count + 1
+ self.fix_dllmap(c, lambda line: "dllmap" in line)
+ print count
+
+ def verify(self, f):
+ result = " ".join(backtick("otool -L " + f))
+ regex = os.path.join(self.MONO_ROOT, "Versions", r"(\d+\.\d+\.\d+)")
+
+ match = re.search(regex, result)
+ if match is None:
+ return
+ token = match.group(1)
+ trace(token)
+ if self.RELEASE_VERSION not in token:
+ raise Exception("%s references Mono %s\n%s" % (f, token, text))
+
+ def verify_binaries(self):
+ bindir = os.path.join(self.package_root, "bin")
+ for path, dirs, files in os.walk(bindir):
+ for name in files:
+ f = os.path.join(path, name)
+ file_type = backtick('file "%s"' % f)
+ if "Mach-O executable" in "".join(file_type):
+ self.verify(f)
+
+ def shell(self):
+ envscript = '''#!/bin/sh
+ PROFNAME="%s"
+ INSTALLDIR="%s"
+ ROOT="%s"
+ export DYLD_FALLBACK_LIBRARY_PATH="$INSTALLDIR/lib:/lib:/usr/lib"
+ export ACLOCAL_PATH="$INSTALLDIR/share/aclocal"
+ export CONFIG_SITE="$INSTALLDIR/$PROFNAME-config.site"
+ export MONO_GAC_PREFIX="$INSTALLDIR"
+ export MONO_ADDINS_REGISTRY="$ROOT/addinreg"
+ export MONO_INSTALL_PREFIX="$INSTALLDIR"
+
+ export PS1="\[\e[1;3m\][$PROFNAME] \w @ "
+ bash -i
+ ''' % (self.profile_name, self.staged_prefix, self.root)
+
+ path = os.path.join(self.root, self.profile_name + '.sh')
+
+ with open(path, 'w') as f:
+ f.write(envscript)
+
+ os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC)
+
+ subprocess.call(['bash', '-c', path])
+
+MonoReleaseProfile()
\ No newline at end of file
--- /dev/null
+Package('sqlite-autoconf', '3090200', sources=[
+ 'http://www.sqlite.org/2015/%{name}-%{version}.tar.gz'
+])
--- /dev/null
+class XamarinGtkThemePackage (Package):
+
+ def __init__(self):
+ Package.__init__(self, 'xamarin-gtk-theme',
+ sources=[
+ 'git://github.com/mono/xamarin-gtk-theme.git'],
+ revision='cc3fb66e56d494e968be3a529a0737a60e31c1f3')
+
+ def build(self):
+ try:
+ self.sh('./autogen.sh --prefix=%{staged_prefix}')
+ except:
+ pass
+ finally:
+ #self.sh ('intltoolize --force --copy --debug')
+ #self.sh ('./configure --prefix="%{package_prefix}"')
+ Package.build(self)
+
+
+XamarinGtkThemePackage()
--- /dev/null
+class XspPackage (GitHubTarballPackage):
+
+ def __init__(self):
+ GitHubTarballPackage.__init__(self, 'mono', 'xsp', '4.4',
+ 'c98e068f5647fb06ff2fbef7cd5f1b35417362b1',
+ configure='./autogen.sh --prefix="%{package_prefix}"')
+
+ def install(self):
+ # scoop up some mislocated files
+ misdir = '%s%s' % (self.stage_root, self.staged_profile)
+ unprotect_dir(self.stage_root)
+ Package.install(self)
+ if not os.path.exists(misdir):
+ for path in iterate_dir(self.stage_root):
+ print path
+ error('Could not find mislocated files')
+
+ self.sh('rsync -a --ignore-existing %s/* %s' %
+ (misdir, self.profile.staged_prefix))
+ self.sh('rm -rf %s/*' % misdir)
+
+
+XspPackage()
-Subproject commit c558be34ceb5f2a5c318018df69c516230ff2942
+Subproject commit 0aadc7379b354dcd01b97d8568ff4643a7668577