Logo Search packages:      
Sourcecode: jackeq version File versions  Download package

gtkmeter1.c

/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include <math.h>
#include <stdio.h>
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>

#include "gtkmeter.h"

#define METER_DEFAULT_WIDTH 10
#define METER_DEFAULT_LENGTH 100
#define METERSCALE_MAX_FONT_SIZE 8

/* Forward declarations */

static void gtk_meter_class_init               (GtkMeterClass    *klass);
static void gtk_meter_init                     (GtkMeter         *meter);
static void gtk_meter_destroy                  (GtkObject        *object);
static void gtk_meter_realize                  (GtkWidget        *widget);
static void gtk_meter_size_request             (GtkWidget      *widget,
                                      GtkRequisition *requisition);
static void gtk_meter_size_allocate            (GtkWidget     *widget,
                                      GtkAllocation *allocation);
static gint gtk_meter_expose                (GtkWidget        *widget,
                                    GdkEventExpose   *event);
static void meterscale_draw_notch_label(GtkMeter *meterscale, float db,
            int mark, PangoRectangle *last_label_rect);
static void meterscale_draw_notch(GtkMeter *meterscale, float db, int mark);                                
static void gtk_meter_update                   (GtkMeter *meter);
static void gtk_meter_adjustment_changed       (GtkAdjustment    *adjustment,
                                    gpointer          data);
static void gtk_meter_adjustment_value_changed (GtkAdjustment    *adjustment,
                                    gpointer          data);

static float iec_scale(float db);

/* Local data */

static GtkWidgetClass *parent_class = NULL;

guint
gtk_meter_get_type ()
{
  static guint meter_type = 0;

  if (!meter_type)
    {
      GtkTypeInfo meter_info =
      {
      "GtkMeter",
      sizeof (GtkMeter),
      sizeof (GtkMeterClass),
      (GtkClassInitFunc) gtk_meter_class_init,
      (GtkObjectInitFunc) gtk_meter_init,
      /*(GtkArgSetFunc)*/ NULL,
      /*(GtkArgGetFunc)*/ NULL,
      };

      meter_type = gtk_type_unique (gtk_widget_get_type (), &meter_info);
    }

  return meter_type;
}

static void
gtk_meter_class_init (GtkMeterClass *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_meter_destroy;

  widget_class->realize = gtk_meter_realize;
  widget_class->expose_event = gtk_meter_expose;
  widget_class->size_request = gtk_meter_size_request;
  widget_class->size_allocate = gtk_meter_size_allocate;
}

static void
gtk_meter_init (GtkMeter *meter)
{
  meter->button = 0;
  meter->direction = GTK_METER_UP;
  meter->sides = GTK_METERSCALE_LEFT;
  meter->timer = 0;
  meter->amber_level = -6.0f;
  meter->amber_frac = 0.0f;
  meter->iec_lower = 0.0f;
  meter->iec_upper = 0.0f;
  meter->min_width = -1;
  meter->min_height = -1;
  meter->old_value = 0.0;
  meter->old_lower = 0.0;
  meter->old_upper = 0.0;
  meter->adjustment = NULL;
  
}

GtkWidget*
gtk_meter_new (GtkAdjustment *adjustment, gint direction, gint sides, gfloat min, gfloat max)
{
  GtkMeter *meter;

  meter = gtk_type_new (gtk_meter_get_type ());

  if (!adjustment)
    adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);

  gtk_meter_set_adjustment (meter, adjustment);
  meter->direction = direction;
  meter->sides = sides;
  meter->lower = min;
  meter->upper = max;
  meter->iec_lower = iec_scale(min);
  meter->iec_upper = iec_scale(max);
  
  gtk_object_ref(GTK_OBJECT(meter));

  return GTK_WIDGET (meter);
}

static void
gtk_meter_destroy (GtkObject *object)
{
  GtkMeter *meter;

  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_METER (object));

  meter = GTK_METER (object);

  if (meter->adjustment)
    gtk_object_unref (GTK_OBJECT (meter->adjustment));

  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

GtkAdjustment*
gtk_meter_get_adjustment (GtkMeter *meter)
{
  g_return_val_if_fail (meter != NULL, NULL);
  g_return_val_if_fail (GTK_IS_METER (meter), NULL);

  return meter->adjustment;
}

