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