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