It appears Solaris's cc is ignoring the signedness of bitfield types.
[xiph/unicode.git] / sushivision / slider.c
blob036fc0bdfdaa2e229351e898d52ff1fa043a5f5b
1 /*
3 * sushivision copyright (C) 2006-2007 Monty <monty@xiph.org>
5 * sushivision is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * sushivision is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with sushivision; see the file COPYING. If not, write to the
17 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 #define _GNU_SOURCE
23 #include <stdlib.h>
24 #include <math.h>
25 #include <string.h>
26 #include <gtk/gtk.h>
27 #include <gdk/gdkkeysyms.h>
28 #include "internal.h"
30 static float val_to_pixel(_sv_slider_t *s, float val);
32 static int total_slice_width(_sv_slider_t *s){
33 int i;
34 int count=0;
35 if(s->flip)
36 for(i=0;i<s->num_slices;i++)
37 count += s->slices[i]->allocation.height;
38 else
39 for(i=0;i<s->num_slices;i++)
40 count += s->slices[i]->allocation.width;
41 return count;
44 static int slice_width(_sv_slider_t *s,int slices){
45 int i;
46 int count=0;
47 if(s->flip)
48 for(i=0;i<slices;i++)
49 count += s->slices[i]->allocation.height;
50 else
51 for(i=0;i<slices;i++)
52 count += s->slices[i]->allocation.width;
53 return count;
56 static int total_slice_height(_sv_slider_t *s){
57 int i;
58 int max=0;
59 if(s->flip){
60 for(i=0;i<s->num_slices;i++)
61 if(max<s->slices[i]->allocation.width)
62 max = s->slices[i]->allocation.width;
63 }else{
64 for(i=0;i<s->num_slices;i++)
65 if(max<s->slices[i]->allocation.height)
66 max = s->slices[i]->allocation.height;
68 return max;
71 /* guess where I came from. */
72 static void rounded_rectangle (cairo_t *c,
73 float x, float y, float w, float h,
74 float radius)
76 cairo_move_to (c, x+radius, y);
77 cairo_arc (c, x+w-radius, y+radius, radius, M_PI * 1.5, M_PI * 2);
78 cairo_arc (c, x+w-radius, y+h-radius, radius, 0, M_PI * 0.5);
79 cairo_arc (c, x+radius, y+h-radius, radius, M_PI * 0.5, M_PI);
80 cairo_arc (c, x+radius, y+radius, radius, M_PI, M_PI * 1.5);
83 static float shades[] = {1.15, 0.95, 0.896, 0.82, 0.7, 0.665, 0.5, 0.45, 0.4};
85 static void bg_set(GtkWidget *w, cairo_t *c){
86 _sv_slice_t *sl = SLICE(w);
87 GdkColor *bg = &w->style->bg[sl->thumb_state?GTK_STATE_ACTIVE:GTK_STATE_NORMAL];
88 float shade_r=bg->red/65535.;
89 float shade_g=bg->green/65535.;
90 float shade_b=bg->blue/65535.;
92 cairo_set_source_rgb (c, shade_r,shade_g,shade_b);
95 static void fg_shade(GtkWidget *w, cairo_t *c, int shade){
96 _sv_slice_t *sl = SLICE(w);
97 GdkColor *fg = &w->style->fg[sl->thumb_state?GTK_STATE_ACTIVE:GTK_STATE_NORMAL];
98 float shade_r=fg->red*shades[shade]/65535;
99 float shade_g=fg->green*shades[shade]/65535;
100 float shade_b=fg->blue*shades[shade]/65535;
102 cairo_set_source_rgb (c, shade_r,shade_g,shade_b);
105 static void parent_shade(_sv_slider_t *s, cairo_t *c, int shade){
106 GtkWidget *parent=gtk_widget_get_parent(s->slices[0]);
107 GdkColor *bg = &parent->style->bg[GTK_STATE_NORMAL];
108 float shade_r=bg->red*shades[shade]/65535;
109 float shade_g=bg->green*shades[shade]/65535;
110 float shade_b=bg->blue*shades[shade]/65535;
112 cairo_set_source_rgb (c, shade_r,shade_g,shade_b);
115 static void _sv_slider_draw_background(_sv_slider_t *s){
116 if(!s->realized)return;
118 int i;
119 GtkWidget *parent=gtk_widget_get_parent(s->slices[0]);
120 GdkColor *text = &s->slices[0]->style->text[0];
121 GdkColor *bg = &parent->style->bg[0];
122 int textborder=1;
123 float textr=text->red;
124 float textg=text->green;
125 float textb=text->blue;
127 int x=0;
128 int y=0;
129 int w=s->w;
130 int h=s->h;
132 int tx=x;
133 int ty=y+s->ypad;
134 int tw=w;
135 int th=h - s->ypad*2;
136 cairo_pattern_t *pattern;
138 // prepare background
139 cairo_t *c = cairo_create(s->background);
141 // Fill with bg color
142 gdk_cairo_set_source_color(c,bg);
143 cairo_rectangle(c,0,0,w,h);
144 cairo_fill(c);
146 cairo_rectangle (c, x+1, ty, w-2, th);
147 bg_set(s->slices[0],c);
148 cairo_fill (c);
149 cairo_surface_flush(s->background);
151 // Create trough innards
152 if(s->gradient>=0){
153 // background map gradient
154 // this happens 'under' cairo
155 u_int32_t *pixel=s->backdata+ty*s->w;
157 for(i=tx;i<tx+tw;i++){
158 _sv_lcolor_t outc = {0,0,0,0};
160 mapfunc[s->gradient](rint(_sv_slider_pixel_to_mapdel(s,i)*65536.f),255,&outc);
162 pixel[i] = mixfunc[s->gradient]( (_sv_ucolor_t)(u_int32_t)((outc.a<<24) + (outc.r<<16) + (outc.g<<8) + outc.b),
163 (_sv_ucolor_t)pixel[i]).u | 0xff000000;
165 for(i=ty+1;i<ty+th;i++){
166 memcpy(pixel+w,pixel,w*4);
167 pixel+=s->w;
169 }else{
170 // normal background
171 textborder=0;
174 // Top shadow
175 cairo_rectangle (c, tx+1, ty, tw-2, 4);
176 pattern = cairo_pattern_create_linear (0, ty-1, 0, ty+3);
177 cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0., 0., 0., 0.2);
178 cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0., 0., 0., 0.);
179 cairo_set_source (c, pattern);
180 cairo_fill (c);
181 cairo_pattern_destroy (pattern);
183 // Left shadow
184 cairo_rectangle (c, tx+1, ty, tx+4, th);
185 pattern = cairo_pattern_create_linear (tx, ty-1, tx+3, ty-1);
186 cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0., 0., 0., 0.1);
187 cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0., 0., 0., 0.);
188 cairo_set_source (c, pattern);
189 cairo_fill (c);
190 cairo_pattern_destroy (pattern);
192 // lines & labels
193 for(i=0;i<s->labels;i++){
194 int x=val_to_pixel(s,s->label_vals[i])+.5;
195 int y=s->h-s->ypad-1.5;
197 cairo_move_to(c,x+.5,s->ypad+2);
198 cairo_line_to(c,x+.5,s->h-s->ypad-2);
199 cairo_set_source_rgba(c,textr,textg,textb,.8);
200 cairo_set_line_width(c,1);
201 cairo_stroke(c);
203 if(i>0){
204 cairo_text_extents_t ex;
205 cairo_text_extents (c, s->label[i], &ex);
207 x-=2;
208 x-=ex.width;
210 }else{
211 x+=2;
214 if(textborder){
215 cairo_set_source_rgba(c,1.,1.,1.,.5);
216 cairo_set_line_width(c,2.5);
217 cairo_move_to (c, x,y);
218 cairo_text_path (c, s->label[i]);
219 cairo_stroke(c);
222 cairo_set_source_rgba(c,textr,textg,textb,1.);
223 cairo_move_to (c, x,y);
224 cairo_show_text (c, s->label[i]);
227 // Draw border
228 rounded_rectangle (c, tx+0.5, ty-0.5, tw-1, th+1, 1.5);
229 parent_shade(s,c,7);
230 cairo_set_line_width (c, 1.0);
231 cairo_stroke (c);
233 cairo_destroy(c);
236 void _sv_slider_draw(_sv_slider_t *s){
237 if(!s->realized)return;
239 int i;
240 cairo_t *c;
241 //int w=s->w;
242 int h=s->h;
243 int w=s->w;
245 c = cairo_create(s->foreground);
247 if(s->flip){
248 cairo_matrix_t m = {0.,-1., 1.,0., 0.,w};
249 cairo_set_matrix(c,&m);
252 cairo_set_source_surface(c,s->background,0,0);
253 cairo_rectangle(c,0,0,w,h);
254 cairo_fill(c);
256 // thumbs
257 for(i=0;i<s->num_slices;i++){
258 GtkWidget *sl = s->slices[i];
259 float x = rint(val_to_pixel(s,((_sv_slice_t *)(s->slices[i]))->thumb_val))+.5;
261 float rad = 2.;
263 float y = rint(h/2)+.5;
264 float xd = y*.575;
265 float rx = 1.73*rad;
266 cairo_pattern_t *pattern;
268 if(SLICE(sl)->thumb_active){
269 if ((s->num_slices == 1) || (s->num_slices == 3 && i==1)){
270 // outline
271 cairo_move_to(c,x,s->h/2);
272 cairo_arc_negative(c, x+xd-rx, rad+.5, rad, 30.*(M_PI/180.), 270.*(M_PI/180.));
273 cairo_arc_negative(c, x-xd+rx, rad+.5, rad, 270.*(M_PI/180.), 150.*(M_PI/180.));
274 cairo_close_path(c);
276 fg_shade(sl,c,2);
277 cairo_fill_preserve(c);
279 cairo_set_line_width(c,1);
280 fg_shade(sl,c,7);
282 if(((_sv_slice_t *)s->slices[i])->thumb_focus)
283 cairo_set_source_rgba(c,0,0,0,1);
285 cairo_stroke_preserve(c);
286 cairo_set_dash (c, NULL, 0, 0.);
288 // top highlight
289 pattern = cairo_pattern_create_linear (0, 0, 0, 4);
290 cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.2);
291 cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);
292 cairo_set_source (c, pattern);
293 cairo_fill_preserve (c);
294 cairo_pattern_destroy (pattern);
296 // Left highlight
297 pattern = cairo_pattern_create_linear (x-xd+3, 0, x-xd+6, 0);
298 cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.1);
299 cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);
300 cairo_set_source (c, pattern);
301 cairo_fill (c);
302 cairo_pattern_destroy (pattern);
304 // needle shadow
305 cairo_set_line_width(c,2);
306 cairo_move_to(c,x,s->h/2+3);
307 cairo_line_to(c,x,s->ypad/2);
308 cairo_set_source_rgba(c,0.,0.,0.,.5);
309 cairo_stroke(c);
311 // needle
312 cairo_set_line_width(c,1);
313 cairo_move_to(c,x,s->h/2+2);
314 cairo_line_to(c,x,s->ypad/2);
315 cairo_set_source_rgb(c,1.,1.,0);
316 cairo_stroke(c);
318 }else{
319 if(i==0){
320 // bracket left
322 // outline
323 cairo_move_to(c,x,s->h/2);
324 cairo_line_to(c,x-xd/2,s->h/2);
325 cairo_arc_negative(c, x-xd*3/2+rx, h-rad-.5, rad, 210.*(M_PI/180.), 90.*(M_PI/180.));
326 cairo_line_to(c, x, h-.5);
327 cairo_close_path(c);
329 fg_shade(sl,c,2);
330 cairo_set_line_width(c,1);
331 cairo_fill_preserve(c);
332 fg_shade(sl,c,7);
333 if(((_sv_slice_t *)s->slices[i])->thumb_focus)
334 cairo_set_source_rgba(c,0,0,0,1);
335 cairo_stroke_preserve(c);
337 // top highlight
338 pattern = cairo_pattern_create_linear (0, y, 0, y+4);
339 cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.2);
340 cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);
341 cairo_set_source (c, pattern);
342 cairo_fill_preserve (c);
343 cairo_pattern_destroy (pattern);
345 // Left highlight
346 pattern = cairo_pattern_create_linear (x-xd*3/2+3, 0, x-xd*3/2+6, 0);
347 cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.1);
348 cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);
349 cairo_set_source (c, pattern);
350 cairo_fill (c);
351 cairo_pattern_destroy (pattern);
352 }else{
354 // bracket right
356 // outline
357 cairo_move_to(c,x,s->h/2);
358 cairo_line_to(c,x+xd/2,s->h/2);
359 cairo_arc(c, x+xd*3/2-rx, h-rad-.5, rad, 330.*(M_PI/180.), 90.*(M_PI/180.));
360 cairo_line_to(c, x, h-.5);
361 cairo_close_path(c);
363 fg_shade(sl,c,2);
364 cairo_set_line_width(c,1);
365 cairo_fill_preserve(c);
366 fg_shade(sl,c,7);
367 if(((_sv_slice_t *)s->slices[i])->thumb_focus)
368 cairo_set_source_rgba(c,0,0,0,1);
369 cairo_stroke_preserve(c);
371 // top highlight
372 pattern = cairo_pattern_create_linear (0, y, 0, y+4);
373 cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.2);
374 cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);
375 cairo_set_source (c, pattern);
376 cairo_fill_preserve (c);
377 cairo_pattern_destroy (pattern);
379 // Left highlight
380 pattern = cairo_pattern_create_linear (x+1, 0, x+4, 0);
381 cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.1);
382 cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);
383 cairo_set_source (c, pattern);
384 cairo_fill (c);
385 cairo_pattern_destroy (pattern);
388 // needle shadow
389 cairo_set_line_width(c,2);
390 cairo_move_to(c,x,s->h/2-3);
391 cairo_line_to(c,x,h-s->ypad/2);
392 cairo_set_source_rgba(c,0.,0.,0.,.5);
393 cairo_stroke(c);
395 // needle
396 cairo_set_line_width(c,1);
397 cairo_move_to(c,x,s->h/2-2);
398 cairo_line_to(c,x,h-s->ypad/2);
399 cairo_set_source_rgb(c,1.,1.,0);
400 cairo_stroke(c);
405 cairo_destroy(c);
408 void _sv_slider_realize(_sv_slider_t *s){
409 int w = total_slice_width(s);
410 int h = total_slice_height(s);
411 if(s->background == 0 || w != s->w || h != s->h){
413 if(s->background)
414 cairo_surface_destroy(s->background);
416 if(s->foreground)
417 cairo_surface_destroy(s->foreground);
419 if(s->backdata)
420 free(s->backdata);
422 s->backdata = calloc(w*h,4);
424 s->background = cairo_image_surface_create_for_data ((unsigned char *)s->backdata,
425 CAIRO_FORMAT_RGB24,
426 w,h,w*4);
427 if(s->flip){
428 s->foreground = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
429 h,w);
430 }else{
431 s->foreground = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
432 w,h);
435 s->w=w;
436 s->h=h;
438 s->xpad=h*.45;
439 if(s->xpad<4)s->xpad=4;
440 s->realized = 1;
441 _sv_slider_draw_background(s);
442 _sv_slider_draw(s);
445 s->realized = 1;
448 static float val_to_pixel(_sv_slider_t *s,float v){
449 int j;
450 float ret=0;
451 float neg = (s->neg? -1.: 1.);
452 int tx=s->xpad;
453 int tw=s->w - tx*2;
455 v*=neg;
457 if( v<s->label_vals[0]*neg){
458 ret=0;
459 }else if(v>s->label_vals[s->labels-1]*neg){
460 ret=tw;
461 }else{
462 for(j=0;j<s->labels;j++){
463 if(v>=s->label_vals[j]*neg && v<=s->label_vals[j+1]*neg){
464 v*=neg;
465 float del=(v-s->label_vals[j])/(s->label_vals[j+1]-s->label_vals[j]);
466 float pixlo=rint((float)(j)/(s->labels-1)*tw);
467 float pixhi=rint((float)(j+1)/(s->labels-1)*tw);
468 ret=pixlo*(1.-del)+pixhi*del+tx;
469 break;
474 return ret;
477 float _sv_slider_val_to_del(_sv_slider_t *s,float v){
478 if(isnan(v))return NAN;
479 int j=s->labels-1;
481 if(s->neg){
482 while(--j)
483 if(v<=s->label_vals[j])break;
484 }else{
485 while(--j)
486 if(v>s->label_vals[j])break;
488 return (j + (v-s->label_vals[j])/
489 (s->label_vals[j+1]-s->label_vals[j]))/
490 (ret->labels-1);
494 float _sv_slider_val_to_mapdel(_sv_slider_t *s,float v){
495 int j=s->labels-1;
496 if(isnan(v))return NAN;
498 if(s->neg){
500 if(v > s->al)return NAN;
501 if(v >= s->lo)return 0.;
502 if(v <= s->hi)return 1.;
503 while(--j)
504 if(v<=s->label_vals[j])break;
506 }else{
508 if(v < s->al)return NAN;
509 if(v <= s->lo)return 0.;
510 if(v >= s->hi)return 1.;
511 while(--j)
512 if(v>s->label_vals[j])break;
516 return v*s->labeldelB[j] + s->labelvalB[j];
519 void _sv_slider_expose_slice(_sv_slider_t *s, int slicenum){
520 _sv_slice_t *slice = (_sv_slice_t *)(s->slices[slicenum]);
521 GtkWidget *w = GTK_WIDGET(slice);
523 if(GTK_WIDGET_REALIZED(w)){
524 cairo_t *c = gdk_cairo_create(w->window);
526 _sv_slider_realize(s);
527 if(s->flip){
528 cairo_set_source_surface(c,s->foreground,0, slice_width(s,slicenum+1)-total_slice_width(s));
529 }else{
530 cairo_set_source_surface(c,s->foreground,-slice_width(s,slicenum),0);
532 cairo_rectangle(c,0,0,w->allocation.width,w->allocation.height);
533 cairo_fill(c);
535 cairo_destroy(c);
539 void _sv_slider_expose(_sv_slider_t *s){
540 int i;
541 for(i=0;i<s->num_slices;i++)
542 _sv_slider_expose_slice(s,i);
545 void _sv_slider_size_request_slice(_sv_slider_t *s,GtkRequisition *requisition){
546 int maxx=0,x0=0,x1=0,maxy=0,i,w;
548 // need a dummy surface to find text sizes
549 cairo_surface_t *dummy=cairo_image_surface_create(CAIRO_FORMAT_RGB24,1,1);
550 cairo_t *c = cairo_create(dummy);
552 // find the widest label
553 for(i=0;i<s->labels;i++){
554 cairo_text_extents_t ex;
555 cairo_text_extents(c, s->label[i], &ex);
556 if(i==0) x0 = ex.width;
557 if(i==1) x1 = ex.width;
558 if(ex.width > maxx)maxx=ex.width;
559 if(ex.height > maxy)maxy=ex.height;
562 maxx*=1.5;
563 // also check first + second label width
564 if(x0+x1*1.2 > maxx)maxx=(x0+x1)*1.2;
566 w = (maxx+2)*s->labels+4;
567 if(w<200)w=200;
569 if(s->flip){
570 requisition->height = (w+s->num_slices-1)/s->num_slices;
571 requisition->width = maxy+4+s->ypad*2;
572 }else{
573 requisition->width = (w+s->num_slices-1)/s->num_slices;
574 requisition->height = maxy+4+s->ypad*2;
576 cairo_destroy(c);
577 cairo_surface_destroy(dummy);
580 static float slice_adjust_pixel(_sv_slider_t *s,int slicenum, float x){
581 float width = slice_width(s,slicenum);
582 return x+width;
585 static float quant(_sv_slider_t *s, float val){
586 if(s->quant_denom!=0.){
587 val *= s->quant_denom;
588 val /= s->quant_num;
590 val = rint(val);
592 val *= s->quant_num;
593 val /= s->quant_denom;
595 return val;
598 float _sv_slider_pixel_to_val(_sv_slider_t *s,float x){
599 int tx=s->xpad;
600 int tw=s->w - tx*2;
601 float del = (float)(x-tx)/tw;
602 if(del<0)
603 return quant(s,s->label_vals[0]);
604 if(del>=1.)
605 return quant(s,(s->label_vals[s->labels-1]));
606 return _sv_slider_del_to_val(s,del);
609 float _sv_slider_pixel_to_del(_sv_slider_t *s,float x){
610 int tx=s->xpad;
611 int tw=s->w - tx*2;
612 x-=tx;
614 if(x<=0){
615 return 0.;
616 }else if (x>tw){
617 return 1.;
618 }else
619 return x/tw;
622 float _sv_slider_pixel_to_mapdel(_sv_slider_t *s,float x){
623 int tx=s->xpad;
624 int tw=s->w - tx*2;
625 x = ((x-tx)/tw - s->lodel)*s->idelrange;
627 if (x<=0.) return 0.;
628 if (x>=1.) return 1.;
629 return x;
632 float _sv_slider_del_to_val(_sv_slider_t *s, float del){
633 int base;
634 if(isnan(del))return del;
636 del *= (s->labels-1);
637 base = floor(del);
638 del -= base;
640 return quant(s,( (1.-del)*s->label_vals[base] + del*s->label_vals[base+1] ));
643 void _sv_slider_vals_bound(_sv_slider_t *s,int slicenum){
644 int i,flag=-1;
645 _sv_slice_t *center = SLICE(s->slices[slicenum]);
646 float min = (s->neg ? s->label_vals[s->labels-1] : s->label_vals[0]);
647 float max = (s->neg ? s->label_vals[0] : s->label_vals[s->labels-1]);
648 int flip = (s->neg? 1: 0);
650 if(center->thumb_val < min)
651 center->thumb_val = min;
653 if(center->thumb_val > max)
654 center->thumb_val = max;
656 // now make sure other sliders have valid spacing
657 if( (s->flags & _SV_SLIDER_FLAG_INDEPENDENT_MIDDLE) &&
658 s->num_slices == 3)
659 flag=1;
661 for(i=slicenum-1; i>=0;i--){
662 int i2 = i+1;
663 if(i==flag)continue;
664 if(i2 == flag)i2++;
665 if(i2>=s->num_slices)continue;
667 _sv_slice_t *sl = SLICE(s->slices[i]);
668 _sv_slice_t *sl2 = SLICE(s->slices[i2]);
669 if((sl->thumb_val>sl2->thumb_val)^flip)
670 _sv_slice_thumb_set(sl,sl2->thumb_val);
673 for(i=slicenum+1; i<s->num_slices;i++){
674 int i2 = i-1;
675 if(i==flag)continue;
676 if(i2 == flag)i2--;
677 if(i2<0)continue;
679 _sv_slice_t *sl = SLICE(s->slices[i]);
680 _sv_slice_t *sl2 = SLICE(s->slices[i2]);
681 if((sl->thumb_val<sl2->thumb_val)^flip)
682 _sv_slice_thumb_set(sl,sl2->thumb_val);
687 static int determine_thumb(_sv_slider_t *s,int slicenum,int x,int y){
688 int i;
689 int best=-1;
690 float bestdist=s->w+1;
691 int n = s->num_slices;
693 if(s->flip){
694 _sv_slice_t *sl = SLICE(s->slices[slicenum]);
695 int temp = x;
696 x = sl->widget.allocation.height - y -1;
697 y = temp;
700 x=slice_adjust_pixel(s,slicenum,x);
701 for(i=0;i<n;i++){
702 _sv_slice_t *sl = SLICE(s->slices[i]);
703 if(sl->thumb_active){
704 float tx = val_to_pixel(s,sl->thumb_val) + i - s->num_slices/2;
705 float ty = ((n==3 && i==1) ? 0:s->h);
706 float d = hypot (x-tx,y-ty);
707 if(d<bestdist){
708 best=i;
709 bestdist=d;
713 return best;
716 static int lit_thumb(_sv_slider_t *s){
717 int i;
718 for(i=0;i<s->num_slices;i++){
719 _sv_slice_t *sl = SLICE(s->slices[i]);
720 if(sl->thumb_state)return i;
722 return -1;
725 void _sv_slider_unlight(_sv_slider_t *s){
726 int i;
727 for(i=0;i<s->num_slices;i++){
728 _sv_slice_t *sl = SLICE(s->slices[i]);
729 if(!sl->thumb_grab)
730 sl->thumb_state = 0;
734 int _sv_slider_lightme(_sv_slider_t *s,int slicenum,int x,int y){
735 int last = lit_thumb(s);
736 int best = determine_thumb(s,slicenum,x,y);
737 if(last != best){
738 _sv_slider_unlight(s);
739 if(best>-1){
740 _sv_slice_t *sl = SLICE(s->slices[best]);
741 sl->thumb_state=1;
743 return 1;
745 return 0;
748 void _sv_slider_button_press(_sv_slider_t *s,int slicenum,int x,int y){
749 int i;
750 for(i=0;i<s->num_slices;i++){
751 _sv_slice_t *sl = SLICE(s->slices[i]);
752 if(sl->thumb_state){
753 sl->thumb_grab=1;
754 sl->thumb_focus=1;
755 gtk_widget_grab_focus(GTK_WIDGET(sl));
757 if(sl->callback)sl->callback(sl->callback_data,0);
758 _sv_slider_motion(s,slicenum,x,y);
759 }else{
760 sl->thumb_grab=0;
761 sl->thumb_focus=0;
764 _sv_slider_draw(s);
765 _sv_slider_expose(s);
768 void _sv_slider_button_release(_sv_slider_t *s,int slicenum,int x,int y){
769 int i;
770 for(i=0;i<s->num_slices;i++){
771 _sv_slice_t *sl = SLICE(s->slices[i]);
773 if(sl->thumb_grab){
774 sl->thumb_grab=0;
775 if(sl->callback)sl->callback(sl->callback_data,2);
780 static void update_gradient(_sv_slider_t *s){
781 if(s->gradient && s->num_slices==3){
782 _sv_slice_t *sl = SLICE(s->slices[0]);
783 _sv_slice_t *sa = SLICE(s->slices[1]);
784 _sv_slice_t *sh = SLICE(s->slices[2]);
785 float ldel = _sv_slider_val_to_del(s,sl->thumb_val);
786 float hdel = _sv_slider_val_to_del(s,sh->thumb_val);
788 s->al = sa->thumb_val;
790 if(s->gradient->low != ldel ||
791 s->gradient->high != hdel){
792 int j;
794 s->idelrange = 1./(hdel-ldel);
795 s->lodel = ldel;
796 _sv_slider_draw_background(s);
797 _sv_slider_draw(s);
799 _sv_slider_expose(s);
803 void _sv_slider_motion(_sv_slider_t *s,int slicenum,int x,int y){
804 float altered[s->num_slices];
805 int i, grabflag=0;
806 _sv_slice_t *sl = SLICE(s->slices[slicenum]);
807 int px = (s->flip?sl->widget.allocation.height-y-1 : x);
809 for(i=0;i<s->num_slices;i++){
810 sl = SLICE(s->slices[i]);
811 altered[i] = sl->thumb_val;
814 /* is a thumb already grabbed? */
815 for(i=0;i<s->num_slices;i++){
816 sl = SLICE(s->slices[i]);
817 if(sl->thumb_grab){
818 sl->thumb_val=
819 _sv_slider_pixel_to_val(s,slice_adjust_pixel(s,slicenum,px));
820 _sv_slider_vals_bound(s,i);
821 grabflag = 1;
825 // did a gradient get altered?
826 update_gradient(s);
828 if(grabflag){
829 _sv_slider_draw(s);
830 _sv_slider_expose(s);
832 // call slice callbacks on all slices that were altered; value
833 // bounding might have affected slices other than the grabbed one.
835 for(i=0;i<s->num_slices;i++){
836 _sv_slice_t *sl = SLICE(s->slices[i]);
838 if(sl->thumb_val != altered[i])
839 if(sl->callback)sl->callback(sl->callback_data,1);
842 }else{
843 /* nothing grabbed right now; determine if we're in a thumb's area */
844 if(_sv_slider_lightme(s,slicenum,x,y)){
845 _sv_slider_draw(s);
846 _sv_slider_expose(s);
851 gboolean _sv_slider_key_press(_sv_slider_t *s,GdkEventKey *event,int slicenum){
852 _sv_slice_t *sl = (_sv_slice_t *)(s->slices[slicenum]);
853 int shift = (event->state&GDK_SHIFT_MASK);
854 if(event->state&GDK_MOD1_MASK) return FALSE;
855 if(event->state&GDK_CONTROL_MASK)return FALSE;
857 /* non-control keypresses */
858 switch(event->keyval){
859 case GDK_Left:
861 float x = val_to_pixel(s,sl->thumb_val);
862 while(sl->thumb_val > s->label_vals[0] &&
863 sl->thumb_val == _sv_slider_pixel_to_val(s,x))x--;
864 if(shift)
865 x-=9;
866 sl->thumb_val=_sv_slider_pixel_to_val(s,x);
867 _sv_slider_vals_bound(s,slicenum);
868 // did a gradient get altered?
869 update_gradient(s);
871 if(sl->callback){
872 sl->callback(sl->callback_data,0);
873 sl->callback(sl->callback_data,1);
874 sl->callback(sl->callback_data,2);
876 _sv_slider_draw(s);
877 _sv_slider_expose(s);
880 return TRUE;
882 case GDK_Right:
884 float x = val_to_pixel(s,sl->thumb_val);
885 while(sl->thumb_val < s->label_vals[s->labels-1] &&
886 sl->thumb_val == _sv_slider_pixel_to_val(s,x))x++;
887 if(shift)
888 x+=9;
889 sl->thumb_val=_sv_slider_pixel_to_val(s,x);
890 _sv_slider_vals_bound(s,slicenum);
891 // did a gradient get altered?
892 update_gradient(s);
894 if(sl->callback){
895 sl->callback(sl->callback_data,0);
896 sl->callback(sl->callback_data,1);
897 sl->callback(sl->callback_data,2);
899 _sv_slider_draw(s);
900 _sv_slider_expose(s);
902 return TRUE;
905 return FALSE; // keep processing
908 _sv_slider_t *_sv_slider_new(_sv_slice_t **slices, int num_slices, char **labels, float *label_vals, int num_labels,
909 unsigned flags){
910 int i;
911 _sv_slider_t *ret = calloc(1,sizeof(*ret));
913 ret->slices = (GtkWidget **)slices;
914 ret->num_slices = num_slices;
915 ret->quant_num=0.;
916 ret->quant_denom=0.;
918 ret->label = calloc(num_labels,sizeof(*ret->label));
919 for(i=0;i<num_labels;i++)
920 ret->label[i]=strdup(labels[i]);
922 ret->label_vals = calloc(num_labels,sizeof(*ret->label_vals));
923 memcpy(ret->label_vals,label_vals,sizeof(*ret->label_vals)*num_labels);
924 ret->labels = num_labels;
926 // set up each slice
927 for(i=0;i<num_slices;i++){
928 slices[i]->slider = ret;
929 slices[i]->slicenum = i;
931 ret->ypad=8;
932 ret->xpad=5;
933 //ret->minstep=minstep;
934 //ret->step=step;
936 if(label_vals[0]>label_vals[1])
937 ret->neg = 1;
939 ret->flags=flags;
940 if(flags & _SV_SLIDER_FLAG_VERTICAL) ret->flip = 1;
942 ret->lo = ret->label_vals[0];
943 ret->hi = ret->label_vals[ret->labels-1];
944 ret->lodel = 0.;
945 ret->idelrange = 1.;
946 ret->gradient = -1;
948 return ret;
951 void _sv_slider_set_gradient(_sv_slider_t *s, int m){
952 s->gradient = m;
953 if(s->realized){
954 _sv_slider_draw_background(s);
955 _sv_slider_draw(s);
956 _sv_slider_expose(s);
960 void _sv_slider_set_thumb_active(_sv_slider_t *s, int thumbnum, int activep){
961 _sv_slice_set_active(SLICE(s->slices[thumbnum]),activep);
964 float _sv_slider_get_value(_sv_slider_t *s, int thumbnum){
965 GtkWidget *w;
966 if(thumbnum >= s->num_slices)return 0;
967 if(thumbnum < 0)return 0;
968 w = s->slices[thumbnum];
969 return SLICE(w)->thumb_val;
972 void _sv_slider_set_value(_sv_slider_t *s, int thumbnum, float v){
973 GtkWidget *w;
974 if(thumbnum >= s->num_slices)return;
975 if(thumbnum < 0)return;
976 w = s->slices[thumbnum];
977 _sv_slice_thumb_set(SLICE(w),v);
978 update_gradient(s);
981 void _sv_slider_set_quant(_sv_slider_t *s,float num, float denom){
982 s->quant_num=num;
983 s->quant_denom=denom;
986 float _sv_slider_print_height(_sv_slider_t *s){
987 return (s->slices[0]->allocation.height - s->ypad*2)*1.2;
990 void _sv_slider_print(_sv_slider_t *s, cairo_t *c, int w, int h){
991 cairo_save(c);
992 float ypad = h*.1;
993 float neg = (s->neg? -1.: 1.);
995 // set clip region
996 cairo_rectangle(c,0,ypad,w,h-ypad*2);
997 cairo_clip(c);
999 // determine start/end deltas
1000 // eliminate label sections that are completely unused
1001 int slices = s->num_slices;
1002 float lo = (slices>0?SLICE(s->slices[0])->thumb_val:s->label_vals[0]) * neg;
1003 float hi = (slices>0?SLICE(s->slices[slices-1])->thumb_val:s->label_vals[s->labels-1]) * neg;
1005 // alpha could push up the unused area
1006 if(slices==3 && SLICE(s->slices[1])->thumb_val*neg>lo)
1007 lo = SLICE(s->slices[1])->thumb_val*neg;
1009 // if lo>hi (due to alpha), show the whole scale empty
1010 if(lo>hi){
1011 lo = s->label_vals[0]*neg;
1012 hi = s->label_vals[s->labels-1]*neg;
1015 int firstlabel=0;
1016 int lastlabel=s->labels-1;
1017 int i;
1019 for(i=s->labels-2;i>0;i--)
1020 if(lo>s->label_vals[i]*neg){
1021 firstlabel=i;
1022 break;
1025 for(i=1;i<s->labels-1;i++)
1026 if(hi<s->label_vals[i]*neg){
1027 lastlabel=i;
1028 break;
1031 float lodel = 1. / (s->labels-1) * firstlabel;
1032 float hidel = 1. / (s->labels-1) * lastlabel;
1033 float alphadel = (slices==3 ?
1034 _sv_slider_val_to_del(s,SLICE(s->slices[1])->thumb_val):0.);
1036 // create background image
1038 cairo_surface_t *image = cairo_image_surface_create(CAIRO_FORMAT_RGB24,w,h);
1039 cairo_t *ci = cairo_create(image);
1040 int x,y;
1042 cairo_save(c);
1043 cairo_set_source_rgb (ci, .5,.5,.5);
1044 cairo_paint(ci);
1045 cairo_set_source_rgb (ci, .314,.314,.314);
1046 for(y=0;y<=h/2;y+=8){
1047 int phase = (y>>3)&1;
1048 for(x=0;x<w;x+=8){
1049 if(phase)
1050 cairo_rectangle(ci,x,y+h/2.,8.,8.);
1051 else
1052 cairo_rectangle(ci,x,h/2.-y-8,8.,8.);
1053 cairo_fill(ci);
1054 phase=!phase;
1058 for(y=0;y<h;y++){
1059 _sv_ucolor_t *line = (_sv_ucolor_t *)cairo_image_surface_get_data(image) + w*y;
1060 for(x=0;x<w;x++){
1061 float del = (hidel - lodel) / (w-1) * x + lodel;
1062 _sv_lcolor_t outc = {0,0,0,0};
1064 mapfunc[s->gradient](rint(del*65536.f),255,&outc);
1065 line[x] = mixfunc[s->gradient]( (_sv_ucolor_t)(u_int32_t)((outc.a<<24) + (outc.r<<16) + (outc.g<<8) + outc.b),
1066 (_sv_ucolor_t)line[x]).u | 0xff000000;
1071 // composite background with correct resample filter
1072 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
1073 cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
1074 cairo_set_source(c,pattern);
1075 cairo_rectangle(c,0,0,w,h);
1076 cairo_fill(c);
1078 cairo_destroy(ci);
1079 cairo_pattern_destroy(pattern);
1080 cairo_surface_destroy(image);
1081 cairo_restore(c);
1084 // add labels
1085 cairo_set_font_size(c,h-ypad*2-3);
1086 for(i=firstlabel;i<=lastlabel;i++){
1087 float x = (float)(i-firstlabel) / (lastlabel - firstlabel) * (w-1);
1088 float y;
1089 cairo_text_extents_t ex;
1091 cairo_move_to(c,x+.5,ypad);
1092 cairo_line_to(c,x+.5,h-ypad);
1093 cairo_set_source_rgba(c,0,0,0,.8);
1094 cairo_set_line_width(c,1);
1095 cairo_stroke(c);
1097 cairo_text_extents (c, s->label[i], &ex);
1098 if(i>firstlabel){
1100 x-=2;
1101 x-=ex.width;
1103 }else{
1104 x+=2;
1106 y = h/2. - ex.y_bearing/2.;
1108 cairo_set_source_rgba(c,1.,1.,1.,.5);
1109 cairo_set_line_width(c,2.5);
1110 cairo_move_to (c, x,y);
1111 cairo_text_path (c, s->label[i]);
1112 cairo_stroke(c);
1114 cairo_set_source_rgba(c,0,0,0,1.);
1115 cairo_move_to (c, x,y);
1116 cairo_show_text (c, s->label[i]);
1119 cairo_restore(c);
1122 // Slidermaps concentrate the data needed for map delta computations
1123 // outside of the GDK lock. Like with other rendering data, writes to
1124 // the slidermap (aside from heap changes) are not locked against
1125 // reads as any momentary inconsistency is a) cosmetic and b) will be
1126 // flushed immediately and replaced
1128 void _sv_slidermap_init(slider_map_t *m, sv_slider_t *s){
1129 sv_slice_t *sl = SLICE(s->slices[0]);
1130 sv_slice_t *sa = SLICE(s->slices[1]);
1131 sv_slice_t *sh = SLICE(s->slices[2]);
1133 m->n = s->labels;
1134 m->neg = s->neg;
1135 m->lo = sl->thumb_val;
1136 m->al = sa->thumb_val;
1137 m->hi = sh->thumb_val;
1139 if(m->vals)free(m->vals);
1140 if(m->muls)free(m->muls);
1141 if(m->offs)free(m->offs);
1143 m->vals = calloc(m->n,sizeof(*m->vals));
1144 m->muls = calloc(m->n-1,sizeof(*m->muls));
1145 m->offs = calloc(m->n-1,sizeof(*m->offs));
1147 for(j=0;j<m->n-1;j++){
1148 float labeldel = 1./(s->label_vals[j+1]-s->label_vals[j]);
1149 m->muls[j] = labeldel * s->idelrange * s->labelinv;
1150 m->offs[j] = (j-s->label_vals[j]*s->labeldel-
1151 s->lodel*(s->labels-1))*s->idelrange*s->labelinv;
1152 m->vals[j] = s->vals[j];
1154 m->vals[j] = s->vals[j];
1158 // assumes no scale change. We don't automagically check for scale
1159 // changes here as a scale update requires a different locking
1160 // strategy at higher levels from just acting on a thumb val change
1161 void _sv_slidermap_partial_update(slider_map_t *m, sv_slider_t *s){
1162 sv_slice_t *sl = SLICE(s->slices[0]);
1163 sv_slice_t *sa = SLICE(s->slices[1]);
1164 sv_slice_t *sh = SLICE(s->slices[2]);
1166 // not a complete check, but will prevent bounds errors.
1167 if(m->n != s->labels){
1168 fprintf(stderr,"sushivision: internal error; slidermap_partial_update called when full\n"
1169 "\tupdate (with additional locking) required.\n");
1170 return;
1173 m->lo = sl->thumb_val;
1174 m->al = sa->thumb_val;
1175 m->hi = sh->thumb_val;
1177 for(j=0;j<m->n-1;j++){
1178 float labeldel = 1./(s->label_vals[j+1]-s->label_vals[j]);
1179 m->muls[j] = labeldel * s->idelrange * s->labelinv;
1180 m->offs[j] = (j-s->label_vals[j]*s->labeldel-
1181 s->lodel*(s->labels-1))*s->idelrange*s->labelinv;
1182 m->vals[j] = s->vals[j];
1184 m->vals[j] = s->vals[j];
1188 void _sv_slidermap_clear(slider_map_t *m){
1189 if(m->vals)free(m->vals);
1190 if(m->muls)free(m->muls);
1191 if(m->offs)free(m->offs);
1192 memset(m,0,sizeof(m));
1195 float _sv_slidermap_to_mapdel(slider_map_t *s,float v){
1196 int j=s->n-1;
1198 if(isnan(v))return NAN;
1200 if(s->neg){
1202 if(v > s->al && s->al < s->lo)return NAN;
1203 if(v >= s->lo)return 0.;
1204 if(v <= s->hi)return 1.;
1205 while(--j)
1206 if(v<=s->vals[j])break;
1208 }else{
1210 if(v < s->al && s->al > s->lo)return NAN;
1211 if(v <= s->lo)return 0.;
1212 if(v >= s->hi)return 1.;
1213 while(--j)
1214 if(v>s->vals[j])break;
1218 return v*s->muls[j] + s->offs[j];