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

plot_window.c

Go to the documentation of this file.
/**
 * @file    plot_window.c
 * @brief   Main plot window drawing routine.
 *
 *          Routines for setting up the plot window and putting data,
 *          axes, legends, etc. on it.
 *
 * @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 <math.h>
#include <string.h>
#include "ygraph.h"

/**
 * @brief    Menu entries for the plot windows.
 */
00035 static GtkItemFactoryEntry menu_items[] = {
  {"/_File", NULL, NULL, 0, "<Branch>"},
  {"/File/_New window", "<control>N", plot_window_empty_new, 0, NULL},
  {"/File/_Open...", "<control>O",  file_select, 1, NULL},
  {"/File/O_pen in new window...", "<control>P", file_select_new_window, 1,
   NULL},
  {"/File/_Reload", "<control>R", data_reload_plot, 1, NULL},
  {"/File/_Export image...", "<control>E", image_export_dialog, 1, NULL},
  {"/File/_Export frames...", "<control>M", image_dir_export_dialog, 1, NULL},
  {"/File/", "", NULL, 0, "<Separator>"},
  {"/File/_Close", "<control>W", plot_window_close, 1, NULL},
  {"/File/Quit", "<control>Q", gtk_main_quit, 0, NULL},
  {"/_Plot", NULL, NULL, 0, "<Branch>"},
  {"/Plot/_Derivative", "<control>D", plot_window_derivative, 1, NULL},
  {"/Plot/_Subtract A-B...","<control>S", subtract_select_dialog_create, 1,
   NULL},
  {"/Plot/Scale...","<control>m", scale_panel_create, 1, NULL},
  {"/_Settings", NULL, NULL, 0, "<Branch>"},
  {"/Settings/_Un-zoom", "<control>U", plot_window_unzoom, 1, NULL},
  {"/Settings/_Range...", "<control>X", range_dialog_create, 1, NULL},
  {"/Settings/", "", NULL, 0, "<Separator>"},
  {"/Settings/Draw _points", "<control>P", plot_window_draw_points_toggle, 1,
   "<ToggleItem>"},
  {"/Settings/Draw _grid", "<control>G", plot_window_draw_grid_toggle, 1, 
   "<ToggleItem>"},
  {"/Settings/Draw _range", "<control>R", plot_window_draw_range_toggle, 1, 
   "<ToggleItem>"},
  {"/_Help", NULL, NULL, 0, "<LastBranch>"},
  {"/Help/_Keybindings...", "<control>K", keybinding_window_create, 0, NULL },
  {"/Help/_Help...", "<control>H", help_window_create, 0, NULL },
  {"/Help/About...", NULL, about_window_create, 0, NULL } 
};

/**
 * @brief    Returns overflow-secure coordinate from gdouble to gint16
 *
 * @param    x  The coordinate.
 */
gint
00074 secure_coord_get(gdouble x)
{
  if (x>-MAX_PIXELS)
    if (x<MAX_PIXELS)
      return x;
    else
      return MAX_PIXELS;
  else
    return -MAX_PIXELS;
}

/**
 * @brief    Given data and a plot window of a given size, calculate the
 *           integer (i,j) coordinates of points in the window.
 *
 * @param    plot   The Plot where the line should go.
 * @param    frame  The frame to be plotted.
 * @param    line   The particular line to be plotted.
 */
void
00094 plot_line_set_points(Plot* plot, Frame* frame, PlotLine* line)
{
  GtkWidget* plot_area;
  GArray* xy_data;
  GdkPoint* ij_data;
  gdouble* xy_point;
  gdouble* x_range;
  gdouble* y_range;
  gint npoints;
  gint i;
  gint i_origin;
  gint j_origin;
  gdouble i_fac;
  gdouble j_fac;
  gdouble i_point;
  gdouble j_point;
  
  plot_area = plot->plot_area;
  x_range = plot->x_range;
  y_range = plot->y_range;
  npoints = frame->npoints;
  xy_data = frame->xy_data;

  ij_data = (GdkPoint*) g_malloc(npoints*sizeof(GdkPoint));

  i_origin = plot->i_origin;
  j_origin = plot->j_origin;
  i_fac = set_fac(axis_transformation(x_range[0], plot->x_axis),
                  axis_transformation(x_range[1], plot->x_axis),
                  plot->i_size);
  j_fac = set_fac(axis_transformation(y_range[0], plot->y_axis),
                  axis_transformation(y_range[1], plot->y_axis),
                  plot->j_size);

  for (i=0; i<npoints; ++i)
    {
      xy_point = g_array_index(xy_data, gdouble*, i);
      i_point = i_fac * (axis_transformation(xy_point[0], plot->x_axis) -
                         axis_transformation(x_range[0], plot->x_axis));
      i_point = x_to_i(plot, xy_point[0]);
      j_point = plot->j_size -
                j_fac * (axis_transformation(xy_point[1], plot->y_axis) -
                         axis_transformation(y_range[0], plot->y_axis));
      j_point = plot->j_size + y_to_j(plot, xy_point[1]);

      ij_data[i].x =  secure_coord_get(i_point);
      ij_data[i].y =  secure_coord_get(j_point);
    }

  line->ij_data = ij_data;
  line->frame = frame;
}

