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

legend.c

Go to the documentation of this file.
/**
 * @file    legend.c
 * @brief   Routines for constructing a plot legend.
 *
 * @author  Denis Pollney
 * @date    1 Oct 2001
 *
 * @par Copyright (C) 2001-2002 Denis Pollney
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 * @par
 *  This program 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 General Public License for more details
 * @par
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include<assert.h>
#include<string.h>

#include "ygraph.h"

/**
 * @brief    Free up the space that has been allocated to a legend.
 * 
 * @param    legend  The legend to be freed.
 * @todo     Check that the legend entries are freed correctly.
 */
void
00037 legend_free(Legend* legend)
{
  guint i;
  if (legend->pixmap)
    gdk_pixmap_unref(legend->pixmap);

  if (legend->gc)
    gdk_gc_unref(legend->gc);

  if (legend->font)
    gdk_font_unref(legend->font);

  if (legend->names)
    for (i=0; i<legend->names->len; i++)
      g_free(g_array_index(legend->names, gchar* ,i));
  g_array_free(legend->names, TRUE);

  g_free(legend);
  legend=NULL;
}

/**
 * @brief    Determine the position of the legend relative to the plot data.
 *
 * @param    legend  The Legend.
 */
gint
00064 legend_set_position(Legend* legend)
{
  gchar p = option_legend_position[0];
  UNUSED(legend);

  if (p == '0')
    return LEGEND_OFF;
  else if (p == 'r')
    return LEGEND_RIGHT;
  else if (p == 'a')
    return LEGEND_TOP;
  else
    return LEGEND_OVER;
}

/**
 * @brief    Go through the datasets in a plot window and get their names,
 *           which are to be put in the legend.
 *
 * @param    legend  The legend to be constructed.
 * @param    plot    The plot for which the legend is being built.
 */
void
00087 legend_set_names(Legend* legend, Plot* plot)
{
  DataSet* data_set;
  gchar scale_str[SCALE_ENTRY_SIZE];
  gchar* name;
  guint i;

  if (legend->names != NULL)
  {
    for (i=0; i<legend->names->len; i++)
      g_free(g_array_index(legend->names, gchar*, i));
    g_array_free(legend->names, TRUE);
    legend->names=NULL;
  }

  legend->names = g_array_new(FALSE, FALSE, sizeof(gchar*));

  for (i=0; i<plot->data->len; ++i)
    {
      data_set = plot_get_data_index(plot, i);

      if (data_set->scale != 1.0)
        {
          g_snprintf(scale_str, SCALE_ENTRY_SIZE, "%g", data_set->scale);
          name = g_strconcat( scale_str, " * ", data_set->name, NULL);
        }
      else
        name = g_strdup(data_set->name);

      g_array_append_val(legend->names, name);
    }
}

/**
 * @brief    Calculate the width taken up by the legend due to its text
 *           content, including padding.
 *
 * @param    legend  The Legend in question.
 * @returns  The width (in pixels) required to render the legend.
 */
gint
00128 legend_calc_width(Legend* legend)
{
  gchar* name;
  gint width = 0;
  guint i;
  for (i=0; i<legend->names->len; ++i)
    {
      name = g_array_index(legend->names, gchar*, i);
      width = MAX(width, gdk_text_width(legend->font, name, strlen(name)));
    }
  return width + 2*LEGEND_WIDTH_PADDING;
}


/**
 * @brief    Calculate the height taken up by the legend due to its text
 *           content, including padding.
 *
 * @param    legend  The Legend in question.
 * @returns  The height (in pixels) required to render the legend.
 */
gint
00150 legend_calc_height(Legend* legend)
{
  gchar* name;
  gint height = 0;
  guint i;

  for (i=0; i<legend->names->len; ++i)
    {
      name = g_array_index(legend->names, gchar*, i);
      height += (legend->font->ascent + legend->font->descent
                 + LEGEND_LINE_THICKNESS + 3*LEGEND_HEIGHT_PADDING);
    }
  return height + 2*LEGEND_HEIGHT_PADDING;
}

/**
 * @brief    Create a new legend structure given a plot window and its
 *           corresponding data content.
 *
 * @param    plot  The Plot for which the legend is being created.
 * @note     The Legend structure should be freed when it is no longer
 *           needed (eg. when the plot window is destroyed).
 */
