/* GtkDatabox - An extension to the gtk+ library
 * Copyright (C) 1998 - 2002  Dr. Roland Bock
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 * 
 * 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 Lesser General Public License for more details.
 * 
 * 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.
 */
/* gtkdatabox.h */

#ifndef __GTK_DATABOX_H__
#define __GTK_DATABOX_H__


#include <gdk/gdk.h>
#include <gtk/gtkvbox.h> /* We need this to pack a table containing the data, */
                         /* scrollbars, rulers etc.*/


#ifdef __cplusplus
extern "C"
{
#endif				/* __cplusplus */

#define GTK_TYPE_DATABOX            (gtk_databox_get_type ())
#define GTK_DATABOX(obj)            GTK_CHECK_CAST (obj, gtk_databox_get_type (), GtkDatabox)
#define GTK_DATABOX_CLASS(klass)    GTK_CHECK_CLASS_CAST (klass, gtk_databox_get_type (), GtkDataboxClass)
#define GTK_IS_DATABOX(obj)         GTK_CHECK_TYPE (obj, gtk_databox_get_type ())
#define GTK_IS_DATABOX_CLASS(klass) GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DATABOX)
#define GTK_DATABOX_GET_CLASS(obj)        (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DATABOX, GtkDataboxClass))


   typedef struct _GtkDatabox GtkDatabox;
   typedef struct _GtkDataboxClass GtkDataboxClass;

   typedef enum                 /* Types of data display */
   {
      GTK_DATABOX_NOT_DISPLAYED = 0, /* hidden */
      GTK_DATABOX_POINTS,       /* Simple points or rectangles */
      GTK_DATABOX_LINES,        /* Lines connecting data points */
      GTK_DATABOX_BARS,         /* Vertical bars from X axis to data points */
      GTK_DATABOX_CROSS_SIMPLE,	/* X and Y axis */
      GTK_DATABOX_GRID,		/* Grid like in an oscilloscope */
   } GtkDataboxDataType;

   typedef struct
   {
      gint x;
      gint y;
   } GtkDataboxCoord;          /* Pixel coordinates */

   typedef struct
   {
      gfloat x;
      gfloat y;
   } GtkDataboxValue;          /* Data coordinates */

   struct _GtkDatabox
   {
      GtkVBox box;

      /* This list contains the data sets that are to be drawn. */
      GList *data;

      /* Table containing the display, scrollbars and rulers */
      GtkWidget *table;

      /* This is where we display the data */
      GtkWidget *draw;

      /* The rulers */
      GtkWidget *hrule;
      GtkWidget *vrule;

      /* The scrollbars and their adjustments */
      GtkWidget *hscroll;
      GtkWidget *vscroll;
      GtkAdjustment *adjX;
      GtkAdjustment *adjY;

      /* The pixmap for double buffering */
      GdkPixmap *pixmap;

      /* A number of flags (zoom enabled/disabled, etc.) */
      /* FIXME selection_flag should be included here */
      gulong flags;
      
      /* Is there an active selection? It is safe to let this untouched. */
      gboolean selection_flag;

      /* Number of values of the largest data set currently in the databox */
      guint max_points;

      /* An array of max_points points */
      GdkPoint *points;
      
      /* The selection box is drawn using this gc. You can manipulate it
       * by using 
       * gtk_databox_show_selection_filled or 
       * gtk_databox_hide_selection_filled
       */
      GdkGC *select_gc;

      /* Size (in pixel) of the display area */
      GtkDataboxCoord size;

      /* Position (pixel) of the last mouse click */
      GtkDataboxCoord marked;

      /* During creation of a selection box this is the second corner of
       * the box, opposed to marked
       */
      GtkDataboxCoord select;

      /* The extrema of the data values. These are calculated when 
       * gtkdatabox_rescale is called. You can set them directly via
       * gtkdatabox_rescale_with_values.
       */
      GtkDataboxValue min;
      GtkDataboxValue max;
      
      /* The visible extrema in the top-left and bottom-right corner.
       * These are influenced by zooming and scrolling.
       */
      GtkDataboxValue top_left;
      GtkDataboxValue bottom_right;
      
      /* Factor for translation of data values to screen coordinates
       * and vice versa. This is influenced by zooming and the extrema of
       * the data. You can use gtk_databox_rescale_with_values for
       * manipulating the extrema.
       */
      GtkDataboxValue factor;

      /* If we zoom to far into the data, we will get funny results, because
       * of overflow effects. Therefore zooming has to be limited.
       * The default zoom limit is 0.01, meaning that you can magnify your
       * data by a factor of 100.
       * Use gtk_set_zoom_limit to change this value.
       */
      gfloat zoom_limit;
   };

   struct _GtkDataboxClass
   {
      GtkVBoxClass parent_class;

      void (*gtk_databox) (GtkDatabox * box);

      /* Funktion pointers for signals, needed (mostly) for gtk-- wrapper */
      /* For use of the signals, see "examples/signals.c" */
      void (*gtk_databox_zoomed) (GtkDatabox * box,
				  GtkDataboxValue * top_left,
				  GtkDataboxValue * bottom_right);
      void (*gtk_databox_marked) (GtkDatabox * box, GtkDataboxCoord * marked);
      void (*gtk_databox_selection_started) (GtkDatabox * box,
					     GtkDataboxCoord * marked);
      void (*gtk_databox_selection_changed) (GtkDatabox * box,
					     GtkDataboxCoord * marked,
					     GtkDataboxCoord * select);
      void (*gtk_databox_selection_stopped) (GtkDatabox * box,
					     GtkDataboxCoord * marked,
					     GtkDataboxCoord * select);
      void (*gtk_databox_selection_cancelled) (GtkDatabox * box);

   };

   guint gtk_databox_get_type (void);

/* Before you can do much useful with the rest of the functions you will 
   need a GtkDatabox. The following function creates one for you :-) */
   GtkWidget *gtk_databox_new (void);

/* When you have added new data or changed "old" data, you have to tell 
   GtkDatabox to redraw the data, otherwise you will see no changes.
   This might be the most often called function... */
   void gtk_databox_redraw (GtkDatabox * box);

/* The next few functions allow you to make your data known to 
   the GtkDatabox. After all, the GtkDatabox is nothing without
   your data :-) */

/* This function is used to add two arrays of float values to the 
   list of data of the GtkDatabox. One array contains the X values
   the other contains the Y values of your data points.
   GtkDatabox will not copy or backup the data. You have to make sure
   that X and Y pointers will stay valid until you remove the data
   from the GtkDatabox or until you destroy the GtkDatabox itself.
   
   You also have to define the color, size/width and type of the data
   for display. See the GtkDataboxDataType enumeration for the type options.
   If you want to add a grid or coordinate system, choose the appropriate
   type and use any float arrays you happen to have, because the data is 
   obsolete in thes cases. Configuration of the grid is done with 
   the gtk_databox_data_set_grid_config function. 

   After adding data you may need to rescale the GtkDatabox thus assuring
   that your data will be visible on the screen. Use 
   gtk_databox_rescale and gtk_databox_rescale_with_values for this 
   purpose.
   
   The return value is the internal index of the data.
   */
   gint gtk_databox_data_add_x_y (GtkDatabox * box, guint length, gfloat * X,
				  gfloat * Y, GdkColor color,
				  GtkDataboxDataType type, guint dot_size);

/* This function is used for adding another set of data to your 
   GtkDatabox that has the same Y values as one of the added 
   data sets but has different X values. 
   
   See gtk_databox_data_add_x_y for additional information. */
   gint gtk_databox_data_add_x (GtkDatabox * box, guint length, gfloat * X,
				gint shared_Y_index, GdkColor color,
				GtkDataboxDataType type, guint dot_size);

/* This function is used for adding another set of data to your 
   GtkDatabox that has the same X values as one of the added 
   data sets but has different Y values. 
   
   See gtk_databox_data_add_x_y for additional information. */
   gint gtk_databox_data_add_y (GtkDatabox * box, guint length, gfloat * Y,
				gint shared_X_index, GdkColor color,
				GtkDataboxDataType type, guint dot_size);

/* The next two funtions are used to remove data sets from the 
   GtkDatabox. They remove the set with the given index or all sets.
   When removing only one set, X or Y data that has been used for 
   several data sets will be taken care of. */
   gint gtk_databox_data_remove (GtkDatabox * box, gint index);
   gint gtk_databox_data_remove_all (GtkDatabox * box);

/* In contrast to the two functions above the following functions not 
   only erase the data references from GtkDatabox's memory, the data 
   arrays are also freed. */
   gint gtk_databox_data_destroy (GtkDatabox * box, gint index);
   gint gtk_databox_data_destroy_all (GtkDatabox * box);

/* The following two functions are used to adjust the GtkDatabox to 
   your data. By default, the GtkDatabox will display data in a range 
   of (-0.5, -0.5) to (1.5, 1.5). Typically you data will not fit well into 
   this range. 
   
   By calling gtk_databox_rescale the GtkDatabox will analyse
   your data and make itself fit around the data, leaving a border of
   a few pixels for optical reasons.
   
   By calling gtk_databox_rescale_with_values you can define the 
   range of visible data yourself. This is helpful when your data
   will be undergoing changes but you know the theoretical maximum 
   minimum values. In that case you can once rescale the GtkDatabox with 
   these extrema and your data will always fit in (that is, if your
   theory is correct). Defining the extrema yourself is also useful 
   if your data contains a few "wrong" values that are far away from 
   the interesting parts.*/
   void gtk_databox_rescale (GtkDatabox * box);
   void gtk_databox_rescale_with_values (GtkDatabox * box,
					 GtkDataboxValue min,
					 GtkDataboxValue max);
 
/* Use the following functions to get or set the color and type 
   of your displayed data. The index is the return value of 
   gtk_databox_data_add_x_y or similar function. */
   gint gtk_databox_data_get_color (GtkDatabox * box, gint index,
				    GdkColor * color);
   gint gtk_databox_data_set_color (GtkDatabox * box, gint index,
				    GdkColor color);
   gint gtk_databox_data_get_type (GtkDatabox * box, gint index,
				   GtkDataboxDataType * type,
				   guint * dot_size);
   gint gtk_databox_data_set_type (GtkDatabox * box, gint index,
				   GtkDataboxDataType type, guint dot_size);

/* The next two functions do nothing useful unless the data type is GRID.
   Adding data and using it as grid or coordinate cross afterwards looks 
   crazy at first, but it has a lot of benefits, like GC management (that
   is one of my own benefits) or being able to make  sure that you 
   have your grid below, between or on top of the rest of the data (that is 
   supposed to be your benefit...) */
   gint gtk_databox_data_get_grid_config (GtkDatabox * box, gint index,
					  guint * hlines, guint * vlines);
   gint gtk_databox_data_set_grid_config (GtkDatabox * box, gint index,
					  guint hlines, guint vlines);

/* The following function calculates the "data coordinates" for
   a given pair of pixel coordinates. The results of this 
   calculation depend on your current zoom/scroll status, and on 
   the last rescale action, see gtk_databox_rescale and 
   gtk_databox_rescale_with_values. */
   void gtk_databox_data_get_value (GtkDatabox * box, GtkDataboxCoord coord,
				    GtkDataboxValue * value);
   
/* Get the minimum X/Y and the maximum X/Y "data coordinates". These
   are the coordinates of the bottom-left and top-right corners when 
   you zoom out completely (shift + button-3). 
   The result depends on the last call of gtk_databox_rescale or
   gtk_databox_rescale_with_values */
   void gtk_databox_data_get_extrema (GtkDatabox * box, GtkDataboxValue * min,
				      GtkDataboxValue * max);
/* Get the "data coordinates" (not the pixel coordinates) of the 
   bottom-left and top-right corners of the visible data currently 
   shown in the GtkDatabox. When zoomed out completely 
   the result will be the same as for gtk_databox_data_get_extrema 
   The result depends on the last call of gtk_databox_rescale or
   gtk_databox_rescale_with_values */
   void gtk_databox_data_get_visible_extrema (GtkDatabox * box,
					      GtkDataboxValue * min,
					      GtkDataboxValue * max);


/* Even you are happy with the default behaviour of the GtkDatabox
   you should also take a look at the following functions. 
   They might even increase you satisfaction :-) */

/* By default, the GtkDatabox will come with a pair of scrollbars and
   rulers. Use the following functions to get rid of them or 
   get them back again */

   void gtk_databox_show_rulers (GtkDatabox * box);
   void gtk_databox_hide_rulers (GtkDatabox * box);
   void gtk_databox_show_scrollbars (GtkDatabox * box);
   void gtk_databox_hide_scrollbars (GtkDatabox * box);


/* Decide whether the selection box is filled or not, the default
   is not filled */
   void gtk_databox_show_selection_filled (GtkDatabox * box);
   void gtk_databox_hide_selection_filled (GtkDatabox * box);

/* Selection is possible as default, but you may disable that feature */
   void gtk_databox_enable_selection (GtkDatabox * box);
   void gtk_databox_disable_selection (GtkDatabox * box);

/* Zooming into the data and back is enabled as default, but you 
   can disable zooming if it makes you nervous*/
   void gtk_databox_enable_zoom (GtkDatabox * box);
   void gtk_databox_disable_zoom (GtkDatabox * box);

/* When you zoom into the GtkDatabox, you might stumble over dubious
   effects like lines or points that are not where you would expect them 
   to be. These are range overflow or limited precision problems. 
   These problems are avoided if you simply do not zoom into the data ever
   deeped, but stop at some point. This point is the so-called 
   zoom limit. It is a value between 0 and 1 (use other values at 
   your own risk), the default is 0.01. It defines the smallest fraction
   in X and Y dimension that can be zoomed to. Thus, the default allows 
   you to see everything enlarged by a maximum factor of 100. 
   
   If you think you need to play with that behaviour, use the following 
   two functions, to get and set the zoom limit. 
   
   Note: Zooming in very deep might make the GtkDatabox rather slow
   when you are displaying your data as lines. */
   void gtk_databox_set_zoom_limit (GtkDatabox * box,
				       gfloat zoom_limit);
   gfloat gtk_databox_get_zoom_limit (GtkDatabox * box);

#ifdef __cplusplus
}
#endif				/* __cplusplus */

#endif				/* __GTK_DATABOX_H__ */
