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