Make --with-glade actually work.
[aspectbin.git] / aspectbin.c
blobba3bbda562e6fb690803e1ac789d1be8763aca23
1 /* AspectBin - A GTK+ container for packing with consrained aspect ratio.
2 * Copyright (C) 2009 Nick Bowler
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include <stdio.h>
21 #include "aspectbin.h"
23 enum {
24 PROP_0,
25 PROP_CONSTRAIN,
26 PROP_FILL,
29 enum {
30 CHILD_PROP_0,
31 CHILD_PROP_ALIGN,
34 static void aspect_bin_size_request(GtkWidget *, GtkRequisition *);
35 static void aspect_bin_size_allocate(GtkWidget *, GtkAllocation *);
36 static void aspect_bin_add(GtkContainer *, GtkWidget *);
37 static void aspect_bin_remove(GtkContainer *, GtkWidget *);
38 static void aspect_bin_forall(GtkContainer *, gboolean, GtkCallback, gpointer);
39 static GType aspect_bin_child_type(GtkContainer *);
41 static void aspect_bin_get_property(GObject *, guint, GValue *, GParamSpec *);
42 static void aspect_bin_set_property(GObject *, guint, const GValue *,
43 GParamSpec *);
44 static void aspect_bin_set_child_property(GtkContainer *, GtkWidget *, guint,
45 const GValue *, GParamSpec *);
46 static void aspect_bin_get_child_property(GtkContainer *, GtkWidget *, guint,
47 GValue *, GParamSpec *);
49 G_DEFINE_TYPE(AspectBin, aspect_bin, GTK_TYPE_CONTAINER)
51 static void aspect_bin_init(AspectBin *abin)
53 GTK_WIDGET_SET_FLAGS(abin, GTK_NO_WINDOW);
55 abin->body = NULL;
56 abin->side = NULL;
57 abin->ratio = 1;
58 abin->body_align = 0.5;
59 abin->side_align = 0.5;
60 abin->constrain = FALSE;
61 abin->fill = TRUE;
64 static void aspect_bin_class_init(AspectBinClass *class)
66 GObjectClass *object_class = G_OBJECT_CLASS(class);
67 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
68 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(class);
70 widget_class->size_request = aspect_bin_size_request;
71 widget_class->size_allocate = aspect_bin_size_allocate;
73 container_class->add = aspect_bin_add;
74 container_class->remove = aspect_bin_remove;
75 container_class->forall = aspect_bin_forall;
76 container_class->child_type = aspect_bin_child_type;
78 container_class->set_child_property = aspect_bin_set_child_property;
79 container_class->get_child_property = aspect_bin_get_child_property;
81 object_class->set_property = aspect_bin_set_property;
82 object_class->get_property = aspect_bin_get_property;
84 g_object_class_install_property(object_class,
85 PROP_FILL,
86 g_param_spec_boolean("fill",
87 "Fill",
88 "Allocate all remaining space to the side.",
89 TRUE,
90 G_PARAM_READWRITE));
91 g_object_class_install_property(object_class,
92 PROP_CONSTRAIN,
93 g_param_spec_boolean("constrain",
94 "Constrain",
95 "Try not to place the side beyond the body's edges.",
96 FALSE,
97 G_PARAM_READWRITE));
98 gtk_container_class_install_child_property(container_class,
99 CHILD_PROP_ALIGN,
100 g_param_spec_float("align",
101 "Alignment",
102 "Alignment of the child within the available space.",
103 0.0, 1.0, 0.5,
104 G_PARAM_READWRITE));
107 GtkWidget *aspect_bin_new(void)
109 return GTK_WIDGET(g_object_new(ASPECT_BIN_TYPE, NULL));
112 static void aspect_bin_set_property(GObject *object,
113 guint prop_id,
114 const GValue *value,
115 GParamSpec *pspec)
117 AspectBin *abin = ASPECT_BIN(object);
119 switch (prop_id) {
120 case PROP_CONSTRAIN:
121 abin->constrain = g_value_get_boolean(value);
122 gtk_widget_queue_resize(GTK_WIDGET(abin));
123 break;
124 case PROP_FILL:
125 abin->fill = g_value_get_boolean(value);
126 gtk_widget_queue_resize(GTK_WIDGET(abin));
127 break;
128 default:
129 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
133 static void aspect_bin_get_property(GObject *object,
134 guint prop_id,
135 GValue *value,
136 GParamSpec *pspec)
138 AspectBin *abin = ASPECT_BIN(object);
140 switch (prop_id) {
141 case PROP_CONSTRAIN:
142 g_value_set_boolean(value, abin->constrain);
143 break;
144 case PROP_FILL:
145 g_value_set_boolean(value, abin->fill);
146 break;
147 default:
148 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
152 static void aspect_bin_set_child_property(GtkContainer *container,
153 GtkWidget *child,
154 guint prop_id,
155 const GValue *value,
156 GParamSpec *pspec)
158 AspectBin *abin = ASPECT_BIN(container);
159 gfloat align;
161 g_assert(child == abin->body || child == abin->side);
163 switch (prop_id) {
164 case CHILD_PROP_ALIGN:
165 align = g_value_get_float(value);
166 if (child == abin->body)
167 abin->body_align = align;
168 else if (child == abin->side)
169 abin->side_align = align;
170 gtk_widget_queue_resize(GTK_WIDGET(abin));
171 break;
172 default:
173 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(container,
174 prop_id, pspec);
178 static void aspect_bin_get_child_property(GtkContainer *container,
179 GtkWidget *child,
180 guint prop_id,
181 GValue *value,
182 GParamSpec *pspec)
184 AspectBin *abin = ASPECT_BIN(container);
185 gfloat align = 0;
187 g_assert(child == abin->body || child == abin->side);
189 switch (prop_id) {
190 case CHILD_PROP_ALIGN:
191 if (child == abin->body)
192 align = abin->body_align;
193 else if (child == abin->side)
194 align = abin->side_align;
195 g_value_set_float(value, align);
196 break;
197 default:
198 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(container,
199 prop_id, pspec);
203 static void aspect_bin_add(GtkContainer *container, GtkWidget *widget)
205 AspectBin *abin;
206 g_return_if_fail(IS_ASPECT_BIN(container));
207 abin = ASPECT_BIN(container);
209 if (!abin->body)
210 aspect_bin_set_body(abin, widget, 1);
211 else if (!abin->side)
212 aspect_bin_set_side(abin, widget);
213 else
214 g_warning("AspectBin cannot have more than 2 children.\n");
217 static void aspect_bin_remove(GtkContainer *container, GtkWidget *child)
219 AspectBin *abin = ASPECT_BIN(container);
221 if (abin->body == child) {
222 aspect_bin_set_body(abin, NULL, 1);
223 } else if (abin->side == child) {
224 aspect_bin_set_side(abin, NULL);
228 static void aspect_bin_forall(GtkContainer *container,
229 gboolean include_internals,
230 GtkCallback callback,
231 gpointer callback_data)
233 AspectBin *abin = ASPECT_BIN(container);
234 g_return_if_fail(callback != NULL);
236 if (abin->body)
237 callback(abin->body, callback_data);
238 if (abin->side)
239 callback(abin->side, callback_data);
242 static GType aspect_bin_child_type(GtkContainer *container)
244 if (!ASPECT_BIN(container)->body || !ASPECT_BIN(container)->side)
245 return GTK_TYPE_WIDGET;
246 return G_TYPE_NONE;
249 static void
250 aspect_bin_size_request(GtkWidget *widget, GtkRequisition *requisition)
252 AspectBin *abin = ASPECT_BIN(widget);
253 GtkRequisition creq = {0}, areq = {0};
255 if (abin->side && GTK_WIDGET_VISIBLE(abin->side)) {
256 gtk_widget_size_request(abin->side, &creq);
259 if (abin->body && GTK_WIDGET_VISIBLE(abin->body)) {
260 int wtmp, htmp;
261 gtk_widget_size_request(abin->body, &areq);
262 wtmp = areq.height * abin->ratio + 0.5;
263 htmp = areq.width / abin->ratio + 0.5;
265 if (wtmp > areq.width) {
266 areq.width = wtmp;
267 } else {
268 areq.height = htmp;
272 requisition->width = areq.width + creq.width;
273 requisition->height = MAX(areq.height, creq.height);
276 static void
277 aspect_bin_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
279 AspectBin *abin = ASPECT_BIN(widget);
280 GtkRequisition creq = {0};
281 GtkAllocation csize = {0}, asize = {0};
283 /* First find the best fit for the body. */
284 if (abin->body && GTK_WIDGET_VISIBLE(abin->body)) {
285 asize.height = allocation->height;
286 asize.width = asize.height * abin->ratio + 0.5;
288 if (asize.width > allocation->width) {
289 asize.width = allocation->width;
290 asize.height = asize.width / abin->ratio + 0.5;
294 /* Now try to fit the side. */
295 if (abin->side && GTK_WIDGET_VISIBLE(abin->side)) {
296 gtk_widget_get_child_requisition(abin->side, &creq);
298 if (allocation->width - asize.width < creq.width) {
299 /* It didn't fit, squish the constrained guy. */
300 asize.width = allocation->width - creq.width;
301 asize.height = asize.width / abin->ratio + 0.5;
304 csize.width = allocation->width - asize.width;
305 csize.x = asize.width;
307 if (abin->fill && abin->constrain) {
308 csize.height = asize.height;
309 } else if (abin->fill) {
310 csize.height = allocation->height;
311 } else {
312 csize.height = MIN(creq.height, allocation->height);
316 asize.y = (allocation->height - asize.height) * abin->body_align + 0.5;
318 if (abin->constrain && csize.height <= asize.height) {
319 csize.y = asize.y + (asize.height-csize.height)
320 * abin->side_align + 0.5;
321 } else if (abin->constrain) {
322 csize.y = (allocation->height - csize.height)
323 * abin->body_align + 0.5;
324 } else {
325 csize.y = (allocation->height - csize.height)
326 * abin->side_align + 0.5;
329 if (abin->body)
330 gtk_widget_size_allocate(abin->body, &asize);
331 if (abin->side)
332 gtk_widget_size_allocate(abin->side, &csize);
335 static gboolean
336 set_widget(GtkWidget **dest, GtkWidget *parent, GtkWidget *widget)
338 gboolean need_resize = FALSE;
340 if (*dest == widget)
341 return FALSE;
343 if (*dest) {
344 need_resize |= GTK_WIDGET_VISIBLE(*dest);
345 gtk_widget_unparent(*dest);
348 *dest = widget;
349 if (widget) {
350 gtk_widget_set_parent(widget, parent);
351 need_resize |= GTK_WIDGET_VISIBLE(widget);
354 return GTK_WIDGET_VISIBLE(parent) && need_resize;
357 void aspect_bin_set_body(AspectBin *abin, GtkWidget *widget, gfloat ratio)
359 g_return_if_fail(IS_ASPECT_BIN(abin));
360 g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget));
361 g_return_if_fail(widget == NULL || widget->parent == NULL);
363 if (set_widget(&abin->body, GTK_WIDGET(abin), widget))
364 gtk_widget_queue_resize(GTK_WIDGET(abin));
367 void aspect_bin_set_side(AspectBin *abin, GtkWidget *widget)
369 g_return_if_fail(IS_ASPECT_BIN(abin));
370 g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget));
371 g_return_if_fail(widget == NULL || widget->parent == NULL);
373 if (set_widget(&abin->side, GTK_WIDGET(abin), widget))
374 gtk_widget_queue_resize(GTK_WIDGET(abin));
377 GtkWidget *aspect_bin_get_body(AspectBin *abin)
379 g_return_val_if_fail(IS_ASPECT_BIN(abin), NULL);
380 return abin->body;
383 GtkWidget *aspect_bin_get_side(AspectBin *abin)
385 g_return_val_if_fail(IS_ASPECT_BIN(abin), NULL);
386 return abin->side;