* doc/tmpl/* * hildon-widgets/* moved widget descriptions to respective source file...
[hildon] / hildon-widgets / hildon-color-selector.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-color-selector
27  * @short_description: A widget for selecting a color
28  * @see_also: #HildonColorButton, #HildonColorPopup
29  * 
30  * #HildonColorSelector allows selection of a color from a standard
31  * 16-color palette or a palette of 8 user-customizable colors.
32  * The user-customizable colors can be modified through HildonColorPopup.
33  */
34
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 #include <gtk/gtktable.h>
41 #include <gtk/gtkhbox.h>
42 #include <gtk/gtklabel.h>
43 #include <gtk/gtkdrawingarea.h>
44 #include <gtk/gtkbutton.h>
45 #include <gdk/gdkkeysyms.h>
46 #include <gconf/gconf-client.h> 
47
48 #include "hildon-color-selector.h"
49 #include "hildon-color-popup.h"
50
51 #include <libintl.h>
52 #define _(String) dgettext(PACKAGE, String)
53
54 /* Color amounts */
55 #define HILDON_BASE_COLOR_NUM             16
56 #define HILDON_CUSTOM_COLOR_NUM            8
57 #define HILDON_TOTAL_COLOR_NUM (HILDON_BASE_COLOR_NUM + HILDON_CUSTOM_COLOR_NUM)
58 #define BLACKIND                           0
59 #define GREYIND                            6
60 #define WHITEIND                           9
61
62 #define SELECTION_BORDER_COLOR_NAME        "ImageBorderColor"
63 #define FOCUS_BORDER_COLOR                 "#8080FF"
64 #define FOCUS_BORDER_WIDTH                 2
65
66 /* Pixel sizes */
67 #define HILDON_COLOR_SELECTOR_BOX_W           26
68 #define HILDON_COLOR_SELECTOR_BOX_H           26
69 #define HILDON_COLOR_SELECTOR_BORDER_WIDTH     1
70 #define HILDON_COLOR_SELECTOR_PADDING_WIDTH    1
71
72 #define HILDON_COLOR_SELECTOR_BOX_FULL_WIDTH    \
73       ( HILDON_COLOR_SELECTOR_BOX_W             \
74       + HILDON_COLOR_SELECTOR_BORDER_WIDTH  * 2 \
75       + HILDON_COLOR_SELECTOR_PADDING_WIDTH * 2 )
76
77 #define HILDON_COLOR_SELECTOR_BOX_FULL_HEIGHT   \
78       ( HILDON_COLOR_SELECTOR_BOX_H             \
79       + HILDON_COLOR_SELECTOR_BORDER_WIDTH  * 2 \
80       + HILDON_COLOR_SELECTOR_PADDING_WIDTH * 2 )
81
82 #define HILDON_COLOR_SELECTOR_COLS             8
83 #define HILDON_COLOR_SELECTOR_ROWS             3
84
85 /* gconf definitions */
86 #define HILDON_COLOR_GCONF_PATH  "/system/osso/af/color_selector"
87 #define HILDON_COLOR_GCONF_KEYS  "/system/osso/af/color_selector/custom_colors"
88
89 /* Pointer parent class */
90 static GtkDialogClass *parent_class;
91
92 struct _HildonColorSelectorPriv 
93 {
94     GConfClient *client;
95     GtkWidget   *drawing_area;
96     GtkWidget   *modify_button;
97     gint         selected_index;
98     gint         focused_index;
99     guint        notify_id;
100     /* one extra place for the modified base color */
101     GdkColor color[HILDON_TOTAL_COLOR_NUM + 1];
102 };
103
104 enum 
105 {
106   PROP_NONE,
107   PROP_COLOR
108 };
109
110 /* 
111  * Private function prototype definitions 
112  */
113 static void
114 hildon_color_selector_class_init (HildonColorSelectorClass * selector_class);
115
116 static void 
117 hildon_color_selector_init (HildonColorSelector * selector);
118
119 static gboolean
120 hildon_color_selector_expose  (GtkWidget      * widget,
121                                GdkEventExpose * event, 
122                                gpointer         data);
123
124 static gboolean key_pressed   (GtkWidget      * widget,
125                                GdkEventKey    * event);
126
127 static gboolean color_pressed (GtkWidget      * widget,
128                                GdkEventButton * event,
129                                gpointer         user_data);
130
131 static void     select_color_index (HildonColorSelector *selector,
132                                     gint                 idx,
133                                     gboolean             motion);
134
135 static void     select_color  (HildonColorSelector * selector,
136                                int      event_x,
137                                int      event_y,
138                                gboolean motion);
139
140 static gboolean color_moved   (GtkWidget      * widget,
141                                GdkEventMotion * event,
142                                gpointer         data);
143
144 static void
145 modify_button_clicked         (GtkWidget           * button,
146                                HildonColorSelector * selector);
147
148 static void modify_focused(HildonColorSelector * colselector);
149
150 static void
151 hildon_color_selector_set_property(GObject      *object,
152                                    guint         param_id,
153                                    const GValue *value,
154                                    GParamSpec   *pspec);
155 static void
156 hildon_color_selector_get_property(GObject    *object,
157                                    guint       param_id,
158                                    GValue     *value,
159                                    GParamSpec *pspec);
160
161 GType
162 hildon_color_selector_get_type(void)
163 {
164     static GType selector_type = 0;
165
166     if (!selector_type) 
167     {
168         static const GTypeInfo selector_info = 
169             {
170                 sizeof(HildonColorSelectorClass),
171                 NULL,       /* base_init      */
172                 NULL,       /* base_finalize  */
173                 (GClassInitFunc) hildon_color_selector_class_init,
174                 NULL,       /* class_finalize */
175                 NULL,       /* class_data     */
176                 sizeof(HildonColorSelector),
177                 0,          /* n_preallocs    */
178                 (GInstanceInitFunc) hildon_color_selector_init,
179             };
180
181         selector_type = g_type_register_static(GTK_TYPE_DIALOG,
182                                                "HildonColorSelector",
183                                                &selector_info, 0);
184     }
185     return selector_type;
186 }
187
188 static void
189 hildon_color_selector_destroy(GtkObject *obj)
190 {
191   HildonColorSelectorPriv *priv = HILDON_COLOR_SELECTOR(obj)->priv;
192
193   if (priv->client)
194   {
195     gconf_client_notify_remove(priv->client, priv->notify_id);
196     g_object_unref(priv->client);
197     priv->client = NULL;
198   }
199
200   GTK_OBJECT_CLASS(parent_class)->destroy(obj);
201 }
202
203 static void
204 hildon_color_selector_class_init(HildonColorSelectorClass * selector_class)
205 {
206     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(selector_class);
207     GObjectClass *gobject_class = G_OBJECT_CLASS (selector_class);
208
209     parent_class = g_type_class_peek_parent(selector_class);
210
211     widget_class->key_press_event = key_pressed;
212
213     g_type_class_add_private(selector_class,
214                              sizeof(HildonColorSelectorPriv));
215
216     GTK_OBJECT_CLASS(selector_class)->destroy = hildon_color_selector_destroy;
217   
218     gobject_class->get_property = hildon_color_selector_get_property;
219     gobject_class->set_property = hildon_color_selector_set_property;
220   
221     /**
222      * HildonColorSelector:color:
223      *
224      * The selected color.
225      */
226     g_object_class_install_property (gobject_class, PROP_COLOR,
227             g_param_spec_boxed ("color",
228                 "Current Color",
229                 "The selected color",
230                 GDK_TYPE_COLOR,
231                 G_PARAM_READWRITE));
232 }
233
234
235 /**
236  * hildon_color_selector_new:
237  * @parent:  the parent window. The X window ID of the parent window
238  *           has to be the same as the X window ID of the application
239  *
240  * Creates a new #HildonColorSelector dialog with 3x8 layout of 
241  * base colors and 'OK', 'More..' and 'Cancel' buttons.
242  *
243  * Returns: new #HildonColorSelector
244  **/
245 GtkWidget *hildon_color_selector_new(GtkWindow * parent)
246 {
247     GtkWidget *dialog = g_object_new(HILDON_TYPE_COLOR_SELECTOR, NULL);
248
249     g_return_val_if_fail(dialog, NULL);
250
251     if (parent) 
252     {
253         gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
254     }
255
256     return dialog;
257 }
258
259 static void
260 hildon_color_selector_set_custom_colors(
261   HildonColorSelector *selector,
262   GConfValue *value)
263 {
264   GSList *list;
265   gint i;
266
267   g_assert(HILDON_IS_COLOR_SELECTOR(selector));
268
269   /* We have to be really careful. At least gconftool's
270      stress test may generate unexpected value setups */
271   if (value == NULL
272       || value->type != GCONF_VALUE_LIST
273       || gconf_value_get_list_type(value) != GCONF_VALUE_STRING)
274     list = NULL;
275   else
276     list = gconf_value_get_list(value);
277
278   /* Use list to fill in the selector's color property */
279   for ( i = 0; i < HILDON_CUSTOM_COLOR_NUM; ++i)
280   {
281     const gchar *color_string = NULL;
282
283     if (list) {
284       color_string = gconf_value_get_string(list->data);
285       list = list->next;
286     } else {
287       color_string = "#FFFFFF";
288     }
289
290 /*    g_print("custom_color: %s\n", color_string);  */
291
292     selector->priv->color[i].pixel = 0;
293     gdk_color_parse (color_string, 
294        &(selector->priv->color[HILDON_BASE_COLOR_NUM+i]));
295   }
296 }
297
298 static void
299 gconf_notify_func(GConfClient *client,
300                              guint cnxn_id,
301                              GConfEntry *entry,
302                              gpointer data)
303 {
304   hildon_color_selector_set_custom_colors(HILDON_COLOR_SELECTOR(data), 
305     gconf_entry_get_value(entry));
306   gtk_widget_queue_draw(GTK_WIDGET(data));
307 }
308
309 static void 
310 hildon_color_selector_init(HildonColorSelector * selector)
311 {
312     guint i;
313     GtkWidget *hbox;
314     GConfValue *value;
315
316     /* 16 standard Windows colors */
317     static char *base_colours[HILDON_BASE_COLOR_NUM] = {
318         "#000000", "#FFFFFF", "#FF0000", "#660000", "#0000FF", "#000066",
319         "#FF33FF", "#660066", "#33CC33", "#006600", "#FFCC00", "#CC9900",
320         "#999999", "#666666", "#00CCCC", "#006666"
321     };
322
323     selector->priv =
324         G_TYPE_INSTANCE_GET_PRIVATE(selector,
325                                     HILDON_TYPE_COLOR_SELECTOR,
326                                     HildonColorSelectorPriv);
327     
328     /*  ***********test GConf***********   */
329     selector->priv->client = gconf_client_get_default ();
330     gconf_client_set_error_handling (selector->priv->client, 
331       GCONF_CLIENT_HANDLE_UNRETURNED);
332     
333     /* Add our directory to the list of directories the GConfClient will
334        watch. */
335     gconf_client_add_dir (selector->priv->client, HILDON_COLOR_GCONF_PATH,
336                               GCONF_CLIENT_PRELOAD_NONE,
337                               NULL);
338     
339     /* Use the value directed by GConfValue to set the color */
340     value = gconf_client_get(selector->priv->client, 
341       HILDON_COLOR_GCONF_KEYS, NULL);    
342
343     hildon_color_selector_set_custom_colors(selector, value);    
344
345     if (value) {
346       gconf_value_free(value);
347     }
348     
349     /* Listen to changes to our key. */
350     selector->priv->notify_id = gconf_client_notify_add (selector->priv->client, 
351         HILDON_COLOR_GCONF_KEYS, gconf_notify_func, selector, NULL, NULL); 
352
353     /* ************************************  */
354
355     selector->priv->selected_index = GREYIND;
356     selector->priv->focused_index  = GREYIND;
357     
358     /* init base colors for color boxes */
359     for (i = 0; i < HILDON_BASE_COLOR_NUM; ++i) 
360     {
361         selector->priv->color[i].pixel = 0;
362         gdk_color_parse (base_colours[i], &(selector->priv->color[i]));
363     }
364     
365     gtk_dialog_set_has_separator(GTK_DIALOG(selector), FALSE);
366
367     /* create drawing area */
368     hbox = gtk_hbox_new(TRUE, 0);
369     selector->priv->drawing_area = gtk_drawing_area_new();
370     
371     /* receive focus from dialog buttons */
372     GTK_WIDGET_SET_FLAGS (selector->priv->drawing_area, GTK_CAN_FOCUS);
373
374     gtk_widget_add_events (selector->priv->drawing_area,
375                           GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
376
377     /* Arrange size of the drawing area. */
378     gtk_widget_set_size_request (selector->priv->drawing_area,
379                                  (HILDON_COLOR_SELECTOR_COLS *
380                                   HILDON_COLOR_SELECTOR_BOX_FULL_WIDTH),
381                                  (HILDON_COLOR_SELECTOR_ROWS *
382                                   HILDON_COLOR_SELECTOR_BOX_FULL_HEIGHT));
383
384     gtk_box_pack_start (GTK_BOX(GTK_DIALOG(selector)->vbox), 
385                        hbox, FALSE, FALSE, 0);
386     gtk_box_pack_start (GTK_BOX(hbox), selector->priv->drawing_area,
387                        FALSE, FALSE, 0);
388
389     /* Register callback functions for the drawing area */
390     g_signal_connect (selector->priv->drawing_area, "expose_event",
391                       G_CALLBACK(hildon_color_selector_expose), selector);
392     g_signal_connect (selector->priv->drawing_area, "button_press_event", 
393                       G_CALLBACK(color_pressed), selector);
394     g_signal_connect (selector->priv->drawing_area, "motion-notify-event", 
395                       G_CALLBACK(color_moved), selector);
396
397     gtk_dialog_add_button (GTK_DIALOG(selector), 
398                            _("ecdg_bd_colour_selector_ok"), GTK_RESPONSE_OK);
399
400     selector->priv->modify_button =
401         gtk_button_new_with_label(_("ecdg_bd_colour_selector_modify"));
402     gtk_widget_set_sensitive(selector->priv->modify_button, FALSE);
403     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(selector)->action_area),
404                        selector->priv->modify_button, FALSE, TRUE, 0);
405
406     g_signal_connect(selector->priv->modify_button, "clicked",
407                      G_CALLBACK(modify_button_clicked), selector);
408
409     gtk_dialog_add_button (GTK_DIALOG(selector),
410                            _("ecdg_bd_colour_selector_cancel"),
411                            GTK_RESPONSE_CANCEL);
412
413     gtk_dialog_set_default_response (GTK_DIALOG(selector), GTK_RESPONSE_OK);
414     
415     gtk_window_set_title ( GTK_WINDOW(selector),  
416                           _("ecdg_ti_colour_selector") );
417     gtk_widget_show_all (GTK_DIALOG(selector)->vbox);
418 }
419
420 static gboolean 
421 hildon_color_selector_expose(GtkWidget * widget,
422                              GdkEventExpose * event,
423                              gpointer data)
424 {
425     HildonColorSelector *selector;
426     GdkColor color;
427     GdkGC *gc, *gc_focus;
428     gint x, y, idx;
429
430     g_return_val_if_fail (GTK_IS_WIDGET(widget), FALSE);
431     g_return_val_if_fail (event, FALSE);
432     g_return_val_if_fail (HILDON_IS_COLOR_SELECTOR(data), FALSE);
433
434     if (!GTK_WIDGET_DRAWABLE(widget))
435       return FALSE;
436
437     selector = HILDON_COLOR_SELECTOR(data);
438     gc = gdk_gc_new (widget->window); 
439
440     gc_focus = gdk_gc_new(widget->window);
441     gdk_gc_set_line_attributes(gc_focus, FOCUS_BORDER_WIDTH,
442                                GDK_LINE_SOLID, GDK_CAP_BUTT,
443                                GDK_JOIN_MITER);
444
445     /* draw the color boxes and a focus for one of them */
446     for (y = 0, idx = 0; y < HILDON_COLOR_SELECTOR_ROWS; ++y)
447     {
448       for (x = 0; x < HILDON_COLOR_SELECTOR_COLS; ++x, ++idx)
449         {
450             gint xpos = x * HILDON_COLOR_SELECTOR_BOX_FULL_WIDTH;
451             gint ypos = y * HILDON_COLOR_SELECTOR_BOX_FULL_HEIGHT;
452
453             /* frames on color box */
454             gdk_draw_rectangle(widget->window, widget->style->black_gc, FALSE,
455                                xpos + HILDON_COLOR_SELECTOR_PADDING_WIDTH,
456                                ypos + HILDON_COLOR_SELECTOR_PADDING_WIDTH,
457                                HILDON_COLOR_SELECTOR_BOX_W +
458                                HILDON_COLOR_SELECTOR_PADDING_WIDTH,
459                                HILDON_COLOR_SELECTOR_BOX_H +
460                                HILDON_COLOR_SELECTOR_PADDING_WIDTH);
461             
462             /* color box */
463             gdk_gc_set_rgb_fg_color(gc, &(selector->priv->color[idx]));
464             gdk_draw_rectangle(widget->window, gc,
465                                TRUE,    /* filled */
466                                xpos + HILDON_COLOR_SELECTOR_PADDING_WIDTH +
467                                HILDON_COLOR_SELECTOR_BORDER_WIDTH,
468                                ypos + HILDON_COLOR_SELECTOR_PADDING_WIDTH +
469                                HILDON_COLOR_SELECTOR_BORDER_WIDTH,
470                                HILDON_COLOR_SELECTOR_BOX_W,
471                                HILDON_COLOR_SELECTOR_BOX_H);
472
473             /* focus around the selected and focused color box */
474             if (idx == selector->priv->selected_index)
475             {
476                 /* selected color box */
477                 if (!gtk_style_lookup_logical_color(widget->style,
478                                     SELECTION_BORDER_COLOR_NAME, &color))
479                 {
480                     /* set default color if color looking up fails */
481                     color.red = color.green = color.blue = 0;
482                 }
483
484                 gdk_gc_set_rgb_fg_color(gc_focus, &color);
485                 gdk_draw_rectangle(widget->window, gc_focus, FALSE,
486                                    xpos + 1-(FOCUS_BORDER_WIDTH % 2),
487                                    ypos + 1-(FOCUS_BORDER_WIDTH % 2),
488                                    HILDON_COLOR_SELECTOR_BOX_W +
489                                    HILDON_COLOR_SELECTOR_BORDER_WIDTH * 2 +
490                                    (FOCUS_BORDER_WIDTH % 2),
491                                    HILDON_COLOR_SELECTOR_BOX_H +
492                                    HILDON_COLOR_SELECTOR_BORDER_WIDTH * 2 +
493                                    (FOCUS_BORDER_WIDTH % 2));
494             }
495             else if (idx == selector->priv->focused_index)
496             {
497                 /* focused color box.
498                    There's only zero or one so allocate GC in here. */
499                 gdk_color_parse(FOCUS_BORDER_COLOR, &color);
500
501                 gdk_gc_set_rgb_fg_color(gc_focus, &color);
502                 gdk_draw_rectangle(widget->window, gc_focus, FALSE,
503                                    xpos + 1-(FOCUS_BORDER_WIDTH % 2),
504                                    ypos + 1-(FOCUS_BORDER_WIDTH % 2),
505                                    HILDON_COLOR_SELECTOR_BOX_W +
506                                    HILDON_COLOR_SELECTOR_BORDER_WIDTH * 2 +
507                                    (FOCUS_BORDER_WIDTH % 2),
508                                    HILDON_COLOR_SELECTOR_BOX_H +
509                                    HILDON_COLOR_SELECTOR_BORDER_WIDTH * 2 +
510                                    (FOCUS_BORDER_WIDTH % 2));
511             }
512         }
513     }
514
515     g_object_unref(gc);
516     g_object_unref(gc_focus);
517     return TRUE;
518 }
519
520 /**
521  * hildon_color_selector_get_color:
522  * @selector: a #HildonColorSelector
523  * 
524  * Returns: the currently selected #GdkColor. The returned pointer must 
525  * not be freed.
526  */
527 G_CONST_RETURN GdkColor *hildon_color_selector_get_color(HildonColorSelector * selector)
528 {
529     g_return_val_if_fail(HILDON_IS_COLOR_SELECTOR(selector), NULL);    
530     return &(selector->priv->color[selector->priv->selected_index]);
531 }
532
533 /**
534  * hildon_color_selector_set_color:
535  * @selector: #HildonColorSelector
536  * @color: #Gdkcolor to set
537  * 
538  * Selects the color specified. Does nothing if the color does not 
539  * exist among the standard colors.
540  */
541 void hildon_color_selector_set_color(HildonColorSelector * selector,
542                                      GdkColor * color)
543 {
544     gint i;
545
546     g_return_if_fail(HILDON_IS_COLOR_SELECTOR(selector));
547     g_return_if_fail(color);
548
549     /* Select the specified color */
550     for (i = 0; 
551          i < (HILDON_BASE_COLOR_NUM + HILDON_CUSTOM_COLOR_NUM);
552          ++i) 
553     {
554         if (selector->priv->color[i].red   == color->red &&
555             selector->priv->color[i].green == color->green &&
556             selector->priv->color[i].blue  == color->blue) 
557         {
558             selector->priv->selected_index = i;
559             selector->priv->focused_index  = i;
560
561             /* The modify button is active if the color index is bigger than
562              * the number of base colours.
563              */
564             gtk_widget_set_sensitive(selector->priv->modify_button,
565                                      selector->priv->focused_index >= HILDON_BASE_COLOR_NUM);
566             gtk_widget_queue_draw(selector->priv->drawing_area);
567             break;
568         }
569     }
570
571     /* Notify the color selector that the color has changed */  
572     g_object_notify (G_OBJECT (selector), "color");
573 }
574
575 static gboolean
576 color_pressed(GtkWidget * widget, GdkEventButton * event,
577               gpointer user_data)
578 {
579     select_color(HILDON_COLOR_SELECTOR(user_data), event->x, event->y, FALSE);
580     return TRUE;
581 }
582
583 /* Handle key press of right, left, up, down and return */
584 static gboolean key_pressed(GtkWidget * widget,
585                             GdkEventKey * event)
586 {
587     HildonColorSelector *selector;
588     gint index;
589
590     g_assert(widget);
591
592     selector = HILDON_COLOR_SELECTOR(widget);
593     index = selector->priv->focused_index;
594
595     /* if dialog buttons has the focus */
596     if (GTK_WIDGET_HAS_FOCUS(selector->priv->drawing_area) == FALSE)
597         return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
598
599     /* Since wrapping is not allowed,
600      * move only if the next index is a valid one.
601      */
602     switch (event->keyval) {
603     case GDK_KP_Right:
604     case GDK_Right:
605         if (index == (HILDON_BASE_COLOR_NUM + HILDON_CUSTOM_COLOR_NUM - 1)
606             || index == (HILDON_CUSTOM_COLOR_NUM - 1)
607             || index == (2*HILDON_CUSTOM_COLOR_NUM - 1)) 
608         {
609             return TRUE;
610         }
611         index++;
612         break;
613     case GDK_KP_Left:
614     case GDK_Left:
615         if (index == 0 
616             || index == (HILDON_CUSTOM_COLOR_NUM)
617             || index == (2*HILDON_CUSTOM_COLOR_NUM)) 
618         {
619             return TRUE;
620         }
621         index--;
622         break;
623     case GDK_KP_Up:
624     case GDK_Up:
625         if (index > (HILDON_COLOR_SELECTOR_COLS - 1)) 
626         {
627             index -= HILDON_COLOR_SELECTOR_COLS;
628         } 
629         else 
630         {
631             return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
632         }
633         break;
634     case GDK_KP_Down:
635     case GDK_Down:
636         if (index < (HILDON_COLOR_SELECTOR_COLS + HILDON_CUSTOM_COLOR_NUM)) 
637         {
638             index += HILDON_COLOR_SELECTOR_COLS;  
639         } 
640         else 
641         {
642             return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
643         }
644         break;
645     case GDK_KP_Enter:
646     case GDK_Return:
647         if (index < HILDON_BASE_COLOR_NUM)
648             select_color_index(selector, selector->priv->focused_index, FALSE);
649         else
650             modify_focused(selector);
651     default:
652         return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
653     }
654
655     if (index < (HILDON_BASE_COLOR_NUM + HILDON_CUSTOM_COLOR_NUM)) 
656     {
657         selector->priv->focused_index = index;
658     } 
659     else 
660     {
661         selector->priv->focused_index =
662             HILDON_BASE_COLOR_NUM + HILDON_CUSTOM_COLOR_NUM - 1;
663     }
664     /* The modify button is active if the color index is bigger than
665      * the number of base colours.
666      */
667     gtk_widget_set_sensitive(selector->priv->modify_button,
668                              selector->priv->focused_index >= HILDON_BASE_COLOR_NUM);
669
670     gtk_widget_queue_draw(selector->priv->drawing_area);
671     
672     return TRUE;
673 }
674
675 static void
676 select_color_index(HildonColorSelector * selector, gint idx, gboolean motion)
677 {
678     /* If a custom colour is selected, open a popup to modify the colour */ 
679     if (!motion &&
680         selector->priv->focused_index >= HILDON_BASE_COLOR_NUM &&
681         selector->priv->focused_index == idx)
682         modify_focused(selector);
683
684     selector->priv->focused_index = selector->priv->selected_index = idx;
685     gtk_widget_set_sensitive(selector->priv->modify_button,
686                              selector->priv->focused_index >= HILDON_BASE_COLOR_NUM);
687     
688     gtk_widget_queue_draw(selector->priv->drawing_area);
689 }
690
691 static void
692 select_color(HildonColorSelector * selector, gint event_x, gint event_y,
693              gboolean motion)
694 {
695     gint x, y;
696
697     g_assert(HILDON_IS_COLOR_SELECTOR(selector));
698
699     /* Get the selection coordinates */ 
700     x = event_x / HILDON_COLOR_SELECTOR_BOX_FULL_WIDTH;
701     y = event_y / HILDON_COLOR_SELECTOR_BOX_FULL_HEIGHT;
702
703     /* Get the row and column numbers for the selected color */ 
704     if (x > (HILDON_COLOR_SELECTOR_COLS + HILDON_CUSTOM_COLOR_NUM - 1)) 
705     {
706         x = HILDON_COLOR_SELECTOR_COLS + HILDON_CUSTOM_COLOR_NUM - 1;
707     } 
708     else if (x < 0) 
709     {
710         x = 0;
711     }
712     if (y > (HILDON_COLOR_SELECTOR_ROWS - 1)) 
713     {
714         y = HILDON_COLOR_SELECTOR_ROWS - 1;
715     } 
716     else if (y < 0) 
717     {
718         y = 0;
719     }
720
721     select_color_index(selector, (x + y * HILDON_COLOR_SELECTOR_COLS), motion);
722 }
723
724 static gboolean
725 color_moved(GtkWidget * widget, GdkEventMotion * event, gpointer data)
726 {
727     if ( event->state &
728          (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK) ) 
729     {
730         select_color(HILDON_COLOR_SELECTOR(data), event->x, event->y, TRUE);
731         return TRUE;
732     }
733     return FALSE;
734 }
735
736 static void
737 modify_button_clicked(GtkWidget * button, HildonColorSelector * selector)
738 {
739     modify_focused (selector);
740 }
741
742 static void
743 modify_focused(HildonColorSelector * colselector)
744 {
745     HildonColorPopup popupdata;
746     GtkWidget *popup;
747     
748     /* Create a HildonColorPopup dialog */     
749     popup = hildon_color_popup_new(GTK_WINDOW(colselector), 
750                     hildon_color_selector_get_color(colselector), &popupdata);
751     
752     if ( gtk_dialog_run(GTK_DIALOG(popup) ) == GTK_RESPONSE_OK) 
753     {        
754       GdkColor *color;
755
756       /* We cannot modify a base color */
757       if (colselector->priv->focused_index < HILDON_BASE_COLOR_NUM)
758       {
759         colselector->priv->color[HILDON_TOTAL_COLOR_NUM] = 
760           colselector->priv->color[colselector->priv->focused_index];
761         colselector->priv->focused_index = HILDON_TOTAL_COLOR_NUM;
762       }
763
764       /* Update the focused color with the color selected in color popup */
765       color = &(colselector->priv->color[colselector->priv->focused_index]);
766       hildon_color_popup_set_color_from_sliders(color, &popupdata);
767
768       /* If we modified a base color we just accept the dialog */      
769       if( colselector->priv->focused_index >=  HILDON_TOTAL_COLOR_NUM)
770       {          
771         gtk_dialog_response(GTK_DIALOG(colselector), GTK_RESPONSE_OK); 
772       }
773       else /* If we mofied custom colors we have to save to gconf */
774       {
775         GConfValue *value;
776         GSList * list;
777         gint i;  
778
779         value = gconf_value_new(GCONF_VALUE_LIST);
780         gconf_value_set_list_type(value, GCONF_VALUE_STRING);
781         list = NULL;
782
783         for ( i = HILDON_BASE_COLOR_NUM; i < HILDON_TOTAL_COLOR_NUM; i++) 
784         {
785             GConfValue *item;
786             char buffer[32];
787                   
788             g_snprintf(buffer, sizeof(buffer), "#%.2X%.2X%.2X",
789                             (colselector->priv->color[i].red>>8)&0xFF,
790                             (colselector->priv->color[i].green>>8)&0xFF,
791                             (colselector->priv->color[i].blue>>8)&0xFF ); 
792   
793             item = gconf_value_new(GCONF_VALUE_STRING);
794             gconf_value_set_string(item, buffer);
795             list = g_slist_append (list, item);
796         } 
797         
798         gconf_value_set_list_nocopy(value, list);
799
800         /* gconf client handles the possible error */
801         gconf_client_set(colselector->priv->client, 
802           HILDON_COLOR_GCONF_KEYS, value, NULL);
803     
804         gconf_value_free(value);
805       }  
806     }
807
808     gtk_widget_destroy (popup); 
809     gtk_window_present (GTK_WINDOW(colselector));
810 }
811
812 static void
813 hildon_color_selector_set_property(GObject *object, guint param_id,
814                                                    const GValue *value, GParamSpec *pspec)
815 {
816   HildonColorSelector *selector = HILDON_COLOR_SELECTOR(object);
817
818   switch (param_id) 
819     {
820     case PROP_COLOR:
821       hildon_color_selector_set_color(selector, (GdkColor*)g_value_get_boxed(value));
822       break;
823     default:
824       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
825       break;
826     }
827 }
828
829 static void
830 hildon_color_selector_get_property(GObject *object, guint param_id,
831                                                    GValue *value, GParamSpec *pspec)
832 {
833   HildonColorSelector *selector = HILDON_COLOR_SELECTOR(object);
834
835   switch (param_id) 
836     {
837     case PROP_COLOR:
838       g_value_set_boxed(value, (gconstpointer)hildon_color_selector_get_color(selector));
839       break;
840     default:
841       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
842       break;
843     }
844 }
845