/**
 * @brief    Set the location of the plot origin in the plot window.
 *
 * @param    plot  The Plot.
 */
void
00153 i_origin_set(Plot* plot)
{
  plot->i_origin = plot->y_axis->width + LONG_TICK_LENGTH 
    + TEXT_Y_WIDTH_PADDING;
}

/**
 * @brief    Set the location of the plot origin in the plot window.
 *
 * @param    plot  The Plot.
 */
void
00165 j_origin_set(Plot* plot)
{
  plot->j_origin = plot->plot_area->allocation.height - 
    (plot->x_axis->height + LONG_TICK_LENGTH + TEXT_X_HEIGHT_PADDING);
}

/**
 * @brief    Set the size (in pixels) of the i-axis (ie. x-axis).
 *
 * @param    plot  The Plot.
 */
void
00177 i_size_set(Plot* plot)
{
  if ((plot->legend != NULL) && (plot->legend->position == LEGEND_RIGHT))
    plot->i_size = plot->plot_area->allocation.width - plot->i_origin
      - plot->legend->width - TEXT_X_WIDTH_PADDING;
  else
    plot->i_size = plot->plot_area->allocation.width - plot->i_origin -
      X_PADDING;
}

/**
 * @brief    Set the size (in pixels) of the j-axis (ie. y-axis).
 *
 * @param    plot  The Plot.
 */
void
00193 j_size_set(Plot* plot)
{
  if ((plot->legend != NULL) && (plot->legend->position == LEGEND_TOP))
    plot->j_size = plot->j_origin - plot->legend->height 
      - TEXT_Y_HEIGHT_PADDING;
  else
    plot->j_size = plot->j_origin - Y_PADDING;
}

/**
 * @brief    Set the plot location (ie. origin) and size in plot window (i-j)
 *           coordinates.
 *
 * @param    plot  The plot.
 */
void
00209 ij_dimensions_set(Plot* plot)
{
  i_origin_set(plot);
  j_origin_set(plot);

  i_size_set(plot);
  j_size_set(plot);
}

/**
 * @brief    Create plot coordinates for a set of lines of data.
 *
 * @param    plot      The Plot whose lines are being created.
 * @param    data_set  The DataSet containing the line.
 * @returns  A PlotLine corresponding to the given data set.
 */
PlotLine*
00226 data_set_line_create(Plot* plot, DataSet* data_set)
{
  PlotLine* data_set_line;
  Frame* frame;
  gint nframes;
  gint i;

  nframes = data_set->nframes;
  data_set_line = g_malloc(nframes*sizeof(PlotLine));

  for (i=0; i<nframes; ++i)
    {
      data_set_line[i].gc=NULL;
      data_set_line[i].ij_data=NULL;
      data_set_line[i].start_color=NULL;
      data_set_line[i].end_color=NULL;
      frame = g_array_index(data_set->frame, Frame*, i);
      plot_line_set_points(plot, frame, &data_set_line[i]);
      data_set_line[i].npoints = frame->npoints;
    }
  return data_set_line;
}

/**
 * @brief    Free up the data allocated to a PlotLine.
 *
 * @param    plotline  The PlotLine to be freed.
 *
 * @todo     Check that all the components of the PlotLine are freed properly.
 */
void
00257 plotline_free(PlotLine* plotline)
{
  g_free(plotline->ij_data);
  g_free(plotline->start_color);
  g_free(plotline->end_color);
  frame_free(plotline->frame);
  g_free(plotline);
}

/**
 * @brief    Set the plot window title.
 *
 *           Set the title of the plot window to be either the default program
 *           name and version, or if data has been loaded, then a list of the
 *           current active datasets displayed in the window.
 *
 * @param    plot  The Plot.
 */