void
gtk_meter_set_adjustment (GtkMeter      *meter,
                    GtkAdjustment *adjustment)
{
  g_return_if_fail (meter != NULL);
  g_return_if_fail (GTK_IS_METER (meter));

  if (meter->adjustment)
    {
      gtk_signal_disconnect_by_data (GTK_OBJECT (meter->adjustment), (gpointer) meter);
      gtk_object_unref (GTK_OBJECT (meter->adjustment));
    }

  meter->adjustment = adjustment;
  gtk_object_ref (GTK_OBJECT (meter->adjustment));

  gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
                  (GtkSignalFunc) gtk_meter_adjustment_changed,
                  (gpointer) meter);
  gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
                  (GtkSignalFunc) gtk_meter_adjustment_value_changed,
                  (gpointer) meter);

  meter->old_value = adjustment->value;
  meter->old_lower = adjustment->lower;
  meter->old_upper = adjustment->upper;
  meter->iec_lower = iec_scale(adjustment->lower);
  meter->iec_upper = iec_scale(adjustment->upper);
  meter->amber_frac = (iec_scale(meter->amber_level) - meter->iec_lower) /
                  (meter->iec_upper - meter->iec_lower);

  gtk_meter_update (meter);
}

static void
gtk_meter_realize (GtkWidget *widget)
{
  GtkMeter *meter;
  GdkWindowAttr attributes;
  GdkColor green, amber, red, peak;
  gint attributes_mask;
  PangoContext *pc = gtk_widget_get_pango_context(widget);
  PangoLayout *pl;
  PangoFontDescription *pfd;
  PangoRectangle rect;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_METER (widget));

  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  meter = GTK_METER (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_ACTIVE);

  meter->green_gc = gdk_gc_new(widget->window);
  green.red = 0;
  green.green = 60000;
  green.blue = 0;
  gdk_colormap_alloc_color (attributes.colormap, &green, FALSE, TRUE);
  gdk_gc_set_foreground(meter->green_gc, &green);

  meter->amber_gc = gdk_gc_new(widget->window);
  amber.red = 50000;
  amber.green = 55000;
  amber.blue = 0;
  gdk_colormap_alloc_color (attributes.colormap, &amber, FALSE, TRUE);
  gdk_gc_set_foreground(meter->amber_gc, &amber);

  meter->red_gc = gdk_gc_new(widget->window);
  red.red = 60000;
  red.green = 0;
  red.blue = 0;
  gdk_colormap_alloc_color (attributes.colormap, &red, FALSE, TRUE);
  gdk_gc_set_foreground(meter->red_gc, &red);

  meter->peak_gc = gdk_gc_new(widget->window);
  peak.red = 60000;
  peak.green = 60000;
  peak.blue = 0;
  gdk_colormap_alloc_color (attributes.colormap, &peak, FALSE, TRUE);
  gdk_gc_set_foreground(meter->peak_gc, &peak);
  
 /* pfd = pango_font_description_new();
  pango_font_description_set_family(pfd, "sans");
  pango_font_description_set_size(pfd, METERSCALE_MAX_FONT_SIZE * PANGO_SCALE);
  pango_context_set_font_description(pc, pfd);
  pl = pango_layout_new(pc);
  pango_layout_set_text(pl, "99", -1);
  pango_layout_get_pixel_extents(pl, &rect, NULL);

  if (meter->sides & GTK_METERSCALE_LEFT ||
      meter->sides & GTK_METERSCALE_RIGHT) {
    meter->min_width = rect.width + METERSCALE_MAX_FONT_SIZE;
  }
  if (meter->sides & GTK_METERSCALE_TOP ||
      meter->sides & GTK_METERSCALE_BOTTOM) {
    meter->min_height = rect.height + METERSCALE_MAX_FONT_SIZE + 1;
  }
  gtk_widget_set_usize(widget, meter->min_width, meter->min_height);
  */
  
}

static void 
gtk_meter_size_request (GtkWidget     *widget,
                   GtkRequisition *requisition)
{
  GtkMeter *meter = GTK_METER(widget);

  if (meter->direction == GTK_METER_UP || meter->direction == GTK_METER_DOWN) {
    requisition->width = METER_DEFAULT_WIDTH;
    requisition->height = METER_DEFAULT_LENGTH;
  } else {
    requisition->width = METER_DEFAULT_LENGTH;
    requisition->height = METER_DEFAULT_WIDTH;
  }
}

