3c55e30d8150a787ad23765bc3f9ca930ed1ae1f
[hildon] / hildon-widgets-plugins / hildon-color-chooser-hsv.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation.
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.
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 #include <gtk/gtk.h>
27
28 #include <hildon-widgets/hildon-color-chooser.h>
29
30
31 const char *parent_name = "HildonColorChooser";
32 const char *plugin_name = "HSV color chooser";
33
34 GType export_type(void);
35
36
37 typedef struct {
38   HildonColorChooser parent;
39
40   GtkAllocation hba;
41   GtkAllocation spa;
42
43   unsigned short currhue;
44   unsigned short currsat;
45   unsigned short currval;
46
47   int mousestate;
48   gboolean mousein;
49
50
51   GdkWindow *event_window;
52
53
54   struct {
55     unsigned short last_expose_hue;
56
57     GTimeVal last_expose_time;
58
59     int expose_queued;
60   } expose_info;
61 } HildonColorChooserHSV;
62
63 typedef struct {
64   HildonColorChooserClass parent;
65 } HildonColorChooserHSVClass;
66
67
68 #define HILDON_COLOR_CHOOSER_HSV(obj)         (G_TYPE_CHECK_INSTANCE_CAST ((obj), export_type(), HildonColorChooserHSV))
69 #define HILDON_COLOR_CHOOSER_HSV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), export_type(), HildonColorChooserHSVClass))
70
71
72 static HildonColorChooserClass *parent_class = NULL;
73
74
75   /* "crosshair" is hardcoded for now */
76 static gchar crosshair[64] = { 0, 0, 0, 2, 2, 0, 0, 0,
77                                0, 2, 2, 3, 3, 2, 2, 0,
78                                0, 2, 3, 0, 0, 3, 2, 0,
79                                2, 3, 0, 0, 0, 0, 3, 2,
80                                2, 3, 0, 0, 0, 0, 3, 2,
81                                0, 2, 3, 0, 0, 3, 2, 0,
82                                0, 2, 2, 3, 3, 2, 2, 0,
83                                0, 0, 0, 2, 2, 0, 0, 0};
84
85
86 static void hildon_color_chooser_hsv_init(HildonColorChooserHSV *sel);
87 static void hildon_color_chooser_hsv_class_init(HildonColorChooserHSVClass *klass);
88
89 static void hildon_color_chooser_hsv_destroy(GtkObject *obj);
90
91 static void hildon_color_chooser_hsv_size_request(GtkWidget *widget, GtkRequisition *req);
92 static void hildon_color_chooser_hsv_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
93
94 static void hildon_color_chooser_hsv_realize(GtkWidget *widget);
95
96 static void hildon_color_chooser_hsv_map(GtkWidget *widget);
97 static void hildon_color_chooser_hsv_unmap(GtkWidget *widget);
98
99 static gboolean hildon_color_chooser_hsv_expose(GtkWidget *widget, GdkEventExpose *event);
100
101 static gboolean hildon_color_chooser_button_press(GtkWidget *widget, GdkEventButton *event);
102 static gboolean hildon_color_chooser_button_release(GtkWidget *widget, GdkEventButton *event);
103 static gboolean hildon_color_chooser_pointer_motion(GtkWidget *widget, GdkEventMotion *event);
104
105 static void hildon_color_chooser_hsv_set_color(HildonColorChooser *sel, GdkColor *color);
106
107
108 static void internal_get_border(GtkWidget *w, char *name, GtkBorder *b);
109 static void _internal_init_borders(GtkWidget *w, GtkBorder *inner, GtkBorder *outer);
110
111 static void internal_invoke_color_changed(HildonColorChooserHSV *sel);
112
113
114 inline void inline_clip_to_alloc(void *s, GtkAllocation *a);
115
116 inline void inline_sub_times(GTimeVal *result, GTimeVal *greater, GTimeVal *lesser);
117
118 inline void inline_limited_expose(HildonColorChooserHSV *sel);
119
120 inline void inline_draw_hue_bar(GtkWidget *widget, int x, int y, int w, int h, int sy, int sh);
121 inline void inline_draw_hue_bar_dimmed(GtkWidget *widget, int x, int y, int w, int h, int sy, int sh);
122
123 inline void inline_draw_sv_plane(HildonColorChooserHSV *sel, int x, int y, int w, int h);
124 inline void inline_draw_sv_plane_dimmed(HildonColorChooserHSV *sel, int x, int y, int w, int h);
125
126 inline void inline_draw_crosshair(unsigned char *buf, int x, int y, int w, int h);
127
128
129 inline void inline_h2rgb(unsigned short hue, unsigned long *rgb);
130
131
132 static gboolean hildon_color_chooser_hsv_expose_timer(gpointer data);
133
134
135 GType export_type()
136 {
137   static GType chooser_type = 0;
138
139   if (!chooser_type) {
140     static const GTypeInfo chooser_info =
141     {
142       sizeof (HildonColorChooserHSVClass),
143       NULL,
144       NULL,
145       (GClassInitFunc) hildon_color_chooser_hsv_class_init,
146       NULL,
147       NULL,
148       sizeof (HildonColorChooserHSV),
149       0,
150       (GInstanceInitFunc) hildon_color_chooser_hsv_init,
151       NULL
152     };
153
154     chooser_type = g_type_register_static (HILDON_TYPE_COLOR_CHOOSER,
155                                            "HildonColorChooserHSV",
156                                            &chooser_info, 0);
157   }
158
159   return chooser_type;
160 }
161
162 static void hildon_color_chooser_hsv_init(HildonColorChooserHSV *sel)
163 {
164   GTK_WIDGET_SET_FLAGS (sel, GTK_NO_WINDOW);
165
166
167   sel->currhue = 0;
168   sel->currsat = 0;
169   sel->currval = 0;
170
171   sel->mousestate = 0;
172   sel->mousein = FALSE;
173
174
175   g_get_current_time(&sel->expose_info.last_expose_time);
176
177   sel->expose_info.last_expose_hue = sel->currhue;
178   sel->expose_info.expose_queued = 0;
179 }
180
181 static void hildon_color_chooser_hsv_class_init(HildonColorChooserHSVClass *klass)
182 {
183   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
184   GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass);
185
186   HildonColorChooserClass *selection_class = HILDON_COLOR_CHOOSER_CLASS(klass);
187
188
189   parent_class = g_type_class_peek_parent(klass);
190
191
192   object_class->destroy = hildon_color_chooser_hsv_destroy;
193
194
195   widget_class->size_request = hildon_color_chooser_hsv_size_request;
196   widget_class->size_allocate = hildon_color_chooser_hsv_size_allocate;
197
198   widget_class->realize = hildon_color_chooser_hsv_realize;
199
200   widget_class->map = hildon_color_chooser_hsv_map;
201   widget_class->unmap = hildon_color_chooser_hsv_unmap;
202
203   widget_class->expose_event = hildon_color_chooser_hsv_expose;
204
205   widget_class->button_press_event = hildon_color_chooser_button_press;
206   widget_class->button_release_event = hildon_color_chooser_button_release;
207   widget_class->motion_notify_event = hildon_color_chooser_pointer_motion;
208
209
210   selection_class->set_color = hildon_color_chooser_hsv_set_color;
211
212
213   gtk_widget_class_install_style_property(widget_class,
214                                           g_param_spec_boxed("inner_size",
215                                                              "Inner sizes",
216                                                              "Sizes of SV plane, H bar and spacing",
217                                                              GTK_TYPE_BORDER,
218                                                              G_PARAM_READABLE));
219   gtk_widget_class_install_style_property(widget_class,
220                                           g_param_spec_boxed("graphic_border",
221                                                              "Graphical borders",
222                                                              "Size of graphical border",
223                                                              GTK_TYPE_BORDER,
224                                                              G_PARAM_READABLE));
225 }
226
227
228 static void hildon_color_chooser_hsv_destroy(GtkObject *obj)
229 {
230   GTK_OBJECT_CLASS(parent_class)->destroy(obj);
231 }
232
233
234 static void hildon_color_chooser_hsv_size_request(GtkWidget *widget, GtkRequisition *req)
235 {
236   GtkBorder inner, outer;
237
238
239   _internal_init_borders(widget, &inner, &outer);
240
241
242   req->width = inner.left + inner.top + inner.bottom + outer.left + outer.right;
243   req->height = inner.right + outer.top + outer.bottom;
244 }
245
246 static void hildon_color_chooser_hsv_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
247 {
248   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
249   GtkBorder outer, inner;
250
251
252   widget->allocation = *alloc;
253
254
255   _internal_init_borders(widget, &inner, &outer);
256
257
258   sel->hba.height = alloc->height - outer.top - outer.bottom;
259   sel->hba.y = alloc->y + outer.top;
260   sel->hba.width = inner.top;
261   sel->hba.x = alloc->x + alloc->width - outer.right - inner.top;
262
263   sel->spa.x = alloc->x + outer.left;
264   sel->spa.y = alloc->y + outer.top;
265   sel->spa.height = alloc->height - outer.top - outer.bottom;
266   sel->spa.width = alloc->width - outer.left - outer.right - inner.top - inner.bottom;
267
268
269   if(GTK_WIDGET_REALIZED(widget)) {
270     gdk_window_move_resize(sel->event_window, widget->allocation.x, widget->allocation.y, widget->allocation.width, widget->allocation.height);
271   }
272 }
273
274
275 static void hildon_color_chooser_hsv_realize(GtkWidget *widget)
276 {
277   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
278   GdkWindowAttr attributes;
279   gint attributes_mask;
280
281   attributes.x = widget->allocation.x;
282   attributes.y = widget->allocation.y;
283   attributes.width = widget->allocation.width;
284   attributes.height = widget->allocation.height;
285   attributes.wclass = GDK_INPUT_ONLY;
286   attributes.window_type = GDK_WINDOW_CHILD;
287   attributes.event_mask = gtk_widget_get_events(widget) | GDK_BUTTON_PRESS_MASK | 
288                           GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
289                           GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK |
290                           GDK_BUTTON1_MOTION_MASK;
291   attributes.visual = gtk_widget_get_visual(widget);
292   attributes.colormap = gtk_widget_get_colormap(widget);
293
294   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_WMCLASS;
295   sel->event_window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
296
297
298   gdk_window_set_user_data(sel->event_window, widget);
299
300
301   widget->window = gtk_widget_get_parent_window(widget);
302
303
304   widget->style = gtk_style_attach(widget->style, widget->window);
305
306
307   GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
308 }
309
310
311 static void hildon_color_chooser_hsv_map(GtkWidget *widget)
312 {
313   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
314
315
316   GTK_WIDGET_CLASS(parent_class)->map(widget);
317
318   if(sel->event_window) {
319     gdk_window_show(sel->event_window);
320   }
321 }
322
323 static void hildon_color_chooser_hsv_unmap(GtkWidget *widget)
324 {
325   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
326
327
328   if(sel->event_window) {
329     gdk_window_hide(sel->event_window);
330   }
331
332   GTK_WIDGET_CLASS(parent_class)->unmap(widget);
333 }
334
335
336 inline void inline_clip_to_alloc(void *s, GtkAllocation *a)
337 {
338   struct {
339     int x, y, w, h;
340   } *area = s;
341
342
343   if(area->x < a->x) {
344     area->w -= a->x - area->x;
345     area->x = a->x;
346   } if(area->y < a->y) {
347     area->h -= a->y - area->y;
348     area->y = a->y;
349   }
350   if(area->x + area->w > a->x + a->width) area->w = a->width - (area->x - a->x);
351   if(area->y + area->h > a->y + a->height) area->h = a->height - (area->y - a->y);
352 }
353
354 static gboolean hildon_color_chooser_hsv_expose(GtkWidget *widget, GdkEventExpose *event)
355 {
356   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
357   GtkBorder graphical_border;
358   struct {
359     int x, y, w, h;
360   } area;
361
362
363   if(!GTK_WIDGET_REALIZED(widget)) {
364     return FALSE;
365   }
366
367
368   internal_get_border(widget, "graphic_border", &graphical_border);
369
370
371   if(event->area.width || event->area.height) {
372     if(graphical_border.top) {
373       gtk_paint_box(gtk_widget_get_style(widget), widget->window, GTK_WIDGET_STATE(widget),
374                     GTK_SHADOW_NONE, &event->area, widget, "border-top",
375                     sel->hba.x - graphical_border.left, sel->hba.y - graphical_border.top,
376                     sel->hba.width + graphical_border.left + graphical_border.right,
377                     graphical_border.top);
378       gtk_paint_box(gtk_widget_get_style(widget), widget->window, GTK_WIDGET_STATE(widget),
379                     GTK_SHADOW_NONE, &event->area, widget, "border-top",
380                     sel->spa.x - graphical_border.left, sel->spa.y - graphical_border.top,
381                     sel->spa.width + graphical_border.left + graphical_border.right,
382                     graphical_border.top);
383     }
384
385     if(graphical_border.bottom) {
386       gtk_paint_box(gtk_widget_get_style(widget), widget->window, GTK_WIDGET_STATE(widget),
387                     GTK_SHADOW_NONE, &event->area, widget, "border-bottom",
388                     sel->hba.x - graphical_border.left, sel->hba.y + sel->hba.height,
389                     sel->hba.width + graphical_border.left + graphical_border.right,
390                     graphical_border.bottom);
391       gtk_paint_box(gtk_widget_get_style(widget), widget->window, GTK_WIDGET_STATE(widget),
392                     GTK_SHADOW_NONE, &event->area, widget, "border-bottom",
393                     sel->spa.x - graphical_border.left, sel->spa.y + sel->spa.height,
394                     sel->spa.width + graphical_border.left + graphical_border.right,
395                     graphical_border.bottom);
396     }
397
398     if(graphical_border.left) {
399       gtk_paint_box(gtk_widget_get_style(widget), widget->window, GTK_WIDGET_STATE(widget),
400                     GTK_SHADOW_NONE, &event->area, widget, "border-left",
401                     sel->hba.x - graphical_border.left, sel->hba.y,
402                     graphical_border.left, sel->hba.height);
403       gtk_paint_box(gtk_widget_get_style(widget), widget->window, GTK_WIDGET_STATE(widget),
404                     GTK_SHADOW_NONE, &event->area, widget, "border-left",
405                     sel->spa.x - graphical_border.left, sel->spa.y,
406                     graphical_border.left, sel->spa.height);
407     }
408
409     if(graphical_border.right) {
410       gtk_paint_box(gtk_widget_get_style(widget), widget->window, GTK_WIDGET_STATE(widget),
411                     GTK_SHADOW_NONE, &event->area, widget, "border-right",
412                     sel->hba.x + sel->hba.width, sel->hba.y,
413                     graphical_border.right, sel->hba.height);
414       gtk_paint_box(gtk_widget_get_style(widget), widget->window, GTK_WIDGET_STATE(widget),
415                     GTK_SHADOW_NONE, &event->area, widget, "border-right",
416                     sel->spa.x + sel->spa.width, sel->spa.y,
417                     graphical_border.right, sel->spa.height);
418     }
419   }
420
421
422   if(sel->expose_info.expose_queued) {
423     if(GTK_WIDGET_SENSITIVE(widget)) {
424       inline_draw_hue_bar(widget, sel->hba.x, sel->hba.y, sel->hba.width, sel->hba.height, sel->hba.y, sel->hba.height);
425
426       inline_draw_sv_plane(sel, sel->spa.x, sel->spa.y, sel->spa.width, sel->spa.height);
427     } else {
428       inline_draw_hue_bar_dimmed(widget, sel->hba.x, sel->hba.y, sel->hba.width, sel->hba.height, sel->hba.y, sel->hba.height);
429
430       inline_draw_sv_plane_dimmed(sel, sel->spa.x, sel->spa.y, sel->spa.width, sel->spa.height);
431     }
432
433
434     sel->expose_info.expose_queued = 0;
435
436     g_get_current_time(&sel->expose_info.last_expose_time);
437   } else {
438       /* clip hue bar region */
439     area.x = event->area.x;
440     area.y = event->area.y;
441     area.w = event->area.width;
442     area.h = event->area.height;
443
444     inline_clip_to_alloc(&area, &sel->hba);
445
446     if(GTK_WIDGET_SENSITIVE(widget)) {
447       inline_draw_hue_bar(widget, area.x, area.y, area.w, area.h, sel->hba.y, sel->hba.height);
448     } else {
449       inline_draw_hue_bar_dimmed(widget, area.x, area.y, area.w, area.h, sel->hba.y, sel->hba.height);
450     }
451
452
453     area.x = event->area.x;
454     area.y = event->area.y;
455     area.w = event->area.width;
456     area.h = event->area.height;
457
458     inline_clip_to_alloc(&area, &sel->spa);
459
460     if(GTK_WIDGET_SENSITIVE(widget)) {
461       inline_draw_sv_plane(sel, area.x, area.y, area.w, area.h);
462     } else {
463       inline_draw_sv_plane_dimmed(sel, area.x, area.y, area.w, area.h);
464     }
465   }
466
467
468   return FALSE;
469 }
470
471
472 inline void inline_sub_times(GTimeVal *result, GTimeVal *greater, GTimeVal *lesser)
473 {
474   result->tv_sec = greater->tv_sec - lesser->tv_sec;
475   result->tv_usec = greater->tv_usec - lesser->tv_usec;
476
477   if(result->tv_usec < 0) {
478     result->tv_sec--;
479     result->tv_usec += 1000000;
480   }
481 }
482
483 #define EXPOSE_INTERVAL 50000
484 inline void inline_limited_expose(HildonColorChooserHSV *sel)
485 {
486   GTimeVal curr_time, result;
487   GdkEventExpose event;
488
489
490   if(!GTK_WIDGET_REALIZED(GTK_WIDGET(sel))) {
491     return;
492   }
493
494
495   if(sel->currhue == sel->expose_info.last_expose_hue) {
496     return; /* no need to redraw */
497   }
498
499
500   sel->expose_info.last_expose_hue  = sel->currhue;
501
502
503   g_get_current_time(&curr_time);
504
505   inline_sub_times(&result, &curr_time, &sel->expose_info.last_expose_time);
506
507   if(result.tv_sec != 0 || result.tv_usec >= EXPOSE_INTERVAL) {
508     sel->expose_info.expose_queued = 1;
509
510 #if 1
511     event.type = GDK_EXPOSE;
512     event.area.width = 0;
513     event.area.height = 0;
514     event.window = GTK_WIDGET(sel)->window;
515
516     gtk_widget_send_expose(GTK_WIDGET(sel), (GdkEvent *)&event);
517 #else
518     gtk_widget_queue_draw(GTK_WIDGET(sel));
519 #endif
520   } else if(!sel->expose_info.expose_queued) {
521     sel->expose_info.expose_queued = 1;
522
523
524     g_timeout_add((EXPOSE_INTERVAL - result.tv_usec)/1000, hildon_color_chooser_hsv_expose_timer, sel);
525   }
526 }
527
528 static gboolean hildon_color_chooser_button_press(GtkWidget *widget, GdkEventButton *event)
529 {
530   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
531   int x, y, tmp;
532
533
534   x = (int)event->x + widget->allocation.x;
535   y = (int)event->y + widget->allocation.y;
536
537
538   if(x >= sel->spa.x && x <= sel->spa.x + sel->spa.width &&
539      y >= sel->spa.y && y <= sel->spa.y + sel->spa.height) {
540     tmp = y - sel->spa.y;
541     sel->currsat = tmp * 0xffff / sel->spa.height;
542     tmp = x - sel->spa.x;
543     sel->currval = tmp * 0xffff / sel->spa.width;
544
545     internal_invoke_color_changed(sel);
546     gtk_widget_queue_draw(widget);
547
548     sel->mousestate = 1;
549     sel->mousein = TRUE;
550
551     gtk_grab_add(widget);
552   } else if(x >= sel->hba.x && x <= sel->hba.x + sel->hba.width &&
553             y >= sel->hba.y && y <= sel->hba.y + sel->hba.height) {
554     tmp = y - sel->hba.y;
555     sel->currhue = tmp * 0xffff / sel->hba.height;
556
557     internal_invoke_color_changed(sel);
558     inline_limited_expose(sel);
559
560     sel->mousestate = 2;
561     sel->mousein = TRUE;
562
563     gtk_grab_add(widget);
564   }
565
566
567   return FALSE;
568 }
569
570 static gboolean hildon_color_chooser_button_release(GtkWidget *widget, GdkEventButton *event)
571 {
572   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
573
574
575   if(sel->mousestate) {
576     gtk_grab_remove(widget);
577   }
578
579
580   sel->mousestate = 0;
581   sel->mousein = FALSE;
582
583
584   return FALSE;
585 }
586
587 static gboolean hildon_color_chooser_pointer_motion(GtkWidget *widget, GdkEventMotion *event)
588 {
589   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
590   GdkModifierType mods;
591   gint x, y, tmp;
592
593
594   if (event->is_hint || (event->window != widget->window))
595     gdk_window_get_pointer (widget->window, &x, &y, &mods);
596
597
598   if(sel->mousestate == 1) {
599     if(x >= sel->spa.x && x <= sel->spa.x + sel->spa.width &&
600       y >= sel->spa.y && y <= sel->spa.y + sel->spa.height) {
601       sel->currsat = (((long)(y - sel->spa.y)) * 0xffff)/sel->spa.height;
602       sel->currval = (((long)(x - sel->spa.x)) * 0xffff)/sel->spa.width;
603
604       internal_invoke_color_changed(sel);
605       gtk_widget_queue_draw(widget);
606     } else if(sel->mousein == TRUE) {
607     }
608   } else if(sel->mousestate == 2) {
609     if(x >= sel->hba.x && x <= sel->hba.x + sel->hba.width &&
610       y >= sel->hba.y && y <= sel->hba.y + sel->hba.height) {
611       tmp = y - sel->hba.y;
612       tmp *= 0xffff;
613       tmp /= sel->hba.height;
614
615       if(tmp != sel->currhue) {
616         sel->currhue = tmp;
617
618         internal_invoke_color_changed(sel);
619         inline_limited_expose(sel);
620       }
621     } else if(sel->mousein == TRUE) {
622     }
623   }
624
625   return FALSE;
626 }
627
628
629 static void internal_get_border(GtkWidget *w, char *name, GtkBorder *b)
630 {
631   GtkBorder *tb;
632
633   gtk_widget_style_get(w, name, &tb, NULL);
634
635   if(tb) {
636     *b = *tb;
637     g_free(tb);
638   } else {
639     b->left = 0;
640     b->right = 0;
641     b->top = 0;
642     b->bottom = 0;
643   }
644 }
645
646
647 static void _internal_init_borders(GtkWidget *w, GtkBorder *inner, GtkBorder *outer)
648 {
649   GtkBorder *tb;
650
651
652   internal_get_border(w, "outer_border", outer);
653
654
655   gtk_widget_style_get(w, "inner_size", &tb, NULL);
656
657   if(tb) {
658     *inner = *tb;
659     g_free(tb);
660   } else {
661     inner->left = 64;
662     inner->right = 64;
663     inner->top = 12;
664     inner->bottom = 2;
665   }
666
667   if(inner->left < 2) inner->left = 2;
668   if(inner->right < 2) inner->right = 2;
669   if(inner->top < 2) inner->top = 2;
670 }
671
672   /* calculate RGB color & emit signal */
673 static void internal_invoke_color_changed(HildonColorChooserHSV *sel)
674 {
675   HildonColorChooser *parent_sel = HILDON_COLOR_CHOOSER(sel);
676   GdkVisual *system_visual = gdk_visual_get_system();
677   unsigned long rgb[3], rgb2[3];
678
679
680   inline_h2rgb(sel->currhue, rgb);
681
682   rgb2[0] = 0xffffff - rgb[0];
683   rgb2[1] = 0xffffff - rgb[1];
684   rgb2[2] = 0xffffff - rgb[2];
685
686
687   parent_sel->color.red   = ((rgb[0] >> 8) + ((rgb2[0] >> 8) * (0xffff - sel->currsat) / 0xffff)) * sel->currval / 0xffff;
688   parent_sel->color.green = ((rgb[1] >> 8) + ((rgb2[1] >> 8) * (0xffff - sel->currsat) / 0xffff)) * sel->currval / 0xffff;
689   parent_sel->color.blue  = ((rgb[2] >> 8) + ((rgb2[2] >> 8) * (0xffff - sel->currsat) / 0xffff)) * sel->currval / 0xffff;
690
691   parent_sel->color.pixel = ((parent_sel->color.red >> (16 - system_visual->red_prec)) << system_visual->red_shift) |
692                             ((parent_sel->color.green >> (16 - system_visual->green_prec)) << system_visual->green_shift) |
693                             ((parent_sel->color.blue >> (16 - system_visual->blue_prec)) << system_visual->blue_shift);
694
695
696   hildon_color_chooser_emit_color_changed(HILDON_COLOR_CHOOSER(sel));
697 }
698
699   /* do the RGB -> HSV conversion here, not so time critical */
700 static void hildon_color_chooser_hsv_set_color(HildonColorChooser *sel, GdkColor *color)
701 {
702   HildonColorChooserHSV *sel_hsv = HILDON_COLOR_CHOOSER_HSV(sel);
703   unsigned short hue, sat, val;
704   unsigned long min, max;
705   signed long tmp, diff;
706
707     /* ugly nesting */
708   min = MIN(MIN(color->red, color->green), color->blue);
709   max = MAX(MAX(color->red, color->green), color->blue);
710   diff = max - min;
711
712
713   val = max;
714
715   if(val > 0 && diff != 0) {
716     sat = (diff * 0x0000ffff) / max;
717
718     if(color->red == max) {
719       tmp = (signed)color->green - (signed)color->blue;
720       tmp *= 10922;
721       tmp /= diff;
722       if(tmp < 0) {
723         tmp += 65532;
724       }
725       hue = tmp;
726     } else if(color->green == max) {
727       hue = (((signed long)color->blue - (signed long)color->red)*10922 / diff) + 21844;
728     } else {
729       hue = (((signed long)color->red - (signed long)color->green)*10922 / diff) + 43688;
730     }
731   } else {
732     hue = 0;
733     sat = 0;
734   }
735
736
737   sel_hsv->currhue = hue;
738   sel_hsv->currsat = sat;
739   sel_hsv->currval = val;
740
741
742   inline_limited_expose(sel_hsv);
743 }
744
745
746 #define FULL_COLOR 0x00ffffff
747 inline void inline_h2rgb(unsigned short hue, unsigned long *rgb)
748 {
749   unsigned short hue_rotation, hue_value;
750
751   hue_rotation  = hue / 10922;
752   hue_value     = hue % 10922;
753
754
755   switch(hue_rotation) {
756   case 0:
757   case 6:
758     rgb[0] = FULL_COLOR;
759     rgb[1] = hue_value * 6*256;
760     rgb[2] = 0;
761     break;
762   case 1:
763     rgb[0] = FULL_COLOR - (hue_value * 6*256);
764     rgb[1] = FULL_COLOR;
765     rgb[2] = 0;
766     break;
767   case 2:
768     rgb[0] = 0;
769     rgb[1] = FULL_COLOR;
770     rgb[2] = hue_value * 6*256;
771     break;
772   case 3:
773     rgb[0] = 0;
774     rgb[1] = FULL_COLOR - (hue_value * 6*256);
775     rgb[2] = FULL_COLOR;
776     break;
777   case 4:
778     rgb[0] = hue_value * 6*256;
779     rgb[1] = 0;
780     rgb[2] = FULL_COLOR;
781     break;
782   case 5:
783     rgb[0] = FULL_COLOR;
784     rgb[1] = 0;
785     rgb[2] = FULL_COLOR - (hue_value * 6*256);
786     break;
787   default:
788     rgb[0] = 0;
789     rgb[1] = 0;
790     rgb[2] = 0;
791     break;
792   }
793 }
794
795 #define FULL_COLOR8 0xff
796 static void intern_h2rgb8(unsigned short hue, unsigned char *rgb)
797 {
798   unsigned short hue_rotation, hue_value;
799
800   hue >>= 8;
801   hue_rotation  = hue / 42;
802   hue_value     = hue % 42;
803
804
805   switch(hue_rotation) {
806   case 0:
807   case 6:
808     rgb[0] = FULL_COLOR8;
809     rgb[1] = hue_value * 6;
810     rgb[2] = 0;
811     break;
812   case 1:
813     rgb[0] = FULL_COLOR8 - (hue_value * 6);
814     rgb[1] = FULL_COLOR8;
815     rgb[2] = 0;
816     break;
817   case 2:
818     rgb[0] = 0;
819     rgb[1] = FULL_COLOR8;
820     rgb[2] = hue_value * 6;
821     break;
822   case 3:
823     rgb[0] = 0;
824     rgb[1] = FULL_COLOR8 - (hue_value * 6);
825     rgb[2] = FULL_COLOR8;
826     break;
827   case 4:
828     rgb[0] = hue_value * 6;
829     rgb[1] = 0;
830     rgb[2] = FULL_COLOR8;
831     break;
832   case 5:
833     rgb[0] = FULL_COLOR8;
834     rgb[1] = 0;
835     rgb[2] = FULL_COLOR8 - (hue_value * 6);
836     break;
837   default:
838     rgb[0] = 0;
839     rgb[1] = 0;
840     rgb[2] = 0;
841     break;
842   }
843 }
844
845
846   /* optimization: do not ask hue for each round but have bilinear vectors */
847   /* rethink: benefits from handling data 8 bit? (no shift round) */
848 inline void inline_draw_hue_bar(GtkWidget *widget, int x, int y, int w, int h, int sy, int sh)
849 {
850   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(widget);
851   unsigned short hvec, hcurr;
852   unsigned char *buf, *ptr, tmp[3];
853   int i, j, tmpy;
854
855
856   if(w <= 0 || h <= 0) {
857     return;
858   }
859
860
861   buf = (unsigned char *)g_malloc(w*h*3);
862
863   hvec = 65535/sh;
864   hcurr = hvec * (y - sy);
865
866   ptr = buf;
867
868
869   for(i = 0; i < h; i++) {
870     intern_h2rgb8(hcurr, tmp);
871
872     for(j = 0; j < w; j++) {
873       ptr[0] = tmp[0];
874       ptr[1] = tmp[1];
875       ptr[2] = tmp[2];
876       ptr += 3;
877     }
878
879     hcurr += hvec;
880   }
881
882
883   gdk_draw_rgb_image(widget->parent->window, widget->style->fg_gc[0], x, y, w, h, GDK_RGB_DITHER_NONE, buf, w*3);
884
885   tmpy = sel->hba.y + (sel->currhue * sel->hba.height / 0xffff);
886   gdk_draw_line(widget->parent->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], sel->hba.x, tmpy, sel->hba.x + sel->hba.width - 1, tmpy);
887
888   if((((sel->currhue * sel->hba.height) & 0xffff) > 0x8000) && (tmpy < (sel->hba.y + sel->hba.height))) {
889     gdk_draw_line(widget->parent->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], sel->hba.x, tmpy+1, sel->hba.x + sel->hba.width - 1, tmpy+1);
890   } else if(tmpy > sel->hba.y) {
891     gdk_draw_line(widget->parent->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], sel->hba.x, tmpy-1, sel->hba.x + sel->hba.width - 1, tmpy-1);
892   }
893
894
895   g_free(buf);
896 }
897
898 inline void inline_draw_hue_bar_dimmed(GtkWidget *widget, int x, int y, int w, int h, int sy, int sh)
899 {
900   unsigned short hvec, hcurr, avg;
901   unsigned char *buf, *ptr, tmp[3];
902   int i, j;
903
904
905   if(w <= 0 || h <= 0) {
906     return;
907   }
908
909
910   buf = (unsigned char *)g_malloc(w*h*3);
911
912   hvec = 65535/sh;
913   hcurr = hvec * (y - sy);
914
915   ptr = buf;
916
917
918   for(i = 0; i < h; i++) {
919     intern_h2rgb8(hcurr, tmp);
920
921     for(j = 0; j < w; j++) {
922       avg = ((unsigned short)tmp[0]*3 + (unsigned short)tmp[1]*2 + (unsigned short)tmp[2])/6;
923       ptr[0] = avg;
924       ptr[1] = avg;
925       ptr[2] = avg;
926       ptr += 3;
927     }
928
929     hcurr += hvec;
930   }
931
932
933   gdk_draw_rgb_image(widget->parent->window, widget->style->fg_gc[0], x, y, w, h, GDK_RGB_DITHER_NONE, buf, w*3);
934
935
936   g_free(buf);
937 }
938
939
940 inline void inline_draw_crosshair(unsigned char *buf, int x, int y, int w, int h)
941 {
942   int i, j, sx, sy;
943
944     /* bad "clipping", clip the loop to save cpu */
945   for(i = 0; i < 8; i++) {
946     for(j = 0; j < 8; j++) {
947       sx = j + x; sy = i + y;
948
949       if(sx >= 0 && sx < w && sy >= 0 && sx < h) {
950         if(crosshair[j + 8*i]) {
951           if(crosshair[j + 8*i] & 0x1) {
952             buf[(sx)*3+(sy)*w*3+0] = 255;
953             buf[(sx)*3+(sy)*w*3+1] = 255;
954             buf[(sx)*3+(sy)*w*3+2] = 255;
955           } else {
956             buf[(sx)*3+(sy)*w*3+0] = 0;
957             buf[(sx)*3+(sy)*w*3+1] = 0;
958             buf[(sx)*3+(sy)*w*3+2] = 0;
959           }
960         }
961       }
962     }
963   }
964 }
965
966
967 inline void inline_draw_sv_plane(HildonColorChooserHSV *sel, int x, int y, int w, int h)
968 {
969   GtkWidget *widget = GTK_WIDGET(sel);
970   unsigned char *buf, *ptr;
971   unsigned long rgbx[3] = { 0x00ffffff, 0x00ffffff, 0x00ffffff }, rgbtmp[3];
972   signed long rgby[3];
973   int tmp = sel->spa.width*sel->spa.height, i, j;
974
975
976   if(w <= 0 || h <= 0) {
977     return;
978   }
979
980
981   buf = (unsigned char *)g_malloc(w*h*3);
982
983   ptr = buf;
984
985
986   inline_h2rgb(sel->currhue, rgbtmp);
987
988   rgby[0] = rgbtmp[0] - rgbx[0];
989   rgby[1] = rgbtmp[1] - rgbx[1];
990   rgby[2] = rgbtmp[2] - rgbx[2];
991
992   rgbx[0] /= sel->spa.width;
993   rgbx[1] /= sel->spa.width;
994   rgbx[2] /= sel->spa.width;
995
996   rgby[0] /= tmp;
997   rgby[1] /= tmp;
998   rgby[2] /= tmp;
999
1000
1001   rgbx[0] += (y - sel->spa.y)*rgby[0];
1002   rgbx[1] += (y - sel->spa.y)*rgby[1];
1003   rgbx[2] += (y - sel->spa.y)*rgby[2];
1004
1005
1006   for(i = 0; i < h; i++) {
1007     rgbtmp[0] = rgbx[0] * (x - sel->spa.x);
1008     rgbtmp[1] = rgbx[1] * (x - sel->spa.x);
1009     rgbtmp[2] = rgbx[2] * (x - sel->spa.x);
1010
1011     for(j = 0; j < w; j++) {
1012       ptr[0] = rgbtmp[0] >> 16;
1013       ptr[1] = rgbtmp[1] >> 16;
1014       ptr[2] = rgbtmp[2] >> 16;
1015       rgbtmp[0] += rgbx[0];
1016       rgbtmp[1] += rgbx[1];
1017       rgbtmp[2] += rgbx[2];
1018       ptr += 3;
1019     }
1020
1021     rgbx[0] += rgby[0];
1022     rgbx[1] += rgby[1];
1023     rgbx[2] += rgby[2];
1024   }
1025
1026
1027   inline_draw_crosshair(buf, (sel->spa.width * sel->currval / 0xffff) - x + sel->spa.x - 4, (sel->spa.height * sel->currsat / 0xffff) - y + sel->spa.y - 4, w, h);
1028
1029
1030   gdk_draw_rgb_image(widget->parent->window, widget->style->fg_gc[0], x, y, w, h, GDK_RGB_DITHER_NONE, buf, w*3);
1031
1032
1033   g_free(buf);
1034 }
1035
1036 inline void inline_draw_sv_plane_dimmed(HildonColorChooserHSV *sel, int x, int y, int w, int h)
1037 {
1038   GtkWidget *widget = GTK_WIDGET(sel);
1039   unsigned char *buf, *ptr;
1040   unsigned long rgbx[3] = { 0x00ffffff, 0x00ffffff, 0x00ffffff }, rgbtmp[3];
1041   unsigned long avg;
1042   signed long rgby[3];
1043   int tmp = sel->spa.width*sel->spa.height, i, j;
1044
1045
1046   if(w <= 0 || h <= 0) {
1047     return;
1048   }
1049
1050
1051   buf = (unsigned char *)g_malloc(w*h*3);
1052
1053   ptr = buf;
1054
1055
1056     /* possibe optimization: as we are drawing grayscale plane, there might
1057        be some simpler algorithm to do this*/
1058   rgbtmp[0] = 0x00ffffff;
1059   rgbtmp[1] = 0x00000000;
1060   rgbtmp[2] = 0x00000000;
1061
1062   rgby[0] = rgbtmp[0] - rgbx[0];
1063   rgby[1] = rgbtmp[1] - rgbx[1];
1064   rgby[2] = rgbtmp[2] - rgbx[2];
1065
1066   rgbx[0] /= sel->spa.width;
1067   rgbx[1] /= sel->spa.width;
1068   rgbx[2] /= sel->spa.width;
1069
1070   rgby[0] /= tmp;
1071   rgby[1] /= tmp;
1072   rgby[2] /= tmp;
1073
1074
1075   rgbx[0] += (y - sel->spa.y)*rgby[0];
1076   rgbx[1] += (y - sel->spa.y)*rgby[1];
1077   rgbx[2] += (y - sel->spa.y)*rgby[2];
1078
1079
1080   for(i = 0; i < h; i++) {
1081     rgbtmp[0] = rgbx[0] * (x - sel->spa.x);
1082     rgbtmp[1] = rgbx[1] * (x - sel->spa.x);
1083     rgbtmp[2] = rgbx[2] * (x - sel->spa.x);
1084
1085     for(j = 0; j < w; j++) {
1086       avg = (rgbtmp[0] + rgbtmp[1] + rgbtmp[2])/3;
1087       avg >>= 16;
1088       ptr[0] = avg;
1089       ptr[1] = avg;
1090       ptr[2] = avg;
1091       rgbtmp[0] += rgbx[0];
1092       rgbtmp[1] += rgbx[1];
1093       rgbtmp[2] += rgbx[2];
1094       ptr += 3;
1095     }
1096
1097     rgbx[0] += rgby[0];
1098     rgbx[1] += rgby[1];
1099     rgbx[2] += rgby[2];
1100   }
1101
1102
1103   gdk_draw_rgb_image(widget->parent->window, widget->style->fg_gc[0], x, y, w, h, GDK_RGB_DITHER_NONE, buf, w*3);
1104
1105
1106   g_free(buf);
1107 }
1108
1109
1110 static gboolean hildon_color_chooser_hsv_expose_timer(gpointer data)
1111 {
1112   HildonColorChooserHSV *sel = HILDON_COLOR_CHOOSER_HSV(data);
1113
1114
1115   if(sel->expose_info.expose_queued) {
1116     gtk_widget_queue_draw(GTK_WIDGET(data));
1117   }
1118
1119
1120   return FALSE;
1121 }