void
00276 plot_window_set_title(Plot* plot)
{
  gchar* title_str_zero;
  gchar** title_str;
  gchar* tmp;
  gint n_str;
  gint i;

  n_str = plot->data->len;

  if (n_str == 0)
  {
    title_str_zero=g_strdup_printf("%s %s", PACKAGE, VERSION);
    gtk_window_set_title(GTK_WINDOW(plot->window), title_str_zero);
    g_free(title_str_zero);
  }
  else
  {
    title_str = g_malloc((n_str+1)*sizeof(gchar*));

    for (i=0; i<n_str; ++i)
      title_str[i] = g_strdup(plot_get_data_index(plot, i)->name);
    title_str[n_str] = NULL;
    gtk_window_set_title(GTK_WINDOW(plot->window), 
                         tmp=g_strjoinv(", ", title_str));
    g_free(tmp);
    for (i=0; i<n_str; ++i)
      g_free(title_str[i]);
    g_free(title_str);
  }

}

/**
 * @brief    Create a pointer to a plot structure and give it some reasonable
 *           default values.
 *
 * @param    data  The data to be added to the plot.
 * @returns  A new Plot structure filled with data.
 * @todo     The returned Plot should be freed when it is no longer needed
 *           (eg. the window is destroyed). Is this done?
 */
Plot*
00319 plot_create(GArray* data)
{
  Plot* plot;
  gchar* tmp;

  plot = g_malloc(sizeof(Plot));

  plot->plot_nbr = ++global_plot_nbr;

  plot->window = NULL;
  plot->plot_area = NULL;
  plot->colormap = NULL;
  plot->pixmap = NULL;
  plot->menu = NULL;
  plot->gc = NULL;
  plot->font = gdk_font_load(DEFAULT_PLOT_FONT);
  plot->font_height = gdk_text_height(plot->font, ALPHABET, strlen(ALPHABET));

  plot->data = data;
  plot->plot_data = NULL;
  plot->current_frame = NULL;

  plot->legend = NULL;
  plot->x_axis = NULL;
  plot->y_axis = NULL;
  
  plot->zoom_x_start = NO_ZOOM;
  plot->zoom_y_start = NO_ZOOM;
  plot->zoom_history_start = NULL;
  plot->zoom_history_end = NULL;

  plot->draw_points = option_draw_points;
  plot->fixed_range = FALSE;
  plot->draw_grid = option_draw_grid;
  plot->draw_range = option_draw_range;

  plot->current_directory = g_strdup_printf("%s/", tmp=g_get_current_dir());
  g_free(tmp);

  return plot;
}

/**
 * @brief    Free the space that has been allocated to a plot structure.
 *
 * @param    plot  The plot to free.
 * 
 * @todo     Check that all the Plot components are properly freed.
 */
void
00369 plot_free(Plot* plot)
{
  guint i;

  for (i=0; i<plot->data->len; ++i)
    plotline_free(g_array_index(plot->plot_data, PlotLine*, i));
  g_array_free(plot->data, TRUE);
  g_array_free(plot->plot_data, TRUE);
  g_array_free(plot->current_frame, TRUE);
  if (plot->zoom_history_start)
    g_array_free(plot->zoom_history_start, TRUE);
  if (plot->zoom_history_end)
    g_array_free(plot->zoom_history_end, TRUE);
  g_free(plot->current_directory);
  g_free(plot);
  plot=NULL;
}

/**
 * @brief    Go through the datasets and convert xy data to ij data. Each
 *           frame is stored as a PlotLine.
 *
 * @param    plot  The Plot.
 * @returns  An array of PlotLines.
 */
GArray*
00395 plot_line_create(Plot* plot)
{
  GArray* plot_data;
  DataSet* data_set;
  PlotLine* data_set_line;
  guint i;

  plot_data = g_array_new(FALSE, FALSE, sizeof(PlotLine*));
  for (i=0; i<plot->data->len; ++i)
    {
      data_set = plot_get_data_index(plot, i);
      data_set_line = data_set_line_create(plot, data_set);
      g_array_append_val(plot_data, data_set_line);
      plotline_set_colors(plot, data_set_line, i);
    }
  return plot_data;
}

/**
 * @brief    Initialise a new plot window given a collection of data (eg. at
 *           startup or when a new plot window is created).
 *
 * @param    data  The data for the new plot.
 * @returns  A new plot structure containing the passed data.
 * @note     The returned Plot should be freed when obsolete, but it is also
 *           appended to the global plot GArray
 */
