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)
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.
27 #include <gdk/gdkkeysyms.h>
30 static float val_to_pixel(_sv_slider_t
*s
, float val
);
32 static int total_slice_width(_sv_slider_t
*s
){
36 for(i
=0;i
<s
->num_slices
;i
++)
37 count
+= s
->slices
[i
]->allocation
.height
;
39 for(i
=0;i
<s
->num_slices
;i
++)
40 count
+= s
->slices
[i
]->allocation
.width
;
44 static int slice_width(_sv_slider_t
*s
,int slices
){
49 count
+= s
->slices
[i
]->allocation
.height
;
52 count
+= s
->slices
[i
]->allocation
.width
;
56 static int total_slice_height(_sv_slider_t
*s
){
60 for(i
=0;i
<s
->num_slices
;i
++)
61 if(max
<s
->slices
[i
]->allocation
.width
)
62 max
= s
->slices
[i
]->allocation
.width
;
64 for(i
=0;i
<s
->num_slices
;i
++)
65 if(max
<s
->slices
[i
]->allocation
.height
)
66 max
= s
->slices
[i
]->allocation
.height
;
71 /* guess where I came from. */
72 static void rounded_rectangle (cairo_t
*c
,
73 float x
, float y
, float w
, float h
,
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;
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];
123 float textr
=text
->red
;
124 float textg
=text
->green
;
125 float textb
=text
->blue
;
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
);
146 cairo_rectangle (c
, x
+1, ty
, w
-2, th
);
147 bg_set(s
->slices
[0],c
);
149 cairo_surface_flush(s
->background
);
151 // Create trough innards
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);
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
);
181 cairo_pattern_destroy (pattern
);
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
);
190 cairo_pattern_destroy (pattern
);
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);
204 cairo_text_extents_t ex
;
205 cairo_text_extents (c
, s
->label
[i
], &ex
);
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
]);
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
]);
228 rounded_rectangle (c
, tx
+0.5, ty
-0.5, tw
-1, th
+1, 1.5);
230 cairo_set_line_width (c
, 1.0);
236 void _sv_slider_draw(_sv_slider_t
*s
){
237 if(!s
->realized
)return;
245 c
= cairo_create(s
->foreground
);
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
);
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;
263 float y
= rint(h
/2)+.5;
266 cairo_pattern_t
*pattern
;
268 if(SLICE(sl
)->thumb_active
){
269 if ((s
->num_slices
== 1) || (s
->num_slices
== 3 && i
==1)){
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.));
277 cairo_fill_preserve(c
);
279 cairo_set_line_width(c
,1);
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.);
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
);
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
);
302 cairo_pattern_destroy (pattern
);
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);
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);
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);
330 cairo_set_line_width(c
,1);
331 cairo_fill_preserve(c
);
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
);
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
);
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
);
351 cairo_pattern_destroy (pattern
);
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);
364 cairo_set_line_width(c
,1);
365 cairo_fill_preserve(c
);
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
);
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
);
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
);
385 cairo_pattern_destroy (pattern
);
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);
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);
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
){
414 cairo_surface_destroy(s
->background
);
417 cairo_surface_destroy(s
->foreground
);
422 s
->backdata
= calloc(w
*h
,4);
424 s
->background
= cairo_image_surface_create_for_data ((unsigned char *)s
->backdata
,
428 s
->foreground
= cairo_image_surface_create (CAIRO_FORMAT_RGB24
,
431 s
->foreground
= cairo_image_surface_create (CAIRO_FORMAT_RGB24
,
439 if(s
->xpad
<4)s
->xpad
=4;
441 _sv_slider_draw_background(s
);
448 static float val_to_pixel(_sv_slider_t
*s
,float v
){
451 float neg
= (s
->neg
? -1.: 1.);
457 if( v
<s
->label_vals
[0]*neg
){
459 }else if(v
>s
->label_vals
[s
->labels
-1]*neg
){
462 for(j
=0;j
<s
->labels
;j
++){
463 if(v
>=s
->label_vals
[j
]*neg
&& v
<=s
->label_vals
[j
+1]*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
;
477 float _sv_slider_val_to_del(_sv_slider_t
*s
,float v
){
478 if(isnan(v
))return NAN
;
483 if(v
<=s
->label_vals
[j
])break;
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
]))/
494 float _sv_slider_val_to_mapdel(_sv_slider_t
*s
,float v
){
496 if(isnan(v
))return NAN
;
500 if(v
> s
->al
)return NAN
;
501 if(v
>= s
->lo
)return 0.;
502 if(v
<= s
->hi
)return 1.;
504 if(v
<=s
->label_vals
[j
])break;
508 if(v
< s
->al
)return NAN
;
509 if(v
<= s
->lo
)return 0.;
510 if(v
>= s
->hi
)return 1.;
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
);
528 cairo_set_source_surface(c
,s
->foreground
,0, slice_width(s
,slicenum
+1)-total_slice_width(s
));
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
);
539 void _sv_slider_expose(_sv_slider_t
*s
){
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
;
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;
570 requisition
->height
= (w
+s
->num_slices
-1)/s
->num_slices
;
571 requisition
->width
= maxy
+4+s
->ypad
*2;
573 requisition
->width
= (w
+s
->num_slices
-1)/s
->num_slices
;
574 requisition
->height
= maxy
+4+s
->ypad
*2;
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
);
585 static float quant(_sv_slider_t
*s
, float val
){
586 if(s
->quant_denom
!=0.){
587 val
*= s
->quant_denom
;
593 val
/= s
->quant_denom
;
598 float _sv_slider_pixel_to_val(_sv_slider_t
*s
,float x
){
601 float del
= (float)(x
-tx
)/tw
;
603 return quant(s
,s
->label_vals
[0]);
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
){
622 float _sv_slider_pixel_to_mapdel(_sv_slider_t
*s
,float x
){
625 x
= ((x
-tx
)/tw
- s
->lodel
)*s
->idelrange
;
627 if (x
<=0.) return 0.;
628 if (x
>=1.) return 1.;
632 float _sv_slider_del_to_val(_sv_slider_t
*s
, float del
){
634 if(isnan(del
))return del
;
636 del
*= (s
->labels
-1);
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
){
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
) &&
661 for(i
=slicenum
-1; i
>=0;i
--){
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
++){
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
){
690 float bestdist
=s
->w
+1;
691 int n
= s
->num_slices
;
694 _sv_slice_t
*sl
= SLICE(s
->slices
[slicenum
]);
696 x
= sl
->widget
.allocation
.height
- y
-1;
700 x
=slice_adjust_pixel(s
,slicenum
,x
);
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
);
716 static int lit_thumb(_sv_slider_t
*s
){
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
;
725 void _sv_slider_unlight(_sv_slider_t
*s
){
727 for(i
=0;i
<s
->num_slices
;i
++){
728 _sv_slice_t
*sl
= SLICE(s
->slices
[i
]);
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
);
738 _sv_slider_unlight(s
);
740 _sv_slice_t
*sl
= SLICE(s
->slices
[best
]);
748 void _sv_slider_button_press(_sv_slider_t
*s
,int slicenum
,int x
,int y
){
750 for(i
=0;i
<s
->num_slices
;i
++){
751 _sv_slice_t
*sl
= SLICE(s
->slices
[i
]);
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
);
765 _sv_slider_expose(s
);
768 void _sv_slider_button_release(_sv_slider_t
*s
,int slicenum
,int x
,int y
){
770 for(i
=0;i
<s
->num_slices
;i
++){
771 _sv_slice_t
*sl
= SLICE(s
->slices
[i
]);
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
){
794 s
->idelrange
= 1./(hdel
-ldel
);
796 _sv_slider_draw_background(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
];
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
]);
819 _sv_slider_pixel_to_val(s
,slice_adjust_pixel(s
,slicenum
,px
));
820 _sv_slider_vals_bound(s
,i
);
825 // did a gradient get altered?
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);
843 /* nothing grabbed right now; determine if we're in a thumb's area */
844 if(_sv_slider_lightme(s
,slicenum
,x
,y
)){
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
){
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
--;
866 sl
->thumb_val
=_sv_slider_pixel_to_val(s
,x
);
867 _sv_slider_vals_bound(s
,slicenum
);
868 // did a gradient get altered?
872 sl
->callback(sl
->callback_data
,0);
873 sl
->callback(sl
->callback_data
,1);
874 sl
->callback(sl
->callback_data
,2);
877 _sv_slider_expose(s
);
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
++;
889 sl
->thumb_val
=_sv_slider_pixel_to_val(s
,x
);
890 _sv_slider_vals_bound(s
,slicenum
);
891 // did a gradient get altered?
895 sl
->callback(sl
->callback_data
,0);
896 sl
->callback(sl
->callback_data
,1);
897 sl
->callback(sl
->callback_data
,2);
900 _sv_slider_expose(s
);
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
,
911 _sv_slider_t
*ret
= calloc(1,sizeof(*ret
));
913 ret
->slices
= (GtkWidget
**)slices
;
914 ret
->num_slices
= num_slices
;
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
;
927 for(i
=0;i
<num_slices
;i
++){
928 slices
[i
]->slider
= ret
;
929 slices
[i
]->slicenum
= i
;
933 //ret->minstep=minstep;
936 if(label_vals
[0]>label_vals
[1])
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];
951 void _sv_slider_set_gradient(_sv_slider_t
*s
, int m
){
954 _sv_slider_draw_background(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
){
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
){
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
);
981 void _sv_slider_set_quant(_sv_slider_t
*s
,float num
, float denom
){
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
){
993 float neg
= (s
->neg
? -1.: 1.);
996 cairo_rectangle(c
,0,ypad
,w
,h
-ypad
*2);
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
1011 lo
= s
->label_vals
[0]*neg
;
1012 hi
= s
->label_vals
[s
->labels
-1]*neg
;
1016 int lastlabel
=s
->labels
-1;
1019 for(i
=s
->labels
-2;i
>0;i
--)
1020 if(lo
>s
->label_vals
[i
]*neg
){
1025 for(i
=1;i
<s
->labels
-1;i
++)
1026 if(hi
<s
->label_vals
[i
]*neg
){
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
);
1043 cairo_set_source_rgb (ci
, .5,.5,.5);
1045 cairo_set_source_rgb (ci
, .314,.314,.314);
1046 for(y
=0;y
<=h
/2;y
+=8){
1047 int phase
= (y
>>3)&1;
1050 cairo_rectangle(ci
,x
,y
+h
/2.,8.,8.);
1052 cairo_rectangle(ci
,x
,h
/2.-y
-8,8.,8.);
1059 _sv_ucolor_t
*line
= (_sv_ucolor_t
*)cairo_image_surface_get_data(image
) + w
*y
;
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
);
1079 cairo_pattern_destroy(pattern
);
1080 cairo_surface_destroy(image
);
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);
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);
1097 cairo_text_extents (c
, s
->label
[i
], &ex
);
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
]);
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
]);
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]);
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");
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
){
1198 if(isnan(v
))return NAN
;
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.;
1206 if(v
<=s
->vals
[j
])break;
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.;
1214 if(v
>s
->vals
[j
])break;
1218 return v
*s
->muls
[j
] + s
->offs
[j
];