"Fossies" - the Fresh Open Source Software Archive

Member "gtkdatabox-1.0.0/gtk/gtkdatabox.c" (16 Apr 2021, 89688 Bytes) of package /linux/privat/gtkdatabox-1.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gtkdatabox.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.9.3.1_vs_1.0.0.

    1 /* $Id: gtkdatabox.c 4 2008-06-22 09:19:11Z rbock $ */
    2 /* GtkDatabox - An extension to the gtk+ library
    3  * Copyright (C) 1998 - 2008  Dr. Roland Bock
    4  *
    5  * This program is free software; you can redistribute it and/or
    6  * modify it under the terms of the GNU Lesser General Public License
    7  * as published by the Free Software Foundation; either version 2.1
    8  * of the License, or (at your option) any later version.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  * GNU Lesser General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program; if not, write to the Free Software
   17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   18  */
   19 
   20 #ifdef HAVE_CONFIG_H
   21 #include "config.h"
   22 #endif
   23 
   24 #include <gtkdatabox.h>
   25 #include <gtkdatabox_marshal.h>
   26 #include <gtkdatabox_ruler.h>
   27 #include <gtk/gtk.h>
   28 #include <math.h>
   29 
   30 #ifdef _MSC_VER
   31 #ifndef _DATABOX_LOG2_
   32 #define _DATABOX_LOG2_
   33 /*< private >*/
   34 #define log2(x) (log(x)/log(2))
   35 #endif /*_DATABOX_LOG2_ */
   36 #endif /*_MSC_VER */
   37 
   38 static gint gtk_databox_button_press (GtkWidget * widget,
   39                                       GdkEventButton * event);
   40 static gint gtk_databox_scroll_event (GtkWidget *widget,
   41                                       GdkEventScroll *event);
   42 static gint gtk_databox_button_release (GtkWidget * widget,
   43                                         GdkEventButton * event);
   44 static gint gtk_databox_motion_notify (GtkWidget * widget,
   45                                        GdkEventMotion * event);
   46 static void gtk_databox_realize (GtkWidget * widget);
   47 static void gtk_databox_unrealize (GtkWidget * widget);
   48 static void gtk_databox_size_allocate (GtkWidget * widget,
   49                                        GtkAllocation * allocation);
   50 static gint gtk_databox_draw (GtkWidget * widget,
   51                                 cairo_t * cr);
   52 static void gtk_databox_set_property (GObject * object,
   53                                       guint prop_id,
   54                                       const GValue * value,
   55                                       GParamSpec * pspec);
   56 static void gtk_databox_get_property (GObject * object,
   57                                       guint prop_id,
   58                                       GValue * value,
   59                                       GParamSpec * pspec);
   60 
   61 static gfloat gtk_databox_get_offset_x (GtkDatabox* box);
   62 static gfloat gtk_databox_get_page_size_x (GtkDatabox* box);
   63 static gfloat gtk_databox_get_offset_y (GtkDatabox* box);
   64 static gfloat gtk_databox_get_page_size_y (GtkDatabox* box);
   65 static void gtk_databox_calculate_visible_limits (GtkDatabox * box);
   66 static void gtk_databox_create_backing_surface (GtkDatabox * box);
   67 static void gtk_databox_calculate_selection_values (GtkDatabox * box);
   68 static void gtk_databox_selection_cancel (GtkDatabox * box);
   69 static void gtk_databox_zoomed (GtkDatabox * box);
   70 static void gtk_databox_draw_selection (GtkDatabox * box, gboolean clear);
   71 static void gtk_databox_adjustment_value_changed (GtkDatabox * box);
   72 static void gtk_databox_ruler_update (GtkDatabox * box);
   73 
   74 /* IDs of signals */
   75 enum {
   76     ZOOMED_SIGNAL,
   77     SELECTION_STARTED_SIGNAL,
   78     SELECTION_CHANGED_SIGNAL,
   79     SELECTION_FINALIZED_SIGNAL,
   80     SELECTION_CANCELED_SIGNAL,
   81     LAST_SIGNAL
   82 };
   83 
   84 /* signals will be configured during class_init */
   85 static gint gtk_databox_signals[LAST_SIGNAL] = { 0 };
   86 
   87 
   88 /* IDs of properties */
   89 enum {
   90     ENABLE_SELECTION = 1,
   91     ENABLE_ZOOM,
   92     ADJUSTMENT_X,
   93     ADJUSTMENT_Y,
   94     RULER_X,
   95     RULER_Y,
   96     SCALE_TYPE_X,
   97     SCALE_TYPE_Y,
   98     BOX_SHADOW,
   99     LAST_PROPERTY
  100 };
  101 
  102 /*
  103  * GtkDataboxPrivate
  104  *
  105  * A private data structure used by the #GtkDatabox. It shields all internal things
  106  * from developers who are just using the widget.
  107  *
  108  */
  109 typedef struct _GtkDataboxPrivate GtkDataboxPrivate;
  110 
  111 struct _GtkDataboxPrivate {
  112     cairo_surface_t *backing_surface;
  113     gint old_width;
  114     gint old_height;
  115 
  116     /* Total and visible limits (values, not pixels) */
  117     gfloat total_left;
  118     gfloat total_right;
  119     gfloat total_top;
  120     gfloat total_bottom;
  121     gfloat visible_left;
  122     gfloat visible_right;
  123     gfloat visible_top;
  124     gfloat visible_bottom;
  125 
  126     /* Translation information between values and pixels */
  127     GtkDataboxScaleType scale_type_x;
  128     GtkDataboxScaleType scale_type_y;
  129     gfloat translation_factor_x;
  130     gfloat translation_factor_y;
  131 
  132     /* Properties */
  133     gboolean enable_selection;
  134     gboolean enable_zoom;
  135     GtkAdjustment *adj_x;
  136     GtkAdjustment *adj_y;
  137     GtkDataboxRuler *ruler_x;
  138     GtkDataboxRuler *ruler_y;
  139 
  140     /* Other private stuff */
  141     GList *graphs;
  142     GdkPoint marked;
  143     GdkPoint select;
  144     GtkDataboxValueRectangle selectionValues;
  145     gfloat zoom_limit;
  146 
  147     /* flags */
  148     gboolean selection_active;
  149     gboolean selection_finalized;
  150 
  151     GtkShadowType box_shadow; /* The type of shadow drawn on the pixmap edge */
  152     GtkCssProvider *cssp; /* Internal style sheet used for background-color */
  153 };
  154 
  155 /**
  156  * gtk_databox_get_type
  157  *
  158  * Determines the #GType of the GtkDatabox widget type.
  159  *
  160  * Return value: The #GType of the GtkDatabox widget type.
  161  *
  162  */
  163 G_DEFINE_TYPE_WITH_PRIVATE(GtkDatabox, gtk_databox, GTK_TYPE_WIDGET)
  164 
  165 static void
  166 gtk_databox_class_init (GtkDataboxClass * class) {
  167     GObjectClass *gobject_class;
  168     GtkWidgetClass *widget_class;
  169 
  170     gobject_class = G_OBJECT_CLASS (class);
  171     widget_class = (GtkWidgetClass *) class;
  172 
  173     gobject_class->set_property = gtk_databox_set_property;
  174     gobject_class->get_property = gtk_databox_get_property;
  175 
  176     widget_class->realize = gtk_databox_realize;
  177     widget_class->unrealize = gtk_databox_unrealize;
  178     widget_class->size_allocate = gtk_databox_size_allocate;
  179     widget_class->draw = gtk_databox_draw;
  180     widget_class->motion_notify_event = gtk_databox_motion_notify;
  181     widget_class->button_press_event = gtk_databox_button_press;
  182     widget_class->button_release_event = gtk_databox_button_release;
  183     widget_class->scroll_event = gtk_databox_scroll_event;
  184 
  185     /**
  186      * GtkDatabox:enable-selection:
  187      *
  188      * Defines whether the user can select
  189      * rectangular areas with the mouse (#TRUE) or not (#FALSE).
  190      *
  191      */
  192     g_object_class_install_property (gobject_class,
  193                                      ENABLE_SELECTION,
  194                                      g_param_spec_boolean ("enable-selection",
  195                                              "Enable Selection",
  196                                              "Enable selection of areas via mouse (TRUE/FALSE)",
  197                                              TRUE,       /* default value */
  198                                              G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
  199 
  200     /**
  201      * GtkDatabox:enable-zoom:
  202      *
  203      * Defines whether the user can use the mouse to zoom in or out (#TRUE) or not (#FALSE).
  204      *
  205      */
  206     g_object_class_install_property (gobject_class,
  207                                      ENABLE_ZOOM,
  208                                      g_param_spec_boolean ("enable-zoom",
  209                                              "Enable Zoom",
  210                                              "Enable zooming in or out via mouse click (TRUE/FALSE)",
  211                                              TRUE,       /* default value */
  212                                              G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
  213 
  214     /**
  215      * GtkDatabox:adjustment_x:
  216      *
  217      * The #GtkAdjustment associated with the horizontal scrollbar. The #GtkDatabox widget
  218      * creates a GtkAdjustment itself. Normally there is no need for you to create another
  219      * GtkAdjustment. You could simply use the one you get via gtk_databox_get_adjustment_x().
  220      *
  221      */
  222     g_object_class_install_property (gobject_class,
  223                                      ADJUSTMENT_X,
  224                                      g_param_spec_object ("adjustment-x",
  225                                              "Horizontal Adjustment",
  226                                              "GtkAdjustment for horizontal scrolling",
  227                                              GTK_TYPE_ADJUSTMENT,
  228                                              G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
  229 
  230     /**
  231      * GtkDatabox:adjustment_y:
  232      *
  233      * The #GtkAdjustment associated with the vertical scrollbar. The #GtkDatabox widget
  234      * creates a GtkAdjustment itself. Normally there is no need for you to create another
  235      * GtkAdjustment. You could simply use the one you get via gtk_databox_get_adjustment_y().
  236      *
  237      */
  238     g_object_class_install_property (gobject_class,
  239                                      ADJUSTMENT_Y,
  240                                      g_param_spec_object ("adjustment-y",
  241                                              "Vertical Adjustment",
  242                                              "GtkAdjustment for vertical scrolling",
  243                                              GTK_TYPE_ADJUSTMENT,
  244                                              G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
  245 
  246     /**
  247      * GtkDatabox:ruler_x:
  248      *
  249      * The horizontal %GtkDataboxRuler (or NULL).
  250      *
  251      */
  252     g_object_class_install_property (gobject_class,
  253                                      RULER_X,
  254                                      g_param_spec_object ("ruler-x",
  255                                              "Horizontal Ruler",
  256                                              "A horizontal GtkDataboxRuler or NULL",
  257                                              GTK_DATABOX_TYPE_RULER,
  258                                              G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
  259 
  260     /**
  261      * GtkDatabox:ruler_y:
  262      *
  263      * The vertical %GtkDataboxRuler (or NULL).
  264      *
  265      */
  266     g_object_class_install_property (gobject_class,
  267                                      RULER_Y,
  268                                      g_param_spec_object ("ruler-y",
  269                                              "Vertical Ruler",
  270                                              "A vertical GtkDataboxRuler or NULL",
  271                                              GTK_DATABOX_TYPE_RULER,
  272                                              G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
  273 
  274     /**
  275      * GtkDatabox:scale_type_x:
  276      *
  277      * The horizontal scale type (linear or lograrithmic).
  278      */
  279     g_object_class_install_property (gobject_class,
  280                                      SCALE_TYPE_X,
  281                                      g_param_spec_enum ("scale-type-x",
  282                                              "Horizontal scale type",
  283                                              "Horizontal scale type (linear or logarithmic)",
  284                                              gtk_databox_scale_type_get_type (),
  285                                              GTK_DATABOX_SCALE_LINEAR,
  286                                              G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
  287 
  288     /**
  289      * GtkDatabox:scale_type_y:
  290      *
  291      * The vertical scale type (linear or lograrithmic).
  292      */
  293     g_object_class_install_property (gobject_class,
  294                                      SCALE_TYPE_Y,
  295                                      g_param_spec_enum ("scale-type-y",
  296                                              "Vertical scale type",
  297                                              "Vertical scale type (linear or logarithmic)",
  298                                              gtk_databox_scale_type_get_type (),
  299                                              GTK_DATABOX_SCALE_LINEAR,
  300                                              G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
  301 
  302 
  303     g_object_class_install_property (gobject_class,
  304                                      BOX_SHADOW,
  305                                      g_param_spec_uint ("box-shadow",
  306                                              "Box Shadow",
  307                                              "Style of the box shadow: GTK_SHADOW_NONE, GTK_SHADOW_IN, GTK_SHADOW_OUT, GTK_SHADOW_ETCHED_IN, GTK_SHADOW_ETCHED_OUT",
  308                                              GTK_SHADOW_NONE,
  309                                              GTK_SHADOW_ETCHED_OUT,
  310                                              GTK_SHADOW_NONE,
  311                                              G_PARAM_READWRITE));
  312 
  313     /**
  314      * GtkDatabox::zoomed:
  315      * @box: The #GtkDatabox widget which zoomed in or out.
  316      *
  317      * This signal is emitted each time the zoom of the widget is changed, see for example
  318      * gtk_databox_zoom_to_selection(), gtk_databox_set_visible_limits().
  319      */
  320     gtk_databox_signals[ZOOMED_SIGNAL] =
  321         g_signal_new ("zoomed",
  322                       G_TYPE_FROM_CLASS (gobject_class),
  323                       G_SIGNAL_RUN_FIRST,
  324                       G_STRUCT_OFFSET (GtkDataboxClass, zoomed),
  325                       NULL, /* accumulator */
  326                       NULL, /* accumulator_data */
  327                       gtk_databox_marshal_VOID__VOID,
  328                       G_TYPE_NONE, 0);
  329 
  330     /**
  331      * GtkDatabox::selection-started:
  332      * @box: The #GtkDatabox widget in which the selection has been started.
  333      * @selection_values: The corners of the selection rectangle.
  334      *
  335      * This signal is emitted when the mouse is firstmoved
  336      * with the left button pressed after the mouse-down (and the #GtkDatabox:enable-selection property
  337      * is set). The corners of the selection rectangle are stored in @selection_values.
  338      *
  339      * @see_also: #GtkDatabox::selection-changed
  340      */
  341     gtk_databox_signals[SELECTION_STARTED_SIGNAL] =
  342         g_signal_new ("selection-started",
  343                       G_TYPE_FROM_CLASS (gobject_class),
  344                       G_SIGNAL_RUN_FIRST,
  345                       G_STRUCT_OFFSET (GtkDataboxClass, selection_started),
  346                       NULL, /* accumulator */
  347                       NULL, /* accumulator_data */
  348                       gtk_databox_marshal_VOID__POINTER,
  349                       G_TYPE_NONE,
  350                       1,
  351                       G_TYPE_POINTER);
  352 
  353     /**
  354      * GtkDatabox::selection-changed:
  355      * @box: The #GtkDatabox widget in which the selection was changed.
  356      * @selection_values: The corners of the selection rectangle.
  357      *
  358      * This signal is emitted when the mouse is moved
  359      * with the left button pressed (and the #GtkDatabox:enable-selection property
  360      * is set). The corners of the selection rectangle are stored in @selection_values.
  361      */
  362     gtk_databox_signals[SELECTION_CHANGED_SIGNAL] =
  363         g_signal_new ("selection-changed",
  364                       G_TYPE_FROM_CLASS (gobject_class),
  365                       G_SIGNAL_RUN_FIRST,
  366                       G_STRUCT_OFFSET (GtkDataboxClass, selection_changed),
  367                       NULL, /* accumulator */
  368                       NULL, /* accumulator_data */
  369                       gtk_databox_marshal_VOID__POINTER,
  370                       G_TYPE_NONE,
  371                       1,
  372                       G_TYPE_POINTER);
  373 
  374     /**
  375       * GtkDatabox::selection-finalized:
  376       * @box: The #GtkDatabox widget in which the selection has been stopped.
  377       * @selection_values: The corners of the selection rectangle.
  378       *
  379       * This signal is emitted when the left mouse button
  380       * is released after a selection was started before.
  381       *
  382       * @see_also: #GtkDatabox::selection-changed
  383       */
  384     gtk_databox_signals[SELECTION_FINALIZED_SIGNAL] =
  385         g_signal_new ("selection-finalized",
  386                       G_TYPE_FROM_CLASS (gobject_class),
  387                       G_SIGNAL_RUN_FIRST,
  388                       G_STRUCT_OFFSET (GtkDataboxClass, selection_finalized),
  389                       NULL, /* accumulator */
  390                       NULL, /* accumulator_data */
  391                       gtk_databox_marshal_VOID__POINTER,
  392                       G_TYPE_NONE,
  393                       1,
  394                       G_TYPE_POINTER);
  395 
  396     /**
  397       * GtkDatabox::selection-canceled:
  398       * @box: The #GtkDatabox widget which zoomed in or out.
  399       *
  400       * This signal is emitted after a right click outside
  401       * a selection rectangle.
  402       */
  403     gtk_databox_signals[SELECTION_CANCELED_SIGNAL] =
  404         g_signal_new ("selection-canceled",
  405                       G_TYPE_FROM_CLASS (gobject_class),
  406                       G_SIGNAL_RUN_FIRST,
  407                       G_STRUCT_OFFSET (GtkDataboxClass, selection_canceled),
  408                       NULL, /* accumulator */
  409                       NULL, /* accumulator_data */
  410                       gtk_databox_marshal_VOID__VOID,
  411                       G_TYPE_NONE,
  412                       0);
  413 
  414     class->zoomed = NULL;
  415     class->selection_started = NULL;
  416     class->selection_changed = NULL;
  417     class->selection_finalized = NULL;
  418     class->selection_canceled = NULL;
  419 }
  420 
  421 static void
  422 gtk_databox_init (GtkDatabox * box) {
  423     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  424 
  425     priv->backing_surface = NULL;
  426     priv->scale_type_x = GTK_DATABOX_SCALE_LINEAR;
  427     priv->scale_type_y = GTK_DATABOX_SCALE_LINEAR;
  428     priv->translation_factor_x = 0;
  429     priv->translation_factor_y = 0;
  430     priv->enable_selection = TRUE;
  431     priv->enable_zoom = TRUE;
  432     priv->ruler_x = NULL;
  433     priv->ruler_y = NULL;
  434     priv->graphs = NULL;
  435     priv->zoom_limit = 0.01;
  436     priv->selection_active = FALSE;
  437     priv->selection_finalized = FALSE;
  438     priv->box_shadow=GTK_SHADOW_NONE;
  439     priv->cssp = gtk_css_provider_new ();
  440 
  441     /* gtk_databox_set_total_limits(box, -1., 1., 1., -1.); */
  442     priv->visible_left = priv->total_left = -1.0;
  443     priv->visible_right = priv->total_right = 1.0;
  444     priv->visible_top = priv->total_top = 1.0;
  445     priv->visible_bottom = priv->total_bottom = -1.0;
  446     gtk_databox_set_adjustment_x (box, NULL);
  447     gtk_databox_set_adjustment_y (box, NULL);
  448     /*gtk_databox_set_total_limits(box, -1., 1., 1., -1.);*/
  449     g_object_set(GTK_WIDGET(box), "expand", TRUE, NULL);
  450 }
  451 
  452 /**
  453  * gtk_databox_new
  454  *
  455  * Creates a new #GtkDatabox widget.
  456  *
  457  * Return value: The new #GtkDatabox widget.
  458  *
  459  */
  460 GtkWidget *
  461 gtk_databox_new (void) {
  462     return g_object_new (GTK_TYPE_DATABOX, NULL);
  463 }
  464 
  465 /**
  466  * gtk_databox_get_graphs
  467  * @box: A #GtkDatabox widget
  468  *
  469  * Return a list of graphs that were previously added to @box.
  470  *
  471  * Return value: A #GList that contains all graphs
  472  */
  473 GList *
  474 gtk_databox_get_graphs (GtkDatabox *box) 
  475 {
  476     g_return_val_if_fail (GTK_IS_DATABOX (box), (GList*)-1);
  477     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  478     return priv->graphs;
  479 }
  480 
  481 static gint
  482 gtk_databox_motion_notify (GtkWidget *widget, GdkEventMotion *event) {
  483     GtkDatabox *box = GTK_DATABOX (widget);
  484     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  485     GdkModifierType state;
  486     gint x;
  487     gint y;
  488 
  489     if (event->is_hint) {
  490         gdk_window_get_device_position (gtk_widget_get_window(widget), event->device, &x, &y, &state);
  491     } else {
  492         state = event->state;
  493         x = event->x;
  494         y = event->y;
  495     }
  496 
  497     if (state & GDK_BUTTON1_MASK
  498             && priv->enable_selection && !priv->selection_finalized) {
  499         GdkRectangle rect;
  500         gint width;
  501         gint height;
  502 
  503         width = gdk_window_get_width(gtk_widget_get_window(widget));
  504         height = gdk_window_get_height(gtk_widget_get_window(widget));
  505         x = MAX (0, MIN (width - 1, x));
  506         y = MAX (0, MIN (height - 1, y));
  507 
  508         if (priv->selection_active) {
  509             /* Clear current selection from backing_surface */
  510             gtk_databox_draw_selection (box, TRUE);
  511         } else {
  512             priv->selection_active = TRUE;
  513             priv->marked.x = x;
  514             priv->marked.y = y;
  515             priv->select.x = x;
  516             priv->select.y = y;
  517             gtk_databox_calculate_selection_values (box);
  518             g_signal_emit (G_OBJECT (box),
  519                            gtk_databox_signals[SELECTION_STARTED_SIGNAL], 0,
  520                            &priv->selectionValues);
  521         }
  522 
  523         /* Determine the exposure rectangle (covering old selection and new) */
  524         rect.x = MIN (MIN (priv->marked.x, priv->select.x), x);
  525         rect.y = MIN (MIN (priv->marked.y, priv->select.y), y);
  526         rect.width = MAX (MAX (priv->marked.x, priv->select.x), x)
  527                      - rect.x + 1;
  528         rect.height = MAX (MAX (priv->marked.y, priv->select.y), y)
  529                       - rect.y + 1;
  530 
  531         priv->select.x = x;
  532         priv->select.y = y;
  533 
  534         /* Draw new selection */
  535         gtk_databox_draw_selection (box, FALSE);
  536 
  537         gtk_databox_calculate_selection_values (box);
  538         g_signal_emit (G_OBJECT (box),
  539                        gtk_databox_signals[SELECTION_CHANGED_SIGNAL],
  540                        0, &priv->selectionValues);
  541     }
  542 
  543     return FALSE;
  544 }
  545 
  546 static void
  547 gtk_databox_set_property (GObject * object,
  548                           guint property_id,
  549                           const GValue * value, GParamSpec * pspec) {
  550     GtkDatabox *box = GTK_DATABOX (object);
  551     switch (property_id) {
  552     case ENABLE_SELECTION:
  553         gtk_databox_set_enable_selection (box, g_value_get_boolean (value));
  554         break;
  555     case ENABLE_ZOOM:
  556         gtk_databox_set_enable_zoom (box, g_value_get_boolean (value));
  557         break;
  558     case ADJUSTMENT_X:
  559         gtk_databox_set_adjustment_x (box, g_value_get_object (value));
  560         break;
  561     case ADJUSTMENT_Y:
  562         gtk_databox_set_adjustment_y (box, g_value_get_object (value));
  563         break;
  564     case RULER_X:
  565         gtk_databox_set_ruler_x (box, g_value_get_object (value));
  566         break;
  567     case RULER_Y:
  568         gtk_databox_set_ruler_y (box, g_value_get_object (value));
  569         break;
  570     case SCALE_TYPE_X:
  571         gtk_databox_set_scale_type_x (box, g_value_get_enum (value));
  572         break;
  573     case SCALE_TYPE_Y:
  574         gtk_databox_set_scale_type_y (box, g_value_get_enum (value));
  575         break;
  576     case BOX_SHADOW:
  577         gtk_databox_set_box_shadow (box, (GtkShadowType) g_value_get_uint (value));
  578         break;
  579     default:
  580         /* We don't have any other property... */
  581         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  582         break;
  583     }
  584 }
  585 
  586 static void
  587 gtk_databox_get_property (GObject * object,
  588                           guint property_id,
  589                           GValue * value, GParamSpec * pspec) {
  590     GtkDatabox *box = GTK_DATABOX (object);
  591     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  592 
  593     switch (property_id) {
  594     case ENABLE_SELECTION:
  595         g_value_set_boolean (value, gtk_databox_get_enable_selection (box));
  596         break;
  597     case ENABLE_ZOOM:
  598         g_value_set_boolean (value, gtk_databox_get_enable_zoom (box));
  599         break;
  600     case ADJUSTMENT_X:
  601         g_value_set_object (value, G_OBJECT (gtk_databox_get_adjustment_x (box)));
  602         break;
  603     case ADJUSTMENT_Y:
  604         g_value_set_object (value, G_OBJECT (gtk_databox_get_adjustment_y (box)));
  605         break;
  606     case RULER_X:
  607         g_value_set_object (value, G_OBJECT (gtk_databox_get_ruler_x (box)));
  608         break;
  609     case RULER_Y:
  610         g_value_set_object (value, G_OBJECT (gtk_databox_get_ruler_y (box)));
  611         break;
  612     case SCALE_TYPE_X:
  613         g_value_set_enum (value, gtk_databox_get_scale_type_x (box));
  614         break;
  615     case SCALE_TYPE_Y:
  616         g_value_set_enum (value, gtk_databox_get_scale_type_y (box));
  617         break;
  618     case BOX_SHADOW:
  619         g_value_set_uint (value, priv->box_shadow);
  620         break;
  621     default:
  622         /* We don't have any other property... */
  623         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  624         break;
  625     }
  626 }
  627 
  628 
  629 static void
  630 gtk_databox_realize (GtkWidget * widget) {
  631     GtkDatabox *box;
  632     GdkWindowAttr attributes;
  633     gint attributes_mask;
  634     GtkAllocation allocation;
  635     GtkStyleContext *stylecontext;
  636 
  637     box = GTK_DATABOX (widget);
  638     gtk_widget_set_realized(widget, TRUE);
  639     gtk_widget_get_allocation(widget, &allocation);
  640 
  641     attributes.window_type = GDK_WINDOW_CHILD;
  642     attributes.x = allocation.x;
  643     attributes.y = allocation.y;
  644     attributes.width = allocation.width;
  645     attributes.height = allocation.height;
  646     attributes.wclass = GDK_INPUT_OUTPUT;
  647     attributes.visual = gtk_widget_get_visual (widget);
  648     attributes.event_mask = gtk_widget_get_events (widget);
  649     attributes.event_mask |= (GDK_EXPOSURE_MASK |
  650                   GDK_SCROLL_MASK |
  651                   GDK_TOUCH_MASK |
  652                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
  653                               GDK_POINTER_MOTION_MASK |
  654                               GDK_POINTER_MOTION_HINT_MASK);
  655 
  656     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
  657 
  658     gtk_widget_set_window(widget,
  659         gdk_window_new (gtk_widget_get_parent_window (widget), &attributes,
  660                         attributes_mask));
  661     gdk_window_set_user_data (gtk_widget_get_window(widget), box);
  662 
  663     stylecontext = gtk_widget_get_style_context(widget);
  664     gtk_style_context_add_class(stylecontext, GTK_STYLE_CLASS_BACKGROUND);
  665 
  666     gtk_databox_create_backing_surface (box);
  667 }
  668 
  669 static void
  670 gtk_databox_unrealize (GtkWidget * widget) {
  671     GtkDatabox *box = GTK_DATABOX (widget);
  672     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  673     gtk_widget_set_realized(widget, FALSE);
  674 
  675     if (priv->backing_surface)
  676         cairo_surface_destroy (priv->backing_surface);
  677     priv->backing_surface=NULL;
  678     if (priv->adj_x)
  679         g_object_unref (priv->adj_x);
  680     priv->adj_x=NULL;
  681     if (priv->adj_y)
  682         g_object_unref (priv->adj_y);
  683 
  684     g_list_free (priv->graphs);
  685     priv->graphs=NULL;
  686 
  687     if (GTK_WIDGET_CLASS (gtk_databox_parent_class)->unrealize)
  688         (*GTK_WIDGET_CLASS (gtk_databox_parent_class)->unrealize) (widget);
  689 
  690 }
  691 
  692 
  693 /**
  694  * gtk_databox_set_enable_selection
  695  * @box: A #GtkDatabox widget
  696  * @enable: Whether selection via mouse is enabled or not.
  697  *
  698  * Setter function for the #GtkDatabox:enable-selection property.
  699  *
  700  */
  701 void
  702 gtk_databox_set_enable_selection (GtkDatabox * box, gboolean enable) {
  703     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  704     g_return_if_fail (GTK_IS_DATABOX (box));
  705 
  706     priv->enable_selection = enable;
  707     if (priv->selection_active) {
  708         gtk_databox_selection_cancel (box);
  709     }
  710 
  711     g_object_notify (G_OBJECT (box), "enable-selection");
  712 }
  713 
  714 /**
  715  * gtk_databox_set_enable_zoom
  716  * @box: A #GtkDatabox widget
  717  * @enable: Whether zoom via mouse is enabled or not.
  718  *
  719  * Setter function for the #GtkDatabox:enable-zoom property.
  720  *
  721  */
  722 void
  723 gtk_databox_set_enable_zoom (GtkDatabox * box, gboolean enable) {
  724     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  725     g_return_if_fail (GTK_IS_DATABOX (box));
  726 
  727     priv->enable_zoom = enable;
  728 
  729     g_object_notify (G_OBJECT (box), "enable-zoom");
  730 }
  731 
  732 /**
  733  * gtk_databox_set_adjustment_x
  734  * @box: A #GtkDatabox widget
  735  * @adj: A #GtkAdjustment object
  736  *
  737  * Setter function for the #GtkDatabox:adjustment-x property. Normally, it should not be
  738  * required to use this function, see property documentation.
  739  *
  740  */
  741 void
  742 gtk_databox_set_adjustment_x (GtkDatabox * box, GtkAdjustment * adj) {
  743     gdouble page_size_x;
  744     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  745 
  746     g_return_if_fail (GTK_IS_DATABOX (box));
  747 
  748     if (!adj)
  749         adj = GTK_ADJUSTMENT(gtk_adjustment_new (0, 0, 0, 0, 0, 0));
  750 
  751     g_return_if_fail (GTK_IS_ADJUSTMENT (adj));
  752 
  753     if (priv->adj_x) {
  754         /* @@@ Do we need to disconnect from the signals here? */
  755         g_object_unref (priv->adj_x);
  756         if (g_object_is_floating(G_OBJECT(priv->adj_x)))
  757             g_object_ref_sink (priv->adj_x);
  758     }
  759 
  760     priv->adj_x = adj;
  761     g_object_ref (priv->adj_x);
  762 
  763     /* We always scroll from 0 to 1.0 */
  764     if (priv->total_left != priv->total_right)
  765     {
  766         page_size_x = gtk_databox_get_page_size_x(box);
  767     } else {
  768         page_size_x = 1.0;
  769     }
  770 
  771     gtk_adjustment_configure(priv->adj_x,
  772         gtk_databox_get_offset_x (box), /* value */
  773         0.0, /* lower */
  774         1.0, /* upper */
  775         page_size_x / 20, /* step_increment */
  776         page_size_x * 0.9, /* page_increment */
  777         page_size_x); /* page_size */
  778 
  779     g_signal_connect_swapped (G_OBJECT (priv->adj_x), "value_changed",
  780                               G_CALLBACK
  781                               (gtk_databox_adjustment_value_changed), box);
  782 
  783     g_object_notify (G_OBJECT (box), "adjustment-x");
  784 }
  785 
  786 /**
  787  * gtk_databox_set_adjustment_y
  788  * @box: A #GtkDatabox widget
  789  * @adj: A #GtkAdjustment object
  790  *
  791  * Setter function for the #GtkDatabox:adjustment-y property. Normally, it should not be
  792  * required to use this function, see property documentation.
  793  *
  794  */
  795 void
  796 gtk_databox_set_adjustment_y (GtkDatabox * box, GtkAdjustment * adj) {
  797     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  798     gdouble page_size_y;
  799 
  800     g_return_if_fail (GTK_IS_DATABOX (box));
  801     if (!adj)
  802         adj = GTK_ADJUSTMENT(gtk_adjustment_new (0, 0, 0, 0, 0, 0));
  803 
  804     g_return_if_fail (GTK_IS_ADJUSTMENT (adj));
  805 
  806     if (priv->adj_y) {
  807         /* @@@ Do we need to disconnect from the signals here? */
  808         g_object_unref (priv->adj_y);
  809         if (g_object_is_floating(G_OBJECT(priv->adj_y)))
  810             g_object_ref_sink (priv->adj_y);
  811     }
  812 
  813     priv->adj_y = adj;
  814     g_object_ref (priv->adj_y);
  815 
  816     /* We always scroll from 0 to 1.0 */
  817     if (priv->total_top != priv->total_bottom)
  818     {
  819         page_size_y = gtk_databox_get_page_size_y(box);
  820     } else {
  821         page_size_y = 1.0;
  822     }
  823 
  824     gtk_adjustment_configure(priv->adj_y,
  825         gtk_databox_get_offset_y (box), /* value */
  826         0.0, /* lower */
  827         1.0, /* upper */
  828         page_size_y / 20, /* step_increment */
  829         page_size_y * 0.9, /* page_increment */
  830         page_size_y); /* page_size */
  831 
  832     g_signal_connect_swapped (G_OBJECT (priv->adj_y), "value_changed",
  833                               G_CALLBACK
  834                               (gtk_databox_adjustment_value_changed), box);
  835 
  836     g_object_notify (G_OBJECT (box), "adjustment-y");
  837 }
  838 
  839 /**
  840  * gtk_databox_set_ruler_x
  841  * @box: A #GtkDatabox widget
  842  * @ruler: A #GtkDataboxRuler object
  843  *
  844  * Setter function for the #GtkDatabox:ruler-x property.
  845  *
  846  */
  847 void
  848 gtk_databox_set_ruler_x (GtkDatabox * box, GtkDataboxRuler * ruler) {
  849     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  850 
  851     g_return_if_fail (GTK_IS_DATABOX (box));
  852     g_return_if_fail (ruler == NULL || GTK_DATABOX_IS_RULER (ruler));
  853     g_return_if_fail (ruler == NULL || gtk_databox_ruler_get_orientation(ruler) == GTK_ORIENTATION_HORIZONTAL);
  854 
  855     if (priv->ruler_x) {
  856         /* @@@ Do we need to disconnect the signals here? */
  857         /* @@@ Do we need to call object_ref and object_unref here and for adjustments? */
  858     }
  859 
  860     priv->ruler_x = ruler;
  861 
  862     if (GTK_DATABOX_IS_RULER (ruler)) {
  863         gtk_databox_ruler_set_scale_type (ruler, priv->scale_type_x);
  864 
  865         gtk_databox_ruler_update (box);
  866         g_signal_connect_swapped (box, "motion_notify_event",
  867                                   G_CALLBACK (GTK_WIDGET_GET_CLASS
  868                                               (priv->ruler_x)->
  869                                               motion_notify_event),
  870                                   G_OBJECT (priv->ruler_x));
  871     }
  872 
  873     g_object_notify (G_OBJECT (box), "ruler-x");
  874 }
  875 
  876 /**
  877  * gtk_databox_set_ruler_y
  878  * @box: A #GtkDatabox widget
  879  * @ruler: An #GtkDataboxRuler object
  880  *
  881  * Setter function for the #GtkDatabox:ruler-y property.
  882  *
  883  */
  884 void
  885 gtk_databox_set_ruler_y (GtkDatabox * box, GtkDataboxRuler * ruler) {
  886     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  887 
  888     g_return_if_fail (GTK_IS_DATABOX (box));
  889     g_return_if_fail (ruler == NULL || GTK_DATABOX_IS_RULER (ruler));
  890     g_return_if_fail (ruler == NULL || gtk_databox_ruler_get_orientation(ruler) == GTK_ORIENTATION_VERTICAL);
  891 
  892     if (priv->ruler_y) {
  893         /* @@@ Do we need to disconnect the signals here? */
  894         /* @@@ Do we need to call object_ref and object_unref here and for adjustments? */
  895     }
  896 
  897     priv->ruler_y = ruler;
  898 
  899     if (GTK_DATABOX_IS_RULER (ruler)) {
  900         gtk_databox_ruler_set_scale_type (ruler,
  901                                           priv->scale_type_y);
  902 
  903         gtk_databox_ruler_update (box);
  904         g_signal_connect_swapped (box, "motion_notify_event",
  905                                   G_CALLBACK (GTK_WIDGET_GET_CLASS
  906                                               (priv->ruler_y)->
  907                                               motion_notify_event),
  908                                   G_OBJECT (priv->ruler_y));
  909     }
  910 
  911     g_object_notify (G_OBJECT (box), "ruler-y");
  912 }
  913 
  914 /**
  915  * gtk_databox_set_scale_type_x
  916  * @box: A #GtkDatabox widget
  917  * @scale_type: An #GtkDataboxScaleType (linear or logarithmic)
  918  *
  919  * Setter function for the #GtkDatabox:scale-type-x property.
  920  *
  921  */
  922 void
  923 gtk_databox_set_scale_type_x (GtkDatabox * box,
  924                               GtkDataboxScaleType scale_type) {
  925     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  926     priv->scale_type_x = scale_type;
  927 
  928     if (priv->ruler_x)
  929         gtk_databox_ruler_set_scale_type (priv->ruler_x, scale_type);
  930 
  931     g_object_notify (G_OBJECT (box), "scale-type-x");
  932 }
  933 
  934 /**
  935  * gtk_databox_set_scale_type_y
  936  * @box: A #GtkDatabox widget
  937  * @scale_type: An #GtkDataboxScaleType (linear or logarithmic)
  938  *
  939  * Setter function for the #GtkDatabox:scale-type-y property.
  940  *
  941  */
  942 void
  943 gtk_databox_set_scale_type_y (GtkDatabox * box,
  944                               GtkDataboxScaleType scale_type) {
  945     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  946     priv->scale_type_y = scale_type;
  947 
  948     if (priv->ruler_y)
  949         gtk_databox_ruler_set_scale_type (priv->ruler_y, scale_type);
  950 
  951     g_object_notify (G_OBJECT (box), "scale-type-y");
  952 }
  953 
  954 /**
  955  * gtk_databox_set_box_shadow:
  956  * @box: a #GtkDatabox widget.
  957  * @which_shadow: How to render the box shadow on the GtkDatabox edges.
  958  *
  959  * Sets the shadow type when using gtk_paint_box. This will draw the desired edge shadow.
  960  **/
  961 void
  962 gtk_databox_set_box_shadow(GtkDatabox * box, GtkShadowType which_shadow) {
  963     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  964 
  965     g_return_if_fail (GTK_IS_DATABOX (box));
  966     g_return_if_fail (which_shadow<=GTK_SHADOW_ETCHED_OUT);
  967 
  968     if (priv->box_shadow!=which_shadow) {
  969         priv->box_shadow=which_shadow;
  970         if (gtk_widget_is_drawable (GTK_WIDGET (box)))
  971             gtk_widget_queue_draw (GTK_WIDGET (box));
  972     }
  973 }
  974 
  975 
  976 /**
  977  * gtk_databox_get_enable_selection
  978  * @box: A #GtkDatabox widget.
  979  *
  980  * Getter function for the #GtkDatabox:enable-selection property.
  981  *
  982  * Return value: The #GtkDatabox:enable-selection property value.
  983  *
  984  */
  985 gboolean
  986 gtk_databox_get_enable_selection (GtkDatabox * box) {
  987     g_return_val_if_fail (GTK_IS_DATABOX (box), FALSE);
  988     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
  989     return priv->enable_selection;
  990 }
  991 
  992 /**
  993  * gtk_databox_get_enable_zoom
  994  * @box: A #GtkDatabox widget.
  995  *
  996  * Getter function for the #GtkDatabox:enable-zoom property.
  997  *
  998  * Return value: The #GtkDatabox:enable-zoom property value.
  999  *
 1000  */
 1001 gboolean
 1002 gtk_databox_get_enable_zoom (GtkDatabox * box) {
 1003     g_return_val_if_fail (GTK_IS_DATABOX (box), FALSE);
 1004     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1005     return priv->enable_zoom;
 1006 }
 1007 
 1008 /**
 1009  * gtk_databox_get_adjustment_x
 1010  * @box: A #GtkDatabox widget.
 1011  *
 1012  * Getter function for the #GtkDatabox:adjustment-x property.
 1013  *
 1014  * Return value: The #GtkDatabox:adjustment-x property value.
 1015  *
 1016  */
 1017 GtkAdjustment *
 1018 gtk_databox_get_adjustment_x (GtkDatabox * box) {
 1019     g_return_val_if_fail (GTK_IS_DATABOX (box), NULL);
 1020     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1021     return priv->adj_x;
 1022 }
 1023 
 1024 /**
 1025  * gtk_databox_get_adjustment_y
 1026  * @box: A #GtkDatabox widget.
 1027  *
 1028  * Getter function for the #GtkDatabox:adjustment-y property.
 1029  *
 1030  * Return value: The #GtkDatabox:adjustment-y property value.
 1031  *
 1032  */
 1033 GtkAdjustment *
 1034 gtk_databox_get_adjustment_y (GtkDatabox * box) {
 1035     g_return_val_if_fail (GTK_IS_DATABOX (box), NULL);
 1036     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1037     return priv->adj_y;
 1038 }
 1039 
 1040 /**
 1041  * gtk_databox_get_ruler_x
 1042  * @box: A #GtkDatabox widget.
 1043  *
 1044  * Getter function for the #GtkDatabox:ruler-x property.
 1045  *
 1046  * Return value: The #GtkDatabox:ruler-x property value.
 1047  *
 1048  */
 1049 GtkDataboxRuler *
 1050 gtk_databox_get_ruler_x (GtkDatabox * box) {
 1051     g_return_val_if_fail (GTK_IS_DATABOX (box), NULL);
 1052     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1053     return priv->ruler_x;
 1054 }
 1055 
 1056 /**
 1057  * gtk_databox_get_ruler_y
 1058  * @box: A #GtkDatabox widget.
 1059  *
 1060  * Getter function for the #GtkDatabox:ruler-y property.
 1061  *
 1062  * Return value: The #GtkDatabox:ruler-y property value.
 1063  *
 1064  */
 1065 GtkDataboxRuler *
 1066 gtk_databox_get_ruler_y (GtkDatabox * box) {
 1067     g_return_val_if_fail (GTK_IS_DATABOX (box), NULL);
 1068     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1069     return priv->ruler_y;
 1070 }
 1071 
 1072 /**
 1073  * gtk_databox_get_scale_type_x
 1074  * @box: A #GtkDatabox widget.
 1075  *
 1076  * Getter function for the #GtkDatabox:scale-type-x property.
 1077  *
 1078  * Return value: The #GtkDatabox:scale-type-x property value.
 1079  *
 1080  */
 1081 GtkDataboxScaleType
 1082 gtk_databox_get_scale_type_x (GtkDatabox * box) {
 1083     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1084     return priv->scale_type_x;
 1085 }
 1086 
 1087 /**
 1088  * gtk_databox_get_scale_type_y
 1089  * @box: A #GtkDatabox widget.
 1090  *
 1091  * Getter function for the #GtkDatabox:scale-type-y property.
 1092  *
 1093  * Return value: The #GtkDatabox:scale-type-y property value.
 1094  *
 1095  */
 1096 GtkDataboxScaleType
 1097 gtk_databox_get_scale_type_y (GtkDatabox * box) {
 1098     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1099     return priv->scale_type_y;
 1100 }
 1101 
 1102 /**
 1103  * gtk_databox_get_box_shadow:
 1104  * @box: a #GtkDatabox widget
 1105  *
 1106  * Gets the type of shadow being rendered to the @box (GTK_SHADOW_NONE, GTK_SHADOW_IN, GTK_SHADOW_OUT, GTK_SHADOW_ETCHED_IN, GTK_SHADOW_ETCHED_OUT).
 1107  *
 1108  * Return value: The currently used shadow type of the @box, -1 on failure.
 1109  **/
 1110 GtkShadowType
 1111 gtk_databox_get_box_shadow(GtkDatabox * box) {
 1112 
 1113     g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
 1114     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1115     return priv->box_shadow;
 1116 }
 1117 
 1118 /**
 1119  * gtk_databox_set_bg_color:
 1120  * @box: a #GtkDatabox widget
 1121  * @bg_color: a color name, as used in CSS (html color)
 1122  *
 1123  * Convenience function to override the background color of @box, acording to @bg_color.
 1124  *
 1125  **/
 1126 void
 1127 gtk_databox_set_bg_color (GtkDatabox * box, gchar *bg_color) {
 1128     GtkWidget * widget;
 1129     GtkDataboxPrivate *priv;
 1130     GtkStyleContext *stylecontext;
 1131     gchar *css_bg_color;
 1132 
 1133     g_return_if_fail (GTK_IS_DATABOX (box));
 1134     widget = GTK_WIDGET (box);
 1135     priv = gtk_databox_get_instance_private (box);
 1136 
 1137     stylecontext = gtk_widget_get_style_context (widget);
 1138     gtk_style_context_remove_provider (stylecontext, GTK_STYLE_PROVIDER (priv->cssp));
 1139     css_bg_color = g_strdup_printf (".%s {background-color: %s;}", GTK_STYLE_CLASS_BACKGROUND, bg_color);
 1140     gtk_css_provider_load_from_data (priv->cssp, css_bg_color, -1, NULL);
 1141     gtk_style_context_add_provider (stylecontext, GTK_STYLE_PROVIDER (priv->cssp), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 1142     g_free (css_bg_color);
 1143 }
 1144 
 1145 static void
 1146 gtk_databox_calculate_translation_factors (GtkDatabox * box) {
 1147     /* @@@ Check for all external functions, if type checks are implemented! */
 1148     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1149     GtkWidget *widget = GTK_WIDGET(box);
 1150     GtkAllocation allocation;
 1151 
 1152     gtk_widget_get_allocation(widget, &allocation);
 1153     if (priv->scale_type_x == GTK_DATABOX_SCALE_LINEAR)
 1154         priv->translation_factor_x =
 1155             (gfloat)allocation.width / (priv->visible_right -
 1156                                         priv->visible_left);
 1157     else if (priv->scale_type_x == GTK_DATABOX_SCALE_LOG2)
 1158         priv->translation_factor_x =
 1159             (gfloat)allocation.width / log2 (priv->visible_right /
 1160                                              priv->visible_left);
 1161     else
 1162         priv->translation_factor_x =
 1163             (gfloat)allocation.width / log10 (priv->visible_right /
 1164                                               priv->visible_left);
 1165 
 1166     if (priv->scale_type_y == GTK_DATABOX_SCALE_LINEAR)
 1167         priv->translation_factor_y =
 1168             (gfloat)allocation.height / (priv->visible_bottom -
 1169                                          priv->visible_top);
 1170     else if (priv->scale_type_y == GTK_DATABOX_SCALE_LOG2)
 1171         priv->translation_factor_y =
 1172             (gfloat)allocation.height / log2 (priv->visible_bottom /
 1173                                               priv->visible_top);
 1174     else
 1175         priv->translation_factor_y =
 1176             (gfloat)allocation.height / log10 (priv->visible_bottom /
 1177                                                priv->visible_top);
 1178 }
 1179 
 1180 static void
 1181 gtk_databox_create_backing_surface(GtkDatabox * box) {
 1182     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1183     GtkAllocation allocation;
 1184     GtkWidget *widget;
 1185     GdkDrawingContext *drawc;
 1186     cairo_region_t *crr;
 1187     cairo_t *cr;
 1188     gint width;
 1189     gint height;
 1190 
 1191     widget = GTK_WIDGET (box);
 1192     gtk_widget_get_allocation(widget, &allocation);
 1193     width = allocation.width;
 1194     height = allocation.height;
 1195     if (priv->backing_surface) {
 1196        if ((width == priv->old_width) &&
 1197           (height == priv->old_height))
 1198           return;
 1199        cairo_surface_destroy (priv->backing_surface);
 1200    }
 1201 
 1202    priv->old_width = width;
 1203    priv->old_height = height;
 1204 
 1205    crr = gdk_window_get_visible_region (gtk_widget_get_window (widget));
 1206    drawc = gdk_window_begin_draw_frame (gtk_widget_get_window (widget), crr);
 1207    cr = gdk_drawing_context_get_cairo_context (drawc);
 1208 
 1209    priv->backing_surface = cairo_surface_create_similar(
 1210                                 cairo_get_target (cr),
 1211                                 CAIRO_CONTENT_COLOR,
 1212                                 width, height);
 1213    gdk_window_end_draw_frame (gtk_widget_get_window (widget), drawc);
 1214    cairo_region_destroy (crr);
 1215 }
 1216 
 1217 /**
 1218  * gtk_databox_get_backing_surface:
 1219  * @box: A #GtkDatabox widget
 1220  *
 1221  * This function returns the surface which is used by @box and its #GtkDataboxGraph objects
 1222  * for drawing operations before copying the result to the screen.
 1223  *
 1224  * The function is typically called by the #GtkDataboxGraph objects.
 1225  *
 1226  * Return value: Backing surface
 1227  */
 1228 cairo_surface_t *
 1229 gtk_databox_get_backing_surface(GtkDatabox * box) {
 1230     g_return_val_if_fail (GTK_IS_DATABOX (box), NULL);
 1231     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1232     return priv->backing_surface;
 1233 }
 1234 
 1235 static void
 1236 gtk_databox_size_allocate (GtkWidget * widget, GtkAllocation * allocation) {
 1237     GtkDatabox *box = GTK_DATABOX (widget);
 1238     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1239 
 1240     gtk_widget_set_allocation(widget, allocation);
 1241 
 1242     if (gtk_widget_get_window(widget))  /* don't move_resize an unrealized window */
 1243     {
 1244         gdk_window_move_resize (gtk_widget_get_window(widget),
 1245                                 allocation->x, allocation->y,
 1246                                 allocation->width, allocation->height);
 1247     }
 1248 
 1249     if (priv->selection_active) {
 1250         gtk_databox_selection_cancel (box);
 1251     }
 1252 
 1253     gtk_databox_calculate_translation_factors (box);
 1254 }
 1255 
 1256 static gint
 1257 gtk_databox_draw (GtkWidget * widget, cairo_t * cr) {
 1258     GtkDatabox *box = GTK_DATABOX (widget);
 1259     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1260     GList *list;
 1261     cairo_t *cr2;
 1262     GtkStyleContext *stylecontext = gtk_widget_get_style_context(widget);
 1263     GtkAllocation allocation;
 1264 
 1265     gtk_databox_create_backing_surface (box);
 1266 
 1267     cr2 = cairo_create(priv->backing_surface);
 1268     gtk_widget_get_allocation (widget, &allocation);
 1269     gtk_render_background (stylecontext, cr2, 0.0, 0.0, allocation.width, allocation.height);
 1270     cairo_destroy(cr2);
 1271 
 1272     list = g_list_last (priv->graphs);
 1273     while (list) {
 1274         if (list->data)
 1275             gtk_databox_graph_draw (GTK_DATABOX_GRAPH (list->data), box);
 1276         list = g_list_previous (list);
 1277     }
 1278 
 1279     if (priv->selection_active)
 1280         gtk_databox_draw_selection (box, TRUE);
 1281 
 1282     cairo_set_source_surface (cr, priv->backing_surface, 0, 0);
 1283     cairo_paint(cr);
 1284     /* the following was removed - unsure if it creates problems
 1285     gtk_databox_draw_selection (box, FALSE);
 1286     */
 1287 
 1288     return FALSE;
 1289 }
 1290 
 1291 static void
 1292 gtk_databox_calculate_selection_values (GtkDatabox * box) {
 1293     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1294     priv->selectionValues.x1 =
 1295         gtk_databox_pixel_to_value_x (box, priv->marked.x);
 1296     priv->selectionValues.x2 =
 1297         gtk_databox_pixel_to_value_x (box, priv->select.x);
 1298     priv->selectionValues.y1 =
 1299         gtk_databox_pixel_to_value_y (box, priv->marked.y);
 1300     priv->selectionValues.y2 =
 1301         gtk_databox_pixel_to_value_y (box, priv->select.y);
 1302 }
 1303 
 1304 static gint
 1305 gtk_databox_button_press (GtkWidget * widget, GdkEventButton * event) {
 1306     GtkDatabox *box = GTK_DATABOX (widget);
 1307     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1308 
 1309     if (event->type != GDK_BUTTON_PRESS && event->type != GDK_2BUTTON_PRESS)
 1310         return FALSE;
 1311 
 1312     priv->selection_finalized = FALSE;
 1313     if ((event->button == 1 || event->button == 2) & !(event->type==GDK_2BUTTON_PRESS)) {
 1314         if (priv->selection_active) {
 1315             if (event->x > MIN (priv->marked.x, priv->select.x)
 1316                     && event->x < MAX (priv->marked.x, priv->select.x)
 1317                     && event->y > MIN (priv->marked.y, priv->select.y)
 1318                     && event->y < MAX (priv->marked.y, priv->select.y)) {
 1319                 gtk_databox_zoom_to_selection (box);
 1320             } else {
 1321                 gtk_databox_selection_cancel (box);
 1322             }
 1323             priv->marked.x = priv->select.x = event->x;
 1324             priv->marked.y = priv->select.y = event->y;
 1325             gtk_databox_calculate_selection_values (box);
 1326         }
 1327     }
 1328 
 1329     if ((event->button == 3) || (event->button == 1 && event->type==GDK_2BUTTON_PRESS)) {
 1330         if (event->state & GDK_SHIFT_MASK) {
 1331             gtk_databox_zoom_home (box);
 1332         } else {
 1333             gtk_databox_zoom_out (box);
 1334         }
 1335     }
 1336 
 1337     return FALSE;
 1338 }
 1339 
 1340 static gint
 1341 gtk_databox_button_release (GtkWidget * widget, GdkEventButton * event) {
 1342     GtkDatabox *box = GTK_DATABOX (widget);
 1343     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1344 
 1345     if (event->type != GDK_BUTTON_RELEASE)
 1346         return FALSE;
 1347 
 1348     if (priv->selection_active) {
 1349         priv->selection_finalized = TRUE;
 1350 
 1351         g_signal_emit (G_OBJECT (box),
 1352                        gtk_databox_signals[SELECTION_FINALIZED_SIGNAL],
 1353                        0, &priv->selectionValues);
 1354     }
 1355 
 1356     return FALSE;
 1357 }
 1358 
 1359 static gint
 1360 gtk_databox_scroll_event (GtkWidget *widget, GdkEventScroll *event) {
 1361     GtkDatabox  *box = GTK_DATABOX (widget);
 1362     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1363 
 1364     if (event->state & GDK_CONTROL_MASK && priv->enable_zoom) {
 1365         if (event->direction == GDK_SCROLL_DOWN) {
 1366             gtk_databox_zoom_out(box);
 1367         } else if (event->direction == GDK_SCROLL_UP &&
 1368                    (gtk_adjustment_get_page_size(priv->adj_x) / 2) >= priv->zoom_limit &&
 1369                    (gtk_adjustment_get_page_size(priv->adj_y) / 2) >= priv->zoom_limit) {
 1370             gdouble       x_val, y_val;
 1371             gdouble       x_proportion, y_proportion;
 1372 
 1373             x_val = gtk_databox_pixel_to_value_x(box, event->x);
 1374             y_val = gtk_databox_pixel_to_value_y(box, event->y);
 1375 
 1376             if (priv->scale_type_x == GTK_DATABOX_SCALE_LINEAR) {
 1377                 x_proportion = (x_val - priv->total_left) /
 1378                                (priv->total_right - priv->total_left);
 1379             } else {
 1380                 x_proportion = log(x_val/priv->total_left) /
 1381                                log(priv->total_right / priv->total_left);
 1382             }
 1383 
 1384             if (priv->scale_type_y == GTK_DATABOX_SCALE_LINEAR) {
 1385                 y_proportion = (y_val - priv->total_top) /
 1386                                (priv->total_bottom - priv->total_top);
 1387             } else {
 1388                 y_proportion = log(y_val/priv->total_top) /
 1389                                log(priv->total_bottom / priv->total_top);
 1390             }
 1391 
 1392             g_object_freeze_notify(G_OBJECT(priv->adj_x));
 1393             gtk_adjustment_set_page_size(priv->adj_x, gtk_adjustment_get_page_size(priv->adj_x) / 2);
 1394             gtk_adjustment_set_value(priv->adj_x, (x_proportion +
 1395                                        gtk_adjustment_get_value(priv->adj_x)) / 2);
 1396             g_object_thaw_notify(G_OBJECT(priv->adj_x));
 1397 
 1398             g_object_freeze_notify(G_OBJECT(priv->adj_y));
 1399             gtk_adjustment_set_page_size(priv->adj_y, gtk_adjustment_get_page_size(priv->adj_y) / 2);
 1400             gtk_adjustment_set_value(priv->adj_y, (y_proportion +
 1401                                        gtk_adjustment_get_value(priv->adj_y)) / 2);
 1402             g_object_thaw_notify(G_OBJECT(priv->adj_y));
 1403 
 1404             gtk_databox_calculate_visible_limits(box);
 1405             gtk_databox_zoomed (box);
 1406         }
 1407     } else {
 1408         GtkAdjustment *adj;
 1409         gdouble delta = 0.0, new_value;
 1410 
 1411         if ((event->direction == GDK_SCROLL_UP ||
 1412                 event->direction == GDK_SCROLL_DOWN) &&
 1413                 !(event->state & GDK_SHIFT_MASK)) {
 1414             adj = priv->adj_y;
 1415         } else {
 1416             adj = priv->adj_x;
 1417         }
 1418 
 1419         switch (event->direction) {
 1420         case GDK_SCROLL_UP:
 1421         case GDK_SCROLL_SMOOTH:
 1422         case GDK_SCROLL_LEFT:
 1423             delta = 0 - gtk_adjustment_get_step_increment(adj);
 1424             break;
 1425         case GDK_SCROLL_DOWN:
 1426         case GDK_SCROLL_RIGHT:
 1427             delta = gtk_adjustment_get_step_increment(adj);
 1428             break;
 1429         }
 1430 
 1431         new_value = CLAMP (gtk_adjustment_get_value(adj) + delta, gtk_adjustment_get_lower(adj),
 1432                            gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj));
 1433         gtk_adjustment_set_value(adj, new_value);
 1434     }
 1435 
 1436     return FALSE;
 1437 }
 1438 
 1439 static void
 1440 gtk_databox_selection_cancel (GtkDatabox * box) {
 1441     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1442 
 1443     /* There is no active selection after cancellation */
 1444     priv->selection_active = FALSE;
 1445 
 1446     /* Only active selections can be stopped */
 1447     priv->selection_finalized = FALSE;
 1448 
 1449     /* Remove selection box */
 1450     gtk_databox_draw_selection (box, TRUE);
 1451 
 1452     /* Let everyone know that the selection has been canceled */
 1453     g_signal_emit (G_OBJECT (box),
 1454                    gtk_databox_signals[SELECTION_CANCELED_SIGNAL], 0);
 1455 }
 1456 
 1457 
 1458 static void
 1459 gtk_databox_zoomed (GtkDatabox * box) {
 1460     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1461 
 1462     g_return_if_fail(GTK_IS_DATABOX(box));
 1463     g_return_if_fail(GTK_IS_ADJUSTMENT(priv->adj_x));
 1464     g_return_if_fail(GTK_IS_ADJUSTMENT(priv->adj_y));
 1465 
 1466     priv->selection_active = FALSE;
 1467     priv->selection_finalized = FALSE;
 1468 
 1469 #ifndef GTK3_18
 1470     gtk_adjustment_changed (priv->adj_x);
 1471     gtk_adjustment_changed (priv->adj_y);
 1472 #endif
 1473 
 1474     gtk_widget_queue_draw (GTK_WIDGET(box));
 1475 
 1476     g_signal_emit (G_OBJECT (box),
 1477                    gtk_databox_signals[ZOOMED_SIGNAL], 0, NULL);
 1478 }
 1479 
 1480 /**
 1481  * gtk_databox_zoom_to_selection:
 1482  * @box: A #GtkDatabox widget
 1483  *
 1484  * This is equivalent to left-clicking into the selected area.
 1485  *
 1486  * This function works, if the attribute #enable-zoom is set to #TRUE. Calling the function
 1487  * then zooms to the area selected with the mouse.
 1488  *
 1489  * Side effect: The @box emits #GtkDatabox::zoomed.
 1490  */
 1491 void
 1492 gtk_databox_zoom_to_selection (GtkDatabox * box) {
 1493     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1494     GtkWidget *widget;
 1495     GtkAllocation allocation;
 1496     gdouble temp_value, temp_page_size;
 1497 
 1498     g_return_if_fail(GTK_IS_DATABOX(box));
 1499 
 1500     widget = GTK_WIDGET (box);
 1501     gtk_widget_get_allocation(widget, &allocation);
 1502 
 1503     if (!priv->enable_zoom) {
 1504         gtk_databox_selection_cancel (box);
 1505         return;
 1506     }
 1507 
 1508     g_object_freeze_notify(G_OBJECT(priv->adj_x));
 1509     g_object_freeze_notify(G_OBJECT(priv->adj_y));
 1510 
 1511     temp_value = gtk_adjustment_get_value(priv->adj_x);
 1512     temp_value += (gdouble) (MIN (priv->marked.x, priv->select.x))
 1513                                * gtk_adjustment_get_page_size(priv->adj_x)
 1514                                / allocation.width;
 1515     temp_page_size = gtk_adjustment_get_page_size(priv->adj_x);
 1516     temp_page_size *=
 1517         (gdouble) (ABS (priv->marked.x - priv->select.x) + 1)
 1518         / allocation.width;
 1519 
 1520     gtk_adjustment_set_page_size(priv->adj_x, temp_page_size);
 1521     gtk_adjustment_set_value(priv->adj_x, temp_value);
 1522 
 1523     temp_value = gtk_adjustment_get_value(priv->adj_y);
 1524     temp_value += (gdouble) (MIN (priv->marked.y, priv->select.y))
 1525                                * gtk_adjustment_get_page_size(priv->adj_y)
 1526                                / allocation.height;
 1527     temp_page_size = gtk_adjustment_get_page_size(priv->adj_y);
 1528     temp_page_size *=
 1529         (gfloat) (ABS (priv->marked.y - priv->select.y) + 1)
 1530         / allocation.height;
 1531 
 1532     gtk_adjustment_set_page_size(priv->adj_y, temp_page_size);
 1533     gtk_adjustment_set_value(priv->adj_y, temp_value);
 1534 
 1535     /* If we zoom too far into the data, we will get funny results, because
 1536      * of overflow effects. Therefore zooming is limited to zoom_limit.
 1537      */
 1538     if (gtk_adjustment_get_page_size(priv->adj_x) < priv->zoom_limit) {
 1539         temp_value = (gfloat) MAX (0, gtk_adjustment_get_value(priv->adj_x)
 1540                             - (priv->zoom_limit -
 1541                             gtk_adjustment_get_page_size(priv->adj_x)) /
 1542                             2.0);
 1543         gtk_adjustment_set_page_size(priv->adj_x, priv->zoom_limit);
 1544         gtk_adjustment_set_value(priv->adj_x, temp_value);
 1545     }
 1546 
 1547     if (gtk_adjustment_get_page_size(priv->adj_y) < priv->zoom_limit) {
 1548         temp_value = (gfloat) MAX (0, gtk_adjustment_get_value(priv->adj_y)
 1549                             - (priv->zoom_limit -
 1550                             gtk_adjustment_get_page_size(priv->adj_y)) /
 1551                             2.0);
 1552         gtk_adjustment_set_page_size(priv->adj_y, priv->zoom_limit);
 1553         gtk_adjustment_set_value(priv->adj_y, temp_value);
 1554     }
 1555     g_object_thaw_notify(G_OBJECT(priv->adj_y));
 1556     g_object_thaw_notify(G_OBJECT(priv->adj_x));
 1557 
 1558     gtk_databox_calculate_visible_limits(box);
 1559     gtk_databox_zoomed (box);
 1560 }
 1561 
 1562 /**
 1563  * gtk_databox_zoom_out:
 1564  * @box: A #GtkDatabox widget
 1565  *
 1566  * This is equivalent to right-clicking into the @box.
 1567  *
 1568  * This function works, if the attribute #enable-zoom is set to #TRUE. Calling the function
 1569  * then zooms out by a factor of 2 in both dimensions (the maximum is defined by the total
 1570  * limits, see gtk_databox_set_total_limits()).
 1571  *
 1572  * Side effect: The @box emits #GtkDatabox::zoomed.
 1573  */
 1574 void
 1575 gtk_databox_zoom_out (GtkDatabox * box) {
 1576     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1577     if (!priv->enable_zoom) {
 1578         return;
 1579     }
 1580 
 1581     g_object_freeze_notify(G_OBJECT(priv->adj_x));
 1582     g_object_freeze_notify(G_OBJECT(priv->adj_y));
 1583     gtk_adjustment_set_page_size(priv->adj_x, MIN (1.0, gtk_adjustment_get_page_size(priv->adj_x) * 2));
 1584     gtk_adjustment_set_page_size(priv->adj_y, MIN (1.0, gtk_adjustment_get_page_size(priv->adj_y) * 2));
 1585     gtk_adjustment_set_value(priv->adj_x,
 1586         (gtk_adjustment_get_page_size(priv->adj_x) == 1.0)
 1587         ? 0
 1588         : MAX (0, MIN (gtk_adjustment_get_value(priv->adj_x) - gtk_adjustment_get_page_size(priv->adj_x) / 4,
 1589                        1.0 - gtk_adjustment_get_page_size(priv->adj_x))));
 1590     gtk_adjustment_set_value(priv->adj_y,
 1591         (gtk_adjustment_get_page_size(priv->adj_y) == 1.0)
 1592         ? 0
 1593         : MAX (0, MIN (gtk_adjustment_get_value(priv->adj_y) - gtk_adjustment_get_page_size(priv->adj_y) / 4,
 1594                        1.0 - gtk_adjustment_get_page_size(priv->adj_y))));
 1595     g_object_thaw_notify(G_OBJECT(priv->adj_y));
 1596     g_object_thaw_notify(G_OBJECT(priv->adj_x));
 1597 
 1598     gtk_databox_calculate_visible_limits(box);
 1599     gtk_databox_zoomed (box);
 1600 }
 1601 
 1602 /**
 1603  * gtk_databox_zoom_home:
 1604  * @box: A #GtkDatabox widget
 1605  *
 1606  * This is equivalent to shift right-clicking into the @box.
 1607  *
 1608  * This function works, if the attribute #enable-zoom is set to #TRUE. It is equivalent to
 1609  * calling the gtk_databox_set_visible_limits() with the total limits.
 1610  *
 1611  */
 1612 void
 1613 gtk_databox_zoom_home (GtkDatabox * box) {
 1614     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1615     if (!priv->enable_zoom) {
 1616         return;
 1617     }
 1618 
 1619     gtk_databox_set_visible_limits (box,
 1620                                     priv->total_left, priv->total_right,
 1621                                     priv->total_top, priv->total_bottom);
 1622 }
 1623 
 1624 static void
 1625 gtk_databox_draw_selection (GtkDatabox * box, gboolean clear) {
 1626     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1627     GtkWidget *widget = GTK_WIDGET (box);
 1628     GdkDrawingContext *drawc;
 1629     cairo_region_t *crr;
 1630     cairo_t *cr;
 1631  
 1632     crr = gdk_window_get_visible_region (gtk_widget_get_window (widget));
 1633     drawc = gdk_window_begin_draw_frame (gtk_widget_get_window (widget), crr);
 1634     cr = gdk_drawing_context_get_cairo_context (drawc);
 1635     cairo_rectangle (cr,
 1636                      MIN (priv->marked.x, priv->select.x) - 0.5,
 1637                      MIN (priv->marked.y, priv->select.y) - 0.5,
 1638                      ABS (priv->marked.x - priv->select.x) + 1.0,
 1639                      ABS (priv->marked.y - priv->select.y) + 1.0);
 1640 
 1641    if (clear) {
 1642       cairo_set_source_surface (cr, priv->backing_surface, 0, 0);
 1643       cairo_paint(cr);
 1644       cairo_set_line_width (cr, 2.0);
 1645     } else {
 1646       cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
 1647       cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);
 1648       cairo_set_line_width (cr, 1.0);
 1649     }
 1650     cairo_stroke(cr);
 1651     gdk_window_end_draw_frame (gtk_widget_get_window (widget), drawc);
 1652     cairo_region_destroy (crr);
 1653 }
 1654 
 1655 static void
 1656 gtk_databox_adjustment_value_changed (GtkDatabox * box) {
 1657     gtk_databox_calculate_visible_limits (box);
 1658 
 1659     gtk_widget_queue_draw (GTK_WIDGET(box));
 1660 }
 1661 
 1662 static void
 1663 gtk_databox_ruler_update (GtkDatabox * box) {
 1664     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1665     if (priv->ruler_x) {
 1666         gtk_databox_ruler_set_range (
 1667             GTK_DATABOX_RULER (priv->ruler_x),
 1668             priv->visible_left,
 1669             priv->visible_right,
 1670             0.5 * (priv->visible_left + priv->visible_right));
 1671     }
 1672 
 1673     if (priv->ruler_y) {
 1674         gtk_databox_ruler_set_range (
 1675             GTK_DATABOX_RULER (priv->ruler_y),
 1676             priv->visible_top,
 1677             priv->visible_bottom,
 1678             0.5 * (priv->visible_top + priv->visible_bottom));
 1679     }
 1680 }
 1681 
 1682 /**
 1683  * gtk_databox_auto_rescale:
 1684  * @box: A #GtkDatabox widget
 1685  * @border: Relative border width (e.g. 0.1 means that the border on each side is 10% of the data area).
 1686  *
 1687  * This function is similar to gtk_databox_set_total_limits(). It sets the total limits
 1688  * to match the data extrema (see gtk_databox_calculate_extrema()). If you do not like data pixels exactly at the
 1689  * widget's border, you can add modify the limits using the border parameter: The limits are extended by
 1690  * @border*(max-min) if max!=min. If max==min, they are extended by @border*max (otherwise the data could not be
 1691  * scaled to the pixel realm).
 1692  *
 1693  * After calling this function, x values grow from left to right, y values grow from bottom to top.
 1694  *
 1695  * Return value: 0 on success,
 1696  *          -1 if @box is no GtkDatabox widget,
 1697  *          -2 if no datasets are available
 1698  */
 1699 gint
 1700 gtk_databox_auto_rescale (GtkDatabox * box, gfloat border) {
 1701     gfloat min_x;
 1702     gfloat max_x;
 1703     gfloat min_y;
 1704     gfloat max_y;
 1705     gint extrema_success = gtk_databox_calculate_extrema (box, &min_x, &max_x,
 1706                            &min_y, &max_y);
 1707     if (extrema_success)
 1708         return extrema_success;
 1709     else {
 1710         gfloat width = max_x - min_x;
 1711         gfloat height = max_y - min_y;
 1712 
 1713         if (width == 0) width = max_x;
 1714         if (height == 0) height = max_y;
 1715 
 1716         min_x -= border * width;
 1717         max_x += border * width;
 1718         min_y -= border * height;
 1719         max_y += border * height;
 1720     }
 1721 
 1722     gtk_databox_set_total_limits (GTK_DATABOX (box), min_x, max_x, max_y,
 1723                                   min_y);
 1724 
 1725     return 0;
 1726 }
 1727 
 1728 
 1729 /**
 1730  * gtk_databox_calculate_extrema:
 1731  * @box: A #GtkDatabox widget
 1732  * @min_x: Will be filled with the lowest x value of all datasets
 1733  * @max_x: Will be filled with the highest x value of all datasets
 1734  * @min_y: Will be filled with the lowest y value of all datasets
 1735  * @max_y: Will be filled with the highest y value of all datasets
 1736  *
 1737  * Determines the minimum and maximum x and y values of all
 1738  * #GtkDataboxGraph objects which have been added to the #GtkDatabox widget via gtk_databox_graph_add().
 1739  *
 1740  * Return value: 0 on success,
 1741  *          -1 if @box is no GtkDatabox widget,
 1742  *          -2 if no datasets are available
 1743  */
 1744 gint
 1745 gtk_databox_calculate_extrema (GtkDatabox * box,
 1746                                gfloat * min_x, gfloat * max_x, gfloat * min_y,
 1747                                gfloat * max_y) {
 1748     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1749     GList *list;
 1750     gint return_val = -2;
 1751     gboolean first = TRUE;
 1752 
 1753     g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
 1754 
 1755     list = g_list_last (priv->graphs);
 1756     while (list) {
 1757         gfloat graph_min_x;
 1758         gfloat graph_max_x;
 1759         gfloat graph_min_y;
 1760         gfloat graph_max_y;
 1761         gint value = -1;
 1762 
 1763         if (list->data) {
 1764             value =
 1765                 gtk_databox_graph_calculate_extrema (GTK_DATABOX_GRAPH
 1766                         (list->data), &graph_min_x,
 1767                         &graph_max_x, &graph_min_y,
 1768                         &graph_max_y);
 1769         } else {
 1770             /* Do nothing if data == NULL */
 1771         }
 1772 
 1773         if (value >= 0) {
 1774             return_val = 0;
 1775 
 1776             if (first) {
 1777                 /* The min and max values need to be initialized with the
 1778                  * first valid values from the graph
 1779                  */
 1780                 *min_x = graph_min_x;
 1781                 *max_x = graph_max_x;
 1782                 *min_y = graph_min_y;
 1783                 *max_y = graph_max_y;
 1784 
 1785                 first = FALSE;
 1786             } else {
 1787                 *min_x = MIN (*min_x, graph_min_x);
 1788                 *min_y = MIN (*min_y, graph_min_y);
 1789                 *max_x = MAX (*max_x, graph_max_x);
 1790                 *max_y = MAX (*max_y, graph_max_y);
 1791             }
 1792         }
 1793 
 1794         list = g_list_previous (list);
 1795     }
 1796     return return_val;
 1797 }
 1798 
 1799 static gfloat
 1800 gtk_databox_get_offset_x (GtkDatabox* box) {
 1801     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1802 
 1803     if (priv->scale_type_x == GTK_DATABOX_SCALE_LINEAR)
 1804         return (priv->visible_left - priv->total_left)
 1805                / (priv->total_right - priv->total_left);
 1806     else if (priv->scale_type_x == GTK_DATABOX_SCALE_LOG2)
 1807         return log2 (priv->visible_left / priv->total_left)
 1808                / log2 (priv->total_right / priv->total_left);
 1809     else
 1810         return log10 (priv->visible_left / priv->total_left)
 1811                / log10 (priv->total_right / priv->total_left);
 1812 }
 1813 
 1814 static gfloat
 1815 gtk_databox_get_page_size_x (GtkDatabox* box) {
 1816     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1817 
 1818     if (priv->scale_type_x == GTK_DATABOX_SCALE_LINEAR)
 1819         return (priv->visible_left - priv->visible_right)
 1820                / (priv->total_left - priv->total_right);
 1821     else if (priv->scale_type_x == GTK_DATABOX_SCALE_LOG2)
 1822         return log2 (priv->visible_left / priv->visible_right)
 1823                / log2 (priv->total_left / priv->total_right);
 1824     else
 1825         return log10 (priv->visible_left / priv->visible_right)
 1826                / log10 (priv->total_left / priv->total_right);
 1827 }
 1828 
 1829 static gfloat
 1830 gtk_databox_get_offset_y (GtkDatabox* box) {
 1831     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1832 
 1833     if (priv->scale_type_y == GTK_DATABOX_SCALE_LINEAR)
 1834         return (priv->visible_top - priv->total_top)
 1835                / (priv->total_bottom - priv->total_top);
 1836     else if (priv->scale_type_y == GTK_DATABOX_SCALE_LOG2)
 1837         return log2 (priv->visible_top / priv->total_top)
 1838                / log2 (priv->total_bottom / priv->total_top);
 1839     else
 1840         return log10 (priv->visible_top / priv->total_top)
 1841                / log10 (priv->total_bottom / priv->total_top);
 1842 }
 1843 
 1844 static gfloat
 1845 gtk_databox_get_page_size_y (GtkDatabox* box) {
 1846     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1847 
 1848     if (priv->scale_type_y == GTK_DATABOX_SCALE_LINEAR)
 1849         return (priv->visible_top - priv->visible_bottom)
 1850                / (priv->total_top - priv->total_bottom);
 1851     else if (priv->scale_type_y == GTK_DATABOX_SCALE_LOG2)
 1852         return log2 (priv->visible_top / priv->visible_bottom)
 1853                / log2 (priv->total_top / priv->total_bottom);
 1854     else
 1855         return log10 (priv->visible_top / priv->visible_bottom)
 1856                / log10 (priv->total_top / priv->total_bottom);
 1857 }
 1858 
 1859 /**
 1860  * gtk_databox_set_total_limits:
 1861  * @box: A #GtkDatabox widget
 1862  * @left: Left total limit
 1863  * @right: Right total limit
 1864  * @top: Top total limit
 1865  * @bottom: Bottom total limit
 1866  *
 1867  * This function is used to set the limits of the total
 1868  * display area of @box.
 1869  * This function can be used to invert the orientation of the displayed graphs,
 1870  * e.g. @top=-1000 and  @bottom=0.
 1871  *
 1872  * Side effect: The @box also internally calls gtk_databox_set_visible_limits() with the same values.
 1873  *
 1874  */
 1875 void
 1876 gtk_databox_set_total_limits (GtkDatabox * box,
 1877                               gfloat left, gfloat right,
 1878                               gfloat top, gfloat bottom) {
 1879     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1880     g_return_if_fail (GTK_IS_DATABOX (box));
 1881     g_return_if_fail (left != right);
 1882     g_return_if_fail (top != bottom);
 1883 
 1884     priv->total_left = left;
 1885     priv->total_right = right;
 1886     priv->total_top = top;
 1887     priv->total_bottom = bottom;
 1888 
 1889     gtk_databox_set_visible_limits(box, left, right, top, bottom);
 1890 }
 1891 
 1892 /**
 1893  * gtk_databox_set_visible_limits:
 1894  * @box: A #GtkDatabox widget
 1895  * @left: Left visible limit
 1896  * @right: Right visible limit
 1897  * @top: Top visible limit
 1898  * @bottom: Bottom visible limit
 1899  *
 1900  * This function is used to set the limits of the visible
 1901  * display area of @box. The visible display area can be section of the total
 1902  * area, i.e. the @box zooms in, showing only a part of the complete picture.
 1903  *
 1904  * The orientation of the values have to be the same as in gtk_databox_set_total_limits() and
 1905  * the visible limits have to be within the total limits. The
 1906  * values will not be used otherwise.
 1907  *
 1908  * Side effect: The @box emits #GtkDatabox::zoomed.
 1909  *
 1910  */
 1911 void
 1912 gtk_databox_set_visible_limits (GtkDatabox * box,
 1913                                 gfloat left, gfloat right,
 1914                                 gfloat top, gfloat bottom) {
 1915     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1916     gboolean visible_inside_total = FALSE;
 1917 
 1918     g_return_if_fail (GTK_IS_DATABOX (box));
 1919 
 1920     visible_inside_total =
 1921         ((priv->total_left <= left && left < right
 1922           && right <= priv->total_right)
 1923          || (priv->total_left >= left && left > right
 1924              && right >= priv->total_right))
 1925         &&
 1926         ((priv->total_bottom <= bottom && bottom < top
 1927           && top <= priv->total_top)
 1928          || (priv->total_bottom >= bottom && bottom > top
 1929              && top >= priv->total_top));
 1930 
 1931     g_return_if_fail (visible_inside_total);
 1932 
 1933     priv->visible_left = left;
 1934     priv->visible_right = right;
 1935     priv->visible_top = top;
 1936     priv->visible_bottom = bottom;
 1937 
 1938     gtk_databox_calculate_translation_factors (box);
 1939 
 1940     g_object_freeze_notify(G_OBJECT(priv->adj_x));
 1941     g_object_freeze_notify(G_OBJECT(priv->adj_y));
 1942 
 1943     gtk_adjustment_set_value(priv->adj_x, gtk_databox_get_offset_x (box));
 1944     gtk_adjustment_set_page_size(priv->adj_x, gtk_databox_get_page_size_x (box));
 1945     gtk_adjustment_set_value(priv->adj_y, gtk_databox_get_offset_y (box));
 1946     gtk_adjustment_set_page_size(priv->adj_y, gtk_databox_get_page_size_y (box));
 1947 
 1948     g_object_thaw_notify(G_OBJECT(priv->adj_y));
 1949     g_object_thaw_notify(G_OBJECT(priv->adj_x));
 1950 
 1951     /* Update rulers */
 1952     gtk_databox_ruler_update(box);
 1953 
 1954     gtk_databox_calculate_translation_factors (box);
 1955 
 1956     gtk_databox_zoomed (box);
 1957 }
 1958 
 1959 /**
 1960  * gtk_databox_calculate_visible_limits:
 1961  * @box: A #GtkDatabox widget
 1962  *
 1963  * Calculates the visible limits based on the adjustment values and page sizes
 1964  * and calls gtk_databox_set_visible_limits();
 1965  */
 1966 static void
 1967 gtk_databox_calculate_visible_limits (GtkDatabox * box) {
 1968     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 1969     if (!gtk_widget_get_visible (GTK_WIDGET(box)))
 1970         return;
 1971 
 1972     if (priv->scale_type_x == GTK_DATABOX_SCALE_LINEAR) {
 1973         priv->visible_left =
 1974             priv->total_left
 1975             + (priv->total_right - priv->total_left)
 1976             * gtk_adjustment_get_value(priv->adj_x);
 1977         priv->visible_right =
 1978             priv->total_left
 1979             + (priv->total_right - priv->total_left)
 1980             * (gtk_adjustment_get_value(priv->adj_x) + gtk_adjustment_get_page_size(priv->adj_x));
 1981     } else {
 1982         priv->visible_left =
 1983             priv->total_left
 1984             * pow (priv->total_right / priv->total_left,
 1985                    gtk_adjustment_get_value(priv->adj_x));
 1986         priv->visible_right =
 1987             priv->total_left
 1988             * pow (priv->total_right / priv->total_left,
 1989                    gtk_adjustment_get_value(priv->adj_x) + gtk_adjustment_get_page_size(priv->adj_x));
 1990     }
 1991 
 1992     if (priv->scale_type_y == GTK_DATABOX_SCALE_LINEAR) {
 1993         priv->visible_top =
 1994             priv->total_top
 1995             + (priv->total_bottom - priv->total_top)
 1996             * gtk_adjustment_get_value(priv->adj_y);
 1997         priv->visible_bottom =
 1998             priv->total_top
 1999             + (priv->total_bottom - priv->total_top)
 2000             * (gtk_adjustment_get_value(priv->adj_y) + gtk_adjustment_get_page_size(priv->adj_y));
 2001     } else {
 2002         priv->visible_top =
 2003             priv->total_top
 2004             * pow (priv->total_bottom / priv->total_top,
 2005                    gtk_adjustment_get_value(priv->adj_y)),
 2006             priv->visible_bottom =
 2007                 priv->total_top
 2008                 * pow (priv->total_bottom / priv->total_top,
 2009                        gtk_adjustment_get_value(priv->adj_y) + gtk_adjustment_get_page_size(priv->adj_y));
 2010     }
 2011 
 2012     /* Adjustments are the basis for the calculations in this function
 2013      * so they do not need to be updated
 2014      */
 2015 
 2016     /* Update rulers */
 2017     gtk_databox_ruler_update(box);
 2018 
 2019     gtk_databox_calculate_translation_factors (box);
 2020 }
 2021 
 2022 /**
 2023  * gtk_databox_get_total_limits:
 2024  * @box: A #GtkDatabox widget
 2025  * @left: Space for total left value or #NULL
 2026  * @right: Space for total right value or #NULL
 2027  * @top: Space for total top value or #NULL
 2028  * @bottom: Space for total bottom value or #NULL
 2029  *
 2030  * Gives the total limits (as set by gtk_databox_auto_rescale() or gtk_databox_set_total_limits()).
 2031  */
 2032 void
 2033 gtk_databox_get_total_limits (GtkDatabox * box,
 2034                               gfloat * left, gfloat * right,
 2035                               gfloat * top, gfloat * bottom) {
 2036     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2037     g_return_if_fail (GTK_IS_DATABOX (box));
 2038 
 2039     if (left)
 2040         *left = priv->total_left;
 2041     if (right)
 2042         *right = priv->total_right;
 2043     if (top)
 2044         *top = priv->total_top;
 2045     if (bottom)
 2046         *bottom = priv->total_bottom;
 2047 }
 2048 
 2049 /**
 2050  * gtk_databox_get_visible_limits:
 2051  * @box: A #GtkDatabox widget
 2052  * @left: Space for visible left value or #NULL
 2053  * @right: Space for visible right value or #NULL
 2054  * @top: Space for visible top value or #NULL
 2055  * @bottom: Space for visible bottom value or #NULL
 2056  *
 2057  * Gives the current visible limits. These differ from those given by gtk_databox_get_total_limits() if
 2058  * you zoomed into the data for instance by gtk_databox_zoom_to_selection() or gtk_databox_set_visible_limits() (these values
 2059  * can be changed by scrolling, of course).
 2060  */
 2061 void
 2062 gtk_databox_get_visible_limits (GtkDatabox * box,
 2063                                 gfloat * left, gfloat * right,
 2064                                 gfloat * top, gfloat * bottom) {
 2065     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2066     g_return_if_fail (GTK_IS_DATABOX (box));
 2067 
 2068     if (left)
 2069         *left = priv->visible_left;
 2070     if (right)
 2071         *right = priv->visible_right;
 2072     if (top)
 2073         *top = priv->visible_top;
 2074     if (bottom)
 2075         *bottom = priv->visible_bottom;
 2076 }
 2077 
 2078 
 2079 /**
 2080  * gtk_databox_graph_add:
 2081  * @box: A #GtkDatabox widget
 2082  * @graph: A graph, e.g. a #GtkDataboxPoints or a #GtkDataboxGrid object
 2083  *
 2084  * Adds the @graph to the @box. The next time the @box is re-drawn, the graph will be shown.
 2085  *
 2086  * It might be becessary to modify the total_limits in order for the graph to be displayed properly (see gtk_databox_set_total_limits()).
 2087  *
 2088  * Return value: 0 on success, -1 otherwise
 2089  */
 2090 gint
 2091 gtk_databox_graph_add (GtkDatabox * box, GtkDataboxGraph * graph) {
 2092     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2093     g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
 2094     g_return_val_if_fail (GTK_DATABOX_IS_GRAPH (graph), -1);
 2095 
 2096         priv->graphs = g_list_append (priv->graphs, graph);
 2097 
 2098     return (priv->graphs == NULL) ? -1 : 0;
 2099 }
 2100 
 2101 /**
 2102  * gtk_databox_graph_add_front:
 2103  * @box: A #GtkDatabox widget
 2104  * @graph: A graph, e.g. a #GtkDataboxPoints or a #GtkDataboxGrid object
 2105  *
 2106  * Adds the @graph to the @box and will be plotted on top. The next time the @box is re-drawn, the graph will be shown.
 2107  *
 2108  * It might be becessary to modify the total_limits in order for the graph to be displayed properly (see gtk_databox_set_total_limits()).
 2109  *
 2110  * Return value: 0 on success, -1 otherwise
 2111  */
 2112 gint
 2113 gtk_databox_graph_add_front (GtkDatabox * box, GtkDataboxGraph * graph) {
 2114 
 2115     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2116     g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
 2117     g_return_val_if_fail (GTK_DATABOX_IS_GRAPH (graph), -1);
 2118 
 2119             priv->graphs = g_list_prepend (priv->graphs, graph);
 2120 
 2121     return (priv->graphs == NULL) ? -1 : 0;
 2122 }
 2123 
 2124 /**
 2125  * gtk_databox_graph_remove:
 2126  * @box: A #GtkDatabox widget
 2127  * @graph: A graph, e.g. a #GtkDataboxPoints or a #GtkDataboxGrid object
 2128  *
 2129  * Removes the @graph from the @box once. The next time the @box is re-drawn, the graph will not be shown (unless it was added more
 2130  * than once).
 2131  *
 2132  * Return value: 0 on success, -1 otherwise
 2133  */
 2134 gint
 2135 gtk_databox_graph_remove (GtkDatabox * box, GtkDataboxGraph * graph) {
 2136     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2137     GList *list;
 2138 
 2139     g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
 2140     g_return_val_if_fail (GTK_DATABOX_IS_GRAPH (graph), -1);
 2141 
 2142     list = g_list_find (priv->graphs, graph);
 2143     g_return_val_if_fail (list, -1);
 2144 
 2145     priv->graphs = g_list_delete_link (priv->graphs, list);
 2146     return 0;
 2147 }
 2148 
 2149 /**
 2150  * gtk_databox_graph_remove_all:
 2151  * @box: A #GtkDatabox widget
 2152  *
 2153  * Removes all graphs from the @box. The next time the @box is re-drawn, no graphs will be shown.
 2154  *
 2155  * Return value: 0 on success, -1 otherwise
 2156  */
 2157 gint
 2158 gtk_databox_graph_remove_all (GtkDatabox * box) {
 2159     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2160     g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
 2161 
 2162     g_list_free (priv->graphs);
 2163     priv->graphs = NULL;
 2164 
 2165     return 0;
 2166 }
 2167 
 2168 /**
 2169  * gtk_databox_value_to_pixel_x:
 2170  * @box: A #GtkDatabox widget
 2171  * @value: An x value
 2172  *
 2173  * Calculates the horizontal pixel coordinate which represents the x @value.
 2174  * Pixel coordinates are relative to the top-left corner of the @box which is equivalent to (0,0).
 2175  *
 2176  * Return value: Pixel coordinate
 2177  */
 2178 gint16
 2179 gtk_databox_value_to_pixel_x (GtkDatabox * box, gfloat value) {
 2180     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2181 
 2182     if (priv->scale_type_x == GTK_DATABOX_SCALE_LINEAR)
 2183         return (value -
 2184                 priv->visible_left) *
 2185                priv->translation_factor_x;
 2186     else if (priv->scale_type_x == GTK_DATABOX_SCALE_LOG2)
 2187         return log2 (value / priv->visible_left) *
 2188                priv->translation_factor_x;
 2189     else
 2190         return log10 (value / priv->visible_left) *
 2191                priv->translation_factor_x;
 2192 }
 2193 
 2194 /**
 2195  * gtk_databox_value_to_pixel_y:
 2196  * @box: A #GtkDatabox widget
 2197  * @value: A y value
 2198  *
 2199  * Calculates the vertical pixel coordinate which represents the y @value.
 2200  * Pixel coordinates are relative to the top-left corner of the @box which is equivalent to (0,0).
 2201  *
 2202  * Return value: Pixel coordinate
 2203  */
 2204 gint16
 2205 gtk_databox_value_to_pixel_y (GtkDatabox * box, gfloat value) {
 2206     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2207 
 2208     if (priv->scale_type_y == GTK_DATABOX_SCALE_LINEAR)
 2209         return (value -
 2210                 priv->visible_top) * priv->translation_factor_y;
 2211     else if (priv->scale_type_y == GTK_DATABOX_SCALE_LOG2)
 2212         return log2 (value / priv->visible_top) *
 2213                priv->translation_factor_y;
 2214     else
 2215         return log10 (value / priv->visible_top) *
 2216                priv->translation_factor_y;
 2217 }
 2218 
 2219 /**
 2220  * gtk_databox_values_to_xpixels:
 2221  * @box: A #GtkDatabox widget.
 2222  * @pixels: address to return pixel x coordinates.
 2223  * @values: An x values array.
 2224  * @vtype: GType of @values. 
 2225  * @maxlen: maximum length of the arrays.
 2226  * @start: first value to compute.
 2227  * @stride: bytes per row of plotting.
 2228  * @len: how many values to compute.
 2229  *
 2230  * Calculates the horizontal coordinates for @pixels which represents the x @values.
 2231  * Pixel coordinates are relative to the left corner of the @box which is equivalent to (0).
 2232  *
 2233  * Return value: Pixel coordinates
 2234  */
 2235 void
 2236 gtk_databox_values_to_xpixels (GtkDatabox *box, gint16 *pixels,
 2237     void *values, GType vtype, guint maxlen, guint start, guint stride, guint len)
 2238 {
 2239         GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2240         guint i, indx;
 2241     gfloat fval = 0.0;
 2242     GtkDataboxScaleType scale_type;
 2243     gfloat tf, minvis;
 2244 
 2245     scale_type = priv->scale_type_x;
 2246     tf = priv->translation_factor_x;
 2247     minvis = priv->visible_left;
 2248 
 2249     indx = start * stride;
 2250     i = 0;
 2251     do {
 2252         /* This may be excessive, but it handles every conceivable type */
 2253         if (vtype == G_TYPE_FLOAT)
 2254             fval = ((gfloat *)values)[indx];
 2255         else if (vtype == G_TYPE_DOUBLE)
 2256             fval = ((gdouble *)values)[indx];
 2257         else if (vtype == G_TYPE_INT)
 2258             fval = ((gint *)values)[indx];
 2259         else if (vtype == G_TYPE_UINT)
 2260             fval = ((guint *)values)[indx];
 2261         else if (vtype == G_TYPE_LONG)
 2262             fval = ((glong *)values)[indx];
 2263         else if (vtype == G_TYPE_ULONG)
 2264             fval = ((gulong *)values)[indx];
 2265         else if (vtype == G_TYPE_INT64)
 2266             fval = ((gint64 *)values)[indx];
 2267         else if (vtype == G_TYPE_UINT64)
 2268             fval = ((guint64 *)values)[indx];
 2269         else if (vtype == G_TYPE_CHAR)
 2270             fval = ((gchar *)values)[indx];
 2271         else if (vtype == G_TYPE_UCHAR)
 2272             fval = ((guchar *)values)[indx];
 2273 
 2274         if (scale_type == GTK_DATABOX_SCALE_LINEAR)
 2275             pixels[i] = tf * (fval - minvis);
 2276         else if (scale_type == GTK_DATABOX_SCALE_LOG2)
 2277             pixels[i] = tf * log2(fval / minvis);
 2278         else
 2279             pixels[i] = tf * log10(fval / minvis);
 2280 
 2281         /* handle the wrap-around (ring buffer) issue using modulus.  for efficiency, don't do this for non-wraparound cases. */
 2282         /* note this allows multiple wrap-arounds.  One could hold a single cycle of a sine wave, and plot a continuous wave */
 2283         /* This can be optimized using pointers later */
 2284         if (i + start > maxlen)
 2285             indx = ((i + start) % maxlen) * stride;
 2286         else
 2287             indx += stride;
 2288     } while (++i < len);
 2289 }
 2290 
 2291 /**
 2292  * gtk_databox_values_to_ypixels:
 2293  * @box: A #GtkDatabox widget
 2294  * @pixels: address to return pixel y coordinates.
 2295  * @values: an y values array.
 2296  * @vtype: GType of @values. 
 2297  * @maxlen: maximum length of the arrays.
 2298  * @start: first value to compute.
 2299  * @stride: bytes per row of plotting.
 2300  * @len: how many values to compute.
 2301  *
 2302  * Calculates the vertical coordinates for @pixels which represents the y @values.
 2303  * Pixel coordinates are relative to the top corner of the @box which is equivalent to (0).
 2304  *
 2305  * Return value: Pixel coordinates
 2306  */
 2307 void
 2308 gtk_databox_values_to_ypixels (GtkDatabox *box, gint16 *pixels,
 2309     void *values, GType vtype, guint maxlen, guint start, guint stride, guint len)
 2310 {
 2311     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2312     guint i, indx;
 2313     gfloat fval = 0.0;
 2314     GtkDataboxScaleType scale_type;
 2315     gfloat tf, minvis;
 2316 
 2317     scale_type = priv->scale_type_y;
 2318     tf = priv->translation_factor_y;
 2319     minvis = priv->visible_top;
 2320 
 2321     indx = start * stride;
 2322     i = 0;
 2323     do {
 2324         /* This may be excessive, but it handles every conceivable type */
 2325         if (vtype == G_TYPE_FLOAT)
 2326             fval = ((gfloat *)values)[indx];
 2327         else if (vtype == G_TYPE_DOUBLE)
 2328             fval = ((gdouble *)values)[indx];
 2329         else if (vtype == G_TYPE_INT)
 2330             fval = ((gint *)values)[indx];
 2331         else if (vtype == G_TYPE_UINT)
 2332             fval = ((guint *)values)[indx];
 2333         else if (vtype == G_TYPE_LONG)
 2334             fval = ((glong *)values)[indx];
 2335         else if (vtype == G_TYPE_ULONG)
 2336             fval = ((gulong *)values)[indx];
 2337         else if (vtype == G_TYPE_INT64)
 2338             fval = ((gint64 *)values)[indx];
 2339         else if (vtype == G_TYPE_UINT64)
 2340             fval = ((guint64 *)values)[indx];
 2341         else if (vtype == G_TYPE_CHAR)
 2342             fval = ((gchar *)values)[indx];
 2343         else if (vtype == G_TYPE_UCHAR)
 2344             fval = ((guchar *)values)[indx];
 2345 
 2346         if (scale_type == GTK_DATABOX_SCALE_LINEAR)
 2347             pixels[i] = tf * (fval - minvis);
 2348         else if (scale_type == GTK_DATABOX_SCALE_LOG2)
 2349             pixels[i] = tf * log2(fval / minvis);
 2350         else
 2351             pixels[i] = tf * log10(fval / minvis);
 2352 
 2353         /* handle the wrap-around (ring buffer) issue using modulus.  for efficiency, don't do this for non-wraparound cases. */
 2354         /* note this allows multiple wrap-arounds.  One could hold a single cycle of a sine wave, and plot a continuous wave */
 2355         /* This can be optimized using pointers later */
 2356         if (i + start > maxlen)
 2357             indx = ((i + start) % maxlen) * stride;
 2358         else
 2359             indx += stride;
 2360     } while (++i < len);
 2361 }
 2362 
 2363 /**
 2364  * gtk_databox_pixel_to_value_x:
 2365  * @box: A #GtkDatabox widget
 2366  * @pixel: A horizontal pixel coordinate
 2367  *
 2368  * Calculates the x value which is represented by the horizontal @pixel coordinate.
 2369  * Pixel coordinates are relative to the top-left corner of the @box which is equivalent to (0,0).
 2370  *
 2371  * Return value: x value
 2372  */
 2373 gfloat
 2374 gtk_databox_pixel_to_value_x (GtkDatabox * box, gint16 pixel) {
 2375     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2376 
 2377     if (priv->scale_type_x == GTK_DATABOX_SCALE_LINEAR)
 2378         return priv->visible_left +
 2379                pixel / priv->translation_factor_x;
 2380     else if (priv->scale_type_x == GTK_DATABOX_SCALE_LOG2)
 2381         return priv->visible_left * pow (2,
 2382                                               pixel /
 2383                                               priv->
 2384                                               translation_factor_x);
 2385     else
 2386         return priv->visible_left * pow (10,
 2387                                               pixel /
 2388                                               priv->
 2389                                               translation_factor_x);
 2390 }
 2391 
 2392 /**
 2393  * gtk_databox_pixel_to_value_y:
 2394  * @box: A #GtkDatabox widget
 2395  * @pixel: A vertical pixel coordinate
 2396  *
 2397  * Calculates the y value which is represented by the vertical @pixel coordinate.
 2398  * Pixel coordinates are relative to the top-left corner of the @box which is equivalent to (0,0).
 2399  *
 2400  * Return value: y value
 2401  */
 2402 gfloat
 2403 gtk_databox_pixel_to_value_y (GtkDatabox * box, gint16 pixel) {
 2404     GtkDataboxPrivate *priv = gtk_databox_get_instance_private(box);
 2405 
 2406     if (priv->scale_type_y == GTK_DATABOX_SCALE_LINEAR)
 2407         return priv->visible_top +
 2408                pixel / priv->translation_factor_y;
 2409     else if (priv->scale_type_y == GTK_DATABOX_SCALE_LOG2)
 2410         return priv->visible_top * pow (2,
 2411                                              pixel /
 2412                                              priv->
 2413                                              translation_factor_y);
 2414     else
 2415         return priv->visible_top * pow (10,
 2416                                              pixel /
 2417                                              priv->
 2418                                              translation_factor_y);
 2419 }
 2420 
 2421 /**
 2422  * gtk_databox_create_box_with_scrollbars_and_rulers:
 2423  * @p_box: Will contain a pointer to a #GtkDatabox widget
 2424  * @p_grid: Will contain a pointer to a #GtkGrid widget
 2425  * @scrollbar_x: Whether to attach a horizontal scrollbar
 2426  * @scrollbar_y: Whether to attach a vertical scrollbar
 2427  * @ruler_x: Whether to attach a horizontal ruler
 2428  * @ruler_y: Whether to attach a vertical ruler
 2429  *
 2430  * This is a convenience function which creates a #GtkDatabox widget in a
 2431  * GtkGrid widget optionally accompanied by scrollbars and rulers. You only
 2432  * have to fill in the data (gtk_databox_graph_add()) and adjust the limits
 2433  * (gtk_databox_set_total_limits() or gtk_databox_auto_rescale()).
 2434  *
 2435  * This function produces the default databox with rulers at the top left and
 2436  * scroll bars at the bottom right.
 2437  *
 2438  * Return value: #GtkGrid in the @p_grid object pointer.
 2439  *
 2440  * @see_also: gtk_databox_new(), gtk_databox_set_adjustment_x(), gtk_databox_set_adjustment_y(), gtk_databox_set_ruler_x(), gtk_databox_set_ruler_y()
 2441  */
 2442 void
 2443 gtk_databox_create_box_with_scrollbars_and_rulers (GtkWidget ** p_box,
 2444         GtkWidget ** p_grid,
 2445         gboolean scrollbar_x,
 2446         gboolean scrollbar_y,
 2447         gboolean ruler_x,
 2448         gboolean ruler_y) {
 2449     /* create with rulers top left by default */
 2450     gtk_databox_create_box_with_scrollbars_and_rulers_positioned (p_box, p_grid, scrollbar_x, scrollbar_y, ruler_x, ruler_y, TRUE, TRUE);
 2451 }
 2452 
 2453 /**
 2454  * gtk_databox_create_box_with_scrollbars_and_rulers_positioned:
 2455  * @p_box: Will contain a pointer to a #GtkDatabox widget
 2456  * @p_grid: Will contain a pointer to a #GtkGrid widget
 2457  * @scrollbar_x: Whether to attach a horizontal scrollbar
 2458  * @scrollbar_y: Whether to attach a vertical scrollbar
 2459  * @ruler_x: Whether to attach a horizontal ruler
 2460  * @ruler_y: Whether to attach a vertical ruler
 2461  * @ruler_x_top: Whether to put the ruler_x up the top
 2462  * @ruler_y_left: Whether to put the ruler_y on the left
 2463  *
 2464  * This is a convenience function which creates a #GtkDatabox widget in a
 2465  * GtkGrid widget optionally accompanied by scrollbars and rulers. You only
 2466  * have to fill in the data (gtk_databox_graph_add()) and adjust the limits
 2467  * (gtk_databox_set_total_limits() or gtk_databox_auto_rescale()).
 2468  *
 2469  * This function produces the default databox with rulers at the top left and
 2470  * scroll bars at the bottom right.
 2471  *
 2472  * Return value: #GtkGrid in the @p_grid object pointer.
 2473  *
 2474  * @see_also: gtk_databox_new(), gtk_databox_set_adjustment_x(), gtk_databox_set_adjustment_y(), gtk_databox_set_ruler_x(), gtk_databox_set_ruler_y(), gtk_databox_create_box_with_scrollbars_and_rulers()
 2475  */
 2476 void
 2477 gtk_databox_create_box_with_scrollbars_and_rulers_positioned (GtkWidget ** p_box,
 2478         GtkWidget ** p_grid,
 2479         gboolean scrollbar_x,
 2480         gboolean scrollbar_y,
 2481         gboolean ruler_x,
 2482         gboolean ruler_y,
 2483         gboolean ruler_x_top,
 2484         gboolean ruler_y_left) {
 2485     GtkGrid *grid;
 2486     GtkDatabox *box;
 2487     GtkWidget *scrollbar;
 2488     GtkWidget *ruler;
 2489     gint left_col, top_row;
 2490     GtkDataboxPrivate *priv;
 2491 
 2492     *p_grid = gtk_grid_new ();
 2493     *p_box = gtk_databox_new ();
 2494     box = GTK_DATABOX (*p_box);
 2495     grid = GTK_GRID (*p_grid);
 2496     priv = gtk_databox_get_instance_private(box);
 2497 
 2498     left_col=1;
 2499     top_row=1;
 2500     gtk_grid_attach (grid, GTK_WIDGET (box), left_col, top_row, 1, 1);
 2501 
 2502     if (scrollbar_x) {
 2503         scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, NULL);
 2504         gtk_databox_set_adjustment_x (box,
 2505                                       gtk_range_get_adjustment (GTK_RANGE
 2506                                               (scrollbar)));
 2507         if (ruler_x_top) {
 2508             left_col=1;
 2509             top_row=2;
 2510         } else {
 2511             left_col=1;
 2512             top_row=0;
 2513         }
 2514         gtk_grid_attach (grid, scrollbar, left_col, top_row, 1, 1);
 2515     }
 2516 
 2517     if (scrollbar_y) {
 2518         scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, NULL);
 2519         gtk_databox_set_adjustment_y (box,
 2520                                       gtk_range_get_adjustment (GTK_RANGE
 2521                                               (scrollbar)));
 2522         if (ruler_y_left) {
 2523             left_col=2;
 2524             top_row=1;
 2525         } else {
 2526             left_col=0;
 2527             top_row=1;
 2528         }
 2529         gtk_grid_attach (grid, scrollbar, left_col, top_row, 1, 1);
 2530     }
 2531 
 2532     if (ruler_x) {
 2533         ruler = gtk_databox_ruler_new (GTK_ORIENTATION_HORIZONTAL);
 2534         gtk_databox_ruler_set_scale_type (GTK_DATABOX_RULER (ruler),
 2535                                           priv->scale_type_x);
 2536         if (ruler_x_top) {
 2537             left_col=1;
 2538             top_row=0;
 2539         } else {
 2540             gtk_databox_ruler_set_invert_edge(GTK_DATABOX_RULER(ruler), TRUE); /* set the ruler to reverse its edge */
 2541             left_col=1;
 2542             top_row=2;
 2543         }
 2544         gtk_grid_attach (grid, ruler, left_col, top_row, 1, 1);
 2545         gtk_databox_set_ruler_x (box, GTK_DATABOX_RULER (ruler));
 2546     }
 2547 
 2548     if (ruler_y) {
 2549         ruler = gtk_databox_ruler_new (GTK_ORIENTATION_VERTICAL);
 2550         gtk_databox_ruler_set_scale_type (GTK_DATABOX_RULER (ruler),
 2551                                           priv->scale_type_y);
 2552         if (ruler_y_left) {
 2553             left_col=0;
 2554             top_row=1;
 2555         } else {
 2556             gtk_databox_ruler_set_invert_edge(GTK_DATABOX_RULER(ruler), TRUE); /* set the ruler to reverse its edge */
 2557             left_col=2;
 2558             top_row=1;
 2559         }
 2560         gtk_grid_attach (grid, ruler, left_col, top_row, 1, 1);
 2561         gtk_databox_set_ruler_y (box, GTK_DATABOX_RULER (ruler));
 2562     }
 2563 }