gtk: Port GtkScrolledView and GtkPanedView to Gtk+ 3.16.7.
authorMatt Birkholz <puck@birchwood-abbey.net>
Tue, 15 Dec 2015 01:02:07 +0000 (18:02 -0700)
committerMatt Birkholz <puck@birchwood-abbey.net>
Mon, 4 Jan 2016 03:38:48 +0000 (20:38 -0700)
Attempt to pick from multiple implementations per the Gtk+ version.

src/gtk/Makefile.in
src/gtk/README
src/gtk/autogen.sh [new file with mode: 0755]
src/gtk/configure.ac
src/gtk/gtkpanedview-3.16.7.c [new file with mode: 0644]
src/gtk/gtkpanedview-3.6.0.c [moved from src/gtk/gtkpanedview.c with 100% similarity]
src/gtk/gtkscrolledview-3.16.7.c [new file with mode: 0644]
src/gtk/gtkscrolledview-3.6.0.c [moved from src/gtk/gtkscrolledview.c with 100% similarity]

index baeaebb2764de4d080a8b0a51c5735aec1f012ba..b69a041e029294fe660152b3d3b866e18b599782 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2011, 2012, 2013, 2014 Matthew Birkholz
+# Copyright (C) 2011, 2012, 2013, 2014, 2015 Matthew Birkholz
 #
 # This file is part of an extension to MIT/GNU Scheme.
 #
@@ -77,6 +77,7 @@ distclean: clean
 maintainer-clean: distclean
        rm -f configure config.h.in TAGS
        rm -rf autom4te.cache
+       rm -f gtkscrolledview.c gtkpanedview.c
 
 tags:
        etags *.h \
index a8cd1d78e9a541d4bde9537421a3d5f0db26b3bb..b069ffa9186382c6f951d91f0a39ea492e7747ed 100644 (file)
@@ -1,8 +1,32 @@
-The gtk wrapper.
+The Gtk+ wrapper.
+
+The good news first: this wrapper provides specialized Gtk+ widget
+types that inherit the gesture and kinetic scrolling support in
+GtkScrolledWindow.  Thus a Scheme canvas can be scrolled like any
+other widget.
+
+The bad news: Edwin requires these specialized widget types.
+GtkScrolledWindow and GtkPaned do not suffice.  The specialized types
+require access to private members of their supertypes, so they need
+a struct declaration matching the private declaration in the toolkit.
+
+The trick: Select an implementation of the special widgets that is
+compatible with your system's toolkit.  One is provided and
+corresponds to the latest Ubuntu release.  If your system is using a
+matching or "close enough" version of the toolkit, you will get a
+treat.
+
+       Ubuntu    Gtk+
+       15.10     3.16.7
+
+The solution: Add GtkScrolledView and GtkPanedView to Gtk+.
+Unfortunately, implementing both GtkScrolledWindow and GtkScrolledView
+without duplicating a lot of code makes this a less-than-simple
+extension of Gtk+.
 
 To build:
 
-    ./configure [--with-gtk=directory]...
+    ./configure
     make all check install
 
 The install target copies a shared library shim and compiled Scheme
diff --git a/src/gtk/autogen.sh b/src/gtk/autogen.sh
new file mode 100755 (executable)
index 0000000..6263f54
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+test -n "$srcdir" || srcdir=`dirname "$0"`
+test -n "$srcdir" || srcdir=.
+
+olddir=`pwd`
+cd "$srcdir"
+
+AUTORECONF=`which autoreconf`
+if test -z $AUTORECONF; then
+       echo "*** No autoreconf found, please install it ***"
+       exit 1
+fi
+
+autoreconf --force --install --verbose || exit $?
+
+cd "$olddir"
+test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
index c9863bc0894415b2839728b4f386ec0c5aea0d58..51fcd2f73e5915d92857f5dceb2542b7caa8992d 100644 (file)
@@ -1,16 +1,13 @@
 dnl Process this file with autoconf to produce a configure script.
 