Plot*
00423 plot_data_init(GArray* data)
{
  Plot* plot;
  GArray* current_frame;
  guint i;
  gint f_nbr;

  YDEB("plot_data_init\n");
  
  plot = plot_create(data);

  plot_window_build(plot);

  legend_create(plot);

  plot_range_set(plot, INIT_DATA);
  plot_axes_create(plot);
  ij_dimensions_set(plot);

  plot->plot_data = plot_line_create(plot);

  /*
   * Initialise the current frame array to start at the first frame.
   */
  current_frame = g_array_new(FALSE, FALSE, sizeof(gint));
  f_nbr = FIRST_FRAME;
  for (i=0; i<data->len; ++i)
    g_array_append_val(current_frame, f_nbr);
  plot->current_frame = current_frame;

  plot_window_set_title(plot);

  if (global_plot_window == NULL)
    global_plot_window = g_array_new(FALSE, FALSE, sizeof(Plot*));

  g_array_append_val(global_plot_window, plot);

  return plot;
}

/**
 * @brief    Append a dataset to the data already contained in a plot (eg. 
 *           after loading a new data set from a file).
 *
 * @param    plot          The Plot where data is being appended.
 * @param    data_set_idx  The index of the appended DataSet within the
 *                         global_data_set_list.
 */
void
00472 plot_data_append(Plot* plot, gint data_set_idx)
{
  gint current_frame;

  YDEB("plot_data_append\n");

  if (plot->data == NULL)
    plot->data = g_array_new(FALSE, FALSE, sizeof(gint));

  g_array_append_val(plot->data, data_set_idx);

  plot->legend->rebuild = TRUE;
  legend_create(plot);
  /*
   * Determine the new axis ranges in both xy and ij coordinates.
   */
  plot_range_set(plot, APPEND_DATA);
  plot_axes_create(plot);
  ij_dimensions_set(plot);

  /*
   * Re-calculate the ij data from the xy data for all datasets, since
   * a new range may have been defined.
   */
  if (plot->plot_data != NULL)
    g_array_free(plot->plot_data, TRUE);

  plot->plot_data = plot_line_create(plot);

  current_frame = current_frame_calc(plot_get_data_index(plot, data_set_idx));
  g_array_append_val(plot->current_frame, current_frame);

  plot_window_set_title(plot);
}

/**
 * @brief    Recalculate the ij data when the window size changes or the plot
 *           is zoomed.
 *
 * @param    plot  The Plot.
 */
void
00514 plot_window_reconfigure(Plot* plot)
{
  YDEB("plot_window_reconfigure\n");
  if (!plot->fixed_range)
      plot_range_set(plot, INIT_DATA);
  plot_axes_create(plot);
  ij_dimensions_set(plot);

  plot->plot_data = plot_line_create(plot);

  plot_window_set_title(plot);
}

/**
 * @brief    Set any toggle buttons in the plot menu according to the state
 *           of the command line options when the menu is created.
 *
 * @param    menu  A pointer to the menu.
 * @param    plot  The plot, whose own flags will be modified based on the
 *                 options.
 */
void
00536 plot_menu_set_toggles(GtkItemFactory* menu, Plot* plot)
{
  /*
   * Turn on the draw-points toggle.
   */
  if (option_draw_points)
    {
      GtkItem* toggle_button = (GtkItem*) gtk_item_factory_get_widget
        (menu, "<menu>/Settings/Draw points");
      plot->draw_points = !(plot->draw_points);
      gtk_menu_item_activate(GTK_MENU_ITEM(toggle_button));
    }

  /*
   * Turn on the draw-grid toggle.
   */
  if (option_draw_grid)
    {
      GtkItem* toggle_button = (GtkItem*) gtk_item_factory_get_widget
        (menu, "<menu>/Settings/Draw grid");
      plot->draw_grid = !(plot->draw_grid);
      gtk_menu_item_activate(GTK_MENU_ITEM(toggle_button));
    }

  /*
   * Turn on the draw-range toggle.
   */
  if (option_draw_range)
    {
      GtkItem* toggle_button = (GtkItem*) gtk_item_factory_get_widget
        (menu, "<menu>/Settings/Draw range");
      plot->draw_range = !(plot->draw_range);
      gtk_menu_item_activate(GTK_MENU_ITEM(toggle_button));
    }
}

/**
 * @brief    Build the plot window menu using an item factory.
 *
 * @param    window    The window containing the menu.
 * @param    menu_bar  The menu data.
 * @param    plot      The plot structure controlling the window.
 * @returns  A completed menu in the form of an item factory.
 */
GtkItemFactory*
00581 plot_window_menu_build(GtkWidget* window, GtkWidget** menu_bar, Plot* plot)
{
  GtkAccelGroup* accel_group;
  GtkItemFactory* menu;
  gint menu_size;

  menu_size = sizeof(menu_items) / sizeof(menu_items[0]);

  accel_group = gtk_accel_group_new();
  menu = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<menu>", accel_group);
  gtk_item_factory_create_items(menu, menu_size, menu_items, plot);

  gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);

  plot_menu_set_toggles(menu, plot);

  if (menu_bar)
    *menu_bar = gtk_item_factory_get_widget (menu, "<menu>");

  return menu;
}