static void
gtk_meter_size_allocate (GtkWidget    *widget,
                  GtkAllocation *allocation)
{
  GtkMeter *meter;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_METER (widget));
  g_return_if_fail (allocation != NULL);

  widget->allocation = *allocation;
  meter = GTK_METER (widget);

  if (GTK_WIDGET_REALIZED (widget))
    {

      gdk_window_move_resize (widget->window,
                        allocation->x, allocation->y,
                        allocation->width, allocation->height);

    }
}

static gint
gtk_meter_expose (GtkWidget      *widget,
             GdkEventExpose *event)
{
  GtkMeter *meter;
  float val, frac, peak_frac;
  int g_h, a_h, r_h;
  int length = 0, width = 0;
  PangoRectangle lr;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_METER (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (event->count > 0)
    return FALSE;
  
  meter = GTK_METER (widget);

  switch (meter->direction) {
    case GTK_METER_UP:
    case GTK_METER_DOWN:
      length = widget->allocation.height - 2;
      width = widget->allocation.width - 2;
      break;
    case GTK_METER_LEFT:
    case GTK_METER_RIGHT:
      length = widget->allocation.width - 2;
      width = widget->allocation.height - 2;
      break;
  }

  val = iec_scale(meter->adjustment->value);
  if (val > meter->peak) {
    if (val > meter->iec_upper) {
      meter->peak = meter->iec_upper;
    } else {
      meter->peak = val;
    }
  }

  frac = (val - meter->iec_lower) / (meter->iec_upper - meter->iec_lower);
  peak_frac = (meter->peak - meter->iec_lower) / (meter->iec_upper -
              meter->iec_lower);

  /* Draw the background */
  gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL,
              GTK_SHADOW_IN, NULL, widget, "trough", 0, 0,
              widget->allocation.width, widget->allocation.height);
  gdk_window_clear_area (widget->window, 1, 1, widget->allocation.width - 2,
              widget->allocation.height - 2);
              
  lr.x = 0;
  lr.y = 0;
  lr.width = 0;
  lr.height = 0;

  meterscale_draw_notch_label(meter, 0.0f, 3, &lr);

  for (val = 5.0f; val < meter->upper; val += 5.0f) {
    meterscale_draw_notch_label(meter, val, 2, &lr);
  }

  for (val = -5.0f; val > meter->lower; val -= 5.0f) {
    meterscale_draw_notch_label(meter, val, 2, &lr);
  }

  for (val = -10.0f; val < 10.0f; val += 1.0f) {
    meterscale_draw_notch(meter, val, 1);
  }

  if (frac < meter->amber_frac) {
    g_h = frac * length;
    a_h = g_h;
    r_h = g_h;
  } else if (val <= 100.0f) {
    g_h = meter->amber_frac * length;
    a_h = frac * length;
    r_h = a_h;
  } else {
    g_h = meter->amber_frac * length;
    a_h = length * (100.0f - meter->iec_lower) / (meter->iec_upper -
                meter->iec_lower);
    r_h = frac * length;
  }

  if (a_h > length) {
    a_h = length;
  }
  if (r_h > length) {
    r_h = length;
  }

  switch (meter->direction) {
    case GTK_METER_LEFT:
      gdk_draw_rectangle (widget->window, meter->amber_gc, TRUE, a_h +
                  1, 1, length - a_h, width);
      /*
      gdk_draw_rectangle (widget->window, meter->peak_gc, TRUE, length *
                  (1.0 - peak_frac) + 1, 1, 1, width);
                  */
      break;

    case GTK_METER_RIGHT:
      gdk_draw_rectangle (widget->window, meter->green_gc, TRUE, 1, 1,
                  g_h, width);
      if (a_h > g_h) {
      gdk_draw_rectangle (widget->window, meter->amber_gc, TRUE, 1+g_h, 1,
                  a_h - g_h, width);
      }
      if (r_h > a_h) {
      gdk_draw_rectangle (widget->window, meter->red_gc, TRUE, 1+a_h, 1,
                  r_h - a_h, width);
      }
      gdk_draw_rectangle (widget->window, meter->peak_gc, TRUE, length *
                  peak_frac, 1, 1, width);
      break;

    case GTK_METER_UP:
    default:
      gdk_draw_rectangle (widget->window, meter->green_gc, TRUE, 1, length -
                  g_h + 1, width, g_h);
      if (a_h > g_h) {
      gdk_draw_rectangle (widget->window, meter->amber_gc, TRUE, 1, length -
                  a_h + 1, width, a_h - g_h);
      }
      if (r_h > a_h) {
      gdk_draw_rectangle (widget->window, meter->red_gc, TRUE, 1, length -
                  r_h + 1, width, r_h - a_h);
      }
      if (peak_frac > 0) {
        gdk_draw_rectangle (widget->window, meter->peak_gc, TRUE, 1, length *
                  (1.0f - peak_frac) + 1, width, 1);
      }
      break;
  }
  
  return FALSE;
}


