5 * Copyright (C) 2002-2005 Monty
7 * Postfish is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
12 * Postfish is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Postfish; see the file COPYING. If not, write to the
19 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <gdk/gdkkeysyms.h>
30 static GdkBitmap
*stipple
=NULL
;
31 static GdkBitmap
*stippleB
=NULL
;
33 static float compute_dampening(float width
, float target
,float current
,float delta
,int zerodamp
){
34 float raw_delta
=target
-current
;
35 float testdelta
=delta
+(raw_delta
*.2);
37 if(target
<0 && !zerodamp
){
39 raw_delta
=target
-current
;
40 }else if(current
<0 && !zerodamp
){
41 raw_delta
=target
-current
;
42 }else if(raw_delta
<0){
46 if(raw_delta
<testdelta
)raw_delta
=testdelta
;
52 if(raw_delta
>testdelta
)raw_delta
=testdelta
;
59 /* call me roughly 10-20fps */
60 static void compute(Multibar
*m
,float *lowvals
, float *highvals
, int n
, int draw
){
62 GtkWidget
*widget
=GTK_WIDGET(m
);
64 int height
=widget
->allocation
.height
;
65 int width
=widget
->allocation
.width
;
67 /* figure out the x padding */
79 m
->bartrackers
=calloc(n
,sizeof(*m
->bartrackers
));
81 m
->bartrackers
=realloc(m
->bartrackers
,
82 n
*sizeof(*m
->bartrackers
));
83 memset(m
->bartrackers
+m
->bars
,0,
84 sizeof(*m
->bartrackers
)*(n
-m
->bars
));
87 for(i
=m
->bars
;i
<n
;i
++){
88 m
->bartrackers
[i
].pixelposlo
=-400;
89 m
->bartrackers
[i
].pixelposhi
=-400;
90 m
->bartrackers
[i
].pixeldeltalo
=0;
91 m
->bartrackers
[i
].pixeldeltahi
=0;
99 if(highvals
[i
]>max
)max
=highvals
[i
];
111 m
->peak
+=m
->peakdelta
;
116 m
->peakdelay
=15*2; /* ~2 second hold */
121 int *pixhi
=alloca(n
*sizeof(*pixhi
));
122 int *pixlo
=alloca(n
*sizeof(*pixlo
));
126 for(j
=0;j
<=m
->labels
;j
++)
127 if(lowvals
[i
]>=m
->levels
[j
]){
128 if(lowvals
[i
]<=m
->levels
[j
+1]){
129 float del
=(lowvals
[i
]-m
->levels
[j
])/(m
->levels
[j
+1]-m
->levels
[j
]);
130 pixlo
[i
]=(j
+del
)/m
->labels
*(widget
->allocation
.width
-xpad
*2-1)+xpad
;
132 }else if(j
==m
->labels
){
133 pixlo
[i
]=widget
->allocation
.width
-xpad
+1;
139 for(;j
<=m
->labels
;j
++)
140 if(highvals
[i
]>=m
->levels
[j
]){
141 if(highvals
[i
]<=m
->levels
[j
+1]){
142 float del
=(highvals
[i
]-m
->levels
[j
])/(m
->levels
[j
+1]-m
->levels
[j
]);
143 pixhi
[i
]=(j
+del
)/m
->labels
*(widget
->allocation
.width
-xpad
*2-1)+xpad
;
145 }else if(j
==m
->labels
){
146 pixhi
[i
]=widget
->allocation
.width
-xpad
+1;
152 /* dampen movement according to setup */
155 float trackhi
=m
->bartrackers
[i
].pixelposhi
;
156 float tracklo
=m
->bartrackers
[i
].pixelposlo
;
157 float delhi
=m
->bartrackers
[i
].pixeldeltahi
;
158 float dello
=m
->bartrackers
[i
].pixeldeltalo
;
161 delhi
= compute_dampening(width
-xpad
*2-1,pixhi
[i
],trackhi
,delhi
,m
->dampen_flags
& ZERO_DAMP
);
163 if(pixhi
[i
]>trackhi
){
164 if(m
->dampen_flags
& HI_ATTACK
)pixhi
[i
]=trackhi
+delhi
;
166 if(m
->dampen_flags
& HI_DECAY
)pixhi
[i
]=trackhi
+delhi
;
168 m
->bartrackers
[i
].pixelposhi
=pixhi
[i
];
169 m
->bartrackers
[i
].pixeldeltahi
=delhi
;
172 dello
= compute_dampening(width
-xpad
*2-1,pixlo
[i
],tracklo
,dello
,m
->dampen_flags
& ZERO_DAMP
);
173 if(pixlo
[i
]>tracklo
){
174 if(m
->dampen_flags
& LO_ATTACK
)pixlo
[i
]=tracklo
+dello
;
176 if(m
->dampen_flags
& LO_DECAY
)pixlo
[i
]=tracklo
+dello
;
178 m
->bartrackers
[i
].pixelposlo
=pixlo
[i
];
179 m
->bartrackers
[i
].pixeldeltalo
=dello
;
186 static void draw(GtkWidget
*widget
,int n
){
188 Multibar
*m
=MULTIBAR(widget
);
189 int xpad
=m
->xpad
,upad
=2,lpad
=2;
190 int height
=widget
->allocation
.height
;
191 GtkWidget
*parent
=gtk_widget_get_parent(widget
);
194 int leftover
=height
-widget
->requisition
.height
+3;
195 if(leftover
<height
/4)
202 m
->boxcolor
=gdk_gc_new(m
->backing
);
203 gdk_gc_copy(m
->boxcolor
,widget
->style
->black_gc
);
206 if(m
->readout
&& widget
->state
!=GTK_STATE_INSENSITIVE
){
207 /* draw the pixel positions */
208 while(x
<widget
->allocation
.width
){
209 int r
=0xffff,g
=0xffff,b
=0xffff;
210 GdkColor rgb
={0,0,0,0};
211 int next
=widget
->allocation
.width
;
213 if(m
->bartrackers
[i
].pixelposlo
>x
&& m
->bartrackers
[i
].pixelposlo
<next
)
214 next
=m
->bartrackers
[i
].pixelposlo
;
215 if(m
->bartrackers
[i
].pixelposhi
>x
&& m
->bartrackers
[i
].pixelposhi
<next
)
216 next
=m
->bartrackers
[i
].pixelposhi
;
220 if(m
->bartrackers
[i
].pixelposlo
<=x
&& m
->bartrackers
[i
].pixelposhi
>=next
){
268 gdk_gc_set_rgb_fg_color(m
->boxcolor
,&rgb
);
270 gdk_draw_rectangle(m
->backing
,m
->boxcolor
,1,x
+1,upad
+1,next
-x
,widget
->allocation
.height
-lpad
-upad
-3);
275 gdk_draw_line (m
->backing
,
276 widget
->style
->white_gc
,
277 xpad
, widget
->allocation
.height
-lpad
-1,
278 widget
->allocation
.width
-1-xpad
,
279 widget
->allocation
.height
-lpad
-1);
282 gdk_draw_line (m
->backing
,
283 widget
->style
->fg_gc
[1],
284 xpad
, upad
, widget
->allocation
.width
-1-xpad
, upad
);
286 gdk_draw_line (m
->backing
,
287 widget
->style
->fg_gc
[1],
288 xpad
, widget
->allocation
.height
-lpad
-2,
289 widget
->allocation
.width
-1-xpad
,
290 widget
->allocation
.height
-lpad
-2);
292 gdk_draw_line (m
->backing
,
293 widget
->style
->white_gc
,
294 xpad
, upad
, widget
->allocation
.width
-1-xpad
, upad
);
296 gdk_draw_line (m
->backing
,
297 widget
->style
->white_gc
,
298 xpad
, widget
->allocation
.height
-lpad
-2,
299 widget
->allocation
.width
-1-xpad
,
300 widget
->allocation
.height
-lpad
-2);
304 if(m
->dampen_flags
& PEAK_FOLLOW
){
306 for(j
=0;j
<=m
->labels
+1;j
++)
307 if(m
->peak
>=m
->levels
[j
]){
308 if(m
->peak
<=m
->levels
[j
+1]){
309 float del
=(m
->peak
-m
->levels
[j
])/(m
->levels
[j
+1]-m
->levels
[j
]);
310 x
=(j
+del
)/m
->labels
*(widget
->allocation
.width
-xpad
*2-1)+xpad
;
312 }else if (j
==m
->labels
){
313 x
=widget
->allocation
.width
-xpad
+1;
319 if(x
<m
->bartrackers
[j
].pixelposhi
)
320 x
=m
->bartrackers
[j
].pixelposhi
;
323 int y
=widget
->allocation
.height
-lpad
-1;
325 gdk_draw_line(m
->backing
,widget
->style
->fg_gc
[0],
327 gdk_draw_line(m
->backing
,widget
->style
->fg_gc
[0],
328 x
-2,upad
+1,x
+2,upad
+1);
329 gdk_draw_line(m
->backing
,widget
->style
->fg_gc
[0],
330 x
-1,upad
+2,x
+1,upad
+2);
331 gdk_draw_line(m
->backing
,widget
->style
->fg_gc
[0],
333 gdk_draw_line(m
->backing
,widget
->style
->fg_gc
[0],
335 gdk_draw_line(m
->backing
,widget
->style
->fg_gc
[0],
338 gdk_draw_line(m
->backing
,widget
->style
->fg_gc
[1],
344 int width
=widget
->allocation
.width
-xpad
;
345 int height
=widget
->allocation
.height
;
346 GdkGC
*gc
=parent
->style
->bg_gc
[0];
348 /* blank scale to bg of parent */
349 gdk_draw_rectangle(m
->backing
,gc
,1,xpad
,0,width
-xpad
+1,height
-lpad
);
353 for(i
=0;i
<=m
->labels
;i
++){
354 int x
=rint(((float)i
)/m
->labels
*(widget
->allocation
.width
-xpad
*2-1))+xpad
;
355 int y
=widget
->allocation
.height
-lpad
-upad
;
359 if(m
->levels
[i
]>0.)gc
=1;
362 gdk_draw_line (m
->backing
,
363 widget
->style
->text_gc
[gc
],
366 gdk_draw_line (m
->backing
,
367 widget
->style
->text_gc
[gc
],
368 x
, y
/4+upad
, x
, y
+upad
);
370 pango_layout_get_pixel_size(m
->layout
[i
],&px
,&py
);
381 gdk_draw_layout (m
->backing
,
382 widget
->style
->text_gc
[gc
],
390 int width
=widget
->allocation
.width
;
391 int height
=widget
->allocation
.height
;
392 GdkGC
*gc
=parent
->style
->bg_gc
[0];
393 GdkGC
*light_gc
=parent
->style
->light_gc
[0];
394 GdkGC
*dark_gc
=parent
->style
->dark_gc
[0];
395 GdkGC
*mid_gc
=widget
->style
->bg_gc
[GTK_STATE_ACTIVE
];
397 /* blank side padding to bg of parent */
398 gdk_draw_rectangle(m
->backing
,gc
,1,0,0,xpad
,height
);
399 gdk_draw_rectangle(m
->backing
,gc
,1,width
-xpad
,0,xpad
,height
);
401 /* blank sides of trough */
402 gdk_draw_rectangle(m
->backing
,gc
,1,
407 gdk_draw_rectangle(m
->backing
,gc
,1,
408 m
->thumbhi_x
+xpad
+xpad
,
410 width
-xpad
-xpad
-m
->thumbhi_x
,
415 gdk_draw_line(m
->backing
,dark_gc
,0,0,width
-2,0);
416 gdk_draw_line(m
->backing
,dark_gc
,0,0,0,height
-lpad
);
417 gdk_draw_line(m
->backing
,dark_gc
,1,height
-lpad
,width
-2,height
-lpad
);
418 gdk_draw_line(m
->backing
,dark_gc
,width
-2,height
-lpad
,width
-2,1);
420 gdk_draw_line(m
->backing
,light_gc
,0,height
-lpad
+1,
421 width
-1,height
-lpad
+1);
422 gdk_draw_line(m
->backing
,light_gc
,width
-1,0,width
-1,height
-lpad
+1);
423 gdk_draw_line(m
->backing
,light_gc
,1,1,width
-3,1);
424 gdk_draw_line(m
->backing
,light_gc
,1,1,1,height
-lpad
-1);
429 if(lpad
>2 || m
->readout
==0){
431 gdk_draw_rectangle(m
->backing
,mid_gc
,1,
432 m
->thumblo_x
+1,height
-lpad
+1,
433 m
->thumbhi_x
-m
->thumblo_x
+xpad
*2,lpad
-1);
435 gdk_draw_line(m
->backing
,dark_gc
,
436 m
->thumblo_x
,height
-lpad
,
437 m
->thumblo_x
,height
-1);
441 gdk_draw_line(m
->backing
,light_gc
,
442 m
->thumblo_x
,height
-1,
443 m
->thumbhi_x
+xpad
*2,height
-1);
445 dark_gc
=widget
->style
->dark_gc
[GTK_STATE_ACTIVE
];
447 gdk_draw_line(m
->backing
,dark_gc
,
448 m
->thumblo_x
,height
-lpad
,
449 m
->thumbhi_x
+xpad
*2-1,height
-lpad
);
452 gdk_draw_line(m
->backing
,light_gc
,
453 m
->thumbhi_x
+xpad
*2,height
-1,
454 m
->thumbhi_x
+xpad
*2,height
-lpad
+1);
458 gdk_draw_point(m
->backing
,light_gc
,width
-1,height
-lpad
);
464 /* draw slider thumbs */
466 int height
=widget
->allocation
.height
,i
,j
;
467 GdkGC
*black_gc
=widget
->style
->black_gc
;
472 int inner
=(y1
+y0
)/2-y0
;
474 int A
[3]={outer
,outer
,outer
};
475 int B
[3]={inner
,inner
,inner
};
476 int C
[3]={inner
,inner
,inner
};
477 int D
[3]={outer
,outer
,outer
};
479 GdkColor yellow
={0,0xff00,0xd000,0};
482 /* adjustment required for overlapping thumbs? */
483 int mid
=m
->thumbpixel
[1]-m
->thumbpixel
[0]-1;
484 int midA
=(mid
<0?0:mid
)/2;
485 int midB
=(mid
<0?0:mid
)-midA
;
487 if(midA
<D
[0])D
[0]=midA
;
488 if(midA
<C
[0])C
[0]=midA
;
489 if(midB
<A
[1])A
[1]=midB
;
490 if(midB
<B
[1])B
[1]=midB
;
494 /* adjust for 0,1 overlap if any; different from the 2 case */
495 int mid
=m
->thumbpixel
[1]-m
->thumbpixel
[0]-1;
496 int midA
=(mid
<0?0:mid
)/2;
497 int midB
=(mid
<0?0:mid
)-midA
;
499 if(midA
<D
[0])D
[0]=midA
;
500 if(D
[0]<C
[0]+2)D
[0]=C
[0]+2;
501 if(midB
<A
[1])A
[1]=midB
;
502 if(A
[1]<B
[1]+2)A
[1]=B
[1]+2;
504 /* adjust for 1,2 overlap if any */
506 mid
=m
->thumbpixel
[2]-m
->thumbpixel
[1]-1;
507 midA
=(mid
<0?0:mid
)/2;
508 midB
=(mid
<0?0:mid
)-midA
;
510 if(midA
<D
[1])D
[1]=midA
;
511 if(D
[1]<C
[1]+2)D
[1]=C
[1]+2;
512 if(midB
<A
[2])A
[2]=midB
;
513 if(A
[2]<B
[2]+2)A
[2]=B
[2]+2;
516 for(i
=0;i
<m
->thumbs
;i
++){
518 /* in the three-thumb case, the middle thumb is drawn last */
534 int x
=m
->thumbpixel
[j
]+xpad
;
535 GdkPoint p
[8]={ {x
+D
[j
],y0
+C
[j
]},
544 GdkPoint d
[3]={ {x
-A
[j
]+1,y1
},
546 {x
+D
[j
]-1,y0
+C
[j
]+1}};
548 GdkGC
*gc
=widget
->style
->bg_gc
[m
->thumbstate
[j
]];
549 GdkGC
*light_gc
=widget
->style
->light_gc
[m
->thumbstate
[j
]];
550 GdkGC
*dark_gc
=widget
->style
->dark_gc
[m
->thumbstate
[j
]];
552 gdk_draw_polygon(m
->backing
,gc
,TRUE
,p
,7);
553 gdk_draw_lines(m
->backing
,dark_gc
,d
,3);
555 if(m
->thumbfocus
==j
&& m
->widgetfocus
){
557 gdk_gc_set_stipple(black_gc
,stipple
);
559 gdk_gc_set_stipple(black_gc
,stippleB
);
561 gdk_gc_set_fill(black_gc
,GDK_STIPPLED
);
562 gdk_draw_polygon(m
->backing
,black_gc
,TRUE
,p
,7);
563 gdk_gc_set_fill(black_gc
,GDK_SOLID
);
566 gdk_draw_lines(m
->backing
,light_gc
,p
,6);
567 gdk_draw_lines(m
->backing
,black_gc
,p
+5,3);
569 gdk_gc_set_rgb_fg_color(m
->boxcolor
,&yellow
);
570 gdk_draw_line(m
->backing
,m
->boxcolor
,x
,y1
-1,x
,0);
576 static void draw_and_expose(GtkWidget
*widget
){
577 Multibar
*m
=MULTIBAR(widget
);
578 if(!GDK_IS_DRAWABLE(m
->backing
))return;
579 draw(widget
,m
->bars
);
580 if(!GTK_WIDGET_DRAWABLE(widget
))return;
581 if(!GDK_IS_DRAWABLE(widget
->window
))return;
582 gdk_draw_drawable(widget
->window
,
583 widget
->style
->fg_gc
[GTK_WIDGET_STATE (widget
)],
587 widget
->allocation
.width
,
588 widget
->allocation
.height
);
591 static gboolean
expose( GtkWidget
*widget
, GdkEventExpose
*event
){
592 Multibar
*m
=MULTIBAR(widget
);
593 gdk_draw_drawable(widget
->window
,
594 widget
->style
->fg_gc
[GTK_WIDGET_STATE (widget
)],
596 event
->area
.x
, event
->area
.y
,
597 event
->area
.x
, event
->area
.y
,
598 event
->area
.width
, event
->area
.height
);
603 static void size_request (GtkWidget
*widget
,GtkRequisition
*requisition
){
604 int i
,maxx
=0,maxy
=0,x
,y
,xpad
;
605 Multibar
*m
=MULTIBAR(widget
);
607 for(i
=0;i
<=m
->labels
;i
++){
608 pango_layout_get_pixel_size(m
->layout
[i
],&x
,&y
);
623 requisition
->width
= (maxx
*1.5+2)*m
->labels
+xpad
*2;
624 requisition
->height
= maxy
;
628 static int transition_thumbfocus
=0;
629 static gboolean
multibar_focus (GtkWidget
*widget
,
630 GtkDirectionType direction
){
631 Multibar
*m
=MULTIBAR(widget
);
634 if(m
->thumbs
==0)return FALSE
;
635 if(!m
->widgetfocus
)m
->thumbfocus
=-1;
638 case GTK_DIR_TAB_FORWARD
:
640 if(m
->thumbfocus
+1>=m
->thumbs
){
647 case GTK_DIR_TAB_BACKWARD
:
649 if(m
->thumbfocus
==-1)
650 m
->thumbfocus
=m
->thumbs
-1;
652 if(m
->thumbfocus
-1<0){
662 if(m
->thumbfocus
==-1){
663 if(transition_thumbfocus
>=0 && transition_thumbfocus
<m
->thumbs
)
664 m
->thumbfocus
=transition_thumbfocus
;
669 transition_thumbfocus
=m
->thumbfocus
;
680 if(ret
==TRUE
) gtk_widget_grab_focus(widget
);
681 draw_and_expose(widget
);
686 static gint
determine_thumb(Multibar
*m
,int ix
, int iy
){
687 GtkWidget
*widget
=GTK_WIDGET(m
);
688 int max
=widget
->allocation
.height
;
689 float distances
[3]={-1,-1,-1};
693 if(m
->thumbs
==1 || m
->thumbs
>2){
694 int num
=(m
->thumbs
==1?0:1);
695 int x
= m
->thumbpixel
[num
]-ix
;
696 distances
[num
]=fabs(x
);
701 int x
= m
->thumbpixel
[0]-ix
;
702 distances
[0]=fabs(x
-.1);
707 int num
=(m
->thumbs
==2?1:2);
708 int x
= m
->thumbpixel
[num
]-ix
;
709 distances
[num
]=fabs(x
+.1);
712 if(m
->thumbs
&& distances
[0]<max
)thumb
=0;
713 if(m
->thumbs
>1 && distances
[1]<max
)
714 if(thumb
== -1 || distances
[1]<distances
[0])thumb
=1;
715 if(m
->thumbs
>2 && distances
[2]<max
)
716 if(thumb
== -1 || (distances
[2]<distances
[0] &&
717 distances
[2]<distances
[1]))thumb
=2;
718 if(m
->thumbs
>2 && distances
[1]<max
/2)thumb
=1;
723 static int pixel_bound(Multibar
*m
,int x
){
724 GtkWidget
*w
=GTK_WIDGET(m
);
726 if(x
>w
->allocation
.width
-m
->xpad
*2-1)
727 return w
->allocation
.width
-m
->xpad
*2-1;
731 static float pixel_to_val(Multibar
*m
,int x
){
732 GtkWidget
*w
=GTK_WIDGET(m
);
735 for(j
=0;j
<=m
->labels
;j
++){
736 int pixlo
=rint((float)j
/m
->labels
*(w
->allocation
.width
-m
->xpad
*2-1));
737 int pixhi
=rint((float)(j
+1)/m
->labels
*(w
->allocation
.width
-m
->xpad
*2-1));
739 if(x
>=pixlo
&& x
<=pixhi
){
740 if(pixlo
==pixhi
)return m
->levels
[j
];
741 float del
=(float)(x
-pixlo
)/(pixhi
-pixlo
);
742 return (1.-del
)*m
->levels
[j
] + del
*m
->levels
[j
+1];
748 static int val_to_pixel(Multibar
*m
,float v
){
749 GtkWidget
*w
=GTK_WIDGET(m
);
754 }else if(v
>m
->levels
[m
->labels
]){
755 ret
=w
->allocation
.width
-m
->xpad
*2-1;
757 for(j
=0;j
<=m
->labels
;j
++){
758 if(v
>=m
->levels
[j
] && v
<=m
->levels
[j
+1]){
759 float del
=(v
-m
->levels
[j
])/(m
->levels
[j
+1]-m
->levels
[j
]);
760 int pixlo
=rint((float)j
/m
->labels
*(w
->allocation
.width
-m
->xpad
*2-1));
761 int pixhi
=rint((float)(j
+1)/m
->labels
*(w
->allocation
.width
-m
->xpad
*2-1));
762 ret
=rint(pixlo
*(1.-del
)+pixhi
*del
);
768 ret
=pixel_bound(m
,ret
);
772 static gboolean
configure(GtkWidget
*widget
, GdkEventConfigure
*event
){
773 Multibar
*m
=MULTIBAR(widget
);
777 g_object_unref(m
->backing
);
779 m
->backing
= gdk_pixmap_new(widget
->window
,
780 widget
->allocation
.width
,
781 widget
->allocation
.height
,
783 gdk_draw_rectangle(m
->backing
,widget
->style
->white_gc
,1,0,0,widget
->allocation
.width
,
784 widget
->allocation
.height
);
787 for(i
=0;i
<m
->thumbs
;i
++)
788 m
->thumbpixel
[i
]=val_to_pixel(m
,m
->thumbval
[i
]);
789 m
->thumblo_x
=val_to_pixel(m
,m
->thumblo
);
790 m
->thumbhi_x
=val_to_pixel(m
,m
->thumbhi
);
792 draw_and_expose(widget
);
797 static void partial_vals_bound(Multibar
*m
){
800 if(m
->thumbsmall
>0 && m
->thumblarge
>0)
801 for(i
=0;i
<m
->thumbs
;i
++)
802 m
->thumbval
[i
]=rint(m
->thumbval
[i
]/m
->thumbsmall
)*m
->thumbsmall
;
804 for(i
=0;i
<m
->thumbs
;i
++){
805 if(m
->thumbval
[i
]<m
->thumblo
)m
->thumbval
[i
]=m
->thumblo
;
806 if(m
->thumbval
[i
]>m
->thumbhi
)m
->thumbval
[i
]=m
->thumbhi
;
807 m
->thumbpixel
[i
]=val_to_pixel(m
,m
->thumbval
[i
]);
811 static void vals_bound(Multibar
*m
){
812 partial_vals_bound(m
);
814 if(m
->thumbfocus
>=0){
815 float v
=m
->thumbval
[m
->thumbfocus
];
816 int x
=m
->thumbpixel
[m
->thumbfocus
];
818 if(m
->thumbfocus
==2){
819 if(m
->thumbpixel
[1]>x
){
823 if(m
->thumbpixel
[0]>x
){
829 if(m
->thumbfocus
==1){
830 if(m
->thumbpixel
[2]<x
){
834 if(m
->thumbpixel
[0]>x
){
840 if(m
->thumbfocus
==0){
841 if(m
->thumbpixel
[2]<x
){
845 if(m
->thumbpixel
[1]<x
){
853 static gint
lightme(GtkWidget
*w
,gint x
,gint y
){
854 Multibar
*m
=MULTIBAR(w
);
855 int thumb
=determine_thumb(m
,x
,y
);
856 GtkStateType thumbstate
[3];
857 thumbstate
[0]=GTK_STATE_NORMAL
;
858 thumbstate
[1]=GTK_STATE_NORMAL
;
859 thumbstate
[2]=GTK_STATE_NORMAL
;
860 if(thumb
>=0)thumbstate
[thumb
]=GTK_STATE_PRELIGHT
;
862 if(thumbstate
[0]!=m
->thumbstate
[0] ||
863 thumbstate
[1]!=m
->thumbstate
[1] ||
864 thumbstate
[2]!=m
->thumbstate
[2]){
865 m
->thumbstate
[0]=thumbstate
[0];
866 m
->thumbstate
[1]=thumbstate
[1];
867 m
->thumbstate
[2]=thumbstate
[2];
874 static gint
multibar_motion(GtkWidget
*w
,
875 GdkEventMotion
*event
){
876 Multibar
*m
=MULTIBAR(w
);
878 /* is a thumb already grabbed? */
881 int x
=event
->x
+m
->thumbx
;
885 m
->thumbval
[m
->thumbgrab
]=pixel_to_val(m
,x
);
887 v
=m
->thumbval
[m
->thumbgrab
];
888 x
=m
->thumbpixel
[m
->thumbgrab
]=val_to_pixel(m
,v
);
890 if(m
->callback
)m
->callback(GTK_WIDGET(m
),m
->callbackp
);
894 /* nothing grabbed right now; determine if we're in a a thumb's area */
895 lightme(w
,event
->x
-m
->xpad
,event
->y
);
901 static gint
multibar_enter(GtkWidget
*w
,
902 GdkEventCrossing
*event
){
903 Multibar
*m
=MULTIBAR(w
);
904 lightme(w
,event
->x
-m
->xpad
,event
->y
);
908 static gint
multibar_leave(GtkWidget
*widget
,
909 GdkEventCrossing
*event
){
910 Multibar
*m
=MULTIBAR(widget
);
913 if(m
->thumbstate
[0] ||
920 draw_and_expose(widget
);
926 static gboolean
button_press (GtkWidget
*widget
,
927 GdkEventButton
*event
){
928 Multibar
*m
=MULTIBAR(widget
);
929 int thumb
=determine_thumb(m
,event
->x
-m
->xpad
,event
->y
);
932 gtk_widget_grab_focus(widget
);
933 m
->thumbgrab
=m
->thumbfocus
=0;
934 m
->thumbx
=m
->thumbpixel
[0]-event
->x
;
936 gtk_widget_grab_focus(widget
);
937 m
->thumbgrab
=m
->thumbfocus
=1;
938 m
->thumbx
=m
->thumbpixel
[1]-event
->x
;
940 gtk_widget_grab_focus(widget
);
941 m
->thumbgrab
=m
->thumbfocus
=2;
942 m
->thumbx
=m
->thumbpixel
[2]-event
->x
;
944 draw_and_expose(widget
);
948 static gboolean
button_release (GtkWidget
*widget
,
949 GdkEventButton
*event
){
950 Multibar
*m
=MULTIBAR(widget
);
952 draw_and_expose(widget
);
956 static gboolean
unfocus(GtkWidget
*widget
,
957 GdkEventFocus
*event
){
958 Multibar
*m
=MULTIBAR(widget
);
960 draw_and_expose(widget
);
964 static gboolean
refocus(GtkWidget
*widget
,
965 GdkEventFocus
*event
){
966 Multibar
*m
=MULTIBAR(widget
);
968 draw_and_expose(widget
);
972 gboolean
key_press(GtkWidget
*w
,GdkEventKey
*event
){
973 Multibar
*m
=MULTIBAR(w
);
975 if(event
->state
&GDK_MOD1_MASK
) return FALSE
;
976 if(event
->state
&GDK_CONTROL_MASK
) return FALSE
;
978 if(m
->thumbfocus
>=0){
979 if(m
->thumbsmall
>0 && m
->thumblarge
>0){
981 switch(event
->keyval
){
983 m
->thumbval
[m
->thumbfocus
]-=m
->thumbsmall
;
986 m
->thumbval
[m
->thumbfocus
]-=m
->thumblarge
;
989 m
->thumbval
[m
->thumbfocus
]+=m
->thumbsmall
;
992 m
->thumbval
[m
->thumbfocus
]+=m
->thumblarge
;
999 x
=val_to_pixel(m
,m
->thumbval
[m
->thumbfocus
]);
1001 m
->thumbpixel
[m
->thumbfocus
]=x
;
1005 switch(event
->keyval
){
1007 x
=m
->thumbpixel
[m
->thumbfocus
]-1;
1009 case GDK_underscore
:
1010 x
=m
->thumbpixel
[m
->thumbfocus
]-10;
1013 x
=m
->thumbpixel
[m
->thumbfocus
]+1;
1016 x
=m
->thumbpixel
[m
->thumbfocus
]+10;
1023 m
->thumbpixel
[m
->thumbfocus
]=x
;
1024 m
->thumbval
[m
->thumbfocus
]=pixel_to_val(m
,x
);
1029 if(m
->callback
)m
->callback(GTK_WIDGET(m
),m
->callbackp
);
1038 static GtkDrawingAreaClass
*parent_class
= NULL
;
1041 static void state_changed(GtkWidget
*w
,GtkStateType ps
){
1045 static void multibar_class_init (MultibarClass
*class){
1046 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS (class);
1047 parent_class
= g_type_class_peek_parent (class);
1049 widget_class
->expose_event
= expose
;
1050 widget_class
->configure_event
= configure
;
1051 widget_class
->size_request
= size_request
;
1052 widget_class
->focus
=multibar_focus
;
1053 widget_class
->key_press_event
= key_press
;
1054 widget_class
->button_press_event
= button_press
;
1055 widget_class
->button_release_event
= button_release
;
1056 widget_class
->enter_notify_event
= multibar_enter
;
1057 widget_class
->leave_notify_event
= multibar_leave
;
1058 widget_class
->motion_notify_event
= multibar_motion
;
1059 widget_class
->focus_out_event
= unfocus
;
1060 widget_class
->focus_in_event
= refocus
;
1061 widget_class
->state_changed
= state_changed
;
1063 stipple
=gdk_bitmap_create_from_data(NULL
,"\125\352",2,2);
1064 stippleB
=gdk_bitmap_create_from_data(NULL
,"\352\125",2,2);
1068 static void multibar_init (Multibar
*m
){
1075 GType
multibar_get_type (void){
1076 static GType m_type
= 0;
1078 static const GTypeInfo m_info
={
1079 sizeof (MultibarClass
),
1080 NULL
, /* base_init */
1081 NULL
, /* base_finalize */
1082 (GClassInitFunc
) multibar_class_init
,
1083 NULL
, /* class_finalize */
1084 NULL
, /* class_data */
1087 (GInstanceInitFunc
) multibar_init
,
1091 m_type
= g_type_register_static (GTK_TYPE_DRAWING_AREA
, "Multibar", &m_info
, 0);
1097 GtkWidget
* multibar_new (int n
, char **labels
, float *levels
, int thumbs
,
1100 GtkWidget
*ret
= GTK_WIDGET (g_object_new (multibar_get_type (), NULL
));
1101 Multibar
*m
=MULTIBAR(ret
);
1103 /* not the *proper* way to do it, but this is a one-shot */
1104 m
->levels
=calloc((n
+1),sizeof(*m
->levels
));
1106 memcpy(m
->levels
,levels
,n
*sizeof(*levels
));
1108 m
->layout
=calloc(n
,sizeof(*m
->layout
));
1110 m
->layout
[i
]=gtk_widget_create_pango_layout(ret
,labels
[i
]);
1112 m
->dampen_flags
=flags
;
1115 m
->thumblo
=levels
[0];
1116 m
->thumbhi
=levels
[n
-1];
1117 m
->thumblo_x
=val_to_pixel(m
,m
->thumblo
);
1118 m
->thumbhi_x
=val_to_pixel(m
,m
->thumbhi
);
1120 if(thumbs
<0)thumbs
=0;
1121 if(thumbs
>3)thumbs
=3;
1123 if(m
->thumbs
!=0) GTK_WIDGET_SET_FLAGS (m
, GTK_CAN_FOCUS
);
1126 int events
=gtk_widget_get_events(ret
);
1127 gtk_widget_set_events(ret
, events
|
1128 GDK_POINTER_MOTION_MASK
|
1129 GDK_BUTTON_PRESS_MASK
|
1130 GDK_BUTTON_RELEASE_MASK
|
1131 GDK_KEY_PRESS_MASK
|
1132 GDK_KEY_RELEASE_MASK
|
1133 GDK_ENTER_NOTIFY_MASK
|
1134 GDK_LEAVE_NOTIFY_MASK
|
1135 GDK_FOCUS_CHANGE_MASK
);
1142 GtkWidget
* multibar_slider_new (int n
, char **labels
, float *levels
,
1144 GtkWidget
*ret
= multibar_new(n
,labels
,levels
,thumbs
,0);
1145 Multibar
*m
=MULTIBAR(ret
);
1147 gtk_widget_set_name(ret
,"Multislide");
1152 void multibar_set(Multibar
*m
,float *lo
, float *hi
, int n
, int draw
){
1153 GtkWidget
*widget
=GTK_WIDGET(m
);
1154 compute(m
,lo
,hi
,n
,draw
);
1155 if(draw
)draw_and_expose(widget
);
1158 void multibar_thumb_set(Multibar
*m
,float v
, int n
){
1159 GtkWidget
*w
=GTK_WIDGET(m
);
1163 if(n
>=m
->thumbs
)return;
1167 partial_vals_bound(m
);
1169 x
=m
->thumbpixel
[n
]=val_to_pixel(m
,v
);
1173 if(m
->thumbpixel
[1]<x
){
1177 if(m
->thumbpixel
[2]<x
){
1184 if(m
->thumbpixel
[0]>x
){
1188 if(m
->thumbpixel
[2]<x
){
1195 if(m
->thumbpixel
[0]>x
){
1199 if(m
->thumbpixel
[1]>x
){
1205 if(m
->callback
)m
->callback(GTK_WIDGET(m
),m
->callbackp
);
1209 void multibar_reset(Multibar
*m
){
1214 multibar_set(m
,NULL
,NULL
,0,1);
1217 void multibar_setwarn(Multibar
*m
,int draw
){
1218 GtkWidget
*widget
=GTK_WIDGET(m
);
1222 if(draw
)draw_and_expose(widget
);
1224 m
->clipdelay
=15*10; /* ~ ten second hold */
1227 /* because closures are ludicrously complex for doing something this simple */
1228 void multibar_callback(Multibar
*m
,void (*callback
)(GtkWidget
*,gpointer
),
1230 m
->callback
=callback
;
1234 float multibar_get_value(Multibar
*m
,int n
){
1236 if(n
>m
->thumbs
)return 0.;
1237 return m
->thumbval
[n
];
1240 void multibar_thumb_bounds(Multibar
*m
,float lo
, float hi
){
1241 GtkWidget
*w
=GTK_WIDGET(m
);
1244 if(lo
<m
->levels
[0])lo
=m
->levels
[0];
1245 if(hi
<m
->levels
[0])hi
=m
->levels
[0];
1246 if(lo
>m
->levels
[m
->labels
])lo
=m
->levels
[m
->labels
];
1247 if(hi
>m
->levels
[m
->labels
])hi
=m
->levels
[m
->labels
];
1252 m
->thumblo_x
=val_to_pixel(m
,lo
);
1253 m
->thumbhi_x
=val_to_pixel(m
,hi
);
1256 if(m
->callback
)m
->callback(GTK_WIDGET(m
),m
->callbackp
);
1260 void multibar_thumb_increment(Multibar
*m
,float small
, float large
){
1261 GtkWidget
*w
=GTK_WIDGET(m
);
1262 if(small
>large
)return;
1264 m
->thumbsmall
=small
;
1265 m
->thumblarge
=large
;
1268 if(m
->callback
)m
->callback(GTK_WIDGET(m
),m
->callbackp
);
1272 int multibar_thumb_grab_p(Multibar
*m
){
1273 return m
->widgetfocus
;
1276 int multibar_thumb_focus(Multibar
*m
){
1277 return m
->thumbfocus
;