* hildon-widgets/hildon-calendar-popup.c * hildon-widgets/hildon-color-button.c ...
[hildon] / hildon-widgets / hildon-grid.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.c
27  *
28  * This file contains the implementation of HildonGrid. HildonGrid is used
29  * in views like Home and Control Panel which have single-tap activated
30  * items.
31  */
32
33 /*
34  * TODO
35  * - there must be a predefined place for the "no items" -label...
36  * - performance :-)
37  * - dimmed items & scrolling by scrollbar
38  */
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/gtkrange.h>
51 #include <gtk/gtkvscrollbar.h>
52 #include <gtk/gtkmain.h>
53 #include <gtk/gtkwidget.h>
54 #include <gtk/gtkenums.h>
55 #include <gdk/gdkkeysyms.h>
56
57 #include "hildon-grid-item-private.h"
58 #include "hildon-marshalers.h"
59 #include <hildon-widgets/hildon-grid.h>
60 #include <hildon-widgets/hildon-grid-item.h>
61
62 #include <libintl.h>
63 #define _(String) dgettext(PACKAGE, String)
64
65 #define HILDON_GRID_GET_PRIVATE(obj) \
66         (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_GRID, \
67                                       HildonGridPrivate))
68
69
70 #define DEFAULT_STYLE   "largeicons-home"
71
72 #define DEFAULT_N_COLUMNS       3
73 #define GRID_LABEL_POS_PAD     16
74
75 #define DRAG_SENSITIVITY        6
76
77
78 enum {
79     ACTIVATE_CHILD,
80     POPUP_CONTEXT,
81     LAST_SIGNAL
82 };
83
84 enum {
85     PROP_0,
86     PROP_EMPTY_LABEL,
87     PROP_STYLE,
88     PROP_SCROLLBAR_POS
89 };
90
91
92 typedef struct _HildonGridChild HildonGridChild;
93 typedef struct _HildonGridPrivate HildonGridPrivate;
94
95
96 struct _HildonGridChild {
97     GtkWidget *widget;
98 };
99
100
101 struct _HildonGridPrivate {
102     GList *children;
103     GtkWidget *scrollbar;
104     gint old_sb_pos;
105     GdkWindow *event_window;
106
107     gchar *style;
108     gint emblem_size;
109     GtkWidget *empty_label;
110
111     gint item_width;
112     gint item_height;
113     gint h_margin;
114     gint v_margin;
115     gint focus_margin;
116     gint icon_label_margin;
117     gint icon_width;
118     gint num_columns;
119     HildonGridPositionType label_pos;
120     gint label_height;
121
122     gint focus_index;
123     guint click_x;
124     guint click_y;
125
126     /* Handy variables outsize _allocate. */
127     gint area_height;
128     gint area_rows;
129     gint scrollbar_width;
130
131     gint first_index;
132     GdkEventType last_button_event;
133     gint old_item_height;
134 };
135
136
137
138 /* Prototypes. */
139 static void hildon_grid_class_init(HildonGridClass * klass);
140 static void hildon_grid_init(HildonGrid * grid);
141 static void hildon_grid_realize(GtkWidget * widget);
142 static void hildon_grid_unrealize(GtkWidget * widget);
143 static void hildon_grid_map(GtkWidget * widget);
144 static void hildon_grid_unmap(GtkWidget * widget);
145 static gboolean hildon_grid_expose(GtkWidget * widget,
146                                    GdkEventExpose * event);
147 static void hildon_grid_size_request(GtkWidget * widget,
148                                      GtkRequisition * requisition);
149 static void hildon_grid_size_allocate(GtkWidget * widget,
150                                       GtkAllocation * allocation);
151 static void hildon_grid_add(GtkContainer * container, GtkWidget * widget);
152 static void hildon_grid_remove(GtkContainer * container,
153                                GtkWidget * widget);
154 static void hildon_grid_set_focus_child(GtkContainer * container,
155                                         GtkWidget * widget);
156 static void hildon_grid_forall(GtkContainer * container,
157                                gboolean include_internals,
158                                GtkCallback callback,
159                                gpointer callback_data);
160 static void hildon_grid_tap_and_hold_setup(GtkWidget * widget,
161                                            GtkWidget * menu,
162                                            GtkCallback func,
163                                            GtkWidgetTapAndHoldFlags flags);
164
165 static GType hildon_grid_child_type(GtkContainer * container);
166
167
168 static void hildon_grid_set_property(GObject * object,
169                                      guint prop_id,
170                                      const GValue * value,
171                                      GParamSpec * pspec);
172 static void hildon_grid_get_property(GObject * object,
173                                      guint prop_id,
174                                      GValue * value, GParamSpec * pspec);
175
176 static void hildon_grid_set_empty_label(HildonGrid *grid,
177                                         const gchar *empty_label);
178 static const gchar *hildon_grid_get_empty_label(HildonGrid * grid);
179 static void hildon_grid_set_num_columns(HildonGrid *grid, gint num_cols);
180 static void hildon_grid_set_label_pos(HildonGrid *grid,
181                                       HildonGridPositionType label_pos);
182 static void hildon_grid_set_focus_margin(HildonGrid *grid,
183                                          gint focus_margin);
184 static void hildon_grid_set_icon_label_margin(HildonGrid *grid,
185                                               gint icon_label_margin);
186 static void hildon_grid_set_icon_width(HildonGrid *grid, gint icon_width);
187 static void hildon_grid_set_emblem_size(HildonGrid *grid, gint emblem_size);
188 static void hildon_grid_set_label_height(HildonGrid *grid,
189                                          gint label_height);
190 static void hildon_grid_destroy(GtkObject * self);
191 static void hildon_grid_finalize(GObject * object);
192
193 /* Signal handlers. */
194 static gboolean hildon_grid_button_pressed(GtkWidget * widget,
195                                            GdkEventButton * event);
196 static gboolean hildon_grid_button_released(GtkWidget * widget,
197                                             GdkEventButton * event);
198 static gboolean hildon_grid_key_pressed(GtkWidget * widget,
199                                         GdkEventKey * event);
200 static gboolean hildon_grid_scrollbar_moved(GtkWidget * widget,
201                                             gpointer data);
202 static gboolean hildon_grid_state_changed(GtkWidget * widget,
203                                           GtkStateType state,
204                                           gpointer data);
205
206 /* Other internal functions. */
207 static void get_style_properties(HildonGrid * grid);
208 static gint get_child_index(HildonGridPrivate * priv, GtkWidget * child);
209 static gint get_child_index_by_coord(HildonGridPrivate * priv,
210                                      gint x, gint y);
211 static GtkWidget *get_child_by_index(HildonGridPrivate * priv, gint index);
212
213 static gboolean jump_scrollbar_to_focused(HildonGrid * grid);
214 static gboolean adjust_scrollbar_height(HildonGrid * grid);
215 static gboolean update_contents(HildonGrid * grid);
216 static void set_focus(HildonGrid * grid,
217                       GtkWidget * widget, gboolean refresh_view);
218
219 static GtkContainerClass *parent_class = NULL;
220 static guint grid_signals[LAST_SIGNAL] = { 0 };
221
222
223 GType hildon_grid_get_type(void)
224 {
225     static GType grid_type = 0;
226
227     if (!grid_type) {
228         static const GTypeInfo grid_info = {
229             sizeof(HildonGridClass),
230             NULL,       /* base_init */
231             NULL,       /* base_finalize */
232             (GClassInitFunc) hildon_grid_class_init,
233             NULL,       /* class_finalize */
234             NULL,       /* class_data */
235             sizeof(HildonGrid),
236             0,  /* n_preallocs */
237             (GInstanceInitFunc) hildon_grid_init,
238         };
239         grid_type = g_type_register_static(GTK_TYPE_CONTAINER,
240                                            "HildonGrid", &grid_info, 0);
241     }
242
243     return grid_type;
244 }
245
246
247
248 static void hildon_grid_class_init(HildonGridClass * klass)
249 {
250     GObjectClass *gobject_class;
251     GtkWidgetClass *widget_class;
252     GtkContainerClass *container_class;
253
254     widget_class = GTK_WIDGET_CLASS(klass);
255     container_class = GTK_CONTAINER_CLASS(klass);
256     gobject_class = G_OBJECT_CLASS(klass);
257
258     parent_class = g_type_class_peek_parent(klass);
259
260     g_type_class_add_private(klass, sizeof(HildonGridPrivate));
261
262     GTK_OBJECT_CLASS(klass)->destroy = hildon_grid_destroy;
263     gobject_class->finalize = hildon_grid_finalize;
264     gobject_class->set_property = hildon_grid_set_property;
265     gobject_class->get_property = hildon_grid_get_property;
266
267     widget_class->realize = hildon_grid_realize;
268     widget_class->unrealize = hildon_grid_unrealize;
269     widget_class->map = hildon_grid_map;
270     widget_class->unmap = hildon_grid_unmap;
271     widget_class->expose_event = hildon_grid_expose;
272     widget_class->size_request = hildon_grid_size_request;
273     widget_class->size_allocate = hildon_grid_size_allocate;
274     widget_class->tap_and_hold_setup = hildon_grid_tap_and_hold_setup;
275     widget_class->key_press_event = hildon_grid_key_pressed;
276     widget_class->button_press_event = hildon_grid_button_pressed;
277     widget_class->button_release_event = hildon_grid_button_released;
278
279     container_class->add = hildon_grid_add;
280     container_class->remove = hildon_grid_remove;
281     container_class->forall = hildon_grid_forall;
282     container_class->child_type = hildon_grid_child_type;
283     container_class->set_focus_child = hildon_grid_set_focus_child;
284
285     /* Install properties to the class */
286     g_object_class_install_property(gobject_class, PROP_EMPTY_LABEL,
287         g_param_spec_string("empty_label",
288                             "Empty label",
289                             "Label to show when grid has no items",
290                             _("Ckct_wi_grid_no_items"), G_PARAM_READWRITE));
291
292     g_object_class_install_property(gobject_class, PROP_STYLE,
293         g_param_spec_string("style",
294                             "Style",
295                             "Widget's Style. Setting style sets widget size, "
296                             "spacing, label position, number of columns, "
297                             "and icon sizeLabel to show when grid has no items",
298                             DEFAULT_STYLE, G_PARAM_READWRITE));
299
300     g_object_class_install_property(gobject_class, PROP_SCROLLBAR_POS,
301         g_param_spec_int("scrollbar-position",
302                          "Scrollbar Position",
303                          "View (scrollbar) position.",
304                          G_MININT, G_MAXINT, 0, G_PARAM_READWRITE));
305
306     gtk_widget_class_install_style_property(widget_class,
307         g_param_spec_uint("item_width",
308                           "Item width",
309                           "Total width of an item (obsolete)",
310                           1, G_MAXINT, 212, G_PARAM_READABLE));
311
312     gtk_widget_class_install_style_property(widget_class,
313         g_param_spec_uint("item_height",
314                           "Item height",
315                           "Total height of an item",
316                           1, G_MAXINT, 96, G_PARAM_READABLE));
317
318     gtk_widget_class_install_style_property(widget_class,
319         g_param_spec_uint("item_hspacing",
320                           "Item horizontal spacing",
321                           "Margin between two columns and labels",
322                           0, G_MAXINT, 12, G_PARAM_READABLE));
323
324     gtk_widget_class_install_style_property(widget_class,
325         g_param_spec_uint("item_vspacing",
326                           "Item vertical spacing",
327                           "Icon on right: Margin between rows / Icon at bottom: Vertical margin betweeb label and icon",
328                           0, G_MAXINT, 6, G_PARAM_READABLE));
329
330     gtk_widget_class_install_style_property(widget_class,
331         g_param_spec_uint("label_hspacing",
332                           "Focus margin",
333                           "Margin between focus edge and item edge",
334                           0, G_MAXINT, 6, G_PARAM_READABLE));
335
336     gtk_widget_class_install_style_property(widget_class,
337         g_param_spec_uint("label_vspacing",
338                           "Vertical label spacing",
339                           "Vertical margin between item and label",
340                           0, G_MAXINT, 6, G_PARAM_READABLE));
341
342     gtk_widget_class_install_style_property(widget_class,
343         g_param_spec_uint("label_height",
344                           "Label height",
345                           "Height of icon label",
346                           1, G_MAXINT, 30, G_PARAM_READABLE));
347
348     gtk_widget_class_install_style_property(widget_class,
349         g_param_spec_uint("n_columns",
350                           "Columns",
351                           "Number of columns",
352                           0, G_MAXINT, DEFAULT_N_COLUMNS, G_PARAM_READABLE));
353
354     gtk_widget_class_install_style_property(widget_class,
355         g_param_spec_uint("label_pos",
356                           "Label position",
357                           "Position of label related to the icon",
358                           1, 2, 1, G_PARAM_READABLE));
359
360     gtk_widget_class_install_style_property(widget_class,
361         g_param_spec_uint("icon_size",
362                           "Icon size",
363                           "Size of the icon in pixels (width)",
364                           1, G_MAXINT, 64, G_PARAM_READABLE));
365
366     gtk_widget_class_install_style_property(widget_class,
367         g_param_spec_uint("emblem_size",
368                           "Emblem size",
369                           "Size of the emblem in pixels",
370                           1, G_MAXINT, 25, G_PARAM_READABLE));
371
372     /**
373      * HildonGrid::activate-child:
374      *
375      * Emitted when a child (@HildonGridItem) is activated either by
376      * tapping on it or by pressing enter.
377      */
378     grid_signals[ACTIVATE_CHILD] =
379         g_signal_new("activate-child",
380                      G_OBJECT_CLASS_TYPE(gobject_class),
381                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
382                      G_STRUCT_OFFSET(HildonGridClass, activate_child),
383                      NULL, NULL,
384                      g_cclosure_marshal_VOID__OBJECT,
385                      G_TYPE_NONE, 1, HILDON_TYPE_GRID_ITEM);
386
387     /**
388      * HildonGrid::popup-context-menu:
389      *
390      * Emitted when popup-menu is supposed to open. Used for tap-and-hold.
391      */
392     grid_signals[POPUP_CONTEXT] =
393         g_signal_new("popup-context-menu",
394                      G_OBJECT_CLASS_TYPE(gobject_class),
395                      G_SIGNAL_RUN_LAST,
396                      G_STRUCT_OFFSET(HildonGridClass, popup_context_menu),
397                      g_signal_accumulator_true_handled, NULL,
398                      _hildon_marshal_BOOLEAN__INT_INT_INT,
399                      G_TYPE_BOOLEAN, 3,
400                      G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
401 }
402
403
404
405 /*
406  * hildon_grid_set_empty_label:
407  * @grid:           #HildonGrid
408  * @empty_label:    New label
409  *
410  * Sets empty label.
411  */
412 static void
413 hildon_grid_set_empty_label(HildonGrid * grid, const gchar * empty_label)
414 {
415     /* No need to worry about update -- label receives a signal for it. */
416     gtk_label_set_label(GTK_LABEL(HILDON_GRID_GET_PRIVATE
417                                   (grid)->empty_label),
418                         empty_label == NULL ? "" : empty_label);
419 }
420
421 /*
422  * _hildon_grid_get_empty_label:
423  * @grid:   #HildonGrid
424  *
425  * Returns empty label. Label must not be modified nor freed.
426  *
427  * Return value: Label
428  */
429 static const gchar *
430 hildon_grid_get_empty_label(HildonGrid * grid)
431 {
432     return gtk_label_get_label(GTK_LABEL(HILDON_GRID_GET_PRIVATE
433                                          (grid)->empty_label));
434 }
435
436 /*
437  * hildon_grid_set_num_columns:
438  * @grid:       #HildonGrid
439  * @columsn:    Number of columns
440  *
441  * Sets number of columns.
442  */
443 static void
444 hildon_grid_set_num_columns(HildonGrid * grid, gint columns)
445 {
446     HildonGridPrivate *priv;
447
448     g_return_if_fail(HILDON_IS_GRID(grid));
449     priv = HILDON_GRID_GET_PRIVATE(grid);
450
451     if (priv->num_columns == columns) {
452         return;
453     }
454
455     if (columns != 0)
456         priv->num_columns = columns;
457     else
458         priv->num_columns = DEFAULT_N_COLUMNS;
459     
460     /* Update estimated row-count for jump_scrollbar... */
461     priv->area_rows = MAX(priv->area_height / priv->num_columns, 1);
462
463     /* Size could have changed. Scroll view so there's something to show. */
464     adjust_scrollbar_height(grid);
465     jump_scrollbar_to_focused(grid);
466     gtk_widget_queue_resize(GTK_WIDGET(grid));
467 }
468
469 /*
470  * hildon_grid_set_label_pos:
471  * @grid:       #HildonGrid
472  * @label_pos:  Label position
473  *
474  * Sets icon label position.
475  */
476 static void
477 hildon_grid_set_label_pos(HildonGrid * grid,
478                           HildonGridPositionType label_pos)
479 {
480     HildonGridPrivate *priv;
481     GList *list;
482     GtkWidget *child;
483
484     priv = HILDON_GRID_GET_PRIVATE(grid);
485
486     if (label_pos == priv->label_pos)
487         return;
488
489     /* gtknotebook doesn't check if we use valid values -- why should
490        we?-) */
491
492     priv->label_pos = label_pos;
493
494     /* Set label position to each HildonGridItem */
495     for (list = priv->children; list != NULL; list = list->next) {
496         child = ((HildonGridChild *) list->data)->widget;
497
498         _hildon_grid_item_set_label_pos(HILDON_GRID_ITEM(child),
499                                         label_pos);
500     }
501 }
502
503 /*
504  * hildon_grid_set_focus_margin:
505  * @grid:         #HildonGrid
506  * @focus_margin: Focus margin
507  *
508  * Sets margin between icon edge and label edge
509  */
510 static void
511 hildon_grid_set_focus_margin(HildonGrid *grid, gint focus_margin)
512 {
513     HildonGridPrivate *priv;
514     GList *list;
515     GtkWidget *child;
516
517     priv = HILDON_GRID_GET_PRIVATE(grid);
518     if (focus_margin == priv->focus_margin)
519         return;
520
521     priv->focus_margin = focus_margin;
522
523     /* Update children. */
524     for (list = priv->children; list != NULL; list = list->next) {
525         child = ((HildonGridChild *) list->data)->widget;
526
527         _hildon_grid_item_set_focus_margin(HILDON_GRID_ITEM(child),
528                                            priv->focus_margin);
529     }
530 }
531
532
533 /*
534  * hildon_grid_set_icon_label_margin:
535  * @grid:       #HildonGrid
536  * @hspacing:   Vertical spacing
537  *
538  * Sets vertical spacing for label.
539  * XXX
540  */
541 static void
542 hildon_grid_set_icon_label_margin(HildonGrid *grid, gint icon_label_margin)
543 {
544     HildonGridPrivate *priv;
545
546     priv = HILDON_GRID_GET_PRIVATE(grid);
547     if (icon_label_margin == priv->icon_label_margin)
548         return;
549
550     priv->icon_label_margin = icon_label_margin;
551 }
552
553
554 /*
555  * hildon_grid_set_icon_width:
556  * @grid:       #HildonGrid
557  * @icon_size:  Icon size (width)
558  *
559  * Sets icon size (in pixels).
560  */
561 static void
562 hildon_grid_set_icon_width(HildonGrid * grid, gint icon_width)
563 {
564     HildonGridPrivate *priv;
565     GList *list;
566     GtkWidget *child;
567
568     priv = HILDON_GRID_GET_PRIVATE(grid);
569
570     if (icon_width == priv->icon_width)
571         return;
572
573     priv->icon_width = icon_width;
574
575     for (list = priv->children; list != NULL; list = list->next) {
576         child = ((HildonGridChild *) list->data)->widget;
577
578         _hildon_grid_item_set_icon_width(HILDON_GRID_ITEM(child),
579                                          icon_width);
580     }
581 }
582
583
584 /*
585  * hildon_grid_set_emblem_size:
586  * @grid:           #HildonGrid
587  * @emblem_size:    Emblem size
588  *
589  * Sets emblem size (in pixels).
590  */
591 static void
592 hildon_grid_set_emblem_size(HildonGrid *grid, gint emblem_size)
593 {
594     HildonGridPrivate *priv;
595     GList *list;
596     GtkWidget *child;
597
598     priv = HILDON_GRID_GET_PRIVATE(grid);
599
600     if (emblem_size == priv->emblem_size)
601         return;
602
603     priv->emblem_size = emblem_size;
604
605     for (list = priv->children; list != NULL; list = list->next) {
606         child = ((HildonGridChild *) list->data)->widget;
607
608         _hildon_grid_item_set_emblem_size(HILDON_GRID_ITEM(child),
609                                           emblem_size);
610     }
611 }
612
613
614 static void
615 hildon_grid_set_label_height(HildonGrid *grid,
616                              gint label_height)
617 {
618     HildonGridPrivate *priv;
619     GList *list;
620     GtkWidget *child;
621
622     priv = HILDON_GRID_GET_PRIVATE(grid);
623
624     if (label_height == priv->label_height)
625         return;
626
627     priv->label_height = label_height;
628
629     for (list = priv->children; list != NULL; list = list->next) {
630         child = ((HildonGridChild *) list->data)->widget;
631
632         _hildon_grid_item_set_label_height(HILDON_GRID_ITEM(child),
633                                            label_height);
634     }
635 }
636
637
638 static GType hildon_grid_child_type(GtkContainer * container)
639 {
640     return GTK_TYPE_WIDGET;
641 }
642
643 static void hildon_grid_init(HildonGrid * grid)
644 {
645     HildonGridPrivate *priv;
646
647     priv = HILDON_GRID_GET_PRIVATE(grid);
648
649     GTK_CONTAINER(grid)->focus_child = NULL;
650     priv->focus_index = -1;
651
652     priv->scrollbar = gtk_vscrollbar_new(NULL);
653     priv->empty_label = gtk_label_new(_("Ckct_wi_grid_no_items"));
654     priv->style = NULL;
655
656     priv->area_height = 1;
657     priv->area_rows = 1;
658     priv->children = NULL;
659
660     priv->first_index = 0;
661     priv->click_x = 0;
662     priv->click_y = 0;
663
664     priv->item_height = 96;
665     priv->h_margin = 12;
666     priv->v_margin = 6;
667     priv->focus_margin = 6;
668     priv->icon_label_margin = 6;
669     priv->icon_width = 64;
670     priv->label_pos = HILDON_GRID_ITEM_LABEL_POS_BOTTOM;
671
672     priv->old_sb_pos = -1;
673     priv->old_item_height = -1;
674
675     gtk_widget_set_parent(priv->scrollbar, GTK_WIDGET(grid));
676     gtk_widget_set_parent(priv->empty_label, GTK_WIDGET(grid));
677
678     priv->last_button_event = GDK_NOTHING;
679
680     GTK_WIDGET_SET_FLAGS(grid, GTK_NO_WINDOW);
681
682     /* Signal for scrollbar. */
683     g_signal_connect(G_OBJECT(priv->scrollbar), "value-changed",
684                      G_CALLBACK(hildon_grid_scrollbar_moved), grid);
685
686     /* Signal for key press. */
687     GTK_WIDGET_SET_FLAGS(GTK_WIDGET(grid), GTK_CAN_FOCUS);
688     gtk_widget_set_events(GTK_WIDGET(grid), GDK_KEY_PRESS_MASK);
689
690     GTK_WIDGET_UNSET_FLAGS(priv->scrollbar, GTK_CAN_FOCUS);
691     hildon_grid_set_style(grid, DEFAULT_STYLE);
692 }
693
694 /**
695  * hildon_grid_new:
696  *
697  * Creates a new #HildonGrid.
698  *
699  * Return value: A new #HildonGrid
700  */
701 GtkWidget *hildon_grid_new(void)
702 {
703
704     HildonGrid *grid;
705
706     grid = g_object_new(HILDON_TYPE_GRID, NULL);
707
708     return GTK_WIDGET(grid);
709 }
710
711
712 static void hildon_grid_realize(GtkWidget * widget)
713 {
714     HildonGrid *grid;
715     HildonGridPrivate *priv;
716     GdkWindowAttr attr;
717     gint attr_mask;
718
719
720     GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
721
722     grid = HILDON_GRID(widget);
723     priv = HILDON_GRID_GET_PRIVATE(grid);
724
725     /* Create GdkWindow for catching events. */
726     attr.x = widget->allocation.x;
727     attr.y = widget->allocation.y;
728     attr.width = widget->allocation.width - priv->scrollbar_width;
729     attr.height = widget->allocation.height;
730     attr.window_type = GDK_WINDOW_CHILD;
731     attr.event_mask = gtk_widget_get_events(widget)
732         | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
733
734     widget->window = gtk_widget_get_parent_window(widget);
735     g_object_ref(widget->window);
736
737     attr.wclass = GDK_INPUT_ONLY;
738     attr_mask = GDK_WA_X | GDK_WA_Y;
739
740     priv->event_window = gdk_window_new(widget->window, &attr, attr_mask);
741     gdk_window_set_user_data(priv->event_window, widget);
742
743     widget->style = gtk_style_attach(widget->style, widget->window);
744
745     gtk_style_set_background(widget->style,
746                              widget->window, GTK_STATE_NORMAL);
747 }
748
749
750 static void hildon_grid_unrealize(GtkWidget * widget)
751 {
752     HildonGridPrivate *priv;
753
754     priv = HILDON_GRID_GET_PRIVATE(HILDON_GRID(widget));
755
756     if (priv->event_window != NULL) {
757         gdk_window_set_user_data(priv->event_window, NULL);
758         gdk_window_destroy(priv->event_window);
759         priv->event_window = NULL;
760     }
761
762     if (GTK_WIDGET_CLASS(parent_class)->unrealize) {
763         (*GTK_WIDGET_CLASS(parent_class)->unrealize) (widget);
764     }
765 }
766
767
768
769 static void hildon_grid_map(GtkWidget * widget)
770 {
771     HildonGrid *grid;
772     HildonGridPrivate *priv;
773     GList *list;
774     GtkWidget *child;
775
776     g_return_if_fail(HILDON_IS_GRID(widget));
777
778     if (!GTK_WIDGET_VISIBLE(widget))
779         return;
780
781     grid = HILDON_GRID(widget);
782     priv = HILDON_GRID_GET_PRIVATE(grid);
783
784     (*GTK_WIDGET_CLASS(parent_class)->map) (widget);
785
786     /* We shouldn't really need the following...*/
787     if (priv->scrollbar != NULL && GTK_WIDGET_VISIBLE(priv->scrollbar)) {
788         if (!GTK_WIDGET_MAPPED(priv->scrollbar)) {
789             gtk_widget_map(priv->scrollbar);
790         }
791     }
792
793     if (priv->empty_label != NULL &&
794         GTK_WIDGET_VISIBLE(priv->empty_label)) {
795         if (!GTK_WIDGET_MAPPED(priv->empty_label)) {
796             gtk_widget_map(priv->empty_label);
797         }
798     }
799
800     for (list = priv->children; list != NULL; list = list->next) {
801         child = ((HildonGridChild *) list->data)->widget;
802
803         if (GTK_WIDGET_VISIBLE(child)) {
804             if (!GTK_WIDGET_MAPPED(child)) {
805                 gtk_widget_map(child);
806             }
807         }
808     }
809     /* END OF don't really need */
810
811     /* Also make event window visible. */
812     gdk_window_show(priv->event_window);
813 }
814
815
816
817 static void hildon_grid_unmap(GtkWidget * widget)
818 {
819     HildonGridPrivate *priv;
820
821     priv = HILDON_GRID_GET_PRIVATE(HILDON_GRID(widget));
822
823     if (priv->event_window != NULL) {
824         gdk_window_hide(priv->event_window);
825     }
826
827     (*GTK_WIDGET_CLASS(parent_class)->unmap) (widget);
828 }
829
830
831
832 static gboolean
833 hildon_grid_expose(GtkWidget * widget, GdkEventExpose * event)
834 {
835     HildonGrid *grid;
836     HildonGridPrivate *priv;
837     GtkContainer *container;
838     GList *list;
839     gint child_no;
840
841     g_return_val_if_fail(widget, FALSE);
842     g_return_val_if_fail(HILDON_IS_GRID(widget), FALSE);
843     g_return_val_if_fail(event, FALSE);
844
845     grid = HILDON_GRID(widget);
846     priv = HILDON_GRID_GET_PRIVATE(grid);
847     container = GTK_CONTAINER(grid);
848
849     /* If grid has no children,
850      * propagate the expose event to the label is one exists */ 
851     if (priv->children == NULL || g_list_length(priv->children) == 0) {
852         if (priv->empty_label != NULL) {
853             gtk_container_propagate_expose(container,
854                                            priv->empty_label, event);
855         }
856         return FALSE;
857     }
858
859     /* Only expose visible children. */
860
861     /* Jump over invisible. */
862     for (list = priv->children, child_no = 0;
863          list != NULL && child_no < priv->first_index;
864          list = list->next, child_no++) {
865         ;       /* Nothing here. */
866     }
867
868     for (; list != NULL && child_no < priv->first_index +
869          priv->num_columns * priv->area_rows; list = list->next) {
870         gtk_container_propagate_expose(container,
871                                        ((HildonGridChild *) list->data)
872                                        ->widget, event);
873     }
874
875     /* Keep focused item focused. */
876     if (container->focus_child != NULL
877         && !GTK_WIDGET_HAS_FOCUS(container->focus_child)) {
878         set_focus(grid, container->focus_child, FALSE);
879     }
880     if (priv->scrollbar_width > 0 && priv->scrollbar != NULL) {
881         gtk_container_propagate_expose(container, priv->scrollbar, event);
882     }
883
884     return FALSE;
885 }
886
887
888 static void
889 hildon_grid_size_request(GtkWidget * widget, GtkRequisition * requisition)
890 {
891     HildonGrid *grid;
892     HildonGridPrivate *priv;
893     GList *list;
894     GtkWidget *child;
895     GtkRequisition req;
896
897     g_return_if_fail(widget);
898     g_return_if_fail(requisition);
899
900     grid = HILDON_GRID(widget);
901     priv = HILDON_GRID_GET_PRIVATE(grid);
902
903     /* Want as big as possible. */
904     requisition->width = 0x7fff;        /* Largest possible gint16 */
905     requisition->height = 0x7fff;       /* Largest possible gint16 */
906
907     if (priv->children == NULL) {
908         if (priv->empty_label != NULL &&
909             GTK_WIDGET_VISIBLE(priv->empty_label)) {
910             gtk_widget_size_request(priv->empty_label, &req);
911         }
912     }
913
914     if (priv->scrollbar != NULL && GTK_WIDGET_VISIBLE(priv->scrollbar)) {
915         gtk_widget_size_request(priv->scrollbar, &req);
916     }
917
918     for (list = priv->children; list != NULL; list = list->next) {
919         child = ((HildonGridChild *) list->data)->widget;
920
921         gtk_widget_size_request(child, &req);
922     }
923 }
924
925 /*
926  * hildon_grid_size_allocate:
927  *
928  * Supposingly called when size of grid changes and after view have moved so
929  * that items need to be relocated.
930  */
931 static void
932 hildon_grid_size_allocate(GtkWidget * widget, GtkAllocation * allocation)
933 {
934     HildonGrid *grid;
935     HildonGridPrivate *priv;
936     GList *list;
937     GtkWidget *child;
938     gint child_no;
939     gint y_offset;
940     gint row_margin;
941
942     GtkAllocation alloc;
943     GtkRequisition req;
944
945     g_return_if_fail(widget);
946     g_return_if_fail(allocation);
947
948     grid = HILDON_GRID(widget);
949     priv = HILDON_GRID_GET_PRIVATE(grid);
950     widget->allocation = *allocation;
951
952     get_style_properties(grid);
953
954     /* First of all, make sure GdkWindow is over our widget. */
955     if (priv->event_window != NULL) {
956         gdk_window_move_resize(priv->event_window,
957                                widget->allocation.x,
958                                widget->allocation.y,
959                                widget->allocation.width -
960                                    priv->scrollbar_width,
961                                widget->allocation.height);
962     }
963     /* Show the label if there are no items. */
964     if (priv->children == NULL) {
965         /* 
966          * We probably don't need this as scrollbar should be hidden when
967          * removing items, but one can never be too sure...
968          */
969         if (priv->scrollbar != NULL &&
970             GTK_WIDGET_VISIBLE(priv->scrollbar)) {
971             priv->scrollbar_width = 0;
972             gtk_widget_hide(priv->scrollbar);
973         }
974
975         /* Show label if creating one actually worked. */
976         if (priv->empty_label != NULL) {
977             gtk_widget_get_child_requisition(priv->empty_label, &req);
978
979             /* ...for sure we must have a position for the label here... */
980             alloc.x = allocation->x + GRID_LABEL_POS_PAD;
981             alloc.y = allocation->y + GRID_LABEL_POS_PAD;
982             alloc.width = MIN(req.width, allocation->width -
983                               GRID_LABEL_POS_PAD);
984             alloc.height = MIN(req.height, allocation->height -
985                                GRID_LABEL_POS_PAD);
986
987             /* Make sure we don't use negative values. */
988             if (alloc.width < 0) {
989                 alloc.width = 0;
990             }
991             if (alloc.height < 0) {
992                 alloc.height = 0;
993             }
994
995             gtk_widget_size_allocate(priv->empty_label, &alloc);
996
997             if (!GTK_WIDGET_VISIBLE(priv->empty_label)) {
998                 gtk_widget_show(priv->empty_label);
999             }
1000         }
1001
1002         return;
1003     }
1004
1005     /* As we have some items, hide label if it was visible. */
1006     if (priv->empty_label != NULL &&
1007         GTK_WIDGET_VISIBLE(priv->empty_label)) {
1008         gtk_widget_hide(priv->empty_label);
1009     }
1010
1011     priv->area_height = allocation->height;
1012     priv->area_rows = allocation->height / priv->item_height;
1013
1014     /* Adjust/show/hide scrollbar. */
1015     adjust_scrollbar_height(grid);
1016     if (priv->old_item_height != priv->item_height) {
1017         priv->old_item_height = priv->item_height;
1018         jump_scrollbar_to_focused(grid);
1019     }
1020
1021     /* Update item width. */
1022     if (priv->num_columns == 1) {
1023         priv->item_width = allocation->width - priv->scrollbar_width -
1024             priv->h_margin - priv->scrollbar_width;
1025     } else {
1026         priv->item_width = (allocation->width - priv->scrollbar_width) /
1027             priv->num_columns;
1028     }
1029
1030     priv->first_index =
1031         (int) gtk_range_get_value(GTK_RANGE(priv->scrollbar)) /
1032         priv->item_height * priv->num_columns;
1033
1034     /* Hide items before visible ones. */
1035     for (list = priv->children, child_no = 0;
1036          list != NULL && child_no < priv->first_index;
1037          list = list->next, child_no++) {
1038         child = ((HildonGridChild *) list->data)->widget;
1039
1040         if (GTK_WIDGET_VISIBLE(child)) {
1041             gtk_widget_hide(child);
1042         }
1043     }
1044
1045     /* Allocate visible items. */
1046     alloc.width = priv->item_width - priv->h_margin;
1047     switch (priv->label_pos) {
1048     case HILDON_GRID_ITEM_LABEL_POS_BOTTOM:
1049         row_margin = priv->icon_label_margin;
1050         break;
1051     case HILDON_GRID_ITEM_LABEL_POS_RIGHT:
1052         row_margin = priv->v_margin;
1053         break;
1054     default:
1055         row_margin = 0;
1056         break;
1057     }
1058     alloc.height = priv->item_height - row_margin;
1059
1060     for (y_offset = priv->first_index / priv->num_columns * priv->item_height;
1061          list != NULL && child_no < priv->first_index +
1062          priv->area_rows * priv->num_columns;
1063          list = list->next, child_no++) {
1064         child = ((HildonGridChild *) list->data)->widget;
1065
1066         if (!GTK_WIDGET_VISIBLE(child)) {
1067             gtk_widget_show(child);
1068         }
1069
1070         /* Don't update icons which are not visible... */
1071         alloc.y = (child_no / priv->num_columns) * priv->item_height +
1072                   allocation->y - y_offset + row_margin;
1073         alloc.x = (child_no % priv->num_columns) * priv->item_width +
1074                   allocation->x;
1075
1076         _hildon_grid_item_done_updating_settings(HILDON_GRID_ITEM(child));
1077         gtk_widget_size_allocate(child, &alloc);
1078     }
1079
1080     /* Hide items after visible items. */
1081     for (; list != NULL; list = list->next) {
1082         child = ((HildonGridChild *) list->data)->widget;
1083
1084         if (GTK_WIDGET_VISIBLE(child)) {
1085             gtk_widget_hide(child);
1086         }
1087     }
1088 }
1089
1090
1091
1092 /**
1093  * hildon_grid_add:
1094  * @container:  Container (#HildonGrid) to add HildonGridItem into.
1095  * @widget:     #GtkWidget (#HildonGridItem) to add.
1096  *
1097  * Adds a new HildonGridItem into HildonGrid.
1098  */
1099 static void hildon_grid_add(GtkContainer * container, GtkWidget * widget)
1100 {
1101     HildonGrid *grid;
1102     HildonGridPrivate *priv;
1103     HildonGridChild *child;
1104
1105
1106     g_return_if_fail(HILDON_IS_GRID(container));
1107     g_return_if_fail(HILDON_IS_GRID_ITEM(widget));
1108
1109     grid = HILDON_GRID(container);
1110     priv = HILDON_GRID_GET_PRIVATE(HILDON_GRID(grid));
1111     GTK_WIDGET_SET_FLAGS(widget, GTK_NO_WINDOW);
1112
1113     child = g_new(HildonGridChild, 1);
1114     if (child == NULL) {
1115         g_critical("no memory for child - not adding");
1116         return;
1117     }
1118     child->widget = widget;
1119
1120     _hildon_grid_item_set_label_pos   (HILDON_GRID_ITEM(widget), priv->label_pos);
1121     _hildon_grid_item_set_focus_margin(HILDON_GRID_ITEM(widget), priv->focus_margin);
1122     _hildon_grid_item_set_icon_width  (HILDON_GRID_ITEM(widget), priv->icon_width);
1123     _hildon_grid_item_set_emblem_size (HILDON_GRID_ITEM(widget), priv->emblem_size);
1124
1125     /* Add the new item to the grid */
1126     priv->children = g_list_append(priv->children, child);
1127     gtk_widget_set_parent(widget, GTK_WIDGET(grid));
1128
1129     /* Property changes (child's set_sensitive) */
1130     g_signal_connect_after(G_OBJECT(widget), "state-changed",
1131                            G_CALLBACK(hildon_grid_state_changed), grid);
1132
1133     /* Matches both empty grid and all-dimmed grid. */
1134     if (GTK_CONTAINER(grid)->focus_child == NULL)
1135         set_focus(grid, widget, TRUE);
1136
1137     /* 
1138      * If item was added in visible area, relocate items. Otherwise update
1139      * scrollbar and see if items need relocating.
1140      */
1141     if (g_list_length(priv->children) < priv->first_index +
1142         priv->area_rows * priv->num_columns) {
1143         gtk_widget_queue_resize(GTK_WIDGET(grid));
1144     } else {
1145         gboolean updated;
1146
1147         updated = adjust_scrollbar_height(grid);
1148         /* Basically this other test is useless -- shouldn't need to jump. 
1149          */
1150         updated |= jump_scrollbar_to_focused(grid);
1151
1152         if (updated) {
1153             gtk_widget_queue_resize(GTK_WIDGET(grid));
1154         }
1155     }
1156 }
1157
1158 /**
1159  * hildon_grid_remove:
1160  * @container:  Container (#HildonGrid) to remove #HildonGridItem from.
1161  * @widget:     Widget (#HildonGridItem) to be removed.
1162  *
1163  * Removes HildonGridItem from HildonGrid.
1164  */
1165 static void
1166 hildon_grid_remove(GtkContainer * container, GtkWidget * widget)
1167 {
1168     HildonGrid *grid;
1169     HildonGridPrivate *priv;
1170     HildonGridChild *child;
1171     GtkWidget *child_widget;
1172     GList *list;
1173     gint index, old_index;
1174     gboolean deleted;
1175     gboolean updated;
1176
1177     g_return_if_fail(HILDON_IS_GRID(container));
1178     g_return_if_fail(HILDON_IS_GRID_ITEM(widget));
1179
1180     grid = HILDON_GRID(container);
1181     priv = HILDON_GRID_GET_PRIVATE(container);
1182
1183     old_index = priv->focus_index;
1184     updated = GTK_WIDGET_VISIBLE(widget);
1185
1186     for (list = priv->children, index = 0, deleted = FALSE;
1187          list != NULL; list = list->next, index++) {
1188         child = (HildonGridChild *) list->data;
1189         child_widget = child->widget;
1190
1191         /* Remove the Item if it is found in the grid */
1192         if (child_widget == widget) {
1193             gtk_widget_unparent(child_widget);
1194             priv->children = g_list_remove_link(priv->children, list);
1195             g_list_free(list);
1196             g_free(child);
1197
1198             deleted = TRUE;
1199
1200             break;
1201         }
1202     }
1203
1204     /* Emit warning if the item is not found */
1205     if (!deleted) {
1206         g_warning("tried to remove unexisting item");
1207         return;
1208     }
1209
1210     /* Move focus somewhere. */
1211     if (old_index == index) {
1212         if (old_index == g_list_length(priv->children)) {
1213             if (index == 0) {
1214                 set_focus(grid, NULL, TRUE);
1215             } else {
1216                 set_focus(grid,
1217                           get_child_by_index(priv, old_index - 1), TRUE);
1218             }
1219         } else {
1220             set_focus(grid, get_child_by_index(priv, old_index), TRUE);
1221         }
1222     } else {
1223         set_focus(grid, GTK_CONTAINER(grid)->focus_child, TRUE);
1224     }
1225
1226     updated |= adjust_scrollbar_height(grid);
1227     updated |= jump_scrollbar_to_focused(grid);
1228
1229     if (updated) {
1230         gtk_widget_queue_resize(GTK_WIDGET(grid));
1231     }
1232 }
1233
1234 /**
1235  * hildon_grid_set_focus_child:
1236  * @container:  HildonGrid
1237  * @widget:     HildonGridItem
1238  *
1239  * Sets focus.
1240  */
1241 static void
1242 hildon_grid_set_focus_child(GtkContainer * container, GtkWidget * widget)
1243 {
1244     HildonGrid *grid;
1245     HildonGridPrivate *priv;
1246
1247     g_return_if_fail(HILDON_IS_GRID(container));
1248     g_return_if_fail(HILDON_IS_GRID_ITEM(widget) || widget == NULL);
1249
1250     grid = HILDON_GRID(container);
1251     priv = HILDON_GRID_GET_PRIVATE(grid);
1252
1253     if (GTK_CONTAINER(grid)->focus_child == widget || widget == NULL)
1254         return;
1255
1256     set_focus(grid, widget, TRUE);
1257 }
1258
1259
1260
1261 static void
1262 set_focus(HildonGrid * grid, GtkWidget * widget, gboolean refresh_view)
1263 {
1264     HildonGridPrivate *priv;
1265     GtkContainer *container;
1266     gboolean view_updated;
1267
1268
1269     priv = HILDON_GRID_GET_PRIVATE(grid);
1270     container = GTK_CONTAINER(grid);
1271
1272     /* If widget is NULL -> unfocus */ 
1273     if (widget == NULL && container->focus_child != NULL)
1274         GTK_WIDGET_UNSET_FLAGS(container->focus_child, GTK_HAS_FOCUS);
1275
1276     GTK_CONTAINER(grid)->focus_child = widget;
1277     if (widget == NULL) {
1278         priv->focus_index = -1;
1279         return;
1280     }
1281
1282     /* Get the child index which the user wanted to focus */
1283     priv->focus_index = get_child_index(priv, widget);
1284
1285     gtk_widget_grab_focus(widget);
1286
1287     if (refresh_view) {
1288         view_updated = jump_scrollbar_to_focused(grid);
1289     } else {
1290         view_updated = FALSE;
1291     }
1292
1293     if (view_updated) {
1294         hildon_grid_size_allocate(GTK_WIDGET(grid),
1295                                   &GTK_WIDGET(grid)->allocation);
1296     }
1297 }
1298
1299 static void
1300 hildon_grid_forall(GtkContainer * container,
1301                    gboolean include_internals,
1302                    GtkCallback callback, gpointer callback_data)
1303 {
1304     HildonGrid *grid;
1305     HildonGridPrivate *priv;
1306     GList *list;
1307
1308     g_return_if_fail(container);
1309     g_return_if_fail(callback);
1310
1311     grid = HILDON_GRID(container);
1312     priv = HILDON_GRID_GET_PRIVATE(grid);
1313
1314     /* Connect callback functions */
1315     if (include_internals) {
1316         if (priv->scrollbar != NULL) {
1317             (*callback) (priv->scrollbar, callback_data);
1318         }
1319         if (priv->empty_label != NULL) {
1320             (*callback) (priv->empty_label, callback_data);
1321         }
1322     }
1323
1324     for (list = priv->children; list != NULL; list = list->next) {
1325         (*callback) (((HildonGridChild *) list->data)->widget,
1326                      callback_data);
1327     }
1328 }
1329
1330 static void hildon_grid_destroy(GtkObject * self)
1331 {
1332     HildonGridPrivate *priv;
1333
1334     g_return_if_fail(self != NULL);
1335     g_return_if_fail(HILDON_IS_GRID(self));
1336
1337     priv = HILDON_GRID_GET_PRIVATE(self);
1338
1339     if (GTK_WIDGET(self)->window != NULL) {
1340         g_object_unref(G_OBJECT(GTK_WIDGET(self)->window));
1341     }
1342
1343     gtk_container_forall(GTK_CONTAINER(self),
1344                          (GtkCallback) gtk_object_ref, NULL);
1345     gtk_container_forall(GTK_CONTAINER(self),
1346                          (GtkCallback) gtk_widget_unparent, NULL);
1347
1348     GTK_OBJECT_CLASS(parent_class)->destroy(self);
1349 }
1350
1351 static void hildon_grid_finalize(GObject * object)
1352 {
1353     HildonGrid *grid;
1354     HildonGridPrivate *priv;
1355
1356     grid = HILDON_GRID(object);
1357     priv = HILDON_GRID_GET_PRIVATE(grid);
1358
1359     gtk_container_forall(GTK_CONTAINER(object),
1360                          (GtkCallback) gtk_object_unref, NULL);
1361
1362     if (priv->style != NULL) {
1363         g_free(priv->style);
1364     }
1365     if (G_OBJECT_CLASS(parent_class)->finalize) {
1366         G_OBJECT_CLASS(parent_class)->finalize(object);
1367     }
1368 }
1369
1370 /*
1371  * hildon_grid_key_pressed:
1372  * @widget: Widget where we get the signal from
1373  * @event:  EventKey
1374  * @data:   #HildonGrid
1375  *
1376  * Handle user key press (keyboard navigation).
1377  *
1378  * And here's how it works if some items are dimmed (moving to right):
1379  * . . . .      . . # .     . 2 # .     . # . .
1380  * . 1 # 2      . 1 # #     1 # # #     1 # # #
1381  * . . .        . . 2       . . .       . 2 .
1382  *
1383  * '.' = item,
1384  * '#' = dimmed item, 
1385  * '1' = starting position,
1386  * '2' = final position
1387  *
1388  * ...although only the first example is implemented right now.
1389  *
1390  * Return value: Signal handled
1391  */
1392 static gboolean
1393 hildon_grid_key_pressed(GtkWidget * widget,
1394                         GdkEventKey * event)
1395 {
1396     GtkAdjustment *adjustment;
1397     GtkContainer *container;
1398     GtkWidget *new_focus;
1399     HildonGrid *grid;
1400     HildonGridPrivate *priv;
1401     gboolean shift;
1402     gint keyval;
1403     gint x, y;
1404     gint focus_index;
1405     gint child_count, child_rows;
1406     gint t;
1407     gint addition, max_add;
1408
1409     g_return_val_if_fail(widget, FALSE);
1410
1411     grid = HILDON_GRID(widget);
1412     priv = HILDON_GRID_GET_PRIVATE(grid);
1413
1414     /* 
1415      * If focus was never lost, we could just see if an item is focused - 
1416      * if not, there's nothing else to focus...
1417      */
1418
1419     /* No items? */
1420     if (priv->children == NULL || g_list_length(priv->children) == 0)
1421         return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1422
1423     /* Focused item is dimmed? */
1424     /* If we have no focus, allow non-existing focus to move... */
1425     container = GTK_CONTAINER(grid);
1426     if (container->focus_child != NULL
1427         && !GTK_WIDGET_IS_SENSITIVE(container->focus_child)) {
1428         return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1429     }
1430     /* At the moment we don't want to do anything here if alt or control
1431        or MODX is pressed, so return now. Shift + TAB are accepted (from
1432        hildon-table-grid) And right now modifiers do not make any
1433        difference... */
1434
1435     /* Said somewhere that "foo = a == b" is not desirable. */
1436     if (event->state & GDK_SHIFT_MASK) {
1437         shift = TRUE;
1438     } else {
1439         shift = FALSE;
1440     }
1441
1442     keyval = event->keyval;
1443     if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) {
1444         switch (event->keyval) {
1445         case GDK_Left:
1446             keyval = GDK_Right;
1447             break;
1448         case GDK_KP_Left:
1449             keyval = GDK_KP_Right;
1450             break;
1451         case GDK_Right:
1452             keyval = GDK_Left;
1453             break;
1454         case GDK_KP_Right:
1455             keyval = GDK_KP_Left;
1456             break;
1457         }
1458     }
1459
1460     child_count = g_list_length(priv->children);
1461     child_rows = (child_count - 1) / priv->num_columns + 1;
1462
1463     if (priv->focus_index != -1) {
1464         x = priv->focus_index % priv->num_columns;
1465         y = priv->focus_index / priv->num_columns;
1466     } else {
1467         x = y = 0;
1468     }
1469
1470     switch (keyval) {
1471     case GDK_KP_Page_Up:
1472     case GDK_Page_Up:
1473         if (priv->first_index == 0) {
1474             if (priv->focus_index == 0) {
1475                 return TRUE;
1476             }
1477             set_focus(grid, get_child_by_index(priv, 0), TRUE);
1478             return TRUE;
1479         }
1480
1481         t = MAX(priv->first_index / priv->num_columns - priv->area_rows, 0);
1482         adjustment = gtk_range_get_adjustment(GTK_RANGE(priv->scrollbar));
1483         adjustment->value = (gdouble) (t * priv->item_height);
1484         gtk_range_set_adjustment(GTK_RANGE(priv->scrollbar), adjustment);
1485         gtk_widget_queue_draw(priv->scrollbar);
1486         update_contents(grid);
1487
1488         /* Want to update now. */
1489         hildon_grid_size_allocate(GTK_WIDGET(grid),
1490                                   &GTK_WIDGET(grid)->allocation);
1491
1492         return TRUE;
1493         break;
1494
1495     case GDK_KP_Page_Down:
1496     case GDK_Page_Down:
1497         if (priv->first_index / priv->num_columns ==
1498             child_rows - priv->area_rows) {
1499             if (priv->focus_index == child_count - 1) {
1500                 return TRUE;
1501             }
1502             set_focus(grid, get_child_by_index(priv, child_count - 1),
1503                       TRUE);
1504             return TRUE;
1505         }
1506
1507         t = MIN(priv->first_index / priv->num_columns +
1508                 priv->area_rows, child_rows - priv->area_rows);
1509         adjustment = gtk_range_get_adjustment(GTK_RANGE(priv->scrollbar));
1510         adjustment->value = (gdouble) (t * priv->item_height);
1511         gtk_range_set_adjustment(GTK_RANGE(priv->scrollbar), adjustment);
1512         gtk_widget_queue_draw(priv->scrollbar);
1513         update_contents(grid);
1514
1515         /* Want to update now. */
1516         hildon_grid_size_allocate(GTK_WIDGET(grid),
1517                                   &GTK_WIDGET(grid)->allocation);
1518
1519         return TRUE;
1520         break;
1521
1522     case GDK_KP_Up:
1523     case GDK_Up:
1524         if (y <= 0) {
1525             return TRUE;
1526         }
1527         addition = -priv->num_columns;
1528         max_add = y;
1529         y--;
1530         break;
1531
1532     case GDK_KP_Down:
1533     case GDK_Down:
1534         if (y >= (child_count - 1) / priv->num_columns) {
1535             return TRUE;
1536         }
1537         t = child_count % priv->num_columns;
1538         if (t == 0) {
1539             t = priv->num_columns;
1540         }
1541         if (y == (child_count - 1) / priv->num_columns - 1 && x >= t) {
1542             x = t - 1;
1543         }
1544         y++;
1545         addition = priv->num_columns;
1546         max_add = child_rows - y;
1547         break;
1548
1549     case GDK_KP_Left:
1550     case GDK_Left:
1551         if (x <= 0) {
1552             return TRUE;
1553         }
1554         addition = -1;
1555         max_add = x;
1556         x--;
1557         break;
1558
1559     case GDK_KP_Right:
1560     case GDK_Right:
1561         if (x >= priv->num_columns - 1) {
1562             return TRUE;
1563         }
1564         if (y == 0 && x >= child_count - 1) {
1565             return TRUE;
1566         }
1567         x++;
1568         addition = 1;
1569         max_add = priv->num_columns - x;
1570         if (y * priv->num_columns + x == child_count) {
1571             y--;
1572         }
1573         break;
1574     case GDK_KP_Enter:
1575     case GDK_Return:
1576         hildon_grid_activate_child(grid,
1577                                    HILDON_GRID_ITEM
1578                                    (GTK_CONTAINER(grid)->focus_child));
1579         return TRUE;
1580         break;
1581     default:
1582         return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1583         break;
1584     }
1585
1586     focus_index = y * priv->num_columns + x;
1587     new_focus = get_child_by_index(priv, focus_index);
1588
1589     while (new_focus != NULL &&
1590            focus_index < child_count && !GTK_WIDGET_SENSITIVE(new_focus)) {
1591         max_add--;
1592
1593         if (max_add == 0) {
1594             return TRUE;
1595         }
1596         focus_index += addition;
1597         new_focus = get_child_by_index(priv, focus_index);
1598     }
1599
1600     if (new_focus != NULL) {
1601         set_focus(grid, new_focus, TRUE);
1602     }
1603     return TRUE;
1604 }
1605
1606
1607 /*
1608  * hildon_grid_button_pressed:
1609  * @widget: Widget where signal is coming from
1610  * @event:  #EventButton
1611  * @data:   #HildonGrid
1612  *
1613  * Handle mouse button press.
1614  *
1615  * Return value: Signal handled
1616  */
1617 static gboolean
1618 hildon_grid_button_pressed(GtkWidget * widget,
1619                            GdkEventButton * event)
1620 {
1621     HildonGrid *grid;
1622     HildonGridPrivate *priv;
1623     GtkWidget *child;
1624     int child_no;
1625
1626     grid = HILDON_GRID(widget);
1627     priv = HILDON_GRID_GET_PRIVATE(grid);
1628
1629 /* Watch out for double/triple click press events */
1630
1631     if (event->type == GDK_2BUTTON_PRESS ||
1632         event->type == GDK_3BUTTON_PRESS) {
1633         priv->last_button_event = event->type;
1634         return FALSE;
1635     }
1636
1637     priv->last_button_event = event->type;
1638
1639     if (event->type != GDK_BUTTON_PRESS)
1640         return FALSE;
1641
1642
1643     child_no = get_child_index_by_coord(priv, event->x, event->y);
1644
1645     if (child_no == -1 || child_no >= g_list_length(priv->children))
1646         return FALSE;
1647
1648     child = get_child_by_index(priv, child_no);
1649     if (!GTK_WIDGET_IS_SENSITIVE(child))
1650         return FALSE;
1651
1652     set_focus(grid, child, TRUE);
1653
1654     priv->click_x = event->x;
1655     priv->click_y = event->y;
1656
1657     return FALSE;
1658 }
1659
1660 /*
1661  * hildon_grid_button_released:
1662  * @widget: Widget the signal is coming from
1663  * @event:  #EventButton
1664  * @data:   #HildonGrid
1665  *
1666  * Handle mouse button release.
1667  *
1668  * Return value: Signal handled
1669  */
1670 static gboolean
1671 hildon_grid_button_released(GtkWidget * widget,
1672                             GdkEventButton * event)
1673 {
1674     HildonGrid *grid;
1675     HildonGridPrivate *priv;
1676     GtkWidget *child;
1677     int child_no;
1678
1679     grid = HILDON_GRID(widget);
1680     priv = HILDON_GRID_GET_PRIVATE(grid);
1681
1682     /* In case of double/triple click, silently ignore the release event */
1683
1684     if (priv->last_button_event == GDK_2BUTTON_PRESS ||
1685         priv->last_button_event == GDK_3BUTTON_PRESS) {
1686         priv->last_button_event = event->type;
1687         return FALSE;
1688     }
1689
1690     child_no = get_child_index_by_coord(priv, event->x, event->y);
1691
1692     if (child_no == -1 || child_no >= g_list_length(priv->children)) {
1693         return FALSE;
1694     }
1695     child = get_child_by_index(priv, child_no);
1696     if (!GTK_WIDGET_IS_SENSITIVE(child)) {
1697         return FALSE;
1698     }
1699     if (abs(priv->click_x - event->x) >= DRAG_SENSITIVITY
1700         && abs(priv->click_y - event->y) >= DRAG_SENSITIVITY) {
1701         return FALSE;
1702     }
1703     set_focus(grid, child, TRUE);
1704     priv->last_button_event = event->type;
1705     hildon_grid_activate_child(grid, HILDON_GRID_ITEM(child));
1706
1707     return FALSE;
1708 }
1709
1710 /*
1711  * hildon_grid_scrollbar_moved:
1712  * @widget: Widget which sent the signal
1713  * @data:   #HildonGrid
1714  *
1715  * Update HildonGrid contents when scrollbar is moved.
1716  *
1717  * Return value: Signal handeld
1718  */
1719 static gboolean
1720 hildon_grid_scrollbar_moved(GtkWidget * widget, gpointer data)
1721 {
1722     HildonGrid *grid;
1723     HildonGridPrivate *priv;
1724     gboolean updated = FALSE;
1725
1726     grid = HILDON_GRID(data);
1727     priv = HILDON_GRID_GET_PRIVATE(grid);
1728     updated = update_contents(grid);
1729
1730     /* 
1731      * If grid changes focus while dragging scrollbar and pointer leaves
1732      * scrollbar, focus is moved to prev_focus... This prevents that.
1733      */
1734     gtk_window_set_prev_focus_widget(GTK_WINDOW
1735                                      (gtk_widget_get_toplevel(widget)),
1736                                      GTK_CONTAINER(grid)->focus_child);
1737
1738     if (updated)
1739         /* Don't just queue it, let's do it now! */
1740         hildon_grid_size_allocate(GTK_WIDGET(grid),
1741                                   &GTK_WIDGET(grid)->allocation);
1742
1743     return TRUE;
1744 }
1745
1746
1747 /*
1748  * update_contents:
1749  * @grid:   #HildonGrid
1750  *
1751  * Update the view if scrollbar has moved so that first visible row
1752  * should've changed. Returns true if location actually changed.
1753  *
1754  * Return value: Content changed
1755  */
1756 static gboolean update_contents(HildonGrid * grid)
1757 {
1758     HildonGridPrivate *priv;
1759     gint new_row;
1760
1761     priv = HILDON_GRID_GET_PRIVATE(grid);
1762     new_row = (int) gtk_range_get_value(GTK_RANGE(priv->scrollbar))
1763         / priv->item_height;
1764
1765     if (new_row != priv->old_sb_pos) {
1766         priv->old_sb_pos = new_row;
1767         priv->first_index = new_row * priv->num_columns;
1768
1769         return TRUE;
1770     }
1771     return FALSE;
1772 }
1773
1774 /*
1775  * jump_scrollbar_to_focused:
1776  * @grid:   #HildonGrid
1777  *
1778  * Moves scrollbar position so that focused item will be shown 
1779  * in visible area.
1780  * Returns TRUE if visible position of widgets have changed.
1781  *
1782  * Return value: Content changed
1783  */
1784 static gboolean jump_scrollbar_to_focused(HildonGrid * grid)
1785 {
1786     HildonGridPrivate *priv;
1787     GtkAdjustment *adjustment;
1788     gint child_count;
1789     gint empty_grids;
1790     gint new_row;
1791
1792     priv = HILDON_GRID_GET_PRIVATE(grid);
1793     /* If we don't have scrollbar, let the focus be broken, too. */
1794     g_return_val_if_fail(priv->scrollbar != NULL, FALSE);
1795
1796     /* Make sure "first widget" is something sensible. */
1797     priv->first_index = priv->first_index -
1798         priv->first_index % priv->num_columns;
1799
1800     child_count = g_list_length(priv->children);
1801     empty_grids = priv->num_columns * priv->area_rows - child_count +
1802         priv->first_index;
1803
1804     /* Determine the position of the new row */ 
1805     if (priv->focus_index < priv->first_index) {
1806         new_row = priv->focus_index / priv->num_columns;
1807     } else if (priv->focus_index >= priv->first_index +
1808                priv->area_rows * priv->num_columns) {
1809         gint last_top_row;
1810         new_row = priv->focus_index / priv->num_columns -
1811             priv->area_rows + 1;
1812         last_top_row = child_count / priv->num_columns - priv->area_rows + 1;
1813         if (child_count % priv->num_columns != 0) {
1814             last_top_row++;
1815         }
1816         if (new_row > last_top_row) {
1817             new_row = last_top_row;
1818         }
1819     } else if (empty_grids >= priv->num_columns) {
1820         new_row = ((child_count - 1) / priv->num_columns + 1)
1821             - priv->area_rows;
1822         if (new_row < 0) {
1823             new_row = 0;
1824         }
1825     } else {
1826         return FALSE;
1827     }
1828
1829     /* Move scrollbar accordingly. */
1830     adjustment = gtk_range_get_adjustment(GTK_RANGE(priv->scrollbar));
1831     adjustment->value = (gdouble) (new_row * priv->item_height);
1832     gtk_range_set_adjustment(GTK_RANGE(priv->scrollbar), adjustment);
1833     priv->first_index = new_row * priv->num_columns;
1834     priv->old_sb_pos = new_row;
1835
1836     gtk_widget_queue_draw(priv->scrollbar);
1837
1838     return TRUE;
1839 }
1840
1841
1842 /*
1843  * adjust_scrollbar_height:
1844  * @grid:   HildonGridPrivate
1845  *
1846  * Return value: View should change
1847  *
1848  * Adjust scrollbar according the #HildonGrid contents. 
1849  * Show/hide scrollbar if
1850  * appropriate. Also sets priv->first_index.
1851  */
1852 static gboolean adjust_scrollbar_height(HildonGrid * grid)
1853 {
1854     HildonGridPrivate *priv;
1855     GtkRequisition req;
1856     GtkAdjustment *adj;
1857     GtkAllocation alloc;
1858     GtkAllocation *gridalloc;
1859     gint old_upper;
1860     gint need_rows;
1861     gint need_pixels;
1862     gboolean updated;
1863
1864     priv = HILDON_GRID_GET_PRIVATE(grid);
1865     g_return_val_if_fail(priv->scrollbar != NULL, FALSE);
1866
1867     updated = FALSE;
1868     gridalloc = &GTK_WIDGET(grid)->allocation;
1869
1870     /* See if we need scrollbar at all. */
1871     if (priv->num_columns == 0) {
1872         priv->num_columns = DEFAULT_N_COLUMNS;
1873     } else {
1874         priv->num_columns = MAX(1, priv->num_columns);
1875     }
1876
1877     if (g_list_length(priv->children) != 0) {
1878         need_rows = (g_list_length(priv->children) - 1) /
1879             priv->num_columns + 1;
1880     } else {
1881         need_rows = 0;
1882     }
1883
1884     if (need_rows <= priv->area_rows) {
1885         updated = priv->first_index != 0;
1886         priv->scrollbar_width = 0;
1887
1888         priv->first_index = 0;
1889         if (GTK_WIDGET_VISIBLE(priv->scrollbar)) {
1890             gtk_widget_hide(priv->scrollbar);
1891             updated = TRUE;
1892         }
1893
1894         return updated;
1895     }
1896
1897     /* All right then, we need scrollbar. Place scrollbar on the screen. */
1898     gtk_widget_get_child_requisition(priv->scrollbar, &req);
1899     priv->scrollbar_width = req.width;
1900
1901     alloc.width = req.width;
1902     alloc.height = gridalloc->height;
1903     alloc.x = gridalloc->width - req.width + gridalloc->x;
1904     alloc.y = gridalloc->y;
1905     gtk_widget_size_allocate(priv->scrollbar, &alloc);
1906
1907     if (!GTK_WIDGET_VISIBLE(priv->scrollbar)) {
1908         gtk_widget_show(priv->scrollbar);
1909         updated = TRUE;
1910     }
1911
1912
1913     need_pixels = need_rows * priv->item_height;
1914
1915     /* Once we know how much space we need, update the scrollbar. */
1916     adj = gtk_range_get_adjustment(GTK_RANGE(priv->scrollbar));
1917     old_upper = (int) adj->upper;
1918     adj->lower = 0.0;
1919     adj->upper = (gdouble) need_pixels;
1920     adj->step_increment = (gdouble) priv->item_height;
1921     adj->page_increment = (gdouble) (priv->area_rows * priv->item_height);
1922     adj->page_size =
1923         (gdouble) (priv->area_height - priv->area_height % priv->item_height);
1924
1925     /* Also update position if needed to show focused item. */
1926
1927     gtk_range_set_adjustment(GTK_RANGE(priv->scrollbar), adj);
1928
1929     /* Then set first_index. */
1930     priv->first_index = (int) adj->value / priv->item_height *
1931                               priv->num_columns;
1932
1933     /* Finally, ask Gtk to redraw the scrollbar. */
1934     if (old_upper != (int) adj->upper) {
1935         gtk_widget_queue_draw(priv->scrollbar);
1936     }
1937     return updated;
1938 }
1939
1940 /*
1941  * get_child_index_by_coord:
1942  * @priv:   HildonGridPrivate
1943  * @x:      X-coordinate
1944  * @y:      Y-coordinate
1945  *
1946  * Returns index of child at given coordinates, -1 if no child.
1947  *
1948  * Return value: Index
1949  */
1950 static gint
1951 get_child_index_by_coord(HildonGridPrivate * priv, gint x, gint y)
1952 {
1953     int xgap, ygap;
1954     int t;
1955
1956     xgap = x % priv->item_width;
1957     ygap = y % priv->item_height;
1958
1959     if (xgap > priv->item_width - priv->h_margin) { /*FIXME*/
1960         return -1;
1961     }
1962
1963     t = y / priv->item_height * priv->num_columns +
1964         x / priv->item_width + priv->first_index;
1965
1966     if (t >= priv->first_index + priv->area_rows * priv->num_columns ||
1967         t >= g_list_length(priv->children) || t < 0) {
1968         return -1;
1969     }
1970     return t;
1971 }
1972
1973 /*
1974  * get_child_by_index:
1975  * @priv:   HildonGridPrivate
1976  * @index:  Index of child
1977  *
1978  * Returns child that is #th in HildonGrid or NULL if child was not found
1979  * among the children.
1980  *
1981  * Return value: GtkWidget
1982  */
1983 static GtkWidget *get_child_by_index(HildonGridPrivate * priv, gint index)
1984 {
1985     GList *list;
1986     int i = 0;
1987
1988     if (index >= g_list_length(priv->children) || index < 0) {
1989         return NULL;
1990     }
1991     for (list = priv->children, i = 0; list != NULL;
1992          list = list->next, i++) {
1993         if (index == i) {
1994             return ((HildonGridChild *) list->data)->widget;
1995         }
1996     }
1997
1998     g_warning("no such child");
1999     return NULL;
2000 }
2001
2002 /*
2003  * get_child_index:
2004  * @priv:   HildonGridPrivate
2005  * @child:  #GtkWidget to look for
2006  *
2007  * Returns index of a child or -1 if child was not found among the
2008  * children.
2009  *
2010  * Return value: Index
2011  */
2012 static gint get_child_index(HildonGridPrivate * priv, GtkWidget * child)
2013 {
2014     GList *list;
2015     gint index;
2016
2017     if (child == NULL)
2018         return -1;
2019
2020     for (list = priv->children, index = 0;
2021          list != NULL; list = list->next, index++) {
2022         if (((HildonGridChild *) list->data)->widget == child) {
2023             return index;
2024         }
2025     }
2026
2027     g_warning("no such child");
2028     return -1;
2029 }
2030
2031
2032 /**
2033  * hildon_grid_activate_child:
2034  * @grid:   #HildonGrid
2035  * @item:   #HildonGridItem
2036  *
2037  * Emits a signal to tell HildonGridItem was actiavated.
2038  */
2039 void hildon_grid_activate_child(HildonGrid * grid, HildonGridItem * item)
2040 {
2041     g_return_if_fail(HILDON_IS_GRID(grid));
2042
2043     g_signal_emit(grid, grid_signals[ACTIVATE_CHILD], 0, item);
2044 }
2045
2046
2047
2048 /**
2049  * hildon_grid_set_style:
2050  * @grid:       #HildonGrid
2051  * @style_name: Style name
2052  *
2053  * Sets style. Setting style sets widget size, spacing, label position,
2054  * number of columns, and icon size.
2055  */
2056 void hildon_grid_set_style(HildonGrid * grid, const gchar * style_name)
2057 {
2058     HildonGridPrivate *priv;
2059
2060     g_return_if_fail(HILDON_IS_GRID(grid));
2061
2062
2063     priv = HILDON_GRID_GET_PRIVATE(grid);
2064     if (priv->style != NULL) {
2065         g_free((gpointer) priv->style);
2066     }
2067     if (style_name != NULL) {
2068         priv->style = g_strdup(style_name);
2069     } else {
2070         priv->style = NULL;
2071     }
2072
2073     gtk_widget_set_name(GTK_WIDGET(grid), style_name);
2074     get_style_properties(grid);
2075
2076     gtk_widget_queue_resize(GTK_WIDGET(grid));
2077 }
2078
2079 /**
2080  * hildon_grid_get_style:
2081  * @grid:   #HildonGrid
2082  *
2083  * Returns the name of style currently used in HildonGrid.
2084  *
2085  * Return value: Style name
2086  */
2087 const gchar *hildon_grid_get_style(HildonGrid * grid)
2088 {
2089     g_return_val_if_fail(HILDON_IS_GRID(grid), NULL);
2090
2091     return gtk_widget_get_name(GTK_WIDGET(grid));
2092 }
2093
2094 /*
2095  * get_style_properties:
2096  * @grid:   #HildonGrid
2097  *
2098  * Gets widget size and other stuff from gtkrc. If some stuff changed, let
2099  * children know this, too.
2100  */
2101 static void get_style_properties(HildonGrid * grid)
2102 {
2103     GList *iter;
2104     gint num_columns;
2105     HildonGridPositionType label_pos;
2106     gint emblem_size;
2107
2108     gint h_margin, v_margin;
2109     gint item_height;
2110     gint icon_width;
2111     gint focus_margin, icon_label_margin;
2112     gint label_height;
2113
2114     HildonGridPrivate *priv;
2115     g_return_if_fail(HILDON_IS_GRID(grid));
2116     priv = HILDON_GRID_GET_PRIVATE(grid);
2117
2118     gtk_widget_style_get(GTK_WIDGET(grid),
2119                          "item_hspacing", &h_margin,
2120                          "item_vspacing", &v_margin,
2121                          "item_height", &item_height,
2122                          "icon_size", &icon_width,
2123                          "n_columns", &num_columns,
2124                          "label_pos", &label_pos,
2125                          "label_hspacing", &focus_margin,
2126                          "label_vspacing", &icon_label_margin,
2127                          "emblem_size", &emblem_size,
2128                          "label_height", &label_height,
2129                          NULL);
2130
2131     hildon_grid_set_icon_width(grid, icon_width);
2132     hildon_grid_set_num_columns(grid, num_columns);
2133     hildon_grid_set_label_pos(grid, label_pos);
2134     hildon_grid_set_focus_margin(grid, focus_margin);
2135     hildon_grid_set_icon_label_margin(grid, icon_label_margin);
2136     hildon_grid_set_emblem_size(grid, emblem_size);
2137     hildon_grid_set_label_height(grid, label_height);
2138
2139     priv->h_margin = h_margin;
2140     priv->v_margin = v_margin;
2141     priv->item_height = item_height;
2142
2143     iter = NULL;
2144     /*
2145     for (iter = priv->children; iter != NULL; iter = iter->next) {
2146         HildonGridItem *child;
2147         child = HILDON_GRID_ITEM(((HildonGridChild *) iter->data)->widget);
2148         _hildon_grid_item_done_updating_settings(child);
2149     }
2150     */
2151 }
2152
2153
2154
2155 /**
2156  * hildon_grid_set_scrollbar_pos:
2157  * @grid:           #HildonGrid
2158  * @scrollbar_pos:  new position (in pixels)
2159  *
2160  * Sets view (scrollbar) to specified position.
2161  */
2162 void hildon_grid_set_scrollbar_pos(HildonGrid * grid, gint scrollbar_pos)
2163 {
2164     HildonGridPrivate *priv;
2165     GtkAdjustment *adjustment;
2166
2167     g_return_if_fail(HILDON_IS_GRID(grid));
2168
2169     priv = HILDON_GRID_GET_PRIVATE(grid);
2170     adjustment = gtk_range_get_adjustment(GTK_RANGE(priv->scrollbar));
2171     adjustment->value = (gdouble) scrollbar_pos;
2172
2173     gtk_range_set_adjustment(GTK_RANGE(priv->scrollbar), adjustment);
2174
2175     g_object_notify (G_OBJECT (grid), "scrollbar-position");
2176
2177     /* If grid isn't drawable, updating anything could mess up focus. */
2178     if (!GTK_WIDGET_DRAWABLE(GTK_WIDGET(grid)))
2179         return;
2180
2181     update_contents(grid);
2182 }
2183
2184 /**
2185  * hildon_grid_get_scrollbar_pos:
2186  * @grid:   #HildonGrid
2187  *
2188  * Returns position of scrollbar (in pixels).
2189  *
2190  * Return value: Scrollbar position
2191  */
2192 gint hildon_grid_get_scrollbar_pos(HildonGrid * grid)
2193 {
2194     GtkAdjustment *adjustment;
2195
2196     g_return_val_if_fail(HILDON_IS_GRID(grid), -1);
2197
2198     adjustment = gtk_range_get_adjustment(GTK_RANGE
2199                                           (HILDON_GRID_GET_PRIVATE
2200                                            (grid)->scrollbar));
2201     return (int) adjustment->value;
2202 }
2203
2204 static void
2205 hildon_grid_set_property(GObject * object,
2206                          guint prop_id,
2207                          const GValue * value, GParamSpec * pspec)
2208 {
2209     HildonGrid *grid;
2210
2211     grid = HILDON_GRID(object);
2212
2213     switch (prop_id) {
2214     case PROP_EMPTY_LABEL:
2215         hildon_grid_set_empty_label(grid, g_value_get_string(value));
2216         break;
2217
2218     case PROP_STYLE:
2219         hildon_grid_set_style(grid, g_value_get_string(value));
2220         break;
2221
2222     case PROP_SCROLLBAR_POS:
2223         hildon_grid_set_scrollbar_pos(grid, g_value_get_int(value));
2224         break;
2225
2226     default:
2227         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2228         break;
2229     }
2230 }
2231
2232 static void
2233 hildon_grid_get_property(GObject * object,
2234                          guint prop_id, GValue * value, GParamSpec * pspec)
2235 {
2236     HildonGrid *grid;
2237
2238     grid = HILDON_GRID(object);
2239
2240     switch (prop_id) {
2241     case PROP_EMPTY_LABEL:
2242         g_value_set_string(value, hildon_grid_get_empty_label(grid));
2243         break;
2244
2245     case PROP_STYLE:
2246         g_value_set_string(value, hildon_grid_get_style(grid));
2247         break;
2248
2249     case PROP_SCROLLBAR_POS:
2250         g_value_set_int(value, hildon_grid_get_scrollbar_pos(grid));
2251         break;
2252
2253     default:
2254         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2255         break;
2256     }
2257 }
2258
2259 static gboolean
2260 hildon_grid_state_changed(GtkWidget * widget,
2261                           GtkStateType state, gpointer data)
2262 {
2263     HildonGrid *grid;
2264     HildonGridPrivate *priv;
2265     GList *list;
2266     GtkWidget *current;
2267     GtkWidget *prev_focusable, *next_focusable;
2268     gboolean found_old;
2269
2270     g_return_val_if_fail(HILDON_IS_GRID(data), FALSE);
2271     g_return_val_if_fail(HILDON_IS_GRID_ITEM(widget), FALSE);
2272
2273     grid = HILDON_GRID(data);
2274     priv = HILDON_GRID_GET_PRIVATE(grid);
2275
2276
2277     if (GTK_WIDGET_IS_SENSITIVE(widget))
2278         return FALSE;
2279
2280     prev_focusable = next_focusable = NULL;
2281     found_old = FALSE;
2282
2283     for (list = priv->children; list != NULL; list = list->next) {
2284         current = ((HildonGridChild *) list->data)->widget;
2285
2286         if (GTK_WIDGET_IS_SENSITIVE(current)) {
2287             if (found_old) {
2288                 next_focusable = current;
2289                 break;
2290             } else {
2291                 prev_focusable = current;
2292             }
2293         } else if (current == widget) {
2294             found_old = TRUE;
2295         }
2296     }
2297
2298     if (next_focusable == NULL) {
2299         next_focusable = prev_focusable;
2300     }
2301
2302     gtk_container_set_focus_child(GTK_CONTAINER(grid), next_focusable);
2303
2304     return FALSE;
2305 }
2306
2307
2308
2309 static void
2310 hildon_grid_tap_and_hold_setup(GtkWidget * widget,
2311                                GtkWidget * menu,
2312                                GtkCallback func,
2313                                GtkWidgetTapAndHoldFlags flags)
2314 {
2315     g_return_if_fail(HILDON_IS_GRID(widget) && GTK_IS_MENU(menu));
2316
2317     parent_class->parent_class.tap_and_hold_setup
2318         (widget, menu, func, flags | GTK_TAP_AND_HOLD_NO_INTERNALS);
2319 }