void
00174 legend_create(Plot* plot)
{
  Legend* legend;

  if (plot->data == NULL)
    return;

  if (plot->legend != NULL)
    legend_free(plot->legend);

  legend = g_malloc(sizeof(Legend));

  legend->pixmap = NULL;
  legend->gc=NULL;
  legend->names=NULL;
  legend_set_names(legend, plot);
  legend->font = gdk_font_load(DEFAULT_LEGEND_FONT);
  legend->width = legend_calc_width(legend);
  legend->height = legend_calc_height(legend);
  legend->position = legend_set_position(legend);
  legend->rebuild = FALSE;

  plot->legend = legend;
}

/**
 * @brief    Draw fancy legend lines for each data set, fading from the
 *           start_color to the end_color used by SHOW_ALL_MODE for each plot.
 *
 * @param    plot         The Plot for which the legend is being drawn.
 * @param    legend       The legend.
 * @param    i_start      The leftmost extent (in plot pixels) of legend bars.
 * @param    i_end        The rightmost extent (in plot pixels) of legend bars.
 * @param    j_pos        The j coordinate of the legend bar.
 * @param    start_color  The colour of the leftmost edge of the bar.
 * @param    end_color    The colour of the rightmost edge of the bar.
 * @param    nframes      The number of frames in the DataSet.
 */
void
00213 legend_line_draw(Plot* plot, Legend* legend, gint i_start, gint i_end,
                 gint j_pos, GdkColor* start_color, GdkColor* end_color,
                 gint nframes)
{
  GdkColor* color;
  GdkGC* gc;
  gdouble i_div;
  gdouble s;
  gint i;
  gint ip0;
  gint ip1;
  gulong red;
  gulong green;
  gulong blue;

  /*
   * Break the line into a number of segments, each of which will have it's
   * own color, fading from the start to the end colors.
   */
  if ((global_display_mode == SHOW_ALL_MODE) && (nframes > 1))
    {
      i_div = ((gdouble) (i_end - i_start)) / LEGEND_LINE_DIVISIONS;
      
      for (i=0; i<LEGEND_LINE_DIVISIONS; ++i)
        {
          /*
           * Assign a color as a linear combination of the start and
           * end colors.
           */
          s = (gdouble) i/LEGEND_LINE_DIVISIONS;

          red = start_color->red + s*(end_color->red - start_color->red);
          green = start_color->green + s*(end_color->green
                                          - start_color->green);
          blue = start_color->blue + s*(end_color->blue - start_color->blue);

          color = color_set_rgb(plot->colormap, red, green, blue);

          gc = gdk_gc_new(legend->pixmap);
      
          gdk_gc_set_foreground(gc, color);
          g_free(color);

          /*
           * Draw a segment of the line with the corresponding color.
           */
          gdk_gc_set_line_attributes(gc, LEGEND_LINE_THICKNESS, GDK_LINE_SOLID,
                                     GDK_CAP_BUTT, GDK_JOIN_MITER);

          ip0 = i_start + s*legend->width;
          ip1 = i_start + (s+i_div)*legend->width;

          gdk_draw_line(legend->pixmap, gc, ip0, j_pos, ip1, j_pos);
          gdk_gc_unref(gc);
        }
    }
  /*
   * Otherwise, for ANIMATE_MODE, just use the start_color of the plot.
   */
  else
    {
      color = color_set_rgb(plot->colormap, start_color->red,
                            start_color->green, start_color->blue);
      gc = gdk_gc_new(legend->pixmap);
      
      gdk_gc_set_foreground(gc, color);
      g_free(color);

      gdk_gc_set_line_attributes(gc, LEGEND_LINE_THICKNESS, GDK_LINE_SOLID,
                                 GDK_CAP_BUTT, GDK_JOIN_MITER);

      gdk_draw_line(legend->pixmap, gc, i_start, j_pos, 
                    i_start + legend->width, j_pos);
      gdk_gc_unref(gc);
    }
}

