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 double val_to_pixel(_sv_slider_t
*s
, double 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 double x
, double y
, double w
, double 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 double 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 double shade_r
=bg
->red
/65535.;
89 double shade_g
=bg
->green
/65535.;
90 double 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 double shade_r
=fg
->red
*shades
[shade
]/65535;
99 double shade_g
=fg
->green
*shades
[shade
]/65535;
100 double 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 double shade_r
=bg
->red
*shades
[shade
]/65535;
109 double shade_g
=bg
->green
*shades
[shade
]/65535;
110 double 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 double textr
=text
->red
;
124 double textg
=text
->green
;
125 double 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 s
->mapfunc(rint(val
*65536.f
),255,&outc
);
162 return m
->mixfunc( (_sv_ucolor_t
)(u_int32_t
)((outc
.a
<<24) + (outc
.r
<<16) + (outc
.g
<<8) + outc
.b
),
163 (_sv_ucolor_t
)mix
).u
| 0xff000000;
165 pixel
[i
]=_sv_mapping_calc(s
->gradient
,_sv_slider_pixel_to_mapdel(s
,i
), pixel
[i
]);
167 for(i
=ty
+1;i
<ty
+th
;i
++){
168 memcpy(pixel
+w
,pixel
,w
*4);
177 cairo_rectangle (c
, tx
+1, ty
, tw
-2, 4);
178 pattern
= cairo_pattern_create_linear (0, ty
-1, 0, ty
+3);
179 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, 0., 0., 0., 0.2);
180 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, 0., 0., 0., 0.);
181 cairo_set_source (c
, pattern
);
183 cairo_pattern_destroy (pattern
);
186 cairo_rectangle (c
, tx
+1, ty
, tx
+4, th
);
187 pattern
= cairo_pattern_create_linear (tx
, ty
-1, tx
+3, ty
-1);
188 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, 0., 0., 0., 0.1);
189 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, 0., 0., 0., 0.);
190 cairo_set_source (c
, pattern
);
192 cairo_pattern_destroy (pattern
);
195 for(i
=0;i
<s
->labels
;i
++){
196 int x
=val_to_pixel(s
,s
->label_vals
[i
])+.5;
197 int y
=s
->h
-s
->ypad
-1.5;
199 cairo_move_to(c
,x
+.5,s
->ypad
+2);
200 cairo_line_to(c
,x
+.5,s
->h
-s
->ypad
-2);
201 cairo_set_source_rgba(c
,textr
,textg
,textb
,.8);
202 cairo_set_line_width(c
,1);
206 cairo_text_extents_t ex
;
207 cairo_text_extents (c
, s
->label
[i
], &ex
);
217 cairo_set_source_rgba(c
,1.,1.,1.,.5);
218 cairo_set_line_width(c
,2.5);
219 cairo_move_to (c
, x
,y
);
220 cairo_text_path (c
, s
->label
[i
]);
224 cairo_set_source_rgba(c
,textr
,textg
,textb
,1.);
225 cairo_move_to (c
, x
,y
);
226 cairo_show_text (c
, s
->label
[i
]);
230 rounded_rectangle (c
, tx
+0.5, ty
-0.5, tw
-1, th
+1, 1.5);
232 cairo_set_line_width (c
, 1.0);
238 void _sv_slider_draw(_sv_slider_t
*s
){
239 if(!s
->realized
)return;
247 c
= cairo_create(s
->foreground
);
250 cairo_matrix_t m
= {0.,-1., 1.,0., 0.,w
};
251 cairo_set_matrix(c
,&m
);
254 cairo_set_source_surface(c
,s
->background
,0,0);
255 cairo_rectangle(c
,0,0,w
,h
);
259 for(i
=0;i
<s
->num_slices
;i
++){
260 GtkWidget
*sl
= s
->slices
[i
];
261 double x
= rint(val_to_pixel(s
,((_sv_slice_t
*)(s
->slices
[i
]))->thumb_val
))+.5;
265 float y
= rint(h
/2)+.5;
268 cairo_pattern_t
*pattern
;
270 if(SLICE(sl
)->thumb_active
){
271 if ((s
->num_slices
== 1) || (s
->num_slices
== 3 && i
==1)){
273 cairo_move_to(c
,x
,s
->h
/2);
274 cairo_arc_negative(c
, x
+xd
-rx
, rad
+.5, rad
, 30.*(M_PI
/180.), 270.*(M_PI
/180.));
275 cairo_arc_negative(c
, x
-xd
+rx
, rad
+.5, rad
, 270.*(M_PI
/180.), 150.*(M_PI
/180.));
279 cairo_fill_preserve(c
);
281 cairo_set_line_width(c
,1);
284 if(((_sv_slice_t
*)s
->slices
[i
])->thumb_focus
)
285 cairo_set_source_rgba(c
,0,0,0,1);
287 cairo_stroke_preserve(c
);
288 cairo_set_dash (c
, NULL
, 0, 0.);
291 pattern
= cairo_pattern_create_linear (0, 0, 0, 4);
292 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, 1., 1., 1., 0.2);
293 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, 1., 1., 1., 0.);
294 cairo_set_source (c
, pattern
);
295 cairo_fill_preserve (c
);
296 cairo_pattern_destroy (pattern
);
299 pattern
= cairo_pattern_create_linear (x
-xd
+3, 0, x
-xd
+6, 0);
300 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, 1., 1., 1., 0.1);
301 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, 1., 1., 1., 0.);
302 cairo_set_source (c
, pattern
);
304 cairo_pattern_destroy (pattern
);
307 cairo_set_line_width(c
,2);
308 cairo_move_to(c
,x
,s
->h
/2+3);
309 cairo_line_to(c
,x
,s
->ypad
/2);
310 cairo_set_source_rgba(c
,0.,0.,0.,.5);
314 cairo_set_line_width(c
,1);
315 cairo_move_to(c
,x
,s
->h
/2+2);
316 cairo_line_to(c
,x
,s
->ypad
/2);
317 cairo_set_source_rgb(c
,1.,1.,0);
325 cairo_move_to(c
,x
,s
->h
/2);
326 cairo_line_to(c
,x
-xd
/2,s
->h
/2);
327 cairo_arc_negative(c
, x
-xd
*3/2+rx
, h
-rad
-.5, rad
, 210.*(M_PI
/180.), 90.*(M_PI
/180.));
328 cairo_line_to(c
, x
, h
-.5);
332 cairo_set_line_width(c
,1);
333 cairo_fill_preserve(c
);
335 if(((_sv_slice_t
*)s
->slices
[i
])->thumb_focus
)
336 cairo_set_source_rgba(c
,0,0,0,1);
337 cairo_stroke_preserve(c
);
340 pattern
= cairo_pattern_create_linear (0, y
, 0, y
+4);
341 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, 1., 1., 1., 0.2);
342 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, 1., 1., 1., 0.);
343 cairo_set_source (c
, pattern
);
344 cairo_fill_preserve (c
);
345 cairo_pattern_destroy (pattern
);
348 pattern
= cairo_pattern_create_linear (x
-xd
*3/2+3, 0, x
-xd
*3/2+6, 0);
349 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, 1., 1., 1., 0.1);
350 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, 1., 1., 1., 0.);
351 cairo_set_source (c
, pattern
);
353 cairo_pattern_destroy (pattern
);
359 cairo_move_to(c
,x
,s
->h
/2);
360 cairo_line_to(c
,x
+xd
/2,s
->h
/2);
361 cairo_arc(c
, x
+xd
*3/2-rx
, h
-rad
-.5, rad
, 330.*(M_PI
/180.), 90.*(M_PI
/180.));
362 cairo_line_to(c
, x
, h
-.5);
366 cairo_set_line_width(c
,1);
367 cairo_fill_preserve(c
);
369 if(((_sv_slice_t
*)s
->slices
[i
])->thumb_focus
)
370 cairo_set_source_rgba(c
,0,0,0,1);
371 cairo_stroke_preserve(c
);
374 pattern
= cairo_pattern_create_linear (0, y
, 0, y
+4);
375 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, 1., 1., 1., 0.2);
376 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, 1., 1., 1., 0.);
377 cairo_set_source (c
, pattern
);
378 cairo_fill_preserve (c
);
379 cairo_pattern_destroy (pattern
);
382 pattern
= cairo_pattern_create_linear (x
+1, 0, x
+4, 0);
383 cairo_pattern_add_color_stop_rgba (pattern
, 0.0, 1., 1., 1., 0.1);
384 cairo_pattern_add_color_stop_rgba (pattern
, 1.0, 1., 1., 1., 0.);
385 cairo_set_source (c
, pattern
);
387 cairo_pattern_destroy (pattern
);
391 cairo_set_line_width(c
,2);
392 cairo_move_to(c
,x
,s
->h
/2-3);
393 cairo_line_to(c
,x
,h
-s
->ypad
/2);
394 cairo_set_source_rgba(c
,0.,0.,0.,.5);
398 cairo_set_line_width(c
,1);
399 cairo_move_to(c
,x
,s
->h
/2-2);
400 cairo_line_to(c
,x
,h
-s
->ypad
/2);
401 cairo_set_source_rgb(c
,1.,1.,0);
410 void _sv_slider_realize(_sv_slider_t
*s
){
411 int w
= total_slice_width(s
);
412 int h
= total_slice_height(s
);
413 if(s
->background
== 0 || w
!= s
->w
|| h
!= s
->h
){
416 cairo_surface_destroy(s
->background
);
419 cairo_surface_destroy(s
->foreground
);
424 s
->backdata
= calloc(w
*h
,4);
426 s
->background
= cairo_image_surface_create_for_data ((unsigned char *)s
->backdata
,
430 s
->foreground
= cairo_image_surface_create (CAIRO_FORMAT_RGB24
,
433 s
->foreground
= cairo_image_surface_create (CAIRO_FORMAT_RGB24
,
441 if(s
->xpad
<4)s
->xpad
=4;
443 _sv_slider_draw_background(s
);
450 static double val_to_pixel(_sv_slider_t
*s
,double v
){
453 double neg
= (s
->neg
? -1.: 1.);
459 if( v
<s
->label_vals
[0]*neg
){
461 }else if(v
>s
->label_vals
[s
->labels
-1]*neg
){
464 for(j
=0;j
<s
->labels
;j
++){
465 if(v
>=s
->label_vals
[j
]*neg
&& v
<=s
->label_vals
[j
+1]*neg
){
467 double del
=(v
-s
->label_vals
[j
])/(s
->label_vals
[j
+1]-s
->label_vals
[j
]);
468 double pixlo
=rint((double)(j
)/(s
->labels
-1)*tw
);
469 double pixhi
=rint((double)(j
+1)/(s
->labels
-1)*tw
);
470 ret
=pixlo
*(1.-del
)+pixhi
*del
+tx
;
479 double _sv_slider_val_to_del(_sv_slider_t
*s
,double v
){
480 if(isnan(v
))return NAN
;
485 if(v
<=s
->label_vals
[j
])break;
488 if(v
>s
->label_vals
[j
])break;
490 return (j
+ (v
-s
->label_vals
[j
])*s
->labeldel
[j
])*s
->labelinv
;
494 double _sv_slider_val_to_mapdel(_sv_slider_t
*s
,double 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 double slice_adjust_pixel(_sv_slider_t
*s
,int slicenum
, double x
){
581 double width
= slice_width(s
,slicenum
);
585 static double quant(_sv_slider_t
*s
, double val
){
586 if(s
->quant_denom
!=0.){
587 val
*= s
->quant_denom
;
593 val
/= s
->quant_denom
;
598 double _sv_slider_pixel_to_val(_sv_slider_t
*s
,double x
){
601 double del
= (double)(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 double _sv_slider_pixel_to_del(_sv_slider_t
*s
,double x
){
622 double _sv_slider_pixel_to_mapdel(_sv_slider_t
*s
,double x
){
625 x
= ((x
-tx
)/tw
- s
->lodel
)*s
->idelrange
;
627 if (x
<=0.) return 0.;
628 if (x
>=1.) return 1.;
632 double _sv_slider_del_to_val(_sv_slider_t
*s
, double 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 double min
= (s
->neg
? s
->label_vals
[s
->labels
-1] : s
->label_vals
[0]);
647 double 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 double ldel
= _sv_slider_val_to_del(s
,sl
->thumb_val
);
786 double 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
->lo
= sl
->thumb_val
;
795 s
->hi
= sh
->thumb_val
;
796 s
->idelrange
= 1./(hdel
-ldel
);
799 for(j
=0;j
<s
->labels
-1;j
++){
800 s
->labeldelB
[j
] = s
->labeldel
[j
] * s
->idelrange
* s
->labelinv
;
801 s
->labelvalB
[j
] = (j
-s
->label_vals
[j
]*s
->labeldel
[j
]-
802 s
->lodel
*(s
->labels
-1))*s
->idelrange
*s
->labelinv
;
805 _sv_mapping_set_lo(s
->gradient
,ldel
);
806 _sv_mapping_set_hi(s
->gradient
,hdel
);
807 _sv_slider_draw_background(s
);
810 _sv_slider_expose(s
);
814 void _sv_slider_motion(_sv_slider_t
*s
,int slicenum
,int x
,int y
){
815 double altered
[s
->num_slices
];
817 _sv_slice_t
*sl
= SLICE(s
->slices
[slicenum
]);
818 int px
= (s
->flip
?sl
->widget
.allocation
.height
-y
-1 : x
);
820 for(i
=0;i
<s
->num_slices
;i
++){
821 sl
= SLICE(s
->slices
[i
]);
822 altered
[i
] = sl
->thumb_val
;
825 /* is a thumb already grabbed? */
826 for(i
=0;i
<s
->num_slices
;i
++){
827 sl
= SLICE(s
->slices
[i
]);
830 _sv_slider_pixel_to_val(s
,slice_adjust_pixel(s
,slicenum
,px
));
831 _sv_slider_vals_bound(s
,i
);
836 // did a gradient get altered?
841 _sv_slider_expose(s
);
843 // call slice callbacks on all slices that were altered; value
844 // bounding might have affected slices other than the grabbed one.
846 for(i
=0;i
<s
->num_slices
;i
++){
847 _sv_slice_t
*sl
= SLICE(s
->slices
[i
]);
849 if(sl
->thumb_val
!= altered
[i
])
850 if(sl
->callback
)sl
->callback(sl
->callback_data
,1);
854 /* nothing grabbed right now; determine if we're in a thumb's area */
855 if(_sv_slider_lightme(s
,slicenum
,x
,y
)){
857 _sv_slider_expose(s
);
862 gboolean
_sv_slider_key_press(_sv_slider_t
*s
,GdkEventKey
*event
,int slicenum
){
863 _sv_slice_t
*sl
= (_sv_slice_t
*)(s
->slices
[slicenum
]);
864 int shift
= (event
->state
&GDK_SHIFT_MASK
);
865 if(event
->state
&GDK_MOD1_MASK
) return FALSE
;
866 if(event
->state
&GDK_CONTROL_MASK
)return FALSE
;
868 /* non-control keypresses */
869 switch(event
->keyval
){
872 double x
= val_to_pixel(s
,sl
->thumb_val
);
873 while(sl
->thumb_val
> s
->label_vals
[0] &&
874 sl
->thumb_val
== _sv_slider_pixel_to_val(s
,x
))x
--;
877 sl
->thumb_val
=_sv_slider_pixel_to_val(s
,x
);
878 _sv_slider_vals_bound(s
,slicenum
);
879 // did a gradient get altered?
883 sl
->callback(sl
->callback_data
,0);
884 sl
->callback(sl
->callback_data
,1);
885 sl
->callback(sl
->callback_data
,2);
888 _sv_slider_expose(s
);
895 double x
= val_to_pixel(s
,sl
->thumb_val
);
896 while(sl
->thumb_val
< s
->label_vals
[s
->labels
-1] &&
897 sl
->thumb_val
== _sv_slider_pixel_to_val(s
,x
))x
++;
900 sl
->thumb_val
=_sv_slider_pixel_to_val(s
,x
);
901 _sv_slider_vals_bound(s
,slicenum
);
902 // did a gradient get altered?
906 sl
->callback(sl
->callback_data
,0);
907 sl
->callback(sl
->callback_data
,1);
908 sl
->callback(sl
->callback_data
,2);
911 _sv_slider_expose(s
);
916 return FALSE
; // keep processing
919 _sv_slider_t
*_sv_slider_new(_sv_slice_t
**slices
, int num_slices
, char **labels
, double *label_vals
, int num_labels
,
922 _sv_slider_t
*ret
= calloc(1,sizeof(*ret
));
924 ret
->slices
= (GtkWidget
**)slices
;
925 ret
->num_slices
= num_slices
;
929 ret
->label
= calloc(num_labels
,sizeof(*ret
->label
));
930 for(i
=0;i
<num_labels
;i
++)
931 ret
->label
[i
]=strdup(labels
[i
]);
933 ret
->label_vals
= calloc(num_labels
,sizeof(*ret
->label_vals
));
934 memcpy(ret
->label_vals
,label_vals
,sizeof(*ret
->label_vals
)*num_labels
);
935 ret
->labels
= num_labels
;
938 for(i
=0;i
<num_slices
;i
++){
939 slices
[i
]->slider
= ret
;
940 slices
[i
]->slicenum
= i
;
944 //ret->minstep=minstep;
947 if(label_vals
[0]>label_vals
[1])
951 if(flags
& _SV_SLIDER_FLAG_VERTICAL
) ret
->flip
= 1;
953 ret
->labelinv
= 1./(ret
->labels
-1);
954 ret
->labeldel
= calloc(ret
->labels
-1,sizeof(*ret
->labeldel
));
955 for(i
=0;i
<ret
->labels
-1;i
++)
956 ret
->labeldel
[i
] = 1./(ret
->label_vals
[i
+1]-ret
->label_vals
[i
]);
957 ret
->lo
= ret
->label_vals
[0];
958 ret
->hi
= ret
->label_vals
[ret
->labels
-1];
962 ret
->labeldelB
= calloc(ret
->labels
-1,sizeof(*ret
->labeldelB
));
963 ret
->labelvalB
= calloc(ret
->labels
-1,sizeof(*ret
->labelvalB
));
964 for(i
=0;i
<ret
->labels
-1;i
++){
965 ret
->labeldelB
[i
] = ret
->labeldel
[i
] * ret
->labelinv
;
966 ret
->labelvalB
[i
] = (i
-ret
->label_vals
[i
]*ret
->labeldel
[i
])*ret
->labelinv
;
972 void _sv_slider_set_gradient(_sv_slider_t
*s
, _sv_mapping_t
*m
){
975 _sv_slider_draw_background(s
);
977 _sv_slider_expose(s
);
981 void _sv_slider_set_thumb_active(_sv_slider_t
*s
, int thumbnum
, int activep
){
982 _sv_slice_set_active(SLICE(s
->slices
[thumbnum
]),activep
);
985 double _sv_slider_get_value(_sv_slider_t
*s
, int thumbnum
){
987 if(thumbnum
>= s
->num_slices
)return 0;
988 if(thumbnum
< 0)return 0;
989 w
= s
->slices
[thumbnum
];
990 return SLICE(w
)->thumb_val
;
993 void _sv_slider_set_value(_sv_slider_t
*s
, int thumbnum
, double v
){
995 if(thumbnum
>= s
->num_slices
)return;
996 if(thumbnum
< 0)return;
997 w
= s
->slices
[thumbnum
];
998 _sv_slice_thumb_set(SLICE(w
),v
);
1002 void _sv_slider_set_quant(_sv_slider_t
*s
,double num
, double denom
){
1004 s
->quant_denom
=denom
;
1007 double _sv_slider_print_height(_sv_slider_t
*s
){
1008 return (s
->slices
[0]->allocation
.height
- s
->ypad
*2)*1.2;
1011 void _sv_slider_print(_sv_slider_t
*s
, cairo_t
*c
, int w
, int h
){
1014 double neg
= (s
->neg
? -1.: 1.);
1017 cairo_rectangle(c
,0,ypad
,w
,h
-ypad
*2);
1020 // determine start/end deltas
1021 // eliminate label sections that are completely unused
1022 int slices
= s
->num_slices
;
1023 double lo
= (slices
>0?SLICE(s
->slices
[0])->thumb_val
:s
->label_vals
[0]) * neg
;
1024 double hi
= (slices
>0?SLICE(s
->slices
[slices
-1])->thumb_val
:s
->label_vals
[s
->labels
-1]) * neg
;
1026 // alpha could push up the unused area
1027 if(slices
==3 && SLICE(s
->slices
[1])->thumb_val
*neg
>lo
)
1028 lo
= SLICE(s
->slices
[1])->thumb_val
*neg
;
1030 // if lo>hi (due to alpha), show the whole scale empty
1032 lo
= s
->label_vals
[0]*neg
;
1033 hi
= s
->label_vals
[s
->labels
-1]*neg
;
1037 int lastlabel
=s
->labels
-1;
1040 for(i
=s
->labels
-2;i
>0;i
--)
1041 if(lo
>s
->label_vals
[i
]*neg
){
1046 for(i
=1;i
<s
->labels
-1;i
++)
1047 if(hi
<s
->label_vals
[i
]*neg
){
1052 double lodel
= 1. / (s
->labels
-1) * firstlabel
;
1053 double hidel
= 1. / (s
->labels
-1) * lastlabel
;
1054 double alphadel
= (slices
==3 ?
1055 _sv_slider_val_to_del(s
,SLICE(s
->slices
[1])->thumb_val
):0.);
1057 // create background image
1059 cairo_surface_t
*image
= cairo_image_surface_create(CAIRO_FORMAT_RGB24
,w
,h
);
1060 cairo_t
*ci
= cairo_create(image
);
1064 cairo_set_source_rgb (ci
, .5,.5,.5);
1066 cairo_set_source_rgb (ci
, .314,.314,.314);
1067 for(y
=0;y
<=h
/2;y
+=8){
1068 int phase
= (y
>>3)&1;
1071 cairo_rectangle(ci
,x
,y
+h
/2.,8.,8.);
1073 cairo_rectangle(ci
,x
,h
/2.-y
-8,8.,8.);
1080 _sv_ucolor_t
*line
= (_sv_ucolor_t
*)cairo_image_surface_get_data(image
) + w
*y
;
1082 double del
= (hidel
- lodel
) / (w
-1) * x
+ lodel
;
1084 line
[x
].u
= _sv_mapping_calc(s
->gradient
, del
, line
[x
].u
);
1088 // composite background with correct resample filter
1089 cairo_pattern_t
*pattern
= cairo_pattern_create_for_surface(image
);
1090 cairo_pattern_set_filter(pattern
, CAIRO_FILTER_NEAREST
);
1091 cairo_set_source(c
,pattern
);
1092 cairo_rectangle(c
,0,0,w
,h
);
1096 cairo_pattern_destroy(pattern
);
1097 cairo_surface_destroy(image
);
1102 cairo_set_font_size(c
,h
-ypad
*2-3);
1103 for(i
=firstlabel
;i
<=lastlabel
;i
++){
1104 double x
= (double)(i
-firstlabel
) / (lastlabel
- firstlabel
) * (w
-1);
1106 cairo_text_extents_t ex
;
1108 cairo_move_to(c
,x
+.5,ypad
);
1109 cairo_line_to(c
,x
+.5,h
-ypad
);
1110 cairo_set_source_rgba(c
,0,0,0,.8);
1111 cairo_set_line_width(c
,1);
1114 cairo_text_extents (c
, s
->label
[i
], &ex
);
1123 y
= h
/2. - ex
.y_bearing
/2.;
1125 cairo_set_source_rgba(c
,1.,1.,1.,.5);
1126 cairo_set_line_width(c
,2.5);
1127 cairo_move_to (c
, x
,y
);
1128 cairo_text_path (c
, s
->label
[i
]);
1131 cairo_set_source_rgba(c
,0,0,0,1.);
1132 cairo_move_to (c
, x
,y
);
1133 cairo_show_text (c
, s
->label
[i
]);