/*
 * Create the plot window.
 */
void
plot_window_build(Plot* plot)
{
  GtkWidget* plot_window;
  GtkWidget* plot_area;
  GtkWidget* menu_bar;
  GtkWidget* vbox;
  GtkItemFactory* menu;

  plot_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size(GTK_WINDOW(plot_window),
                              DEFAULT_I_SIZE, DEFAULT_J_SIZE);
  gtk_signal_connect (GTK_OBJECT (plot_window), "delete_event",
                      GTK_SIGNAL_FUNC (plot_window_destroy), plot);
  gtk_signal_connect(GTK_OBJECT(plot_window), "key_press_event",
                     GTK_SIGNAL_FUNC(plot_window_key_press_event), plot);


  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(plot_window), vbox);
  gtk_widget_show(vbox);


  /*
   * Menu bar.
   */
  menu = plot_window_menu_build(plot_window, &menu_bar, plot);
  gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, TRUE, 0);
  gtk_widget_show(menu_bar);

  /*
   * Create the a drawing area in which to put axes and data.
   */
  plot_area = gtk_drawing_area_new();

  gtk_drawing_area_size(GTK_DRAWING_AREA(plot_area), 
                        plot_window->allocation.width,
                        plot_window->allocation.height);
  gtk_signal_connect(GTK_OBJECT(plot_area), "expose_event",
                     GTK_SIGNAL_FUNC(plot_window_expose), plot);
  gtk_signal_connect(GTK_OBJECT(plot_area), "configure_event",
                     GTK_SIGNAL_FUNC(plot_window_configure), plot);
  gtk_signal_connect(GTK_OBJECT(plot_area), "button_press_event",
                     GTK_SIGNAL_FUNC(plot_area_button_press_event), plot);
  gtk_signal_connect(GTK_OBJECT(plot_area), "button_release_event",
                     GTK_SIGNAL_FUNC(plot_area_button_release_event), plot);
  gtk_signal_connect(GTK_OBJECT(plot_area), "motion_notify_event",
                     GTK_SIGNAL_FUNC(plot_area_motion_notify_event), plot);

  gtk_widget_set_events (plot_area, GDK_EXPOSURE_MASK
                         | GDK_BUTTON_PRESS_MASK
                         | GDK_BUTTON_RELEASE_MASK
                         | GDK_POINTER_MOTION_MASK
                         | GDK_POINTER_MOTION_HINT_MASK
                         | GDK_KEY_PRESS_MASK);

  gtk_box_pack_start(GTK_BOX(vbox), plot_area, TRUE, TRUE, 0);

  gtk_widget_show(plot_area);

  /*
   * Store widgets in the plot data structure.
   */
  plot->colormap = gtk_widget_get_colormap(plot_window);
  plot->window = plot_window;
  plot->plot_area = plot_area;
  plot->menu = menu;
}

/**
 * @brief    Erase the entire window, including axes.
 *
 * @param    plot  The Plot to be erased.
 */
void
00681 plot_window_expunge(Plot* plot)
{
  gdk_draw_rectangle(plot->pixmap, plot->plot_area->style->white_gc, TRUE,
                     0, 0, 
                     plot->plot_area->allocation.width,
                     plot->plot_area->allocation.height);
}

/**
 * @brief    Redraw the entire plot window, including axes.
 *
 * @param    plot  The Plot to be redrawn.
 */
void
00695 plot_window_display_all(Plot* plot)
{
  plot_window_expunge(plot);

  axis_draw(plot);

  plot->legend->rebuild = TRUE;
  legend_draw(plot);

  plot_window_draw_time(plot);
  plot_area_draw(plot);

  gdk_draw_pixmap
    (plot->plot_area->window,
     plot->plot_area->style->fg_gc[GTK_WIDGET_STATE(plot->plot_area)],
     plot->pixmap,
     0, 0, 0, 0, -1, -1);
}

/**
 * @brief    Go through the list of plot windows an show each.
 */
void
00718 plot_window_show(void)
{
  Plot* plot;
  guint i;

  if (global_plot_window == NULL)
    return;

  for (i=0; i<global_plot_window->len; ++i)
    {
      plot = g_array_index(global_plot_window, Plot*, i);
      gtk_widget_show(plot->window);
    }
}

Generated by  Doxygen 1.6.0   Back to index