/**
 * @brief    Create a pixmap containing the image of a plot legend.
 *
 * @param    plot    The plot for which the legend is to be drawn.
 * @param    legend  The legend data to be drawn.
 * @returns  A pixmap containing the legend.
 */
void
00298 legend_pixmap_create(Plot* plot, Legend* legend)
{
  GdkColor* start_color;
  GdkColor* end_color;
  PlotLine* plot_set;
  DataSet* data_set;
  gint data_set_idx;
  gint i_start;
  gint i_end;
  gint j_pos;
  guint i;
  gint name_len;
  gchar* name;

  if (legend->pixmap != NULL)
    gdk_pixmap_unref(legend->pixmap);

  legend->pixmap = gdk_pixmap_new(plot->plot_area->window, legend->width,
                                  legend->height, -1);

  if (legend->font == NULL)
    legend->font = gdk_font_load(DEFAULT_LEGEND_FONT);

  if (legend->gc)
    {
      gdk_gc_unref(legend->gc);
      legend->gc=NULL;
    }
  legend->gc = gdk_gc_new(legend->pixmap);

  /*
   * Erase a region along the right-hand margin of the plot where the legend
   * will be placed.
   */
  gdk_draw_rectangle(legend->pixmap, plot->plot_area->style->white_gc, TRUE,
                     0, 0, legend->width, plot->j_size);

  i_start = LEGEND_WIDTH_PADDING;
  i_end = legend->width - LEGEND_WIDTH_PADDING;

  j_pos = LEGEND_HEIGHT_PADDING;
  
  /*
   * Loop over the data set names in the legend.
   */
  for (i=0; i<legend->names->len; ++i)
    {
      plot_set = g_array_index(plot->plot_data, PlotLine*, i);
      data_set_idx = g_array_index(plot->data, gint, i);
      data_set = g_array_index(global_data_set_list, DataSet*, data_set_idx);

      /*
       * Draw a legend line.
       */
      start_color = plot_set->start_color;
      end_color = plot_set->end_color;

      legend_line_draw(plot, legend, i_start, i_end,
                       j_pos+LEGEND_LINE_THICKNESS/2, start_color,
                       end_color, data_set->nframes);
      j_pos += (1*LEGEND_HEIGHT_PADDING + LEGEND_LINE_THICKNESS);

      name = g_array_index(legend->names, gchar*, i);
      name_len = strlen(name);

      /*
       * Move down a bit and draw the corresponding text.
       */
      j_pos += legend->font->ascent;
      gdk_draw_text
        (legend->pixmap, legend->font,
         plot->plot_area->style->fg_gc[GTK_WIDGET_STATE(plot->plot_area)],
         i_start, j_pos, name, name_len);
      j_pos += (2*LEGEND_HEIGHT_PADDING + legend->font->descent);
    }
}

/**
 * @brief    Put the legend data onto the window.
 *
 * @param    plot  The plot for which the legend is to be drawn.
 */
void
00381 legend_draw(Plot* plot)
{
  Legend* legend;

  legend = plot->legend;
  if ((legend == NULL) || (legend->position == LEGEND_OFF))
    return;

  if (legend->rebuild == TRUE)
      legend_create(plot);
  legend = plot->legend;

  legend_pixmap_create(plot, legend);

  /*
   * Copy the legend pixmap onto the plot_area pixmap so it will be displayed
   * when the plot_area is updated.
   */
  if (legend->position == LEGEND_RIGHT)
    gdk_draw_pixmap(plot->pixmap, legend->gc, legend->pixmap, 0, 0,
                    plot->i_origin + plot->i_size,
                    plot->j_origin - plot->j_size,
                    -1, -1);
  else if (legend->position == LEGEND_TOP)
    gdk_draw_pixmap(plot->pixmap, legend->gc, legend->pixmap, 0, 0,
                    plot->i_origin + plot->i_size - legend->width,
                    plot->j_origin - plot->j_size - legend->height,
                    -1, -1);
  else if (legend->position == LEGEND_OVER)
    gdk_draw_pixmap(plot->pixmap, legend->gc, legend->pixmap, 0, 0,
                    plot->i_origin + plot->i_size - legend->width,
                    plot->j_origin - plot->j_size,
                    -1, -1);
}


Generated by  Doxygen 1.6.0   Back to index