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