-AC_INIT([MIT/GNU Scheme gtk interface],
-        [0.1],
-        [bug-mit-scheme@gnu.org],
-        [mit-scheme-gtk])
+AC_INIT([MIT/GNU Scheme gtk plugin], [0.2], [matt@birchwood-abbey.net], [mit-scheme-gtk])
 AC_CONFIG_SRCDIR([gtk.pkg])
 AC_CONFIG_HEADERS([config.h])
 
 AC_COPYRIGHT(
-[Copyright (C) 2013  Matthew Birkholz
+[Copyright (C) 2013, 2015  Matthew Birkholz
 
-This file is part of an extension to MIT/GNU Scheme.
+This file is part of a plugin for MIT/GNU Scheme.
 
 MIT/GNU Scheme is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -30,9 +27,9 @@ USA.
 
 AH_TOP([/*
 
-Copyright (C) 2013 Matthew Birkholz
+Copyright (C) 2013, 2015 Matthew Birkholz
 
-This file is part of MIT/GNU Scheme.
+This file is part of a plugin for MIT/GNU Scheme.
 
 MIT/GNU Scheme is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -51,12 +48,32 @@ USA.
 
 */])
 
+AC_ARG_WITH([gtk-version],
+    AS_HELP_STRING([--with-gtk-version],
+       [Use widgets specialized for the given Gtk+ version [[guess]]]))
+: ${with_gtk_version='guess'}
+
 AC_CHECK_PROG([PKG_CONFIG], [pkg-config], [yes])
 
 if ! pkg-config --exists gtk+-3.0 2>/dev/null; then
     AC_MSG_ERROR([Gtk 3.0 not found.])
 fi
 
+if pkg-config --exists 'gtk+-3.0 >= 3.16.7' 2>/dev/null; then
+    GTK_VERSION=3.16.7
+dnl elif pkg-config --exists 'gtk+-3.0 >= 3.14.13' 2>/dev/null; then
+dnl     GTK_VERSION=3.14.13
+dnl elif pkg-config --exists 'gkt+-3.0 >= 3.10.8' 2>/dev/null; then
+dnl     GTK_VERSION=3.10.8
+elif pkg-config --exists 'gkt+-3.0 >= 3.6.0' 2>/dev/null; then
+    GTK_VERSION=3.6.0
+else
+    AC_MSG_ERROR([Gtk version too old.])
+fi
+
+ln -sf gtkscrolledview-$GTK_VERSION.c gtkscrolledview.c
+ln -sf gtkpanedview-$GTK_VERSION.c gtkpanedview.c
+
 AC_SUBST([CFLAGS])
 AC_SUBST([CPPFLAGS])
 AC_SUBST([LDFLAGS])
diff --git a/src/gtk/gtkpanedview-3.16.7.c b/src/gtk/gtkpanedview-3.16.7.c
new file mode 100644 (file)
index 0000000..c03e925
--- /dev/null
@@ -0,0 +1,746 @@
+/* -*-C-*-
+
+Copyright (C) 2015  Matthew Birkholz
+
+This file is part of MIT/GNU Scheme.
+
+MIT/GNU Scheme 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 2 of the License, or (at
+your option) any later version.
+
+MIT/GNU Scheme 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 MIT/GNU Scheme; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
+USA.
+
+*/
+
+/* A specialized GtkPaned.  See documentation of <gtk-paned-view>. */
+
+#include "gtkpanedview.h"
+
+static void     gtk_paned_get_preferred_width   (GtkWidget        *widget,
+                                                 gint             *minimum,
+                                                 gint             *natural);
+static void     gtk_paned_get_preferred_height  (GtkWidget        *widget,
+                                                 gint             *minimum,
+                                                 gint             *natural);
+static void     gtk_paned_get_preferred_width_for_height
+                                                (GtkWidget        *widget,
+                                                 gint              height,
+                                                 gint             *minimum,
+                                                 gint             *natural);
+static void     gtk_paned_get_preferred_height_for_width
+                                                (GtkWidget        *widget,
+                                                 gint              width,
+                                                 gint             *minimum,
+                                                 gint              *natural);
+
+static void     gtk_paned_size_allocate         (GtkWidget        *widget,
+                                                 GtkAllocation    *allocation);
+
+static void     gtk_paned_calc_position         (GtkPaned         *paned,
+                                                 gint              allocation,
+                                                 gint              child1_width,
+                                                 gint              child1_nat_width,
+                                                 gint              child2_width,
+                                                 gint              child2_nat_width);
+
+G_DEFINE_TYPE (GtkPanedView, gtk_paned_view,
+              GTK_TYPE_PANED)
+
+static void
+gtk_paned_view_class_init (GtkPanedViewClass *class)
+{
+  GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
+
+  widget_class->get_preferred_width = gtk_paned_get_preferred_width;
+  widget_class->get_preferred_height = gtk_paned_get_preferred_height;
+  widget_class->get_preferred_height_for_width = gtk_paned_get_preferred_height_for_width;
+  widget_class->get_preferred_width_for_height = gtk_paned_get_preferred_width_for_height;
+  widget_class->size_allocate = gtk_paned_size_allocate;
+}
+
+static void
+gtk_paned_view_init (GtkPanedView *paned_view)
+{
+  g_assert (GTK_PANED (paned_view) ->priv != NULL);
+}
+
+/**
+ * gtk_paned_view_new:
+ * @orientation: GTK_ORIENTATION_VERTICAL or GTK_ORIENTATION_HORIZONTAL.
+ *
+ * Creates a new paned view.
+ *
+ * Returns: a new paned view.
+ */
+GtkWidget*
+gtk_paned_view_new (GtkOrientation orientation)
+{
+  return (g_object_new (GTK_TYPE_PANED_VIEW,
+                       "orientation", orientation,
+                       NULL));
+}
+
+/* Copied from gtkprivate.h. */
+#define OPPOSITE_ORIENTATION(_orientation) (1 - (_orientation))
+
+/* Copied from gtksizerequest.c. */
+static void
+_gtk_widget_get_preferred_size_for_size (GtkWidget      *widget,
+                                         GtkOrientation  orientation,
+                                         gint            size,
+                                         gint           *minimum,
+                                         gint           *natural,
+                                         gint           *minimum_baseline,
+                                         gint           *natural_baseline)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (size >= -1);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      if (size < 0)
+        gtk_widget_get_preferred_width (widget, minimum, natural);
+      else
+        gtk_widget_get_preferred_width_for_height (widget, size, minimum, natural);
+
+      if (minimum_baseline)
+        *minimum_baseline = -1;
+      if (natural_baseline)
+        *natural_baseline = -1;
+    }
+  else
+    {
+      gtk_widget_get_preferred_height_and_baseline_for_width (widget,
+                                                              size,
+                                                              minimum,
+                                                              natural,
+                                                              minimum_baseline,
+                                                              natural_baseline);
+    }
+}
+\f
+/* The following definitions were copied unchanged from gtkpaned.c
+   v3.16.7 unless otherwise noted.
+
+   enum { CHILD1, CHILD2 }
+   struct _GtkPanedPrivate
+   static void gtk_paned_compute_position ()                           PATCHED
+   static void gtk_paned_get_preferred_size_for_orientation ()
+   static void gtk_paned_get_preferred_size_for_opposite_orientation ()        PATCHED
+   static void gtk_paned_get_preferred_size ()
+   static void gtk_paned_get_preferred_width ()
+   static void gtk_paned_get_preferred_height ()
+   static void gtk_paned_get_preferred_width_for_height ()
+   static void gtk_paned_get_preferred_height_for_width ()
+   static void flip_child ()
+   static void gtk_paned_set_child_visible ()
+   static void gtk_paned_child_allocate ()
+   static void gtk_paned_size_allocate ()                              PATCHED
+   static void gtk_paned_calc_position ()                              PATCHED
+*/
+
+enum {
+  CHILD1,
+  CHILD2
+};
+
+struct _GtkPanedPrivate
+{
+  GtkPaned       *first_paned;
+  GtkWidget      *child1;
+  GtkWidget      *child2;
+  GdkWindow      *child1_window;
+  GdkWindow      *child2_window;
+  GtkWidget      *last_child1_focus;
+  GtkWidget      *last_child2_focus;
+  GtkWidget      *saved_focus;
+  GtkOrientation  orientation;
+
+  GdkCursorType  cursor_type;
+  GdkRectangle   handle_pos;
+  GdkWindow     *handle;
+
+  GtkGesture    *pan_gesture;
+
+  gint          child1_size;
+  gint          drag_pos;
+  gint          last_allocation;
+  gint          max_position;
+  gint          min_position;
+  gint          original_position;
+
+  guint         handle_prelit : 1;
+  guint         in_recursion  : 1;
+  guint         child1_resize : 1;
+  guint         child1_shrink : 1;
+  guint         child2_resize : 1;
+  guint         child2_shrink : 1;
+  guint         position_set  : 1;
+  guint         panning       : 1;
+};
+
+static void
+gtk_paned_compute_position (GtkPaned *paned,
+                            gint      allocation,
+                            gint      child1_min,
+                            gint      child1_nat,
+                            gint      child2_min,
+                            gint      child2_nat,
+                            gint     *min_pos,
+                            gint     *max_pos,
+                            gint     *out_pos)
+{
+  GtkPanedPrivate *priv = paned->priv;
+  gint min, max, pos;
+
+  min = (!priv->child1_resize ? child1_nat
+        : priv->child1_shrink ? 0 : child1_min);
+
+  max = MAX (0, (allocation
+                - (!priv->child2_resize ? child2_nat
+                   : priv->child2_shrink ? 0 : child2_min)));
+  max = MAX (min, max);
+
+  if (!priv->position_set)
+    {
+      if (priv->child1_resize && !priv->child2_resize)
+       pos = MAX (0, allocation - child2_nat);
+      else if (!priv->child1_resize && priv->child2_resize)
+       pos = MIN (allocation, child1_nat);
+      else if (child1_nat + child2_nat != 0)
+       pos = allocation * ((gdouble)child1_nat / (child1_nat + child2_nat)) + 0.5;
+      else
+       pos = allocation * 0.5 + 0.5;
+    }
+  else
+    {
+      /* If the position was set before the initial allocation.
+       * (priv->last_allocation <= 0) just clamp it and leave it.
+       */
+      if (priv->last_allocation > 0)
+       {
+         if (priv->child1_resize && !priv->child2_resize)
+           pos = priv->child1_size + allocation - priv->last_allocation;
+         else if (!(!priv->child1_resize && priv->child2_resize))
+           pos = allocation * ((gdouble) priv->child1_size / (priv->last_allocation)) + 0.5;
+          else
+            pos = priv->child1_size;
+       }
+      else
+        pos = priv->child1_size;
+    }
+
+  pos = CLAMP (pos, min, max);
+  
+  if (min_pos)
+    *min_pos = min;
+  if (max_pos)
+    *max_pos = max;
+  if (out_pos)
+    *out_pos = pos;
+}
+
+static void
+gtk_paned_get_preferred_size_for_orientation (GtkWidget      *widget,
+                                              gint            size,
+                                              gint           *minimum,
+                                              gint           *natural)
+{
+  GtkPaned *paned = GTK_PANED (widget);
+  GtkPanedPrivate *priv = paned->priv;
+  gint child_min, child_nat;
+
+  *minimum = *natural = 0;
+
+  if (priv->child1 && gtk_widget_get_visible (priv->child1))
+    {
+      _gtk_widget_get_preferred_size_for_size (priv->child1, priv->orientation, size, &child_min, &child_nat, NULL, NULL);
+      if (priv->child1_shrink)
+        *minimum = 0;
+      else
+        *minimum = child_min;
+      *natural = child_nat;
+    }
+
+  if (priv->child2 && gtk_widget_get_visible (priv->child2))
+    {
+      _gtk_widget_get_preferred_size_for_size (priv->child2, priv->orientation, size, &child_min, &child_nat, NULL, NULL);
+
+      if (!priv->child2_shrink)
+        *minimum += child_min;
+      *natural += child_nat;
+    }
+
+  if (priv->child1 && gtk_widget_get_visible (priv->child1) &&
+      priv->child2 && gtk_widget_get_visible (priv->child2))
+    {
+      gint handle_size;
+
+      gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
+
+      *minimum += handle_size;
+      *natural += handle_size;
+    }
+}
+
+static void
+gtk_paned_get_preferred_size_for_opposite_orientation (GtkWidget      *widget,
+                                                       gint            size,
+                                                       gint           *minimum,
+                                                       gint           *natural)
+{
+  GtkPaned *paned = GTK_PANED (widget);
+  GtkPanedPrivate *priv = paned->priv;
+  gint for_child1, for_child2;
+  gint child_min, child_nat;
+
+  if (size > -1 &&
+      priv->child1 && gtk_widget_get_visible (priv->child1) &&
+      priv->child2 && gtk_widget_get_visible (priv->child2))
+    {
+      gint child1_min, child1_nat, child2_min, child2_nat;
+      gint handle_size;
+
+      gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
+
+      _gtk_widget_get_preferred_size_for_size (priv->child1, priv->orientation, -1, &child1_min, &child1_nat, NULL, NULL);
+      _gtk_widget_get_preferred_size_for_size (priv->child2, priv->orientation, -1, &child2_min, &child2_nat, NULL, NULL);
+
+      gtk_paned_compute_position (paned,
+                                  size - handle_size, child1_min, child1_nat, child2_min, child2_nat,
+                                  NULL, NULL, &for_child1);
+
+      for_child2 = size - for_child1 - handle_size;
+    }
+  else
+    {
+      for_child1 = size;
+      for_child2 = size;
+    }
+
+  *minimum = *natural = 0;
+
+  if (priv->child1 && gtk_widget_get_visible (priv->child1))
+    {
+      _gtk_widget_get_preferred_size_for_size (priv->child1,
+                                               OPPOSITE_ORIENTATION (priv->orientation),
+                                               for_child1,
+                                               &child_min, &child_nat,
+                                               NULL, NULL);
+      
+      *minimum = child_min;
+      *natural = child_nat;
+    }
+
+  if (priv->child2 && gtk_widget_get_visible (priv->child2))
+    {
+      _gtk_widget_get_preferred_size_for_size (priv->child2,
+                                               OPPOSITE_ORIENTATION (priv->orientation),
+                                               for_child2,
+                                               &child_min, &child_nat,
+                                               NULL, NULL);
+
+      *minimum = MAX (*minimum, child_min);
+      *natural = MAX (*natural, child_nat);
+    }
+}
+
+static void
+gtk_paned_get_preferred_size (GtkWidget      *widget,
+                              GtkOrientation  orientation,
+                              gint            size,
+                              gint           *minimum,
+                              gint           *natural)
+{
+  GtkPaned *paned = GTK_PANED (widget);
+  GtkPanedPrivate *priv = paned->priv;
+
+  if (orientation == priv->orientation)
+    gtk_paned_get_preferred_size_for_orientation (widget, size, minimum, natural);
+  else
+    gtk_paned_get_preferred_size_for_opposite_orientation (widget, size, minimum, natural);
+}
+
+static void
+gtk_paned_get_preferred_width (GtkWidget *widget,
+                               gint      *minimum,
+                               gint      *natural)
+{
+  gtk_paned_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural);
+}
+
+static void
+gtk_paned_get_preferred_height (GtkWidget *widget,
+                                gint      *minimum,
+                                gint      *natural)
+{
+  gtk_paned_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, -1, minimum, natural);
+}
+
+static void
+gtk_paned_get_preferred_width_for_height (GtkWidget *widget,
+                                          gint       height,
+                                          gint      *minimum,
+                                          gint      *natural)
+{
+  gtk_paned_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
+}
+
+static void
+gtk_paned_get_preferred_height_for_width (GtkWidget *widget,
+                                          gint       width,
+                                          gint      *minimum,
+                                          gint      *natural)
+{
+  gtk_paned_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
+}
+
+static void
+flip_child (GtkWidget     *widget,
+            GtkAllocation *child_pos)
+{
+  GtkAllocation allocation;
+  gint x, width;
+
+  gtk_widget_get_allocation (widget, &allocation);
+  x = allocation.x;
+  width = allocation.width;
+
+  child_pos->x = 2 * x + width - child_pos->x - child_pos->width;
+}
+
+static void
+gtk_paned_set_child_visible (GtkPaned  *paned,
+                             guint      id,
+                             gboolean   visible)
+{
+  GtkPanedPrivate *priv = paned->priv;
+  GtkWidget *child;
+
+  child = id == CHILD1 ? priv->child1 : priv->child2;
+
+  if (child == NULL)
+    return;
+
+  gtk_widget_set_child_visible (child, visible);
+
+  if (gtk_widget_get_mapped (GTK_WIDGET (paned)))
+    {
+      GdkWindow *window = id == CHILD1 ? priv->child1_window : priv->child2_window;
+
+      if (visible != gdk_window_is_visible (window))
+        {
+          if (visible)
+            gdk_window_show (window);
+          else
+            gdk_window_hide (window);
+        }
+    }
+}
+
+static void
+gtk_paned_child_allocate (GtkWidget           *child,
+                          GdkWindow           *child_window, /* can be NULL */
+                          const GtkAllocation *window_allocation,
+                          GtkAllocation       *child_allocation)
+{
+  if (child_window)
+    gdk_window_move_resize (child_window,
+                            window_allocation->x, window_allocation->y,
+                            window_allocation->width, window_allocation->height);
+
+  gtk_widget_size_allocate (child, child_allocation);
+}
+
+static void
+gtk_paned_size_allocate (GtkWidget     *widget,
+                         GtkAllocation *allocation)
+{
+  GtkPaned *paned = GTK_PANED (widget);
+  GtkPanedPrivate *priv = paned->priv;
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (priv->child1 && gtk_widget_get_visible (priv->child1) &&
+      priv->child2 && gtk_widget_get_visible (priv->child2))
+    {
+      GtkAllocation child1_allocation, window1_allocation;
+      GtkAllocation child2_allocation, window2_allocation;
+      GtkAllocation priv_child1_allocation;
+      GdkRectangle old_handle_pos;
+      gint handle_size;
+
+      gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
+
+      old_handle_pos = priv->handle_pos;
+
+      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        {
+          gint child1_width, child1_nat_width, child2_width, child2_nat_width;
+
+          gtk_widget_get_preferred_width_for_height (priv->child1,
+                                                     allocation->height,
+                                                     &child1_width, &child1_nat_width);
+          gtk_widget_get_preferred_width_for_height (priv->child2,
+                                                     allocation->height,
+                                                     &child2_width, &child2_nat_width);
+
+          gtk_paned_calc_position (paned,
+                                   MAX (1, allocation->width - handle_size),
+                                   child1_width, child1_nat_width,
+                                   child2_width, child2_nat_width);
+
+          priv->handle_pos.x = allocation->x + priv->child1_size;
+          priv->handle_pos.y = allocation->y;
+          priv->handle_pos.width = handle_size;
+          priv->handle_pos.height = allocation->height;
+
+          window1_allocation.height = window2_allocation.height = allocation->height;
+          window1_allocation.width = MAX (1, priv->child1_size);
+          window1_allocation.x = allocation->x;
+          window1_allocation.y = window2_allocation.y = allocation->y;
+
+          window2_allocation.x = window1_allocation.x + priv->child1_size + priv->handle_pos.width;
+          window2_allocation.width = MAX (1, allocation->x + allocation->width - window2_allocation.x);
+
+          if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL)
+            {
+              flip_child (widget, &(window2_allocation));
+              flip_child (widget, &(window1_allocation));
+              flip_child (widget, &(priv->handle_pos));
+            }
+
+          child1_allocation.x = child1_allocation.y = 0;
+          child1_allocation.width = window1_allocation.width;
+          child1_allocation.height = window1_allocation.height;
+          if (child1_width > child1_allocation.width)
+            {
+              if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_LTR)
+                child1_allocation.x -= child1_width - child1_allocation.width;
+              child1_allocation.width = child1_width;
+            }
+
+          child2_allocation.x = child2_allocation.y = 0;
+          child2_allocation.width = window2_allocation.width;
+          child2_allocation.height = window2_allocation.height;
+          if (child2_width > child2_allocation.width)
+            {
+              if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL)
+                child2_allocation.x -= child2_width - child2_allocation.width;
+              child2_allocation.width = child2_width;
+            }
+        }
+      else
+        {
+          gint child1_height, child1_nat_height, child2_height, child2_nat_height;
+
+          gtk_widget_get_preferred_height_for_width (priv->child1,
+                                                     allocation->width,
+                                                     &child1_height, &child1_nat_height);
+          gtk_widget_get_preferred_height_for_width (priv->child2,
+                                                     allocation->width,
+                                                     &child2_height, &child2_nat_height);
+
+          gtk_paned_calc_position (paned,
+                                   MAX (1, allocation->height - handle_size),
+                                   child1_height, child1_nat_height,
+                                   child2_height, child2_nat_height);
+
+          priv->handle_pos.x = allocation->x;
+          priv->handle_pos.y = allocation->y + priv->child1_size;
+          priv->handle_pos.width = allocation->width;
+          priv->handle_pos.height = handle_size;
+
+          window1_allocation.width = window2_allocation.width = allocation->width;
+          window1_allocation.height = MAX (1, priv->child1_size);
+          window1_allocation.x = window2_allocation.x = allocation->x;
+          window1_allocation.y = allocation->y;
+
+          window2_allocation.y = window1_allocation.y + priv->child1_size + priv->handle_pos.height;
+          window2_allocation.height = MAX (1, allocation->y + allocation->height - window2_allocation.y);
+
+          child1_allocation.x = child1_allocation.y = 0;
+          child1_allocation.width = window1_allocation.width;
+          child1_allocation.height = window1_allocation.height;
+          if (child1_height > child1_allocation.height)
+            {
+              child1_allocation.y -= child1_height - child1_allocation.height;
+              child1_allocation.height = child1_height;
+            }
+
+          child2_allocation.x = child2_allocation.y = 0;
+          child2_allocation.width = window2_allocation.width;
+          child2_allocation.height = window2_allocation.height;
+          if (child2_height > child2_allocation.height)
+            child2_allocation.height = child2_height;
+        }
+
+      if (gtk_widget_get_mapped (widget) &&
+          (old_handle_pos.x != priv->handle_pos.x ||
+           old_handle_pos.y != priv->handle_pos.y ||
+           old_handle_pos.width != priv->handle_pos.width ||
+           old_handle_pos.height != priv->handle_pos.height))
+        {
+          GdkWindow *window;
+
+          window = gtk_widget_get_window (widget);
+          gdk_window_invalidate_rect (window, &old_handle_pos, FALSE);
+          gdk_window_invalidate_rect (window, &priv->handle_pos, FALSE);
+        }
+
+      if (gtk_widget_get_realized (widget))
+       {
+          GtkBorder margin;
+
+          gtk_style_context_get_margin (gtk_widget_get_style_context (widget),
+                                        gtk_widget_get_state_flags (widget),
+                                        &margin);
+
+         if (gtk_widget_get_mapped (widget))
+           gdk_window_show (priv->handle);
+
+          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+            {
+              gdk_window_move_resize (priv->handle,
+                                      priv->handle_pos.x - margin.left,
+                                      priv->handle_pos.y,
+                                      handle_size + margin.left + margin.right,
+                                      priv->handle_pos.height);
+            }
+          else
+            {
+              gdk_window_move_resize (priv->handle,
+                                      priv->handle_pos.x,
+                                      priv->handle_pos.y - margin.top,
+                                      priv->handle_pos.width,
+                                      handle_size + margin.top + margin.bottom);
+            }
+       }
+
+      /* Now allocate the childen, making sure, when resizing not to
+       * overlap the windows
+       */
+      gtk_widget_get_allocation (priv->child1, &priv_child1_allocation);
+      if (gtk_widget_get_mapped (widget) &&
+          ((priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
+            priv_child1_allocation.width < child1_allocation.width) ||
+
+           (priv->orientation == GTK_ORIENTATION_VERTICAL &&
+            priv_child1_allocation.height < child1_allocation.height)))
+       {
+          gtk_paned_child_allocate (priv->child2,
+                                    priv->child2_window,
+                                    &window2_allocation,
+                                    &child2_allocation);
+          gtk_paned_child_allocate (priv->child1,
+                                    priv->child1_window,
+                                    &window1_allocation,
+                                    &child1_allocation);
+       }
+      else
+       {
+          gtk_paned_child_allocate (priv->child1,
+                                    priv->child1_window,
+                                    &window1_allocation,
+                                    &child1_allocation);
+          gtk_paned_child_allocate (priv->child2,
+                                    priv->child2_window,
+                                    &window2_allocation,
+                                    &child2_allocation);
+       }
+    }
+  else
+    {
+      GtkAllocation window_allocation, child_allocation;
+
+      if (gtk_widget_get_realized (widget))
+       gdk_window_hide (priv->handle);
+
+      window_allocation.x = allocation->x;
+      window_allocation.y = allocation->y;
+      window_allocation.width = allocation->width;
+      window_allocation.height = allocation->height;
+      child_allocation.x = child_allocation.y = 0;
+      child_allocation.width = allocation->width;
+      child_allocation.height = allocation->height;
+
+      if (priv->child1 && gtk_widget_get_visible (priv->child1))
+        {
+          gtk_paned_set_child_visible (paned, 0, TRUE);
+          if (priv->child2)
+            gtk_paned_set_child_visible (paned, 1, FALSE);
+
+          gtk_paned_child_allocate (priv->child1,
+                                    priv->child1_window,
+                                    &window_allocation,
+                                    &child_allocation);
+        }
+      else if (priv->child2 && gtk_widget_get_visible (priv->child2))
+        {
+          gtk_paned_set_child_visible (paned, 1, TRUE);
+          if (priv->child1)
+            gtk_paned_set_child_visible (paned, 0, FALSE);
+
+          gtk_paned_child_allocate (priv->child2,
+                                    priv->child2_window,
+                                    &window_allocation,
+                                    &child_allocation);
+        }
+      else
+        {
+          if (priv->child1)
+            gtk_paned_set_child_visible (paned, 0, FALSE);
+          if (priv->child2)
+            gtk_paned_set_child_visible (paned, 1, FALSE);
+        }
+    }
+}
+
+static void
+gtk_paned_calc_position (GtkPaned *paned,
+                         gint      allocation,
+                         gint      child1_width,
+                        gint      child1_nat_width,
+                         gint      child2_width,
+                        gint      child2_nat_width)
+{
+  GtkPanedPrivate *priv = paned->priv;
+  gint old_position;
+  gint old_min_position;
+  gint old_max_position;
+
+  old_position = priv->child1_size;
+  old_min_position = priv->min_position;
+  old_max_position = priv->max_position;
+
+  gtk_paned_compute_position (paned,
+                              allocation, child1_width, child1_nat_width, child2_width, child2_nat_width,
+                              &priv->min_position, &priv->max_position,
+                              &priv->child1_size);
+
+  if (priv->child1)
+    gtk_paned_set_child_visible (paned, 0, priv->child1_size != 0);
+  
+  if (priv->child2)
+    gtk_paned_set_child_visible (paned, 1, priv->child1_size != allocation); 
+
+  g_object_freeze_notify (G_OBJECT (paned));
+  if (priv->child1_size != old_position)
+    g_object_notify (G_OBJECT (paned), "position");
+  if (priv->min_position != old_min_position)
+    g_object_notify (G_OBJECT (paned), "min-position");
+  if (priv->max_position != old_max_position)
+    g_object_notify (G_OBJECT (paned), "max-position");
+  g_object_thaw_notify (G_OBJECT (paned));
+
+  priv->last_allocation = allocation;
+}
diff --git a/src/gtk/gtkscrolledview-3.16.7.c b/src/gtk/gtkscrolledview-3.16.7.c
new file mode 100644 (file)
index 0000000..58b694a
--- /dev/null
@@ -0,0 +1,329 @@
+/* -*-C-*-
+
+Copyright (C) 2012, 2013, 2015  Matthew Birkholz
+
+This file is part of MIT/GNU Scheme.
+
+MIT/GNU Scheme 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 2 of the License, or (at
+your option) any later version.
+
+MIT/GNU Scheme 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 MIT/GNU Scheme; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
+USA.
+
+*/
+
+/* A specialized GtkScrolledWindow.  See documentation of
+   <gtk-scrolled-view>. */
+
+#include "gtkscrolledview.h"
+
+static void  gtk_scrolled_window_get_preferred_width   (GtkWidget           *widget,
+                                                       gint                *minimum_size,
+                                                       gint                *natural_size);
+static void  gtk_scrolled_window_get_preferred_height  (GtkWidget           *widget,
+                                                       gint                *minimum_size,
+                                                       gint                *natural_size);
+static void  gtk_scrolled_window_get_preferred_height_for_width  (GtkWidget           *layout,
+                                                       gint                 width,
+                                                       gint                *minimum_height,
+                                                       gint                *natural_height);
+static void  gtk_scrolled_window_get_preferred_width_for_height  (GtkWidget           *layout,
+                                                       gint                 width,
+                                                       gint                *minimum_height,
+                                                       gint                *natural_height);
+
+G_DEFINE_TYPE (GtkScrolledView, gtk_scrolled_view,
+              GTK_TYPE_SCROLLED_WINDOW)
+
+static void
+gtk_scrolled_view_class_init (GtkScrolledViewClass *class)
+{
+  GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
+
+  widget_class->get_preferred_width = gtk_scrolled_window_get_preferred_width;
+  widget_class->get_preferred_height = gtk_scrolled_window_get_preferred_height;
+  widget_class->get_preferred_height_for_width = gtk_scrolled_window_get_preferred_height_for_width;
+  widget_class->get_preferred_width_for_height = gtk_scrolled_window_get_preferred_width_for_height;
+}
+
+static void
+gtk_scrolled_view_init (GtkScrolledView *scrolled_view)
+{
+  g_assert (GTK_SCROLLED_WINDOW (scrolled_view) ->priv != NULL);
+}
+
+/**
+ * gtk_scrolled_view_new:
+ * @hadjustment: (allow-none): horizontal adjustment
+ * @vadjustment: (allow-none): vertical adjustment
+ *
+ * Creates a new scrolling window.
+ *
+ * The two arguments are the scrolling window's adjustments; these will be
+ * shared with the scrollbars and the child widget to keep the bars in sync 
+ * with the child. Usually you want to pass %NULL for the adjustments, which 
+ * will cause the scrolling window to create them for you.
+ *
+ * Returns: a new scrolling window
+ */
+GtkWidget*
+gtk_scrolled_view_new (GtkAdjustment *hadjustment,
+                      GtkAdjustment *vadjustment)
+{
+  GtkWidget *scrolled_view;
+
+  if (hadjustment)
+    g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
+
+  if (vadjustment)
+    g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
+
+  scrolled_view = g_object_new (GTK_TYPE_SCROLLED_VIEW,
+                               "hadjustment", hadjustment,
+                               "vadjustment", vadjustment,
+                               NULL);
+
+  return scrolled_view;
+}
+\f
+/* The following definitions were copied unchanged from
+   gtkscrolledwindow.c v3.16.7 unless otherwise noted.
+
+   struct Indicator
+   struct _GtkScrolledWindowPrivate
+   static gint _gtk_scrolled_window_get_scrollbar_spacing ()
+   static void gtk_scrolled_window_get_preferred_size ()               PATCHED
+   static void gtk_scrolled_window_get_preferred_width ()
+   static void gtk_scrolled_window_get_preferred_height ()
+   static void gtk_scrolled_window_get_preferred_height_for_width ()
+   static void gtk_scrolled_window_get_preferred_width_for_height ()
+*/
+
+typedef struct
+{
+  GtkWidget *scrollbar;
+  GdkWindow *window;
+  gboolean   over; /* either mouse over, or while dragging */
+  gint64     last_scroll_time;
+  guint      conceil_timer;
+
+  gdouble    current_pos;
+  gdouble    source_pos;
+  gdouble    target_pos;
+  gint64     start_time;
+  gint64     end_time;
+  guint      tick_id;
+  guint      over_timeout_id;
+} Indicator;
+
+struct _GtkScrolledWindowPrivate
+{
+  GtkWidget     *hscrollbar;
+  GtkWidget     *vscrollbar;
+
+  Indicator hindicator;
+  Indicator vindicator;
+
+  GtkCornerType  window_placement;
+  guint16  shadow_type;
+
+  guint    hscrollbar_policy      : 2;
+  guint    vscrollbar_policy      : 2;
+  guint    hscrollbar_visible     : 1;
+  guint    vscrollbar_visible     : 1;
+  guint    focus_out              : 1; /* used by ::move-focus-out implementation */
+  guint    overlay_scrolling      : 1;
+  guint    use_indicators         : 1;
+
+  gint     min_content_width;
+  gint     min_content_height;
+
+  guint scroll_events_overshoot_id;
+
+  /* Kinetic scrolling */
+  GtkGesture *long_press_gesture;
+  GtkGesture *swipe_gesture;
+
+  /* These two gestures are mutually exclusive */
+  GtkGesture *drag_gesture;
+  GtkGesture *pan_gesture;
+
+  gdouble drag_start_x;
+  gdouble drag_start_y;
+
+  GdkDevice             *drag_device;
+  guint                  kinetic_scrolling         : 1;
+  guint                  capture_button_press      : 1;
+  guint                  in_drag                   : 1;
+
+  guint                  deceleration_id;
+
+  gdouble                x_velocity;
+  gdouble                y_velocity;
+
+  gdouble                unclamped_hadj_value;
+  gdouble                unclamped_vadj_value;
+};
+
+static gint
+_gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
+{
+  GtkScrolledWindowClass *class;
+
+  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
+
+  class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
+
+  if (class->scrollbar_spacing >= 0)
+    return class->scrollbar_spacing;
+  else
+    {
+      gint scrollbar_spacing;
+
+      gtk_widget_style_get (GTK_WIDGET (scrolled_window),
+                           "scrollbar-spacing", &scrollbar_spacing,
+                           NULL);
+
+      return scrollbar_spacing;
+    }
+}
+
+static void
+gtk_scrolled_window_get_preferred_size (GtkWidget      *widget,
+                                        GtkOrientation  orientation,
+                                        gint           *minimum_size,
+                                        gint           *natural_size)
+{
+  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+  GtkBin *bin = GTK_BIN (scrolled_window);
+  GtkWidget *child;
+  gint min_size, nat_size;
+
+  child = gtk_bin_get_child (bin);
+  if (child && gtk_widget_get_visible (child))
+    {
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+       {
+         gtk_widget_get_preferred_width (child, &min_size, &nat_size);
+         if (priv->min_content_width >= 0)
+           min_size = MAX (min_size, priv->min_content_width);
+       }
+      else /* GTK_ORIENTATION_VERTICAL */
+       {
+         gtk_widget_get_preferred_height (child, &min_size, &nat_size);
+         if (priv->min_content_height >= 0)
+           min_size = MAX (min_size, priv->min_content_height);
+       }
+    }
+
+  if (!priv->use_indicators)
+    {
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+       {
+         if (priv->vscrollbar_policy != GTK_POLICY_NEVER)
+           {
+             gint min_width, nat_width;
+             gint scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+             gtk_widget_get_preferred_width (priv->vscrollbar, &min_width, &nat_width);
+             min_size += MAX (0, scrollbar_spacing + min_width);
+             nat_size += MAX (0, scrollbar_spacing + nat_width);
+           }
+         if (priv->hscrollbar_policy != GTK_POLICY_NEVER)
+           {
+             gint min_width, nat_width;
+             gtk_widget_get_preferred_width (priv->hscrollbar, &min_width, &nat_width);
+             min_size = MAX (min_size, min_width);
+             nat_size = MAX (nat_size, nat_width);
+           }
+       }
+      else /* orientation == GTK_ORIENTATION_VERTICAL */
+       {
+         if (priv->hscrollbar_policy != GTK_POLICY_NEVER)
+           {
+             gint min_height, nat_height;
+             gint scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+             gtk_widget_get_preferred_height (priv->hscrollbar, &min_height, &nat_height);
+             min_size += MAX (0, scrollbar_spacing + min_height);
+             nat_size += MAX (0, scrollbar_spacing + nat_height);
+           }
+         if (priv->vscrollbar_policy != GTK_POLICY_NEVER)
+           {
+             gint min_height, nat_height;
+             gtk_widget_get_preferred_height (priv->vscrollbar, &min_height, &nat_height);
+             min_size = MAX (min_size, min_height);
+             nat_size = MAX (nat_size, nat_height);
+           }
+       }
+    }
+
+  if (priv->shadow_type != GTK_SHADOW_NONE)
+    {
+      GtkStyleContext *context;
+      GtkStateFlags state;
+      GtkBorder padding, border;
+
+      context = gtk_widget_get_style_context (GTK_WIDGET (widget));
+      state = gtk_style_context_get_state (context);
+
+      gtk_style_context_get_padding (context, state, &padding);
+      gtk_style_context_get_border (context, state, &border);
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+       {
+         min_size += padding.left + padding.right + border.left + border.right;
+         nat_size += padding.left + padding.right + border.left + border.right;
+       }
+      else
+       {
+         min_size += padding.top + padding.bottom + border.top + border.bottom;
+         nat_size += padding.top + padding.bottom + border.top + border.bottom;
+       }
+    }
+
+  *minimum_size = min_size;
+  *natural_size = nat_size;
+}
+
+static void
+gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
+                                         gint      *minimum_size,
+                                         gint      *natural_size)
+{
+  gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
+}
+
+static void
+gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
+                                          gint      *minimum_size,
+                                          gint      *natural_size)
+{
+  gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
+}
+
+static void
+gtk_scrolled_window_get_preferred_height_for_width (GtkWidget *widget,
+                                                    gint       width,
+                                                    gint      *minimum_height,
+                                                    gint      *natural_height)
+{
+  GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
+}
+
+static void
+gtk_scrolled_window_get_preferred_width_for_height (GtkWidget *widget,
+                                                    gint       height,
+                                                    gint      *minimum_width,
+                                                    gint      *natural_width)
+{
+  GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
+}