added proposed fixes
[hildon] / hildon-widgets / hildon-grid-item.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005 Nokia Corporation.
5  *
6  * Contact: Luc Pionchon <luc.pionchon@nokia.com>
7  *
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; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
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.
17  *
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
21  * 02110-1301 USA
22  *
23  */
24
25 /*
26  * @file hildon-grid-item.c
27  *
28  * This file contains the implementation of HildonGridItem.
29  * HildonGridItem is an item mainly used in HildonGrid. It has an icon,
30  * emblem and a label. 
31  *
32  */
33
34 /*
35  * TODO:
36  * - play with libtool to get _-functions private but accesable from grid
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include <gtk/gtklabel.h>
48 #include <gtk/gtkicontheme.h>
49 #include <gtk/gtkimage.h>
50 #include <gtk/gtkmisc.h>
51 #include <gtk/gtkwidget.h>
52 #include <gtk/gtkenums.h>
53 #include <pango/pango.h>
54
55 #include "hildon-grid-item-private.h"
56 #include <hildon-widgets/hildon-grid-item.h>
57
58 #include <libintl.h>
59 #define _(String) dgettext(PACKAGE, String)
60
61 #define HILDON_GRID_ITEM_GET_PRIVATE(obj) \
62         (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_GRID_ITEM, \
63                                       HildonGridItemPrivate))
64
65 typedef struct _HildonGridItemPrivate HildonGridItemPrivate;
66
67
68 /* Default icon. */
69 #define DEFAULT_ICON_BASENAME   "qgn_list_gene_unknown_file"
70 #define HILDON_GRID_ICON_SIZE         26
71 #define HILDON_GRID_EMBLEM_SIZE       16
72
73 /* Use some alpha-thing for emblems. */
74 #define USE_DIRTY_ALPHA
75
76 struct _HildonGridItemPrivate {
77     gchar *icon_basename;
78     gint icon_size;
79     GtkWidget *icon;
80
81     gchar *emblem_basename;
82     gint emblem_size;
83
84     GtkWidget *label;   /* TODO use pango! */
85     HildonGridPositionType label_pos;
86
87     gint focus_margin;
88     gint label_height;
89     gint label_icon_margin;
90     gint column_margin;
91     gint icon_width;
92     gint icon_height;
93     gint row_height;
94
95     gint pending_icon_size;
96     gint pending_emblem_size;
97     HildonGridPositionType pending_label_pos;
98     gint pending_focus_margin;
99     gint pending_label_height;
100     gint pending_label_icon_margin;
101     gint pending_icon_width;
102     gint pending_icon_height;
103
104     gboolean selected;
105 };
106
107
108
109 /* Prototypes. */
110 static void hildon_grid_item_class_init(HildonGridItemClass * klass);
111 static void hildon_grid_item_init(HildonGridItem * item);
112 static gboolean hildon_grid_item_expose(GtkWidget * widget,
113                                         GdkEventExpose * event);
114 static void hildon_grid_item_size_request(GtkWidget * widget,
115                                           GtkRequisition * requisition);
116 static void hildon_grid_item_size_allocate(GtkWidget * widget,
117                                            GtkAllocation * allocation);
118 static void hildon_grid_item_forall(GtkContainer * container,
119                                     gboolean include_int,
120                                     GtkCallback callback,
121                                     gpointer callback_data);
122 static void hildon_grid_item_remove(GtkContainer * container,
123                                     GtkWidget * child);
124
125 static void hildon_grid_item_finalize(GObject * object);
126
127 static void update_icon(HildonGridItem * item);
128 static void set_label_justify(HildonGridItem * item);
129
130 static void hildon_grid_item_set_icon_size(HildonGridItem *item,
131                                    HildonGridItemIconSizeType icon_size);
132
133
134 static GtkContainerClass *parent_class = NULL;
135
136
137 GType
138 hildon_grid_item_get_type(void)
139 {
140     static GType grid_item_type = 0;
141
142     if (!grid_item_type) {
143         static const GTypeInfo grid_item_info = {
144             sizeof(HildonGridItemClass),
145             NULL,       /* base_init */
146             NULL,       /* base_finalize */
147             (GClassInitFunc) hildon_grid_item_class_init,
148             NULL,       /* class_finalize */
149             NULL,       /* class_data */
150             sizeof(HildonGridItem),
151             0,  /* n_preallocs */
152             (GInstanceInitFunc) hildon_grid_item_init,
153         };
154         grid_item_type = g_type_register_static(GTK_TYPE_CONTAINER,
155                                                 "HildonGridItem",
156                                                 &grid_item_info, 0);
157     }
158
159     return grid_item_type;
160 }
161
162 static void
163 hildon_grid_item_class_init(HildonGridItemClass *klass)
164 {
165     GtkWidgetClass *widget_class;
166     GtkContainerClass *container_class;
167     GObjectClass *gobject_class;
168
169     widget_class = GTK_WIDGET_CLASS(klass);
170     gobject_class = G_OBJECT_CLASS(klass);
171     container_class = GTK_CONTAINER_CLASS(klass);
172
173     parent_class = g_type_class_peek_parent(klass);
174
175     g_type_class_add_private(klass, sizeof(HildonGridItemPrivate));
176
177     gobject_class->finalize = hildon_grid_item_finalize;
178
179     widget_class->expose_event = hildon_grid_item_expose;
180     widget_class->size_request = hildon_grid_item_size_request;
181     widget_class->size_allocate = hildon_grid_item_size_allocate;
182
183     container_class->forall = hildon_grid_item_forall;
184     container_class->remove = hildon_grid_item_remove;
185 }
186
187 static void
188 hildon_grid_item_init(HildonGridItem *item)
189 {
190     HildonGridItemPrivate *priv;
191
192     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
193
194     priv->icon_basename = NULL;
195     priv->pending_icon_size = priv->icon_size = HILDON_GRID_ICON_SIZE;
196     priv->icon = NULL;
197
198     priv->emblem_basename = NULL;
199     priv->pending_emblem_size = priv->emblem_size = HILDON_GRID_EMBLEM_SIZE;
200
201     priv->label = NULL;
202     priv->pending_label_pos = priv->label_pos =
203         HILDON_GRID_ITEM_LABEL_POS_BOTTOM;
204
205     priv->selected = FALSE;
206
207     priv->pending_focus_margin = priv->focus_margin = 6;
208     priv->pending_label_height = priv->label_height = 30;
209     priv->pending_label_icon_margin = priv->label_icon_margin = 6;
210     priv->pending_icon_width = priv->icon_width = 64;
211     priv->pending_icon_height = priv->icon_height = 54;
212     priv->pending_label_height = priv->label_height = 30;
213 }
214
215 /**
216  * hildon_grid_item_new:
217  * @icon_basename:  Icon base name
218  *
219  * Creates a new #HildonGridItem.
220  *
221  * Return value: A new #HildonGridItem
222  */
223 GtkWidget *
224 hildon_grid_item_new(const gchar *icon_basename)
225 {
226     HildonGridItem *item;
227     HildonGridItemPrivate *priv;
228
229     item = g_object_new(HILDON_TYPE_GRID_ITEM, NULL);
230     GTK_WIDGET_SET_FLAGS(GTK_WIDGET(item), GTK_CAN_FOCUS);
231
232     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
233     priv->icon_basename = g_strdup(icon_basename);
234
235     priv->label = gtk_label_new("");
236     gtk_widget_set_parent(priv->label, GTK_WIDGET(item));
237     gtk_widget_set_name(priv->label, "hildon-grid-item-label");
238
239     update_icon(item);
240     set_label_justify(item);
241
242     gtk_widget_show(priv->label);
243
244     return GTK_WIDGET(item);
245 }
246
247 /**
248  * hildon_grid_item_new_with_label:
249  * @icon_basename:  Icon base name
250  * @label:          Text label for icon
251  *
252  * Creates a new #HildonGridItem.
253  *
254  * Return value: A new #HildonGridItem
255  */
256 GtkWidget *
257 hildon_grid_item_new_with_label(const gchar *icon_basename,
258                                 const gchar *label)
259 {
260     HildonGridItem *item;
261     HildonGridItemPrivate *priv;
262
263
264     item = g_object_new(HILDON_TYPE_GRID_ITEM, NULL);
265     GTK_WIDGET_SET_FLAGS(GTK_WIDGET(item), GTK_CAN_FOCUS);
266
267     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
268     priv->icon_basename = g_strdup(icon_basename);
269
270     priv->label = gtk_label_new(label != NULL ? label : "");
271     gtk_widget_set_name(priv->label, "hildon-grid-item-label");
272     gtk_widget_set_parent(priv->label, GTK_WIDGET(item));
273
274     update_icon(item);
275     set_label_justify(item);
276
277     gtk_widget_show(priv->label);
278
279     return GTK_WIDGET(item);
280 }
281
282
283 static void
284 update_icon(HildonGridItem *item)
285 {
286     GtkIconTheme *icon_theme;
287     GdkPixbuf *icon;
288     GdkPixbuf *emblem_icon;
289     HildonGridItemPrivate *priv;
290     GError *error;
291
292     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
293
294     if (priv->icon != NULL) {
295         if (GTK_WIDGET_VISIBLE(priv->icon))
296             gtk_widget_hide(priv->icon);
297         gtk_widget_unparent(priv->icon);
298     }
299
300     icon_theme = gtk_icon_theme_get_default();
301
302     /* Load icon. Fall to default it loading fails. */
303     error = NULL;
304     icon = gtk_icon_theme_load_icon(icon_theme,
305                                     priv->icon_basename,
306                                     priv->icon_size, 0, &error);
307     if (icon == NULL) {
308         g_warning("Couldn't load icon \"%s\": %s", priv->icon_basename,
309                   error->message);
310         g_error_free(error);
311
312         error = NULL;
313         icon = gtk_icon_theme_load_icon(icon_theme,
314                                         DEFAULT_ICON_BASENAME,
315                                         priv->icon_size, 0, &error);
316         if (icon == NULL) {
317             g_warning("Couldn't load default icon: %s!\n", error->message);
318             g_error_free(error);
319         }
320     }
321     priv->icon_width = gdk_pixbuf_get_width(icon);
322     priv->icon_height = gdk_pixbuf_get_height(icon);
323
324
325     /* Load and merge emblem if one is specified. */
326     if (priv->emblem_basename != NULL) {
327         error = NULL;
328         emblem_icon = gtk_icon_theme_load_icon(icon_theme,
329                                                priv->emblem_basename,
330                                                priv->emblem_size,
331                                                0, &error);
332         if (emblem_icon == NULL) {
333             g_warning("Couldn't load emblem \"%s\": %s",
334                       priv->emblem_basename, error->message);
335             g_error_free(error);
336         } else {
337             gint icon_height;
338             gint width, height, y;
339
340 #ifdef USE_DIRTY_ALPHA
341             GdkPixbuf *tmp;
342 #endif
343
344             icon_height = gdk_pixbuf_get_height(icon);
345             width = MIN(gdk_pixbuf_get_width(emblem_icon),
346                         gdk_pixbuf_get_width(icon));
347             height = MIN(gdk_pixbuf_get_height(emblem_icon), icon_height);
348             y = icon_height - height;
349 #ifndef USE_DIRTY_ALPHA
350             gdk_pixbuf_copy_area(emblem_icon, 0, 0, width, height,
351                                  icon, 0, y);
352 #else
353             /* 
354              * Using composite to copy emblem to lower left corner creates
355              * some garbage on top of emblem. This way it can be avoided.
356              */
357             tmp = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE,
358                                  8, width, icon_height);
359             gdk_pixbuf_fill(tmp, 0x00000000);
360             gdk_pixbuf_copy_area(emblem_icon, 0, 0, width, height,
361                                  tmp, 0, y);
362             gdk_pixbuf_composite(tmp, icon,
363                                  0, 0, width, icon_height,
364                                  0.0, 0.0, 1.0, 1.0,
365                                  GDK_INTERP_NEAREST, 255);
366             g_object_unref(tmp);
367 #endif /* ifndef else USE_DIRTY_ALPHA */
368             g_object_unref(emblem_icon);
369         }
370     }
371
372     priv->icon = gtk_image_new_from_pixbuf(icon);
373     g_object_unref(icon);
374
375     gtk_widget_set_parent(priv->icon, GTK_WIDGET(item));
376     gtk_widget_show(priv->icon);
377
378     gtk_widget_queue_draw(priv->icon);
379 }
380
381 void
382 _hildon_grid_item_set_label(HildonGridItem *item, const gchar *label)
383 {
384     HildonGridItemPrivate *priv;
385
386     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
387
388     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
389     if ((priv->label == NULL && label == NULL) ||
390         (priv->label != NULL && label != NULL &&
391          strcmp((char *) priv->label, (char *) label) == 0)) {
392         return;
393     }
394     gtk_label_set_label(GTK_LABEL(priv->label), label);
395 }
396
397 static void
398 hildon_grid_item_set_icon_size(HildonGridItem             *item,
399                                HildonGridItemIconSizeType icon_size)
400 {
401     HildonGridItemPrivate *priv;
402
403     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
404
405     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
406     if (priv->pending_icon_size == icon_size) {
407         return;
408     }
409     priv->pending_icon_size = icon_size;
410 }
411
412
413 void
414 _hildon_grid_item_set_label_pos(HildonGridItem          *item,
415                                 HildonGridPositionType  label_pos)
416 {
417     HildonGridItemPrivate *priv;
418
419     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
420
421     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
422     if (priv->pending_label_pos == label_pos) {
423         return;
424     }
425     priv->pending_label_pos = label_pos;
426 }
427
428
429 void
430 _hildon_grid_item_set_emblem_size(HildonGridItem *item, gint emblem_size)
431 {
432     HildonGridItemPrivate *priv;
433
434     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
435
436     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
437     if (priv->pending_emblem_size == emblem_size) {
438         return;
439     }
440     priv->pending_emblem_size = emblem_size;
441 }
442
443
444 void
445 _hildon_grid_item_set_focus_margin(HildonGridItem *item,
446                                    const gint focus_margin)
447 {
448     HildonGridItemPrivate *priv;
449
450     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
451
452     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
453     if (priv->pending_focus_margin == focus_margin) {
454         return;
455     }
456     priv->pending_focus_margin = focus_margin;
457 }
458
459
460 void
461 _hildon_grid_item_set_label_height(HildonGridItem *item,
462                                    const gint label_height)
463 {
464     HildonGridItemPrivate *priv;
465
466     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
467
468     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
469     if (priv->pending_label_height == label_height) {
470         return;
471     }
472     priv->pending_label_height = label_height;
473 }
474
475
476 void
477 _hildon_grid_item_set_label_icon_margin(HildonGridItem *item,
478                                         const gint label_icon_margin)
479 {
480     HildonGridItemPrivate *priv;
481
482     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
483
484     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
485     if (priv->pending_label_icon_margin == label_icon_margin) {
486         return;
487     }
488     priv->pending_label_icon_margin = label_icon_margin;
489 }
490
491
492 void
493 _hildon_grid_item_set_icon_height(HildonGridItem *item,
494                                   const gint icon_height)
495 {
496     HildonGridItemPrivate *priv;
497
498     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
499
500     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
501     if (priv->pending_icon_height == icon_height) {
502         return;
503     }
504     priv->pending_icon_height = icon_height;
505 }
506
507
508 void
509 _hildon_grid_item_set_icon_width(HildonGridItem *item,
510                                  const gint icon_width)
511 {
512     HildonGridItemPrivate *priv;
513
514     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
515
516     hildon_grid_item_set_icon_size(item, icon_width);
517
518     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
519     if (priv->pending_icon_width == icon_width) {
520         return;
521     }
522     priv->pending_icon_width = icon_width;
523 }
524
525
526 static void
527 set_label_justify(HildonGridItem *item)
528 {
529     HildonGridItemPrivate *priv;
530
531     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
532
533     if (priv->label != NULL) {
534         switch (priv->label_pos) {
535         case HILDON_GRID_ITEM_LABEL_POS_BOTTOM:
536             gtk_misc_set_alignment(GTK_MISC(priv->label), 0.5, 0.5);
537             break;
538
539         case HILDON_GRID_ITEM_LABEL_POS_RIGHT:
540             gtk_misc_set_alignment(GTK_MISC(priv->label), 0.0, 0.5);
541             break;
542
543         default:
544             g_warning("Invalid position!");
545             break;
546         }
547     }
548 }
549
550 static void
551 hildon_grid_item_remove(GtkContainer *container, GtkWidget *child)
552 {
553     HildonGridItem *item;
554     HildonGridItemPrivate *priv;
555
556     item = HILDON_GRID_ITEM(container);
557     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
558
559     g_return_if_fail(GTK_IS_WIDGET(child));
560     g_return_if_fail(child == priv->label || child == priv->icon);
561
562     if (child == priv->label) {
563         gtk_widget_unparent(child);
564         priv->label = NULL;
565     } else if (child == priv->icon) {
566         gtk_widget_unparent(child);
567         priv->icon = NULL;
568     }
569 }
570
571 static gboolean
572 hildon_grid_item_expose(GtkWidget *widget, GdkEventExpose *event)
573 {
574     HildonGridItem *item;
575     HildonGridItemPrivate *priv;
576
577     g_return_val_if_fail(widget, FALSE);
578     g_return_val_if_fail(HILDON_IS_GRID_ITEM(widget), FALSE);
579     g_return_val_if_fail(event, FALSE);
580
581     item = HILDON_GRID_ITEM(widget);
582     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
583
584     if (priv->label == NULL && priv->icon == NULL) {
585         return FALSE;
586     }
587     if (GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(item))) {
588         GdkRectangle clip;
589         GtkWidget *focused;
590         
591         if (priv->label != NULL) {
592             focused = priv->label;
593         } else {
594             focused = priv->icon;
595         }
596
597         switch (priv->label_pos) {
598         case HILDON_GRID_ITEM_LABEL_POS_BOTTOM:
599             clip.x = focused->allocation.x - priv->focus_margin;
600             clip.y = focused->allocation.y;
601             clip.width = focused->allocation.width + priv->focus_margin * 2;
602             clip.height = focused->allocation.height;
603             if (clip.x < widget->allocation.x ||
604                 clip.width > widget->allocation.width) {
605                 clip.x = widget->allocation.x;
606                 clip.width = widget->allocation.width;
607             }
608             if (clip.y + clip.height >
609                 widget->allocation.y + widget->allocation.height) {
610                 clip.height = widget->allocation.y +
611                     widget->allocation.height - clip.y;
612             }
613             break;
614
615         case HILDON_GRID_ITEM_LABEL_POS_RIGHT:
616             clip.x = widget->allocation.x;
617             clip.y = widget->allocation.y;
618             clip.width = widget->allocation.width;
619             clip.height = widget->allocation.height;
620             break;
621         }
622
623         gtk_paint_box(focused->style,
624                       gtk_widget_get_toplevel(focused)->window,
625                       GTK_STATE_SELECTED,
626                       GTK_SHADOW_NONE,
627                       &clip, focused, "selected",
628                       clip.x, clip.y, clip.width, clip.height);
629     }
630
631     /* 
632      * Items are not exposed unless they are visible.
633      * -> No need to "optimize" by checking if they need exposing.
634      */
635     gtk_container_propagate_expose(GTK_CONTAINER(widget),
636                                    priv->icon, event);
637     gtk_container_propagate_expose(GTK_CONTAINER(widget),
638                                    priv->label, event);
639     return TRUE;
640 }
641
642
643 static void
644 hildon_grid_item_size_request(GtkWidget *widget, GtkRequisition *requisition)
645 {
646     HildonGridItem *item;
647     HildonGridItemPrivate *priv;
648     GtkRequisition label_req;
649     gint label_margin;
650
651     item = HILDON_GRID_ITEM(widget);
652     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
653
654     label_margin = priv->focus_margin;
655
656     gtk_widget_size_request(priv->icon, requisition);
657     gtk_widget_size_request(priv->label, &label_req);
658
659     switch (priv->label_pos) {
660     case HILDON_GRID_ITEM_LABEL_POS_BOTTOM:
661         requisition->width = MAX(requisition->width, label_req.width);
662         requisition->height += label_req.height + label_margin;
663         break;
664
665     case HILDON_GRID_ITEM_LABEL_POS_RIGHT:
666         requisition->width += label_req.width + label_margin;
667         requisition->height = MAX(requisition->height, label_req.height);
668         break;
669     default:
670         g_warning("bad position");
671         return;
672         break;
673     }
674 }
675
676 static void
677 hildon_grid_item_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
678 {
679     HildonGridItem *item;
680     HildonGridItemPrivate *priv;
681     GtkRequisition l_req;
682     GtkAllocation i_alloc, l_alloc;
683
684     g_return_if_fail(widget);
685     g_return_if_fail(allocation);
686
687     item = HILDON_GRID_ITEM(widget);
688     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
689     widget->allocation = *allocation;
690
691     /* If creating label and icon failed, don't show a thing... */
692     if (priv->label == NULL && priv->icon == NULL) {
693         return;
694     }
695     if (priv->label != NULL) {
696         gtk_widget_get_child_requisition(priv->label, &l_req);
697     } else {
698         l_req.width = l_req.height = 0;
699     }
700
701     switch (priv->label_pos) {
702     case HILDON_GRID_ITEM_LABEL_POS_BOTTOM:
703         i_alloc.x = (allocation->width - priv->icon_width) / 2 +
704             allocation->x;
705         if (priv->label != NULL) {
706             i_alloc.y = allocation->y + (allocation->height -
707                                          priv->label_height -
708                                          priv->label_icon_margin -
709                                          priv->icon_height) / 2;
710         } else {
711             i_alloc.y = (allocation->height - priv->icon_height) / 2 +
712                 allocation->y;
713         }
714
715         if (priv->label != NULL) {
716             l_alloc.x = allocation->x + priv->focus_margin;
717             l_alloc.y = i_alloc.y + priv->icon_height +
718                 priv->label_icon_margin;
719             l_alloc.width = allocation->width - priv->focus_margin * 2;
720             l_alloc.height = priv->label_height;
721         }
722         break;
723
724     case HILDON_GRID_ITEM_LABEL_POS_RIGHT:
725         i_alloc.x = allocation->x + priv->focus_margin;
726         i_alloc.y = allocation->y +
727           (priv->label_height - priv->icon_height) / 2;
728
729         if (priv->label != NULL) {
730             l_alloc.x = allocation->x + priv->focus_margin +
731                 priv->icon_width + priv->label_icon_margin;
732             l_alloc.y = allocation->y;
733             l_alloc.width = allocation->width - priv->focus_margin * 2 -
734                 priv->label_icon_margin - priv->icon_width;
735             l_alloc.height = priv->label_height;
736         }
737         break;
738     default:
739         g_warning("bad label position");
740         return;
741         break;
742     }
743
744     if (i_alloc.y < allocation->y) {
745         i_alloc.height -= i_alloc.height - allocation->height;
746         i_alloc.y = allocation->y;
747     }
748     if (i_alloc.y + i_alloc.height > allocation->y + allocation->height) {
749         i_alloc.height-= i_alloc.y + i_alloc.height -
750             allocation->y - allocation->height;
751     }
752       
753
754     i_alloc.width = priv->icon_width;
755     i_alloc.height = priv->icon_height;
756
757     if (priv->label != NULL) {
758         gtk_widget_size_allocate(priv->label, &l_alloc);
759     }
760     if (priv->icon != NULL) {
761         gtk_widget_size_allocate(priv->icon, &i_alloc);
762     }
763 }
764
765 static void
766 hildon_grid_item_forall(GtkContainer    *container,
767                         gboolean        include_int,
768                         GtkCallback     callback,
769                         gpointer        callback_data)
770 {
771     HildonGridItem *item;
772     HildonGridItemPrivate *priv;
773
774     g_return_if_fail(container);
775     g_return_if_fail(callback);
776
777     item = HILDON_GRID_ITEM(container);
778     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
779
780     if (priv->icon != NULL) {
781         (*callback) (priv->icon, callback_data);
782     }
783     if (priv->label != NULL) {
784         (*callback) (priv->label, callback_data);
785     }
786 }
787
788 static void
789 hildon_grid_item_finalize(GObject *object)
790 {
791     HildonGridItem *item;
792     HildonGridItemPrivate *priv;
793
794     item = HILDON_GRID_ITEM(object);
795     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
796
797     g_free(priv->icon_basename);
798     if (priv->emblem_basename != NULL) {
799         g_free(priv->emblem_basename);
800     }
801
802     G_OBJECT_CLASS(parent_class)->finalize(object);
803 }
804
805 #if 0
806 static int hildon_time_get_font_width(GtkWidget * widget)
807 {
808     PangoContext *context;
809     PangoFontMetrics *metrics;
810     gint digit_width;
811
812     context = gtk_widget_get_pango_context(widget);
813     metrics = pango_context_get_metrics(context,
814                                         widget->style->font_desc,
815                                         pango_context_get_language
816                                         (context));
817
818     digit_width = pango_font_metrics_get_approximate_digit_width(metrics);
819     digit_width = PANGO_PIXELS(digit_width);
820
821     pango_font_metrics_unref(metrics);
822
823     return digit_width;
824 }
825 #endif
826
827
828 /**
829  * hildon_grid_item_set_emblem_type:
830  * @item:               #HildonGridItem
831  * @emblem_basename:    Emblem's basename
832  *
833  * Sets item emblem type.
834  */
835 void
836 hildon_grid_item_set_emblem_type(HildonGridItem *item,
837                                  const gchar *emblem_basename)
838 {
839     HildonGridItemPrivate *priv;
840
841     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
842
843     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
844
845     if (priv->emblem_basename != NULL) {
846         g_free(priv->emblem_basename);
847     }
848
849     priv->emblem_basename = g_strdup(emblem_basename);
850
851     update_icon(item);
852 }
853
854 /**
855  * hildon_grid_item_get_emblem_type:
856  * @item:   #HildonGridItem
857  *
858  * Returns emblem's basename. Must not be changed or freed.
859  *
860  * Return value: Emblem's basename
861  */
862 const gchar *
863 hildon_grid_item_get_emblem_type(HildonGridItem *item)
864 {
865     g_return_val_if_fail(HILDON_IS_GRID_ITEM(item), NULL);
866
867     return HILDON_GRID_ITEM_GET_PRIVATE(item)->emblem_basename;
868 }
869
870
871
872 void
873 _hildon_grid_item_done_updating_settings(HildonGridItem *item)
874 {
875     gboolean need_update_icon;
876     gboolean need_resize;
877
878     HildonGridItemPrivate *priv;
879     g_return_if_fail(HILDON_IS_GRID_ITEM(item));
880     priv = HILDON_GRID_ITEM_GET_PRIVATE(item);
881
882     need_update_icon = need_resize = FALSE;
883     
884     if (priv->pending_icon_size != priv->icon_size) {
885         if (priv->pending_icon_size > 0) {
886             priv->icon_size = priv->pending_icon_size;
887         } else {
888             priv->icon_size = 1;
889         }
890         need_update_icon = TRUE;
891     }
892     if (priv->pending_emblem_size != priv->emblem_size) {
893         priv->emblem_size = priv->pending_emblem_size;
894         need_update_icon = TRUE;
895     }
896     if (priv->pending_label_pos != priv->label_pos) {
897         priv->label_pos = priv->pending_label_pos;
898         /* No refresh here, grid will do it. */
899         set_label_justify(item);
900     }
901     /*
902      * grid will take care of this
903      *
904     if (priv->pending_focus_margin != priv->focus_margin) {
905         priv->focus_margin = priv->pending_focus_margin;
906         need_resize = TRUE;
907     }
908     if (priv->pending_label_height != priv->label_height) {
909         priv->label_height = priv->pending_label_height;
910         need_resize = TRUE;
911     }
912     if (priv->pending_label_icon_margin != priv->label_icon_margin) {
913         priv->label_icon_margin = priv->pending_label_icon_margin;
914         need_resize = TRUE;
915     }
916     if (priv->pending_icon_height != priv->icon_height) {
917         priv->icon_height = priv->pending_icon_height;
918         need_resize = TRUE;
919     }
920     if (priv->pending_icon_width != priv->icon_width) {
921         priv->icon_width = priv->pending_icon_width;
922         need_resize = TRUE;
923     }
924      */
925
926     if (need_update_icon == TRUE) {
927         update_icon(HILDON_GRID_ITEM(item));
928     }
929     /*
930     if (need_resize == TRUE) {
931         gtk_widget_queue_resize(GTK_WIDGET(item));
932     }
933     */
934 }