1 /* GtkDatabox - An extension to the gtk+ library
2 * Copyright (C) 1998 - 2002 Dr. Roland Bock
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 2.1
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18 /* gtkdatabox.c */
19
20 #include <string.h>
21 #include "gtk/gtksignal.h"
22 #include "gtk/gtktable.h"
23 #include "gtk/gtktogglebutton.h"
24 #include "gtk/gtkdrawingarea.h"
25 #include "gtk/gtkhscrollbar.h"
26 #include "gtk/gtkvscrollbar.h"
27 #include "gtk/gtkruler.h"
28 #include "gtk/gtkhruler.h"
29 #include "gtk/gtkvruler.h"
30 #include <vdk/gtkdatabox.h>
31 #include <vdk/gtkdataboxmarshal.h>
32
33 #define CROSS_BORDER 10
34
35 enum
36 {
37 GTK_DATABOX_SHOW_RULERS = 0,
38 GTK_DATABOX_SHOW_SCROLLBARS,
39 GTK_DATABOX_ENABLE_SELECTION,
40 GTK_DATABOX_SHOW_SELECTION_FILLED,
41 GTK_DATABOX_ENABLE_ZOOM,
42 GTK_DATABOX_REDRAW_REQUEST,
43 GTK_DATABOX_SELECTION_STOPPED,
44 };
45
46 enum
47 {
48 GTK_DATABOX_DATA_HAS_GC = 0,
49 };
50
51 typedef struct _GtkDataboxData GtkDataboxData;
52
53 typedef void (*GtkDataboxDrawFunc) (GtkDatabox * box, GtkDataboxData * data);
54 #define GTK_DATABOX_DRAW_FUNC(f) ((GtkDataboxDrawFunc) (f))
55
56 static GtkObjectClass *parent_class = NULL;
57
58 struct _GtkDataboxData
59 {
60 gfloat *X; /* X (horizontal) values */
61 gfloat *Y; /* Y (vertical) values */
62 guint length; /* Number of data points */
63
64 GtkDataboxDataType type; /* How this data set is to be displayed */
65 GtkDataboxDrawFunc draw; /* Function that draws the data */
66
67 GdkColor color; /* Dot-color */
68 guint size; /* Dot-size */
69 GdkGC *gc; /* Dot-gc */
70
71 glong flags; /* ..HAS_GC, etc. */
72
73 guint hlines; /* Only used for grid */
74 guint vlines; /* Only used for grid */
75 };
76
77 static void gtk_databox_class_init (GtkDataboxClass * klass);
78 static void gtk_databox_init (GtkDatabox * box);
79 static gint gtk_databox_destroy_callback (GtkWidget * widget,
80 GtkDatabox * box);
81 static gint gtk_databox_expose_callback (GtkWidget * widget,
82 GdkEventExpose * event,
83 GtkDatabox * box);
84 static gint gtk_databox_configure_callback (GtkWidget * widget,
85 GdkEventConfigure * event,
86 GtkDatabox * box);
87 static void gtk_databox_zoom_to_selection (GtkWidget * widget,
88 GtkDatabox * box);
89 static void gtk_databox_zoom_out (GtkWidget * widget, GtkDatabox * box);
90 static void gtk_databox_zoom_home (GtkWidget * widget, GtkDatabox * box);
91 static void gtk_databox_zoomed (GtkWidget * widget, GtkDatabox * box,
92 gboolean redraw_flag);
93 static void gtk_databox_x_adjustment_callback (GtkWidget * widget,
94 GtkDatabox * box);
95 static void gtk_databox_y_adjustment_callback (GtkWidget * widget,
96 GtkDatabox * box);
97 static gint gtk_databox_button_press_callback (GtkWidget * widget,
98 GdkEventButton * event,
99 GtkDatabox * box);
100 static gint gtk_databox_button_release_callback (GtkWidget * widget,
101 GdkEventButton * event,
102 GtkDatabox * box);
103 static gint gtk_databox_motion_notify_callback (GtkWidget * widget,
104 GdkEventMotion * event,
105 GtkDatabox * box);
106 static void gtk_databox_draw_request_full (GtkWidget * widget, gboolean now,
107 GtkDatabox * box);
108 static gint gtk_databox_draw_selection (GtkWidget * widget, GtkDatabox * box,
109 GdkRectangle * rect);
110 static void gtk_databox_draw (GtkWidget * widget, GtkDatabox * box,
111 GdkEventExpose * event);
112 static void gtk_databox_draw_points (GtkDatabox * box, GtkDataboxData * data);
113 static void gtk_databox_draw_lines (GtkDatabox * box, GtkDataboxData * data);
114 static void gtk_databox_draw_bars (GtkDatabox * box, GtkDataboxData * data);
115 static void gtk_databox_draw_cross_simple (GtkDatabox * box,
116 GtkDataboxData * data);
117 static void gtk_databox_draw_grid (GtkDatabox * box, GtkDataboxData * data);
118 static void gtk_databox_new_data_gc (GtkWidget * widget, GtkDatabox * box,
119 GtkDataboxData * data);
120 static void gtk_databox_update_x_ruler (GtkDatabox * box);
121 static void gtk_databox_update_y_ruler (GtkDatabox * box);
122 static void gtk_databox_data_calc_extrema (GtkDatabox * box,
123 GtkDataboxValue * min,
124 GtkDataboxValue * max);
125 static gint gtk_databox_check_x_links (GList * list, gfloat * values);
126 static gint gtk_databox_check_y_links (GList * list, gfloat * values);
127 static void gtk_databox_destroy_data (GtkDatabox * box, GtkDataboxData * data,
128 GList * list, gboolean free_flag);
129 static gint gtk_databox_data_destroy_with_flag (GtkDatabox * box, gint index,
130 gboolean free_flag);
131 static gint gtk_databox_data_destroy_all_with_flag (GtkDatabox * box,
132 gboolean free_flag);
133 enum
134 {
135 GTK_DATABOX_ZOOMED_SIGNAL,
136 GTK_DATABOX_MARKED_SIGNAL,
137 GTK_DATABOX_SELECTION_STARTED_SIGNAL,
138 GTK_DATABOX_SELECTION_CHANGED_SIGNAL,
139 GTK_DATABOX_SELECTION_STOPPED_SIGNAL,
140 GTK_DATABOX_SELECTION_CANCELLED_SIGNAL,
141 LAST_SIGNAL
142 };
143
144 static gint gtk_databox_signals[LAST_SIGNAL] = { 0 };
145
146 /* FIXME: We should take a closer look at some other widgets for better
147 understanding... */
148 guint
gtk_databox_get_type()149 gtk_databox_get_type ()
150 {
151 static GType databox_type = 0;
152
153 if (!databox_type)
154 {
155 static const GTypeInfo databox_info =
156 {
157 sizeof (GtkDataboxClass),
158 NULL, /* base_init */
159 NULL, /* base_finalize */
160 (GClassInitFunc) gtk_databox_class_init,
161 NULL, /* class_finalize */
162 NULL, /* class_data */
163 sizeof (GtkDatabox),
164 0, /* no pre-allocation */
165 (GInstanceInitFunc) gtk_databox_init,
166 };
167
168 databox_type = g_type_register_static (GTK_TYPE_VBOX,
169 "GtkDatabox",
170 &databox_info,
171 0);
172 }
173
174 return databox_type;
175 }
176
177 /* FIXME: We should take a closer look at some other widgets for better
178 understanding... */
179 static void
gtk_databox_class_init(GtkDataboxClass * klass)180 gtk_databox_class_init (GtkDataboxClass * klass)
181 {
182 GtkObjectClass *object_class = (GtkObjectClass *) klass;
183 /* GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;*/
184
185 parent_class = g_type_class_peek_parent (klass);
186
187 /* FIXME Many Gtk widgets use something like the following */
188 /* Should that be used here as well? */
189 /*
190 object_class->destroy = gtk_databox_destroy;
191 widget_class->configure_event = gtk_databox_configure;
192 widget_class->expose_event = gtk_databox_expose;
193 widget_class->button_press_event = gtk_databox_button_press;
194 widget_class->button_release_event = gtk_databox_button_release;
195 widget_class->motion_notify_event = gtk_databox_motion_notify;
196 */
197
198 gtk_databox_signals[GTK_DATABOX_ZOOMED_SIGNAL] =
199 g_signal_new ("gtk_databox_zoomed",
200 G_TYPE_FROM_CLASS (object_class),
201 G_SIGNAL_RUN_FIRST,
202 G_STRUCT_OFFSET (GtkDataboxClass, gtk_databox_zoomed),
203 NULL, /* accumulator */
204 NULL, /* accumulator_data */
205 gtk_databox_marshal_VOID__POINTER_POINTER,
206 G_TYPE_NONE,
207 2, G_TYPE_POINTER, G_TYPE_POINTER);
208 gtk_databox_signals[GTK_DATABOX_MARKED_SIGNAL] =
209 g_signal_new ("gtk_databox_marked",
210 G_TYPE_FROM_CLASS (object_class),
211 G_SIGNAL_RUN_FIRST,
212 G_STRUCT_OFFSET (GtkDataboxClass, gtk_databox_marked),
213 NULL, /* accumulator */
214 NULL, /* accumulator_data */
215 gtk_databox_marshal_VOID__POINTER,
216 G_TYPE_NONE,
217 1, G_TYPE_POINTER);
218 gtk_databox_signals[GTK_DATABOX_SELECTION_STARTED_SIGNAL] =
219 g_signal_new ("gtk_databox_selection_started",
220 G_TYPE_FROM_CLASS (object_class),
221 G_SIGNAL_RUN_FIRST,
222 G_STRUCT_OFFSET (GtkDataboxClass, gtk_databox_selection_started),
223 NULL, /* accumulator */
224 NULL, /* accumulator_data */
225 gtk_databox_marshal_VOID__POINTER,
226 G_TYPE_NONE,
227 1, G_TYPE_POINTER);
228 gtk_databox_signals[GTK_DATABOX_SELECTION_CHANGED_SIGNAL] =
229 g_signal_new ("gtk_databox_selection_changed",
230 G_TYPE_FROM_CLASS (object_class),
231 G_SIGNAL_RUN_FIRST,
232 G_STRUCT_OFFSET (GtkDataboxClass, gtk_databox_selection_changed),
233 NULL, /* accumulator */
234 NULL, /* accumulator_data */
235 gtk_databox_marshal_VOID__POINTER_POINTER,
236 G_TYPE_NONE,
237 2, G_TYPE_POINTER, G_TYPE_POINTER);
238 gtk_databox_signals[GTK_DATABOX_SELECTION_STOPPED_SIGNAL] =
239 g_signal_new ("gtk_databox_selection_stopped",
240 G_TYPE_FROM_CLASS (object_class),
241 G_SIGNAL_RUN_FIRST,
242 G_STRUCT_OFFSET (GtkDataboxClass, gtk_databox_selection_stopped),
243 NULL, /* accumulator */
244 NULL, /* accumulator_data */
245 gtk_databox_marshal_VOID__POINTER_POINTER,
246 G_TYPE_NONE,
247 2, G_TYPE_POINTER, G_TYPE_POINTER);
248 gtk_databox_signals[GTK_DATABOX_SELECTION_CANCELLED_SIGNAL] =
249 g_signal_new ("gtk_databox_selection_cancelled",
250 G_TYPE_FROM_CLASS (object_class),
251 G_SIGNAL_RUN_FIRST,
252 G_STRUCT_OFFSET (GtkDataboxClass, gtk_databox_selection_cancelled),
253 NULL, /* accumulator */
254 NULL, /* accumulator_data */
255 gtk_databox_marshal_VOID__VOID,
256 G_TYPE_NONE,
257 0);
258
259 klass->gtk_databox = NULL;
260 klass->gtk_databox_zoomed = NULL;
261 klass->gtk_databox_marked = NULL;
262 klass->gtk_databox_selection_started = NULL;
263 klass->gtk_databox_selection_changed = NULL;
264 klass->gtk_databox_selection_stopped = NULL;
265 klass->gtk_databox_selection_cancelled = NULL;
266 }
267
268 static void
gtk_databox_init(GtkDatabox * box)269 gtk_databox_init (GtkDatabox * box)
270 {
271 GtkWidget *widget = NULL;
272
273 box->table = gtk_table_new (3, 3, FALSE);
274 gtk_container_add (GTK_CONTAINER (box), box->table);
275 gtk_widget_show (box->table);
276
277 widget = box->draw = gtk_drawing_area_new ();
278 gtk_widget_set_events (widget,
279 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
280 GDK_POINTER_MOTION_MASK |
281 GDK_POINTER_MOTION_HINT_MASK);
282 g_signal_connect (GTK_OBJECT (widget), "destroy",
283 GTK_SIGNAL_FUNC (gtk_databox_destroy_callback), box);
284 g_signal_connect (GTK_OBJECT (widget), "configure_event",
285 GTK_SIGNAL_FUNC (gtk_databox_configure_callback), box);
286 g_signal_connect (GTK_OBJECT (widget), "expose_event",
287 GTK_SIGNAL_FUNC (gtk_databox_expose_callback), box);
288 g_signal_connect (GTK_OBJECT (widget), "button_press_event",
289 GTK_SIGNAL_FUNC (gtk_databox_button_press_callback),
290 box);
291 g_signal_connect (GTK_OBJECT (widget), "button_release_event",
292 GTK_SIGNAL_FUNC (gtk_databox_button_release_callback),
293 box);
294 g_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
295 GTK_SIGNAL_FUNC (gtk_databox_motion_notify_callback),
296 box);
297 gtk_widget_set_size_request (widget, 20, 30);
298
299 gtk_table_attach (GTK_TABLE (box->table), widget, 1, 2, 1, 2,
300 GTK_FILL | GTK_EXPAND | GTK_SHRINK,
301 GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
302 gtk_widget_show (widget);
303
304 box->adjX =
305 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 1.0, 0.1, 0.9, 1.0));
306 box->adjY =
307 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 1.0, 0.1, 0.9, 1.0));
308 g_object_ref (GTK_OBJECT (box->adjX));
309 g_object_ref (GTK_OBJECT (box->adjY));
310
311 g_signal_connect (GTK_OBJECT (box->adjY), "value_changed",
312 GTK_SIGNAL_FUNC (gtk_databox_y_adjustment_callback),
313 box);
314 g_signal_connect (GTK_OBJECT (box->adjX), "value_changed",
315 GTK_SIGNAL_FUNC (gtk_databox_x_adjustment_callback),
316 box);
317
318 box->flags = 0;
319 gtk_databox_show_rulers (box);
320 gtk_databox_show_scrollbars (box);
321 gtk_databox_enable_zoom (box);
322 gtk_databox_enable_selection (box);
323 gtk_databox_hide_selection_filled (box);
324 gtk_databox_set_zoom_limit (box, 0.01);
325
326 box->pixmap = NULL;
327 box->data = NULL;
328 box->max_points = 0;
329 box->points = NULL;
330 box->select_gc = NULL;
331
332 gtk_databox_rescale (box);
333 }
334
335 GtkWidget *
gtk_databox_new()336 gtk_databox_new ()
337 {
338 return gtk_widget_new (GTK_TYPE_DATABOX, NULL);
339 }
340
341 void
gtk_databox_show_rulers(GtkDatabox * box)342 gtk_databox_show_rulers (GtkDatabox * box)
343 {
344 g_return_if_fail (GTK_IS_DATABOX (box));
345
346 if (!(box->flags & (1 << GTK_DATABOX_SHOW_RULERS)))
347 {
348 box->hrule = gtk_hruler_new ();
349 gtk_ruler_set_metric (GTK_RULER (box->hrule), GTK_PIXELS);
350 gtk_ruler_set_range (GTK_RULER (box->hrule), 1.5, -0.5, 0.5, 20);
351 g_signal_connect_closure (box->draw, "motion_notify_event",
352 g_cclosure_new_object_swap (GTK_SIGNAL_FUNC(GTK_WIDGET_GET_CLASS(box->hrule)->motion_notify_event), G_OBJECT (box->hrule)),
353 FALSE);
354 box->vrule = gtk_vruler_new ();
355 gtk_ruler_set_metric (GTK_RULER (box->vrule), GTK_PIXELS);
356 gtk_ruler_set_range (GTK_RULER (box->vrule), 1.5, -0.5, 0.5, 20);
357 g_signal_connect_closure (box->draw, "motion_notify_event",
358 g_cclosure_new_object_swap (GTK_SIGNAL_FUNC(GTK_WIDGET_GET_CLASS(box->vrule)->motion_notify_event), G_OBJECT (box->vrule)),
359 FALSE);
360
361 gtk_table_attach (GTK_TABLE (box->table), box->hrule, 1, 2, 0, 1,
362 GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0);
363 gtk_table_attach (GTK_TABLE (box->table), box->vrule, 0, 1, 1, 2,
364 GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
365
366 gtk_widget_show (box->hrule);
367 gtk_widget_show (box->vrule);
368 box->flags |= 1 << GTK_DATABOX_SHOW_RULERS;
369 }
370 }
371
372 void
gtk_databox_hide_rulers(GtkDatabox * box)373 gtk_databox_hide_rulers (GtkDatabox * box)
374 {
375 g_return_if_fail (GTK_IS_DATABOX (box));
376
377 if (box->flags & (1 << GTK_DATABOX_SHOW_RULERS))
378 {
379 gtk_widget_destroy (box->hrule);
380 box->hrule = NULL;
381 gtk_widget_destroy (box->vrule);
382 box->vrule = NULL;
383 }
384 box->flags &= ~(1 << GTK_DATABOX_SHOW_RULERS);
385 }
386
387 void
gtk_databox_show_scrollbars(GtkDatabox * box)388 gtk_databox_show_scrollbars (GtkDatabox * box)
389 {
390 g_return_if_fail (GTK_IS_DATABOX (box));
391
392 if (!(box->flags & (1 << GTK_DATABOX_SHOW_SCROLLBARS)))
393 {
394 box->hscroll = gtk_hscrollbar_new (box->adjX);
395 box->vscroll = gtk_vscrollbar_new (box->adjY);
396 gtk_table_attach (GTK_TABLE (box->table), box->hscroll, 1, 2, 2, 3,
397 GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL, 0, 0);
398 gtk_table_attach (GTK_TABLE (box->table), box->vscroll, 2, 3, 1, 2,
399 GTK_FILL, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
400
401 gtk_widget_show (box->hscroll);
402 gtk_widget_show (box->vscroll);
403 }
404 box->flags |= 1 << GTK_DATABOX_SHOW_SCROLLBARS;
405 }
406
407 void
gtk_databox_hide_scrollbars(GtkDatabox * box)408 gtk_databox_hide_scrollbars (GtkDatabox * box)
409 {
410 g_return_if_fail (GTK_IS_DATABOX (box));
411
412 if ((box->flags & (1 << GTK_DATABOX_SHOW_SCROLLBARS)))
413 {
414 gtk_widget_destroy (box->hscroll);
415 gtk_widget_destroy (box->vscroll);
416 }
417 box->flags &= ~(1 << GTK_DATABOX_SHOW_SCROLLBARS);
418 }
419
420 void
gtk_databox_enable_selection(GtkDatabox * box)421 gtk_databox_enable_selection (GtkDatabox * box)
422 {
423 g_return_if_fail (GTK_IS_DATABOX (box));
424
425 box->flags |= 1 << GTK_DATABOX_ENABLE_SELECTION;
426 }
427
428 void
gtk_databox_disable_selection(GtkDatabox * box)429 gtk_databox_disable_selection (GtkDatabox * box)
430 {
431 g_return_if_fail (GTK_IS_DATABOX (box));
432
433 box->flags &= ~(1 << GTK_DATABOX_ENABLE_SELECTION);
434
435 box->selection_flag = 0;
436 g_signal_emit (GTK_OBJECT (box),
437 gtk_databox_signals
438 [GTK_DATABOX_SELECTION_CANCELLED_SIGNAL], 0);
439
440 }
441
442 void
gtk_databox_show_selection_filled(GtkDatabox * box)443 gtk_databox_show_selection_filled (GtkDatabox * box)
444 {
445 g_return_if_fail (GTK_IS_DATABOX (box));
446
447 box->flags |= 1 << GTK_DATABOX_SHOW_SELECTION_FILLED;
448 }
449
450 void
gtk_databox_hide_selection_filled(GtkDatabox * box)451 gtk_databox_hide_selection_filled (GtkDatabox * box)
452 {
453 g_return_if_fail (GTK_IS_DATABOX (box));
454
455 box->flags &= ~(1 << GTK_DATABOX_SHOW_SELECTION_FILLED);
456 }
457
458 void
gtk_databox_enable_zoom(GtkDatabox * box)459 gtk_databox_enable_zoom (GtkDatabox * box)
460 {
461 g_return_if_fail (GTK_IS_DATABOX (box));
462
463 box->flags |= 1 << GTK_DATABOX_ENABLE_ZOOM;
464 }
465
466 void
gtk_databox_disable_zoom(GtkDatabox * box)467 gtk_databox_disable_zoom (GtkDatabox * box)
468 {
469 g_return_if_fail (GTK_IS_DATABOX (box));
470
471 box->flags &= ~(1 << GTK_DATABOX_ENABLE_ZOOM);
472 }
473
474 void
gtk_databox_set_zoom_limit(GtkDatabox * box,gfloat zoom_limit)475 gtk_databox_set_zoom_limit (GtkDatabox * box, gfloat zoom_limit)
476 {
477 g_return_if_fail (GTK_IS_DATABOX (box));
478
479 box->zoom_limit = zoom_limit;
480 }
481
482
483 gfloat
gtk_databox_get_zoom_limit(GtkDatabox * box)484 gtk_databox_get_zoom_limit (GtkDatabox * box)
485 {
486 g_return_val_if_fail (GTK_IS_DATABOX (box), 0);
487
488 return box->zoom_limit;
489 }
490
491
492 static gint
gtk_databox_destroy_callback(GtkWidget * widget,GtkDatabox * box)493 gtk_databox_destroy_callback (GtkWidget * widget, GtkDatabox * box)
494 {
495 if (box->pixmap)
496 g_object_unref (box->pixmap);
497 if (box->select_gc)
498 {
499 g_object_unref (box->select_gc);
500 }
501 g_object_unref (GTK_OBJECT (box->adjX));
502 g_object_unref (GTK_OBJECT (box->adjY));
503
504 return FALSE;
505 }
506
507 static gint
gtk_databox_configure_callback(GtkWidget * widget,GdkEventConfigure * event,GtkDatabox * box)508 gtk_databox_configure_callback (GtkWidget * widget, GdkEventConfigure * event,
509 GtkDatabox * box)
510 {
511 gdk_drawable_get_size (widget->window, &(box->size.x), &(box->size.y));
512
513 if (box->pixmap)
514 g_object_unref (box->pixmap);
515
516 box->pixmap =
517 gdk_pixmap_new (widget->window, box->size.x, box->size.y, -1);
518
519 gdk_draw_rectangle (box->pixmap, widget->style->bg_gc[0], TRUE, 0, 0,
520 box->size.x, box->size.y);
521
522 if (box->selection_flag)
523 {
524 box->selection_flag = 0;
525 g_signal_emit (GTK_OBJECT (box),
526 gtk_databox_signals
527 [GTK_DATABOX_SELECTION_CANCELLED_SIGNAL], 0);
528 }
529 gtk_databox_zoomed (widget, box, FALSE);
530
531 return FALSE;
532 }
533
534 static gint
gtk_databox_expose_callback(GtkWidget * widget,GdkEventExpose * event,GtkDatabox * box)535 gtk_databox_expose_callback (GtkWidget * widget, GdkEventExpose * event,
536 GtkDatabox * box)
537 {
538 gtk_databox_draw (box->draw, box, event);
539
540 gdk_draw_drawable (widget->window,
541 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
542 box->pixmap, event->area.x, event->area.y, event->area.x,
543 event->area.y, event->area.width, event->area.height);
544
545 return FALSE;
546 }
547
548 static gint
gtk_databox_button_press_callback(GtkWidget * widget,GdkEventButton * event,GtkDatabox * box)549 gtk_databox_button_press_callback (GtkWidget * widget, GdkEventButton * event,
550 GtkDatabox * box)
551 {
552 gint x;
553 gint y;
554 guint button;
555 GdkRectangle rect;
556
557
558 if (event->type != GDK_BUTTON_PRESS)
559 return FALSE;
560
561 box->flags &= ~(1 << GTK_DATABOX_SELECTION_STOPPED);
562
563 button = event->button;
564 x = event->x;
565 y = event->y;
566
567 if (box->selection_flag)
568 {
569 rect.x = MIN (box->marked.x, box->select.x);
570 rect.y = MIN (box->marked.y, box->select.y);
571 rect.width = MAX (box->marked.x, box->select.x) - rect.x + 1;
572 rect.height = MAX (box->marked.x, box->select.x) - rect.y + 1;
573
574 gtk_databox_draw_selection (box->draw, box, &rect);
575 }
576
577 if (button == 1 || button == 2)
578 {
579 if (box->selection_flag)
580 {
581 box->selection_flag = 0;
582 if (rect.x < x && x < MAX (box->marked.x, box->select.x)
583 && rect.y < y && y < MAX (box->marked.y, box->select.y))
584 {
585 gtk_databox_zoom_to_selection (widget, box);
586 }
587 else
588 {
589 g_signal_emit (GTK_OBJECT (box),
590 gtk_databox_signals
591 [GTK_DATABOX_SELECTION_CANCELLED_SIGNAL], 0);
592 }
593 }
594 }
595 else if (button == 3)
596 {
597 if (box->selection_flag)
598 {
599 box->selection_flag = 0;
600 g_signal_emit (GTK_OBJECT (box),
601 gtk_databox_signals
602 [GTK_DATABOX_SELECTION_CANCELLED_SIGNAL], 0);
603
604 }
605 if (event->state & GDK_SHIFT_MASK)
606 {
607 gtk_databox_zoom_home (widget, box);
608 }
609 else
610 {
611 gtk_databox_zoom_out (widget, box);
612 }
613 }
614 box->marked.x = x;
615 box->marked.y = y;
616 g_signal_emit (GTK_OBJECT (box),
617 gtk_databox_signals[GTK_DATABOX_MARKED_SIGNAL], 0,
618 &box->marked);
619
620 return FALSE;
621 }
622
623 static gint
gtk_databox_button_release_callback(GtkWidget * widget,GdkEventButton * event,GtkDatabox * box)624 gtk_databox_button_release_callback (GtkWidget * widget,
625 GdkEventButton * event, GtkDatabox * box)
626 {
627 if (event->type != GDK_BUTTON_RELEASE)
628 return FALSE;
629
630 if (box->selection_flag)
631 {
632 box->flags |= 1 << GTK_DATABOX_SELECTION_STOPPED;
633
634 g_signal_emit (GTK_OBJECT (box),
635 gtk_databox_signals
636 [GTK_DATABOX_SELECTION_STOPPED_SIGNAL], 0, &box->marked,
637 &box->select);
638 }
639
640 return FALSE;
641 }
642
643 static gint
gtk_databox_motion_notify_callback(GtkWidget * widget,GdkEventMotion * event,GtkDatabox * box)644 gtk_databox_motion_notify_callback (GtkWidget * widget,
645 GdkEventMotion * event, GtkDatabox * box)
646 {
647 gint x, y;
648 GdkModifierType state;
649
650 x = event->x;
651 y = event->y;
652 state = event->state;
653
654 if (event->is_hint || (event->window != widget->window))
655 gdk_window_get_pointer (widget->window, &x, &y, &state);
656
657 if (state & GDK_BUTTON1_MASK
658 && (box->flags & (1 << GTK_DATABOX_ENABLE_SELECTION))
659 && !(box->flags & (1 << GTK_DATABOX_SELECTION_STOPPED)))
660 {
661 GdkRectangle rect;
662 gint width;
663 gint height;
664
665 gdk_drawable_get_size (widget->window, &width, &height);
666 x = MAX (0, MIN (width - 1, x));
667 y = MAX (0, MIN (height - 1, y));
668
669 if (box->selection_flag)
670 {
671 /* Clear current selection from pixmap */
672 gtk_databox_draw_selection (box->draw, box, NULL);
673 }
674 else
675 {
676 box->selection_flag = 1;
677 box->marked.x = x;
678 box->marked.y = y;
679 box->select.x = x;
680 box->select.y = y;
681 g_signal_emit (GTK_OBJECT (box),
682 gtk_databox_signals
683 [GTK_DATABOX_SELECTION_STARTED_SIGNAL], 0,
684 &box->marked);
685 }
686
687 /* Determine the exposure rectangle (covering old selection and new) */
688 rect.x = MIN (MIN (box->marked.x, box->select.x), x);
689 rect.y = MIN (MIN (box->marked.y, box->select.y), y);
690 rect.width = MAX (MAX (box->marked.x, box->select.x), x) - rect.x + 1;
691 rect.height = MAX (MAX (box->marked.y, box->select.y), y) - rect.y + 1;
692
693 box->select.x = x;
694 box->select.y = y;
695
696 /* Draw new selection */
697 gtk_databox_draw_selection (box->draw, box, &rect);
698
699 g_signal_emit (GTK_OBJECT (box),
700 gtk_databox_signals
701 [GTK_DATABOX_SELECTION_CHANGED_SIGNAL], 0, &box->marked,
702 &box->select);
703 }
704
705 return FALSE;
706 }
707
708 void
gtk_databox_data_get_value(GtkDatabox * box,GtkDataboxCoord point,GtkDataboxValue * coord)709 gtk_databox_data_get_value (GtkDatabox * box, GtkDataboxCoord point,
710 GtkDataboxValue * coord)
711 {
712 g_return_if_fail (GTK_IS_DATABOX (box) && coord);
713
714 coord->x =
715 box->top_left.x + point.x / box->factor.x;
716 coord->y =
717 box->top_left.y + point.y / box->factor.y;
718 }
719
720 void
gtk_databox_data_get_marked_value(GtkDatabox * box,GtkDataboxValue * coord)721 gtk_databox_data_get_marked_value (GtkDatabox * box, GtkDataboxValue * coord)
722 {
723 gtk_databox_data_get_value (box, box->marked, coord);
724 }
725
726 void
gtk_databox_data_get_delta_value(GtkDatabox * box,GtkDataboxValue * coord)727 gtk_databox_data_get_delta_value (GtkDatabox * box, GtkDataboxValue * coord)
728 {
729 GtkDataboxValue drooc;
730
731 g_return_if_fail (GTK_IS_DATABOX (box) && coord);
732
733 gtk_databox_data_get_value (box, box->marked, &drooc);
734 gtk_databox_data_get_value (box, box->select, coord);
735 coord->x -= drooc.x;
736 coord->y -= drooc.y;
737 }
738
739 static void
gtk_databox_data_calc_extrema(GtkDatabox * box,GtkDataboxValue * min,GtkDataboxValue * max)740 gtk_databox_data_calc_extrema (GtkDatabox * box, GtkDataboxValue * min,
741 GtkDataboxValue * max)
742 {
743 gint i;
744 GtkDataboxData *data = NULL;
745 GList *list = NULL;
746 GtkDataboxValue border;
747
748 g_return_if_fail (GTK_IS_DATABOX (box) && min && max);
749
750 if (!box->data)
751 {
752 min->x = -0.5;
753 min->y = -0.5;
754 max->x = 1.5;
755 max->y = 1.5;
756 return;
757 }
758
759 list = box->data;
760 if (list)
761 data = (GtkDataboxData *) list->data;
762 else
763 data = NULL;
764
765 min->x = data->X[0];
766 min->y = data->Y[0];
767 max->x = data->X[0];
768 max->y = data->Y[0];
769
770 while (data)
771 {
772 for (i = 0; i < data->length; i++)
773 {
774 min->x = MIN (data->X[i], min->x);
775 max->x = MAX (data->X[i], max->x);
776 }
777 for (i = 0; i < data->length; i++)
778 {
779 min->y = MIN (data->Y[i], min->y);
780 max->y = MAX (data->Y[i], max->y);
781 }
782 list = g_list_next (list);
783 if (list)
784 data = (GtkDataboxData *) list->data;
785 else
786 data = NULL;
787 }
788
789 border.x = (max->x - min->x) / 10.;
790 border.y = (max->y - min->y) / 10.;
791
792 min->x = min->x - border.x;
793 min->y = min->y - border.y;
794 max->x = max->x + border.x;
795 max->y = max->y + border.y;
796
797 return;
798 }
799
800 void
gtk_databox_data_get_extrema(GtkDatabox * box,GtkDataboxValue * min,GtkDataboxValue * max)801 gtk_databox_data_get_extrema (GtkDatabox * box, GtkDataboxValue * min,
802 GtkDataboxValue * max)
803 {
804 g_return_if_fail (GTK_IS_DATABOX (box) && min && max);
805
806 *min = box->min;
807 *max = box->max;
808
809 return;
810 }
811
812 void
gtk_databox_data_get_visible_extrema(GtkDatabox * box,GtkDataboxValue * min,GtkDataboxValue * max)813 gtk_databox_data_get_visible_extrema (GtkDatabox * box, GtkDataboxValue * min,
814 GtkDataboxValue * max)
815 {
816 /* GtkDataboxValue pre_min;
817 GtkDataboxValue pre_max;
818 GtkDataboxCoord top_left;
819 GtkDataboxCoord bottom_right;
820 */
821
822 g_return_if_fail (GTK_IS_DATABOX (box) && min && max);
823
824 /* top_left.x = top_left.y = 0;
825 bottom_right.x = box->size.x - 1;
826 bottom_right.y = box->size.y - 1;
827
828
829 gtk_databox_data_get_value (box, top_left, &pre_min);
830 gtk_databox_data_get_value (box, bottom_right, &pre_max);
831
832 min->x = MIN (pre_min.x, pre_max.x);
833 min->y = MIN (pre_min.y, pre_max.y);
834 max->x = MAX (pre_min.x, pre_max.x);
835 max->y = MAX (pre_min.y, pre_max.y);
836 */
837 min->x = box->top_left.x;
838 max->x = box->bottom_right.x;
839
840 min->y = box->bottom_right.y;
841 max->y = box->top_left.y;
842
843 return;
844 }
845
846 void
gtk_databox_rescale_with_values(GtkDatabox * box,GtkDataboxValue min,GtkDataboxValue max)847 gtk_databox_rescale_with_values (GtkDatabox * box, GtkDataboxValue min,
848 GtkDataboxValue max)
849 {
850 g_return_if_fail (GTK_IS_DATABOX (box));
851
852 box->min.x = min.x;
853 box->max.x = max.x;
854 box->min.y = min.y;
855 box->max.y = max.y;
856
857 if (box->max.x - box->min.x < 1e-10)
858 {
859 box->min.x -= 0.5e-10;
860 box->max.x += 0.5e-10;
861 }
862 if (box->max.y - box->min.y < 1e-10)
863 {
864 box->min.y -= 0.5e-10;
865 box->max.y += 0.5e-10;
866 }
867
868 gtk_databox_zoom_home (box->draw, box);
869 }
870
871 void
gtk_databox_rescale(GtkDatabox * box)872 gtk_databox_rescale (GtkDatabox * box)
873 {
874 GtkDataboxValue min, max;
875
876 g_return_if_fail (GTK_IS_DATABOX (box));
877
878 gtk_databox_data_calc_extrema (box, &min, &max);
879
880 gtk_databox_rescale_with_values (box, min, max);
881 }
882
883 void
gtk_databox_redraw(GtkDatabox * box)884 gtk_databox_redraw (GtkDatabox * box)
885 {
886 gtk_databox_draw_request_full (box->draw, TRUE, box);
887 }
888
889
890 static void
gtk_databox_zoom_to_selection(GtkWidget * widget,GtkDatabox * box)891 gtk_databox_zoom_to_selection (GtkWidget * widget, GtkDatabox * box)
892 {
893 if (!(box->flags & (1 << GTK_DATABOX_ENABLE_ZOOM)))
894 return;
895
896 /* We always scroll from 0 to 1.0 */
897 box->adjX->lower = 0;
898 box->adjX->upper = 1.0;
899
900 /* The left border of the selection box is used for calculating
901 * the left end of the visible "page" in scollbar-coordinates
902 * (remember: we scroll from 0 to 1.0)
903 */
904 box->adjX->value +=
905 (gfloat) (MIN (box->marked.x, box->select.x)) * box->adjX->page_size /
906 box->size.x;
907
908 /* The size of the selection box is used for calculating
909 * the size of the visible "page" in scollbar-coordinates
910 * (remember: we scroll from 0 to 1.0)
911 */
912 box->adjX->page_size *=
913 (gfloat) (ABS (box->marked.x - box->select.x) + 1) / box->size.x;
914
915 /* If we zoom to far into the data, we will get funny results, because
916 * of overflow effects. Therefore zooming is limited to box->zoom_limit.
917 */
918 if (box->adjX->page_size < box->zoom_limit)
919 {
920 box->adjX->value = (gfloat) MAX (0,
921 box->adjX->value
922 - (box->zoom_limit - box->adjX->page_size) / 2.0);
923 box->adjX->page_size = box->zoom_limit;
924 }
925
926 /* Setting the scroll increments */
927 box->adjX->step_increment = box->adjX->page_size / 20;
928 box->adjX->page_increment = box->adjX->page_size * 0.9;
929
930 /* And now the analog steps for the vertical scrollbar */
931 box->adjY->lower = 0;
932 box->adjY->upper = 1.0;
933
934 box->adjY->value +=
935 (gfloat) (MIN (box->marked.y, box->select.y)) * box->adjY->page_size /
936 box->size.y;
937
938 box->adjY->page_size *=
939 (gfloat) (ABS (box->marked.y - box->select.y) + 1) / box->size.y;
940
941 if (box->adjY->page_size < box->zoom_limit)
942 {
943 box->adjY->value = (gfloat) MAX (0,
944 box->adjY->value
945 - (box->zoom_limit - box->adjY->page_size) / 2.0);
946 box->adjY->page_size = box->zoom_limit;
947 }
948
949 box->adjY->step_increment = box->adjY->page_size / 20;
950 box->adjY->page_increment = box->adjY->page_size * 0.9;
951
952 gtk_databox_zoomed (widget, box, TRUE);
953 }
954
955 static void
gtk_databox_zoom_out(GtkWidget * widget,GtkDatabox * box)956 gtk_databox_zoom_out (GtkWidget * widget, GtkDatabox * box)
957 {
958 if (!(box->flags & (1 << GTK_DATABOX_ENABLE_ZOOM)))
959 return;
960
961 box->adjX->lower = 0;
962 box->adjY->lower = 0;
963 box->adjX->page_size = MIN (1.0, box->adjX->page_size * 2);
964 box->adjY->page_size = MIN (1.0, box->adjY->page_size * 2);
965 box->adjX->value =
966 (box->adjX->page_size ==
967 1.0) ? 0 : (MAX (0, (box->adjX->value - box->adjX->page_size / 4)));
968 box->adjY->value =
969 (box->adjY->page_size ==
970 1.0) ? 0 : (MAX (0, (box->adjY->value - box->adjY->page_size / 4)));
971 box->adjX->upper = 1.0;
972 box->adjY->upper = 1.0;
973 box->adjY->step_increment = box->adjY->page_size / 20;
974 box->adjY->page_increment = box->adjY->page_size * 0.9;
975 box->adjX->step_increment = box->adjX->page_size / 20;
976 box->adjX->page_increment = box->adjX->page_size * 0.9;
977
978 gtk_databox_zoomed (widget, box, TRUE);
979 }
980
981 static void
gtk_databox_zoom_home(GtkWidget * widget,GtkDatabox * box)982 gtk_databox_zoom_home (GtkWidget * widget, GtkDatabox * box)
983 {
984
985 if (!(box->flags & (1 << GTK_DATABOX_ENABLE_ZOOM)))
986 return;
987
988 box->selection_flag = 0;
989
990 box->adjX->lower = 0;
991 box->adjY->lower = 0;
992 box->adjX->page_size = 1.0;
993 box->adjY->page_size = 1.0;
994 box->adjX->value = 0;
995 box->adjY->value = 0;
996 box->adjX->upper = 1.0;
997 box->adjY->upper = 1.0;
998 box->adjY->step_increment = box->adjY->page_size / 20;
999 box->adjY->page_increment = box->adjY->page_size * 0.9;
1000 box->adjX->step_increment = box->adjX->page_size / 20;
1001 box->adjX->page_increment = box->adjX->page_size * 0.9;
1002
1003 gtk_databox_zoomed (widget, box, TRUE);
1004 }
1005
1006 static void
gtk_databox_zoomed(GtkWidget * widget,GtkDatabox * box,gboolean redraw_flag)1007 gtk_databox_zoomed (GtkWidget * widget, GtkDatabox * box,
1008 gboolean redraw_flag)
1009 {
1010 /* This function is called after configure events even if zoom
1011 * is disabled, and we need it because box->factor is re-calculated here.
1012 * (It is also called after zoom events, of course...)
1013 */
1014 box->flags |= 1 << GTK_DATABOX_REDRAW_REQUEST;
1015
1016 gtk_adjustment_changed (box->adjX);
1017 gtk_adjustment_changed (box->adjY);
1018 gtk_databox_x_adjustment_callback (widget, box);
1019 gtk_databox_y_adjustment_callback (widget, box);
1020
1021 box->factor.x = box->size.x / (box->bottom_right.x - box->top_left.x);
1022 box->factor.y = box->size.y / (box->bottom_right.y - box->top_left.y);
1023
1024 if (redraw_flag)
1025 {
1026 box->flags &= ~(1 << GTK_DATABOX_REDRAW_REQUEST);
1027 gtk_databox_draw_request_full (box->draw, TRUE, box);
1028 }
1029
1030 g_signal_emit (GTK_OBJECT (box),
1031 gtk_databox_signals[GTK_DATABOX_ZOOMED_SIGNAL], 0,
1032 &box->top_left, &box->bottom_right);
1033 }
1034
1035 static void
gtk_databox_x_adjustment_callback(GtkWidget * widget,GtkDatabox * box)1036 gtk_databox_x_adjustment_callback (GtkWidget * widget, GtkDatabox * box)
1037 {
1038 if (box->adjX->page_size == 1.0)
1039 {
1040 box->top_left.x = box->min.x;
1041 box->bottom_right.x = box->max.x;
1042 }
1043 else
1044 {
1045 box->top_left.x =
1046 box->min.x + (box->max.x - box->min.x) * box->adjX->value;
1047 box->bottom_right.x =
1048 box->top_left.x + (box->max.x - box->min.x) * box->adjX->page_size;
1049 }
1050
1051 gtk_databox_update_x_ruler (box);
1052 gtk_databox_draw_request_full (box->draw, TRUE, box);
1053 }
1054
1055 static void
gtk_databox_y_adjustment_callback(GtkWidget * widget,GtkDatabox * box)1056 gtk_databox_y_adjustment_callback (GtkWidget * widget, GtkDatabox * box)
1057 {
1058 if (box->adjY->page_size == 1.0)
1059 {
1060 box->top_left.y = box->max.y;
1061 box->bottom_right.y = box->min.y;
1062 }
1063 else
1064 {
1065 box->top_left.y =
1066 box->max.y - (box->max.y - box->min.y) * box->adjY->value;
1067 box->bottom_right.y =
1068 box->top_left.y - (box->max.y - box->min.y) * box->adjY->page_size;
1069 }
1070
1071 gtk_databox_update_y_ruler (box);
1072 gtk_databox_draw_request_full (box->draw, TRUE, box);
1073 }
1074
1075 static void
gtk_databox_update_x_ruler(GtkDatabox * box)1076 gtk_databox_update_x_ruler (GtkDatabox * box)
1077 {
1078 if (box->flags & (1 << GTK_DATABOX_SHOW_RULERS))
1079 {
1080 gtk_ruler_set_range (GTK_RULER (box->hrule), box->top_left.x,
1081 box->bottom_right.x,
1082 0.5 * (box->top_left.x + box->bottom_right.x), 20);
1083 }
1084 }
1085
1086 static void
gtk_databox_update_y_ruler(GtkDatabox * box)1087 gtk_databox_update_y_ruler (GtkDatabox * box)
1088 {
1089 if (box->flags & (1 << GTK_DATABOX_SHOW_RULERS))
1090 {
1091 gtk_ruler_set_range (GTK_RULER (box->vrule), box->top_left.y,
1092 box->bottom_right.y,
1093 0.5 * (box->top_left.y + box->bottom_right.y), 20);
1094 }
1095 }
1096
1097 static void
gtk_databox_draw_request_full(GtkWidget * widget,gboolean now,GtkDatabox * box)1098 gtk_databox_draw_request_full (GtkWidget * widget, gboolean now,
1099 GtkDatabox * box)
1100 {
1101 GdkRectangle redraw_rect;
1102
1103 redraw_rect.x = 0;
1104 redraw_rect.y = 0;
1105 redraw_rect.width = box->size.x;
1106 redraw_rect.height = box->size.y;
1107
1108 if (box->flags & (1 << GTK_DATABOX_REDRAW_REQUEST))
1109 {
1110 return;
1111 }
1112
1113 box->flags |= 1 << GTK_DATABOX_REDRAW_REQUEST;
1114
1115 if (now)
1116 gtk_widget_queue_draw_area (widget, 0, 0, box->size.x, box->size.y);
1117 /* FIXME: Maybe we want to send an expose event instead. This guarantees
1118 immediate redrawing */
1119 /* gtk_widget_draw (widget, &redraw_rect);*/
1120 }
1121
1122 static gint
gtk_databox_draw_selection(GtkWidget * widget,GtkDatabox * box,GdkRectangle * rect)1123 gtk_databox_draw_selection (GtkWidget * widget, GtkDatabox * box,
1124 GdkRectangle * rect)
1125 {
1126 if (!box->select_gc)
1127 {
1128 gboolean color_allocate_success;
1129 GdkGCValues values;
1130 GdkColormap *colormap;
1131 GdkColor color;
1132
1133 color.red = 65535;
1134 color.green = 65535;
1135 color.blue = 65535;
1136 colormap = gtk_widget_get_colormap (widget);
1137 color_allocate_success = gdk_colormap_alloc_color (colormap,
1138 &color,
1139 FALSE,
1140 TRUE);
1141 /* FIXME We should add a message here ... */
1142 g_return_val_if_fail (color_allocate_success, FALSE);
1143
1144 values.foreground = color;
1145 values.function = GDK_XOR;
1146 box->select_gc =
1147 gdk_gc_new_with_values (widget->window, &values,
1148 GDK_GC_FUNCTION | GDK_GC_FOREGROUND);
1149 }
1150
1151
1152 gdk_draw_rectangle (box->pixmap, box->select_gc,
1153 box->flags & (1 << GTK_DATABOX_SHOW_SELECTION_FILLED),
1154 MIN (box->marked.x, box->select.x), MIN (box->marked.y,
1155 box->select.
1156 y),
1157 ABS (box->marked.x - box->select.x),
1158 ABS (box->marked.y - box->select.y));
1159
1160 if (rect)
1161 gdk_draw_drawable (widget->window,
1162 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1163 box->pixmap, rect->x, rect->y, rect->x, rect->y,
1164 rect->width, rect->height);
1165
1166
1167 return TRUE;
1168 }
1169
1170 gint
gtk_databox_data_get_color(GtkDatabox * box,gint index,GdkColor * color)1171 gtk_databox_data_get_color (GtkDatabox * box, gint index, GdkColor * color)
1172 {
1173 GtkDataboxData *data = NULL;
1174
1175 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1176 g_return_val_if_fail (color, -1);
1177
1178 data = (GtkDataboxData *) g_list_nth_data (box->data, index);
1179 g_return_val_if_fail (data, -1);
1180
1181 *color = data->color;
1182
1183 return 0;
1184 }
1185
1186 gint
gtk_databox_data_get_type(GtkDatabox * box,gint index,GtkDataboxDataType * type,guint * dot_size)1187 gtk_databox_data_get_type (GtkDatabox * box, gint index,
1188 GtkDataboxDataType * type, guint * dot_size)
1189 {
1190 GtkDataboxData *data = NULL;
1191
1192 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1193 g_return_val_if_fail (type, -1);
1194 g_return_val_if_fail (dot_size, -1);
1195
1196 data = (GtkDataboxData *) g_list_nth_data (box->data, index);
1197 g_return_val_if_fail (data, -1);
1198
1199 *type = data->type;
1200 *dot_size = data->size;
1201
1202 return 0;
1203 }
1204
1205 gint
gtk_databox_data_set_type(GtkDatabox * box,gint index,GtkDataboxDataType type,guint dot_size)1206 gtk_databox_data_set_type (GtkDatabox * box, gint index,
1207 GtkDataboxDataType type, guint dot_size)
1208 {
1209 GtkDataboxData *data = NULL;
1210
1211 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1212
1213 data = (GtkDataboxData *) g_list_nth_data (box->data, index);
1214 g_return_val_if_fail (data, -1);
1215
1216 if (data->flags & (1 << GTK_DATABOX_DATA_HAS_GC))
1217 {
1218 g_object_unref (data->gc);
1219 data->flags &= ~(1 << GTK_DATABOX_DATA_HAS_GC);
1220 }
1221
1222 switch (type)
1223 {
1224 case GTK_DATABOX_NOT_DISPLAYED:
1225 data->draw = GTK_DATABOX_DRAW_FUNC (NULL);
1226 break;
1227 case GTK_DATABOX_POINTS:
1228 data->draw = GTK_DATABOX_DRAW_FUNC (gtk_databox_draw_points);
1229 break;
1230 case GTK_DATABOX_LINES:
1231 data->draw = GTK_DATABOX_DRAW_FUNC (gtk_databox_draw_lines);
1232 break;
1233 case GTK_DATABOX_BARS:
1234 data->draw = GTK_DATABOX_DRAW_FUNC (gtk_databox_draw_bars);
1235 break;
1236 case GTK_DATABOX_CROSS_SIMPLE:
1237 data->draw = GTK_DATABOX_DRAW_FUNC (gtk_databox_draw_cross_simple);
1238 break;
1239 case GTK_DATABOX_GRID:
1240 data->draw = GTK_DATABOX_DRAW_FUNC (gtk_databox_draw_grid);
1241 break;
1242
1243 default:
1244 data->draw = GTK_DATABOX_DRAW_FUNC (NULL);
1245 }
1246
1247 data->type = type;
1248 data->size = dot_size;
1249
1250 return 0;
1251 }
1252
1253 gint
gtk_databox_data_set_color(GtkDatabox * box,gint index,GdkColor color)1254 gtk_databox_data_set_color (GtkDatabox * box, gint index, GdkColor color)
1255 {
1256 GtkDataboxData *data = NULL;
1257
1258 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1259
1260 data = (GtkDataboxData *) g_list_nth_data (box->data, index);
1261 g_return_val_if_fail (data, -1);
1262
1263 if (data->flags & (1 << GTK_DATABOX_DATA_HAS_GC))
1264 {
1265 g_object_unref (data->gc);
1266 data->flags &= ~(1 << GTK_DATABOX_DATA_HAS_GC);
1267 }
1268
1269 data->color = color;
1270
1271 return 0;
1272 }
1273
1274 gint
gtk_databox_data_get_grid_config(GtkDatabox * box,gint index,guint * hlines,guint * vlines)1275 gtk_databox_data_get_grid_config (GtkDatabox * box, gint index,
1276 guint * hlines, guint * vlines)
1277 {
1278 GtkDataboxData *data = NULL;
1279
1280 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1281
1282 data = (GtkDataboxData *) g_list_nth_data (box->data, index);
1283 g_return_val_if_fail (data, -1);
1284
1285 *hlines = data->hlines;
1286 *vlines = data->vlines;
1287
1288 return 0;
1289
1290 }
1291
1292 gint
gtk_databox_data_set_grid_config(GtkDatabox * box,gint index,guint hlines,guint vlines)1293 gtk_databox_data_set_grid_config (GtkDatabox * box, gint index, guint hlines,
1294 guint vlines)
1295 {
1296 GtkDataboxData *data = NULL;
1297
1298 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1299
1300 data = (GtkDataboxData *) g_list_nth_data (box->data, index);
1301 g_return_val_if_fail (data, -1);
1302
1303 data->hlines = hlines;
1304 data->vlines = vlines;
1305
1306 return 0;
1307 }
1308
1309 static void
gtk_databox_new_data_gc(GtkWidget * widget,GtkDatabox * box,GtkDataboxData * data)1310 gtk_databox_new_data_gc (GtkWidget * widget, GtkDatabox * box,
1311 GtkDataboxData * data)
1312 {
1313 GdkGCValues values;
1314 gboolean color_allocate_success;
1315 GdkColormap *colormap = NULL;
1316
1317 g_return_if_fail (GTK_IS_DATABOX (box));
1318 g_return_if_fail (GTK_IS_WIDGET (widget));
1319 g_return_if_fail (data);
1320
1321 colormap = gtk_widget_get_colormap (widget);
1322 g_return_if_fail (colormap);
1323 color_allocate_success = gdk_colormap_alloc_color (colormap,
1324 &data->color,
1325 FALSE,
1326 TRUE);
1327 /* FIXME We should add a message here ... */
1328 g_return_if_fail (color_allocate_success);
1329
1330 values.foreground = data->color;
1331 values.function = GDK_COPY;
1332 values.line_width = data->size;
1333 if (data->type == GTK_DATABOX_GRID)
1334 {
1335 values.line_style = GDK_LINE_ON_OFF_DASH;
1336 values.cap_style = GDK_CAP_BUTT;
1337 values.join_style = GDK_JOIN_MITER;
1338 }
1339 else
1340 {
1341 values.line_style = GDK_LINE_SOLID;
1342 values.cap_style = GDK_CAP_BUTT;
1343 values.join_style = GDK_JOIN_MITER;
1344 }
1345 data->gc =
1346 gdk_gc_new_with_values (widget->window, &values,
1347 GDK_GC_FUNCTION | GDK_GC_FOREGROUND |
1348 GDK_GC_LINE_WIDTH | GDK_GC_LINE_STYLE |
1349 GDK_GC_CAP_STYLE | GDK_GC_JOIN_STYLE);
1350
1351 data->flags |= 1 << GTK_DATABOX_DATA_HAS_GC;
1352 }
1353
1354 static void
gtk_databox_draw(GtkWidget * widget,GtkDatabox * box,GdkEventExpose * event)1355 gtk_databox_draw (GtkWidget * widget, GtkDatabox * box,
1356 GdkEventExpose * event)
1357 {
1358 GList *list = NULL;
1359 GtkDataboxData *data = NULL;
1360
1361 box->flags &= ~(1 << GTK_DATABOX_REDRAW_REQUEST);
1362
1363 g_return_if_fail (GTK_IS_DATABOX (box));
1364 if (!GTK_WIDGET_VISIBLE (widget))
1365 return;
1366
1367 gdk_draw_rectangle (box->pixmap, widget->style->bg_gc[0], TRUE, 0, 0,
1368 box->size.x, box->size.y);
1369
1370 if (!box->data || !box->max_points)
1371 return;
1372
1373 /* Draw last data set first so first is on top */
1374 list = g_list_last (box->data);
1375 if (list)
1376 data = (GtkDataboxData *) list->data;
1377 else
1378 data = NULL;
1379
1380 while (data)
1381 {
1382 if (!data->gc || !(data->flags & (1 << GTK_DATABOX_DATA_HAS_GC)))
1383 {
1384 gtk_databox_new_data_gc (widget, box, data);
1385 }
1386 if (data->length && data->draw)
1387 {
1388 data->draw (box, data);
1389 }
1390
1391 list = g_list_previous (list);
1392 if (list)
1393 data = (GtkDataboxData *) list->data;
1394 else
1395 data = NULL;
1396 }
1397
1398 if (box->selection_flag)
1399 {
1400 gtk_databox_draw_selection (widget, box, NULL);
1401 }
1402
1403 return;
1404 }
1405
1406 static void
gtk_databox_draw_points(GtkDatabox * box,GtkDataboxData * data)1407 gtk_databox_draw_points (GtkDatabox * box, GtkDataboxData * data)
1408 {
1409 gint i = 0;
1410 gint count = 0;
1411
1412
1413 for (i = 0; i < data->length; i++)
1414 {
1415 box->points[i].x = (gint16) ((data->X[i] - box->top_left.x) * box->factor.x);
1416 box->points[i].y = (gint16) ((data->Y[i] - box->top_left.y) * box->factor.y);
1417 }
1418 count = data->length;
1419 if (data->size < 2)
1420 {
1421 /* More than 2^16 points will cause X IO error on most XServers
1422 (Hint from Paul Barton-Davis <pbd@Op.Net>) */
1423 for (i = 0; i < count; i += 65536)
1424 {
1425 gdk_draw_points (box->pixmap, data->gc, box->points + i,
1426 MIN (65536, count - i));
1427 }
1428 }
1429 else
1430 {
1431 for (i = 0; i < count; i++)
1432 {
1433 /* Why on earth is there no gdk_draw_rectangles?? */
1434 gdk_draw_rectangle (box->pixmap, data->gc, TRUE,
1435 box->points[i].x - data->size / 2,
1436 box->points[i].y - data->size / 2, data->size,
1437 data->size);
1438 }
1439 }
1440 }
1441
1442 static void
gtk_databox_draw_lines(GtkDatabox * box,GtkDataboxData * data)1443 gtk_databox_draw_lines (GtkDatabox * box, GtkDataboxData * data)
1444 {
1445 gint i;
1446
1447 for (i = 0; i < data->length; i++)
1448 {
1449 box->points[i].x = (gint16) ((data->X[i] - box->top_left.x) * box->factor.x);
1450 box->points[i].y = (gint16) ((data->Y[i] - box->top_left.y) * box->factor.y);
1451 }
1452
1453 /* More than 2^16 points will cause X IO error on most XServers
1454 (Hint from Paul Barton-Davis <pbd@Op.Net>) */
1455 for (i = 0; i < data->length; i += 65535)
1456 {
1457 gdk_draw_lines (box->pixmap, data->gc, box->points + i,
1458 MIN (65536, data->length - i));
1459 }
1460 }
1461
1462 static void
gtk_databox_draw_bars(GtkDatabox * box,GtkDataboxData * data)1463 gtk_databox_draw_bars (GtkDatabox * box, GtkDataboxData * data)
1464 {
1465 gint i = 0;
1466 gint count = 0;
1467 GdkSegment *segments = (GdkSegment *) box->points;
1468 gfloat axis = 0;
1469
1470 axis = ((0 - box->top_left.y) * box->factor.y);
1471 for (i = 0; i < data->length; i++)
1472 {
1473 segments[i].x1 = segments[i].x2 =
1474 (gint16) ((data->X[i] - box->top_left.x) * box->factor.x);
1475 segments[i].y1 = axis;
1476 segments[i].y2 = (gint16) ((data->Y[i] - box->top_left.y) * box->factor.y);
1477 }
1478 count = data->length;
1479 for (i = 0; i < count; i += 65536)
1480 {
1481 gdk_draw_segments (box->pixmap, data->gc, segments,
1482 MIN (65536, count - i));
1483 }
1484 }
1485
1486 static void
gtk_databox_draw_cross_simple(GtkDatabox * box,GtkDataboxData * data)1487 gtk_databox_draw_cross_simple (GtkDatabox * box, GtkDataboxData * data)
1488 {
1489 gint x = 0;
1490 gint y = 0;
1491 gboolean xflag = FALSE;
1492 gboolean yflag = FALSE;
1493
1494 if (0 >= box->top_left.x && 0 < box->bottom_right.x)
1495 {
1496 x = (0 - box->top_left.x) * box->factor.x;
1497 if (x >= CROSS_BORDER && x < box->size.x - CROSS_BORDER)
1498 {
1499 gdk_draw_line (box->pixmap, data->gc, x, CROSS_BORDER, x,
1500 box->size.y - CROSS_BORDER);
1501 xflag = TRUE;
1502 }
1503 }
1504 if (0 <= box->top_left.y && 0 > box->bottom_right.y)
1505 {
1506 y = (0 - box->top_left.y) * box->factor.y;
1507 if (y >= CROSS_BORDER && y < box->size.y - CROSS_BORDER)
1508 {
1509 gdk_draw_line (box->pixmap, data->gc, CROSS_BORDER, y,
1510 box->size.x - CROSS_BORDER, y);
1511 yflag = TRUE;
1512 }
1513 }
1514 }
1515
1516 static void
gtk_databox_draw_grid(GtkDatabox * box,GtkDataboxData * data)1517 gtk_databox_draw_grid (GtkDatabox * box, GtkDataboxData * data)
1518 {
1519 gint x = 0;
1520 gint y = 0;
1521 gint i = 0;
1522 guint v_lines = data->vlines;
1523 guint h_lines = data->hlines;
1524
1525 for (i = 0; i < v_lines; i++)
1526 {
1527 x = box->min.x + (i + 1) * (box->max.x - box->min.x) / (v_lines + 1);
1528 x = (gint16) ((x - box->top_left.x) * box->factor.x);
1529 gdk_draw_line (box->pixmap, data->gc, x, 0, x, box->size.y);
1530 }
1531 for (i = 0; i < h_lines; i++)
1532 {
1533 y = box->min.y + (i + 1) * (box->max.y - box->min.y) / (v_lines + 1);
1534 y = (gint16) ((y - box->top_left.y) * box->factor.y);
1535 gdk_draw_line (box->pixmap, data->gc, 0, y, box->size.x, y);
1536 }
1537 }
1538
1539
1540 gint
gtk_databox_data_add_x_y(GtkDatabox * box,guint length,gfloat * X,gfloat * Y,GdkColor color,GtkDataboxDataType type,guint dot_size)1541 gtk_databox_data_add_x_y (GtkDatabox * box, guint length, gfloat * X,
1542 gfloat * Y, GdkColor color, GtkDataboxDataType type,
1543 guint dot_size)
1544 {
1545 GtkDataboxData *data;
1546 guint index;
1547
1548 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1549 g_return_val_if_fail (X, -1);
1550 g_return_val_if_fail (Y, -1);
1551 g_return_val_if_fail (length, -1);
1552
1553 box->max_points = MAX (length, box->max_points);
1554 if (box->max_points)
1555 {
1556 /* We need twice as many GdkPoints, because draw bars uses segments
1557 * and one segment equals two points.
1558 */
1559 box->points = (GdkPoint *) g_realloc (box->points,
1560 sizeof (GdkPoint) * box->max_points * 2);
1561 }
1562 data = g_new0 (GtkDataboxData, 1);
1563
1564 data->X = X;
1565 data->Y = Y;
1566 data->length = length;
1567 data->flags = 0;
1568 data->gc = NULL;
1569
1570 box->data = g_list_append (box->data, data);
1571 index = g_list_length (box->data) - 1;
1572
1573 gtk_databox_data_set_type (box, index, type, dot_size);
1574 gtk_databox_data_set_color (box, index, color);
1575
1576 return index;
1577 }
1578
1579 gint
gtk_databox_data_add_x(GtkDatabox * box,guint length,gfloat * X,gint shared_Y_index,GdkColor color,GtkDataboxDataType type,guint dot_size)1580 gtk_databox_data_add_x (GtkDatabox * box, guint length, gfloat * X,
1581 gint shared_Y_index, GdkColor color,
1582 GtkDataboxDataType type, guint dot_size)
1583 {
1584 GtkDataboxData *data;
1585
1586 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1587 g_return_val_if_fail (X, -1);
1588
1589 data = (GtkDataboxData *) g_list_nth_data (box->data, shared_Y_index);
1590 g_return_val_if_fail (data, -1);
1591 g_return_val_if_fail (data->length == length, -1);
1592
1593 return gtk_databox_data_add_x_y (box, length, X, data->Y, color, type,
1594 dot_size);
1595 }
1596
1597 gint
gtk_databox_data_add_y(GtkDatabox * box,guint length,gfloat * Y,gint shared_X_index,GdkColor color,GtkDataboxDataType type,guint dot_size)1598 gtk_databox_data_add_y (GtkDatabox * box, guint length, gfloat * Y,
1599 gint shared_X_index, GdkColor color,
1600 GtkDataboxDataType type, guint dot_size)
1601 {
1602 GtkDataboxData *data;
1603
1604 g_return_val_if_fail (GTK_IS_DATABOX (box), -1);
1605 g_return_val_if_fail (Y, -1);
1606
1607 data = (GtkDataboxData *) g_list_nth_data (box->data, shared_X_index);
1608 g_return_val_if_fail (data, -1);
1609 g_return_val_if_fail (data->length == length, -1);
1610
1611 return gtk_databox_data_add_x_y (box, length, data->X, Y, color, type,
1612 dot_size);
1613 }
1614
1615 static gint
gtk_databox_check_x_links(GList * list,gfloat * values)1616 gtk_databox_check_x_links (GList * list, gfloat * values)
1617 {
1618 GtkDataboxData *data;
1619 gint counter = 0;
1620
1621 if (list)
1622 data = (GtkDataboxData *) list->data;
1623 else
1624 return 0;
1625
1626 while (data)
1627 {
1628 if (data->X == values)
1629 counter++;
1630 list = g_list_next (list);
1631 if (list)
1632 data = (GtkDataboxData *) list->data;
1633 else
1634 data = NULL;
1635 }
1636
1637 return counter;
1638 }
1639
1640 static gint
gtk_databox_check_y_links(GList * list,gfloat * values)1641 gtk_databox_check_y_links (GList * list, gfloat * values)
1642 {
1643 GtkDataboxData *data;
1644 gint counter = 0;
1645
1646 if (list)
1647 data = (GtkDataboxData *) list->data;
1648 else
1649 return 0;
1650
1651 while (data)
1652 {
1653 if (data->Y == values)
1654 counter++;
1655 list = g_list_next (list);
1656 if (list)
1657 data = (GtkDataboxData *) list->data;
1658 else
1659 data = NULL;
1660 }
1661
1662 return counter;
1663 }
1664
1665 static void
gtk_databox_destroy_data(GtkDatabox * box,GtkDataboxData * data,GList * list,gboolean free_flag)1666 gtk_databox_destroy_data (GtkDatabox * box, GtkDataboxData * data,
1667 GList * list, gboolean free_flag)
1668 {
1669 GdkColormap *colormap;
1670
1671 if (free_flag && gtk_databox_check_x_links (box->data, data->X) == 1)
1672 {
1673 g_free (data->X);
1674 }
1675 if (free_flag && gtk_databox_check_y_links (box->data, data->Y) == 1)
1676 {
1677 g_free (data->Y);
1678 }
1679 if (data->flags & (1 << GTK_DATABOX_DATA_HAS_GC))
1680 {
1681 colormap = gtk_widget_get_colormap (box->draw);
1682 gdk_colormap_free_colors (colormap, &data->color, 1);
1683 }
1684 if (data->gc)
1685 g_object_unref (data->gc);
1686
1687 g_free (data);
1688
1689 }
1690
1691 gint
gtk_databox_data_destroy_all_with_flag(GtkDatabox * box,gboolean free_flag)1692 gtk_databox_data_destroy_all_with_flag (GtkDatabox * box, gboolean free_flag)
1693 {
1694 GList *list = NULL;
1695 GtkDataboxData *data = NULL;
1696
1697 g_return_val_if_fail (GTK_IS_DATABOX (box), 0);
1698
1699 if (!box->data)
1700 return 0;
1701
1702 list = box->data;
1703 if (list)
1704 data = (GtkDataboxData *) list->data;
1705 else
1706 data = NULL;
1707
1708 while (data)
1709 {
1710 gtk_databox_destroy_data (box, data, list, free_flag);
1711
1712 list = g_list_next (list);
1713 if (list)
1714 data = (GtkDataboxData *) list->data;
1715 else
1716 data = NULL;
1717 }
1718
1719 g_list_free (box->data);
1720
1721 box->data = NULL;
1722 box->max_points = 0;
1723
1724 g_free (box->points);
1725 box->points = NULL;
1726
1727 return 0;
1728 }
1729
1730 gint
gtk_databox_data_destroy_with_flag(GtkDatabox * box,gint index,gboolean free_flag)1731 gtk_databox_data_destroy_with_flag (GtkDatabox * box, gint index,
1732 gboolean free_flag)
1733 {
1734 GList *list = NULL;
1735 GtkDataboxData *data = NULL;
1736
1737 g_return_val_if_fail (GTK_IS_DATABOX (box), 0);
1738
1739 if (!box->data)
1740 return -1;
1741
1742 list = g_list_nth (box->data, index);
1743 if (list)
1744 data = (GtkDataboxData *) list->data;
1745 else
1746 return -1;
1747
1748 gtk_databox_destroy_data (box, data, list, free_flag);
1749
1750 box->data = g_list_remove_link (box->data, list);
1751 g_list_free_1 (list);
1752
1753 return 0;
1754 }
1755
1756 gint
gtk_databox_data_remove_all(GtkDatabox * box)1757 gtk_databox_data_remove_all (GtkDatabox * box)
1758 {
1759 return gtk_databox_data_destroy_all_with_flag (box, FALSE);
1760 }
1761
1762 gint
gtk_databox_data_remove(GtkDatabox * box,gint index)1763 gtk_databox_data_remove (GtkDatabox * box, gint index)
1764 {
1765 return gtk_databox_data_destroy_with_flag (box, index, FALSE);
1766 }
1767
1768 gint
gtk_databox_data_destroy_all(GtkDatabox * box)1769 gtk_databox_data_destroy_all (GtkDatabox * box)
1770 {
1771 return gtk_databox_data_destroy_all_with_flag (box, TRUE);
1772 }
1773
1774 gint
gtk_databox_data_destroy(GtkDatabox * box,gint index)1775 gtk_databox_data_destroy (GtkDatabox * box, gint index)
1776 {
1777 return gtk_databox_data_destroy_with_flag (box, index, TRUE);
1778 }
1779