From ca3b1eb2ee3611d7721483c67872dbb36d6c2019 Mon Sep 17 00:00:00 2001 From: Matt Birkholz Date: Tue, 15 Dec 2015 13:12:06 -0700 Subject: [PATCH] gtk: Port GtkScrolledView and GtkPanedView to Gtk+ 3.10.8. --- src/gtk/README | 3 +- src/gtk/configure.ac | 4 +- src/gtk/gtkpanedview-3.10.8.c | 741 +++++++++++++++++++++++++++++++ src/gtk/gtkscrolledview-3.10.8.c | 306 +++++++++++++ 4 files changed, 1051 insertions(+), 3 deletions(-) create mode 100644 src/gtk/gtkpanedview-3.10.8.c create mode 100644 src/gtk/gtkscrolledview-3.10.8.c diff --git a/src/gtk/README b/src/gtk/README index 6f18b80ef..bdf98c2ca 100644 --- a/src/gtk/README +++ b/src/gtk/README @@ -11,7 +11,7 @@ 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. Two are provided and +compatible with your system's toolkit. Three are provided and correspond to recent Ubuntu releases. If your system is using a matching or "close enough" version of the toolkit, you will get a treat. @@ -19,6 +19,7 @@ treat. Ubuntu Gtk+ 15.10 3.16.7 15.04 3.14.13 + 14.10lts 3.10.8 The solution: Add GtkScrolledView and GtkPanedView to Gtk+. Unfortunately, implementing both GtkScrolledWindow and GtkScrolledView diff --git a/src/gtk/configure.ac b/src/gtk/configure.ac index f7ca76aa6..a036fe6af 100644 --- a/src/gtk/configure.ac +++ b/src/gtk/configure.ac @@ -65,8 +65,8 @@ elif pkg-config --exists 'gtk+-3.0 >= 3.16.7' 2>/dev/null; then GTK_VERSION=3.16.7 elif pkg-config --exists 'gtk+-3.0 >= 3.14.13' 2>/dev/null; then 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 'gtk+-3.0 >= 3.10.8' 2>/dev/null; then + 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 diff --git a/src/gtk/gtkpanedview-3.10.8.c b/src/gtk/gtkpanedview-3.10.8.c new file mode 100644 index 000000000..8096a29c6 --- /dev/null +++ b/src/gtk/gtkpanedview-3.10.8.c @@ -0,0 +1,741 @@ +/* -*-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 . */ + +#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); + } +} + +/* The following definitions were copied unchanged from gtkpaned.c + v3.14.13 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; + GdkDevice *grab_device; + GdkRectangle handle_pos; + GdkWindow *handle; + + gint child1_size; + gint drag_pos; + gint last_allocation; + gint max_position; + gint min_position; + gint original_position; + + guint32 grab_time; + + guint handle_prelit : 1; + guint in_drag : 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; +}; + +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) + return gtk_paned_get_preferred_size_for_orientation (widget, size, minimum, natural); + else + return 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)) + { + 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, + priv->handle_pos.y, + handle_size, + priv->handle_pos.height); + } + else + { + gdk_window_move_resize (priv->handle, + priv->handle_pos.x, + priv->handle_pos.y, + priv->handle_pos.width, + handle_size); + } + } + + /* 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.10.8.c b/src/gtk/gtkscrolledview-3.10.8.c new file mode 100644 index 000000000..d86be57c6 --- /dev/null +++ b/src/gtk/gtkscrolledview-3.10.8.c @@ -0,0 +1,306 @@ +/* -*-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 + . */ + +#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; +} + +/* The following definitions were copied unchanged from + gtkscrolledwindow.c v3.10.8 unless otherwise noted. + + struct _GtkScrolledWindowPrivate + 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 () +*/ + +struct _GtkScrolledWindowPrivate +{ + GtkWidget *hscrollbar; + GtkWidget *vscrollbar; + + 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; /* Flag used by ::move-focus-out implementation */ + + gint min_content_width; + gint min_content_height; + + /* Kinetic scrolling */ + GdkEvent *button_press_event; + GdkWindow *overshoot_window; + GdkDevice *drag_device; + 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; +}; + +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 (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) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + 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) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width); +} -- 2.25.1