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