static void meterscale_draw_notch_label(GtkMeter *meterscale, float db,
            int mark, PangoRectangle *last_label_rect)
{
    GtkWidget *widget = GTK_WIDGET(meterscale);
    int length, width, pos;
    int vertical = 0;

    if (meterscale->sides & GTK_METERSCALE_LEFT ||
        meterscale->sides & GTK_METERSCALE_RIGHT) {
          length = widget->allocation.height - 2;
          width = widget->allocation.width - 2;
          pos = length - length * (iec_scale(db) - meterscale->iec_lower) /
                (meterscale->iec_upper - meterscale->iec_lower);
          vertical = 1;
    } else {
          length = widget->allocation.width - 2;
          width = widget->allocation.height - 2;
          pos = length * (iec_scale(db) - meterscale->iec_lower) /
                (meterscale->iec_upper - meterscale->iec_lower);
    }


    if (last_label_rect) {
        PangoContext *pc = gtk_widget_get_pango_context(widget);
        PangoLayout *pl;
      PangoFontDescription *pfd;
      PangoRectangle rect;
      char text[128];
      int x, y, size;

      size = (6 + length / 150);
      if (size > METERSCALE_MAX_FONT_SIZE) {
          size = METERSCALE_MAX_FONT_SIZE;
      }
      pfd = pango_font_description_new();
      pango_font_description_set_family(pfd, "sans");
      pango_font_description_set_size(pfd, size * PANGO_SCALE);
      pango_context_set_font_description(pc, pfd);

      pl = pango_layout_new(pc);

      snprintf(text, 127, "%.0f", fabs(db));
      pango_layout_set_text(pl, text, -1);
      pango_layout_get_pixel_extents(pl, NULL, &rect);

      if (vertical) {
          x = width/2 - rect.width/2 + 1;
          y = pos - rect.height/2;
          if (y < 1) {
            y = 1;
          }
      } else {
          x = pos - rect.width/2 + 1;
          y = width/2 - rect.height / 2 + 1;
          if (x < 1) {
            x = 1;
          } else if (x + rect.width > length) {
              x = length - rect.width + 1;
          }
      }

      if (vertical && last_label_rect->y < y + rect.height + 2 && last_label_rect->y + last_label_rect->height + 2 > y) {
          return;
      }
      if (!vertical && last_label_rect->x < x + rect.width + 2 && last_label_rect->x + last_label_rect->width + 2 > y) {
          return;
      }

      gdk_draw_layout(widget->window, widget->style->black_gc, x, y, pl);
      last_label_rect->width = rect.width;
      last_label_rect->height = rect.height;
      last_label_rect->x = x;
      last_label_rect->y = y;
    }

    meterscale_draw_notch(meterscale, db, mark);
}

static void meterscale_draw_notch(GtkMeter *meterscale, float db,
            int mark)
{
    GtkWidget *widget = GTK_WIDGET(meterscale);
    int pos, length, width;

    if (meterscale->sides & GTK_METERSCALE_LEFT ||
        meterscale->sides & GTK_METERSCALE_RIGHT) {
          length = widget->allocation.height - 2;
          width = widget->allocation.width - 2;
          pos = length - length * (iec_scale(db) - meterscale->iec_lower) /
                (meterscale->iec_upper - meterscale->iec_lower);
    } else {
          length = widget->allocation.width - 2;
          width = widget->allocation.height - 2;
          pos = length * (iec_scale(db) - meterscale->iec_lower) /
                (meterscale->iec_upper - meterscale->iec_lower);
    }

    if (meterscale->sides & GTK_METERSCALE_LEFT) {
      gdk_draw_rectangle(widget->window, widget->style->black_gc, TRUE,
          0, pos, mark, 1);
    }
    if (meterscale->sides & GTK_METERSCALE_RIGHT) {
      gdk_draw_rectangle(widget->window, widget->style->black_gc, TRUE,
          width - mark + 1, pos, mark, 1);
    }
    if (meterscale->sides & GTK_METERSCALE_TOP) {
      gdk_draw_rectangle(widget->window, widget->style->black_gc, TRUE,
          pos+1, 1, 1, mark);
    }
    if (meterscale->sides & GTK_METERSCALE_BOTTOM) {
      gdk_draw_rectangle(widget->window, widget->style->black_gc, TRUE,
          pos+1, width -mark + 1, 1, mark);
    }
}


