1 /* Copyright (c) 2007, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * SECTION:modest-scroll-area
33 * @short_description: A helper to create Maemo specific views,
34 * which are using scrollable area
36 * #GtkScrollArea combines a large widget that needs scrolling (like a
37 * text editor or a tree view) and other widgets that wouldn't fit one
38 * the screen normally without scrolling (like entries, toolbars etc.)
39 * into one scrollable area.
42 #include "modest-scroll-area.h"
43 #include <gtk/gtkscrolledwindow.h>
44 #include <gtk/gtkfixed.h>
45 #include <gtk/gtkadjustment.h>
46 #include <gtk/gtkwidget.h>
53 /* Scrolled windows */
57 /* Widget that's being contained */
60 /* Vertical adjustment for scrolled windows */
61 GtkAdjustment *outadj;
67 static void modest_scroll_area_outer_value_changed (GtkAdjustment *adjustment,
68 ModestScrollArea *sc);
69 static void modest_scroll_area_inner_value_changed (GtkAdjustment *adjustment,
70 ModestScrollArea *sc);
71 static void modest_scroll_area_size_allocate (GtkWidget *widget,
72 GtkAllocation *allocation,
73 ModestScrollArea *sc);
74 static void modest_scroll_area_child_requisition (GtkWidget *widget,
76 ModestScrollArea *sc);
77 static void modest_scroll_area_fixed_allocate (GtkWidget *widget,
78 GtkAllocation *allocation,
79 ModestScrollArea *sc);
81 static int calculate_size (GtkWidget *widget);
84 * modest_scroll_area_new:
85 * @sw: #GtkWidget - #GtkScrolledWindow
86 * @child: #GtkWidget - child to be place inside the sw
88 * This is not a widget. It's a helper function to create
89 * hildon-specific scrolling methods.
90 * A common situation where the scroll area should be used
91 * might be following. A view containing @GtkTreeView based widget,
92 * (or any similar widget which has built-in @GtkScrolledWindow support)
93 * and eg. couple buttons. Normaly @GtkScrolledWindow can not handle
94 * the situation so that the @GtkTreeView built-in support
95 * would work. The scroll area is connecting this built-in system to
96 * the scrolled window and also noticing the buttons. To use, one should
97 * create a box to which pack the buttons and the scroll area.
98 * The scroll area then contains the problematic widget eg. the @GtkTreeView.
99 * Then the box should be placed in the @GtkScrolledWindow.
100 * The function is currently assuming that the newly created scroll area
101 * hierarchy is not modified in anyway. Or if it is, it may lead to
102 * unwanted problems. Also assumed, that the @child will be packed
105 * Returns: a @GtkFixed
108 modest_scroll_area_new (GtkWidget *sw, GtkWidget *child)
112 ModestScrollArea *sc;
114 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (sw)
115 && GTK_IS_WIDGET (child), NULL);
117 swi = gtk_scrolled_window_new (NULL, NULL);
118 fixed = gtk_fixed_new ();
119 sc = g_malloc (sizeof (ModestScrollArea));
120 memset (sc, 0, sizeof (ModestScrollArea));
122 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swi),
123 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
125 gtk_container_add (GTK_CONTAINER (swi), child);
126 gtk_fixed_put (GTK_FIXED (fixed), swi, 0, 0);
132 sc->outadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw));
133 sc->inadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (swi));
135 g_signal_connect_after (G_OBJECT (child), "size-request",
136 G_CALLBACK (modest_scroll_area_child_requisition), sc);
138 g_signal_connect_after (G_OBJECT (sc->outadj), "value_changed",
139 G_CALLBACK (modest_scroll_area_outer_value_changed), sc);
140 g_signal_connect_after (G_OBJECT (sc->inadj), "value_changed",
141 G_CALLBACK (modest_scroll_area_inner_value_changed), sc);
143 g_signal_connect_after (G_OBJECT (sw), "size-allocate",
144 G_CALLBACK (modest_scroll_area_size_allocate), sc);
145 g_signal_connect (G_OBJECT (sc->fixed), "size-allocate",
146 G_CALLBACK (modest_scroll_area_fixed_allocate), sc);
147 g_signal_connect_swapped (G_OBJECT (sw), "destroy",
148 G_CALLBACK (g_free), sc);
150 gtk_widget_show_all (sw);
152 gtk_widget_set_redraw_on_allocate (GTK_WIDGET(sc->fixed), FALSE);
153 gtk_widget_set_redraw_on_allocate (GTK_WIDGET(sw), FALSE);
154 gtk_widget_set_redraw_on_allocate (GTK_WIDGET(child), FALSE);
159 modest_scroll_area_fixed_allocate (GtkWidget *widget,
160 GtkAllocation *allocation,
161 ModestScrollArea *sc)
163 gtk_widget_set_size_request (sc->swinner, -1,
164 MIN (sc->outadj->page_size, allocation->height));
169 calculate_size (GtkWidget *widget)
173 if (GTK_IS_TEXT_VIEW (widget))
176 if (GTK_IS_CONTAINER (widget)) {
178 GList *children = gtk_container_get_children (GTK_CONTAINER (widget));
179 for (tmp = children; tmp != NULL; tmp = g_list_next (tmp)) {
180 GtkWidget *wid = GTK_WIDGET (tmp->data);
181 gint sz = calculate_size (wid);
182 if ((GTK_WIDGET_VISIBLE (wid))) {
186 g_list_free (children);
188 size = widget->allocation.height;
195 modest_scroll_area_child_requisition (GtkWidget *widget,
197 ModestScrollArea *sc)
199 /* Limit height to fixed height */
200 gint new_req = MAX (req->height, sc->fixed->allocation.height);
201 gint adjust_factor = calculate_size (sc->swouter) * 0.7;
203 adjust_factor = MAX (0, adjust_factor - sc->outadj->value);
204 new_req = MIN (sc->outadj->page_size - adjust_factor, new_req);
206 /* FIXME: hack, to provent gtk criticals */
210 gtk_widget_set_size_request (sc->fixed, -1, req->height);
211 /* Request inner scrolled window at most page size */
212 gtk_widget_set_size_request (sc->swinner, -1, new_req);
216 modest_scroll_area_outer_value_changed (GtkAdjustment *adjustment,
217 ModestScrollArea *sc)
221 gtk_widget_size_request (sc->child, &req);
223 /* Update inner adjustment position based on outer one, update fixed position */
224 if ((sc->outadj->value + sc->outadj->page_size) > sc->fixed->allocation.y
225 && sc->outadj->value < (sc->fixed->allocation.y + req.height))
229 new_pos = MAX (sc->outadj->value - sc->fixed->allocation.y, 0);
230 new_pos = MIN (new_pos, req.height - sc->inadj->page_size);
231 new_pos = MAX (new_pos, 0);
233 gtk_fixed_move (GTK_FIXED (sc->fixed), sc->swinner, 0, new_pos);
234 gtk_adjustment_set_value (sc->inadj, new_pos);
239 modest_scroll_area_inner_value_changed (GtkAdjustment *adjustment,
240 ModestScrollArea *sc)
242 /* Update outer adjustment based on inner adjustment position */
243 if (sc->outadj->value != sc->fixed->allocation.y + adjustment->value)
244 gtk_adjustment_set_value (sc->outadj,
245 sc->fixed->allocation.y + adjustment->value);
248 __inline__ static gint
249 calculate_width (ModestScrollArea *sc)
251 GtkScrolledWindow *scwin = GTK_SCROLLED_WINDOW (sc->swouter);
252 return (scwin->hscrollbar_visible * scwin->hscrollbar->allocation.width);
256 modest_scroll_area_size_allocate (GtkWidget *widget,
257 GtkAllocation *allocation,
258 ModestScrollArea *sc)
260 g_return_if_fail (widget);
261 g_return_if_fail (allocation);
262 g_return_if_fail (sc);
264 gtk_widget_set_size_request (sc->fixed, calculate_width (sc),
265 sc->fixed->allocation.height);
266 gtk_widget_set_size_request (sc->child,
267 sc->fixed->allocation.width, -1);