2008-09-15 Alberto Garcia <agarcia@igalia.com>
[hildon] / src / hildon-color-chooser.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Author: Kuisma Salonen <kuisma.salonen@nokia.com>
7  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  *
24  */
25
26 /**
27  * SECTION:hildon-color-chooser
28  * @short_description: A widget used to select a color from an HSV colorspace.
29  * @see_also: #HildonColorChooserDialog
30  *
31  * HildonColorChooser is a widget that displays an HSV colorspace. 
32  * The user can manipulate the colorspace and easily select and shade of any color
33  * he wants. 
34  *
35  * Normally you should not need to use this widget directly. Using #HildonColorButton or
36  * #HildonColorChooserDialog is much more handy. 
37  * 
38  */
39
40 #include                                        "hildon-color-chooser.h"
41 #include                                        "hildon-color-chooser-private.h"
42
43 static GtkWidgetClass*                          parent_class = NULL;
44
45 /* "crosshair" is hardcoded for now */
46 static gchar crosshair[64]                      = { 0, 0, 0, 2, 2, 0, 0, 0,
47                                                     0, 2, 2, 3, 3, 2, 2, 0,
48                                                     0, 2, 3, 0, 0, 3, 2, 0,
49                                                     2, 3, 0, 0, 0, 0, 3, 2,
50                                                     2, 3, 0, 0, 0, 0, 3, 2,
51                                                     0, 2, 3, 0, 0, 3, 2, 0,
52                                                     0, 2, 2, 3, 3, 2, 2, 0,
53                                                     0, 0, 0, 2, 2, 0, 0, 0};
54
55 static void 
56 hildon_color_chooser_init                       (HildonColorChooser *self);
57
58 static void 
59 hildon_color_chooser_class_init                 (HildonColorChooserClass *klass);
60
61 static void 
62 hildon_color_chooser_dispose                    (HildonColorChooser *self);
63
64 static void 
65 hildon_color_chooser_size_request               (GtkWidget *widget, 
66                                                  GtkRequisition *req);
67
68 static void 
69 hildon_color_chooser_size_allocate              (GtkWidget *widget, 
70                                                  GtkAllocation *alloc);
71
72 static void
73 hildon_color_chooser_realize                    (GtkWidget *widget);
74
75 static void
76 hildon_color_chooser_unrealize                  (GtkWidget *widget);
77
78 static void 
79 hildon_color_chooser_map                        (GtkWidget *widget);
80
81 static void 
82 hildon_color_chooser_unmap                      (GtkWidget *widget);
83
84 static gboolean 
85 hildon_color_chooser_expose                     (GtkWidget *widget, 
86                                                  GdkEventExpose *event);
87
88 static gboolean
89 hildon_color_chooser_button_press               (GtkWidget *widget, 
90                                                  GdkEventButton *event);
91
92 static gboolean
93 hildon_color_chooser_button_release             (GtkWidget *widget, 
94                                                  GdkEventButton *event);
95
96 static gboolean 
97 hildon_color_chooser_pointer_motion             (GtkWidget *widget, 
98                                                  GdkEventMotion *event);
99
100 static void 
101 get_border                                      (GtkWidget *w, 
102                                                  char *name, 
103                                                  GtkBorder *b);
104
105 static void 
106 init_borders                                    (GtkWidget *w, 
107                                                  GtkBorder *inner, 
108                                                  GtkBorder *outer);
109
110 inline void 
111 inline_clip_to_alloc                            (void *s, 
112                                                  GtkAllocation *a);
113
114 inline void 
115 inline_sub_times                                (GTimeVal *result, 
116                                                  GTimeVal *greater, 
117                                                  GTimeVal *lesser);
118
119 inline void 
120 inline_limited_expose                           (HildonColorChooser *self);
121
122 inline void 
123 inline_draw_hue_bar                             (GtkWidget *widget, 
124                                                  int x, 
125                                                  int y, 
126                                                  int w, 
127                                                  int h, 
128                                                  int sy, 
129                                                  int sh);
130
131 inline void
132 inline_draw_hue_bar_dimmed                      (GtkWidget *widget, 
133                                                  int x, 
134                                                  int y, 
135                                                  int w, 
136                                                  int h, 
137                                                  int sy, 
138                                                  int sh);
139
140 inline void 
141 inline_draw_sv_plane                            (HildonColorChooser *self, 
142                                                  int x, 
143                                                  int y, 
144                                                  int w, 
145                                                  int h);
146
147 inline void 
148 inline_draw_sv_plane_dimmed                     (HildonColorChooser *self, 
149                                                  int x, 
150                                                  int y, 
151                                                  int w, 
152                                                  int h);
153
154 inline void 
155 inline_draw_crosshair                           (unsigned char *buf, 
156                                                  int x, 
157                                                  int y, 
158                                                  int w, 
159                                                  int h);
160
161 inline void 
162 inline_h2rgb                                    (unsigned short hue, 
163                                                  unsigned long *rgb);
164
165 static gboolean
166 hildon_color_chooser_expose_timer               (gpointer data);
167
168 static void
169 hildon_color_chooser_set_property               (GObject *object, 
170                                                  guint param_id,
171                                                  const GValue *value, 
172                                                  GParamSpec *pspec);
173
174 static void
175 hildon_color_chooser_get_property               (GObject *object, 
176                                                  guint param_id,
177                                                  GValue *value, 
178                                                  GParamSpec *pspec);
179
180 #define                                         EXPOSE_INTERVAL 50000
181
182 #define                                         FULL_COLOR8 0xff
183
184 #define                                         FULL_COLOR 0x00ffffff
185
186 enum 
187 {
188     COLOR_CHANGED,
189     LAST_SIGNAL
190 };
191
192 enum
193 {
194     PROP_0,
195     PROP_COLOR
196 };
197
198 static guint                                    color_chooser_signals [LAST_SIGNAL] = { 0 };
199
200 GType G_GNUC_CONST
201 hildon_color_chooser_get_type                   (void)
202 {
203     static GType chooser_type = 0;
204
205     if (!chooser_type) {
206         static const GTypeInfo chooser_info =
207         {
208             sizeof (HildonColorChooserClass),
209             NULL,
210             NULL,
211             (GClassInitFunc) hildon_color_chooser_class_init,
212             NULL,
213             NULL,
214             sizeof (HildonColorChooser),
215             0,
216             (GInstanceInitFunc) hildon_color_chooser_init,
217             NULL
218         };
219
220         chooser_type = g_type_register_static (GTK_TYPE_WIDGET,
221                 "HildonColorChooser",
222                 &chooser_info, 0);
223     }
224
225     return chooser_type;
226 }
227
228 static void
229 hildon_color_chooser_init                       (HildonColorChooser *sel)
230 {
231
232     GTK_WIDGET_SET_FLAGS (sel, GTK_NO_WINDOW);
233     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (sel);
234     g_assert (priv);
235
236     priv->currhue = 0;
237     priv->currsat = 0;
238     priv->currval = 0;
239
240     priv->mousestate = 0;
241     priv->mousein = FALSE;
242
243     g_get_current_time (&priv->expose_info.last_expose_time);
244
245     priv->expose_info.last_expose_hue = priv->currhue;
246     priv->expose_info.expose_queued = 0;
247
248     priv->dimmed_plane = NULL;
249     priv->dimmed_bar = NULL;
250 }
251
252 static void
253 hildon_color_chooser_class_init                 (HildonColorChooserClass *klass)
254 {
255     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
256     GObjectClass *object_class = G_OBJECT_CLASS (klass);
257
258     parent_class = g_type_class_peek_parent (klass);
259     
260     object_class->dispose               = (gpointer) hildon_color_chooser_dispose;
261     object_class->get_property          = hildon_color_chooser_get_property;
262     object_class->set_property          = hildon_color_chooser_set_property;
263
264     widget_class->size_request          = hildon_color_chooser_size_request;
265     widget_class->size_allocate         = hildon_color_chooser_size_allocate;
266     widget_class->realize               = hildon_color_chooser_realize;
267     widget_class->unrealize             = hildon_color_chooser_unrealize;
268     widget_class->map                   = hildon_color_chooser_map;
269     widget_class->unmap                 = hildon_color_chooser_unmap;
270     widget_class->expose_event          = hildon_color_chooser_expose;
271     widget_class->button_press_event    = hildon_color_chooser_button_press;
272     widget_class->button_release_event  = hildon_color_chooser_button_release;
273     widget_class->motion_notify_event   = hildon_color_chooser_pointer_motion;
274
275     gtk_widget_class_install_style_property (widget_class,
276                                              g_param_spec_boxed ("inner_size",
277                                                                  "Inner sizes",
278                                                                  "Sizes of SV plane, H bar and spacing",
279                                                                  GTK_TYPE_BORDER,
280                                                                  G_PARAM_READABLE));
281
282     gtk_widget_class_install_style_property (widget_class,
283                                              g_param_spec_boxed ("outer_border",
284                                                                  "Outer border",
285                                                                  "The outer border for the chooser",
286                                                                  GTK_TYPE_BORDER,
287                                                                  G_PARAM_READABLE));
288
289     gtk_widget_class_install_style_property (widget_class,
290                                              g_param_spec_boxed ("graphic_border",
291                                                                  "Graphical borders",
292                                                                  "Size of graphical border",
293                                                                  GTK_TYPE_BORDER,
294                                                                  G_PARAM_READABLE));
295
296     /**
297      * HildonColorChooser:color:
298      *
299      * The currently selected color.
300      */
301     g_object_class_install_property (object_class, PROP_COLOR,
302             g_param_spec_boxed ("color",
303                 "Current Color",
304                 "The selected color",
305                 GDK_TYPE_COLOR,
306                 G_PARAM_READWRITE));
307
308     color_chooser_signals[COLOR_CHANGED] = g_signal_new("color-changed", 
309             G_OBJECT_CLASS_TYPE (object_class),
310             G_SIGNAL_RUN_FIRST, 
311             G_STRUCT_OFFSET (HildonColorChooserClass, color_changed),
312             NULL, 
313             NULL, 
314             g_cclosure_marshal_VOID__VOID, 
315             G_TYPE_NONE, 
316             0);
317
318     g_type_class_add_private (klass, sizeof (HildonColorChooserPrivate));
319 }
320
321 static void
322 hildon_color_chooser_dispose                    (HildonColorChooser *sel)
323 {
324     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (sel);
325     g_assert (priv);
326
327     if (priv->dimmed_bar != NULL) {
328         g_object_unref (priv->dimmed_bar);
329         priv->dimmed_bar = NULL;
330     }
331
332     if (priv->dimmed_plane != NULL) {
333         g_object_unref (priv->dimmed_plane);
334         priv->dimmed_plane = NULL;
335     }
336
337     G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (sel));
338 }
339
340 static void
341 hildon_color_chooser_size_request               (GtkWidget *widget, 
342                                                  GtkRequisition *req)
343 {
344     GtkBorder inner, outer;
345
346     init_borders (widget, &inner, &outer);
347
348     req->width = inner.left + inner.top + inner.bottom + outer.left + outer.right;
349     req->height = inner.right + outer.top + outer.bottom;
350 }
351
352 static void 
353 hildon_color_chooser_size_allocate              (GtkWidget *widget, 
354                                                  GtkAllocation *alloc)
355 {
356     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
357     GtkBorder outer, inner;
358
359     g_assert (priv);
360
361     widget->allocation = *alloc;
362     
363     init_borders(widget, &inner, &outer);
364
365     priv->hba.height = alloc->height - outer.top - outer.bottom;
366     priv->hba.y = alloc->y + outer.top;
367     priv->hba.width = inner.top;
368     priv->hba.x = alloc->x + alloc->width - outer.right - inner.top;
369
370     priv->spa.x = alloc->x + outer.left;
371     priv->spa.y = alloc->y + outer.top;
372     priv->spa.height = alloc->height - outer.top - outer.bottom;
373     priv->spa.width = alloc->width - outer.left - outer.right - inner.top - inner.bottom;
374
375     if (GTK_WIDGET_REALIZED (widget)) {
376         gdk_window_move_resize (priv->event_window, 
377                 widget->allocation.x, 
378                 widget->allocation.y, 
379                 widget->allocation.width, 
380                 widget->allocation.height);
381     }
382 }
383
384 static void
385 hildon_color_chooser_realize                    (GtkWidget *widget)
386 {
387     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
388
389     g_assert (priv);
390     GdkWindowAttr attributes;
391     gint attributes_mask;
392
393     attributes.x = widget->allocation.x;
394     attributes.y = widget->allocation.y;
395     attributes.width = widget->allocation.width;
396     attributes.height = widget->allocation.height;
397     attributes.wclass = GDK_INPUT_ONLY;
398     attributes.window_type = GDK_WINDOW_CHILD;
399
400     attributes.event_mask = gtk_widget_get_events (widget) | 
401         GDK_BUTTON_PRESS_MASK           | 
402         GDK_BUTTON_RELEASE_MASK         | 
403         GDK_POINTER_MOTION_MASK         |
404         GDK_POINTER_MOTION_HINT_MASK    | 
405         GDK_BUTTON_MOTION_MASK          |
406         GDK_BUTTON1_MOTION_MASK;
407
408     attributes.visual = gtk_widget_get_visual (widget);
409     attributes.colormap = gtk_widget_get_colormap (widget);
410
411     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_WMCLASS;
412     priv->event_window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
413
414
415     gdk_window_set_user_data (priv->event_window, widget);
416
417     GTK_WIDGET_CLASS (parent_class)->realize (widget);
418 }
419
420 static void
421 hildon_color_chooser_unrealize                  (GtkWidget *widget)
422 {
423     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
424
425     g_assert (priv);
426
427     if (priv->event_window) {
428         gdk_window_set_user_data (priv->event_window, NULL);
429         gdk_window_destroy (priv->event_window);
430         priv->event_window = NULL;
431     }
432
433     GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
434 }
435
436 static void 
437 hildon_color_chooser_map                        (GtkWidget *widget)
438 {
439     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
440
441     g_assert (priv);
442     GTK_WIDGET_CLASS(parent_class)->map(widget);
443
444     if (priv->event_window) {
445         gdk_window_show (priv->event_window);
446     }
447 }
448
449 static void 
450 hildon_color_chooser_unmap                      (GtkWidget *widget)
451 {
452     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
453
454     g_assert (priv);
455
456     if (priv->event_window) {
457         gdk_window_hide (priv->event_window);
458     }
459
460     GTK_WIDGET_CLASS (parent_class)->unmap (widget);
461 }
462
463 inline void 
464 inline_clip_to_alloc                            (void *s, 
465                                                  GtkAllocation *a)
466 {
467     struct {
468         int x, y, w, h;
469     } *area = s;
470
471
472     if (area->x < a->x) {
473         area->w -= a->x - area->x;
474         area->x = a->x;
475     } if (area->y < a->y) {
476         area->h -= a->y - area->y;
477         area->y = a->y;
478     }
479     if (area->x + area->w > a->x + a->width) 
480         area->w = a->width - (area->x - a->x);
481
482     if (area->y + area->h > a->y + a->height) 
483         area->h = a->height - (area->y - a->y);
484 }
485
486 static gboolean 
487 hildon_color_chooser_expose                     (GtkWidget *widget, 
488                                                  GdkEventExpose *event)
489 {
490     HildonColorChooser *sel = HILDON_COLOR_CHOOSER (widget);
491     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
492
493     g_assert (priv);
494
495     GtkBorder graphical_border;
496
497     struct {
498         int x, y, w, h;
499     } area;
500
501
502     if(! GTK_WIDGET_REALIZED (widget)) {
503         return FALSE;
504     }
505
506     get_border (widget, "graphic_border", &graphical_border);
507     
508     if (event->area.width || event->area.height) {
509
510         gdk_draw_rectangle (widget->window,
511                 widget->style->black_gc,
512                 FALSE,
513                 priv->hba.x - 2, 
514                 priv->hba.y - 2, 
515                 priv->hba.width + 3,
516                 priv->hba.height + 3);
517
518         gdk_draw_rectangle (widget->window,
519                 widget->style->black_gc,
520                 FALSE,
521                 priv->spa.x - 2, 
522                 priv->spa.y - 2, 
523                 priv->spa.width + 3,
524                 priv->spa.height + 3);
525     }
526
527     if (priv->expose_info.expose_queued) {
528         if (GTK_WIDGET_SENSITIVE (widget)) {
529             inline_draw_hue_bar (widget, priv->hba.x, priv->hba.y, priv->hba.width, priv->hba.height, priv->hba.y, priv->hba.height);
530
531             inline_draw_sv_plane (sel, priv->spa.x, priv->spa.y, priv->spa.width, priv->spa.height);
532         } else {
533             inline_draw_hue_bar_dimmed (widget, priv->hba.x, priv->hba.y, priv->hba.width, priv->hba.height, priv->hba.y, priv->hba.height);
534
535             inline_draw_sv_plane_dimmed (sel, priv->spa.x, priv->spa.y, priv->spa.width, priv->spa.height);
536         }
537
538         priv->expose_info.expose_queued = 0;
539
540         g_get_current_time (&priv->expose_info.last_expose_time);
541
542     } else {
543         /* clip hue bar region */
544         area.x = event->area.x;
545         area.y = event->area.y;
546         area.w = event->area.width;
547         area.h = event->area.height;
548
549         inline_clip_to_alloc (&area, &priv->hba);
550
551         if(GTK_WIDGET_SENSITIVE (widget)) {
552             inline_draw_hue_bar (widget, area.x, area.y, area.w, area.h, priv->hba.y, priv->hba.height);
553         } else {
554             inline_draw_hue_bar_dimmed (widget, area.x, area.y, area.w, area.h, priv->hba.y, priv->hba.height);
555         }
556         
557         area.x = event->area.x;
558         area.y = event->area.y;
559         area.w = event->area.width;
560         area.h = event->area.height;
561
562         inline_clip_to_alloc (&area, &priv->spa);
563
564         if (GTK_WIDGET_SENSITIVE (widget)) {
565             inline_draw_sv_plane (sel, area.x, area.y, area.w, area.h);
566         } else {
567             inline_draw_sv_plane_dimmed (sel, area.x, area.y, area.w, area.h);
568         }
569     }
570
571     return FALSE;
572 }
573
574
575 inline void 
576 inline_sub_times                                (GTimeVal *result, 
577                                                  GTimeVal *greater, 
578                                                  GTimeVal *lesser)
579 {
580     result->tv_sec = greater->tv_sec - lesser->tv_sec;
581     result->tv_usec = greater->tv_usec - lesser->tv_usec;
582
583     if (result->tv_usec < 0) {
584         result->tv_sec--;
585         result->tv_usec += 1000000;
586     }
587 }
588
589 inline void 
590 inline_limited_expose                           (HildonColorChooser *sel)
591 {
592     GTimeVal curr_time, result;
593     GdkEventExpose event;
594     HildonColorChooserPrivate *priv; 
595    
596     if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sel))) {
597         return;
598     }
599
600     priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (sel);
601     g_assert (priv);
602
603     if(priv->currhue == priv->expose_info.last_expose_hue) {
604         return; /* no need to redraw */
605     }
606
607     priv->expose_info.last_expose_hue = priv->currhue;
608
609     g_get_current_time (&curr_time);
610
611     inline_sub_times (&result, &curr_time, &priv->expose_info.last_expose_time);
612
613     if(result.tv_sec != 0 || result.tv_usec >= EXPOSE_INTERVAL) {
614
615         priv->expose_info.expose_queued = 1;
616
617         event.type = GDK_EXPOSE;
618         event.area.width = 0;
619         event.area.height = 0;
620         event.window = GTK_WIDGET(sel)->window;
621
622         gtk_widget_send_expose(GTK_WIDGET(sel), (GdkEvent *)&event);
623
624     } else if(! priv->expose_info.expose_queued) {
625         priv->expose_info.expose_queued = 1;
626         g_timeout_add ((EXPOSE_INTERVAL - result.tv_usec) / 1000, hildon_color_chooser_expose_timer, sel);
627     }
628 }
629
630 static gboolean 
631 hildon_color_chooser_button_press               (GtkWidget *widget, 
632                                                  GdkEventButton *event)
633 {
634     HildonColorChooser *sel = HILDON_COLOR_CHOOSER (widget);
635     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
636
637     g_assert (priv);
638     int x, y, tmp;
639
640     x = (int) event->x + widget->allocation.x;
641     y = (int) event->y + widget->allocation.y;
642
643     if (x >= priv->spa.x && x <= priv->spa.x + priv->spa.width &&
644         y >= priv->spa.y && y <= priv->spa.y + priv->spa.height) {
645
646         tmp = y - priv->spa.y;
647         priv->currsat = tmp * 0xffff / priv->spa.height;
648         tmp = x - priv->spa.x;
649         priv->currval = tmp * 0xffff / priv->spa.width;
650
651         g_signal_emit (sel, color_chooser_signals[COLOR_CHANGED], 0);
652         gtk_widget_queue_draw (widget);
653
654         priv->mousestate = 1;
655         priv->mousein = TRUE;
656
657         gtk_grab_add(widget);
658
659     } else if (x >= priv->hba.x && x <= priv->hba.x + priv->hba.width &&
660                y >= priv->hba.y && y <= priv->hba.y + priv->hba.height) {
661
662         tmp = y - priv->hba.y;
663         priv->currhue = tmp * 0xffff / priv->hba.height;
664
665         g_signal_emit (sel, color_chooser_signals[COLOR_CHANGED], 0);
666         inline_limited_expose (sel);
667
668         priv->mousestate = 2;
669         priv->mousein = TRUE;
670
671         gtk_grab_add (widget);
672     }
673
674     return FALSE;
675 }
676
677 static gboolean
678 hildon_color_chooser_button_release             (GtkWidget *widget, 
679                                                  GdkEventButton *event)
680 {
681     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
682
683     g_assert (priv);
684
685     if (priv->mousestate) {
686         gtk_grab_remove (widget);
687     }
688
689     priv->mousestate = 0;
690     priv->mousein = FALSE;
691
692     return FALSE;
693 }
694
695 static gboolean 
696 hildon_color_chooser_pointer_motion             (GtkWidget *widget, 
697                                                  GdkEventMotion *event)
698 {
699     HildonColorChooser *sel = HILDON_COLOR_CHOOSER (widget);
700     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
701
702     GdkModifierType mods;
703     gint x, y, tmp;
704
705     g_assert (priv);
706
707     if (event->is_hint || (event->window != widget->window))
708         gdk_window_get_pointer (widget->window, &x, &y, &mods);
709
710     if (priv->mousestate == 1) {
711         if (x >= priv->spa.x && x <= priv->spa.x + priv->spa.width &&
712             y >= priv->spa.y && y <= priv->spa.y + priv->spa.height) {
713
714             priv->currsat = (((long)(y - priv->spa.y)) * 0xffff) / priv->spa.height;
715             priv->currval = (((long)(x - priv->spa.x)) * 0xffff) / priv->spa.width;
716
717             g_signal_emit (sel, color_chooser_signals[COLOR_CHANGED], 0);
718             gtk_widget_queue_draw(widget);
719
720         } else if (priv->mousein == TRUE) {
721         }
722
723     } else if (priv->mousestate == 2) {
724         if (x >= priv->hba.x && x <= priv->hba.x + priv->hba.width &&
725             y >= priv->hba.y && y <= priv->hba.y + priv->hba.height) {
726             tmp = y - priv->hba.y;
727             tmp *= 0xffff;
728             tmp /= priv->hba.height;
729
730             if(tmp != priv->currhue) {
731                 priv->currhue = tmp;
732
733                 g_signal_emit (sel, color_chooser_signals[COLOR_CHANGED], 0);
734                 inline_limited_expose (sel);
735             }
736
737         } else if (priv->mousein == TRUE) {
738         }
739     }
740
741     return FALSE;
742 }
743
744 static void 
745 get_border                                      (GtkWidget *w, 
746                                                  char *name, 
747                                                  GtkBorder *b)
748 {
749     GtkBorder *tb = NULL;
750
751     gtk_widget_style_get (w, name, &tb, NULL);
752
753     if (tb) {
754         *b = *tb;
755         gtk_border_free (tb);
756     } else {
757         b->left = 0;
758         b->right = 0;
759         b->top = 0;
760         b->bottom = 0;
761     }
762 }
763
764 static void 
765 init_borders                                    (GtkWidget *w, 
766                                                  GtkBorder *inner, 
767                                                  GtkBorder *outer)
768 {
769     GtkBorder *tb;
770
771     get_border (w, "outer_border", outer);
772
773     gtk_widget_style_get (w, "inner_size", &tb, NULL);
774
775     if (tb) {
776         *inner = *tb;
777         gtk_border_free (tb);
778     } else {
779         inner->left = 64;
780         inner->right = 64;
781         inner->top = 12;
782         inner->bottom = 2;
783     }
784
785     if (inner->left < 2) inner->left = 2;
786     if (inner->right < 2) inner->right = 2;
787     if (inner->top < 2) inner->top = 2;
788 }
789
790 /**
791  * hildon_color_chooser_set_color:
792  * @chooser: a #HildonColorChooser
793  * @color: a color to be set
794  *
795  * Sets the color selected in the widget.
796  * Will move the crosshair pointer to indicate the passed color.
797  */
798 void 
799 hildon_color_chooser_set_color                  (HildonColorChooser *chooser, 
800                                                  GdkColor *color)
801 {
802     unsigned short hue, sat, val;
803     unsigned long min, max;
804     signed long tmp, diff;
805     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (chooser);
806
807     g_assert (priv);
808
809     /* ugly nesting */
810     min = MIN (MIN (color->red, color->green), color->blue);
811     max = MAX (MAX (color->red, color->green), color->blue);
812     diff = max - min;
813     
814     val = max;
815
816     if (val > 0 && diff != 0) {
817         sat = (diff * 0x0000ffff) / max;
818
819         if (color->red == max) {
820             tmp = (signed) color->green - (signed) color->blue;
821             tmp *= 10922;
822             tmp /= diff;
823             if(tmp < 0) {
824                 tmp += 65532;
825             }
826             hue = tmp;
827         } else if (color->green == max) {
828             hue = (((signed long) color->blue - (signed long)color->red) * 10922 / diff) + 21844;
829         } else {
830             hue = (((signed long) color->red -(signed long) color->green) * 10922 / diff) + 43688;
831         }
832     } else {
833         hue = 0;
834         sat = 0;
835     }
836
837     priv->currhue = hue;
838     priv->currsat = sat;
839     priv->currval = val;
840
841     inline_limited_expose (chooser);
842     g_signal_emit (chooser, color_chooser_signals[COLOR_CHANGED], 0);
843 }
844
845 inline void
846 inline_h2rgb                                    (unsigned short hue, 
847                                                  unsigned long *rgb)
848 {
849     unsigned short hue_rotation, hue_value;
850
851     hue_rotation  = hue / 10922;
852     hue_value     = hue % 10922;
853
854     switch (hue_rotation) {
855
856         case 0:
857         case 6:
858             rgb[0] = FULL_COLOR;
859             rgb[1] = hue_value * 6*256;
860             rgb[2] = 0;
861             break;
862
863         case 1:
864             rgb[0] = FULL_COLOR - (hue_value * 6*256);
865             rgb[1] = FULL_COLOR;
866             rgb[2] = 0;
867             break;
868
869         case 2:
870             rgb[0] = 0;
871             rgb[1] = FULL_COLOR;
872             rgb[2] = hue_value * 6*256;
873             break;
874
875         case 3:
876             rgb[0] = 0;
877             rgb[1] = FULL_COLOR - (hue_value * 6*256);
878             rgb[2] = FULL_COLOR;
879             break;
880
881         case 4:
882             rgb[0] = hue_value * 6*256;
883             rgb[1] = 0;
884             rgb[2] = FULL_COLOR;
885             break;
886
887         case 5:
888             rgb[0] = FULL_COLOR;
889             rgb[1] = 0;
890             rgb[2] = FULL_COLOR - (hue_value * 6*256);
891             break;
892
893         default:
894             rgb[0] = 0;
895             rgb[1] = 0;
896             rgb[2] = 0;
897             break;
898     }
899 }
900
901 static void 
902 intern_h2rgb8                                   (unsigned short hue, 
903                                                  unsigned char *rgb)
904 {
905     unsigned short hue_rotation, hue_value;
906
907     hue >>= 8;
908     hue_rotation  = hue / 42;
909     hue_value     = hue % 42;
910
911     switch (hue_rotation) {
912         case 0:
913         case 6:
914             rgb[0] = FULL_COLOR8;
915             rgb[1] = hue_value * 6;
916             rgb[2] = 0;
917             break;
918
919         case 1:
920             rgb[0] = FULL_COLOR8 - (hue_value * 6);
921             rgb[1] = FULL_COLOR8;
922             rgb[2] = 0;
923             break;
924
925         case 2:
926             rgb[0] = 0;
927             rgb[1] = FULL_COLOR8;
928             rgb[2] = hue_value * 6;
929             break;
930
931         case 3:
932             rgb[0] = 0;
933             rgb[1] = FULL_COLOR8 - (hue_value * 6);
934             rgb[2] = FULL_COLOR8;
935             break;
936
937         case 4:
938             rgb[0] = hue_value * 6;
939             rgb[1] = 0;
940             rgb[2] = FULL_COLOR8;
941             break;
942
943         case 5:
944             rgb[0] = FULL_COLOR8;
945             rgb[1] = 0;
946             rgb[2] = FULL_COLOR8 - (hue_value * 6);
947             break;
948
949         default:
950             rgb[0] = 0;
951             rgb[1] = 0;
952             rgb[2] = 0;
953             break;
954     }
955 }
956
957 /* optimization: do not ask hue for each round but have bilinear vectors */
958 /* rethink: benefits from handling data 8 bit? (no shift round) */
959 inline void 
960 inline_draw_hue_bar                             (GtkWidget *widget, 
961                                                  int x, 
962                                                  int y, 
963                                                  int w, 
964                                                  int h, 
965                                                  int sy, 
966                                                  int sh)
967 {
968     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (widget);
969
970     unsigned short hvec, hcurr;
971     unsigned char *buf, *ptr, tmp[3];
972     int i, j, tmpy;
973     g_assert (priv);
974
975     if (w <= 0 || h <= 0) {
976         return;
977     }
978
979     buf = (unsigned char *) g_malloc (w * h * 3);
980
981     hvec = 65535 / sh;
982     hcurr = hvec * (y - sy);
983     
984     ptr = buf;
985
986     for (i = 0; i < h; i++) {
987         intern_h2rgb8 (hcurr, tmp);
988
989         for (j = 0; j < w; j++) {
990             ptr[0] = tmp[0];
991             ptr[1] = tmp[1];
992             ptr[2] = tmp[2];
993             ptr += 3;
994         }
995
996         hcurr += hvec;
997     }
998
999
1000     gdk_draw_rgb_image (widget->parent->window, 
1001             widget->style->fg_gc[0], 
1002             x, y, 
1003             w, h, 
1004             GDK_RGB_DITHER_NONE, buf, w * 3);
1005
1006     tmpy = priv->hba.y + (priv->currhue * priv->hba.height / 0xffff);
1007     gdk_draw_line (widget->parent->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], priv->hba.x, tmpy, priv->hba.x + priv->hba.width - 1, tmpy);
1008
1009     if ((((priv->currhue * priv->hba.height) & 0xffff) > 0x8000) && (tmpy < (priv->hba.y + priv->hba.height))) {
1010         gdk_draw_line (widget->parent->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], 
1011                 priv->hba.x, tmpy+1, priv->hba.x + priv->hba.width - 1, tmpy+1);
1012     } else if (tmpy > priv->hba.y) {
1013         gdk_draw_line(widget->parent->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], priv->hba.x, 
1014                 tmpy-1, priv->hba.x + priv->hba.width - 1, tmpy-1);
1015     }
1016
1017     g_free(buf);
1018 }
1019
1020 inline void 
1021 inline_draw_hue_bar_dimmed                      (GtkWidget *widget, 
1022                                                  int x, 
1023                                                  int y, 
1024                                                  int w, 
1025                                                  int h, 
1026                                                  int sy, 
1027                                                  int sh)
1028 {
1029     HildonColorChooser *sel = HILDON_COLOR_CHOOSER (widget);
1030     HildonColorChooserPrivate *priv;
1031
1032     if (w <= 0 || h <= 0) {
1033         return;
1034     }
1035
1036     priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (sel);
1037     g_assert (priv);
1038
1039     /* We need to create (and cache) the pixbuf if we don't 
1040      * have it yet */
1041     if (priv->dimmed_bar == NULL) {
1042         int i, j;
1043         unsigned short hvec, hcurr, avg;
1044         unsigned char *buf, *ptr, tmp[3];
1045         buf = (unsigned char *) g_malloc (w * h * 3);
1046
1047         hvec = 65535 / sh;
1048         hcurr = hvec * (y - sy);
1049         ptr = buf;
1050
1051         for (i = 0; i < h; i++) {
1052             intern_h2rgb8 (hcurr, tmp);
1053
1054             for(j = 0; j < w; j++) {
1055                 avg = ((unsigned short) tmp[0]*3 + (unsigned short) tmp[1]*2 + (unsigned short) tmp[2])/6;
1056                 ptr[0] = ((((i % 2) + j) % 2) == 0) ? MIN ((avg * 0.7) + 180, 255) : MIN ((avg * 0.7) + 120, 255);
1057                 ptr[1] = ((((i % 2) + j) % 2) == 0) ? MIN ((avg * 0.7) + 180, 255) : MIN ((avg * 0.7) + 120, 255);
1058                 ptr[2] = ((((i % 2) + j) % 2) == 0) ? MIN ((avg * 0.7) + 180, 255) : MIN ((avg * 0.7) + 120, 255);
1059                 ptr += 3;
1060             }
1061
1062             hcurr += hvec;
1063         }
1064
1065         priv->dimmed_bar = gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB, FALSE, 8, w, h, w * 3, (gpointer) g_free, buf);
1066     }
1067
1068     gdk_draw_pixbuf (widget->parent->window, widget->style->fg_gc [0], priv->dimmed_bar, 0, 0, x, y, w, h, GDK_RGB_DITHER_NONE, 0, 0);
1069 }
1070
1071 inline void 
1072 inline_draw_crosshair                           (unsigned char *buf, 
1073                                                  int x, 
1074                                                  int y, 
1075                                                  int w, 
1076                                                  int h)
1077 {
1078     int i, j, sx, sy;
1079
1080     /* bad "clipping", clip the loop to save cpu */
1081     for(i = 0; i < 8; i++) {
1082         for(j = 0; j < 8; j++) {
1083             sx = j + x; sy = i + y;
1084
1085             if (sx >= 0 && sx < w && sy >= 0 && sy < h) {
1086                 if (crosshair[j + 8*i]) {
1087                     if (crosshair[j + 8*i] & 0x1) {
1088                         buf[(sx)*3+(sy)*w*3+0] = 255;
1089                         buf[(sx)*3+(sy)*w*3+1] = 255;
1090                         buf[(sx)*3+(sy)*w*3+2] = 255;
1091                     } else {
1092                         buf[(sx)*3+(sy)*w*3+0] = 0;
1093                         buf[(sx)*3+(sy)*w*3+1] = 0;
1094                         buf[(sx)*3+(sy)*w*3+2] = 0;
1095                     }
1096                 }
1097             }
1098         }
1099     }
1100 }
1101
1102 inline void 
1103 inline_draw_sv_plane                            (HildonColorChooser *sel, 
1104                                                  int x, 
1105                                                  int y, 
1106                                                  int w, 
1107                                                  int h)
1108 {
1109     GtkWidget *widget = GTK_WIDGET (sel);
1110     unsigned char *buf, *ptr;
1111     unsigned long rgbx[3] = { 0x00ffffff, 0x00ffffff, 0x00ffffff }, rgbtmp[3];
1112     signed long rgby[3];
1113     HildonColorChooserPrivate *priv;
1114     int i, j;
1115     int tmp;
1116
1117     if (w <= 0 || h <= 0) {
1118         return;
1119     }
1120
1121     priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (sel);
1122     g_assert (priv);
1123     tmp = priv->spa.width * priv->spa.height;
1124
1125     buf = (unsigned char *) g_malloc (w * h * 3);
1126     ptr = buf;
1127
1128     inline_h2rgb (priv->currhue, rgbtmp);
1129
1130     rgby[0] = rgbtmp[0] - rgbx[0];
1131     rgby[1] = rgbtmp[1] - rgbx[1];
1132     rgby[2] = rgbtmp[2] - rgbx[2];
1133
1134     rgbx[0] /= priv->spa.width;
1135     rgbx[1] /= priv->spa.width;
1136     rgbx[2] /= priv->spa.width;
1137
1138     rgby[0] /= tmp;
1139     rgby[1] /= tmp;
1140     rgby[2] /= tmp;
1141
1142     rgbx[0] += (y - priv->spa.y) * rgby[0];
1143     rgbx[1] += (y - priv->spa.y) * rgby[1];
1144     rgbx[2] += (y - priv->spa.y) * rgby[2];
1145
1146     for(i = 0; i < h; i++) {
1147         rgbtmp[0] = rgbx[0] * (x - priv->spa.x);
1148         rgbtmp[1] = rgbx[1] * (x - priv->spa.x);
1149         rgbtmp[2] = rgbx[2] * (x - priv->spa.x);
1150
1151         for(j = 0; j < w; j++) {
1152             ptr[0] = rgbtmp[0] >> 16;
1153             ptr[1] = rgbtmp[1] >> 16;
1154             ptr[2] = rgbtmp[2] >> 16;
1155             rgbtmp[0] += rgbx[0];
1156             rgbtmp[1] += rgbx[1];
1157             rgbtmp[2] += rgbx[2];
1158             ptr += 3;
1159         }
1160
1161         rgbx[0] += rgby[0];
1162         rgbx[1] += rgby[1];
1163         rgbx[2] += rgby[2];
1164     }
1165
1166     inline_draw_crosshair (buf, 
1167             (priv->spa.width * priv->currval / 0xffff) - x + priv->spa.x - 4, 
1168             (priv->spa.height * priv->currsat / 0xffff) - y + priv->spa.y - 4, 
1169             w, h);
1170
1171     gdk_draw_rgb_image (widget->parent->window, widget->style->fg_gc[0], x, y, w, h, GDK_RGB_DITHER_NONE, buf, w * 3);
1172     g_free(buf);
1173 }
1174
1175 inline void 
1176 inline_draw_sv_plane_dimmed                     (HildonColorChooser *sel, 
1177                                                  int x, 
1178                                                  int y, 
1179                                                  int w, 
1180                                                  int h)
1181 {
1182     GtkWidget *widget = GTK_WIDGET (sel);
1183     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (sel);
1184
1185     g_assert (priv);
1186
1187     if (w <= 0 || h <= 0) {
1188         return;
1189     }
1190
1191     /* We need to create (and cache) the pixbuf if we don't 
1192      * have it yet */
1193     if (priv->dimmed_plane == NULL) {
1194         unsigned char *buf, *ptr;
1195         unsigned long rgbx[3] = { 0x00ffffff, 0x00ffffff, 0x00ffffff }, rgbtmp[3];
1196         unsigned long avg;
1197         signed long rgby[3];
1198         int tmp = priv->spa.width * priv->spa.height, i, j;
1199
1200         buf = (unsigned char *) g_malloc (w * h * 3);
1201
1202         ptr = buf;
1203
1204         /* possibe optimization: as we are drawing grayscale plane, there might
1205            be some simpler algorithm to do this*/
1206         rgbtmp[0] = 0x00ffffff;
1207         rgbtmp[1] = 0x00000000;
1208         rgbtmp[2] = 0x00000000;
1209
1210         rgby[0] = rgbtmp[0] - rgbx[0];
1211         rgby[1] = rgbtmp[1] - rgbx[1];
1212         rgby[2] = rgbtmp[2] - rgbx[2];
1213
1214         rgbx[0] /= priv->spa.width;
1215         rgbx[1] /= priv->spa.width;
1216         rgbx[2] /= priv->spa.width;
1217
1218         rgby[0] /= tmp;
1219         rgby[1] /= tmp;
1220         rgby[2] /= tmp;
1221
1222         rgbx[0] += (y - priv->spa.y) * rgby[0];
1223         rgbx[1] += (y - priv->spa.y) * rgby[1];
1224         rgbx[2] += (y - priv->spa.y) * rgby[2];
1225
1226         for(i = 0; i < h; i++) {
1227             rgbtmp[0] = rgbx[0] * (x - priv->spa.x);
1228             rgbtmp[1] = rgbx[1] * (x - priv->spa.x);
1229             rgbtmp[2] = rgbx[2] * (x - priv->spa.x);
1230
1231             for(j = 0; j < w; j++) {
1232                 avg = (rgbtmp[0] + rgbtmp[1] + rgbtmp[2])/3;
1233                 avg >>= 16;
1234                 ptr[0] = ((((i % 2) + j) % 2) == 0) ? MIN ((avg * 0.7) + 180, 255) : MIN ((avg * 0.7) + 120, 255);
1235                 ptr[1] = ((((i % 2) + j) % 2) == 0) ? MIN ((avg * 0.7) + 180, 255) : MIN ((avg * 0.7) + 120, 255);
1236                 ptr[2] = ((((i % 2) + j) % 2) == 0) ? MIN ((avg * 0.7) + 180, 255) : MIN ((avg * 0.7) + 120, 255);
1237                 rgbtmp[0] += rgbx[0];
1238                 rgbtmp[1] += rgbx[1];
1239                 rgbtmp[2] += rgbx[2];
1240                 ptr += 3;
1241             }
1242
1243             rgbx[0] += rgby[0];
1244             rgbx[1] += rgby[1];
1245             rgbx[2] += rgby[2];
1246         }
1247
1248         priv->dimmed_plane = gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB, FALSE, 8, w, h, w * 3, (gpointer) g_free, buf);
1249     }
1250
1251     gdk_draw_pixbuf (widget->parent->window, widget->style->fg_gc [0], priv->dimmed_plane, 0, 0, x, y, w, h, GDK_RGB_DITHER_NONE, 0, 0);
1252 }
1253
1254
1255 static gboolean 
1256 hildon_color_chooser_expose_timer               (gpointer data)
1257 {
1258     HildonColorChooser *sel = HILDON_COLOR_CHOOSER (data);
1259     HildonColorChooserPrivate *priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (sel);
1260
1261     g_assert (priv);
1262
1263     if (priv->expose_info.expose_queued) {
1264         gtk_widget_queue_draw (GTK_WIDGET (data));
1265     }
1266
1267     return FALSE;
1268 }
1269
1270 /**
1271  * hildon_color_chooser_get_color:
1272  * @chooser: a #HildonColorChooser
1273  * @color: a color structure to fill with the currently selected color
1274  *
1275  * Retrives the currently selected color in the chooser.
1276  *
1277  */
1278 void
1279 hildon_color_chooser_get_color                  (HildonColorChooser *chooser, 
1280                                                  GdkColor *color)
1281 {
1282     HildonColorChooserPrivate *priv;
1283     GdkVisual *system_visual = gdk_visual_get_system ();
1284     unsigned long rgb[3], rgb2[3];
1285
1286     g_return_if_fail (HILDON_IS_COLOR_CHOOSER (chooser));
1287     g_return_if_fail (color != NULL);
1288
1289     priv = HILDON_COLOR_CHOOSER_GET_PRIVATE (chooser);
1290     g_assert (priv);
1291
1292     inline_h2rgb (priv->currhue, rgb);
1293
1294     rgb2[0] = 0xffffff - rgb[0];
1295     rgb2[1] = 0xffffff - rgb[1];
1296     rgb2[2] = 0xffffff - rgb[2];
1297
1298     color->red   = ((rgb[0] >> 8) + ((rgb2[0] >> 8) * (0xffff - priv->currsat) / 0xffff)) * priv->currval / 0xffff;
1299     color->green = ((rgb[1] >> 8) + ((rgb2[1] >> 8) * (0xffff - priv->currsat) / 0xffff)) * priv->currval / 0xffff;
1300     color->blue  = ((rgb[2] >> 8) + ((rgb2[2] >> 8) * (0xffff - priv->currsat) / 0xffff)) * priv->currval / 0xffff;
1301
1302     color->pixel = ((color->red >> (16 - system_visual->red_prec)) << system_visual->red_shift) |
1303         ((color->green >> (16 - system_visual->green_prec)) << system_visual->green_shift) |
1304         ((color->blue >> (16 - system_visual->blue_prec)) << system_visual->blue_shift);
1305 }
1306
1307 GtkWidget*
1308 hildon_color_chooser_new                        (void)
1309 {
1310     return (GtkWidget *) g_object_new (HILDON_TYPE_COLOR_CHOOSER, NULL);
1311 }
1312
1313 static void
1314 hildon_color_chooser_set_property               (GObject *object, 
1315                                                  guint param_id,
1316                                                  const GValue *value, 
1317                                                  GParamSpec *pspec)
1318 {
1319     g_return_if_fail (HILDON_IS_COLOR_CHOOSER (object));
1320
1321     switch (param_id) 
1322     {
1323
1324         case PROP_COLOR: {
1325             GdkColor *color = g_value_get_boxed (value);
1326             hildon_color_chooser_set_color ((HildonColorChooser *) object, color);
1327             } break;
1328
1329         default:
1330             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1331             break;
1332     }
1333 }
1334
1335 static void
1336 hildon_color_chooser_get_property               (GObject *object, 
1337                                                  guint param_id,
1338                                                  GValue *value, 
1339                                                  GParamSpec *pspec)
1340 {
1341     g_return_if_fail (HILDON_IS_COLOR_CHOOSER (object));
1342
1343     switch (param_id) 
1344     {
1345
1346         case PROP_COLOR: {
1347             GdkColor color;
1348             hildon_color_chooser_get_color ((HildonColorChooser *) object, &color);
1349             g_value_set_boxed (value, &color);
1350             } break;
1351
1352         default:
1353             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1354             break;
1355     }
1356 }
1357
1358