/*************************************************************************** * gtkvumeter.c * * Fri Jan 10 20:06:23 2003 * Copyright 2003 Todd Goyen * wettoad@knighthoodofbuh.org ****************************************************************************/ #include #include #include "gtkvumeter.h" #define MIN_HORIZONTAL_VUMETER_WIDTH 40 #define HORIZONTAL_VUMETER_HEIGHT 20 #define VERTICAL_VUMETER_WIDTH 20 #define MIN_VERTICAL_VUMETER_HEIGHT 40 static void gtk_vumeter_init (GtkVUMeter *vumeter); static void gtk_vumeter_class_init (GtkVUMeterClass *class); static void gtk_vumeter_destroy (GtkObject *object); static void gtk_vumeter_realize (GtkWidget *widget); static void gtk_vumeter_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_vumeter_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gint gtk_vumeter_expose (GtkWidget *widget, GdkEventExpose *event); static void gtk_vumeter_free_colors (GtkVUMeter *vumeter); static void gtk_vumeter_setup_colors (GtkVUMeter *vumeter); static gint gtk_vumeter_sound_level_to_draw_level (GtkVUMeter *vumeter); static GdkColor default_f_gradient_keys[3] = {{0,65535,0,0},{0,65535,65535,0},{0,0,65535,0}}; static GdkColor default_b_gradient_keys[3] = {{0,49151,0,0},{0,49151,49151,0},{0,0,49151,0}}; static GtkWidgetClass *parent_class = NULL; GtkType gtk_vumeter_get_type (void) { static GType vumeter_type = 0; if (!vumeter_type) { static const GTypeInfo vumeter_info = { sizeof (GtkVUMeterClass), NULL, NULL, (GClassInitFunc) gtk_vumeter_class_init, NULL, NULL, sizeof (GtkVUMeter), 0, (GInstanceInitFunc) gtk_vumeter_init, }; vumeter_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkVUMeter", &vumeter_info, 0); } return vumeter_type; } GtkWidget* gtk_vumeter_new (gboolean vertical) { GtkVUMeter *vumeter; vumeter = gtk_type_new (GTK_TYPE_VUMETER); vumeter->vertical = vertical; return GTK_WIDGET (vumeter); } static void gtk_vumeter_init (GtkVUMeter *vumeter) { vumeter->colormap = NULL; vumeter->colors = 0; vumeter->f_gc = NULL; vumeter->b_gc = NULL; vumeter->f_colors = NULL; vumeter->b_colors = NULL; vumeter->level = 0; vumeter->min = 0; vumeter->max = 32767; vumeter->peaks_falloff = GTK_VUMETER_PEAKS_FALLOFF_MEDIUM; vumeter->peak_level = 0; vumeter->scale = GTK_VUMETER_SCALE_LINEAR; //XXX A bit heavy... gtk_vumeter_set_gradient(vumeter, 3, default_f_gradient_keys, 3, default_b_gradient_keys); } static void gtk_vumeter_class_init (GtkVUMeterClass *class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; object_class = (GtkObjectClass*) class; widget_class = (GtkWidgetClass*) class; parent_class = gtk_type_class(gtk_widget_get_type()); object_class->destroy = gtk_vumeter_destroy; widget_class->realize = gtk_vumeter_realize; widget_class->expose_event = gtk_vumeter_expose; widget_class->size_request = gtk_vumeter_size_request; widget_class->size_allocate = gtk_vumeter_size_allocate; } static void gtk_vumeter_destroy (GtkObject *object) { GtkVUMeter *vumeter = GTK_VUMETER (object); gtk_vumeter_free_colors (vumeter); GTK_OBJECT_CLASS (parent_class)->destroy (object); } static void gtk_vumeter_realize (GtkWidget *widget) { GtkVUMeter *vumeter; GdkWindowAttr attributes; gint attributes_mask; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_VUMETER (widget)); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); vumeter = GTK_VUMETER (widget); attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); widget->style = gtk_style_attach (widget->style, widget->window); gdk_window_set_user_data (widget->window, widget); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); /* colors */ vumeter->colormap = gdk_colormap_get_system (); gtk_vumeter_setup_colors (vumeter); } static void gtk_vumeter_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkVUMeter *vumeter; g_return_if_fail (GTK_IS_VUMETER (widget)); g_return_if_fail (requisition != NULL); vumeter = GTK_VUMETER (widget); if (vumeter->vertical == TRUE) { requisition->width = VERTICAL_VUMETER_WIDTH; requisition->height = MIN_VERTICAL_VUMETER_HEIGHT; } else { requisition->width = MIN_HORIZONTAL_VUMETER_WIDTH; requisition->height = HORIZONTAL_VUMETER_HEIGHT; } } static void gtk_vumeter_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkVUMeter *vumeter; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_VUMETER (widget)); g_return_if_fail (allocation != NULL); widget->allocation = *allocation; vumeter = GTK_VUMETER (widget); if (GTK_WIDGET_REALIZED (widget)) { if (vumeter->vertical == TRUE) { /* veritcal */ gdk_window_move_resize (widget->window, allocation->x, allocation->y, VERTICAL_VUMETER_WIDTH, MAX(allocation->height, MIN_VERTICAL_VUMETER_HEIGHT)); } else { /* horizontal */ gdk_window_move_resize (widget->window, allocation->x, allocation->y, MAX(allocation->width, MIN_HORIZONTAL_VUMETER_WIDTH), HORIZONTAL_VUMETER_HEIGHT); } /* Fix the colours */ gtk_vumeter_setup_colors (vumeter); } } static gint gtk_vumeter_expose (GtkWidget *widget, GdkEventExpose *event) { GtkVUMeter *vumeter; gint index, level; gint width, height; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_VUMETER (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (event->count > 0) return FALSE; vumeter = GTK_VUMETER (widget); level = gtk_vumeter_sound_level_to_draw_level (vumeter); if (vumeter->vertical == TRUE) { width = widget->allocation.width - 2; height = widget->allocation.height; /* draw border */ gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, "trough", 0, 0, widget->allocation.width, height); /* draw background gradient */ for (index = 0; index < level; index++) { gdk_draw_line (widget->window, vumeter->b_gc[index], 1, index + 1, width, index + 1); } /* draw foreground gradient */ for (index = level; index < height - 2; index++) { gdk_draw_line (widget->window, vumeter->f_gc[index], 1, index + 1, width, index + 1); } } else { /* Horizontal */ width = widget->allocation.width; height = widget->allocation.height - 2; /* draw border */ gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, "trough", 0, 0, width, widget->allocation.height); /* draw background gradient */ for (index = 0; index < level; index++) { gdk_draw_line (widget->window, vumeter->b_gc[index], index + 1, 1, index + 1, height); } /* draw foreground gradient */ for (index = level; index < height - 2; index++) { gdk_draw_line (widget->window, vumeter->f_gc[index], index + 1, 1, index + 1, height); } } return FALSE; } static void gtk_vumeter_free_colors (GtkVUMeter *vumeter) { gint index; /* Free old gc's */ if (vumeter->f_gc && vumeter->b_gc) { for (index = 0; index < vumeter->colors; index++) { if (vumeter->f_gc[index]) { g_object_unref (G_OBJECT(vumeter->f_gc[index])); } if (vumeter->b_gc[index]) { g_object_unref (G_OBJECT(vumeter->b_gc[index])); } } g_free(vumeter->f_gc); g_free(vumeter->b_gc); vumeter->f_gc = NULL; vumeter->b_gc = NULL; } /* Free old Colors */ if (vumeter->f_colors) { gdk_colormap_free_colors (vumeter->colormap, vumeter->f_colors, vumeter->colors); g_free (vumeter->f_colors); vumeter->f_colors = NULL; } if (vumeter->b_colors) { gdk_colormap_free_colors (vumeter->colormap, vumeter->b_colors, vumeter->colors); g_free (vumeter->b_colors); vumeter->b_colors = NULL; } } static void gtk_vumeter_setup_colors (GtkVUMeter *vumeter) { gint index; gint max; gint f_key_len, b_key_len; gint f_key_index, b_key_index; gdouble f_key_pos, b_key_pos; GdkColor *fgk, *bgk; g_return_if_fail (vumeter->colormap != NULL); gtk_vumeter_free_colors (vumeter); /* Set new size */ if (vumeter->vertical == TRUE) { vumeter->colors = MAX(GTK_WIDGET(vumeter)->allocation.height - 2, 0); } else { vumeter->colors = MAX(GTK_WIDGET(vumeter)->allocation.width - 2, 0); } /* allocate new memory */ vumeter->f_colors = g_malloc (vumeter->colors * sizeof(GdkColor)); vumeter->b_colors = g_malloc (vumeter->colors * sizeof(GdkColor)); vumeter->f_gc = g_malloc (vumeter->colors * sizeof(GdkGC *)); vumeter->b_gc = g_malloc (vumeter->colors * sizeof(GdkGC *)); /* Initialize stuff */ max=vumeter->colors; f_key_len = max / (vumeter->f_gradient_key_count-1) + 1; b_key_len = max / (vumeter->b_gradient_key_count-1) + 1; fgk=vumeter->f_gradient_keys; bgk=vumeter->b_gradient_keys; for (index = 0; index < max; index++) { f_key_index=index/f_key_len; f_key_pos=((gdouble) (index%f_key_len)/f_key_len); b_key_index=index/f_key_len; b_key_pos=((gdouble) (index%b_key_len)/b_key_len); /* Generate the Colours */ /* foreground */ vumeter->f_colors[index].red = fgk[f_key_index].red*(1.0-f_key_pos) + fgk[f_key_index+1].red*f_key_pos; vumeter->f_colors[index].green = fgk[f_key_index].green*(1.0-f_key_pos) + fgk[f_key_index+1].green*f_key_pos; vumeter->f_colors[index].blue = fgk[f_key_index].blue*(1.0-f_key_pos) + fgk[f_key_index+1].blue*f_key_pos; /* background */ vumeter->b_colors[index].red = bgk[b_key_index].red*(1.0-b_key_pos) + bgk[b_key_index+1].red*b_key_pos; vumeter->b_colors[index].green = bgk[b_key_index].green*(1.0-b_key_pos) + bgk[b_key_index+1].green*b_key_pos; vumeter->b_colors[index].blue = bgk[b_key_index].blue*(1.0-b_key_pos) + bgk[b_key_index+1].blue*b_key_pos; /* Allocate the Colours */ /* foreground */ //TODO : try to use gdk_colormap_alloc_colors or suppress f_colors array gdk_colormap_alloc_color (vumeter->colormap, &vumeter->f_colors[index], FALSE, TRUE); vumeter->f_gc[index] = gdk_gc_new(GTK_WIDGET(vumeter)->window); gdk_gc_set_foreground(vumeter->f_gc[index], &vumeter->f_colors[index]); /* background */ gdk_colormap_alloc_color (vumeter->colormap, &vumeter->b_colors[index], FALSE, TRUE); vumeter->b_gc[index] = gdk_gc_new(GTK_WIDGET(vumeter)->window); gdk_gc_set_foreground(vumeter->b_gc[index], &vumeter->b_colors[index]); } } static gint gtk_vumeter_sound_level_to_draw_level (GtkVUMeter *vumeter) { gdouble draw_level; gdouble level, min, max, height; gdouble log_level, log_max; level = (gdouble)vumeter->level; min = (gdouble)vumeter->min; max = (gdouble)vumeter->max; height = (gdouble)vumeter->colors; if (vumeter->scale == GTK_VUMETER_SCALE_LINEAR) { draw_level = (1.0 - (level - min)/(max - min)) * height; } else { log_level = log10((level - min + 1)/(max - min + 1)); log_max = log10(1/(max - min + 1)); draw_level = log_level/log_max * height; } return ((gint)draw_level); } void gtk_vumeter_set_min_max (GtkVUMeter *vumeter, gint min, gint max) { g_return_if_fail (vumeter != NULL); g_return_if_fail (GTK_IS_VUMETER (vumeter)); vumeter->max = MAX(max, min); vumeter->min = MIN(min, max); if (vumeter->max == vumeter->min) { vumeter->max++; } vumeter->level = CLAMP (vumeter->level, vumeter->min, vumeter->max); gtk_widget_queue_draw (GTK_WIDGET(vumeter)); } void gtk_vumeter_set_level (GtkVUMeter *vumeter, gint level) { g_return_if_fail (vumeter != NULL); g_return_if_fail (GTK_IS_VUMETER (vumeter)); vumeter->level = CLAMP (level, vumeter->min, vumeter->max); gtk_widget_queue_draw (GTK_WIDGET(vumeter)); } void gtk_vumeter_set_peaks_falloff (GtkVUMeter *vumeter, gint peaks_falloff) { g_return_if_fail (vumeter != NULL); g_return_if_fail (GTK_IS_VUMETER (vumeter)); } void gtk_vumeter_set_scale (GtkVUMeter *vumeter, gint scale) { g_return_if_fail (vumeter != NULL); g_return_if_fail (GTK_IS_VUMETER (vumeter)); if (scale != vumeter->scale) { vumeter->scale = CLAMP(scale, GTK_VUMETER_SCALE_LINEAR, GTK_VUMETER_SCALE_LAST - 1); if (GTK_WIDGET_REALIZED(vumeter)) { gtk_vumeter_setup_colors (vumeter); gtk_widget_queue_draw (GTK_WIDGET(vumeter)); } } } void gtk_vumeter_set_gradient (GtkVUMeter *vumeter, gint f_gradient_key_count, GdkColor *f_gradient_keys, gint b_gradient_key_count, GdkColor *b_gradient_keys) { //XXX : memdup is a bad idea ? GdkColor *fgk = g_memdup(f_gradient_keys, f_gradient_key_count*sizeof(GdkColor)); GdkColor *bgk = g_memdup(b_gradient_keys, b_gradient_key_count*sizeof(GdkColor)); g_return_if_fail (fgk != NULL); g_return_if_fail (bgk != NULL); vumeter->f_gradient_keys = fgk; vumeter->f_gradient_key_count=f_gradient_key_count; vumeter->b_gradient_keys = bgk; vumeter->b_gradient_key_count=b_gradient_key_count; }