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