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