sort #includes by directories
[obnox/wireshark/wip.git] / gtk / gtkvumeter.c
1 /***************************************************************************
2  *            gtkvumeter.c
3  *
4  *  Fri Jan 10 20:06:23 2003
5  *  Copyright  2003  Todd Goyen
6  *  wettoad@knighthoodofbuh.org
7  *
8  *  Mon May 01 04:04:00 2006
9  *  Copyright  2006  Ulf Lamping
10  *  ulf.lamping@web.de
11  *
12  *  Source code is LGPL'd,
13  *  but may be distributed under any other open source license
14  ****************************************************************************/
15
16 #include <math.h>
17 #include <gtk/gtk.h>
18 #include "gtk/gtkvumeter.h"
19
20
21 #define MIN_DYNAMIC_SIDE    40
22
23 #define SMALL_PITCH_LINE    2
24 #define LARGE_PITCH_LINE    4
25
26 #define SPARE_LEFT          1
27 #define SPARE_RIGHT         1
28 #define SPARE_TOP           1
29 #define SPARE_BOTTOM        1
30
31
32 static void gtk_vumeter_init (GtkVUMeter *vumeter);
33 static void gtk_vumeter_class_init (GtkVUMeterClass *class);
34 static void gtk_vumeter_destroy (GtkObject *object);
35 static void gtk_vumeter_realize (GtkWidget *widget);
36 static void gtk_vumeter_size_calculate (GtkWidget *widget, GtkRequisition *requisition);
37 static void gtk_vumeter_size_request (GtkWidget *widget, GtkRequisition *requisition);
38 static void gtk_vumeter_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
39 static gint gtk_vumeter_expose (GtkWidget *widget, GdkEventExpose *event);
40 static void gtk_vumeter_free_colors (GtkVUMeter *vumeter);
41 static void gtk_vumeter_setup_colors (GtkVUMeter *vumeter);
42 static gint gtk_vumeter_sound_level_to_draw_level (GtkVUMeter *vumeter, gint sound_level);
43 static gboolean gtk_vumeter_redraw_timeout (GtkVUMeter *vumeter);
44 static void gtk_vumeter_setup_scale_items(GtkVUMeter *vumeter, GList *scale_items);
45
46 static GtkWidgetClass *parent_class = NULL;
47
48 GtkType gtk_vumeter_get_type (void)
49 {
50     static GType vumeter_type = 0;
51     
52     if (!vumeter_type) {
53         static const GTypeInfo vumeter_info = {
54             sizeof (GtkVUMeterClass),
55             NULL, NULL,
56             (GClassInitFunc) gtk_vumeter_class_init, NULL, NULL,
57             sizeof (GtkVUMeter), 0, (GInstanceInitFunc) gtk_vumeter_init,
58             NULL
59         };
60         vumeter_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkVUMeter", &vumeter_info, 0);
61     }
62
63     return vumeter_type;
64 }
65
66 /**
67  * gtk_vumeter_new:
68  *
69  * Creates a new VUMeter widget.
70  */
71 GtkWidget* gtk_vumeter_new (void)
72 {
73     GtkVUMeter *vumeter;
74
75     vumeter = GTK_VUMETER (g_object_new (GTK_TYPE_VUMETER, NULL));
76
77     return GTK_WIDGET (vumeter);
78 }
79
80 static void gtk_vumeter_init (GtkVUMeter *vumeter)
81 {
82     vumeter->vertical = TRUE;
83     vumeter->scale_inverted = FALSE;
84     vumeter->thickness = 30;
85     vumeter->reduced_thickness = 0;
86     vumeter->scaling = GTK_VUMETER_SCALING_LINEAR;
87     vumeter->scale_items = NULL;
88     vumeter->scale_pitch_holes = 0;
89
90     vumeter->padding_left = 1;
91     vumeter->padding_right = 1;
92     vumeter->padding_top = 1;
93     vumeter->padding_bottom = 1;
94
95     vumeter->colormap = NULL;
96     vumeter->colors = 0;
97     vumeter->f_gc = NULL;
98     vumeter->b_gc = NULL;
99     vumeter->f_colors = NULL;
100     vumeter->b_colors = NULL;
101     vumeter->f_brightness = 65535;
102     vumeter->b_brightness = 49151;
103     vumeter->yellow_level = 16383;
104     vumeter->colors_inverted = FALSE;
105
106     vumeter->level = 0;
107     vumeter->level_min = 0;
108     vumeter->level_max = 32767;
109     
110     vumeter->peak = FALSE;
111     vumeter->peak_level = 0;
112     vumeter->peak_timeout = 0;
113     vumeter->peak_hold_factor = 0;
114     vumeter->peak_hold = 0;
115     vumeter->peak_falloff_mode = GTK_VUMETER_PEAK_FALLOFF_MEDIUM;
116     vumeter->peak_falloff_rate = 3278;
117 }
118
119 static void gtk_vumeter_class_init (GtkVUMeterClass *class)
120 {
121     GtkObjectClass *object_class;
122     GtkWidgetClass *widget_class;
123
124     object_class = (GtkObjectClass*) class;
125     widget_class = (GtkWidgetClass*) class;
126     parent_class = gtk_type_class (gtk_widget_get_type ());
127
128     object_class->destroy = gtk_vumeter_destroy;
129     
130     widget_class->realize = gtk_vumeter_realize;
131     widget_class->expose_event = gtk_vumeter_expose;
132     widget_class->size_request = gtk_vumeter_size_request;
133     widget_class->size_allocate = gtk_vumeter_size_allocate;    
134 }
135
136 static void gtk_vumeter_destroy (GtkObject *object)
137 {
138     GtkVUMeter *vumeter = GTK_VUMETER (object);
139
140     if(vumeter->peak_timeout) {
141         gtk_timeout_remove(vumeter->peak_timeout);
142     }
143
144     gtk_vumeter_free_colors (vumeter);
145     
146     GTK_OBJECT_CLASS (parent_class)->destroy (object);
147 }
148
149 static void gtk_vumeter_realize (GtkWidget *widget)
150 {
151     GtkVUMeter *vumeter;
152     GdkWindowAttr attributes;
153     gint attributes_mask;
154     
155     g_return_if_fail (widget != NULL);
156     g_return_if_fail (GTK_IS_VUMETER (widget));
157
158     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
159     vumeter = GTK_VUMETER (widget);
160
161     attributes.x = widget->allocation.x;
162     attributes.y = widget->allocation.y;
163     attributes.width = widget->allocation.width;
164     attributes.height = widget->allocation.height;
165     attributes.wclass = GDK_INPUT_OUTPUT;
166     attributes.window_type = GDK_WINDOW_CHILD;
167     attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
168     attributes.visual = gtk_widget_get_visual (widget);
169     attributes.colormap = gtk_widget_get_colormap (widget);
170     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
171     widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
172
173     widget->style = gtk_style_attach (widget->style, widget->window);
174
175     gdk_window_set_user_data (widget->window, widget);
176     gtk_style_set_background (widget->style, widget->window,  GTK_STATE_NORMAL);
177     
178     /* colors */
179     vumeter->colormap = gdk_colormap_get_system ();
180     gtk_vumeter_setup_colors (vumeter);
181 }
182
183 static void gtk_vumeter_size_calculate (GtkWidget *widget, GtkRequisition *requisition)
184 {
185     GtkVUMeter *vumeter;
186     gint max_x = 0;
187     gint max_y = 0;
188     gint layout_width;
189     gint layout_height;
190     gint pitches = 0;
191     GList * current;
192
193     g_return_if_fail (GTK_IS_VUMETER (widget));
194     g_return_if_fail (requisition != NULL);
195
196     vumeter = GTK_VUMETER (widget);
197
198     if(vumeter->scale_items != NULL) {
199         /* iterate through scale items to get the highest scale item */
200         for (current = vumeter->scale_items; current != NULL; current = g_list_next(current)) {
201             GtkVUMeterScaleItem * item = current->data;
202
203             pitches++;
204
205             if(item->label) {
206                 PangoLayout * layout = gtk_widget_create_pango_layout (widget, item->label);
207                 pango_layout_get_pixel_size(layout, &layout_width, &layout_height);
208                 /* XXX - memleak */
209             }
210
211             if (vumeter->vertical == TRUE) {
212                 max_x = MAX(max_x, item->large ? LARGE_PITCH_LINE : SMALL_PITCH_LINE);
213                 max_y = MAX(max_y, 1);
214                 if(item->label) {
215                     max_x = MAX(max_x, LARGE_PITCH_LINE+3+layout_width);
216                     max_y = MAX(max_y, layout_height);
217                 }
218             } else {
219                 max_x = MAX(max_x, 1);
220                 max_y = MAX(max_y, item->large ? LARGE_PITCH_LINE : SMALL_PITCH_LINE);
221                 if(item->label) {
222                     max_x = MAX(max_x, layout_width);
223                     max_y = MAX(max_y, LARGE_PITCH_LINE+/*3+*/layout_height-2);
224                 }
225             }
226         }
227     }
228
229     pitches = MAX((vumeter->scale_pitch_holes+1)*pitches-1, MIN_DYNAMIC_SIDE);
230
231     if (vumeter->vertical == TRUE) {
232         vumeter->padding_left = SPARE_LEFT;
233         vumeter->padding_right = SPARE_RIGHT + max_x;
234         vumeter->padding_top = SPARE_TOP + max_y / 2;
235         vumeter->padding_bottom = SPARE_BOTTOM + max_y / 2;
236         requisition->width = vumeter->padding_left + vumeter->thickness + vumeter->padding_right;
237         requisition->height = vumeter->padding_top + pitches + vumeter->padding_bottom;
238     } else {
239         vumeter->padding_left = SPARE_LEFT + max_x / 2;
240         vumeter->padding_right = SPARE_RIGHT + max_x / 2;
241         vumeter->padding_top = SPARE_TOP;
242         vumeter->padding_bottom = SPARE_BOTTOM + max_y;
243         requisition->width = vumeter->padding_left + pitches + vumeter->padding_right;
244         requisition->height = vumeter->padding_top + vumeter->thickness + vumeter->padding_bottom;
245     }
246 }
247
248 static void gtk_vumeter_size_request (GtkWidget *widget, GtkRequisition *requisition)
249 {
250     gtk_vumeter_size_calculate(widget, requisition);
251 }
252
253 static void gtk_vumeter_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
254 {
255     GtkVUMeter *vumeter;
256     GtkRequisition requisition;
257     
258     g_return_if_fail (GTK_IS_VUMETER (widget));
259     g_return_if_fail (allocation != NULL);
260
261     widget->allocation = *allocation;
262     vumeter = GTK_VUMETER (widget);
263
264     gtk_vumeter_size_calculate(widget, &requisition);
265
266     if (GTK_WIDGET_REALIZED (widget)) {
267         gdk_window_move_resize (widget->window, allocation->x, allocation->y,
268                         MAX (allocation->width, requisition.width),
269             MAX (allocation->height, requisition.height));
270
271         /* Fix the colours */
272         gtk_vumeter_setup_colors (vumeter);
273     }
274 }
275
276 static gint gtk_vumeter_expose (GtkWidget *widget, GdkEventExpose *event)
277 {
278     GtkVUMeter *vumeter;
279     gint index, level, peak_level = 0;
280     gint width, height;
281     gint w, h, inc;
282     GList * current;
283     
284     g_return_val_if_fail (GTK_IS_VUMETER (widget), FALSE);
285     g_return_val_if_fail (event != NULL, FALSE);
286
287     vumeter = GTK_VUMETER (widget);
288
289     level = gtk_vumeter_sound_level_to_draw_level (vumeter, vumeter->level);
290     if (vumeter->peak == TRUE) {
291         peak_level = gtk_vumeter_sound_level_to_draw_level (vumeter, vumeter->peak_level);
292     }
293
294     /* the dimentions of the bar (leaving some space for the scale) */
295     width = widget->allocation.width - vumeter->padding_left - vumeter->padding_right;
296     height = widget->allocation.height - vumeter->padding_top - vumeter->padding_bottom;
297
298     /* clear widget and draw border */
299     gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, 
300         NULL, widget, "trough", 0, 0, widget->allocation.width, widget->allocation.height);
301
302 #if 0
303     /* clear bar only */
304     gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, 
305         NULL, widget, "trough", vumeter->padding_left, vumeter->padding_top, width+1, height+1);
306 #endif
307
308     if (vumeter->vertical == TRUE) {
309         if (vumeter->scale_inverted == TRUE) {
310             h = height + vumeter->padding_top;
311             inc = -1;
312         } else {
313             h = vumeter->padding_top;
314             inc = 1;
315         }
316         
317         /* draw scale */
318         if(vumeter->scale_items != NULL) {
319             /* iterate through scale items */
320             for (current = vumeter->scale_items; current != NULL; current = g_list_next(current)) {
321                 GtkVUMeterScaleItem * item = current->data;
322                 int scale_level = gtk_vumeter_sound_level_to_draw_level (vumeter, item->level);
323
324                 /* XXX - use a fixed color for the scale? */
325                 gdk_draw_line (widget->window, vumeter->b_gc[scale_level], 
326                     vumeter->padding_left+width, h + inc*scale_level,
327                     vumeter->padding_left+width+(item->large==TRUE ? LARGE_PITCH_LINE : SMALL_PITCH_LINE)-1, h + inc*scale_level);
328
329                 if(item->label) {
330                     int layout_width;
331                     int layout_height;
332                     PangoLayout * layout = gtk_widget_create_pango_layout (widget, item->label);
333                     pango_layout_get_pixel_size(layout, &layout_width, &layout_height);
334                     gdk_draw_layout(widget->window,
335                                              vumeter->b_gc[scale_level],
336                                              vumeter->padding_left+width+vumeter->padding_right-1-layout_width,
337                                              h + inc*scale_level - layout_height/2 - 1,
338                                              layout);
339                 }
340             }
341         }
342
343         /* draw background gradient */
344         for (index = 0; index < level; index++, h += inc) {
345             gdk_draw_line (widget->window, vumeter->b_gc[index], 
346                 vumeter->padding_left+vumeter->reduced_thickness, h, 
347                 vumeter->padding_left+width-1-vumeter->reduced_thickness, h);
348         }
349         /* draw foreground gradient */
350         for (index = level; index < height; index++, h += inc) {
351             gdk_draw_line (widget->window, vumeter->f_gc[index], 
352                 vumeter->padding_left,h, 
353                 vumeter->padding_left+width-1, h);
354         }
355         /* Draw the peak */
356         if (vumeter->peak == TRUE) {
357             /* Reset w */
358             index = MAX (peak_level - 1, 0);
359             for (; index < MIN (peak_level + 1, height - 2); index++, h += inc) {
360                 h = vumeter->scale_inverted == TRUE ? height +vumeter->padding_top - (index + 2) : vumeter->padding_top + index + 1;
361                 gdk_draw_line (widget->window, vumeter->f_gc[index], vumeter->padding_left, h, vumeter->padding_left+width-1, h);
362             }            
363         }
364     } else { /* Horizontal */
365         /* the start point of the bar */
366         if (vumeter->scale_inverted == TRUE) {
367             w = width-1 + vumeter->padding_left;
368             inc = -1;
369         } else {
370             w = vumeter->padding_left;
371             inc = 1;
372         }
373
374         /* draw scale */
375         if(vumeter->scale_items != NULL) {
376             /* iterate through scale items */
377             for (current = vumeter->scale_items; current != NULL; current = g_list_next(current)) {
378                 GtkVUMeterScaleItem * item = current->data;
379                 int scale_level = gtk_vumeter_sound_level_to_draw_level (vumeter, item->level);
380
381                 /* XXX - use a fixed color for the scale? */
382                 gdk_draw_line (widget->window, vumeter->b_gc[scale_level], 
383                     w + inc*scale_level, vumeter->padding_top+height, 
384                     w + inc*scale_level, vumeter->padding_top+height+(item->large==TRUE ? LARGE_PITCH_LINE : SMALL_PITCH_LINE)-1);
385
386                 if(item->label) {
387                     int layout_width;
388                     int layout_height;
389                     PangoLayout * layout = gtk_widget_create_pango_layout (widget, item->label);
390                     pango_layout_get_pixel_size(layout, &layout_width, &layout_height);
391                     gdk_draw_layout(widget->window, vumeter->b_gc[scale_level],
392                                              w + inc*scale_level - layout_width/2,
393                                              height + vumeter->padding_top + 3,
394                                              layout);
395                 }
396             }
397         }
398
399         /* draw background gradient */
400         for (index = 0; index < level; index++, w += inc) {
401             gdk_draw_line (widget->window, vumeter->b_gc[index], 
402                 w, vumeter->padding_top+vumeter->reduced_thickness, 
403                 w, vumeter->padding_top+height-1-vumeter->reduced_thickness);
404         }
405         /* draw foreground gradient */
406         for (index = level; index < width; index++, w+= inc) {
407             gdk_draw_line (widget->window, vumeter->f_gc[index], 
408                 w, vumeter->padding_top, 
409                 w, vumeter->padding_top+height-1);
410         }
411
412         /* Draw the peak */
413         if (vumeter->peak == TRUE) {
414             /* Reset w */
415             index = MAX (peak_level - 1, 0);
416             w = vumeter->scale_inverted == TRUE ? width + vumeter->padding_left - (index + 1) : vumeter->padding_left + index + 1;
417             for (; index <= MIN (peak_level, width - 2); index++, w+= inc) {
418                 gdk_draw_line (widget->window, vumeter->f_gc[index], w, vumeter->padding_top, w, vumeter->padding_top+height-1);
419             }
420         }       
421     }
422     
423     return TRUE;
424 }
425
426 static void gtk_vumeter_free_colors (GtkVUMeter *vumeter)
427 {
428     gint index;
429
430     if (vumeter->colors == 0) { return; }
431
432     /* Free old gc's */
433     if (vumeter->f_gc && vumeter->b_gc) {
434         for (index = 0; index < vumeter->colors; index++) {    
435             if (vumeter->f_gc[index]) {
436                 g_object_unref (G_OBJECT(vumeter->f_gc[index]));
437             }
438             if (vumeter->b_gc[index]) {        
439                 g_object_unref (G_OBJECT(vumeter->b_gc[index]));
440             }
441         }
442         g_free(vumeter->f_gc);
443         g_free(vumeter->b_gc);
444         vumeter->f_gc = NULL;
445         vumeter->b_gc = NULL;
446     }
447     
448     /* Free old Colors */
449     if (vumeter->f_colors) {
450         gdk_colormap_free_colors (vumeter->colormap, vumeter->f_colors, vumeter->colors);
451         g_free (vumeter->f_colors);
452         vumeter->f_colors = NULL;
453     }
454     if (vumeter->b_colors) {
455         gdk_colormap_free_colors (vumeter->colormap, vumeter->b_colors, vumeter->colors);
456         g_free (vumeter->b_colors);
457         vumeter->b_colors = NULL;
458     }
459 }
460
461 static void gtk_vumeter_setup_colors (GtkVUMeter *vumeter)
462 {
463     gint index;
464     gint f_step, b_step;
465     gint first, second;
466     gint max = 0, min = 0, log_max = 0;
467     
468     g_return_if_fail (vumeter->colormap != NULL);
469     
470     gtk_vumeter_free_colors (vumeter);
471     
472     /* Set new size */
473     if (vumeter->vertical == TRUE) {
474         vumeter->colors = MAX(GTK_WIDGET(vumeter)->allocation.height - vumeter->padding_top - vumeter->padding_bottom, 0);
475     } else {
476         vumeter->colors = MAX(GTK_WIDGET(vumeter)->allocation.width - vumeter->padding_left - vumeter->padding_right, 0);
477     }
478     
479     /* allocate new memory */
480     vumeter->f_colors = g_malloc (vumeter->colors * sizeof(GdkColor));
481     vumeter->b_colors = g_malloc (vumeter->colors * sizeof(GdkColor));    
482     vumeter->f_gc = g_malloc (vumeter->colors * sizeof(GdkGC *));
483     vumeter->b_gc = g_malloc (vumeter->colors * sizeof(GdkGC *));    
484
485     /* Initialize stuff */
486     if (vumeter->scaling == GTK_VUMETER_SCALING_LINEAR) {
487         first = 1+gtk_vumeter_sound_level_to_draw_level (vumeter, vumeter->yellow_level);
488         second = vumeter->colors;      
489     } else {
490         max = vumeter->level_max;
491         min = vumeter->level_min;
492         log_max = (gint) (- 20.0 * log10(1.0/(max - min + 1.0)));
493         first = (gint)((gdouble)vumeter->colors * 6.0 / log_max);
494         second = (gint)((gdouble)vumeter->colors * 18.0 / log_max);
495     }
496
497     if(vumeter->colors_inverted) {
498         vumeter->f_colors[0].red = 0;
499         vumeter->f_colors[0].green = vumeter->f_brightness;
500         vumeter->f_colors[0].blue = 0;
501
502         vumeter->b_colors[0].red = 0;
503         vumeter->b_colors[0].green = vumeter->b_brightness;
504         vumeter->b_colors[0].blue = 0;
505
506         /* Allocate from Green to Yellow */
507         f_step = vumeter->f_brightness / (first - 1);
508         b_step = vumeter->b_brightness / (first - 1);
509         for (index = 1; index < first; index++) {
510             /* foreground */
511             vumeter->f_colors[index].red = vumeter->f_colors[index - 1].red + f_step;
512             vumeter->f_colors[index].green = vumeter->f_brightness;
513             vumeter->f_colors[index].blue = 0;
514             /* background */
515             vumeter->b_colors[index].red = vumeter->b_colors[index - 1].red + b_step;
516             vumeter->b_colors[index].green = vumeter->b_brightness;
517             vumeter->b_colors[index].blue = 0;
518         }
519         /* Allocate from Yellow to Red */
520         if(second != first) {
521             f_step = vumeter->f_brightness / (second - first);
522             b_step = vumeter->b_brightness / (second - first);
523             for (index = first; index < second; index++) {
524                 /* foreground */
525                 vumeter->f_colors[index].red = vumeter->f_colors[index - 1].red;
526                 vumeter->f_colors[index].green = vumeter->f_colors[index - 1].green - f_step;
527                 vumeter->f_colors[index].blue = 0;
528                 /* background */
529                 vumeter->b_colors[index].red = vumeter->b_colors[index - 1].red;
530                 vumeter->b_colors[index].green = vumeter->b_colors[index - 1].green - b_step;
531                 vumeter->b_colors[index].blue = 0;     
532             }
533         }
534     } else {
535         vumeter->f_colors[0].red = vumeter->f_brightness;
536         vumeter->f_colors[0].green = 0;
537         vumeter->f_colors[0].blue = 0;
538
539         vumeter->b_colors[0].red = vumeter->b_brightness;
540         vumeter->b_colors[0].green = 0;
541         vumeter->b_colors[0].blue = 0;
542
543         /* Allocate from Red to Yellow */
544         f_step = vumeter->f_brightness / MAX(first - 1, 1);
545         b_step = vumeter->b_brightness / MAX(first - 1, 1);
546         for (index = 1; index < first; index++) {
547             /* foreground */
548             vumeter->f_colors[index].red = vumeter->f_brightness;
549             vumeter->f_colors[index].green = vumeter->f_colors[index - 1].green + f_step;
550             vumeter->f_colors[index].blue = 0;
551             /* background */
552             vumeter->b_colors[index].red = vumeter->b_brightness;
553             vumeter->b_colors[index].green = vumeter->b_colors[index - 1].green + b_step;
554             vumeter->b_colors[index].blue = 0;
555         }
556         /* Allocate from Yellow to Green */    
557         f_step = vumeter->f_brightness / MAX(second - first, 1);
558         b_step = vumeter->b_brightness / MAX(second - first, 1);        
559         for (index = first; index < second; index++) {
560             /* foreground */
561             vumeter->f_colors[index].red = vumeter->f_colors[index - 1].red - f_step;
562             vumeter->f_colors[index].green = vumeter->f_colors[index - 1].green;
563             vumeter->f_colors[index].blue = 0;
564             /* background */
565             vumeter->b_colors[index].red = vumeter->b_colors[index - 1].red - b_step;
566             vumeter->b_colors[index].green = vumeter->b_colors[index - 1].green;
567             vumeter->b_colors[index].blue = 0;     
568         }
569         if (vumeter->scaling == GTK_VUMETER_SCALING_LOG) {
570             /* Allocate from Green to Dark Green */
571             f_step = vumeter->f_brightness / 2 / (vumeter->colors - second);
572             b_step = vumeter->b_brightness / 2 / (vumeter->colors - second);     
573             for (index = second; index < vumeter->colors; index++) {
574                 /* foreground */
575                 vumeter->f_colors[index].red = 0;
576                 vumeter->f_colors[index].green = vumeter->f_colors[index - 1].green - f_step;
577                 vumeter->f_colors[index].blue = 0;
578                 /* background */
579                 vumeter->b_colors[index].red = 0;
580                 vumeter->b_colors[index].green = vumeter->b_colors[index - 1].green - b_step;
581                 vumeter->b_colors[index].blue = 0;      
582             }
583         }
584     } /* colors_inverted */
585
586     /* Allocate the Colours */
587     for (index = 0; index < vumeter->colors; index++) {
588         /* foreground */
589         gdk_colormap_alloc_color (vumeter->colormap, &vumeter->f_colors[index], FALSE, TRUE);
590         vumeter->f_gc[index] = gdk_gc_new(GTK_WIDGET(vumeter)->window);
591         gdk_gc_set_foreground(vumeter->f_gc[index], &vumeter->f_colors[index]);
592         /* background */
593         gdk_colormap_alloc_color (vumeter->colormap, &vumeter->b_colors[index], FALSE, TRUE);
594         vumeter->b_gc[index] = gdk_gc_new(GTK_WIDGET(vumeter)->window);
595         gdk_gc_set_foreground(vumeter->b_gc[index], &vumeter->b_colors[index]);        
596     }
597 }
598
599 static gint gtk_vumeter_sound_level_to_draw_level (GtkVUMeter *vumeter, gint sound_level)
600 {
601     gdouble draw_level;
602     gdouble level, min, max, height;
603     gdouble log_level, log_max;
604     
605     level = (gdouble)sound_level;
606     min = (gdouble)vumeter->level_min;
607     max = (gdouble)vumeter->level_max;
608     height = (gdouble)vumeter->colors;
609     
610     if (vumeter->scaling == GTK_VUMETER_SCALING_LINEAR) {
611         draw_level = (1.0 - (level - min)/(max - min)) * (height - 1.0);
612         /* to avoid rounding problems */
613         draw_level += 0.001;
614     } else {
615         log_level = log10((level - min + 1)/(max - min + 1));
616         log_max = log10(1/(max - min + 1));
617         draw_level = log_level/log_max * (height - 1.0);
618     }
619     
620     return ((gint)draw_level);
621 }
622
623 static gboolean gtk_vumeter_redraw_timeout (GtkVUMeter *vumeter)
624 {
625     /* Immediately return if need be */
626     if (!GTK_WIDGET_REALIZED (GTK_WIDGET(vumeter))) { return TRUE; }
627     if (vumeter->peak == FALSE) { return TRUE; }
628     if (vumeter->peak_level == vumeter->level) { return TRUE; }
629     
630     if(vumeter->peak_hold != 0) {
631         vumeter->peak_hold--;
632         return TRUE;
633     }
634
635     /* Drop the peak_level by rate */
636     vumeter->peak_level -= vumeter->peak_falloff_rate;
637     vumeter->peak_level = MAX (vumeter->peak_level, vumeter->level);
638     
639     gtk_widget_queue_draw (GTK_WIDGET(vumeter));
640     
641     return TRUE;
642 }
643
644 static GList *gtk_vumeter_clone_scale_items(GList *scale_items)
645 {
646     GList * new_list = NULL;
647
648
649     for ( ; scale_items != NULL; scale_items = g_list_next(scale_items)) {
650         GtkVUMeterScaleItem * item = scale_items->data;
651         GtkVUMeterScaleItem * new_item;
652
653         new_item = g_malloc(sizeof(GtkVUMeterScaleItem));
654         new_item->level = item->level;
655         new_item->large = item->large;
656         new_item->label = g_strdup(item->label);
657         new_list = g_list_append(new_list, new_item);
658     }
659
660     return new_list;
661 }
662
663 static void gtk_vumeter_setup_scale_items(GtkVUMeter *vumeter, GList *scale_items)
664 {
665     GList * new_list = NULL;
666     GList * new_list_item = NULL;
667
668     /* clone the whole list */
669     new_list = gtk_vumeter_clone_scale_items(scale_items);
670     
671     /* clamp the levels */
672     for (new_list_item = new_list; new_list_item != NULL; new_list_item = g_list_next(new_list_item)) {
673         GtkVUMeterScaleItem * item = new_list_item->data;
674
675         item->level = CLAMP(item->level, vumeter->level_min, vumeter->level_max);
676     }
677
678     gtk_vumeter_free_scale_items(vumeter->scale_items);
679     vumeter->scale_items = new_list;
680 }
681
682 void gtk_vumeter_free_scale_items(GList *scale_items)
683 {
684     GList * current;
685     
686     if(scale_items == NULL) return;
687
688     for (current = scale_items; current != NULL; current = g_list_next(current)) {
689         GtkVUMeterScaleItem * item = current->data;
690
691         if(item->label != NULL) {
692             g_free((void *) item->label);
693         }
694         g_free(item);
695     }
696
697     g_list_free(scale_items);
698 }
699
700 /**
701  * gtk_vumeter_set_orientation:
702  * @vumeter: the vumeter widget
703  * @orientation: the direction in which the graph is going for increasing values
704  */
705 void gtk_vumeter_set_orientation (GtkVUMeter *vumeter, GtkVUMeterOrientation orientation)
706 {
707     g_return_if_fail (GTK_IS_VUMETER (vumeter));
708
709     if(orientation == GTK_VUMETER_BOTTOM_TO_TOP || orientation == GTK_VUMETER_TOP_TO_BOTTOM) {
710         vumeter->vertical = TRUE;
711     } else {
712         vumeter->vertical = FALSE;
713     }
714
715     if(orientation == GTK_VUMETER_LEFT_TO_RIGHT || orientation == GTK_VUMETER_BOTTOM_TO_TOP) {
716         vumeter->scale_inverted = TRUE;
717     } else {
718         vumeter->scale_inverted = FALSE;
719     }
720
721     if (GTK_WIDGET_REALIZED (vumeter)) {
722         gtk_widget_queue_draw (GTK_WIDGET (vumeter));
723     }            
724 }
725
726 /**
727  * gtk_vumeter_get_orientation:
728  * @vumeter: the vumeter widget
729  * @return: the direction in which the graph is going for increasing values
730  */
731 GtkVUMeterOrientation gtk_vumeter_get_orientation (GtkVUMeter *vumeter)
732 {
733     if(!GTK_IS_VUMETER (vumeter)) {
734         return GTK_VUMETER_BOTTOM_TO_TOP;
735     }
736
737     /* XXX - might be faster using a lookup table */
738     if(vumeter->vertical == TRUE && vumeter->scale_inverted == TRUE) {
739         return GTK_VUMETER_BOTTOM_TO_TOP;
740     }
741
742     if(vumeter->vertical == TRUE && vumeter->scale_inverted == FALSE) {
743         return GTK_VUMETER_TOP_TO_BOTTOM;
744     }
745
746     if(vumeter->vertical == FALSE && vumeter->scale_inverted == TRUE) {
747         return GTK_VUMETER_LEFT_TO_RIGHT;
748     }
749
750     if(vumeter->vertical == FALSE && vumeter->scale_inverted == FALSE) {
751         return GTK_VUMETER_LEFT_TO_RIGHT;
752     }
753
754     g_assert_not_reached();
755     return GTK_VUMETER_BOTTOM_TO_TOP;
756 }
757
758 /**
759  * gtk_vumeter_set_thickness:
760  * @vumeter: the vumeter widget
761  * @thickness: gtkvumeter's minimum graph thickness in pixels (default:30)
762  *
763  * Allows the user program to change the dimension of the vumeter.
764  * For a vertical meter, this is the width.
765  * Likewise for a horizontal meter, this is the height.
766  */
767 void gtk_vumeter_set_thickness (GtkVUMeter *vumeter, gint thickness)
768 {
769     g_return_if_fail (GTK_IS_VUMETER (vumeter));
770
771     if (vumeter->thickness != thickness) {
772         vumeter->thickness = thickness;
773         vumeter->reduced_thickness = MIN(vumeter->reduced_thickness, vumeter->thickness);
774         gtk_widget_queue_resize (GTK_WIDGET (vumeter));
775     }
776 }
777
778 /**
779  * gtk_vumeter_get_thickness:
780  * @vumeter: the vumeter widget
781  * @return: gtkvumeter's minimum graph thickness in pixels (default:30)
782  *
783  * For a vertical meter, this is the width.
784  * Likewise for a horizontal meter, this is the height.
785  */
786 gint gtk_vumeter_get_thickness (GtkVUMeter *vumeter)
787 {
788     if(!GTK_IS_VUMETER (vumeter)) {
789         return 0;
790     } else {
791         return vumeter->thickness;
792     }
793 }
794
795 /**
796  * gtk_vumeter_set_thickness_reduction:
797  * @vumeter: the vumeter widget
798  * @reduced_thickness: pixels to reduce the "none active" part of the graph (default:0)
799  *
800  * Allows the user program to reduce the thickness of the "background" part of the vumeter graph.
801  * This can be useful to distinguish the border between the foreground and background graph.
802  */
803 void gtk_vumeter_set_thickness_reduction (GtkVUMeter *vumeter, gint reduced_thickness)
804 {
805     g_return_if_fail (GTK_IS_VUMETER (vumeter));
806
807     if (vumeter->reduced_thickness != reduced_thickness) {
808         vumeter->reduced_thickness = reduced_thickness;
809         vumeter->reduced_thickness = CLAMP(vumeter->reduced_thickness, 0, vumeter->thickness/2);
810         gtk_widget_queue_resize (GTK_WIDGET (vumeter));
811     }      
812 }
813
814 /**
815  * gtk_vumeter_get_thickness_reduction:
816  * @vumeter: the vumeter widget
817  * @return: pixels to reduce the "none active" part of the graph (default:0)
818  *
819  * The reduced thickness of the "background" part of the vumeter graph.
820  */
821 gint gtk_vumeter_get_thickness_reduction (GtkVUMeter *vumeter)
822 {
823     if(!GTK_IS_VUMETER (vumeter)) {
824         return 0;
825     } else {
826         return vumeter->reduced_thickness;
827     }
828 }
829
830 /**
831  * gtk_vumeter_set_min_max:
832  * @vumeter: the vumeter widget
833  * @min: the new minimum level shown (default: 0)
834  * @max: the new maximum level shown (default: 32768)
835  *
836  * Sets the minimum and maximum of the VU Meters scale.
837  * It will increment max by one if min == max.
838  * And finally it will clamp the relevant levels into the min, max range.
839  * Either value can be NULL, to keep the current value.
840  *
841  * Don't forget to call %gtk_vumeter_set_yellow_level() if required!
842  *
843  * WARNING: negative values for min or max will currently not work!!!
844  */
845 void gtk_vumeter_set_min_max (GtkVUMeter *vumeter, gint *min, gint *max)
846 {
847     gint mi, ma;
848
849     g_return_if_fail (GTK_IS_VUMETER (vumeter));
850
851     /* Allow min or max to be NULL */
852     mi = (min != NULL) ? *min : vumeter->level_min;
853     ma = (max != NULL) ? *max : vumeter->level_max;    
854
855     /* Ensure that max > min */
856     vumeter->level_max = MAX(ma, mi);
857     vumeter->level_min = MIN(mi, ma);
858     if (vumeter->level_max == vumeter->level_min) {
859         /* Increment max so we have a range */
860             vumeter->level_max++;
861     }
862     /* Clamp the levels to the new range */
863     vumeter->level = CLAMP (vumeter->level, vumeter->level_min, vumeter->level_max);
864     vumeter->peak_level = CLAMP (vumeter->peak_level, vumeter->level, vumeter->level_max);
865     vumeter->yellow_level = CLAMP (vumeter->yellow_level, vumeter->level_min, vumeter->level_max);
866     
867     gtk_widget_queue_draw (GTK_WIDGET(vumeter));
868 }
869
870 /**
871  * gtk_vumeter_get_min_max:
872  * @vumeter: the vumeter widget
873  * @min: the new minimum level shown (default: 0)
874  * @max: the new maximum level shown (default: 32768)
875  *
876  * The minimum and maximum of the VU Meters scale.
877  */
878 void gtk_vumeter_get_min_max (GtkVUMeter *vumeter, gint *min, gint *max)
879 {
880     if(!GTK_IS_VUMETER (vumeter)) {
881         *min = 0;
882         *max = 0;
883     } else {
884         *min = vumeter->level_min;
885         *max = vumeter->level_max;
886     }
887 }
888
889 /**
890  * gtk_vumeter_set_level:
891  * @vumeter: the vumeter widget
892  * @level: the new level shown (default: 0)
893  *
894  * Sets new level value for the vumeter.
895  * The level is clamped to the min max range.
896  * The peak_level will be increased to level if needed.
897  */
898 void gtk_vumeter_set_level (GtkVUMeter *vumeter, gint level)
899 {
900     g_return_if_fail (GTK_IS_VUMETER (vumeter));
901
902     if (vumeter->level != level) {
903         vumeter->level = CLAMP (level, vumeter->level_min, vumeter->level_max);
904         if(vumeter->level > vumeter->peak_level) {
905             vumeter->peak_hold = vumeter->peak_hold_factor;
906             vumeter->peak_level = vumeter->level;
907         }
908         gtk_widget_queue_draw (GTK_WIDGET(vumeter));
909     }        
910 }
911
912 /**
913  * gtk_vumeter_get_level:
914  * @vumeter: the vumeter widget
915  * @return: the level shown (default: 0)
916  *
917  * Gets the level value of the vumeter.
918  */
919 gint gtk_vumeter_get_level (GtkVUMeter *vumeter)
920 {
921     if(!GTK_IS_VUMETER (vumeter)) {
922         return 0;
923     } else {
924         return vumeter->level;
925     }
926 }
927
928 /**
929  * gtk_vumeter_set_scaling:
930  * @vumeter: the vumeter widget
931  * @scale: the scaling mode either GTK_VUMETER_SCALING_LINEAR or GTK_VUMETER_SCALING_LOG
932  *
933  * Sets the scaling mode of the VU Meter.
934  * It is either log or linear and defaults to linear.
935  * No matter which scale you set the input should always be linear, gtkVUMeter
936  * does the log calculation/display.
937  */
938 void gtk_vumeter_set_scaling (GtkVUMeter *vumeter, GtkVUMeterScaling scaling)
939 {
940     g_return_if_fail (GTK_IS_VUMETER (vumeter));  
941
942     if (scaling != vumeter->scaling) {
943         vumeter->scaling = CLAMP (scaling, GTK_VUMETER_SCALING_LINEAR, GTK_VUMETER_SCALING_LOG);
944         if (GTK_WIDGET_REALIZED (vumeter)) {
945             gtk_vumeter_setup_colors (vumeter);
946             gtk_widget_queue_draw (GTK_WIDGET (vumeter));
947         }            
948     }
949 }
950
951 /**
952  * gtk_vumeter_get_scaling:
953  * @vumeter: the vumeter widget
954  * @return: the scaling mode either GTK_VUMETER_SCALING_LINEAR or GTK_VUMETER_SCALING_LOG
955  *
956  * Gets the scaling mode of the VU Meter.
957  * It is either log or linear and defaults to linear.
958  */
959 GtkVUMeterScaling gtk_vumeter_get_scaling (GtkVUMeter *vumeter)
960 {
961     if(!GTK_IS_VUMETER (vumeter)) {
962         return 0;
963     } else {
964         return vumeter->scaling;
965     }
966 }
967
968 /**
969  * gtk_vumeter_set_scale_items:
970  * @vumeter: the vumeter widget
971  * @scale_items: a GList of the pitch lines and labels (default:NULL)
972  *
973  * Set the scale pitch lines and labels.
974  * Must be NULL or a GList containing filled %GtkVUMeterScaleItem items.
975  * Function will make a deep copy of the GList and it's items,
976  * so the given GList and it's items can be safely thrown away after the call.
977  * A side effect: This also sets the minimum size of the widget.
978  */
979 void gtk_vumeter_set_scale_items(GtkVUMeter *vumeter, GList *scale_items)
980 {
981     g_return_if_fail (GTK_IS_VUMETER (vumeter));  
982
983     gtk_vumeter_setup_scale_items(vumeter, scale_items);
984
985     if (GTK_WIDGET_REALIZED (vumeter)) {
986         gtk_widget_queue_draw (GTK_WIDGET (vumeter));
987     }
988 }
989
990 /**
991  * gtk_vumeter_get_scale_items:
992  * @vumeter: the vumeter widget
993  * @return: a GList of the pitch lines and labels (default:NULL)
994  *
995  * Get the scale pitch lines and labels, a GList containing %GtkVUMeterScaleItem items.
996  * The returned GList must be freed with gtk_vumeter_free_scale_items() by the user!!!
997  */
998 GList *gtk_vumeter_get_scale_items(GtkVUMeter *vumeter)
999 {
1000     if(!GTK_IS_VUMETER (vumeter)) {
1001         return NULL;
1002     } else {
1003         return gtk_vumeter_clone_scale_items(vumeter->scale_items);
1004     }
1005 }
1006
1007 /**
1008  * gtk_vumeter_set_scale_hole_size:
1009  * @vumeter: the vumeter widget
1010  * @hole_size:  (default:0)
1011  *
1012  * Set the size of the "holes" between the pitch lines.
1013  * A side effect: This also sets the minimum size of the widget.
1014  */
1015 void gtk_vumeter_set_scale_hole_size (GtkVUMeter *vumeter, gint hole_size)
1016 {
1017     g_return_if_fail (GTK_IS_VUMETER (vumeter));
1018
1019     if (vumeter->scale_pitch_holes != hole_size) {
1020         vumeter->scale_pitch_holes = hole_size;
1021         gtk_widget_queue_resize (GTK_WIDGET (vumeter));
1022     }      
1023 }
1024
1025 /**
1026  * gtk_vumeter_get_scale_hole_size:
1027  * @vumeter: the vumeter widget
1028  * @return:  (default:0)
1029  *
1030  * Get the size of the "holes" between the pitch lines.
1031  */
1032 gint gtk_vumeter_get_scale_hole_size (GtkVUMeter *vumeter)
1033 {
1034     if(!GTK_IS_VUMETER (vumeter)) {
1035         return 0;
1036     } else {
1037         return vumeter->scale_pitch_holes;
1038     }
1039 }
1040
1041 /**
1042  * gtk_vumeter_set_peak:
1043  * @vumeter: the vumeter widget
1044  * @peak: whether or not the peak indicator is drawn
1045  * @redraw_rate: the rate (in milliseconds) at which the peak indicator is redrawn
1046  *
1047  * Enables/Disables the peak meachanism and sets the redraw timeout to redraw_rate milliseconds.
1048  * The redraw operation is intelligent in that the widget is only redrawn
1049  * if the peak_level != level and peak == %TRUE.
1050  *
1051  * Hint: A good redraw_rate is 200ms (default: 0ms -> off)
1052  */
1053 void gtk_vumeter_set_peak (GtkVUMeter *vumeter, gboolean peak, guint redraw_rate)
1054 {
1055     g_return_if_fail (GTK_IS_VUMETER (vumeter));
1056
1057     if (vumeter->peak != peak) {
1058         vumeter->peak = peak;
1059         gtk_widget_queue_draw (GTK_WIDGET (vumeter));
1060     }
1061
1062     vumeter->peak_redraw_rate = redraw_rate;
1063
1064     if(vumeter->peak_timeout) {
1065         gtk_timeout_remove(vumeter->peak_timeout);
1066     }
1067
1068     if(redraw_rate != 0 && vumeter->peak) {
1069         vumeter->peak_timeout = gtk_timeout_add (redraw_rate, (GtkFunction)gtk_vumeter_redraw_timeout, vumeter);
1070     }    
1071 }
1072
1073 /**
1074  * gtk_vumeter_get_peak:
1075  * @vumeter: the vumeter widget
1076  * @peak: whether or not the peak indicator is drawn
1077  * @redraw_rate: the rate (in milliseconds) at which the peak indicator is redrawn
1078  *
1079  */
1080 void gtk_vumeter_get_peak (GtkVUMeter *vumeter, gboolean *peak, guint *redraw_rate)
1081 {
1082     if(!GTK_IS_VUMETER (vumeter)) {
1083         *peak = 0;
1084         *redraw_rate = 0;
1085     } else {
1086         *peak = vumeter->peak;
1087         *redraw_rate = vumeter->peak_redraw_rate;
1088     }
1089 }
1090
1091 /**
1092  * gtk_vumeter_set_peak_hold_factor:
1093  * @vumeter: the vumeter widget
1094  * @hold_factor: number of redraw_rates to wait until peak indicator is decayed (default:0 -> off)
1095  *
1096  * Holds the peak indicator for a limited time at it's highest position.
1097  * The actual rate is dependent on the redraw_rate given to %gtk_vumeter_set_peak().
1098  *
1099  * Hint: For a VU meter, a good hold_factor is 7 with a redraw_rate of 200ms.
1100  */
1101 void gtk_vumeter_set_peak_hold_factor (GtkVUMeter *vumeter, gint hold_factor)
1102 {
1103     g_return_if_fail (GTK_IS_VUMETER (vumeter));
1104
1105     if (vumeter->peak_hold_factor != hold_factor) {
1106         vumeter->peak_hold_factor = hold_factor;
1107     }
1108 }
1109
1110 /**
1111  * gtk_vumeter_get_peak_hold_factor:
1112  * @vumeter: the vumeter widget
1113  * @return: number of redraw_rates to wait until peak indicator is decayed (default:0 -> off)
1114  */
1115 gint gtk_vumeter_get_peak_hold_factor (GtkVUMeter *vumeter)
1116 {
1117     if(!GTK_IS_VUMETER (vumeter)) {
1118         return 0;
1119     } else {
1120         return vumeter->peak_hold_factor;
1121     }
1122 }
1123
1124 /**
1125  * gtk_vumeter_set_peak_falloff:
1126  * @vumeter: the vumeter widget
1127  * @peak_falloff: controls the speed to the peak decay
1128  * @user_rate: pixels to reduce the peak level at each redraw_rate in GTK_VUMETER_PEAK_FALLOFF_USER mode, otherwise ignored
1129  *
1130  * Set the numbers of pixel reduced from the peak indicator each redraw_rate (after the hold period is over).
1131  * The peak_falloff will be around: SLOW:5%, MEDIUM:10%, FAST:20%, USER:user_rate 
1132  * of the current range, reduced from peak at each redraw_rate (%gtk_vumeter_set_peak()).
1133  * 
1134  * Hint: a user_rate of 0 can be used to hold the peak indicator at the highest position ever.
1135  */
1136 void gtk_vumeter_set_peak_falloff (GtkVUMeter *vumeter, GtkVUMeterPeakFalloff peak_falloff, guint user_rate)
1137 {
1138     gint range;
1139     g_return_if_fail (GTK_IS_VUMETER (vumeter));
1140
1141     vumeter->peak_falloff_mode = CLAMP(peak_falloff, GTK_VUMETER_PEAK_FALLOFF_SLOW, GTK_VUMETER_PEAK_FALLOFF_USER);
1142     range = vumeter->level_max - vumeter->level_min;
1143
1144     switch (peak_falloff) {
1145         case GTK_VUMETER_PEAK_FALLOFF_SLOW:
1146             vumeter->peak_falloff_rate = range/20;
1147             break;
1148         default:
1149         case GTK_VUMETER_PEAK_FALLOFF_MEDIUM:
1150             vumeter->peak_falloff_rate = range/10;
1151             break;
1152         case GTK_VUMETER_PEAK_FALLOFF_FAST:
1153             vumeter->peak_falloff_rate = range/5;
1154             break;
1155         case GTK_VUMETER_PEAK_FALLOFF_USER:
1156             vumeter->peak_falloff_rate = (gint)user_rate;
1157             break;
1158     }
1159 }
1160
1161 /**
1162  * gtk_vumeter_get_peak_falloff:
1163  * @vumeter: the vumeter widget
1164  * @peak_falloff: controls the speed to the peak decay
1165  * @user_rate: pixels to lower the peak level each redraw_rate (value valid in every peak_falloff mode)
1166  */
1167 void gtk_vumeter_get_peak_falloff (GtkVUMeter *vumeter, GtkVUMeterPeakFalloff *peak_falloff, guint *user_rate)
1168 {
1169     if(!GTK_IS_VUMETER (vumeter)) {
1170         *peak_falloff = 0;
1171         *user_rate = 0;
1172     } else {
1173         *peak_falloff = vumeter->peak_falloff_mode;
1174         *user_rate = vumeter->peak_falloff_rate;
1175     }
1176 }
1177
1178 /**
1179  * gtk_vumeter_set_colors_inverted:
1180  * @vumeter: the vumeter widget
1181  * @inverted: whether or not the colors are inverted (default:%FALSE)
1182  *
1183  * Usually the graph will be colored with: 0:green, half:yellow, full:red.
1184  * This is used to display signals that won't "work correct" above a maximum level
1185  * (e.g. audio signals may distort if their amplitude is too high).
1186  *
1187  * The inverted colors will be: 0:red, half:yellow, full:green.
1188  * This is used to display signals that need a minimum level to work correct
1189  * (e.g. a received antenna signal must have a minimum amplitude to "work correct").
1190  */
1191 void gtk_vumeter_set_colors_inverted (GtkVUMeter *vumeter, gboolean inverted)
1192 {
1193     g_return_if_fail (GTK_IS_VUMETER (vumeter));  
1194
1195     vumeter->colors_inverted = inverted;
1196     if (GTK_WIDGET_REALIZED (vumeter)) {
1197         gtk_vumeter_setup_colors (vumeter);
1198         gtk_widget_queue_draw (GTK_WIDGET (vumeter));
1199     }            
1200 }
1201
1202 /**
1203  * gtk_vumeter_get_colors_inverted:
1204  * @vumeter: the vumeter widget
1205  * @return: whether or not the colors are inverted (default:%FALSE)
1206  */
1207 gboolean gtk_vumeter_get_colors_inverted (GtkVUMeter *vumeter)
1208 {
1209     if(!GTK_IS_VUMETER (vumeter)) {
1210         return 0;
1211     } else {
1212         return vumeter->colors_inverted;
1213     }
1214 }
1215
1216 /**
1217  * gtk_vumeter_set_yellow_level:
1218  * @vumeter: the vumeter widget
1219  * @yellow_level: set the position of the yellow area (default:16383)
1220  *
1221  * Will be clamped between min and max.
1222  */
1223 void gtk_vumeter_set_yellow_level (GtkVUMeter *vumeter, gint yellow_level)
1224 {
1225     g_return_if_fail (GTK_IS_VUMETER (vumeter));  
1226
1227     vumeter->yellow_level = CLAMP (yellow_level, vumeter->level_min, vumeter->level_max);
1228     if (GTK_WIDGET_REALIZED (vumeter)) {
1229         gtk_vumeter_setup_colors (vumeter);
1230         gtk_widget_queue_draw (GTK_WIDGET (vumeter));
1231     }            
1232 }
1233
1234 /**
1235  * gtk_vumeter_get_yellow_level:
1236  * @vumeter: the vumeter widget
1237  * @return: get the position of the yellow area (default:16383)
1238  */
1239 gint gtk_vumeter_get_yellow_level (GtkVUMeter *vumeter)
1240 {
1241     if(!GTK_IS_VUMETER (vumeter)) {
1242         return 0;
1243     } else {
1244         return vumeter->yellow_level;
1245     }
1246 }
1247
1248 /**
1249  * gtk_vumeter_set_brightness:
1250  * @vumeter: the vumeter widget
1251  * @foreground: set the brightness of the graphs foreground (default:65535)
1252  * @background: set the brightness of the graphs background (default:49151)
1253  *
1254  * Hint: don't turn the brightness too low, otherwise you'll only see a black bar :-)
1255  */
1256 void gtk_vumeter_set_brightness (GtkVUMeter *vumeter, gint foreground, gint background)
1257 {
1258     vumeter->f_brightness = CLAMP(foreground, 0, 65535);
1259     vumeter->b_brightness = CLAMP(background, 0, vumeter->f_brightness);
1260     if (GTK_WIDGET_REALIZED (vumeter)) {
1261         gtk_vumeter_setup_colors (vumeter);
1262         gtk_widget_queue_draw (GTK_WIDGET (vumeter));
1263     }            
1264 }
1265
1266 /**
1267  * gtk_vumeter_get_brightness:
1268  * @vumeter: the vumeter widget
1269  * @foreground: get the brightness of the graphs foreground (default:65535)
1270  * @background: get the brightness of the graphs background (default:49151)
1271  */
1272 void gtk_vumeter_get_brightness (GtkVUMeter *vumeter, gint *foreground, gint *background)
1273 {
1274     if(!GTK_IS_VUMETER (vumeter)) {
1275         *foreground = 0;
1276         *background = 0;
1277     } else {
1278         *foreground = vumeter->f_brightness;
1279         *background = vumeter->b_brightness;
1280     }
1281 }
1282