static void
gtk_meter_update (GtkMeter *meter)
{
  gfloat new_value;
  
  g_return_if_fail (meter != NULL);
  g_return_if_fail (GTK_IS_METER (meter));

  new_value = meter->adjustment->value;
  
  if (new_value < meter->adjustment->lower)
    new_value = meter->adjustment->lower;

  if (new_value > meter->adjustment->upper)
    new_value = meter->adjustment->upper;

  if (new_value != meter->adjustment->value)
    {
      meter->adjustment->value = new_value;
      gtk_signal_emit_by_name (GTK_OBJECT (meter->adjustment), "value_changed");
    }

  gtk_widget_draw(GTK_WIDGET(meter), NULL);
}

static void
gtk_meter_adjustment_changed (GtkAdjustment *adjustment,
                        gpointer       data)
{
  GtkMeter *meter;

  g_return_if_fail (adjustment != NULL);
  g_return_if_fail (data != NULL);

  meter = GTK_METER (data);

  if ((meter->old_lower != adjustment->lower) ||
      (meter->old_upper != adjustment->upper))
    {
      meter->iec_lower = iec_scale(adjustment->lower);
      meter->iec_upper = iec_scale(adjustment->upper);

      gtk_meter_set_warn_point(meter, meter->amber_level);

      gtk_meter_update (meter);

      meter->old_value = adjustment->value;
      meter->old_lower = adjustment->lower;
      meter->old_upper = adjustment->upper;
    } else if (meter->old_value != adjustment->value) {
      gtk_meter_update (meter);

      meter->old_value = adjustment->value;
    }
}

static void
gtk_meter_adjustment_value_changed (GtkAdjustment *adjustment,
                            gpointer       data)
{
  GtkMeter *meter;

  g_return_if_fail (adjustment != NULL);
  g_return_if_fail (data != NULL);

  meter = GTK_METER (data);

  if (meter->old_value != adjustment->value)
    {
      gtk_meter_update (meter);

      meter->old_value = adjustment->value;
    }
}

static float iec_scale(float db)
{
    float def = 0.0f;         /* Meter deflection %age */

    if (db < -70.0f) {
      def = 0.0f;
    } else if (db < -60.0f) {
      def = (db + 70.0f) * 0.25f;
    } else if (db < -50.0f) {
      def = (db + 60.0f) * 0.5f + 5.0f;
    } else if (db < -40.0f) {
      def = (db + 50.0f) * 0.75f + 7.5;
    } else if (db < -30.0f) {
      def = (db + 40.0f) * 1.5f + 15.0f;
    } else if (db < -20.0f) {
      def = (db + 30.0f) * 2.0f + 30.0f;
    } else {
      def = (db + 20.0f) * 2.5f + 50.0f;
    }

    return def;
}

void gtk_meter_reset_peak(GtkMeter *meter)
{
    meter->peak = 0.0f;
}

void gtk_meter_set_warn_point(GtkMeter *meter, gfloat pt)
{
    meter->amber_level = pt;
    if (meter->direction == GTK_METER_LEFT || meter->direction ==
                GTK_METER_DOWN) {
      meter->amber_frac = 1.0f - (iec_scale(meter->amber_level) -
            meter->iec_lower) / (meter->iec_upper - meter->iec_lower);
    } else {
        meter->amber_frac = (iec_scale(meter->amber_level) - meter->iec_lower) /
            (meter->iec_upper - meter->iec_lower);
    }

    gtk_widget_draw(GTK_WIDGET(meter), NULL);
}

Generated by  Doxygen 1.6.0   Back to index