Argh, forgot the _U_:s
[obnox/wireshark/wip.git] / gtk / tcp_graph.c
1 /* tcp_graph.c
2  * TCP graph drawing code
3  * By Pavel Mores <pvl@uh.cz>
4  * Win32 port:  rwh@unifiedtech.com
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdio.h>
32 #include <math.h>
33 #include <string.h>
34
35 #if defined(GDK_DISABLE_DEPRECATED)
36 # undef GDK_DISABLE_DEPRECATED
37 #endif
38
39 #include <gtk/gtk.h>
40 #include <gdk/gdkkeysyms.h>
41 #if GTK_CHECK_VERSION(3,0,0)
42 # include <gdk/gdkkeysyms-compat.h>
43 #endif
44
45 #include <epan/packet.h>
46 #include <epan/ipproto.h>
47 #include <epan/etypes.h>
48 #include <epan/ppptypes.h>
49 #include <epan/epan_dissect.h>
50 #include <epan/dissectors/packet-tcp.h>
51 #include <epan/address.h>
52 #include <epan/tap.h>
53
54 #include "../globals.h"
55 #include "../simple_dialog.h"
56 #include "../stat_menu.h"
57
58 #include "gtk/gui_utils.h"
59 #include "gtk/dlg_utils.h"
60 #include "gtk/gui_stat_menu.h"
61
62 #include "gtk/old-gtk-compat.h"
63
64 #define TH_FIN    0x01
65 #define TH_SYN    0x02
66 #define TH_RST    0x04
67 #define TH_PUSH   0x08
68 #define TH_ACK    0x10
69 #define TH_URG    0x20
70
71 #define TCP_SYN(flags)          ( flags & TH_SYN )
72 #define TCP_ACK(flags)          ( flags & TH_ACK )
73 #define TCP_FIN(flags)          ( flags & TH_FIN )
74
75 #define TXT_WIDTH       850
76 #define TXT_HEIGHT      550
77
78 /* for compare_headers() */
79 /* segment went the same direction as the currently selected one */
80 #define COMPARE_CURR_DIR        0
81 #define COMPARE_ANY_DIR         1
82
83 /* initalize_axis() */
84 #define AXIS_HORIZONTAL         0
85 #define AXIS_VERTICAL           1
86
87 #define WINDOW_TITLE_LENGTH 256
88
89 #define MOUSE_BUTTON_LEFT       1
90 #define MOUSE_BUTTON_MIDDLE     2
91 #define MOUSE_BUTTON_RIGHT      3
92
93 struct segment {
94         struct segment *next;
95         guint32 num;
96         guint32 rel_secs;
97         guint32 rel_usecs;
98         guint32 abs_secs;
99         guint32 abs_usecs;
100
101         guint32 th_seq;
102         guint32 th_ack;
103         guint16 th_flags;
104         guint32 th_win;   /* make it 32 bits so we can handle some scaling */
105         guint32 th_seglen;
106         guint16 th_sport;
107         guint16 th_dport;
108         address ip_src;
109         address ip_dst;
110 };
111
112 struct rect {
113         double x, y, width, height;
114 };
115
116 struct line {
117         double x1, y1, x2, y2;
118 };
119
120 struct irect {
121         int x, y, width, height;
122 };
123
124 struct ipoint {
125         int x, y;
126 };
127
128 typedef enum {
129         ELMT_NONE=0,
130         ELMT_RECT=1,
131         ELMT_LINE=2,
132         ELMT_ELLIPSE=3
133 } ElementType;
134
135 struct rect_params {
136         struct rect dim;
137         gint filled;
138 };
139
140 struct line_params {
141         struct line dim;
142 };
143
144 struct ellipse_params {
145         struct rect dim;
146 };
147
148 struct element {
149         ElementType type;
150         GdkColor *elment_color_p;
151         struct segment *parent;
152         union {
153                 struct ellipse_params ellipse;
154                 struct rect_params rect;
155                 struct line_params line;
156         } p;
157 };
158
159 struct element_list {
160         struct element_list *next;
161         struct element *elements;
162 };
163
164 struct axis {
165         struct graph *g;                        /* which graph we belong to */
166         GtkWidget *drawing_area;
167 #if GTK_CHECK_VERSION(2,22,0)
168         cairo_surface_t *surface[2];
169 #else
170         GdkPixmap *pixmap[2];
171 #endif
172         int displayed;
173 #define AXIS_ORIENTATION        1 << 0
174         int flags;
175         /* dim and orig (relative to origin of window) of axis' pixmap */
176         struct irect p;
177         /* dim and orig (relative to origin of axis' pixmap) of scale itself */
178         struct irect s;
179         gdouble min, max;
180         gdouble major, minor;           /* major and minor ticks */
181         const char **label;
182 };
183
184 #define HAXIS_INIT_HEIGHT       70
185 #define VAXIS_INIT_WIDTH        100
186 #define TITLEBAR_HEIGHT         50
187 #define RMARGIN_WIDTH   30
188
189 struct style_tseq_tcptrace {
190         GdkColor seq_color;
191         GdkColor ack_color[2];
192         int flags;
193 };
194
195 struct style_tseq_stevens {
196         int seq_width;
197         int seq_height;
198         int flags;
199 };
200
201 struct style_tput {
202         int width, height;
203         int nsegs;
204         int flags;
205 };
206
207 struct style_rtt {
208         int width, height;
209         int flags;
210 };
211
212 struct style_wscale {
213         int win_width;
214         int win_height;
215         int flags;
216 };
217
218 /* style flags */
219 #define SEQ_ORIGIN                      0x1
220 /* show absolute sequence numbers (not differences from isn) */
221 #define SEQ_ORIGIN_ZERO         0x1
222 #define SEQ_ORIGIN_ISN          0x0
223 #define TIME_ORIGIN                     0x10
224 /* show time from beginning of capture as opposed to time from beginning
225  * of the connection */
226 #define TIME_ORIGIN_CAP         0x10
227 #define TIME_ORIGIN_CONN        0x0
228
229 /* this is used by rtt module only */
230 struct unack {
231         struct unack *next;
232         double time;
233         unsigned int seqno;
234 };
235
236 struct cross {
237         int x, y;
238         int draw;                       /* indicates whether we should draw cross at all */
239         int erase_needed;
240         GtkToggleButton *on_toggle;
241         GtkToggleButton *off_toggle;
242 };
243
244 struct bounds {
245         double x0, y0, width, height;
246 };
247
248 struct zoom {
249         double x, y;
250 };
251
252 struct zooms {
253         double x, y;
254         double step_x, step_y;
255         struct zoom initial;
256 #define ZOOM_OUT                                (1 << 0)
257 #define ZOOM_HLOCK                              (1 << 1)
258 #define ZOOM_VLOCK                              (1 << 2)
259 #define ZOOM_STEPS_SAME                 (1 << 3)
260 #define ZOOM_STEPS_KEEP_RATIO   (1 << 4)
261         int flags;
262         /* unfortunately, we need them both because gtk_toggle_button_set_active ()
263          * with second argument FALSE doesn't do anything, somehow */
264         struct {
265                 GtkToggleButton *in_toggle;
266                 GtkToggleButton *out_toggle;
267                 GtkEntry *h_zoom;
268                 GtkEntry *v_zoom;
269                 GtkSpinButton *h_step;
270                 GtkSpinButton *v_step;
271         } widget;
272 };
273
274 struct grab {
275         int grabbed;
276         int x, y;
277 };
278
279 struct magnify {
280         int active;
281         int x, y;
282         struct ipoint offset;
283         int width, height;
284         struct zoom zoom;
285         struct graph *g;
286 #define MAGZOOMS_SAME           (1U << 0)
287 #define MAGZOOMS_SAME_RATIO     (1U << 1)
288 #define MAGZOOMS_IGNORE         (1U << 31)
289         guint flags;
290         struct {
291                 GtkSpinButton *h_zoom, *v_zoom;
292         } widget;
293 };
294
295 struct graph {
296         struct graph *next;
297 #define GRAPH_TSEQ_STEVENS      0
298 #define GRAPH_TSEQ_TCPTRACE     1
299 #define GRAPH_THROUGHPUT        2
300 #define GRAPH_RTT                       3
301 #define GRAPH_WSCALE            4
302         int type;
303 #define GRAPH_DESTROYED                         (1 << 0)
304 #define GRAPH_INIT_ON_TYPE_CHANGE       (1 << 1)
305         int flags;
306         GtkWidget *toplevel;    /* keypress handler needs this */
307         GtkWidget *drawing_area;
308         GtkWidget *text;        /* text widget for seg list - probably
309                                                  * temporary
310                                                  */
311         PangoFontDescription *font;     /* font used for annotations etc. */
312 #if GTK_CHECK_VERSION(2,22,0)
313         cairo_surface_t *title_surface;
314         cairo_surface_t *surface[2];
315 #else
316         GdkPixmap *title_pixmap;
317         GdkPixmap *pixmap[2];
318 #endif
319         int displayed;                  /* which of both pixmaps is on screen right now */
320         struct {
321                 GtkWidget *control_panel;
322                 /* this belongs to style structs of graph types that make use of it */
323                 GtkToggleButton *time_orig_conn, *seq_orig_isn;
324         } gui;
325         const char **title;
326         /* Next 4 attribs describe the graph in natural units, before any scaling.
327          * For example, if we want to display graph of TCP conversation that
328          * started 112.309845 s after beginning of the capture and ran until
329          * 479.093582 s, 237019 B went through the connection (in one direction)
330          * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
331          * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
332         struct bounds bounds;
333         /* dimensions and position of the graph, both expressed already in pixels.
334          * x and y give the position of upper left corner of the graph relative
335          * to origin of the graph window, size is basically bounds*zoom */
336         struct irect geom;
337         /* viewport (=graph window area which is reserved for graph itself), its
338          * size and position relative to origin of the graph window */
339         struct irect wp;
340         struct grab grab;
341         /* If we need to display 237019 sequence numbers (=bytes) onto say 500
342          * pixels, we have to scale the graph down by factor of 0.002109. This
343          * number would be zoom.y. Obviously, both directions have separate zooms.*/
344         struct zooms zoom;
345         struct cross cross;
346         struct magnify magnify;
347         struct axis *x_axis, *y_axis;
348         struct segment *segments;
349         struct segment *current;
350         struct element_list *elists;            /* element lists */
351         union {
352                 struct style_tseq_stevens tseq_stevens;
353                 struct style_tseq_tcptrace tseq_tcptrace;
354                 struct style_tput tput;
355                 struct style_rtt rtt;
356                 struct style_wscale wscale;
357         } s;
358         /* This allows keyboard to set the radio button */
359         struct {
360                 GtkToggleButton *graph_rtt, *graph_tput, *graph_tseqstevens, *graph_tseqttrace;
361                 GtkToggleButton *graph_wscale;
362         } gt;
363 };
364
365 #if !GTK_CHECK_VERSION(3,0,0)
366 static GdkGC *xor_gc = NULL;
367 #endif
368 static int refnum=0;
369
370 #define debug(section) if (debugging & section)
371 /* print function entry points */
372 #define DBS_FENTRY                      (1 << 0)
373 #define DBS_AXES_TICKS          (1 << 1)
374 #define DBS_AXES_DRAWING        (1 << 2)
375 #define DBS_GRAPH_DRAWING       (1 << 3)
376 #define DBS_TPUT_ELMTS          (1 << 4)
377 /*int debugging = DBS_FENTRY;*/
378 static int debugging = 0;
379 /*int debugging = DBS_AXES_TICKS;*/
380 /*int debugging = DBS_AXES_DRAWING;*/
381 /*int debugging = DBS_GRAPH_DRAWING;*/
382 /*int debugging = DBS_TPUT_ELMTS;*/
383
384 static void create_gui (struct graph * );
385 #if 0
386 static void create_text_widget (struct graph * );
387 static void display_text (struct graph * );
388 #endif
389 static void create_drawing_area (struct graph * );
390 static void control_panel_create (struct graph * );
391 static GtkWidget *control_panel_create_zoom_group (struct graph * );
392 static GtkWidget *control_panel_create_magnify_group (struct graph * );
393 static GtkWidget *control_panel_create_cross_group (struct graph * );
394 static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
395 static GtkWidget *control_panel_create_graph_type_group (struct graph * );
396 static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
397 static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
398 static void control_panel_add_origin_page (struct graph * , GtkWidget * );
399 static void control_panel_add_cross_page (struct graph * , GtkWidget * );
400 static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
401 static void callback_toplevel_destroy (GtkWidget * , gpointer );
402 static gboolean callback_delete_event(GtkWidget * , GdkEvent * , gpointer);
403 static void callback_close (GtkWidget * , gpointer );
404 static void callback_time_origin (GtkWidget * , gpointer );
405 static void callback_seq_origin (GtkWidget * , gpointer );
406 static void callback_zoomlock_h (GtkWidget * , gpointer );
407 static void callback_zoomlock_v (GtkWidget * , gpointer );
408 static void callback_zoom_inout (GtkWidget * , gpointer );
409 static void callback_zoom_step (GtkWidget * , gpointer );
410 static void callback_zoom_flags (GtkWidget * , gpointer );
411 static void callback_cross_on_off (GtkWidget * , gpointer );
412 static void callback_mag_width (GtkWidget * , gpointer );
413 static void callback_mag_height (GtkWidget * , gpointer );
414 static void callback_mag_x (GtkWidget * , gpointer );
415 static void callback_mag_y (GtkWidget * , gpointer );
416 static void callback_mag_zoom (GtkWidget * , gpointer );
417 static void callback_mag_flags (GtkWidget * , gpointer );
418 static void callback_graph_type (GtkWidget * , gpointer );
419 static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
420 static void callback_create_help (GtkWidget * , gpointer );
421 static void update_zoom_spins (struct graph * );
422 static struct tcpheader *select_tcpip_session (capture_file *, struct segment * );
423 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir);
424 static int get_num_dsegs (struct graph * );
425 static int get_num_acks (struct graph * );
426 static void graph_type_dependent_initialize (struct graph * );
427 static struct graph *graph_new (void);
428 static void graph_destroy (struct graph * );
429 static void graph_initialize_values (struct graph * );
430 static void graph_init_sequence (struct graph * );
431 static void draw_element_line (struct graph * , struct element * );
432 static void draw_element_ellipse (struct graph * , struct element * );
433 static void graph_display (struct graph * );
434 static void graph_pixmaps_create (struct graph * );
435 static void graph_pixmaps_switch (struct graph * );
436 static void graph_pixmap_draw (struct graph * );
437 static void graph_pixmap_display (struct graph * );
438 static void graph_element_lists_make (struct graph * );
439 static void graph_element_lists_free (struct graph * );
440 static void graph_element_lists_initialize (struct graph * );
441 static void graph_title_pixmap_create (struct graph * );
442 static void graph_title_pixmap_draw (struct graph * );
443 static void graph_title_pixmap_display (struct graph * );
444 static void graph_segment_list_get (struct graph * );
445 static void graph_segment_list_free (struct graph * );
446 static void graph_select_segment (struct graph * , int , int );
447 static int line_detect_collision (struct element * , int , int );
448 static int ellipse_detect_collision (struct element * , int , int );
449 static void axis_pixmaps_create (struct axis * );
450 static void axis_pixmaps_switch (struct axis * );
451 static void axis_display (struct axis * );
452 static void v_axis_pixmap_draw (struct axis * );
453 static void h_axis_pixmap_draw (struct axis * );
454 static void axis_pixmap_display (struct axis * );
455 static void axis_compute_ticks (struct axis * , double , double , int );
456 static double axis_zoom_get (struct axis * , int );
457 static void axis_ticks_up (int * , int * );
458 static void axis_ticks_down (int * , int * );
459 static void axis_destroy (struct axis * );
460 static int get_label_dim (struct axis * , int , double );
461 static void toggle_crosshairs (struct graph *g);
462 static void toggle_time_origin (struct graph * );
463 static void toggle_seq_origin (struct graph * );
464 static void restore_initial_graph_view (struct graph *g);
465 static void cross_xor (struct graph * , int , int );
466 static void cross_draw (struct graph * , int , int );
467 static void cross_erase (struct graph * );
468 static void magnify_create (struct graph * , int , int );
469 static void magnify_move (struct graph * , int , int );
470 static void magnify_destroy (struct graph * );
471 static void magnify_draw (struct graph * );
472 static void magnify_get_geom (struct graph * , int , int );
473 static gboolean configure_event (GtkWidget * , GdkEventConfigure * , gpointer );
474 static gboolean expose_event (GtkWidget * , GdkEventExpose * , gpointer );
475 static gboolean button_press_event (GtkWidget * , GdkEventButton * , gpointer );
476 static gboolean button_release_event (GtkWidget * , GdkEventButton * , gpointer );
477 static gboolean motion_notify_event (GtkWidget * , GdkEventMotion * , gpointer );
478 static gboolean key_press_event (GtkWidget * , GdkEventKey * , gpointer );
479 static gboolean key_release_event (GtkWidget * , GdkEventKey * , gpointer );
480 static gboolean leave_notify_event (GtkWidget * , GdkEventCrossing * , gpointer );
481 static gboolean enter_notify_event (GtkWidget * , GdkEventCrossing * , gpointer );
482 static void tseq_initialize (struct graph * );
483 static void tseq_get_bounds (struct graph * );
484 static void tseq_stevens_read_config (struct graph * );
485 static void tseq_stevens_make_elmtlist (struct graph * );
486 static void tseq_stevens_toggle_seq_origin (struct graph * );
487 static void tseq_stevens_toggle_time_origin (struct graph * );
488 static void tseq_tcptrace_read_config (struct graph * );
489 static void tseq_tcptrace_make_elmtlist (struct graph * );
490 static void tseq_tcptrace_toggle_seq_origin (struct graph * );
491 static void tseq_tcptrace_toggle_time_origin (struct graph * );
492 static void tput_initialize (struct graph * );
493 static void tput_read_config (struct graph * );
494 static void tput_make_elmtlist (struct graph * );
495 static void tput_toggle_time_origin (struct graph * );
496 static void rtt_read_config (struct graph * );
497 static void rtt_initialize (struct graph * );
498 static int rtt_is_retrans (struct unack * , unsigned int );
499 static struct unack *rtt_get_new_unack (double , unsigned int );
500 static void rtt_put_unack_on_list (struct unack ** , struct unack * );
501 static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
502 static void rtt_make_elmtlist (struct graph * );
503 static void rtt_toggle_seq_origin (struct graph * );
504 static void wscale_initialize(struct graph *);
505 static void wscale_read_config(struct graph *);
506 static void wscale_make_elmtlist(struct graph *);
507 #if defined(_WIN32) && !defined(__MINGW32__)
508 static int rint (double );      /* compiler template for Windows */
509 #endif
510
511 /*
512  * Uncomment the following define to revert WIN32 to
513  * use original mouse button controls
514  */
515
516 /* #define ORIGINAL_WIN32_BUTTONS 1 */
517
518 /* XXX - what about OS X? */
519 static char helptext[] =
520         "Here's what you can do:\n"
521         "\n"
522 #ifdef ORIGINAL_WIN32_BUTTONS
523         "   <Ctrl>-Left Mouse Button            selects segment under cursor in Wireshark's packet list\n"
524         "\n"
525         "   Left Mouse Button                   zooms in (towards area under mouse pointer)\n"
526         "   <Shift>-Left Mouse Button           zooms out\n"
527         "\n"
528         "   Right Mouse Button                  moves the graph (if zoomed in)\n"
529         "   <Ctrl>-Right Mouse Button           displays a portion of graph under cursor magnified\n"
530 #else /* !ORIGINAL_WIN32_BUTTONS */
531         "   Left Mouse Button                   selects segment under cursor in Wireshark's packet list\n"
532         "\n"
533         "   Middle Mouse Button                 zooms in (towards area under cursor)\n"
534         "   <Shift>-Middle Mouse Button zooms out\n"
535         "\n"
536         "   Right Mouse Button                  moves the graph (if zoomed in)\n"
537         "   <Ctrl>-Right Mouse Button           displays a portion of graph under cursor magnified\n"
538 #endif
539         "\n"
540         "\n"
541         "   '1'                         display Round Trip Time Graph\n"
542         "   '2'                         display Throughput Graph\n"
543         "   '3'                         display Time/Sequence Graph (Stevens)\n"
544         "   '4'                         display Time/Sequence Graph (tcptrace)\n"
545         "   '5'                         display Window Scaling Graph\n"
546         "\n"
547         "   <Space bar> toggles crosshairs on/off\n"
548         "\n"
549         "   'i' or '+'                  zoom in (towards area under mouse pointer)\n"
550         "   'o' or '-'                  zoom out\n"
551         "   'r' or <Home>       restore graph to initial state (zoom out max)\n"
552         "   's'                         toggles relative/absolute sequence numbers\n"
553         "   't'                         toggles time origin\n"
554         "   'g'                         go to frame under cursor in Wireshark's packet list (if possible)\n"
555         "\n"
556         "   <Left>                      move view left by 100 pixels (if zoomed in)\n"
557         "   <Right>             move view right 100 pixels (if zoomed in)\n"
558         "   <Up>                        move view up by 100 pixels (if zoomed in)\n"
559         "   <Down>              move view down by 100 pixels (if zoomed in)\n"
560         "\n"
561         "   <Shift><Left>       move view left by 10 pixels (if zoomed in)\n"
562         "   <Shift><Right>      move view right 10 pixels (if zoomed in)\n"
563         "   <Shift><Up> move view up by 10 pixels (if zoomed in)\n"
564         "   <Shift><Down>       move view down by 10 pixels (if zoomed in)\n"
565         "\n"
566         "   <Ctrl><Left>        move view left by 1 pixel (if zoomed in)\n"
567         "   <Ctrl><Right>       move view right 1 pixel (if zoomed in)\n"
568         "   <Ctrl><Up>  move view up by 1 pixel (if zoomed in)\n"
569         "   <Ctrl><Down>        move view down by 1 pixel (if zoomed in)\n"
570 ;
571
572 #if 0
573 static void debug_coord (struct graph *g, const char *c)
574 {
575         static unsigned count = 0;
576
577         count++;
578         printf("%u: %s\n", count, c);
579         printf("%u:  g->geom.width %d\n", count, g->geom.width);
580         printf("%u: g->geom.height %d\n", count, g->geom.height);
581         printf("%u:      g->geom.x %d\n", count, g->geom.x);
582         printf("%u:      g->geom.y %d\n", count, g->geom.y);
583
584         printf("%u:    g->wp.width %d\n", count, g->wp.width);
585         printf("%u:   g->wp.height %d\n", count, g->wp.height);
586         printf("%u:        g->wp.x %d\n", count, g->wp.x);
587         printf("%u:        g->wp.y %d\n", count, g->wp.y);
588         printf("---------------\n");
589 }
590 #endif
591
592 static void set_busy_cursor(GdkWindow *w)
593 {
594         GdkCursor* cursor;
595
596         cursor = gdk_cursor_new(GDK_WATCH);
597         gdk_window_set_cursor(w, cursor);
598         gdk_flush();
599         gdk_cursor_unref(cursor);
600 }
601
602 static void unset_busy_cursor(GdkWindow *w)
603 {
604         gdk_window_set_cursor(w, NULL);
605         gdk_flush();
606 }
607 #ifdef MAIN_MENU_USE_UIMANAGER
608 void tcp_graph_cb (GtkAction *action, gpointer user_data _U_)
609 {
610         struct segment current;
611         struct graph *g;
612         const gchar *name;
613         guint graph_type;
614
615         name = gtk_action_get_name (action);
616         if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-Stevens") == 0){
617                 graph_type = GRAPH_TSEQ_STEVENS;
618         }else if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-tcptrace") == 0){
619                 graph_type = GRAPH_TSEQ_TCPTRACE;
620         }else if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/Throughput-Graph") == 0){
621                 graph_type = GRAPH_THROUGHPUT;
622         }else if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/RTT-Graph") == 0){
623                 graph_type = GRAPH_RTT;
624         }else if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/Window-Scaling-Graph") == 0){
625                 graph_type = GRAPH_WSCALE;
626         }else{
627                 return;
628         }
629
630         debug(DBS_FENTRY) puts ("tcp_graph_cb()");
631
632         if (! (g = graph_new()))
633                 return;
634
635         refnum++;
636         graph_initialize_values (g);
637
638         g->type = graph_type;
639         if (!select_tcpip_session (&cfile, &current)) {
640                 return;
641         }
642
643         graph_segment_list_get(g);
644         create_gui(g);
645         /* display_text(g); */
646         graph_init_sequence(g);
647
648 }
649 #else
650 static void tcp_graph_cb (GtkWidget *w _U_, gpointer data, guint callback_action /*graph_type*/ _U_)
651 {
652         struct segment current;
653         struct graph *g;
654
655         guint graph_type = GPOINTER_TO_INT(data);
656
657         debug(DBS_FENTRY) puts ("tcp_graph_cb()");
658
659         if (! (g = graph_new()))
660                 return;
661
662         refnum++;
663         graph_initialize_values (g);
664
665         g->type = graph_type;
666         if (!select_tcpip_session (&cfile, &current)) {
667                 return;
668         }
669
670         graph_segment_list_get(g);
671         create_gui(g);
672         /* display_text(g); */
673         graph_init_sequence(g);
674 }
675 #endif
676 static void create_gui (struct graph *g)
677 {
678         debug(DBS_FENTRY) puts ("create_gui()");
679         /* create_text_widget(g); */
680         control_panel_create (g);
681         create_drawing_area(g);
682 }
683
684
685
686 static void create_drawing_area (struct graph *g)
687 {
688 #if GTK_CHECK_VERSION(3,0,0)
689         GtkStyleContext *context;
690 #else
691         GdkColormap *colormap;
692         GdkColor color;
693 #endif
694         char window_title[WINDOW_TITLE_LENGTH];
695         struct segment current;
696         struct tcpheader *thdr;
697         GtkAllocation widget_alloc;
698 #if 0
699         /* Prep. to include the controls in the graph window */
700         GtkWidget *frame;
701         GtkWidget *vbox;
702         GtkWidget *hbox;
703 #endif
704         debug(DBS_FENTRY) puts ("create_drawing_area()");
705         thdr=select_tcpip_session (&cfile, &current);
706         g_snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d -> %s:%d",
707                         refnum,
708                         cf_get_display_name(&cfile),
709                         ep_address_to_str(&(thdr->ip_src)),
710                         thdr->th_sport,
711                         ep_address_to_str(&(thdr->ip_dst)),
712                         thdr->th_dport
713         );
714         g->toplevel = dlg_window_new ("Tcp Graph");
715         gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title);
716         gtk_widget_set_name (g->toplevel, "Test Graph");
717
718         /* Create the drawing area */
719         g->drawing_area = gtk_drawing_area_new ();
720         g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
721         gtk_widget_set_size_request (g->drawing_area,
722                                         g->wp.width + g->wp.x + RMARGIN_WIDTH,
723                                         g->wp.height + g->wp.y + g->x_axis->s.height);
724         gtk_widget_show (g->drawing_area);
725
726         g_signal_connect(g->drawing_area, "expose_event", G_CALLBACK(expose_event), g);
727         /* this has to be done later, after the widget has been shown */
728         /*
729         g_signal_connect(g->drawing_area,"configure_event", G_CALLBACK(configure_event),
730         g);
731          */
732         g_signal_connect(g->drawing_area, "motion_notify_event",
733                        G_CALLBACK(motion_notify_event), g);
734         g_signal_connect(g->drawing_area, "button_press_event",
735                        G_CALLBACK(button_press_event), g);
736         g_signal_connect(g->drawing_area, "button_release_event",
737                        G_CALLBACK(button_release_event), g);
738         g_signal_connect(g->drawing_area, "leave_notify_event",
739                        G_CALLBACK(leave_notify_event), g);
740         g_signal_connect(g->drawing_area, "enter_notify_event",
741                        G_CALLBACK(enter_notify_event), g);
742         g_signal_connect(g->toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
743         /* why doesn't drawing area send key_press_signals? */
744         g_signal_connect(g->toplevel, "key_press_event", G_CALLBACK(key_press_event), g);
745         g_signal_connect(g->toplevel, "key_release_event", G_CALLBACK(key_release_event),
746                        g);
747         gtk_widget_set_events(g->toplevel,
748                               GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
749
750         gtk_widget_set_events (g->drawing_area,
751                                GDK_EXPOSURE_MASK
752                                | GDK_LEAVE_NOTIFY_MASK
753                                | GDK_ENTER_NOTIFY_MASK
754                                | GDK_BUTTON_PRESS_MASK
755                                | GDK_BUTTON_RELEASE_MASK
756                                | GDK_POINTER_MOTION_MASK
757                                | GDK_POINTER_MOTION_HINT_MASK);
758
759 #if 0
760         /* Prep. to include the controls in the graph window */
761
762         vbox = gtk_vbox_new (FALSE, 0);
763         gtk_container_add (GTK_CONTAINER (g->toplevel), vbox);
764         gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
765         gtk_widget_show (vbox);
766
767         frame = gtk_frame_new (NULL);
768         gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
769         gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
770         gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
771         gtk_widget_show (frame);
772
773
774         /*gtk_box_pack_start (GTK_BOX (vbox), g->gui.control_panel, FALSE, FALSE, 0);*/
775
776         hbox=gtk_hbox_new(FALSE, 3);
777         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
778         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
779         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
780         gtk_widget_show(hbox);
781
782         create_ctrl_area(g, hbox);
783
784 #endif
785
786         gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
787         gtk_widget_show (g->toplevel);
788
789         /* in case we didn't get what we asked for */
790         gtk_widget_get_allocation(GTK_WIDGET (g->drawing_area), &widget_alloc);
791         g->wp.width = widget_alloc.width - g->wp.x - RMARGIN_WIDTH;
792         g->wp.height = widget_alloc.height - g->wp.y - g->x_axis->s.height;
793
794 #if GTK_CHECK_VERSION(3,0,0)
795         context = gtk_widget_get_style_context (g->drawing_area);
796         gtk_style_context_get (context, GTK_STATE_NORMAL,
797                                            "font", &g->font,
798                                            NULL);
799 #else
800         g->font = gtk_widget_get_style(g->drawing_area)->font_desc;
801 #endif
802 #if !GTK_CHECK_VERSION(3,0,0)
803         colormap = gtk_widget_get_colormap(GTK_WIDGET(g->drawing_area));
804         if (!xor_gc) {
805                 xor_gc = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
806                 gdk_gc_set_function (xor_gc, GDK_XOR);
807                 if (!gdk_color_parse ("gray15", &color)) {
808                         /*
809                          * XXX - do more than just warn.
810                          */
811                         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
812                             "Could not parse color gray15.");
813                 }
814                 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
815                         /*
816                          * XXX - do more than just warn.
817                          */
818                         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
819                             "Could not allocate color gray15.");
820                 }
821                 gdk_gc_set_foreground (xor_gc, &color);
822         }
823
824         /* this is probably quite an ugly way to get rid of the first configure
825          * event
826          * immediatelly after gtk_widget_show (window) drawing_area gets a configure
827          * event which is handled during the next return to gtk_main which is
828          * probably the gdk_gc_new() call. configure handler calls
829          * graph_element_lists_make() which is not good because the graph struct is
830          * not fully set up yet - namely we're not sure about actual geometry
831          * and we don't have the GC's at all. so we just postpone installation
832          * of configure handler until we're ready to deal with it.
833          *
834          * !!! NEMLLO BY TO BYT NA KONCI graph_init_sequence()? !!!
835          *
836          */
837 #endif
838         g_signal_connect(g->drawing_area, "configure_event", G_CALLBACK(configure_event),
839                        g);
840
841         /* puts ("exiting create_drawing_area()"); */
842 }
843
844 static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
845 {
846         struct graph *g = (struct graph * )data;
847
848         if (!(g->flags & GRAPH_DESTROYED)) {
849                 g->flags |= GRAPH_DESTROYED;
850                 graph_destroy ((struct graph * )data);
851         }
852 }
853
854 static void control_panel_create (struct graph *g)
855 {
856     GtkWidget *toplevel, *notebook;
857     GtkWidget *table;
858     GtkWidget *help_bt, *close_bt, *bbox;
859     char window_title[WINDOW_TITLE_LENGTH];
860
861     debug(DBS_FENTRY) puts ("control_panel_create()");
862
863     notebook = gtk_notebook_new ();
864     control_panel_add_zoom_page (g, notebook);
865     control_panel_add_magnify_page (g, notebook);
866     control_panel_add_origin_page (g, notebook);
867     control_panel_add_cross_page (g, notebook);
868     control_panel_add_graph_type_page (g, notebook);
869
870     g_snprintf (window_title, WINDOW_TITLE_LENGTH,
871                 "Graph %d - Control - Wireshark", refnum);
872     toplevel = dlg_window_new ("tcp-graph-control");
873     gtk_window_set_title(GTK_WINDOW(toplevel), window_title);
874
875     table = gtk_table_new (2, 1,  FALSE);
876     gtk_container_add (GTK_CONTAINER (toplevel), table);
877
878     gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
879                       GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
880
881     /* Button row. */
882     bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_CLOSE, NULL);
883     gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 1, 2,
884                       GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
885
886     help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
887     g_signal_connect(help_bt, "clicked", G_CALLBACK(callback_create_help), g);
888
889     close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
890     window_set_cancel_button(toplevel, close_bt, NULL);
891     g_signal_connect(close_bt, "clicked", G_CALLBACK(callback_close), g);
892
893     g_signal_connect(toplevel, "delete_event", G_CALLBACK(callback_delete_event), g);
894     g_signal_connect(toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
895
896     /* gtk_widget_show_all (table); */
897     /* g->gui.control_panel = table; */
898     gtk_widget_show_all (toplevel);
899     window_present(toplevel);
900
901     g->gui.control_panel = toplevel;
902 }
903
904 static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
905 {
906         GtkWidget *zoom_frame;
907         GtkWidget *zoom_lock_frame;
908         GtkWidget *label;
909         GtkWidget *box;
910
911         zoom_frame = control_panel_create_zoom_group (g);
912         gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
913         zoom_lock_frame = control_panel_create_zoomlock_group (g);
914         gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
915         box = gtk_vbox_new (FALSE, 0);
916         gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
917         gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
918         gtk_widget_show (box);
919         label = gtk_label_new ("Zoom");
920         gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
921 }
922
923 static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
924 {
925         GtkWidget *mag_frame, *label;
926
927         mag_frame = control_panel_create_magnify_group (g);
928         gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
929         label = gtk_label_new ("Magnify");
930         gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
931 }
932
933 static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
934 {
935         GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
936         GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
937         GtkWidget *box, *label;
938
939         /* time origin box */
940         time_orig_cap =
941                         gtk_radio_button_new_with_label (NULL, "beginning of capture");
942         time_orig_conn = gtk_radio_button_new_with_label (
943                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (time_orig_cap)),
944                         "beginning of this TCP connection");
945         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
946         time_orig_box = gtk_vbox_new (TRUE, 0);
947         gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
948         gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
949         time_orig_frame = gtk_frame_new ("Time origin");
950         gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
951         gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
952
953         /* sequence number origin group */
954         seq_orig_isn =
955                         gtk_radio_button_new_with_label (NULL, "initial sequence number");
956         seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_get_group (
957                         GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
958         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
959         seq_orig_box = gtk_vbox_new (TRUE, 0);
960         gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
961         gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
962         seq_orig_frame = gtk_frame_new ("Sequence number origin");
963         gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
964         gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
965
966         g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
967         g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
968
969         g_signal_connect(time_orig_conn, "toggled", G_CALLBACK(callback_time_origin), g);
970         g_signal_connect(seq_orig_isn, "toggled", G_CALLBACK(callback_seq_origin), g);
971
972         box = gtk_vbox_new (FALSE, 0);
973         gtk_container_set_border_width (GTK_CONTAINER (box), 5);
974         gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
975         gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
976         gtk_widget_show (box);
977         label = gtk_label_new ("Origin");
978         gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
979 }
980
981 static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
982 {
983         GtkWidget *cross_frame, *label;
984
985         cross_frame = control_panel_create_cross_group (g);
986         gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
987         label = gtk_label_new ("Cross");
988         gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
989 }
990
991 static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
992 {
993         GtkWidget *frame, *label;
994
995         frame = control_panel_create_graph_type_group (g);
996         gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
997         label = gtk_label_new ("Graph type");
998         gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
999 }
1000
1001 /* Treat this as a cancel, by calling "callback_close()" */
1002 static gboolean
1003 callback_delete_event(GtkWidget *widget _U_, GdkEvent *event _U_,
1004                       gpointer data)
1005 {
1006         callback_close(NULL, data);
1007         return FALSE;
1008 }
1009
1010 static void callback_close (GtkWidget *widget _U_, gpointer data)
1011 {
1012         struct graph *g = (struct graph * )data;
1013
1014         if (!(g->flags & GRAPH_DESTROYED)) {
1015                 g->flags |= GRAPH_DESTROYED;
1016                 graph_destroy ((struct graph * )data);
1017         }
1018 }
1019
1020 static void callback_create_help(GtkWidget *widget _U_, gpointer data _U_)
1021 {
1022         GtkWidget *toplevel, *vbox, *text, *scroll, *bbox, *close_bt;
1023         GtkTextBuffer *buf;
1024
1025         toplevel = dlg_window_new ("Help for TCP graphing");
1026         gtk_window_set_default_size(GTK_WINDOW(toplevel), 500, 400);
1027
1028         vbox = gtk_vbox_new (FALSE, 3);
1029         gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
1030         gtk_container_add (GTK_CONTAINER (toplevel), vbox);
1031
1032         scroll = scrolled_window_new (NULL, NULL);
1033         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
1034                                    GTK_SHADOW_IN);
1035         gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
1036         text = gtk_text_view_new();
1037         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1038         buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
1039         gtk_text_buffer_set_text(buf, helptext, -1);
1040         gtk_container_add (GTK_CONTAINER (scroll), text);
1041
1042         /* Button row. */
1043         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1044         gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
1045         gtk_widget_show(bbox);
1046
1047         close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1048         window_set_cancel_button(toplevel, close_bt, window_cancel_button_cb);
1049
1050         g_signal_connect(toplevel, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1051
1052         gtk_widget_show_all (toplevel);
1053         window_present(toplevel);
1054 }
1055
1056 static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
1057 {
1058         toggle_time_origin ((struct graph * )data);
1059 }
1060
1061 static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
1062 {
1063         toggle_seq_origin ((struct graph * )data);
1064 }
1065
1066 static GtkWidget *control_panel_create_zoom_group (struct graph *g)
1067 {
1068         GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1069         GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1070         GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1071         GtkWidget *zoom_v_step_label, *zoom_v_step;
1072         GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
1073         GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1074         GtkWidget *zoom_h_entry, *zoom_v_entry;
1075         GtkWidget *zoom_h_label, *zoom_v_label;
1076
1077         zoom_in = gtk_radio_button_new_with_label (NULL, "in");
1078         zoom_out = gtk_radio_button_new_with_label (
1079                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_in)), "out");
1080         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
1081         zoom_inout_box = gtk_hbox_new (FALSE, 0);
1082         gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1083         gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1084
1085         zoom_separator1 = gtk_hseparator_new ();
1086
1087         zoom_h_entry = gtk_entry_new ();
1088         gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
1089         gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
1090         zoom_h_label = gtk_label_new ("Horizontal:");
1091
1092         zoom_v_entry = gtk_entry_new ();
1093         gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
1094         gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
1095         zoom_v_label = gtk_label_new ("Vertical:");
1096
1097         g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1098         g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1099
1100         zoom_table = gtk_table_new (2, 2,  FALSE);
1101         gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
1102                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1103         gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
1104                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1105         gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
1106                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1107         gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
1108                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1109
1110         zoom_separator2 = gtk_hseparator_new ();
1111
1112         zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1113         zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
1114         gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
1115         zoom_h_step_label = gtk_label_new ("Horizontal step:");
1116
1117         zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1118         zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
1119         gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
1120         zoom_v_step_label = gtk_label_new ("Vertical step:");
1121
1122         g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1123         g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1124
1125         zoom_same_toggle = gtk_check_button_new_with_label("Keep them the same");
1126         zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1127         g_object_set_data(G_OBJECT(zoom_same_toggle), "flag", (gpointer)ZOOM_STEPS_SAME);
1128         g_object_set_data(G_OBJECT(zoom_ratio_toggle), "flag",
1129                         (gpointer)ZOOM_STEPS_KEEP_RATIO);
1130         g_signal_connect(zoom_same_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1131         g_signal_connect(zoom_ratio_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1132
1133         zoom_step_table = gtk_table_new (4, 2,  FALSE);
1134         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
1135                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1136         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
1137                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1138         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
1139                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1140         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
1141                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1142         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
1143                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1144         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
1145                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1146
1147         zoom_box = gtk_vbox_new (FALSE, 0);
1148         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1149         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
1150         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
1151         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
1152         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
1153         zoom_frame = gtk_frame_new ("Zoom");
1154         gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
1155
1156         g_object_set_data(G_OBJECT(zoom_h_step), "direction", GINT_TO_POINTER(0));
1157         g_object_set_data(G_OBJECT(zoom_v_step), "direction", GINT_TO_POINTER(1));
1158
1159         g_signal_connect(zoom_in, "toggled", G_CALLBACK(callback_zoom_inout), g);
1160         g_signal_connect(zoom_h_step, "changed", G_CALLBACK(callback_zoom_step), g);
1161         g_signal_connect(zoom_v_step, "changed", G_CALLBACK(callback_zoom_step), g);
1162
1163         g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1164         g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1165         return zoom_frame;
1166 }
1167
1168 static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
1169 {
1170         struct graph *g = (struct graph * )data;
1171
1172         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1173                 g->zoom.flags &= ~ZOOM_OUT;
1174         else
1175                 g->zoom.flags |= ZOOM_OUT;
1176 }
1177
1178 static void callback_zoom_step (GtkWidget *spin, gpointer data)
1179 {
1180         struct graph *g = (struct graph * )data;
1181         double value;
1182         int direction;
1183         double *zoom_this, *zoom_other;
1184         GtkSpinButton *widget_this, *widget_other;
1185         double old_this;
1186
1187         direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
1188         value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
1189
1190         if (direction) {
1191                 zoom_this = &g->zoom.step_y;
1192                 zoom_other = &g->zoom.step_x;
1193                 widget_this = g->zoom.widget.v_step;
1194                 widget_other = g->zoom.widget.h_step;
1195         } else {
1196                 zoom_this = &g->zoom.step_x;
1197                 zoom_other = &g->zoom.step_y;
1198                 widget_this = g->zoom.widget.h_step;
1199                 widget_other = g->zoom.widget.v_step;
1200         }
1201
1202         old_this = *zoom_this;
1203         *zoom_this = value;
1204         if (g->zoom.flags & ZOOM_STEPS_SAME) {
1205                 *zoom_other = value;
1206                 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1207         } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1208                 double old_other = *zoom_other;
1209                 *zoom_other *= value / old_this;
1210                 if (*zoom_other < 1.0) {
1211                         *zoom_other = 1.0;
1212                         *zoom_this = old_this * 1.0 / old_other;
1213                         gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1214                 } else if (*zoom_other > 5.0) {
1215                         *zoom_other = 5.0;
1216                         *zoom_this = old_this * 5.0 / old_other;
1217                         gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1218                 }
1219                 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1220         }
1221 }
1222
1223 static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
1224 {
1225         struct graph *g = (struct graph * )data;
1226         int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
1227
1228         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1229                 g->zoom.flags |= flag;
1230         else
1231                 g->zoom.flags &= ~flag;
1232 }
1233
1234 static void update_zoom_spins (struct graph *g)
1235 {
1236         char s[32];
1237
1238         g_snprintf (s, sizeof(s), "%.3f", g->zoom.x / g->zoom.initial.x);
1239         gtk_entry_set_text (g->zoom.widget.h_zoom, s);
1240         g_snprintf (s, sizeof(s), "%.3f", g->zoom.y / g->zoom.initial.y);
1241         gtk_entry_set_text (g->zoom.widget.v_zoom, s);
1242 }
1243
1244 static GtkWidget *control_panel_create_magnify_group (struct graph *g)
1245 {
1246         GtkWidget *mag_width_label, *mag_width;
1247         GtkWidget *mag_height_label, *mag_height;
1248         GtkWidget *mag_x_label, *mag_x;
1249         GtkWidget *mag_y_label, *mag_y;
1250         GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
1251         GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1252         GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1253         GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1254         GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1255         GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1256         GtkWidget *mag_box, *mag_frame;
1257
1258         mag_width_label = gtk_label_new ("Width:");
1259         mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1260         mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
1261
1262         mag_height_label = gtk_label_new ("Height:");
1263         mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1264         mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
1265
1266         mag_x_label = gtk_label_new ("X:");
1267         mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1268         mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
1269
1270         mag_y_label = gtk_label_new ("Y:");
1271         mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1272         mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
1273
1274         mag_wh_table = gtk_table_new (4, 2, FALSE);
1275         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
1276                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1277         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
1278                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1279         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
1280                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1281         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
1282                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1283         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
1284                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1285         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
1286                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1287         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
1288                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1289         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
1290                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1291
1292         mag_h_zoom_label = gtk_label_new ("Horizontal:");
1293         mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1294         mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
1295
1296         mag_v_zoom_label = gtk_label_new ("Vertical:");
1297         mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1298         mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
1299
1300         mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
1301         mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1302
1303         mag_zoom_table = gtk_table_new (4, 2, FALSE);
1304         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
1305                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1306         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
1307                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1308         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
1309                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1310         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
1311                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1312         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
1313                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1314         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
1315                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1316
1317         mag_zoom_frame = gtk_frame_new ("Magnify zoom");
1318         gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
1319         gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
1320
1321         mag_box = gtk_vbox_new (FALSE, 0);
1322         gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
1323         gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1324         mag_frame = gtk_frame_new ("Magnify");
1325         gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
1326
1327         g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1328         g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1329         g_object_set_data(G_OBJECT(mag_h_zoom), "direction", GINT_TO_POINTER(0));
1330         g_object_set_data(G_OBJECT(mag_v_zoom), "direction", GINT_TO_POINTER(1));
1331         g_object_set_data(G_OBJECT(mag_zoom_same), "flag", (gpointer)MAGZOOMS_SAME);
1332         g_object_set_data(G_OBJECT(mag_zoom_ratio), "flag", (gpointer)MAGZOOMS_SAME_RATIO);
1333
1334         g_signal_connect(mag_width, "changed", G_CALLBACK(callback_mag_width), g);
1335         g_signal_connect(mag_height, "changed", G_CALLBACK(callback_mag_height), g);
1336         g_signal_connect(mag_x, "changed", G_CALLBACK(callback_mag_x), g);
1337         g_signal_connect(mag_y, "changed", G_CALLBACK(callback_mag_y), g);
1338         g_signal_connect(mag_h_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1339         g_signal_connect(mag_v_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1340         g_signal_connect(mag_zoom_same, "clicked", G_CALLBACK(callback_mag_flags), g);
1341         g_signal_connect(mag_zoom_ratio, "clicked", G_CALLBACK(callback_mag_flags), g);
1342
1343         return mag_frame;
1344 }
1345
1346 static void callback_mag_width (GtkWidget *spin, gpointer data)
1347 {
1348         struct graph *g = (struct graph * )data;
1349
1350         g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
1351 }
1352
1353 static void callback_mag_height (GtkWidget *spin, gpointer data)
1354 {
1355         struct graph *g = (struct graph * )data;
1356
1357         g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1358 }
1359
1360 static void callback_mag_x (GtkWidget *spin, gpointer data)
1361 {
1362         struct graph *g = (struct graph * )data;
1363
1364         g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1365 }
1366
1367 static void callback_mag_y (GtkWidget *spin, gpointer data)
1368 {
1369         struct graph *g = (struct graph * )data;
1370
1371         g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1372 }
1373
1374 static void callback_mag_zoom (GtkWidget *spin, gpointer data)
1375 {
1376         struct graph *g = (struct graph * )data;
1377         double value;
1378         int direction;
1379         double *zoom_this, *zoom_other;
1380         GtkSpinButton *widget_this, *widget_other;
1381         double old_this;
1382
1383         if (g->magnify.flags & MAGZOOMS_IGNORE) {
1384                 printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
1385                 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1386                 return;
1387         }
1388         direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
1389         value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
1390
1391         if (direction) {
1392                 zoom_this = &g->magnify.zoom.y;
1393                 zoom_other = &g->magnify.zoom.x;
1394                 widget_this = g->magnify.widget.v_zoom;
1395                 widget_other = g->magnify.widget.h_zoom;
1396         } else {
1397                 zoom_this = &g->magnify.zoom.x;
1398                 zoom_other = &g->magnify.zoom.y;
1399                 widget_this = g->magnify.widget.h_zoom;
1400                 widget_other = g->magnify.widget.v_zoom;
1401         }
1402
1403         old_this = *zoom_this;
1404         *zoom_this = value;
1405         if (g->magnify.flags & MAGZOOMS_SAME) {
1406                 *zoom_other = value;
1407                 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1408                 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1409         } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1410                 double old_other = *zoom_other;
1411                 *zoom_other *= value / old_this;
1412                 if (*zoom_other < 1.0) {
1413                         *zoom_other = 1.0;
1414                         *zoom_this = old_this * 1.0 / old_other;
1415                         /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1416                         gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1417                 } else if (*zoom_other > 25.0) {
1418                         *zoom_other = 25.0;
1419                         *zoom_this = old_this * 25.0 / old_other;
1420                         /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1421                         gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1422                 }
1423                 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1424                 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1425         }
1426 }
1427
1428 static void callback_mag_flags (GtkWidget *toggle, gpointer data)
1429 {
1430         struct graph *g = (struct graph * )data;
1431         int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
1432
1433         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1434                 g->magnify.flags |= flag;
1435         else
1436                 g->magnify.flags &= ~flag;
1437 }
1438
1439 static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
1440 {
1441         GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1442         GtkWidget *zoom_lock_frame;
1443
1444         zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
1445         zoom_lock_h = gtk_radio_button_new_with_label (
1446                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1447                                         "horizontal");
1448         zoom_lock_v = gtk_radio_button_new_with_label (
1449                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1450                                         "vertical");
1451         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
1452         zoom_lock_box = gtk_hbox_new (FALSE, 0);
1453         gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_none,
1454                            TRUE, TRUE, 0);
1455         gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1456         gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1457         zoom_lock_frame = gtk_frame_new ("Zoom lock:");
1458         gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
1459
1460         g_signal_connect(zoom_lock_h, "toggled", G_CALLBACK(callback_zoomlock_h), g);
1461         g_signal_connect(zoom_lock_v, "toggled", G_CALLBACK(callback_zoomlock_v), g);
1462
1463         return zoom_lock_frame;
1464 }
1465
1466 static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
1467 {
1468         struct graph *g = (struct graph * )data;
1469
1470         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1471                 g->zoom.flags |= ZOOM_HLOCK;
1472         else
1473                 g->zoom.flags &= ~ZOOM_HLOCK;
1474 }
1475
1476 static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
1477 {
1478         struct graph *g = (struct graph * )data;
1479
1480         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1481                 g->zoom.flags |= ZOOM_VLOCK;
1482         else
1483                 g->zoom.flags &= ~ZOOM_VLOCK;
1484 }
1485
1486 static GtkWidget *control_panel_create_cross_group (struct graph *g)
1487 {
1488         GtkWidget *on, *off, *box, *frame, *vbox, *label;
1489
1490         label = gtk_label_new ("Crosshairs:");
1491         off = gtk_radio_button_new_with_label (NULL, "off");
1492         on = gtk_radio_button_new_with_label (
1493                                 gtk_radio_button_get_group (GTK_RADIO_BUTTON (off)), "on");
1494         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
1495         box = gtk_hbox_new (FALSE, 0);
1496         gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
1497         gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
1498         gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
1499         vbox = gtk_vbox_new (FALSE, 0);
1500         gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
1501         /* frame = gtk_frame_new ("Cross:"); */
1502         frame = gtk_frame_new (NULL);
1503         gtk_container_add (GTK_CONTAINER (frame), vbox);
1504
1505         g_signal_connect(on, "toggled", G_CALLBACK(callback_cross_on_off), g);
1506
1507         g->cross.on_toggle = (GtkToggleButton * )on;
1508         g->cross.off_toggle = (GtkToggleButton * )off;
1509
1510         return frame;
1511 }
1512
1513 static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
1514 {
1515         struct graph *g = (struct graph * )data;
1516
1517         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle))) {
1518                 int x, y;
1519                 g->cross.draw = TRUE;
1520                 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
1521                 cross_draw (g, x, y);
1522         } else {
1523                 g->cross.draw = FALSE;
1524                 cross_erase (g);
1525         }
1526 }
1527
1528 static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
1529 {
1530         GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1531         GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1532         GtkWidget *graph_frame;
1533         GtkWidget *graph_wscale;
1534
1535         graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
1536         graph_tseqttrace = gtk_radio_button_new_with_label (
1537                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1538                                         "Time/Sequence (tcptrace-style)");
1539         graph_tseqstevens = gtk_radio_button_new_with_label (
1540                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1541                                         "Time/Sequence (Stevens'-style)");
1542         graph_rtt = gtk_radio_button_new_with_label (
1543                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1544                                         "Round-trip Time");
1545         graph_wscale = gtk_radio_button_new_with_label (
1546                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1547                         "Window Scaling");
1548
1549         switch (g->type) {
1550         case GRAPH_TSEQ_STEVENS:
1551                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
1552                 break;
1553         case GRAPH_TSEQ_TCPTRACE:
1554                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
1555                 break;
1556         case GRAPH_THROUGHPUT:
1557                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
1558                 break;
1559         case GRAPH_RTT:
1560                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
1561                 break;
1562         case GRAPH_WSCALE:
1563                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_wscale), TRUE);
1564                 break;
1565         }
1566         graph_init = gtk_check_button_new_with_label ("Init on change");
1567         graph_sep = gtk_hseparator_new ();
1568         graph_box = gtk_vbox_new (FALSE, 0);
1569         gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
1570         gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
1571         gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1572         gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1573         gtk_box_pack_start (GTK_BOX (graph_box), graph_wscale, TRUE, TRUE, 0);
1574         gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
1575         gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
1576         graph_frame = gtk_frame_new ("Graph type:");
1577         gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
1578
1579         g_object_set_data(G_OBJECT(graph_tseqstevens), "new-graph-type",
1580                         GINT_TO_POINTER(0));
1581         g_object_set_data(G_OBJECT(graph_tseqttrace), "new-graph-type", GINT_TO_POINTER(1));
1582         g_object_set_data(G_OBJECT(graph_tput), "new-graph-type", GINT_TO_POINTER(2));
1583         g_object_set_data(G_OBJECT(graph_rtt), "new-graph-type", GINT_TO_POINTER(3));
1584         g_object_set_data(G_OBJECT(graph_wscale), "new-graph-type", GINT_TO_POINTER(GRAPH_WSCALE));
1585
1586         g->gt.graph_wscale = (GtkToggleButton *)graph_wscale;
1587         g->gt.graph_rtt = (GtkToggleButton * )graph_rtt;
1588         g->gt.graph_tput = (GtkToggleButton * )graph_tput;
1589         g->gt.graph_tseqstevens = (GtkToggleButton * )graph_tseqstevens;
1590         g->gt.graph_tseqttrace = (GtkToggleButton * )graph_tseqttrace;
1591
1592         g_signal_connect(graph_tseqttrace, "toggled", G_CALLBACK(callback_graph_type), g);
1593         g_signal_connect(graph_tseqstevens, "toggled", G_CALLBACK(callback_graph_type), g);
1594         g_signal_connect(graph_tput, "toggled", G_CALLBACK(callback_graph_type), g);
1595         g_signal_connect(graph_rtt, "toggled", G_CALLBACK(callback_graph_type), g);
1596         g_signal_connect(graph_wscale, "toggled", G_CALLBACK(callback_graph_type), g);
1597         g_signal_connect(graph_init, "toggled", G_CALLBACK(callback_graph_init_on_typechg), g);
1598
1599         return graph_frame;
1600 }
1601
1602 static void callback_graph_type (GtkWidget *toggle, gpointer data)
1603 {
1604         int old_type, new_type;
1605         struct graph *g = (struct graph * )data;
1606
1607         new_type = (long)g_object_get_data(G_OBJECT(toggle),"new-graph-type");
1608
1609         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1610                 return;
1611
1612         old_type = g->type;
1613         g->type = new_type;
1614
1615         graph_element_lists_free (g);
1616         graph_element_lists_initialize (g);
1617
1618         if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
1619                 /* throughput graph uses differently constructed segment list so we
1620                  * need to recreate it */
1621                 graph_segment_list_free (g);
1622                 graph_segment_list_get (g);
1623         }
1624
1625         if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1626                 g->geom.width = g->wp.width;
1627                 g->geom.height = g->wp.height;
1628                 g->geom.x = g->wp.x;
1629                 g->geom.y = g->wp.y;
1630         }
1631         g->x_axis->min = g->y_axis->min = 0;
1632         gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
1633         gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
1634         graph_init_sequence (g);
1635 }
1636
1637 static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
1638 {
1639         ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1640 }
1641
1642 static struct graph *graph_new (void)
1643 {
1644         struct graph *g;
1645
1646         g = (struct graph * )g_malloc0 (sizeof (struct graph));
1647         graph_element_lists_initialize (g);
1648
1649         g->x_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1650         g->y_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1651         g->x_axis->g = g;
1652         g->x_axis->flags = 0;
1653         g->x_axis->flags |= AXIS_ORIENTATION;
1654         g->x_axis->s.x = g->x_axis->s.y = 0;
1655         g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1656         g->x_axis->p.x = VAXIS_INIT_WIDTH;
1657         g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1658         g->y_axis->g = g;
1659         g->y_axis->flags = 0;
1660         g->y_axis->flags &= ~AXIS_ORIENTATION;
1661         g->y_axis->p.x = g->y_axis->p.y = 0;
1662         g->y_axis->p.width = VAXIS_INIT_WIDTH;
1663         g->y_axis->s.x = 0;
1664         g->y_axis->s.y = TITLEBAR_HEIGHT;
1665         g->y_axis->s.width = VAXIS_INIT_WIDTH;
1666
1667         return g;
1668 }
1669
1670 static void graph_initialize_values (struct graph *g)
1671 {
1672         g->geom.width = g->wp.width = 750;
1673         g->geom.height = g->wp.height = 550;
1674         g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1675         g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1676         g->flags = 0;
1677         /* g->zoom.x = g->zoom.y = 1.0; */
1678         g->zoom.step_x = g->zoom.step_y = 1.2;
1679         g->zoom.flags = 0;
1680         g->cross.draw = g->cross.erase_needed = 0;
1681         g->grab.grabbed = 0;
1682         g->magnify.active = 0;
1683         g->magnify.offset.x = g->magnify.offset.y = 0;
1684         g->magnify.width = g->magnify.height = 250;
1685         g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1686         g->magnify.flags = 0;
1687 }
1688
1689 static void graph_init_sequence (struct graph *g)
1690 {
1691         debug(DBS_FENTRY) puts ("graph_init_sequence()");
1692
1693         graph_type_dependent_initialize (g);
1694         g->zoom.initial.x = g->zoom.x;
1695         g->zoom.initial.y = g->zoom.y;
1696         graph_element_lists_make (g);
1697         g->x_axis->s.width = g->wp.width;
1698         g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1699         g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1700         g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1701         g->y_axis->s.height = g->wp.height;
1702         g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1703         graph_pixmaps_create (g);
1704         axis_pixmaps_create (g->y_axis);
1705         axis_pixmaps_create (g->x_axis);
1706         graph_title_pixmap_create (g);
1707         graph_title_pixmap_draw (g);
1708         graph_title_pixmap_display (g);
1709         graph_display (g);
1710         axis_display (g->y_axis);
1711         axis_display (g->x_axis);
1712 }
1713
1714 static void graph_type_dependent_initialize (struct graph *g)
1715 {
1716         switch (g->type) {
1717         case GRAPH_TSEQ_STEVENS:
1718         case GRAPH_TSEQ_TCPTRACE:
1719                 tseq_initialize (g);
1720                 break;
1721         case GRAPH_THROUGHPUT:
1722                 tput_initialize (g);
1723                 break;
1724         case GRAPH_RTT:
1725                 rtt_initialize (g);
1726                 break;
1727         case GRAPH_WSCALE:
1728                 wscale_initialize (g);
1729                 break;
1730         default:
1731                 break;
1732         }
1733 }
1734
1735 static void graph_destroy (struct graph *g)
1736 {
1737         debug(DBS_FENTRY) puts ("graph_destroy()");
1738
1739         axis_destroy (g->x_axis);
1740         axis_destroy (g->y_axis);
1741         /* window_destroy (g->drawing_area); */
1742         window_destroy (g->gui.control_panel);
1743         window_destroy (g->toplevel);
1744         /* window_destroy (g->text); */
1745 #if GTK_CHECK_VERSION(2,22,0)
1746         if(g->title_surface){
1747                  cairo_surface_destroy (g->title_surface);
1748         }
1749         if(g->surface[0]){
1750                  cairo_surface_destroy (g->surface[0]);
1751         }
1752         if(g->surface[1]){
1753                  cairo_surface_destroy (g->surface[1]);
1754         }
1755 #else
1756         g_object_unref (g->pixmap[0]);
1757         g_object_unref (g->pixmap[1]);
1758 #endif /* GTK_CHECK_VERSION(2,22,0) */
1759         g_free (g->x_axis);
1760         g_free (g->y_axis);
1761         g_free ( (gpointer) (g->title) );
1762         graph_segment_list_free (g);
1763         graph_element_lists_free (g);
1764
1765         g_free (g);
1766 }
1767
1768
1769 typedef struct _tcp_scan_t {
1770         struct segment *current;
1771         int direction;
1772         struct graph *g;
1773         struct segment *last;
1774 } tcp_scan_t;
1775
1776 static int
1777 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
1778 {
1779         static struct segment *segment=NULL;
1780         tcp_scan_t *ts=(tcp_scan_t *)pct;
1781         struct tcpheader *tcphdr=(struct tcpheader *)vip;
1782
1783         if(!segment){
1784                 segment=g_malloc(sizeof (struct segment));
1785         }
1786
1787
1788         if (compare_headers(&ts->current->ip_src, &ts->current->ip_dst,
1789                             ts->current->th_sport, ts->current->th_dport,
1790                             &tcphdr->ip_src, &tcphdr->ip_dst,
1791                             tcphdr->th_sport, tcphdr->th_dport,
1792                             ts->direction)) {
1793                 segment->next = NULL;
1794                 segment->num = pinfo->fd->num;
1795                 segment->rel_secs = (guint32) pinfo->fd->rel_ts.secs;
1796                 segment->rel_usecs = pinfo->fd->rel_ts.nsecs/1000;
1797                 segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs;
1798                 segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
1799                 segment->th_seq=tcphdr->th_seq;
1800                 segment->th_ack=tcphdr->th_ack;
1801                 segment->th_win=tcphdr->th_win;
1802                 segment->th_flags=tcphdr->th_flags;
1803                 segment->th_sport=tcphdr->th_sport;
1804                 segment->th_dport=tcphdr->th_dport;
1805                 segment->th_seglen=tcphdr->th_seglen;
1806                 COPY_ADDRESS(&segment->ip_src, &tcphdr->ip_src);
1807                 COPY_ADDRESS(&segment->ip_dst, &tcphdr->ip_dst);
1808                 if (ts->g->segments) {
1809                         ts->last->next = segment;
1810                 } else {
1811                         ts->g->segments = segment;
1812                 }
1813                 ts->last = segment;
1814                 if(pinfo->fd->num==ts->current->num){
1815                         ts->g->current = segment;
1816                 }
1817
1818                 segment=NULL;
1819         }
1820
1821         return 0;
1822 }
1823
1824
1825
1826 /* here we collect all the external data we will ever need */
1827 static void graph_segment_list_get (struct graph *g)
1828 {
1829         struct segment current;
1830         GString *error_string;
1831         tcp_scan_t ts;
1832
1833
1834         debug(DBS_FENTRY) puts ("graph_segment_list_get()");
1835         select_tcpip_session (&cfile, &current);
1836         if (g->type == GRAPH_THROUGHPUT)
1837                 ts.direction = COMPARE_CURR_DIR;
1838         else
1839                 ts.direction = COMPARE_ANY_DIR;
1840
1841         /* rescan all the packets and pick up all interesting tcp headers.
1842          * we only filter for TCP here for speed and do the actual compare
1843          * in the tap listener
1844          */
1845         ts.current=&current;
1846         ts.g=g;
1847         ts.last=NULL;
1848         error_string=register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
1849         if(error_string){
1850                 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1851                     error_string->str);
1852                 g_string_free(error_string, TRUE);
1853                 exit(1);
1854         }
1855         cf_retap_packets(&cfile);
1856         remove_tap_listener(&ts);
1857 }
1858
1859
1860 typedef struct _th_t {
1861         int num_hdrs;
1862         struct tcpheader *tcphdr;
1863 } th_t;
1864
1865 static int
1866 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
1867 {
1868         th_t *th=pct;
1869
1870         th->num_hdrs++;
1871         th->tcphdr=(struct tcpheader *)vip;
1872
1873         return 0;
1874 }
1875
1876
1877
1878 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
1879  * then present the user with a dialog where the user can select WHICH tcp
1880  * session to graph.
1881  */
1882 static struct tcpheader *select_tcpip_session (capture_file *cf, struct segment *hdrs)
1883 {
1884         frame_data *fdata;
1885         epan_dissect_t edt;
1886         dfilter_t *sfcode;
1887         GString *error_string;
1888         th_t th = {0, NULL};
1889
1890         fdata = cf->current_frame;
1891
1892         /* no real filter yet */
1893         if (!dfilter_compile("tcp", &sfcode)) {
1894                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
1895                 return NULL;
1896         }
1897
1898         /* dissect the current frame */
1899         if (!cf_read_frame(cf, fdata))
1900                 return NULL;    /* error reading the frame */
1901
1902
1903         error_string=register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL);
1904         if(error_string){
1905                 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1906                     error_string->str);
1907                 g_string_free(error_string, TRUE);
1908                 exit(1);
1909         }
1910
1911         epan_dissect_init(&edt, TRUE, FALSE);
1912         epan_dissect_prime_dfilter(&edt, sfcode);
1913         tap_queue_init(&edt);
1914         epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
1915         tap_push_tapped_queue(&edt);
1916         epan_dissect_cleanup(&edt);
1917         remove_tap_listener(&th);
1918
1919         if(th.num_hdrs==0){
1920                 /* This "shouldn't happen", as our menu items shouldn't
1921                  * even be enabled if the selected packet isn't a TCP
1922                  * segment, as tcp_graph_selected_packet_enabled() is used
1923                  * to determine whether to enable any of our menu items. */
1924                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1925                     "Selected packet isn't a TCP segment");
1926                 return NULL;
1927         }
1928         /* XXX fix this later, we should show a dialog allowing the user
1929            to select which session he wants here
1930          */
1931         if(th.num_hdrs>1){
1932                 /* can only handle a single tcp layer yet */
1933                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1934                     "The selected packet has more than one TCP"
1935                     "header in it.");
1936                 return NULL;
1937         }
1938
1939         hdrs->num = fdata->num;
1940         hdrs->rel_secs = (guint32) fdata->rel_ts.secs;
1941         hdrs->rel_usecs = fdata->rel_ts.nsecs/1000;
1942         hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
1943         hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
1944         hdrs->th_seq=th.tcphdr->th_seq;
1945         hdrs->th_ack=th.tcphdr->th_ack;
1946         hdrs->th_win=th.tcphdr->th_win;
1947         hdrs->th_flags=th.tcphdr->th_flags;
1948         hdrs->th_sport=th.tcphdr->th_sport;
1949         hdrs->th_dport=th.tcphdr->th_dport;
1950         hdrs->th_seglen=th.tcphdr->th_seglen;
1951         COPY_ADDRESS(&hdrs->ip_src, &th.tcphdr->ip_src);
1952         COPY_ADDRESS(&hdrs->ip_dst, &th.tcphdr->ip_dst);
1953         return th.tcphdr;
1954
1955 }
1956
1957 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir)
1958 {
1959         int dir1, dir2;
1960
1961         dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
1962                 (!(CMP_ADDRESS(daddr1, daddr2))) &&
1963                 (sport1==sport2) &&
1964                 (dport1==dport2));
1965
1966         if(dir==COMPARE_CURR_DIR){
1967                 return dir1;
1968         } else {
1969                 dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
1970                         (!(CMP_ADDRESS(daddr1, saddr2))) &&
1971                         (sport1==dport2) &&
1972                         (dport1==sport2));
1973                 return dir1 || dir2;
1974         }
1975 }
1976
1977 static void graph_segment_list_free (struct graph *g)
1978 {
1979         struct segment *segment;
1980
1981         while (g->segments) {
1982                 segment = g->segments->next;
1983                 g_free (g->segments);
1984                 g->segments = segment;
1985         }
1986         g->segments = NULL;
1987 }
1988
1989 static void graph_element_lists_initialize (struct graph *g)
1990 {
1991         g->elists = (struct element_list *)g_malloc0 (sizeof (struct element_list));
1992 }
1993
1994 static void graph_element_lists_make (struct graph *g)
1995 {
1996         debug(DBS_FENTRY) puts ("graph_element_lists_make()");
1997
1998         switch (g->type) {
1999         case GRAPH_TSEQ_STEVENS:
2000                 tseq_stevens_make_elmtlist (g);
2001                 break;
2002         case GRAPH_TSEQ_TCPTRACE:
2003                 tseq_tcptrace_make_elmtlist (g);
2004                 break;
2005         case GRAPH_THROUGHPUT:
2006                 tput_make_elmtlist (g);
2007                 break;
2008         case GRAPH_RTT:
2009                 rtt_make_elmtlist (g);
2010                 break;
2011         case GRAPH_WSCALE:
2012                 wscale_make_elmtlist (g);
2013                 break;
2014         default:
2015                 printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
2016                 break;
2017         }
2018 }
2019
2020 static void graph_element_lists_free (struct graph *g)
2021 {
2022         struct element_list *list, *next_list;
2023
2024 #if 0
2025         for (list=g->elists; list; list=list->next)
2026                 g_free (list->elements);
2027         while (g->elists->next) {
2028                 list = g->elists->next->next;
2029                 g_free (g->elists->next);
2030                 g->elists->next = list;
2031         }
2032 #endif
2033
2034         for (list=g->elists; list; list=next_list) {
2035                 g_free (list->elements);
2036                 next_list = list->next;
2037                 g_free (list);
2038         }
2039         g->elists = NULL;       /* just to make debugging easier */
2040 }
2041
2042 static void graph_title_pixmap_create (struct graph *g)
2043 {
2044 #if GTK_CHECK_VERSION(2,22,0)
2045         if(g->title_surface){
2046                 cairo_surface_destroy (g->title_surface);
2047                 g->title_surface = NULL;
2048         }
2049
2050         g->title_surface = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
2051                         CAIRO_CONTENT_COLOR,
2052                         g->x_axis->p.width,
2053                         g->wp.y);
2054
2055 #else
2056         if (g->title_pixmap)
2057                 g_object_unref (g->title_pixmap);
2058
2059         g->title_pixmap = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2060                                                         g->x_axis->p.width, g->wp.y, -1);
2061 #endif
2062 }
2063
2064 static void graph_title_pixmap_draw (struct graph *g)
2065 {
2066         int i;
2067         cairo_t *cr;
2068
2069 #if GTK_CHECK_VERSION(2,22,0)
2070         cr = cairo_create (g->title_surface);
2071 #else
2072         cr = gdk_cairo_create (g->title_pixmap);
2073 #endif
2074         cairo_set_source_rgb (cr, 1, 1, 1);
2075         cairo_rectangle (cr, 0, 0,  g->x_axis->p.width, g->wp.y);
2076         cairo_fill (cr);
2077         cairo_destroy (cr);
2078         cr = NULL;
2079
2080         for (i=0; g->title[i]; i++) {
2081                 gint w, h;
2082         PangoLayout *layout;
2083         layout = gtk_widget_create_pango_layout(g->drawing_area,
2084                                                 g->title[i]);
2085         pango_layout_get_pixel_size(layout, &w, &h);
2086 #if GTK_CHECK_VERSION(2,22,0)
2087                 cr = cairo_create (g->title_surface);
2088 #else
2089                 cr = gdk_cairo_create (g->title_pixmap);
2090 #endif
2091                 cairo_move_to (cr, g->wp.width/2 - w/2, 20 + i*(h+3));
2092                 pango_cairo_show_layout (cr, layout);
2093                 cairo_destroy (cr);
2094         g_object_unref(G_OBJECT(layout));
2095         }
2096 }
2097
2098 static void graph_title_pixmap_display (struct graph *g)
2099 {
2100         cairo_t *cr;
2101
2102         cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
2103 #if GTK_CHECK_VERSION(2,22,0)
2104         cairo_set_source_surface (cr, g->title_surface, g->wp.x, 0);
2105 #else
2106         gdk_cairo_set_source_pixmap (cr, g->title_pixmap, g->wp.x, 0);
2107 #endif
2108         cairo_rectangle (cr, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
2109         cairo_fill (cr);
2110         cairo_destroy (cr);
2111 }
2112
2113 static void graph_pixmaps_create (struct graph *g)
2114 {
2115         debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
2116 #if GTK_CHECK_VERSION(2,22,0)
2117         if(g->surface[0]){
2118                 cairo_surface_destroy (g->surface[0]);
2119                 g->surface[0] = NULL;
2120         }
2121
2122         if(g->surface[1]){
2123                 cairo_surface_destroy (g->surface[1]);
2124                 g->surface[1] = NULL;
2125         }
2126
2127         g->surface[0] = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
2128                         CAIRO_CONTENT_COLOR,
2129                         g->wp.width,
2130                         g->wp.height);
2131
2132         g->surface[1] = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
2133                         CAIRO_CONTENT_COLOR,
2134                         g->wp.width,
2135                         g->wp.height);
2136
2137         g->displayed = 0;
2138 #else
2139         if (g->pixmap[0])
2140                 g_object_unref (g->pixmap[0]);
2141         if (g->pixmap[1])
2142                 g_object_unref (g->pixmap[1]);
2143
2144         g->pixmap[0] = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2145                                                                         g->wp.width, g->wp.height, -1);
2146         g->pixmap[1] = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2147                                                                         g->wp.width, g->wp.height, -1);
2148
2149         g->displayed = 0;
2150 #endif /* GTK_CHECK_VERSION(2,22,0) */
2151 }
2152
2153 static void graph_display (struct graph *g)
2154 {
2155         set_busy_cursor (gtk_widget_get_window(g->drawing_area));
2156         graph_pixmap_draw (g);
2157         unset_busy_cursor (gtk_widget_get_window(g->drawing_area));
2158         graph_pixmaps_switch (g);
2159         graph_pixmap_display (g);
2160 }
2161
2162 static void graph_pixmap_display (struct graph *g)
2163 {
2164         cairo_t *cr;
2165
2166         cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
2167 #if GTK_CHECK_VERSION(2,22,0)
2168         cairo_set_source_surface (cr, g->surface[g->displayed], g->wp.x, g->wp.y);
2169 #else
2170         gdk_cairo_set_source_pixmap (cr, g->pixmap[g->displayed], g->wp.x, g->wp.y);
2171 #endif /* GTK_CHECK_VERSION(2,22,0) */
2172         cairo_rectangle (cr, g->wp.x, g->wp.y, g->wp.width, g->wp.height);
2173         cairo_fill (cr);
2174         cairo_destroy (cr);
2175
2176     if (g->cross.erase_needed) {
2177        cross_xor(g, g->cross.x, g->cross.y);
2178     }
2179 }
2180
2181 static void graph_pixmaps_switch (struct graph *g)
2182 {
2183         g->displayed = 1 ^ g->displayed;
2184 }
2185
2186 static void graph_pixmap_draw (struct graph *g)
2187 {
2188         struct element_list *list;
2189         struct element *e;
2190         int not_disp;
2191         cairo_t *cr;
2192
2193         debug(DBS_FENTRY) puts ("graph_display()");
2194         not_disp = 1 ^ g->displayed;
2195
2196 #if GTK_CHECK_VERSION(2,22,0)
2197         cr = cairo_create (g->surface[not_disp]);
2198 #else
2199         cr = gdk_cairo_create (g->pixmap[not_disp]);
2200 #endif /* GTK_CHECK_VERSION(2,22,0) */
2201         cairo_set_source_rgb (cr, 1, 1, 1);
2202         cairo_rectangle (cr, 0, 0, g->wp.width, g->wp.height);
2203         cairo_fill (cr);
2204         cairo_destroy (cr);
2205         cr = NULL;
2206
2207         for (list=g->elists; list; list=list->next)
2208                 for (e=list->elements; e->type != ELMT_NONE; e++) {
2209                         switch (e->type) {
2210                         case ELMT_RECT:
2211                                 break;
2212                         case ELMT_LINE:
2213                                 draw_element_line (g, e);
2214                                 break;
2215                         case ELMT_ELLIPSE:
2216                                 draw_element_ellipse (g, e);
2217                                 break;
2218                         default:
2219                                 break;
2220                         }
2221                 }
2222 }
2223
2224 static void draw_element_line (struct graph *g, struct element *e)
2225 {
2226         int xx1, xx2, yy1, yy2;
2227         cairo_t *cr;
2228
2229         debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2230                                 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2231                                 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2232         xx1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
2233         xx2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
2234         yy1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2235         yy2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2236         if (xx1 > xx2) {
2237                 int tmp=xx2;
2238                 xx2=xx1;
2239                 xx1=tmp;
2240         }
2241         if (yy1 > yy2) {
2242                 int tmp=yy2;
2243                 yy2=yy1;
2244                 yy1=tmp;
2245         }
2246         if ((xx1<0 && xx2<0) || (xx1>=g->wp.width && xx2>=g->wp.width) ||
2247                                 (yy1<0 && yy2<0) || (yy1>=g->wp.height && yy2>=g->wp.height)) {
2248                 debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
2249                                                                         xx1, yy1, xx2, yy2);
2250                 return;
2251         }
2252         if (xx2 > g->wp.width-1)
2253                 xx2 = g->wp.width-1;
2254         if (xx1 < 0)
2255                 xx1 = 0;
2256         if (yy2 > g->wp.height-1)
2257                 yy2 = g->wp.height-1;
2258         if (yy1 < 0)
2259                 yy1 = 0;
2260         debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", xx1, yy1, xx2,yy2);
2261
2262         g_assert(e->elment_color_p!=NULL);
2263
2264 #if GTK_CHECK_VERSION(2,22,0)
2265         cr = cairo_create (g->surface[1^g->displayed]);
2266 #else
2267         cr = gdk_cairo_create (g->pixmap[1^g->displayed]);
2268 #endif
2269         cairo_set_line_width (cr, 1.0);
2270         if(e->elment_color_p!=NULL){
2271                 gdk_cairo_set_source_color (cr, e->elment_color_p);
2272         }
2273         cairo_move_to(cr, xx1+0.5, yy1+0.5);
2274         cairo_line_to(cr, xx2+0.5, yy2+0.5);
2275         cairo_stroke(cr);
2276         cairo_destroy(cr);
2277 }
2278
2279 static void draw_element_ellipse (struct graph *g, struct element *e)
2280 {
2281         cairo_t *cr;
2282         gdouble w = e->p.ellipse.dim.width;
2283         gdouble h = e->p.ellipse.dim.height;
2284         gdouble x = e->p.ellipse.dim.x + g->geom.x - g->wp.x;
2285         gdouble y = g->geom.height-1 - e->p.ellipse.dim.y + g->geom.y - g->wp.y;
2286
2287         debug(DBS_GRAPH_DRAWING) printf ("ellipse: (x, y) -> (w, h): (%f, %f) -> (%f, %f)\n", x, y, w, h);
2288
2289 #if GTK_CHECK_VERSION(2,22,0)
2290         cr = cairo_create (g->surface[1^g->displayed]);
2291 #else
2292         cr = gdk_cairo_create (g->pixmap[1^g->displayed]);
2293 #endif
2294         cairo_translate (cr, x + w / 2., y + h / 2.);
2295         cairo_scale (cr, w / 2., h / 2.);
2296         cairo_arc (cr, 0., 0., 1., 0., 2 * G_PI);
2297         cairo_fill(cr);
2298         cairo_destroy(cr);
2299 }
2300
2301 static void axis_pixmaps_create (struct axis *axis)
2302 {
2303         debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
2304 #if GTK_CHECK_VERSION(2,22,0)
2305         if(axis->surface[0]){
2306                 cairo_surface_destroy (axis->surface[0]);
2307                 axis->surface[0] = NULL;
2308         }
2309         if(axis->surface[1]){
2310                 cairo_surface_destroy (axis->surface[1]);
2311                 axis->surface[1] = NULL;
2312         }
2313         axis->surface[0] = gdk_window_create_similar_surface (gtk_widget_get_window(axis->drawing_area),
2314                         CAIRO_CONTENT_COLOR,
2315                         axis->p.width,
2316                         axis->p.height);
2317
2318         axis->surface[1] = gdk_window_create_similar_surface (gtk_widget_get_window(axis->drawing_area),
2319                         CAIRO_CONTENT_COLOR,
2320                         axis->p.width,
2321                         axis->p.height);
2322
2323         axis->displayed = 0;
2324 #else
2325         if (axis->pixmap[0])
2326                 g_object_unref (axis->pixmap[0]);
2327         if (axis->pixmap[1])
2328                 g_object_unref (axis->pixmap[1]);
2329
2330         axis->pixmap[0] = gdk_pixmap_new (gtk_widget_get_window(axis->drawing_area),
2331                                                         axis->p.width, axis->p.height, -1);
2332         axis->pixmap[1] = gdk_pixmap_new (gtk_widget_get_window(axis->drawing_area),
2333                                                         axis->p.width, axis->p.height, -1);
2334
2335         axis->displayed = 0;
2336 #endif
2337 }
2338
2339 static void axis_destroy (struct axis *axis)
2340 {
2341 #if GTK_CHECK_VERSION(2,22,0)
2342         if(axis->surface[0]){
2343                 cairo_surface_destroy (axis->surface[0]);
2344                 axis->surface[0] = NULL;
2345         }
2346         if(axis->surface[1]){
2347                 cairo_surface_destroy (axis->surface[1]);
2348                 axis->surface[1] = NULL;
2349         }
2350 #else
2351         g_object_unref (axis->pixmap[0]);
2352         g_object_unref (axis->pixmap[1]);
2353 #endif
2354         g_free ( (gpointer) (axis->label) );
2355 }
2356
2357 static void axis_display (struct axis *axis)
2358 {
2359         if (axis->flags & AXIS_ORIENTATION)
2360                 h_axis_pixmap_draw (axis);
2361         else
2362                 v_axis_pixmap_draw (axis);
2363         axis_pixmaps_switch (axis);
2364         axis_pixmap_display (axis);
2365 }
2366
2367 static void v_axis_pixmap_draw (struct axis *axis)
2368 {
2369         struct graph *g = axis->g;
2370         int i;
2371         double major_tick;
2372         int not_disp, rdigits, offset, imin, imax;
2373         double bottom, top, j, fl, corr;
2374         PangoLayout *layout;
2375         cairo_t *cr;
2376
2377         debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
2378         bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2379                                         (double )g->geom.height * g->bounds.height;
2380         bottom += axis->min;
2381         top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2382                                         (double )g->geom.height * g->bounds.height;
2383         top += axis->min;
2384         axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
2385
2386         j = axis->major - floor (axis->major);
2387         for (rdigits=0; rdigits<=6; rdigits++) {
2388                 j *= 10;
2389                 if (j<=0.000001)
2390                         break;
2391                 j = j - floor (j);
2392         }
2393
2394         not_disp = 1 ^ axis->displayed;
2395
2396 #if GTK_CHECK_VERSION(2,22,0)
2397         cr = cairo_create (axis->surface[not_disp]);
2398 #else
2399         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2400 #endif
2401         cairo_set_source_rgb (cr, 1, 1, 1);
2402         cairo_rectangle (cr, 0, 0, axis->p.width, axis->p.height);
2403         cairo_fill (cr);
2404         cairo_destroy (cr);
2405         cr = NULL;
2406
2407         /* axis */
2408 #if GTK_CHECK_VERSION(2,22,0)
2409         cr = cairo_create (axis->surface[not_disp]);
2410 #else
2411         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2412 #endif
2413         cairo_set_line_width (cr, 1.0);
2414         cairo_move_to(cr, axis->p.width - 1.5, (axis->p.height-axis->s.height)/2.0);
2415         cairo_line_to(cr, axis->s.width - 1.5, axis->p.height);
2416         cairo_stroke(cr);
2417         cairo_destroy(cr);
2418         cr = NULL;
2419
2420         offset = g->wp.y + (-g->geom.y);
2421         fl = floor (axis->min / axis->major) * axis->major;
2422         corr = rint ((axis->min - fl) * g->zoom.y);
2423
2424         /* major ticks */
2425         major_tick = axis->major * g->zoom.y;
2426         imin = (int) ((g->geom.height - offset + corr - g->wp.height) / major_tick + 1);
2427         imax = (int) ((g->geom.height - offset + corr) / major_tick);
2428         for (i=imin; i <= imax; i++) {
2429                 gint w, h;
2430                 char desc[32];
2431                 int y = (int) (g->geom.height-1 - (int )rint (i * major_tick) -
2432                                                 offset + corr + axis->s.y);
2433
2434                 debug(DBS_AXES_DRAWING) printf("%f @ %d\n",
2435                                                i*axis->major + fl, y);
2436                 if (y < 0 || y > axis->p.height)
2437                         continue;
2438
2439 #if GTK_CHECK_VERSION(2,22,0)
2440                 cr = cairo_create (axis->surface[not_disp]);
2441 #else
2442                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2443 #endif
2444                 cairo_set_line_width (cr, 1.0);
2445                 cairo_move_to(cr, axis->p.width - 15, y+0.5);
2446                 cairo_line_to(cr, axis->s.width - 1, y+0.5);
2447                 cairo_stroke(cr);
2448                 cairo_destroy(cr);
2449                 cr = NULL;
2450
2451                 g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2452         layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2453         pango_layout_get_pixel_size(layout, &w, &h);
2454 #if GTK_CHECK_VERSION(2,22,0)
2455                 cr = cairo_create (axis->surface[not_disp]);
2456 #else
2457                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2458 #endif
2459                 cairo_move_to (cr, axis->s.width-14-4-w, y - h/2);
2460                 pango_cairo_show_layout (cr, layout);
2461                 cairo_destroy (cr);
2462                 cr = NULL;
2463         g_object_unref(G_OBJECT(layout));
2464         }
2465         /* minor ticks */
2466         if (axis->minor) {
2467                 double minor_tick = axis->minor * g->zoom.y;
2468                 imin = (int) ((g->geom.height - offset + corr - g->wp.height)/minor_tick + 1);
2469                 imax = (int) ((g->geom.height - offset + corr) / minor_tick);
2470                 for (i=imin; i <= imax; i++) {
2471                         int y = (int) (g->geom.height-1 - (int )rint (i*minor_tick) -
2472                                                         offset + corr + axis->s.y);
2473
2474                         debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
2475                         if (y > 0 && y < axis->p.height)
2476 #if GTK_CHECK_VERSION(2,22,0)
2477                                 cr = cairo_create (axis->surface[not_disp]);
2478 #else
2479                                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2480 #endif
2481                                 cairo_set_line_width (cr, 1.0);
2482                                 cairo_move_to(cr, axis->s.width - 8, y+0.5);
2483                                 cairo_line_to(cr, axis->s.width - 1, y+0.5);
2484                                 cairo_stroke(cr);
2485                                 cairo_destroy(cr);
2486                                 cr = NULL;
2487                 }
2488         }
2489         for (i=0; axis->label[i]; i++) {
2490                 gint w, h;
2491         layout = gtk_widget_create_pango_layout(g->drawing_area,
2492                                                 axis->label[i]);
2493         pango_layout_get_pixel_size(layout, &w, &h);
2494 #if GTK_CHECK_VERSION(2,22,0)
2495                 cr = cairo_create (axis->surface[not_disp]);
2496 #else
2497         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2498 #endif
2499         cairo_move_to (cr, (axis->p.width - w)/2, TITLEBAR_HEIGHT-10 - i*(h+3) - h);
2500         pango_cairo_show_layout (cr, layout);
2501         cairo_destroy (cr);
2502         cr = NULL;
2503         g_object_unref(G_OBJECT(layout));
2504     }
2505 }
2506
2507 static void h_axis_pixmap_draw (struct axis *axis)
2508 {
2509         struct graph *g = axis->g;
2510         int i;
2511         double major_tick, minor_tick;
2512         int not_disp, rdigits, offset, imin, imax;
2513         double left, right, j, fl, corr;
2514         PangoLayout *layout;
2515         cairo_t *cr;
2516
2517         debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
2518         left = (g->wp.x-g->geom.x) /
2519                                         (double )g->geom.width * g->bounds.width;
2520         left += axis->min;
2521         right = (g->wp.x-g->geom.x+g->wp.width) /
2522                                         (double )g->geom.width * g->bounds.width;
2523         right += axis->min;
2524         axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
2525
2526         j = axis->major - floor (axis->major);
2527         for (rdigits=0; rdigits<=6; rdigits++) {
2528                 j *= 10;
2529                 if (j<=0.000001)
2530                         break;
2531                 j = j - floor (j);
2532         }
2533
2534         not_disp = 1 ^ axis->displayed;
2535
2536 #if GTK_CHECK_VERSION(2,22,0)
2537         cr = cairo_create (axis->surface[not_disp]);
2538 #else
2539         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2540 #endif
2541         cairo_set_source_rgb (cr, 1, 1, 1);
2542         cairo_rectangle (cr, 0, 0, axis->p.width, axis->p.height);
2543         cairo_fill (cr);
2544         cairo_destroy (cr);
2545         cr = NULL;
2546
2547         /* axis */
2548 #if GTK_CHECK_VERSION(2,22,0)
2549         cr = cairo_create (axis->surface[not_disp]);
2550 #else
2551         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2552 #endif
2553         cairo_set_line_width (cr, 1.0);
2554         cairo_move_to(cr, 0, 0.5);
2555         cairo_line_to(cr, axis->s.width + (axis->p.width-axis->s.width)/2.0, 0.5);
2556         cairo_stroke(cr);
2557         cairo_destroy(cr);
2558         cr = NULL;
2559
2560         offset = g->wp.x - g->geom.x;
2561
2562         fl = floor (axis->min / axis->major) * axis->major;
2563         corr = rint ((axis->min - fl) * g->zoom.x);
2564
2565         /* major ticks */
2566         major_tick = axis->major*g->zoom.x;
2567         imin = (int) ((offset + corr) / major_tick + 1);
2568         imax = (int) ((offset + corr + axis->s.width) / major_tick);
2569         for (i=imin; i <= imax; i++) {
2570                 char desc[32];
2571                 int w, h;
2572                 int x = (int ) (rint (i * major_tick) - offset - corr);
2573
2574                 /* printf ("%f @ %d\n", i*axis->major + fl, x); */
2575                 if (x < 0 || x > axis->s.width)
2576                         continue;
2577 #if GTK_CHECK_VERSION(2,22,0)
2578                 cr = cairo_create (axis->surface[not_disp]);
2579 #else
2580                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2581 #endif
2582                 cairo_set_line_width (cr, 1.0);
2583                 cairo_move_to(cr, x+0.5, 0);
2584                 cairo_line_to(cr, x+0.5, 15);
2585                 cairo_stroke(cr);
2586                 cairo_destroy(cr);
2587                 cr = NULL;
2588
2589                 g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2590         layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2591         pango_layout_get_pixel_size(layout, &w, &h);
2592 #if GTK_CHECK_VERSION(2,22,0)
2593                 cr = cairo_create (axis->surface[not_disp]);
2594 #else
2595                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2596 #endif
2597                 cairo_move_to (cr,  x - w/2, 15+4);
2598                 pango_cairo_show_layout (cr, layout);
2599                 cairo_destroy (cr);
2600                 cr = NULL;
2601
2602         g_object_unref(G_OBJECT(layout));
2603         }
2604         if (axis->minor > 0) {
2605                 /* minor ticks */
2606                 minor_tick = axis->minor*g->zoom.x;
2607                 imin = (int) ((offset + corr) / minor_tick + 1);
2608                 imax = (int) ((offset + corr + g->wp.width) / minor_tick);
2609                 for (i=imin; i <= imax; i++) {
2610                         int x = (int) (rint (i * minor_tick) - offset - corr);
2611                         if (x > 0 && x < axis->s.width){
2612 #if GTK_CHECK_VERSION(2,22,0)
2613                                 cr = cairo_create (axis->surface[not_disp]);
2614 #else
2615                                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2616 #endif
2617                                 cairo_set_line_width (cr, 1.0);
2618                                 cairo_move_to(cr, x+0.5, 0);
2619                                 cairo_line_to(cr, x+0.5, 8);
2620                                 cairo_stroke(cr);
2621                                 cairo_destroy(cr);
2622                                 cr = NULL;
2623                         }
2624                 }
2625         }
2626         for (i=0; axis->label[i]; i++) {
2627                 gint w, h;
2628         layout = gtk_widget_create_pango_layout(g->drawing_area,
2629                                                 axis->label[i]);
2630         pango_layout_get_pixel_size(layout, &w, &h);
2631 #if GTK_CHECK_VERSION(2,22,0)
2632                 cr = cairo_create (axis->surface[not_disp]);
2633 #else
2634                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2635 #endif
2636                 cairo_move_to (cr,  axis->s.width - w - 50, 15+h+15 + i*(h+3));
2637                 pango_cairo_show_layout (cr, layout);
2638                 cairo_destroy (cr);
2639                 cr = NULL;
2640         g_object_unref(G_OBJECT(layout));
2641         }
2642 }
2643
2644 static void axis_pixmaps_switch (struct axis *axis)
2645 {
2646         axis->displayed = 1 ^ axis->displayed;
2647 }
2648
2649 static void axis_pixmap_display (struct axis *axis)
2650 {
2651         cairo_t *cr;
2652
2653         cr = gdk_cairo_create (gtk_widget_get_window(axis->drawing_area));
2654 #if GTK_CHECK_VERSION(2,22,0)
2655         cairo_set_source_surface (cr, axis->surface[axis->displayed], axis->p.x, axis->p.y);
2656 #else
2657         gdk_cairo_set_source_pixmap (cr, axis->pixmap[axis->displayed], axis->p.x, axis->p.y);
2658 #endif
2659         cairo_rectangle (cr, axis->p.x, axis->p.y, axis->p.width, axis->p.height);
2660         cairo_fill (cr);
2661         cairo_destroy (cr);
2662
2663 }
2664
2665 static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
2666 {
2667         int i, j, ii, jj, ms;
2668         double zoom, x, steps[3]={ 0.1, 0.5 };
2669         int dim, check_needed, diminished;
2670         double majthresh[2]={2.0, 3.0};
2671
2672         debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
2673         debug(DBS_AXES_TICKS)
2674                 printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2675
2676         zoom = axis_zoom_get (axis, dir);
2677         x = xmax-x0;
2678         for (i=-9; i<=12; i++) {
2679                 if (x / pow (10, i) < 1)
2680                         break;
2681         }
2682         --i;
2683         ms = (int )(x / pow (10, i));
2684
2685         if (ms > 5) {
2686                 j = 0;
2687                 ++i;
2688         } else if (ms > 2)
2689                 j = 1;
2690         else
2691                 j = 0;
2692
2693         axis->major = steps[j] * pow (10, i);
2694
2695         debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
2696                         " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2697
2698         /* let's compute minor ticks */
2699         jj = j;
2700         ii = i;
2701         axis_ticks_down (&ii, &jj);
2702         axis->minor = steps[jj] * pow (10, ii);
2703         /* we don't want minors if they would be less than 10 pixels apart */
2704         if (axis->minor*zoom < 10) {
2705                 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2706                                         "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2707                 axis->minor = 0;
2708         }
2709
2710         check_needed = TRUE;
2711         diminished = FALSE;
2712         while (check_needed) {
2713                 check_needed = FALSE;
2714                 dim = get_label_dim (axis, dir, xmax);
2715                 debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
2716                                 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2717                                 axis->major, axis->minor, axis->major*zoom/dim,
2718                                 axis->minor*zoom/dim);
2719
2720                 /* corrections: if majors are less than majthresh[dir] times label
2721                 * dimension apart, we need to use bigger ones */
2722                 if (axis->major*zoom / dim < majthresh[dir]) {
2723                         axis_ticks_up (&ii, &jj);
2724                         axis->minor = axis->major;
2725                         axis_ticks_up (&i, &j);
2726                         axis->major = steps[j] * pow (10, i);
2727                         check_needed = TRUE;
2728                         debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
2729                                                                                 axis->major);
2730                 }
2731                 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2732                  * we could  promote them to majors as well */
2733                 if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
2734                         axis_ticks_down (&i, &j);
2735                         axis->major = axis->minor;
2736                         axis_ticks_down (&ii, &jj);
2737                         axis->minor = steps[jj] * pow (10, ii);
2738                         check_needed = TRUE;
2739                         diminished = TRUE;
2740
2741                         debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
2742                                                                                 axis->minor);
2743
2744                         if (axis->minor*zoom < 10) {
2745                                 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2746                                         "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2747                                 axis->minor = 0;
2748                         }
2749                 }
2750         }
2751
2752         debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
2753                                                         "axis->minor == %.1f\n", axis->major, axis->minor);
2754 }
2755
2756 static void axis_ticks_up (int *i, int *j)
2757 {
2758         (*j)++;
2759         if (*j>1) {
2760                 (*i)++;
2761                 *j=0;
2762         }
2763 }
2764
2765 static void axis_ticks_down (int *i, int *j)
2766 {
2767         (*j)--;
2768         if (*j<0) {
2769                 (*i)--;
2770                 *j=1;
2771         }
2772 }
2773
2774 static int get_label_dim (struct axis *axis, int dir, double label)
2775 {
2776         double y;
2777         char str[32];
2778         int rdigits, dim;
2779         PangoLayout *layout;
2780
2781          /* First, let's compute how many digits to the right of radix
2782          * we need to print */
2783         y = axis->major - floor (axis->major);
2784         for (rdigits=0; rdigits<=6; rdigits++) {
2785                 y *= 10;
2786                 if (y<=0.000001)
2787                         break;
2788                 y = y - floor (y);
2789         }
2790         g_snprintf (str, sizeof(str), "%.*f", rdigits, label);
2791         switch (dir) {
2792         case AXIS_HORIZONTAL:
2793                 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2794                                                         str);
2795                 pango_layout_get_pixel_size(layout, &dim, NULL);
2796                 g_object_unref(G_OBJECT(layout));
2797                 break;
2798         case AXIS_VERTICAL:
2799                 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2800                                                         str);
2801                 pango_layout_get_pixel_size(layout, NULL, &dim);
2802                 g_object_unref(G_OBJECT(layout));
2803                 break;
2804         default:
2805                 puts ("initialize axis: an axis must be either horizontal or vertical");
2806                 return -1;
2807         }
2808         return dim;
2809 }
2810
2811 static double axis_zoom_get (struct axis *axis, int dir)
2812 {
2813         switch (dir) {
2814         case AXIS_HORIZONTAL:
2815                 return axis->g->zoom.x;
2816         case AXIS_VERTICAL:
2817                 return axis->g->zoom.y;
2818         default:
2819                 return -1;
2820         }
2821 }
2822
2823 static void graph_select_segment (struct graph *g, int x, int y)
2824 {
2825         struct element_list *list;
2826         struct element *e;
2827         guint num = 0;
2828
2829         debug(DBS_FENTRY) puts ("graph_select_segment()");
2830
2831         x -= g->geom.x;
2832         y = g->geom.height-1 - (y - g->geom.y);
2833
2834         set_busy_cursor (gtk_widget_get_window(g->drawing_area));
2835
2836         for (list=g->elists; list; list=list->next)
2837                 for (e=list->elements; e->type != ELMT_NONE; e++) {
2838                         switch (e->type) {
2839                         case ELMT_RECT:
2840                                 break;
2841                         case ELMT_LINE:
2842                                 if (line_detect_collision (e, x, y)) {
2843                                         num = e->parent->num;
2844                                 }
2845                                 break;
2846                         case ELMT_ELLIPSE:
2847                                 if (ellipse_detect_collision (e, x, y)) {
2848                                         num = e->parent->num;
2849                                 }
2850                                 break;
2851                         default:
2852                                 break;
2853                         }
2854                 }
2855
2856
2857         if (num) {
2858                 cf_goto_frame(&cfile, num);
2859         }
2860         unset_busy_cursor (gtk_widget_get_window(g->drawing_area));
2861 }
2862
2863 static int line_detect_collision (struct element *e, int x, int y)
2864 {
2865         int xx1, yy1, xx2, yy2;
2866
2867         if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2868                 xx1 = (int )rint (e->p.line.dim.x1);
2869                 xx2 = (int )rint (e->p.line.dim.x2);
2870         } else {
2871                 xx1 = (int )rint (e->p.line.dim.x2);
2872                 xx2 = (int )rint (e->p.line.dim.x1);
2873         }
2874         if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2875                 yy1 = (int )rint (e->p.line.dim.y1);
2876                 yy2 = (int )rint (e->p.line.dim.y2);
2877         } else {
2878                 yy1 = (int )rint (e->p.line.dim.y2);
2879                 yy2 = (int )rint (e->p.line.dim.y1);
2880         }
2881         /*
2882         printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
2883          */
2884         if ((xx1==x && xx2==x && yy1<=y && y<=yy2)||(yy1==y && yy2==y && xx1<=x && x<=xx2))
2885                 return TRUE;
2886         else
2887                 return FALSE;
2888 }
2889
2890 static int ellipse_detect_collision (struct element *e, int x, int y)
2891 {
2892         int xx1, yy1, xx2, yy2;
2893
2894         xx1 = (int )rint (e->p.ellipse.dim.x);
2895         xx2 = (int )rint (e->p.ellipse.dim.x + e->p.ellipse.dim.width);
2896         yy1 = (int )rint (e->p.ellipse.dim.y - e->p.ellipse.dim.height);
2897         yy2 = (int )rint (e->p.ellipse.dim.y);
2898         /*
2899         printf ("ellipse: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
2900          */
2901         if (xx1<=x && x<=xx2 && yy1<=y && y<=yy2)
2902                 return TRUE;
2903         else
2904                 return FALSE;
2905 }
2906
2907 static void cross_xor (struct graph *g, int x, int y)
2908 {
2909 #if GTK_CHECK_VERSION(3,0,0)
2910         GdkColor color_gray15 = {0x0, 0x2626, 0x2626, 0x2626};
2911         cairo_t *cr;
2912
2913         /* XXX Fix me: lines do not disapere */
2914         if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2915                                 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2916                 /* Draw horisontal line */
2917                 cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
2918                 cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
2919                 gdk_cairo_set_source_color (cr, &color_gray15);
2920                 cairo_set_line_width (cr, 1.0);
2921                 cairo_move_to(cr,  g->wp.x, y);
2922                 cairo_line_to(cr,  g->wp.x + g->wp.width, y);
2923                 cairo_stroke(cr);
2924                 cairo_destroy(cr);
2925                 /* draw vertical line */
2926                 cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
2927                 cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
2928                 gdk_cairo_set_source_color (cr, &color_gray15);
2929                 cairo_set_line_width (cr, 1.0);
2930                 cairo_move_to(cr,  x, g->wp.y);
2931                 cairo_line_to(cr,  x, g->wp.y + g->wp.height);
2932                 cairo_stroke(cr);
2933                 cairo_destroy(cr);
2934         }
2935
2936 #else
2937
2938         if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2939                                 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2940                 gdk_draw_line (gtk_widget_get_window(g->drawing_area), xor_gc, g->wp.x,
2941                                                 y, g->wp.x + g->wp.width, y);
2942                 gdk_draw_line (gtk_widget_get_window(g->drawing_area), xor_gc, x,
2943                                                 g->wp.y, x, g->wp.y + g->wp.height);
2944         }
2945 #endif
2946 }
2947
2948 static void cross_draw (struct graph *g, int x, int y)
2949 {
2950         cross_xor (g, x, y);
2951         g->cross.x = x;
2952         g->cross.y = y;
2953         g->cross.erase_needed = 1;
2954 }
2955
2956 static void cross_erase (struct graph *g)
2957 {
2958         cross_xor (g, g->cross.x, g->cross.y);
2959         g->cross.erase_needed = 0;
2960 }
2961
2962 static void magnify_create (struct graph *g, int x, int y)
2963 {
2964         struct graph *mg;
2965         struct element_list *list, *new_list;
2966         struct ipoint pos, offsetpos;
2967         GdkEvent *e=NULL;
2968
2969         mg = g->magnify.g = (struct graph * )g_malloc (sizeof (struct graph));
2970         memcpy ((void * )mg, (void * )g, sizeof (struct graph));
2971
2972         mg->toplevel = dlg_window_new("tcp graph magnify");
2973         mg->drawing_area = mg->toplevel;
2974         gtk_window_set_default_size(GTK_WINDOW(mg->toplevel), g->magnify.width, g->magnify.height);
2975         gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
2976                         /*              | GDK_ENTER_NOTIFY_MASK */
2977                         /*              | GDK_ALL_EVENTS_MASK   */
2978                                         );
2979
2980         mg->wp.x = 0;
2981         mg->wp.y = 0;
2982         mg->wp.width = g->magnify.width;
2983         mg->wp.height = g->magnify.height;
2984         mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
2985         mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
2986         mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
2987         mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
2988
2989         /* in order to keep original element lists intact we need our own */
2990         graph_element_lists_initialize (mg);
2991         list = g->elists->next;
2992         new_list = mg->elists;
2993         for ( ; list; list=list->next) {
2994                 new_list->next =
2995                                 (struct element_list * )g_malloc (sizeof (struct element_list));
2996                 new_list = new_list->next;
2997                 new_list->next = NULL;
2998                 new_list->elements = NULL;
2999         }
3000         graph_element_lists_make (mg);
3001
3002         gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &pos.x, &pos.y);
3003         g->magnify.x = pos.x + x - g->magnify.width/2;
3004         g->magnify.y = pos.y + y - g->magnify.height/2;
3005         offsetpos.x = g->magnify.x + g->magnify.offset.x;
3006         offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
3007         offsetpos.y = g->magnify.y + g->magnify.offset.y;
3008         offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
3009         gtk_window_set_position (GTK_WINDOW(mg->drawing_area), GTK_WIN_POS_NONE);
3010         magnify_get_geom (g, x, y);
3011
3012         gtk_widget_show (mg->drawing_area);
3013
3014         /* we need to wait for the first expose event before we start drawing */
3015         while (!gdk_events_pending ());
3016         do {
3017                 e = gdk_event_get ();
3018                 if (e) {
3019                         if (e->any.type == GDK_EXPOSE) {
3020                                 gdk_event_free (e);
3021                                 break;
3022                         }
3023                         gdk_event_free (e);
3024                 }
3025         } while (e);
3026
3027 #if GTK_CHECK_VERSION(2,22,0)
3028         mg->surface[0] = mg->surface[1] = NULL;
3029 #else
3030         mg->pixmap[0] = mg->pixmap[1] = NULL;
3031 #endif /* GTK_CHECK_VERSION(2,22,0) */
3032         graph_pixmaps_create (mg);
3033         magnify_draw (g);
3034         g->magnify.active = 1;
3035 }
3036
3037 static void magnify_move (struct graph *g, int x, int y)
3038 {
3039         struct ipoint pos, offsetpos;
3040
3041         gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &pos.x, &pos.y);
3042         g->magnify.x = pos.x + x - g->magnify.width/2;
3043         g->magnify.y = pos.y + y - g->magnify.height/2;
3044         offsetpos.x = g->magnify.x + g->magnify.offset.x;
3045         offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
3046         offsetpos.y = g->magnify.y + g->magnify.offset.y;
3047         offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
3048         magnify_get_geom (g, x, y);
3049         magnify_draw (g);
3050 }
3051
3052 static void magnify_destroy (struct graph *g)
3053 {
3054         struct element_list *list;
3055         struct graph *mg = g->magnify.g;
3056
3057         window_destroy (GTK_WIDGET (mg->drawing_area));
3058
3059 #if GTK_CHECK_VERSION(2,22,0)
3060         if(mg->surface[0]){
3061                  cairo_surface_destroy (mg->surface[0]);
3062         }
3063         if(mg->surface[1]){
3064                  cairo_surface_destroy (mg->surface[1]);
3065         }
3066 #else
3067         g_object_unref (mg->pixmap[0]);
3068         g_object_unref (mg->pixmap[1]);
3069 #endif /* GTK_CHECK_VERSION(2,22,0) */
3070         for (list=mg->elists; list; list=list->next)
3071                 g_free (list->elements);
3072
3073         if (mg->elists) {
3074     while (mg->elists->next) {
3075       list = mg->elists->next->next;
3076       g_free (mg->elists->next);
3077       mg->elists->next = list;
3078     }
3079         }
3080         g_free (g->magnify.g);
3081         g->magnify.active = 0;
3082 }
3083
3084 static void magnify_get_geom (struct graph *g, int x, int y)
3085 {
3086         int posx, posy;
3087
3088         gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &posx, &posy);
3089
3090         g->magnify.g->geom.x = g->geom.x;
3091         g->magnify.g->geom.y = g->geom.y;
3092
3093         g->magnify.g->geom.x -=
3094                                 (int )rint ((g->magnify.g->geom.width - g->geom.width) *
3095                                 ((x-g->geom.x)/(double )g->geom.width));
3096         g->magnify.g->geom.y -=
3097                                 (int )rint ((g->magnify.g->geom.height - g->geom.height) *
3098                                 ((y-g->geom.y)/(double )g->geom.height));
3099
3100         /* we have coords of origin of graph relative to origin of g->toplevel.
3101          * now we need them to relate to origin of magnify window */
3102         g->magnify.g->geom.x -= (g->magnify.x - posx);
3103         g->magnify.g->geom.y -= (g->magnify.y - posy);
3104 }
3105
3106 static void magnify_draw (struct graph *g)
3107 {
3108         cairo_t *cr;
3109         int not_disp = 1 ^ g->magnify.g->displayed;
3110
3111         graph_pixmap_draw (g->magnify.g);
3112         /* graph pixmap is almost ready, just add border */
3113 #if GTK_CHECK_VERSION(2,22,0)
3114         cr = cairo_create (g->magnify.g->surface[not_disp]);
3115 #else
3116         cr = gdk_cairo_create (g->magnify.g->pixmap[not_disp]);
3117 #endif /* GTK_CHECK_VERSION(2,22,0) */
3118         cairo_set_line_width (cr, 1.0);
3119         cairo_move_to(cr, 0, 0);
3120         cairo_line_to(cr, g->magnify.width - 1, 0);
3121         cairo_stroke(cr);
3122
3123         cairo_move_to(cr, g->magnify.width - 1, 0);
3124         cairo_line_to(cr, g->magnify.width - 1, g->magnify.height);
3125         cairo_stroke(cr);
3126
3127         cairo_move_to(cr, 0, 0);
3128         cairo_line_to(cr, 0,g->magnify.height - 1);
3129         cairo_stroke(cr);
3130
3131         cairo_move_to(cr, 0, g->magnify.height - 1);
3132         cairo_line_to(cr, g->magnify.width - 1, g->magnify.height - 1);
3133         cairo_stroke(cr);
3134         cairo_destroy(cr);
3135
3136         graph_pixmaps_switch (g->magnify.g);
3137         graph_pixmap_display (g->magnify.g);
3138
3139 }
3140
3141 static gboolean configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
3142 {
3143     struct graph *g = user_data;
3144         struct {
3145                 double x, y;
3146         } zoom;
3147         int cur_g_width, cur_g_height;
3148         int cur_wp_width, cur_wp_height;
3149
3150         debug(DBS_FENTRY) puts ("configure_event()");
3151
3152         cur_wp_width = g->wp.width;
3153         cur_wp_height = g->wp.height;
3154         g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
3155         g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
3156         g->x_axis->s.width = g->wp.width;
3157         g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
3158         g->y_axis->p.height = g->wp.height + g->wp.y;
3159         g->y_axis->s.height = g->wp.height;
3160         g->x_axis->p.y = g->y_axis->p.height;
3161         zoom.x = (double )g->wp.width / cur_wp_width;
3162         zoom.y = (double )g->wp.height / cur_wp_height;
3163         cur_g_width = g->geom.width;
3164         cur_g_height = g->geom.height;
3165         g->geom.width = (int )rint (g->geom.width * zoom.x);
3166         g->geom.height = (int )rint (g->geom.height * zoom.y);
3167         g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
3168         g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
3169         /* g->zoom.initial.x = g->zoom.x; */
3170         /* g->zoom.initial.y = g->zoom.y; */
3171
3172         g->geom.x = (int) (g->wp.x - (double )g->geom.width/cur_g_width *
3173                                                         (g->wp.x - g->geom.x));
3174         g->geom.y = (int) (g->wp.y - (double )g->geom.height/cur_g_height *
3175                                                         (g->wp.y - g->geom.y));
3176 #if 0
3177         printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
3178                                 "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
3179                                 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
3180                                 g->zoom.x, g->zoom.y);
3181 #endif
3182
3183         update_zoom_spins (g);
3184         graph_element_lists_make (g);
3185         graph_pixmaps_create (g);
3186         graph_title_pixmap_create (g);
3187         axis_pixmaps_create (g->y_axis);
3188         axis_pixmaps_create (g->x_axis);
3189         /* we don't do actual drawing here; we leave it to expose handler */
3190         graph_pixmap_draw (g);
3191         graph_pixmaps_switch (g);
3192         graph_title_pixmap_draw (g);
3193         h_axis_pixmap_draw (g->x_axis);
3194         axis_pixmaps_switch (g->x_axis);
3195         v_axis_pixmap_draw (g->y_axis);
3196         axis_pixmaps_switch (g->y_axis);
3197         return TRUE;
3198 }
3199
3200 static gboolean expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
3201 {
3202     struct graph *g = user_data;
3203     cairo_t *cr;
3204
3205         debug(DBS_FENTRY) puts ("expose_event()");
3206
3207         if (event->count)
3208                 return TRUE;
3209
3210         /* lower left corner */
3211         cr = gdk_cairo_create (gtk_widget_get_window(widget));
3212         cairo_set_source_rgb (cr, 1, 1, 1);
3213         cairo_rectangle (cr, 0, g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
3214         cairo_fill (cr);
3215         cairo_destroy(cr);
3216         cr = NULL;
3217
3218         /* right margin */
3219         cr = gdk_cairo_create (gtk_widget_get_window(widget));
3220         cairo_set_source_rgb (cr, 1, 1, 1);
3221         cairo_rectangle (cr, g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
3222         cairo_fill (cr);
3223         cairo_destroy(cr);
3224         cr = NULL;
3225
3226         graph_pixmap_display (g);
3227         graph_title_pixmap_display (g);
3228         axis_pixmap_display (g->x_axis);
3229         axis_pixmap_display (g->y_axis);
3230
3231         return TRUE;
3232 }
3233
3234 static void do_zoom_mouse (struct graph *g, GdkEventButton *event)
3235 {
3236         int cur_width = g->geom.width, cur_height = g->geom.height;
3237         struct { double x, y; } factor;
3238
3239         if (g->zoom.flags & ZOOM_OUT) {
3240                 if (g->zoom.flags & ZOOM_HLOCK)
3241                         factor.x = 1.0;
3242                 else
3243                         factor.x = 1 / g->zoom.step_x;
3244                 if (g->zoom.flags & ZOOM_VLOCK)
3245                         factor.y = 1.0;
3246                 else
3247                         factor.y = 1 / g->zoom.step_y;
3248         } else {
3249                 if (g->zoom.flags & ZOOM_HLOCK)
3250                         factor.x = 1.0;
3251                 else
3252                         factor.x = g->zoom.step_x;
3253                 if (g->zoom.flags & ZOOM_VLOCK)
3254                         factor.y = 1.0;
3255                 else
3256                         factor.y = g->zoom.step_y;
3257         }
3258
3259         g->geom.width = (int )rint (g->geom.width * factor.x);
3260         g->geom.height = (int )rint (g->geom.height * factor.y);
3261         if (g->geom.width < g->wp.width)
3262                 g->geom.width = g->wp.width;
3263         if (g->geom.height < g->wp.height)
3264                 g->geom.height = g->wp.height;
3265         g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3266         g->zoom.y = (g->geom.height- 1) / g->bounds.height;
3267
3268         g->geom.x -= (int )rint ((g->geom.width - cur_width) *
3269                         ((event->x-g->geom.x)/(double )cur_width));
3270         g->geom.y -= (int )rint ((g->geom.height - cur_height) *
3271                         ((event->y-g->geom.y)/(double )cur_height));
3272
3273         if (g->geom.x > g->wp.x)
3274                 g->geom.x = g->wp.x;
3275         if (g->geom.y