]> git.karo-electronics.de Git - gbdfed.git/blob - labcon.c
Initial import of upstream V1.6 from
[gbdfed.git] / labcon.c
1 /*
2  * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
18  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
19  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22
23 #include "labcon.h"
24 #include <gtk/gtklabel.h>
25 #include <gtk/gtkdrawingarea.h>
26
27 static GtkContainerClass *parent_class = 0;
28
29 static void
30 labcon_size_request(GtkWidget *w, GtkRequisition *req)
31 {
32     LabCon *l = LABCON(w);
33     guint width = 0;
34     GtkRequisition l_rec, c_rec;
35
36     l_rec.width = c_rec.width = l_rec.height = c_rec.height = 0;
37
38     if (l->label != 0) {
39         if (GTK_WIDGET_VISIBLE(l->label))
40           gtk_widget_size_request(l->label, &l_rec);
41     } else {
42         if (GTK_WIDGET_VISIBLE(l->image))
43           gtk_widget_size_request(l->image, &l_rec);
44     }
45     if (GTK_WIDGET_VISIBLE(l->child))
46       gtk_widget_size_request(l->child, &c_rec);
47
48     if (l->leader)
49       width = LABCON(l->leader)->label_width;
50     width = MAX(width, l_rec.width);
51
52     req->height = MAX(l_rec.height, c_rec.height) +
53         (GTK_CONTAINER(l)->border_width * 2);
54     req->width = width + c_rec.width + l->spacing +
55         (GTK_CONTAINER(l)->border_width * 2);
56 }
57
58 static void
59 labcon_size_allocate(GtkWidget *w, GtkAllocation *all)
60 {
61     guint i;
62     GtkWidget *label;
63     LabCon *leader, *l = LABCON(w);
64     GtkRequisition l_rec, c_rec, ll_rec;
65     GtkAllocation w_all;
66
67     w->allocation = *all;
68
69     l_rec.width = c_rec.width = l_rec.height = c_rec.height = 0;
70
71     label = (l->label != 0) ? l->label : l->image;
72
73     if (GTK_WIDGET_VISIBLE(label))
74       gtk_widget_get_child_requisition(label, &l_rec);
75     if (GTK_WIDGET_VISIBLE(l->child))
76       gtk_widget_get_child_requisition(l->child, &c_rec);
77
78     /*
79      * Make sure the height is non-zero and leaves the border.
80      */
81     w_all.x = all->x + GTK_CONTAINER(l)->border_width;
82     w_all.y = all->y + GTK_CONTAINER(l)->border_width;
83     w_all.height = MAX(1, (gint) all->height -
84                        (gint) (GTK_CONTAINER(l)->border_width * 2));
85
86     if (l->leader != 0) {
87         leader = LABCON(l->leader);
88
89         if (leader->label)
90           gtk_widget_get_child_requisition(leader->label, &ll_rec);
91         else
92           gtk_widget_get_child_requisition(leader->image, &ll_rec);
93
94         if (ll_rec.width < l_rec.width) {
95             if (leader->label)
96               gtk_widget_set_size_request(leader->label,
97                                           l_rec.width, ll_rec.height);
98             else
99               gtk_widget_set_size_request(leader->image,
100                                           l_rec.width, ll_rec.height);
101         }
102
103         leader->label_width = MAX(l_rec.width, leader->label_width);
104         l_rec.width = leader->label_width;
105
106         for (i = 0; i < leader->group_used - 1; i++) {
107             if (LABCON(leader->group[i])->label)
108               gtk_widget_get_child_requisition(LABCON(leader->group[i])->label,
109                                                &ll_rec);
110             else
111               gtk_widget_get_child_requisition(LABCON(leader->group[i])->image,
112                                                &ll_rec);
113             if (ll_rec.width < l_rec.width) {
114                 if (LABCON(leader->group[i])->label)
115                   gtk_widget_set_size_request(LABCON(leader->group[i])->label,
116                                               l_rec.width, ll_rec.height);
117                 else
118                   gtk_widget_set_size_request(LABCON(leader->group[i])->image,
119                                               l_rec.width, ll_rec.height);
120             }
121         }
122     } else
123       l->label_width = l_rec.width;
124
125     if (l->pos == GTK_POS_LEFT) {
126         /*
127          * Position the label on the left of the child.
128          */
129
130         /*
131          * Calculate the allocation for the label widget.
132          */
133         w_all.width = l_rec.width;
134         gtk_widget_size_allocate(label, &w_all);
135
136         /*
137          * Calculate the allocation for the child widget. The child widget
138          * is expanded to fill the remaining space.
139          */
140         w_all.x += w_all.width + l->spacing;
141         w_all.width = all->width - (l_rec.width + l->spacing);
142         gtk_widget_size_allocate(l->child, &w_all);
143     } else {
144         /*
145          * Position the label on the right of the child.
146          */
147
148         /*
149          * Calculate the allocation for the child widget. The child widget
150          * is expanded to fill the remaining space.
151          */
152         w_all.width = all->width - (l_rec.width + l->spacing);
153         gtk_widget_size_allocate(l->child, &w_all);
154
155         /*
156          * Calculate the allocation for the label widget.
157          */
158         w_all.x += w_all.width + l->spacing;
159         w_all.width = l_rec.width;
160         gtk_widget_size_allocate(label, &w_all);
161     }
162 }
163
164 static void
165 labcon_forall(GtkContainer *c, gboolean include_internals,
166               GtkCallback callback, gpointer callback_data)
167 {
168     LabCon *l = LABCON(c);
169
170     if (l->label)
171       (*callback)(l->label, callback_data);
172     else
173       (*callback)(l->image, callback_data);
174     (*callback)(l->child, callback_data);
175 }
176
177 static void
178 labcon_remove(GtkContainer *c, GtkWidget *w)
179 {
180     guint i;
181     LabCon *l = LABCON(c);
182
183     /*
184      * Make sure that the group list has been deallocated so we don't
185      * leak memory.
186      */
187     if (l->child == w) {
188         if (l->group_size > 0) {
189           for (i = 0; i < l->group_used; i++)
190             LABCON(l->group[i])->leader = 0;
191           l->group_size = l->group_used = 0;
192           g_free(l->group);
193           l->group = 0;
194         }
195     }
196 }
197
198 static void
199 labcon_class_init(gpointer g_class, gpointer class_data)
200 {
201     GtkWidgetClass *wc = GTK_WIDGET_CLASS(g_class);
202     LabConClass *lc = LABCON_CLASS(g_class);
203     GtkContainerClass *cc = GTK_CONTAINER_CLASS(g_class);
204
205     wc->size_request = labcon_size_request;
206     wc->size_allocate = labcon_size_allocate;
207
208     cc->remove = labcon_remove;
209     cc->forall = labcon_forall;
210
211     parent_class = g_type_class_peek_parent(lc);
212 }
213
214 static void
215 labcon_init(GTypeInstance *instance, gpointer g_class)
216 {
217     LabCon *l = LABCON(instance);
218
219     GTK_WIDGET_SET_FLAGS(l, GTK_NO_WINDOW);
220     gtk_widget_set_redraw_on_allocate(GTK_WIDGET(l), FALSE);
221
222     l->pixbuf = 0;
223     l->image = l->label = l->child = 0;
224     l->spacing = 0;
225     l->align = LABCON_ALIGN_LEFT;
226     l->pos = GTK_POS_LEFT;
227     l->label_width = 0;
228     l->leader = 0;
229     l->group_size = l->group_used = 0;
230 }
231
232 static gboolean
233 draw_pixbuf(GtkWidget *w, GdkEventExpose *event, gpointer data)
234 {
235     GdkPixbuf *p = GDK_PIXBUF(data);
236     gint x, y, wd, ht;
237
238     wd = gdk_pixbuf_get_width(p);
239     ht = gdk_pixbuf_get_height(p);
240
241     x = (w->allocation.width >> 1) - (wd >> 1);
242     y = (w->allocation.height >> 1) - (ht >> 1);
243     gdk_draw_pixbuf(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)],
244                     p, 0, 0, x, y, wd, ht, GDK_RGB_DITHER_NONE, 0, 0);
245
246     return FALSE;
247 }
248
249 /**********************************************************************
250  *
251  * API functions.
252  *
253  **********************************************************************/
254
255 GType
256 labcon_get_type(void)
257 {
258     static GType labcon_type = 0;
259   
260     if (!labcon_type) {
261         static const GTypeInfo labcon_info = {
262             sizeof (LabConClass),               /* class_size           */
263             0,                                  /* base_init            */
264             0,                                  /* base_finalize        */
265             labcon_class_init,                  /* class_init           */
266             0,                                  /* class_finalize       */
267             0,                                  /* class_data           */
268             sizeof(LabCon),                     /* instance_size        */
269             0,                                  /* n_preallocs          */
270             labcon_init,                        /* instance_init        */
271             0,                                  /* value_table          */
272         };
273
274         labcon_type = g_type_register_static(GTK_TYPE_CONTAINER, "LabCon",
275                                              &labcon_info, 0);
276     }
277   
278     return labcon_type;
279 }
280
281 GtkWidget *
282 labcon_new_label(const gchar *label, LabConAlignment align,
283                  GtkPositionType pos, guint spacing,
284                  GtkWidget *child, GtkWidget *group)
285 {
286     LabCon *l, *leader = LABCON(group);
287
288     g_return_val_if_fail(GTK_IS_WIDGET(child), NULL);
289     if (group) {
290         g_return_val_if_fail(GTK_IS_WIDGET(group), NULL);
291         g_return_val_if_fail(IS_LABCON(group), NULL);
292     }
293
294     l = g_object_new(labcon_get_type(), NULL);
295     l->pos = pos;
296     l->spacing = spacing;
297     l->child = child;
298
299     l->label = gtk_label_new(label);
300     switch (align) {
301       case LABCON_ALIGN_LEFT:
302         gtk_misc_set_alignment(GTK_MISC(l->label), 0.0, 0.5);
303         break;
304       case LABCON_ALIGN_RIGHT:
305         gtk_misc_set_alignment(GTK_MISC(l->label), 1.0, 0.5);
306         break;
307       case LABCON_ALIGN_CENTER:
308         gtk_misc_set_alignment(GTK_MISC(l->label), 0.5, 0.5);
309         break;
310     }
311
312     /*
313      * Go back until we get the group leader.
314      */
315     if (group && LABCON(group)->leader)
316       l->leader = LABCON(group)->leader;
317     else
318       l->leader = group;
319
320     if (l->leader) {
321         /*
322          * Add this widget to the group which should have all the same
323          * width labels.
324          */
325         leader = LABCON(l->leader);
326         if (leader->group_size == leader->group_used) {
327             if (leader->group_size == 0)
328               leader->group = (GtkWidget **) g_malloc(sizeof(GtkWidget *) * 4);
329             else
330               leader->group = (GtkWidget **)
331                   g_realloc(leader->group,
332                             sizeof(GtkWidget *) * (leader->group_size + 4));
333             leader->group_size += 4;
334         }
335         leader->group[leader->group_used++] = GTK_WIDGET(l);
336     }
337
338     gtk_widget_set_parent(l->label, GTK_WIDGET(l)); 
339     gtk_widget_show(l->label);
340     if (l->child) {
341         gtk_widget_set_parent(l->child, GTK_WIDGET(l)); 
342         gtk_widget_show(l->child);
343     }
344
345     return GTK_WIDGET(l);
346 }
347
348 GtkWidget *
349 labcon_new_label_defaults(const gchar *label, GtkWidget *child,
350                           GtkWidget *group)
351 {
352     return labcon_new_label(label, LABCON_ALIGN_RIGHT, GTK_POS_LEFT, 5,
353                             child, group);
354 }
355
356 GtkWidget *
357 labcon_new_pixbuf(const GdkPixbuf *pixbuf, LabConAlignment align,
358                   GtkPositionType pos, guint spacing,
359                   GtkWidget *child, GtkWidget *group)
360 {
361     LabCon *l, *leader = LABCON(group);
362
363     g_return_val_if_fail(GTK_IS_WIDGET(child), NULL);
364     if (group) {
365         g_return_val_if_fail(GTK_IS_WIDGET(group), NULL);
366         g_return_val_if_fail(IS_LABCON(group), NULL);
367     }
368
369     l = g_object_new(labcon_get_type(), NULL);
370     l->pixbuf = pixbuf;
371     l->pos = pos;
372     l->spacing = spacing;
373     l->child = child;
374     l->align = align;
375
376     /*
377      * Make the drawing area just big enough to hold the pixbuf at
378      * first.
379      */
380     l->image = gtk_drawing_area_new();
381     gtk_widget_set_size_request(l->image, 
382                                 gdk_pixbuf_get_width(l->pixbuf),
383                                 gdk_pixbuf_get_height(l->pixbuf));
384     g_signal_connect(G_OBJECT(l->image), "expose_event",
385                      G_CALLBACK(draw_pixbuf), (gpointer) l->pixbuf);
386
387     /*
388      * Go back until we get the group leader.
389      */
390     if (group && LABCON(group)->leader)
391       l->leader = LABCON(group)->leader;
392     else
393       l->leader = group;
394
395     if (l->leader) {
396         /*
397          * Add this widget to the group which should have all the same
398          * width labels or pixbufs.
399          */
400         leader = LABCON(l->leader);
401         if (leader->group_size == leader->group_used) {
402             if (leader->group_size == 0)
403               leader->group = (GtkWidget **) g_malloc(sizeof(GtkWidget *) * 4);
404             else
405               leader->group = (GtkWidget **)
406                   g_realloc(leader->group,
407                             sizeof(GtkWidget *) * (leader->group_size + 4));
408             leader->group_size += 4;
409         }
410         leader->group[leader->group_used++] = GTK_WIDGET(l);
411     }
412
413     gtk_widget_set_parent(l->image, GTK_WIDGET(l)); 
414     gtk_widget_show(l->image);
415     if (l->child) {
416         gtk_widget_set_parent(l->child, GTK_WIDGET(l)); 
417         gtk_widget_show(l->child);
418     }
419
420     return GTK_WIDGET(l);
421 }
422
423 GtkWidget *
424 labcon_new_pixbuf_defaults(const GdkPixbuf *pixbuf, GtkWidget *child,
425                            GtkWidget *group)
426 {
427     return labcon_new_pixbuf(pixbuf, LABCON_ALIGN_RIGHT, GTK_POS_LEFT, 5,
428                              child, group);
429 }
430
431 const GdkPixbuf *
432 labcon_get_pixbuf(LabCon *l)
433 {
434     g_return_val_if_fail(IS_LABCON(l), 0);
435
436     return l ? l->pixbuf : 0;
437 }
438
439 GtkWidget *
440 labcon_get_image(LabCon *l)
441 {
442     g_return_val_if_fail(IS_LABCON(l), 0);
443
444     return l ? l->image : 0;
445 }
446
447 GtkWidget *
448 labcon_get_label(LabCon *l)
449 {
450     g_return_val_if_fail(IS_LABCON(l), 0);
451
452     return l ? l->label : 0;
453 }
454
455 GtkWidget *
456 labcon_get_child(LabCon *l)
457 {
458     g_return_val_if_fail(IS_LABCON(l), 0);
459
460     return l ? l->child : 0;
461 }