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