2 * This file is part of hildon-libs
4 * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
6 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; version 2.1 of
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * SECTION:hildon-scroll-area
27 * @short_description: A helper to create Maemo specific views,
28 * which are using scrollable area
30 * #GtkScrollArea combines a large widget that needs scrolling (like a
31 * text editor or a tree view) and other widgets that wouldn't fit one
32 * the screen normally without scrolling (like entries, toolbars etc.)
33 * into one scrollable area.
36 #include "hildon-scroll-area.h"
37 #include <gtk/gtkscrolledwindow.h>
38 #include <gtk/gtkfixed.h>
39 #include <gtk/gtkadjustment.h>
40 #include <gtk/gtkwidget.h>
47 /* Scrolled windows */
51 /* Widget that's being contained */
54 /* Vertical adjustment for scrolled windows */
55 GtkAdjustment *outadj;
61 static void hildon_scroll_area_outer_value_changed (GtkAdjustment *adjustment,
62 HildonScrollArea *sc);
63 static void hildon_scroll_area_inner_value_changed (GtkAdjustment *adjustment,
64 HildonScrollArea *sc);
65 static void hildon_scroll_area_size_allocate (GtkWidget *widget,
66 GtkAllocation *allocation,
67 HildonScrollArea *sc);
68 static void hildon_scroll_area_child_requisition (GtkWidget *widget,
70 HildonScrollArea *sc);
71 static void hildon_scroll_area_fixed_allocate (GtkWidget *widget,
72 GtkAllocation *allocation,
73 HildonScrollArea *sc);
75 static int calculate_size (GtkWidget *widget);
78 * hildon_scroll_area_new:
79 * @sw: #GtkWidget - #GtkScrolledWindow
80 * @child: #GtkWidget - child to be place inside the sw
82 * This is not a widget. It's a helper function to create
83 * hildon-specific scrolling methods.
84 * A common situation where the scroll area should be used
85 * might be following. A view containing @GtkTreeView based widget,
86 * (or any similar widget which has built-in @GtkScrolledWindow support)
87 * and eg. couple buttons. Normaly @GtkScrolledWindow can not handle
88 * the situation so that the @GtkTreeView built-in support
89 * would work. The scroll area is connecting this built-in system to
90 * the scrolled window and also noticing the buttons. To use, one should
91 * create a box to which pack the buttons and the scroll area.
92 * The scroll area then contains the problematic widget eg. the @GtkTreeView.
93 * Then the box should be placed in the @GtkScrolledWindow.
94 * The function is currently assuming that the newly created scroll area
95 * hierarchy is not modified in anyway. Or if it is, it may lead to
96 * unwanted problems. Also assumed, that the @child will be packed
99 * Returns: a @GtkFixed
101 GtkWidget *hildon_scroll_area_new (GtkWidget *sw, GtkWidget *child)
105 HildonScrollArea *sc;
107 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (sw)
108 && GTK_IS_WIDGET (child), NULL);
110 swi = gtk_scrolled_window_new (NULL, NULL);
111 fixed = gtk_fixed_new ();
112 sc = g_malloc (sizeof (HildonScrollArea));
113 memset (sc, 0, sizeof (HildonScrollArea));
115 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swi),
116 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
118 gtk_container_add (GTK_CONTAINER (swi), child);
119 gtk_fixed_put (GTK_FIXED (fixed), swi, 0, 0);
125 sc->outadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw));
126 sc->inadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (swi));
128 g_signal_connect_after (G_OBJECT (child), "size-request",
129 G_CALLBACK (hildon_scroll_area_child_requisition), sc);
131 g_signal_connect_after (G_OBJECT (sc->outadj), "value_changed",
132 G_CALLBACK (hildon_scroll_area_outer_value_changed), sc);
133 g_signal_connect_after (G_OBJECT (sc->inadj), "value_changed",
134 G_CALLBACK (hildon_scroll_area_inner_value_changed), sc);
136 g_signal_connect_after (G_OBJECT (sw), "size-allocate",
137 G_CALLBACK (hildon_scroll_area_size_allocate), sc);
138 g_signal_connect (G_OBJECT (sc->fixed), "size-allocate",
139 G_CALLBACK (hildon_scroll_area_fixed_allocate), sc);
140 g_signal_connect_swapped (G_OBJECT (sw), "destroy",
141 G_CALLBACK (g_free), sc);
143 gtk_widget_show_all (sw);
147 static void hildon_scroll_area_fixed_allocate (GtkWidget *widget,
148 GtkAllocation *allocation,
149 HildonScrollArea *sc)
151 gtk_widget_set_size_request (sc->swinner, -1,
152 MIN (sc->outadj->page_size, allocation->height));
156 static int calculate_size (GtkWidget *widget)
160 if (GTK_IS_TEXT_VIEW (widget))
163 if (GTK_IS_CONTAINER (widget)) {
164 GList *children = gtk_container_get_children (GTK_CONTAINER (widget));
165 while (children != NULL) {
166 GtkWidget *wid = GTK_WIDGET (children->data);
167 gint sz = calculate_size (wid);
168 if ((GTK_WIDGET_VISIBLE (wid))) {
172 children = g_list_next (children);
175 size = widget->allocation.height;
181 static void hildon_scroll_area_child_requisition (GtkWidget *widget,
183 HildonScrollArea *sc)
185 /* Limit height to fixed height */
186 gint new_req = MAX (req->height, sc->fixed->allocation.height);
187 new_req = MIN (sc->outadj->page_size - adjust_factor, new_req);
188 gint adjust_factor = calculate_size (sc->swouter) * 0.7;
189 adjust_factor = MAX (0, adjust_factor - sc->outadj->value);
191 gtk_widget_set_size_request (sc->fixed, -1, req->height);
192 /* Request inner scrolled window at most page size */
193 gtk_widget_set_size_request (sc->swinner, -1, new_req);
196 static void hildon_scroll_area_outer_value_changed (GtkAdjustment *adjustment,
197 HildonScrollArea *sc)
200 gtk_widget_size_request (sc->child, &req);
202 /* Update inner adjustment position based on outer one, update fixed position */
203 if ((sc->outadj->value + sc->outadj->page_size) > sc->fixed->allocation.y
204 && sc->outadj->value < (sc->fixed->allocation.y + req.height))
208 new_pos = MAX (sc->outadj->value - sc->fixed->allocation.y, 0);
209 new_pos = MIN (new_pos, req.height - sc->inadj->page_size);
210 new_pos = MAX (new_pos, 0);
212 gtk_fixed_move (GTK_FIXED (sc->fixed), sc->swinner, 0, new_pos);
213 gtk_adjustment_set_value (sc->inadj, new_pos);
217 static void hildon_scroll_area_inner_value_changed (GtkAdjustment *adjustment,
218 HildonScrollArea *sc)
220 /* Update outer adjustment based on inner adjustment position */
221 if (sc->outadj->value != sc->fixed->allocation.y + adjustment->value)
222 gtk_adjustment_set_value (sc->outadj,
223 sc->fixed->allocation.y + adjustment->value);
226 __inline__ static gint calculate_width (HildonScrollArea *sc)
228 GtkScrolledWindow *scwin = GTK_SCROLLED_WINDOW (sc->swouter);
229 return (scwin->hscrollbar_visible * scwin->hscrollbar->allocation.width);
232 static void hildon_scroll_area_size_allocate (GtkWidget *widget,
233 GtkAllocation *allocation,
234 HildonScrollArea *sc)
236 gtk_widget_set_size_request (sc->fixed, calculate_width (sc), sc->fixed->allocation.height);
237 gtk_widget_set_size_request (sc->child, sc->fixed->allocation.width, -1);