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