Missed backing out one line...
[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(3,0,0)
313 #else
314         GdkGC *fg_gc;
315 #endif
316 #if GTK_CHECK_VERSION(2,22,0)
317         cairo_surface_t *title_surface;
318         cairo_surface_t *surface[2];
319 #else
320         GdkPixmap *title_pixmap;
321         GdkPixmap *pixmap[2];
322 #endif
323         int displayed;                  /* which of both pixmaps is on screen right now */
324         struct {
325                 GtkWidget *control_panel;
326                 /* this belongs to style structs of graph types that make use of it */
327                 GtkToggleButton *time_orig_conn, *seq_orig_isn;
328         } gui;
329         const char **title;
330         /* Next 4 attribs describe the graph in natural units, before any scaling.
331          * For example, if we want to display graph of TCP conversation that
332          * started 112.309845 s after beginning of the capture and ran until
333          * 479.093582 s, 237019 B went through the connection (in one direction)
334          * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
335          * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
336         struct bounds bounds;
337         /* dimensions and position of the graph, both expressed already in pixels.
338          * x and y give the position of upper left corner of the graph relative
339          * to origin of the graph window, size is basically bounds*zoom */
340         struct irect geom;
341         /* viewport (=graph window area which is reserved for graph itself), its
342          * size and position relative to origin of the graph window */
343         struct irect wp;
344         struct grab grab;
345         /* If we need to display 237019 sequence numbers (=bytes) onto say 500
346          * pixels, we have to scale the graph down by factor of 0.002109. This
347          * number would be zoom.y. Obviously, both directions have separate zooms.*/
348         struct zooms zoom;
349         struct cross cross;
350         struct magnify magnify;
351         struct axis *x_axis, *y_axis;
352         struct segment *segments;
353         struct segment *current;
354         struct element_list *elists;            /* element lists */
355         union {
356                 struct style_tseq_stevens tseq_stevens;
357                 struct style_tseq_tcptrace tseq_tcptrace;
358                 struct style_tput tput;
359                 struct style_rtt rtt;
360                 struct style_wscale wscale;
361         } s;
362         /* This allows keyboard to set the radio button */
363         struct {
364                 GtkToggleButton *graph_rtt, *graph_tput, *graph_tseqstevens, *graph_tseqttrace;
365                 GtkToggleButton *graph_wscale;
366         } gt;
367 };
368
369 #if GTK_CHECK_VERSION(3,0,0)
370 #else
371 static GdkGC *xor_gc = NULL;
372 #endif
373 static int refnum=0;
374
375 #define debug(section) if (debugging & section)
376 /* print function entry points */
377 #define DBS_FENTRY                      (1 << 0)
378 #define DBS_AXES_TICKS          (1 << 1)
379 #define DBS_AXES_DRAWING        (1 << 2)
380 #define DBS_GRAPH_DRAWING       (1 << 3)
381 #define DBS_TPUT_ELMTS          (1 << 4)
382 /*int debugging = DBS_FENTRY;*/
383 static int debugging = 0;
384 /*int debugging = DBS_AXES_TICKS;*/
385 /*int debugging = DBS_AXES_DRAWING;*/
386 /*int debugging = DBS_GRAPH_DRAWING;*/
387 /*int debugging = DBS_TPUT_ELMTS;*/
388
389 static void create_gui (struct graph * );
390 #if 0
391 static void create_text_widget (struct graph * );
392 static void display_text (struct graph * );
393 #endif
394 static void create_drawing_area (struct graph * );
395 static void control_panel_create (struct graph * );
396 static GtkWidget *control_panel_create_zoom_group (struct graph * );
397 static GtkWidget *control_panel_create_magnify_group (struct graph * );
398 static GtkWidget *control_panel_create_cross_group (struct graph * );
399 static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
400 static GtkWidget *control_panel_create_graph_type_group (struct graph * );
401 static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
402 static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
403 static void control_panel_add_origin_page (struct graph * , GtkWidget * );
404 static void control_panel_add_cross_page (struct graph * , GtkWidget * );
405 static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
406 static void callback_toplevel_destroy (GtkWidget * , gpointer );
407 static gboolean callback_delete_event(GtkWidget * , GdkEvent * , gpointer);
408 static void callback_close (GtkWidget * , gpointer );
409 static void callback_time_origin (GtkWidget * , gpointer );
410 static void callback_seq_origin (GtkWidget * , gpointer );
411 static void callback_zoomlock_h (GtkWidget * , gpointer );
412 static void callback_zoomlock_v (GtkWidget * , gpointer );
413 static void callback_zoom_inout (GtkWidget * , gpointer );
414 static void callback_zoom_step (GtkWidget * , gpointer );
415 static void callback_zoom_flags (GtkWidget * , gpointer );
416 static void callback_cross_on_off (GtkWidget * , gpointer );
417 static void callback_mag_width (GtkWidget * , gpointer );
418 static void callback_mag_height (GtkWidget * , gpointer );
419 static void callback_mag_x (GtkWidget * , gpointer );
420 static void callback_mag_y (GtkWidget * , gpointer );
421 static void callback_mag_zoom (GtkWidget * , gpointer );
422 static void callback_mag_flags (GtkWidget * , gpointer );
423 static void callback_graph_type (GtkWidget * , gpointer );
424 static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
425 static void callback_create_help (GtkWidget * , gpointer );
426 static void update_zoom_spins (struct graph * );
427 static struct tcpheader *select_tcpip_session (capture_file *, struct segment * );
428 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir);
429 static int get_num_dsegs (struct graph * );
430 static int get_num_acks (struct graph * );
431 static void graph_type_dependent_initialize (struct graph * );
432 static struct graph *graph_new (void);
433 static void graph_destroy (struct graph * );
434 static void graph_initialize_values (struct graph * );
435 static void graph_init_sequence (struct graph * );
436 static void draw_element_line (struct graph * , struct element * );
437 static void draw_element_ellipse (struct graph * , struct element * );
438 static void graph_display (struct graph * );
439 static void graph_pixmaps_create (struct graph * );
440 static void graph_pixmaps_switch (struct graph * );
441 static void graph_pixmap_draw (struct graph * );
442 static void graph_pixmap_display (struct graph * );
443 static void graph_element_lists_make (struct graph * );
444 static void graph_element_lists_free (struct graph * );
445 static void graph_element_lists_initialize (struct graph * );
446 static void graph_title_pixmap_create (struct graph * );
447 static void graph_title_pixmap_draw (struct graph * );
448 static void graph_title_pixmap_display (struct graph * );
449 static void graph_segment_list_get (struct graph * );
450 static void graph_segment_list_free (struct graph * );
451 static void graph_select_segment (struct graph * , int , int );
452 static int line_detect_collision (struct element * , int , int );
453 static int ellipse_detect_collision (struct element * , int , int );
454 static void axis_pixmaps_create (struct axis * );
455 static void axis_pixmaps_switch (struct axis * );
456 static void axis_display (struct axis * );
457 static void v_axis_pixmap_draw (struct axis * );
458 static void h_axis_pixmap_draw (struct axis * );
459 static void axis_pixmap_display (struct axis * );
460 static void axis_compute_ticks (struct axis * , double , double , int );
461 static double axis_zoom_get (struct axis * , int );
462 static void axis_ticks_up (int * , int * );
463 static void axis_ticks_down (int * , int * );
464 static void axis_destroy (struct axis * );
465 static int get_label_dim (struct axis * , int , double );
466 static void toggle_crosshairs (struct graph *g);
467 static void toggle_time_origin (struct graph * );
468 static void toggle_seq_origin (struct graph * );
469 static void restore_initial_graph_view (struct graph *g);
470 static void cross_xor (struct graph * , int , int );
471 static void cross_draw (struct graph * , int , int );
472 static void cross_erase (struct graph * );
473 static void magnify_create (struct graph * , int , int );
474 static void magnify_move (struct graph * , int , int );
475 static void magnify_destroy (struct graph * );
476 static void magnify_draw (struct graph * );
477 static void magnify_get_geom (struct graph * , int , int );
478 static gboolean configure_event (GtkWidget * , GdkEventConfigure * , gpointer );
479 static gboolean expose_event (GtkWidget * , GdkEventExpose * , gpointer );
480 static gboolean button_press_event (GtkWidget * , GdkEventButton * , gpointer );
481 static gboolean button_release_event (GtkWidget * , GdkEventButton * , gpointer );
482 static gboolean motion_notify_event (GtkWidget * , GdkEventMotion * , gpointer );
483 static gboolean key_press_event (GtkWidget * , GdkEventKey * , gpointer );
484 static gboolean key_release_event (GtkWidget * , GdkEventKey * , gpointer );
485 static gboolean leave_notify_event (GtkWidget * , GdkEventCrossing * , gpointer );
486 static gboolean enter_notify_event (GtkWidget * , GdkEventCrossing * , gpointer );
487 static void tseq_initialize (struct graph * );
488 static void tseq_get_bounds (struct graph * );
489 static void tseq_stevens_read_config (struct graph * );
490 static void tseq_stevens_make_elmtlist (struct graph * );
491 static void tseq_stevens_toggle_seq_origin (struct graph * );
492 static void tseq_stevens_toggle_time_origin (struct graph * );
493 static void tseq_tcptrace_read_config (struct graph * );
494 static void tseq_tcptrace_make_elmtlist (struct graph * );
495 static void tseq_tcptrace_toggle_seq_origin (struct graph * );
496 static void tseq_tcptrace_toggle_time_origin (struct graph * );
497 static void tput_initialize (struct graph * );
498 static void tput_read_config (struct graph * );
499 static void tput_make_elmtlist (struct graph * );
500 static void tput_toggle_time_origin (struct graph * );
501 static void rtt_read_config (struct graph * );
502 static void rtt_initialize (struct graph * );
503 static int rtt_is_retrans (struct unack * , unsigned int );
504 static struct unack *rtt_get_new_unack (double , unsigned int );
505 static void rtt_put_unack_on_list (struct unack ** , struct unack * );
506 static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
507 static void rtt_make_elmtlist (struct graph * );
508 static void rtt_toggle_seq_origin (struct graph * );
509 static void wscale_initialize(struct graph *);
510 static void wscale_read_config(struct graph *);
511 static void wscale_make_elmtlist(struct graph *);
512 #if defined(_WIN32) && !defined(__MINGW32__)
513 static int rint (double );      /* compiler template for Windows */
514 #endif
515
516 /*
517  * Uncomment the following define to revert WIN32 to
518  * use original mouse button controls
519  */
520
521 /* #define ORIGINAL_WIN32_BUTTONS 1 */
522
523 /* XXX - what about OS X? */
524 static char helptext[] =
525         "Here's what you can do:\n"
526         "\n"
527 #ifdef ORIGINAL_WIN32_BUTTONS
528         "   <Ctrl>-Left Mouse Button            selects segment under cursor in Wireshark's packet list\n"
529         "\n"
530         "   Left Mouse Button                   zooms in (towards area under mouse pointer)\n"
531         "   <Shift>-Left Mouse Button           zooms out\n"
532         "\n"
533         "   Right Mouse Button                  moves the graph (if zoomed in)\n"
534         "   <Ctrl>-Right Mouse Button           displays a portion of graph under cursor magnified\n"
535 #else /* !ORIGINAL_WIN32_BUTTONS */
536         "   Left Mouse Button                   selects segment under cursor in Wireshark's packet list\n"
537         "\n"
538         "   Middle Mouse Button                 zooms in (towards area under cursor)\n"
539         "   <Shift>-Middle Mouse Button zooms out\n"
540         "\n"
541         "   Right Mouse Button                  moves the graph (if zoomed in)\n"
542         "   <Ctrl>-Right Mouse Button           displays a portion of graph under cursor magnified\n"
543 #endif
544         "\n"
545         "\n"
546         "   '1'                         display Round Trip Time Graph\n"
547         "   '2'                         display Throughput Graph\n"
548         "   '3'                         display Time/Sequence Graph (Stevens)\n"
549         "   '4'                         display Time/Sequence Graph (tcptrace)\n"
550         "   '5'                         display Window Scaling Graph\n"
551         "\n"
552         "   <Space bar> toggles crosshairs on/off\n"
553         "\n"
554         "   'i' or '+'                  zoom in (towards area under mouse pointer)\n"
555         "   'o' or '-'                  zoom out\n"
556         "   'r' or <Home>       restore graph to initial state (zoom out max)\n"
557         "   's'                         toggles relative/absolute sequence numbers\n"
558         "   't'                         toggles time origin\n"
559         "   'g'                         go to frame under cursor in Wireshark's packet list (if possible)\n"
560         "\n"
561         "   <Left>                      move view left by 100 pixels (if zoomed in)\n"
562         "   <Right>             move view right 100 pixels (if zoomed in)\n"
563         "   <Up>                        move view up by 100 pixels (if zoomed in)\n"
564         "   <Down>              move view down by 100 pixels (if zoomed in)\n"
565         "\n"
566         "   <Shift><Left>       move view left by 10 pixels (if zoomed in)\n"
567         "   <Shift><Right>      move view right 10 pixels (if zoomed in)\n"
568         "   <Shift><Up> move view up by 10 pixels (if zoomed in)\n"
569         "   <Shift><Down>       move view down by 10 pixels (if zoomed in)\n"
570         "\n"
571         "   <Ctrl><Left>        move view left by 1 pixel (if zoomed in)\n"
572         "   <Ctrl><Right>       move view right 1 pixel (if zoomed in)\n"
573         "   <Ctrl><Up>  move view up by 1 pixel (if zoomed in)\n"
574         "   <Ctrl><Down>        move view down by 1 pixel (if zoomed in)\n"
575 ;
576
577 #if 0
578 static void debug_coord (struct graph *g, const char *c)
579 {
580         static unsigned count = 0;
581
582         count++;
583         printf("%u: %s\n", count, c);
584         printf("%u:  g->geom.width %d\n", count, g->geom.width);
585         printf("%u: g->geom.height %d\n", count, g->geom.height);
586         printf("%u:      g->geom.x %d\n", count, g->geom.x);
587         printf("%u:      g->geom.y %d\n", count, g->geom.y);
588
589         printf("%u:    g->wp.width %d\n", count, g->wp.width);
590         printf("%u:   g->wp.height %d\n", count, g->wp.height);
591         printf("%u:        g->wp.x %d\n", count, g->wp.x);
592         printf("%u:        g->wp.y %d\n", count, g->wp.y);
593         printf("---------------\n");
594 }
595 #endif
596
597 static void set_busy_cursor(GdkWindow *w)
598 {
599         GdkCursor* cursor;
600
601         cursor = gdk_cursor_new(GDK_WATCH);
602         gdk_window_set_cursor(w, cursor);
603         gdk_flush();
604         gdk_cursor_unref(cursor);
605 }
606
607 static void unset_busy_cursor(GdkWindow *w)
608 {
609         gdk_window_set_cursor(w, NULL);
610         gdk_flush();
611 }
612 #ifdef MAIN_MENU_USE_UIMANAGER
613 void tcp_graph_cb (GtkAction *action, gpointer user_data _U_)
614 {
615         struct segment current;
616         struct graph *g;
617         const gchar *name;
618         guint graph_type;
619
620         name = gtk_action_get_name (action);
621         if(strcmp(name, "Analyze/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-Stevens") == 0){
622                 graph_type = GRAPH_TSEQ_STEVENS;
623         }else if(strcmp(name, "/Analyze/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-tcptrace") == 0){
624                 graph_type = GRAPH_TSEQ_TCPTRACE;
625         }else if(strcmp(name, "StatisticsMenu/TCPStreamGraphMenu/Throughput-Graph") == 0){
626                 graph_type = GRAPH_THROUGHPUT;
627         }else if(strcmp(name, "/Analyze/StatisticsMenu/TCPStreamGraphMenu/RTT-Graph") == 0){
628                 graph_type = GRAPH_RTT;
629         }else if(strcmp(name, "/Analyze/StatisticsMenu/TCPStreamGraphMenu/Window-Scaling-Graph") == 0){
630                 graph_type = GRAPH_WSCALE;
631         }else{
632                 return;
633         }
634
635         debug(DBS_FENTRY) puts ("tcp_graph_cb()");
636
637         if (! (g = graph_new()))
638                 return;
639
640         refnum++;
641         graph_initialize_values (g);
642
643         g->type = graph_type;
644         if (!select_tcpip_session (&cfile, &current)) {
645                 return;
646         }
647
648         graph_segment_list_get(g);
649         create_gui(g);
650         /* display_text(g); */
651         graph_init_sequence(g);
652
653 }
654 #else
655 static void tcp_graph_cb (GtkWidget *w _U_, gpointer data, guint callback_action /*graph_type*/ _U_)
656 {
657         struct segment current;
658         struct graph *g;
659
660         guint graph_type = GPOINTER_TO_INT(data);
661
662         debug(DBS_FENTRY) puts ("tcp_graph_cb()");
663
664         if (! (g = graph_new()))
665                 return;
666
667         refnum++;
668         graph_initialize_values (g);
669
670         g->type = graph_type;
671         if (!select_tcpip_session (&cfile, &current)) {
672                 return;
673         }
674
675         graph_segment_list_get(g);
676         create_gui(g);
677         /* display_text(g); */
678         graph_init_sequence(g);
679 }
680 #endif
681 static void create_gui (struct graph *g)
682 {
683         debug(DBS_FENTRY) puts ("create_gui()");
684         /* create_text_widget(g); */
685         control_panel_create (g);
686         create_drawing_area(g);
687 }
688
689
690
691 static void create_drawing_area (struct graph *g)
692 {
693 #if GTK_CHECK_VERSION(3,0,0)
694         GtkStyleContext *context;
695 #else
696         GdkColormap *colormap;
697         GdkColor color;
698 #endif
699         char window_title[WINDOW_TITLE_LENGTH];
700         struct segment current;
701         struct tcpheader *thdr;
702         GtkAllocation widget_alloc;
703 #if 0
704         /* Prep. to include the controls in the graph window */
705         GtkWidget *frame;
706         GtkWidget *vbox;
707         GtkWidget *hbox;
708 #endif
709         debug(DBS_FENTRY) puts ("create_drawing_area()");
710         thdr=select_tcpip_session (&cfile, &current);
711         g_snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d -> %s:%d",
712                         refnum,
713                         cf_get_display_name(&cfile),
714                         ep_address_to_str(&(thdr->ip_src)),
715                         thdr->th_sport,
716                         ep_address_to_str(&(thdr->ip_dst)),
717                         thdr->th_dport
718         );
719         g->toplevel = dlg_window_new ("Tcp Graph");
720         gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title);
721         gtk_widget_set_name (g->toplevel, "Test Graph");
722         g_object_set_data(G_OBJECT(g->toplevel), "graph", g);
723
724         /* Create the drawing area */
725         g->drawing_area = gtk_drawing_area_new ();
726         g_object_set_data(G_OBJECT(g->drawing_area), "graph", g);
727         g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
728         gtk_widget_set_size_request (g->drawing_area,
729                                         g->wp.width + g->wp.x + RMARGIN_WIDTH,
730                                         g->wp.height + g->wp.y + g->x_axis->s.height);
731         gtk_widget_show (g->drawing_area);
732
733         g_signal_connect(g->drawing_area, "expose_event", G_CALLBACK(expose_event), NULL);
734         /* this has to be done later, after the widget has been shown */
735         /*
736         g_signal_connect(g->drawing_area,"configure_event", G_CALLBACK(configure_event),
737         NULL);
738          */
739         g_signal_connect(g->drawing_area, "motion_notify_event",
740                        G_CALLBACK(motion_notify_event), NULL);
741         g_signal_connect(g->drawing_area, "button_press_event",
742                        G_CALLBACK(button_press_event), NULL);
743         g_signal_connect(g->drawing_area, "button_release_event",
744                        G_CALLBACK(button_release_event), NULL);
745         g_signal_connect(g->drawing_area, "leave_notify_event",
746                        G_CALLBACK(leave_notify_event), NULL);
747         g_signal_connect(g->drawing_area, "enter_notify_event",
748                        G_CALLBACK(enter_notify_event), NULL);
749         g_signal_connect(g->toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
750         /* why doesn't drawing area send key_press_signals? */
751         g_signal_connect(g->toplevel, "key_press_event", G_CALLBACK(key_press_event), NULL);
752         g_signal_connect(g->toplevel, "key_release_event", G_CALLBACK(key_release_event),
753                        NULL);
754         gtk_widget_set_events(g->toplevel,
755                               GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
756
757         gtk_widget_set_events (g->drawing_area,
758                                GDK_EXPOSURE_MASK
759                                | GDK_LEAVE_NOTIFY_MASK
760                                | GDK_ENTER_NOTIFY_MASK
761                                | GDK_BUTTON_PRESS_MASK
762                                | GDK_BUTTON_RELEASE_MASK
763                                | GDK_POINTER_MOTION_MASK
764                                | GDK_POINTER_MOTION_HINT_MASK);
765
766 #if 0
767         /* Prep. to include the controls in the graph window */
768
769         vbox = gtk_vbox_new (FALSE, 0);
770         gtk_container_add (GTK_CONTAINER (g->toplevel), vbox);
771         gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
772         gtk_widget_show (vbox);
773
774         frame = gtk_frame_new (NULL);
775         gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
776         gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
777         gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
778         gtk_widget_show (frame);
779
780
781         /*gtk_box_pack_start (GTK_BOX (vbox), g->gui.control_panel, FALSE, FALSE, 0);*/
782
783         hbox=gtk_hbox_new(FALSE, 3);
784         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
785         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
786         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
787         gtk_widget_show(hbox);
788
789         create_ctrl_area(g, hbox);
790
791 #endif
792
793         gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
794         gtk_widget_show (g->toplevel);
795
796         /* in case we didn't get what we asked for */
797         gtk_widget_get_allocation(GTK_WIDGET (g->drawing_area), &widget_alloc);
798         g->wp.width = widget_alloc.width - g->wp.x - RMARGIN_WIDTH;
799         g->wp.height = widget_alloc.height - g->wp.y - g->x_axis->s.height;
800
801 #if GTK_CHECK_VERSION(3,0,0)
802         context = gtk_widget_get_style_context (g->drawing_area);
803         gtk_style_context_get (context, GTK_STATE_NORMAL,
804                                            "font", &g->font,
805                                            NULL);
806 #else
807         g->font = gtk_widget_get_style(g->drawing_area)->font_desc;
808 #endif
809 #if GTK_CHECK_VERSION(3,0,0)
810 #else
811         colormap = gtk_widget_get_colormap(GTK_WIDGET(g->drawing_area));
812         if (!xor_gc) {
813                 xor_gc = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
814                 gdk_gc_set_function (xor_gc, GDK_XOR);
815                 if (!gdk_color_parse ("gray15", &color)) {
816                         /*
817                          * XXX - do more than just warn.
818                          */
819                         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
820                             "Could not parse color gray15.");
821                 }
822                 if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
823                         /*
824                          * XXX - do more than just warn.
825                          */
826                         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
827                             "Could not allocate color gray15.");
828                 }
829                 gdk_gc_set_foreground (xor_gc, &color);
830         }
831         g->fg_gc = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
832
833         /* this is probably quite an ugly way to get rid of the first configure
834          * event
835          * immediatelly after gtk_widget_show (window) drawing_area gets a configure
836          * event which is handled during the next return to gtk_main which is
837          * probably the gdk_gc_new() call. configure handler calls
838          * graph_element_lists_make() which is not good because the graph struct is
839          * not fully set up yet - namely we're not sure about actual geometry
840          * and we don't have the GC's at all. so we just postpone installation
841          * of configure handler until we're ready to deal with it.
842          *
843          * !!! NEMLLO BY TO BYT NA KONCI graph_init_sequence()? !!!
844          *
845          */
846 #endif
847         g_signal_connect(g->drawing_area, "configure_event", G_CALLBACK(configure_event),
848                        NULL);
849
850         /* puts ("exiting create_drawing_area()"); */
851 }
852
853 static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
854 {
855         struct graph *g = (struct graph * )data;
856
857         if (!(g->flags & GRAPH_DESTROYED)) {
858                 g->flags |= GRAPH_DESTROYED;
859                 graph_destroy ((struct graph * )data);
860         }
861 }
862
863 static void control_panel_create (struct graph *g)
864 {
865     GtkWidget *toplevel, *notebook;
866     GtkWidget *table;
867     GtkWidget *help_bt, *close_bt, *bbox;
868     char window_title[WINDOW_TITLE_LENGTH];
869
870     debug(DBS_FENTRY) puts ("control_panel_create()");
871
872     notebook = gtk_notebook_new ();
873     control_panel_add_zoom_page (g, notebook);
874     control_panel_add_magnify_page (g, notebook);
875     control_panel_add_origin_page (g, notebook);
876     control_panel_add_cross_page (g, notebook);
877     control_panel_add_graph_type_page (g, notebook);
878
879     g_snprintf (window_title, WINDOW_TITLE_LENGTH,
880                 "Graph %d - Control - Wireshark", refnum);
881     toplevel = dlg_window_new ("tcp-graph-control");
882     gtk_window_set_title(GTK_WINDOW(toplevel), window_title);
883
884     table = gtk_table_new (2, 1,  FALSE);
885     gtk_container_add (GTK_CONTAINER (toplevel), table);
886
887     gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
888                       GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
889
890     /* Button row. */
891     bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_CLOSE, NULL);
892     gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 1, 2,
893                       GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
894
895     help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
896     g_signal_connect(help_bt, "clicked", G_CALLBACK(callback_create_help), g);
897
898     close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
899     window_set_cancel_button(toplevel, close_bt, NULL);
900     g_signal_connect(close_bt, "clicked", G_CALLBACK(callback_close), g);
901
902     g_signal_connect(toplevel, "delete_event", G_CALLBACK(callback_delete_event), g);
903     g_signal_connect(toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
904
905     /* gtk_widget_show_all (table); */
906     /* g->gui.control_panel = table; */
907     gtk_widget_show_all (toplevel);
908     window_present(toplevel);
909
910     g->gui.control_panel = toplevel;
911 }
912
913 static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
914 {
915         GtkWidget *zoom_frame;
916         GtkWidget *zoom_lock_frame;
917         GtkWidget *label;
918         GtkWidget *box;
919
920         zoom_frame = control_panel_create_zoom_group (g);
921         gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
922         zoom_lock_frame = control_panel_create_zoomlock_group (g);
923         gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
924         box = gtk_vbox_new (FALSE, 0);
925         gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
926         gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
927         gtk_widget_show (box);
928         label = gtk_label_new ("Zoom");
929         gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
930 }
931
932 static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
933 {
934         GtkWidget *mag_frame, *label;
935
936         mag_frame = control_panel_create_magnify_group (g);
937         gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
938         label = gtk_label_new ("Magnify");
939         gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
940 }
941
942 static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
943 {
944         GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
945         GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
946         GtkWidget *box, *label;
947
948         /* time origin box */
949         time_orig_cap =
950                         gtk_radio_button_new_with_label (NULL, "beginning of capture");
951         time_orig_conn = gtk_radio_button_new_with_label (
952                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (time_orig_cap)),
953                         "beginning of this TCP connection");
954         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
955         time_orig_box = gtk_vbox_new (TRUE, 0);
956         gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
957         gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
958         time_orig_frame = gtk_frame_new ("Time origin");
959         gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
960         gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
961
962         /* sequence number origin group */
963         seq_orig_isn =
964                         gtk_radio_button_new_with_label (NULL, "initial sequence number");
965         seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_get_group (
966                         GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
967         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
968         seq_orig_box = gtk_vbox_new (TRUE, 0);
969         gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
970         gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
971         seq_orig_frame = gtk_frame_new ("Sequence number origin");
972         gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
973         gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
974
975         g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
976         g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
977
978         g_signal_connect(time_orig_conn, "toggled", G_CALLBACK(callback_time_origin), g);
979         g_signal_connect(seq_orig_isn, "toggled", G_CALLBACK(callback_seq_origin), g);
980
981         box = gtk_vbox_new (FALSE, 0);
982         gtk_container_set_border_width (GTK_CONTAINER (box), 5);
983         gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
984         gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
985         gtk_widget_show (box);
986         label = gtk_label_new ("Origin");
987         gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
988 }
989
990 static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
991 {
992         GtkWidget *cross_frame, *label;
993
994         cross_frame = control_panel_create_cross_group (g);
995         gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
996         label = gtk_label_new ("Cross");
997         gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
998 }
999
1000 static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
1001 {
1002         GtkWidget *frame, *label;
1003
1004         frame = control_panel_create_graph_type_group (g);
1005         gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
1006         label = gtk_label_new ("Graph type");
1007         gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
1008 }
1009
1010 /* Treat this as a cancel, by calling "callback_close()" */
1011 static gboolean
1012 callback_delete_event(GtkWidget *widget _U_, GdkEvent *event _U_,
1013                       gpointer data)
1014 {
1015         callback_close(NULL, data);
1016         return FALSE;
1017 }
1018
1019 static void callback_close (GtkWidget *widget _U_, gpointer data)
1020 {
1021         struct graph *g = (struct graph * )data;
1022
1023         if (!(g->flags & GRAPH_DESTROYED)) {
1024                 g->flags |= GRAPH_DESTROYED;
1025                 graph_destroy ((struct graph * )data);
1026         }
1027 }
1028
1029 static void callback_create_help(GtkWidget *widget _U_, gpointer data _U_)
1030 {
1031         GtkWidget *toplevel, *vbox, *text, *scroll, *bbox, *close_bt;
1032         GtkTextBuffer *buf;
1033
1034         toplevel = dlg_window_new ("Help for TCP graphing");
1035         gtk_window_set_default_size(GTK_WINDOW(toplevel), 500, 400);
1036
1037         vbox = gtk_vbox_new (FALSE, 3);
1038         gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
1039         gtk_container_add (GTK_CONTAINER (toplevel), vbox);
1040
1041         scroll = scrolled_window_new (NULL, NULL);
1042         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
1043                                    GTK_SHADOW_IN);
1044         gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
1045         text = gtk_text_view_new();
1046         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1047         buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
1048         gtk_text_buffer_set_text(buf, helptext, -1);
1049         gtk_container_add (GTK_CONTAINER (scroll), text);
1050
1051         /* Button row. */
1052         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1053         gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
1054         gtk_widget_show(bbox);
1055
1056         close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1057         window_set_cancel_button(toplevel, close_bt, window_cancel_button_cb);
1058
1059         g_signal_connect(toplevel, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1060
1061         gtk_widget_show_all (toplevel);
1062         window_present(toplevel);
1063 }
1064
1065 static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
1066 {
1067         toggle_time_origin ((struct graph * )data);
1068 }
1069
1070 static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
1071 {
1072         toggle_seq_origin ((struct graph * )data);
1073 }
1074
1075 static GtkWidget *control_panel_create_zoom_group (struct graph *g)
1076 {
1077         GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
1078         GtkAdjustment *zoom_h_adj, *zoom_v_adj;
1079         GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
1080         GtkWidget *zoom_v_step_label, *zoom_v_step;
1081         GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
1082         GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
1083         GtkWidget *zoom_h_entry, *zoom_v_entry;
1084         GtkWidget *zoom_h_label, *zoom_v_label;
1085
1086         zoom_in = gtk_radio_button_new_with_label (NULL, "in");
1087         zoom_out = gtk_radio_button_new_with_label (
1088                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_in)), "out");
1089         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
1090         zoom_inout_box = gtk_hbox_new (FALSE, 0);
1091         gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
1092         gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
1093
1094         zoom_separator1 = gtk_hseparator_new ();
1095
1096         zoom_h_entry = gtk_entry_new ();
1097         gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
1098         gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
1099         zoom_h_label = gtk_label_new ("Horizontal:");
1100
1101         zoom_v_entry = gtk_entry_new ();
1102         gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
1103         gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
1104         zoom_v_label = gtk_label_new ("Vertical:");
1105
1106         g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
1107         g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
1108
1109         zoom_table = gtk_table_new (2, 2,  FALSE);
1110         gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
1111                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1112         gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
1113                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1114         gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
1115                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1116         gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
1117                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1118
1119         zoom_separator2 = gtk_hseparator_new ();
1120
1121         zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1122         zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
1123         gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
1124         zoom_h_step_label = gtk_label_new ("Horizontal step:");
1125
1126         zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
1127         zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
1128         gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
1129         zoom_v_step_label = gtk_label_new ("Vertical step:");
1130
1131         g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
1132         g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
1133
1134         zoom_same_toggle = gtk_check_button_new_with_label("Keep them the same");
1135         zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
1136         g_object_set_data(G_OBJECT(zoom_same_toggle), "flag", (gpointer)ZOOM_STEPS_SAME);
1137         g_object_set_data(G_OBJECT(zoom_ratio_toggle), "flag",
1138                         (gpointer)ZOOM_STEPS_KEEP_RATIO);
1139         g_signal_connect(zoom_same_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1140         g_signal_connect(zoom_ratio_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
1141
1142         zoom_step_table = gtk_table_new (4, 2,  FALSE);
1143         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
1144                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1145         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
1146                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1147         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
1148                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1149         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
1150                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1151         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
1152                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1153         gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
1154                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1155
1156         zoom_box = gtk_vbox_new (FALSE, 0);
1157         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
1158         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
1159         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
1160         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
1161         gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
1162         zoom_frame = gtk_frame_new ("Zoom");
1163         gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
1164
1165         g_object_set_data(G_OBJECT(zoom_h_step), "direction", GINT_TO_POINTER(0));
1166         g_object_set_data(G_OBJECT(zoom_v_step), "direction", GINT_TO_POINTER(1));
1167
1168         g_signal_connect(zoom_in, "toggled", G_CALLBACK(callback_zoom_inout), g);
1169         g_signal_connect(zoom_h_step, "changed", G_CALLBACK(callback_zoom_step), g);
1170         g_signal_connect(zoom_v_step, "changed", G_CALLBACK(callback_zoom_step), g);
1171
1172         g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
1173         g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
1174         return zoom_frame;
1175 }
1176
1177 static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
1178 {
1179         struct graph *g = (struct graph * )data;
1180
1181         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1182                 g->zoom.flags &= ~ZOOM_OUT;
1183         else
1184                 g->zoom.flags |= ZOOM_OUT;
1185 }
1186
1187 static void callback_zoom_step (GtkWidget *spin, gpointer data)
1188 {
1189         struct graph *g = (struct graph * )data;
1190         double value;
1191         int direction;
1192         double *zoom_this, *zoom_other;
1193         GtkSpinButton *widget_this, *widget_other;
1194         double old_this;
1195
1196         direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
1197         value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
1198
1199         if (direction) {
1200                 zoom_this = &g->zoom.step_y;
1201                 zoom_other = &g->zoom.step_x;
1202                 widget_this = g->zoom.widget.v_step;
1203                 widget_other = g->zoom.widget.h_step;
1204         } else {
1205                 zoom_this = &g->zoom.step_x;
1206                 zoom_other = &g->zoom.step_y;
1207                 widget_this = g->zoom.widget.h_step;
1208                 widget_other = g->zoom.widget.v_step;
1209         }
1210
1211         old_this = *zoom_this;
1212         *zoom_this = value;
1213         if (g->zoom.flags & ZOOM_STEPS_SAME) {
1214                 *zoom_other = value;
1215                 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1216         } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
1217                 double old_other = *zoom_other;
1218                 *zoom_other *= value / old_this;
1219                 if (*zoom_other < 1.0) {
1220                         *zoom_other = 1.0;
1221                         *zoom_this = old_this * 1.0 / old_other;
1222                         gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1223                 } else if (*zoom_other > 5.0) {
1224                         *zoom_other = 5.0;
1225                         *zoom_this = old_this * 5.0 / old_other;
1226                         gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1227                 }
1228                 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1229         }
1230 }
1231
1232 static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
1233 {
1234         struct graph *g = (struct graph * )data;
1235         int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
1236
1237         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1238                 g->zoom.flags |= flag;
1239         else
1240                 g->zoom.flags &= ~flag;
1241 }
1242
1243 static void update_zoom_spins (struct graph *g)
1244 {
1245         char s[32];
1246
1247         g_snprintf (s, sizeof(s), "%.3f", g->zoom.x / g->zoom.initial.x);
1248         gtk_entry_set_text (g->zoom.widget.h_zoom, s);
1249         g_snprintf (s, sizeof(s), "%.3f", g->zoom.y / g->zoom.initial.y);
1250         gtk_entry_set_text (g->zoom.widget.v_zoom, s);
1251 }
1252
1253 static GtkWidget *control_panel_create_magnify_group (struct graph *g)
1254 {
1255         GtkWidget *mag_width_label, *mag_width;
1256         GtkWidget *mag_height_label, *mag_height;
1257         GtkWidget *mag_x_label, *mag_x;
1258         GtkWidget *mag_y_label, *mag_y;
1259         GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
1260         GtkWidget *mag_h_zoom_label, *mag_h_zoom;
1261         GtkWidget *mag_v_zoom_label, *mag_v_zoom;
1262         GtkWidget *mag_zoom_same, *mag_zoom_ratio;
1263         GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
1264         GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
1265         GtkWidget *mag_box, *mag_frame;
1266
1267         mag_width_label = gtk_label_new ("Width:");
1268         mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1269         mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
1270
1271         mag_height_label = gtk_label_new ("Height:");
1272         mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
1273         mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
1274
1275         mag_x_label = gtk_label_new ("X:");
1276         mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1277         mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
1278
1279         mag_y_label = gtk_label_new ("Y:");
1280         mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
1281         mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
1282
1283         mag_wh_table = gtk_table_new (4, 2, FALSE);
1284         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
1285                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1286         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
1287                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1288         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
1289                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1290         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
1291                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1292         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
1293                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1294         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
1295                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1296         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
1297                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1298         gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
1299                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
1300
1301         mag_h_zoom_label = gtk_label_new ("Horizontal:");
1302         mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1303         mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
1304
1305         mag_v_zoom_label = gtk_label_new ("Vertical:");
1306         mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
1307         mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
1308
1309         mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
1310         mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
1311
1312         mag_zoom_table = gtk_table_new (4, 2, FALSE);
1313         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
1314                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1315         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
1316                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1317         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
1318                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1319         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
1320                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1321         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
1322                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1323         gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
1324                                 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
1325
1326         mag_zoom_frame = gtk_frame_new ("Magnify zoom");
1327         gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
1328         gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
1329
1330         mag_box = gtk_vbox_new (FALSE, 0);
1331         gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
1332         gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
1333         mag_frame = gtk_frame_new ("Magnify");
1334         gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
1335
1336         g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
1337         g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
1338         g_object_set_data(G_OBJECT(mag_h_zoom), "direction", GINT_TO_POINTER(0));
1339         g_object_set_data(G_OBJECT(mag_v_zoom), "direction", GINT_TO_POINTER(1));
1340         g_object_set_data(G_OBJECT(mag_zoom_same), "flag", (gpointer)MAGZOOMS_SAME);
1341         g_object_set_data(G_OBJECT(mag_zoom_ratio), "flag", (gpointer)MAGZOOMS_SAME_RATIO);
1342
1343         g_signal_connect(mag_width, "changed", G_CALLBACK(callback_mag_width), g);
1344         g_signal_connect(mag_height, "changed", G_CALLBACK(callback_mag_height), g);
1345         g_signal_connect(mag_x, "changed", G_CALLBACK(callback_mag_x), g);
1346         g_signal_connect(mag_y, "changed", G_CALLBACK(callback_mag_y), g);
1347         g_signal_connect(mag_h_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1348         g_signal_connect(mag_v_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
1349         g_signal_connect(mag_zoom_same, "clicked", G_CALLBACK(callback_mag_flags), g);
1350         g_signal_connect(mag_zoom_ratio, "clicked", G_CALLBACK(callback_mag_flags), g);
1351
1352         return mag_frame;
1353 }
1354
1355 static void callback_mag_width (GtkWidget *spin, gpointer data)
1356 {
1357         struct graph *g = (struct graph * )data;
1358
1359         g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
1360 }
1361
1362 static void callback_mag_height (GtkWidget *spin, gpointer data)
1363 {
1364         struct graph *g = (struct graph * )data;
1365
1366         g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1367 }
1368
1369 static void callback_mag_x (GtkWidget *spin, gpointer data)
1370 {
1371         struct graph *g = (struct graph * )data;
1372
1373         g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1374 }
1375
1376 static void callback_mag_y (GtkWidget *spin, gpointer data)
1377 {
1378         struct graph *g = (struct graph * )data;
1379
1380         g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1381 }
1382
1383 static void callback_mag_zoom (GtkWidget *spin, gpointer data)
1384 {
1385         struct graph *g = (struct graph * )data;
1386         double value;
1387         int direction;
1388         double *zoom_this, *zoom_other;
1389         GtkSpinButton *widget_this, *widget_other;
1390         double old_this;
1391
1392         if (g->magnify.flags & MAGZOOMS_IGNORE) {
1393                 printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
1394                 g->magnify.flags &= ~MAGZOOMS_IGNORE;
1395                 return;
1396         }
1397         direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
1398         value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
1399
1400         if (direction) {
1401                 zoom_this = &g->magnify.zoom.y;
1402                 zoom_other = &g->magnify.zoom.x;
1403                 widget_this = g->magnify.widget.v_zoom;
1404                 widget_other = g->magnify.widget.h_zoom;
1405         } else {
1406                 zoom_this = &g->magnify.zoom.x;
1407                 zoom_other = &g->magnify.zoom.y;
1408                 widget_this = g->magnify.widget.h_zoom;
1409                 widget_other = g->magnify.widget.v_zoom;
1410         }
1411
1412         old_this = *zoom_this;
1413         *zoom_this = value;
1414         if (g->magnify.flags & MAGZOOMS_SAME) {
1415                 *zoom_other = value;
1416                 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1417                 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1418         } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
1419                 double old_other = *zoom_other;
1420                 *zoom_other *= value / old_this;
1421                 if (*zoom_other < 1.0) {
1422                         *zoom_other = 1.0;
1423                         *zoom_this = old_this * 1.0 / old_other;
1424                         /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1425                         gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1426                 } else if (*zoom_other > 25.0) {
1427                         *zoom_other = 25.0;
1428                         *zoom_this = old_this * 25.0 / old_other;
1429                         /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1430                         gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
1431                 }
1432                 /* g->magnify.flags |= MAGZOOMS_IGNORE; */
1433                 gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
1434         }
1435 }
1436
1437 static void callback_mag_flags (GtkWidget *toggle, gpointer data)
1438 {
1439         struct graph *g = (struct graph * )data;
1440         int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
1441
1442         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1443                 g->magnify.flags |= flag;
1444         else
1445                 g->magnify.flags &= ~flag;
1446 }
1447
1448 static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
1449 {
1450         GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
1451         GtkWidget *zoom_lock_frame;
1452
1453         zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
1454         zoom_lock_h = gtk_radio_button_new_with_label (
1455                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1456                                         "horizontal");
1457         zoom_lock_v = gtk_radio_button_new_with_label (
1458                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
1459                                         "vertical");
1460         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
1461         zoom_lock_box = gtk_hbox_new (FALSE, 0);
1462         gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_none,
1463                            TRUE, TRUE, 0);
1464         gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
1465         gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
1466         zoom_lock_frame = gtk_frame_new ("Zoom lock:");
1467         gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
1468
1469         g_signal_connect(zoom_lock_h, "toggled", G_CALLBACK(callback_zoomlock_h), g);
1470         g_signal_connect(zoom_lock_v, "toggled", G_CALLBACK(callback_zoomlock_v), g);
1471
1472         return zoom_lock_frame;
1473 }
1474
1475 static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
1476 {
1477         struct graph *g = (struct graph * )data;
1478
1479         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1480                 g->zoom.flags |= ZOOM_HLOCK;
1481         else
1482                 g->zoom.flags &= ~ZOOM_HLOCK;
1483 }
1484
1485 static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
1486 {
1487         struct graph *g = (struct graph * )data;
1488
1489         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1490                 g->zoom.flags |= ZOOM_VLOCK;
1491         else
1492                 g->zoom.flags &= ~ZOOM_VLOCK;
1493 }
1494
1495 static GtkWidget *control_panel_create_cross_group (struct graph *g)
1496 {
1497         GtkWidget *on, *off, *box, *frame, *vbox, *label;
1498
1499         label = gtk_label_new ("Crosshairs:");
1500         off = gtk_radio_button_new_with_label (NULL, "off");
1501         on = gtk_radio_button_new_with_label (
1502                                 gtk_radio_button_get_group (GTK_RADIO_BUTTON (off)), "on");
1503         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
1504         box = gtk_hbox_new (FALSE, 0);
1505         gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
1506         gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
1507         gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
1508         vbox = gtk_vbox_new (FALSE, 0);
1509         gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
1510         /* frame = gtk_frame_new ("Cross:"); */
1511         frame = gtk_frame_new (NULL);
1512         gtk_container_add (GTK_CONTAINER (frame), vbox);
1513
1514         g_signal_connect(on, "toggled", G_CALLBACK(callback_cross_on_off), g);
1515
1516         g->cross.on_toggle = (GtkToggleButton * )on;
1517         g->cross.off_toggle = (GtkToggleButton * )off;
1518
1519         return frame;
1520 }
1521
1522 static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
1523 {
1524         struct graph *g = (struct graph * )data;
1525
1526         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle))) {
1527                 int x, y;
1528                 g->cross.draw = TRUE;
1529                 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
1530                 cross_draw (g, x, y);
1531         } else {
1532                 g->cross.draw = FALSE;
1533                 cross_erase (g);
1534         }
1535 }
1536
1537 static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
1538 {
1539         GtkWidget *graph_tseqttrace, *graph_tseqstevens;
1540         GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
1541         GtkWidget *graph_frame;
1542         GtkWidget *graph_wscale;
1543
1544         graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
1545         graph_tseqttrace = gtk_radio_button_new_with_label (
1546                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1547                                         "Time/Sequence (tcptrace-style)");
1548         graph_tseqstevens = gtk_radio_button_new_with_label (
1549                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1550                                         "Time/Sequence (Stevens'-style)");
1551         graph_rtt = gtk_radio_button_new_with_label (
1552                                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1553                                         "Round-trip Time");
1554         graph_wscale = gtk_radio_button_new_with_label (
1555                         gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
1556                         "Window Scaling");
1557
1558         switch (g->type) {
1559         case GRAPH_TSEQ_STEVENS:
1560                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
1561                 break;
1562         case GRAPH_TSEQ_TCPTRACE:
1563                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
1564                 break;
1565         case GRAPH_THROUGHPUT:
1566                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
1567                 break;
1568         case GRAPH_RTT:
1569                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
1570                 break;
1571         case GRAPH_WSCALE:
1572                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_wscale), TRUE);
1573                 break;
1574         }
1575         graph_init = gtk_check_button_new_with_label ("Init on change");
1576         graph_sep = gtk_hseparator_new ();
1577         graph_box = gtk_vbox_new (FALSE, 0);
1578         gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
1579         gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
1580         gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
1581         gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
1582         gtk_box_pack_start (GTK_BOX (graph_box), graph_wscale, TRUE, TRUE, 0);
1583         gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
1584         gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
1585         graph_frame = gtk_frame_new ("Graph type:");
1586         gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
1587
1588         g_object_set_data(G_OBJECT(graph_tseqstevens), "new-graph-type",
1589                         GINT_TO_POINTER(0));
1590         g_object_set_data(G_OBJECT(graph_tseqttrace), "new-graph-type", GINT_TO_POINTER(1));
1591         g_object_set_data(G_OBJECT(graph_tput), "new-graph-type", GINT_TO_POINTER(2));
1592         g_object_set_data(G_OBJECT(graph_rtt), "new-graph-type", GINT_TO_POINTER(3));
1593         g_object_set_data(G_OBJECT(graph_wscale), "new-graph-type", GINT_TO_POINTER(GRAPH_WSCALE));
1594
1595         g->gt.graph_wscale = (GtkToggleButton *)graph_wscale;
1596         g->gt.graph_rtt = (GtkToggleButton * )graph_rtt;
1597         g->gt.graph_tput = (GtkToggleButton * )graph_tput;
1598         g->gt.graph_tseqstevens = (GtkToggleButton * )graph_tseqstevens;
1599         g->gt.graph_tseqttrace = (GtkToggleButton * )graph_tseqttrace;
1600
1601         g_signal_connect(graph_tseqttrace, "toggled", G_CALLBACK(callback_graph_type), g);
1602         g_signal_connect(graph_tseqstevens, "toggled", G_CALLBACK(callback_graph_type), g);
1603         g_signal_connect(graph_tput, "toggled", G_CALLBACK(callback_graph_type), g);
1604         g_signal_connect(graph_rtt, "toggled", G_CALLBACK(callback_graph_type), g);
1605         g_signal_connect(graph_wscale, "toggled", G_CALLBACK(callback_graph_type), g);
1606         g_signal_connect(graph_init, "toggled", G_CALLBACK(callback_graph_init_on_typechg), g);
1607
1608         return graph_frame;
1609 }
1610
1611 static void callback_graph_type (GtkWidget *toggle, gpointer data)
1612 {
1613         int old_type, new_type;
1614         struct graph *g = (struct graph * )data;
1615
1616         new_type = (long)g_object_get_data(G_OBJECT(toggle),"new-graph-type");
1617
1618         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
1619                 return;
1620
1621         old_type = g->type;
1622         g->type = new_type;
1623
1624         graph_element_lists_free (g);
1625         graph_element_lists_initialize (g);
1626
1627         if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
1628                 /* throughput graph uses differently constructed segment list so we
1629                  * need to recreate it */
1630                 graph_segment_list_free (g);
1631                 graph_segment_list_get (g);
1632         }
1633
1634         if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
1635                 g->geom.width = g->wp.width;
1636                 g->geom.height = g->wp.height;
1637                 g->geom.x = g->wp.x;
1638                 g->geom.y = g->wp.y;
1639         }
1640         g->x_axis->min = g->y_axis->min = 0;
1641         gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
1642         gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
1643         graph_init_sequence (g);
1644 }
1645
1646 static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
1647 {
1648         ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
1649 }
1650
1651 static struct graph *graph_new (void)
1652 {
1653         struct graph *g;
1654
1655         g = (struct graph * )g_malloc0 (sizeof (struct graph));
1656         graph_element_lists_initialize (g);
1657
1658         g->x_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1659         g->y_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
1660         g->x_axis->g = g;
1661         g->x_axis->flags = 0;
1662         g->x_axis->flags |= AXIS_ORIENTATION;
1663         g->x_axis->s.x = g->x_axis->s.y = 0;
1664         g->x_axis->s.height = HAXIS_INIT_HEIGHT;
1665         g->x_axis->p.x = VAXIS_INIT_WIDTH;
1666         g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1667         g->y_axis->g = g;
1668         g->y_axis->flags = 0;
1669         g->y_axis->flags &= ~AXIS_ORIENTATION;
1670         g->y_axis->p.x = g->y_axis->p.y = 0;
1671         g->y_axis->p.width = VAXIS_INIT_WIDTH;
1672         g->y_axis->s.x = 0;
1673         g->y_axis->s.y = TITLEBAR_HEIGHT;
1674         g->y_axis->s.width = VAXIS_INIT_WIDTH;
1675
1676         return g;
1677 }
1678
1679 static void graph_initialize_values (struct graph *g)
1680 {
1681         g->geom.width = g->wp.width = 750;
1682         g->geom.height = g->wp.height = 550;
1683         g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
1684         g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
1685         g->flags = 0;
1686         /* g->zoom.x = g->zoom.y = 1.0; */
1687         g->zoom.step_x = g->zoom.step_y = 1.2;
1688         g->zoom.flags = 0;
1689         g->cross.draw = g->cross.erase_needed = 0;
1690         g->grab.grabbed = 0;
1691         g->magnify.active = 0;
1692         g->magnify.offset.x = g->magnify.offset.y = 0;
1693         g->magnify.width = g->magnify.height = 250;
1694         g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
1695         g->magnify.flags = 0;
1696 }
1697
1698 static void graph_init_sequence (struct graph *g)
1699 {
1700         debug(DBS_FENTRY) puts ("graph_init_sequence()");
1701
1702         graph_type_dependent_initialize (g);
1703         g->zoom.initial.x = g->zoom.x;
1704         g->zoom.initial.y = g->zoom.y;
1705         graph_element_lists_make (g);
1706         g->x_axis->s.width = g->wp.width;
1707         g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
1708         g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
1709         g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
1710         g->y_axis->s.height = g->wp.height;
1711         g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
1712         graph_pixmaps_create (g);
1713         axis_pixmaps_create (g->y_axis);
1714         axis_pixmaps_create (g->x_axis);
1715         graph_title_pixmap_create (g);
1716         graph_title_pixmap_draw (g);
1717         graph_title_pixmap_display (g);
1718         graph_display (g);
1719         axis_display (g->y_axis);
1720         axis_display (g->x_axis);
1721 }
1722
1723 static void graph_type_dependent_initialize (struct graph *g)
1724 {
1725         switch (g->type) {
1726         case GRAPH_TSEQ_STEVENS:
1727         case GRAPH_TSEQ_TCPTRACE:
1728                 tseq_initialize (g);
1729                 break;
1730         case GRAPH_THROUGHPUT:
1731                 tput_initialize (g);
1732                 break;
1733         case GRAPH_RTT:
1734                 rtt_initialize (g);
1735                 break;
1736         case GRAPH_WSCALE:
1737                 wscale_initialize (g);
1738                 break;
1739         default:
1740                 break;
1741         }
1742 }
1743
1744 static void graph_destroy (struct graph *g)
1745 {
1746         debug(DBS_FENTRY) puts ("graph_destroy()");
1747
1748         axis_destroy (g->x_axis);
1749         axis_destroy (g->y_axis);
1750         /* window_destroy (g->drawing_area); */
1751         window_destroy (g->gui.control_panel);
1752         window_destroy (g->toplevel);
1753         /* window_destroy (g->text); */
1754 #if GTK_CHECK_VERSION(2,22,0)
1755         if(g->title_surface){
1756                  cairo_surface_destroy (g->title_surface);
1757         }
1758         if(g->surface[0]){
1759                  cairo_surface_destroy (g->surface[0]);
1760         }
1761         if(g->surface[1]){
1762                  cairo_surface_destroy (g->surface[1]);
1763         }
1764 #else
1765         g_object_unref (g->pixmap[0]);
1766         g_object_unref (g->pixmap[1]);
1767 #endif /* GTK_CHECK_VERSION(2,22,0) */
1768         g_free (g->x_axis);
1769         g_free (g->y_axis);
1770         g_free ( (gpointer) (g->title) );
1771         graph_segment_list_free (g);
1772         graph_element_lists_free (g);
1773
1774         g_free (g);
1775 }
1776
1777
1778 typedef struct _tcp_scan_t {
1779         struct segment *current;
1780         int direction;
1781         struct graph *g;
1782         struct segment *last;
1783 } tcp_scan_t;
1784
1785 static int
1786 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
1787 {
1788         static struct segment *segment=NULL;
1789         tcp_scan_t *ts=(tcp_scan_t *)pct;
1790         struct tcpheader *tcphdr=(struct tcpheader *)vip;
1791
1792         if(!segment){
1793                 segment=g_malloc(sizeof (struct segment));
1794         }
1795
1796
1797         if (compare_headers(&ts->current->ip_src, &ts->current->ip_dst,
1798                             ts->current->th_sport, ts->current->th_dport,
1799                             &tcphdr->ip_src, &tcphdr->ip_dst,
1800                             tcphdr->th_sport, tcphdr->th_dport,
1801                             ts->direction)) {
1802                 segment->next = NULL;
1803                 segment->num = pinfo->fd->num;
1804                 segment->rel_secs = (guint32) pinfo->fd->rel_ts.secs;
1805                 segment->rel_usecs = pinfo->fd->rel_ts.nsecs/1000;
1806                 segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs;
1807                 segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
1808                 segment->th_seq=tcphdr->th_seq;
1809                 segment->th_ack=tcphdr->th_ack;
1810                 segment->th_win=tcphdr->th_win;
1811                 segment->th_flags=tcphdr->th_flags;
1812                 segment->th_sport=tcphdr->th_sport;
1813                 segment->th_dport=tcphdr->th_dport;
1814                 segment->th_seglen=tcphdr->th_seglen;
1815                 COPY_ADDRESS(&segment->ip_src, &tcphdr->ip_src);
1816                 COPY_ADDRESS(&segment->ip_dst, &tcphdr->ip_dst);
1817                 if (ts->g->segments) {
1818                         ts->last->next = segment;
1819                 } else {
1820                         ts->g->segments = segment;
1821                 }
1822                 ts->last = segment;
1823                 if(pinfo->fd->num==ts->current->num){
1824                         ts->g->current = segment;
1825                 }
1826
1827                 segment=NULL;
1828         }
1829
1830         return 0;
1831 }
1832
1833
1834
1835 /* here we collect all the external data we will ever need */
1836 static void graph_segment_list_get (struct graph *g)
1837 {
1838         struct segment current;
1839         GString *error_string;
1840         tcp_scan_t ts;
1841
1842
1843         debug(DBS_FENTRY) puts ("graph_segment_list_get()");
1844         select_tcpip_session (&cfile, &current);
1845         if (g->type == GRAPH_THROUGHPUT)
1846                 ts.direction = COMPARE_CURR_DIR;
1847         else
1848                 ts.direction = COMPARE_ANY_DIR;
1849
1850         /* rescan all the packets and pick up all interesting tcp headers.
1851          * we only filter for TCP here for speed and do the actual compare
1852          * in the tap listener
1853          */
1854         ts.current=&current;
1855         ts.g=g;
1856         ts.last=NULL;
1857         error_string=register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
1858         if(error_string){
1859                 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1860                     error_string->str);
1861                 g_string_free(error_string, TRUE);
1862                 exit(1);
1863         }
1864         cf_retap_packets(&cfile);
1865         remove_tap_listener(&ts);
1866 }
1867
1868
1869 typedef struct _th_t {
1870         int num_hdrs;
1871         struct tcpheader *tcphdr;
1872 } th_t;
1873
1874 static int
1875 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
1876 {
1877         th_t *th=pct;
1878
1879         th->num_hdrs++;
1880         th->tcphdr=(struct tcpheader *)vip;
1881
1882         return 0;
1883 }
1884
1885
1886
1887 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
1888  * then present the user with a dialog where the user can select WHICH tcp
1889  * session to graph.
1890  */
1891 static struct tcpheader *select_tcpip_session (capture_file *cf, struct segment *hdrs)
1892 {
1893         frame_data *fdata;
1894         epan_dissect_t edt;
1895         dfilter_t *sfcode;
1896         GString *error_string;
1897         th_t th = {0, NULL};
1898
1899         fdata = cf->current_frame;
1900
1901         /* no real filter yet */
1902         if (!dfilter_compile("tcp", &sfcode)) {
1903                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
1904                 return NULL;
1905         }
1906
1907         /* dissect the current frame */
1908         if (!cf_read_frame(cf, fdata))
1909                 return NULL;    /* error reading the frame */
1910
1911
1912         error_string=register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL);
1913         if(error_string){
1914                 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
1915                     error_string->str);
1916                 g_string_free(error_string, TRUE);
1917                 exit(1);
1918         }
1919
1920         epan_dissect_init(&edt, TRUE, FALSE);
1921         epan_dissect_prime_dfilter(&edt, sfcode);
1922         tap_queue_init(&edt);
1923         epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
1924         tap_push_tapped_queue(&edt);
1925         epan_dissect_cleanup(&edt);
1926         remove_tap_listener(&th);
1927
1928         if(th.num_hdrs==0){
1929                 /* This "shouldn't happen", as our menu items shouldn't
1930                  * even be enabled if the selected packet isn't a TCP
1931                  * segment, as tcp_graph_selected_packet_enabled() is used
1932                  * to determine whether to enable any of our menu items. */
1933                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1934                     "Selected packet isn't a TCP segment");
1935                 return NULL;
1936         }
1937         /* XXX fix this later, we should show a dialog allowing the user
1938            to select which session he wants here
1939          */
1940         if(th.num_hdrs>1){
1941                 /* can only handle a single tcp layer yet */
1942                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1943                     "The selected packet has more than one TCP"
1944                     "header in it.");
1945                 return NULL;
1946         }
1947
1948         hdrs->num = fdata->num;
1949         hdrs->rel_secs = (guint32) fdata->rel_ts.secs;
1950         hdrs->rel_usecs = fdata->rel_ts.nsecs/1000;
1951         hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
1952         hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
1953         hdrs->th_seq=th.tcphdr->th_seq;
1954         hdrs->th_ack=th.tcphdr->th_ack;
1955         hdrs->th_win=th.tcphdr->th_win;
1956         hdrs->th_flags=th.tcphdr->th_flags;
1957         hdrs->th_sport=th.tcphdr->th_sport;
1958         hdrs->th_dport=th.tcphdr->th_dport;
1959         hdrs->th_seglen=th.tcphdr->th_seglen;
1960         COPY_ADDRESS(&hdrs->ip_src, &th.tcphdr->ip_src);
1961         COPY_ADDRESS(&hdrs->ip_dst, &th.tcphdr->ip_dst);
1962         return th.tcphdr;
1963
1964 }
1965
1966 static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir)
1967 {
1968         int dir1, dir2;
1969
1970         dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
1971                 (!(CMP_ADDRESS(daddr1, daddr2))) &&
1972                 (sport1==sport2) &&
1973                 (dport1==dport2));
1974
1975         if(dir==COMPARE_CURR_DIR){
1976                 return dir1;
1977         } else {
1978                 dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
1979                         (!(CMP_ADDRESS(daddr1, saddr2))) &&
1980                         (sport1==dport2) &&
1981                         (dport1==sport2));
1982                 return dir1 || dir2;
1983         }
1984 }
1985
1986 static void graph_segment_list_free (struct graph *g)
1987 {
1988         struct segment *segment;
1989
1990         while (g->segments) {
1991                 segment = g->segments->next;
1992                 g_free (g->segments);
1993                 g->segments = segment;
1994         }
1995         g->segments = NULL;
1996 }
1997
1998 static void graph_element_lists_initialize (struct graph *g)
1999 {
2000         g->elists = (struct element_list *)g_malloc0 (sizeof (struct element_list));
2001 }
2002
2003 static void graph_element_lists_make (struct graph *g)
2004 {
2005         debug(DBS_FENTRY) puts ("graph_element_lists_make()");
2006
2007         switch (g->type) {
2008         case GRAPH_TSEQ_STEVENS:
2009                 tseq_stevens_make_elmtlist (g);
2010                 break;
2011         case GRAPH_TSEQ_TCPTRACE:
2012                 tseq_tcptrace_make_elmtlist (g);
2013                 break;
2014         case GRAPH_THROUGHPUT:
2015                 tput_make_elmtlist (g);
2016                 break;
2017         case GRAPH_RTT:
2018                 rtt_make_elmtlist (g);
2019                 break;
2020         case GRAPH_WSCALE:
2021                 wscale_make_elmtlist (g);
2022                 break;
2023         default:
2024                 printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
2025                 break;
2026         }
2027 }
2028
2029 static void graph_element_lists_free (struct graph *g)
2030 {
2031         struct element_list *list, *next_list;
2032
2033 #if 0
2034         for (list=g->elists; list; list=list->next)
2035                 g_free (list->elements);
2036         while (g->elists->next) {
2037                 list = g->elists->next->next;
2038                 g_free (g->elists->next);
2039                 g->elists->next = list;
2040         }
2041 #endif
2042
2043         for (list=g->elists; list; list=next_list) {
2044                 g_free (list->elements);
2045                 next_list = list->next;
2046                 g_free (list);
2047         }
2048         g->elists = NULL;       /* just to make debugging easier */
2049 }
2050
2051 static void graph_title_pixmap_create (struct graph *g)
2052 {
2053 #if GTK_CHECK_VERSION(2,22,0)
2054         if(g->title_surface){
2055                 cairo_surface_destroy (g->title_surface);
2056                 g->title_surface = NULL;
2057         }
2058
2059         g->title_surface = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
2060                         CAIRO_CONTENT_COLOR,
2061                         g->x_axis->p.width,
2062                         g->wp.y);
2063
2064 #else
2065         if (g->title_pixmap)
2066                 g_object_unref (g->title_pixmap);
2067
2068         g->title_pixmap = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2069                                                         g->x_axis->p.width, g->wp.y, -1);
2070 #endif
2071 }
2072
2073 static void graph_title_pixmap_draw (struct graph *g)
2074 {
2075         int i;
2076         cairo_t *cr;
2077
2078 #if GTK_CHECK_VERSION(2,22,0)
2079         cr = cairo_create (g->title_surface);
2080 #else
2081         cr = gdk_cairo_create (g->title_pixmap);
2082 #endif
2083         cairo_set_source_rgb (cr, 1, 1, 1);
2084         cairo_rectangle (cr, 0, 0,  g->x_axis->p.width, g->wp.y);
2085         cairo_fill (cr);
2086         cairo_destroy (cr);
2087         cr = NULL;
2088
2089         for (i=0; g->title[i]; i++) {
2090                 gint w, h;
2091         PangoLayout *layout;
2092         layout = gtk_widget_create_pango_layout(g->drawing_area,
2093                                                 g->title[i]);
2094         pango_layout_get_pixel_size(layout, &w, &h);
2095 #if GTK_CHECK_VERSION(2,22,0)
2096                 cr = cairo_create (g->title_surface);
2097 #else
2098                 cr = gdk_cairo_create (g->title_pixmap);
2099 #endif
2100                 cairo_move_to (cr, g->wp.width/2 - w/2, 20 + i*(h+3));
2101                 pango_cairo_show_layout (cr, layout);
2102                 cairo_destroy (cr);
2103         g_object_unref(G_OBJECT(layout));
2104         }
2105 }
2106
2107 static void graph_title_pixmap_display (struct graph *g)
2108 {
2109         cairo_t *cr;
2110
2111         cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
2112 #if GTK_CHECK_VERSION(2,22,0)
2113         cairo_set_source_surface (cr, g->title_surface, g->wp.x, 0);
2114 #else
2115         gdk_cairo_set_source_pixmap (cr, g->title_pixmap, g->wp.x, 0);
2116 #endif
2117         cairo_rectangle (cr, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
2118         cairo_fill (cr);
2119         cairo_destroy (cr);
2120 }
2121
2122 static void graph_pixmaps_create (struct graph *g)
2123 {
2124         debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
2125 #if GTK_CHECK_VERSION(2,22,0)
2126         if(g->surface[0]){
2127                 cairo_surface_destroy (g->surface[0]);
2128                 g->surface[0] = NULL;
2129         }
2130
2131         if(g->surface[1]){
2132                 cairo_surface_destroy (g->surface[1]);
2133                 g->surface[1] = NULL;
2134         }
2135
2136         g->surface[0] = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
2137                         CAIRO_CONTENT_COLOR,
2138                         g->wp.width,
2139                         g->wp.height);
2140
2141         g->surface[1] = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
2142                         CAIRO_CONTENT_COLOR,
2143                         g->wp.width,
2144                         g->wp.height);
2145
2146         g->displayed = 0;
2147 #else
2148         if (g->pixmap[0])
2149                 g_object_unref (g->pixmap[0]);
2150         if (g->pixmap[1])
2151                 g_object_unref (g->pixmap[1]);
2152
2153         g->pixmap[0] = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2154                                                                         g->wp.width, g->wp.height, -1);
2155         g->pixmap[1] = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
2156                                                                         g->wp.width, g->wp.height, -1);
2157
2158         g->displayed = 0;
2159 #endif /* GTK_CHECK_VERSION(2,22,0) */
2160 }
2161
2162 static void graph_display (struct graph *g)
2163 {
2164         set_busy_cursor (gtk_widget_get_window(g->drawing_area));
2165         graph_pixmap_draw (g);
2166         unset_busy_cursor (gtk_widget_get_window(g->drawing_area));
2167         graph_pixmaps_switch (g);
2168         graph_pixmap_display (g);
2169 }
2170
2171 static void graph_pixmap_display (struct graph *g)
2172 {
2173         cairo_t *cr;
2174
2175         cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
2176 #if GTK_CHECK_VERSION(2,22,0)
2177         cairo_set_source_surface (cr, g->surface[g->displayed], g->wp.x, g->wp.y);
2178 #else
2179         gdk_cairo_set_source_pixmap (cr, g->pixmap[g->displayed], g->wp.x, g->wp.y);
2180 #endif /* GTK_CHECK_VERSION(2,22,0) */
2181         cairo_rectangle (cr, g->wp.x, g->wp.y, g->wp.width, g->wp.height);
2182         cairo_fill (cr);
2183         cairo_destroy (cr);
2184
2185     if (g->cross.erase_needed) {
2186        cross_xor(g, g->cross.x, g->cross.y);
2187     }
2188 }
2189
2190 static void graph_pixmaps_switch (struct graph *g)
2191 {
2192         g->displayed = 1 ^ g->displayed;
2193 }
2194
2195 static void graph_pixmap_draw (struct graph *g)
2196 {
2197         struct element_list *list;
2198         struct element *e;
2199         int not_disp;
2200         cairo_t *cr;
2201
2202         debug(DBS_FENTRY) puts ("graph_display()");
2203         not_disp = 1 ^ g->displayed;
2204
2205 #if GTK_CHECK_VERSION(2,22,0)
2206         cr = cairo_create (g->surface[not_disp]);
2207 #else
2208         cr = gdk_cairo_create (g->pixmap[not_disp]);
2209 #endif /* GTK_CHECK_VERSION(2,22,0) */
2210         cairo_set_source_rgb (cr, 1, 1, 1);
2211         cairo_rectangle (cr, 0, 0, g->wp.width, g->wp.height);
2212         cairo_fill (cr);
2213         cairo_destroy (cr);
2214         cr = NULL;
2215
2216         for (list=g->elists; list; list=list->next)
2217                 for (e=list->elements; e->type != ELMT_NONE; e++) {
2218                         switch (e->type) {
2219                         case ELMT_RECT:
2220                                 break;
2221                         case ELMT_LINE:
2222                                 draw_element_line (g, e);
2223                                 break;
2224                         case ELMT_ELLIPSE:
2225                                 draw_element_ellipse (g, e);
2226                                 break;
2227                         default:
2228                                 break;
2229                         }
2230                 }
2231 }
2232
2233 static void draw_element_line (struct graph *g, struct element *e)
2234 {
2235         int xx1, xx2, yy1, yy2;
2236         cairo_t *cr;
2237
2238         debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
2239                                 "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
2240                                 e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
2241         xx1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
2242         xx2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
2243         yy1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
2244         yy2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
2245         if (xx1 > xx2) {
2246                 int tmp=xx2;
2247                 xx2=xx1;
2248                 xx1=tmp;
2249         }
2250         if (yy1 > yy2) {
2251                 int tmp=yy2;
2252                 yy2=yy1;
2253                 yy1=tmp;
2254         }
2255         if ((xx1<0 && xx2<0) || (xx1>=g->wp.width && xx2>=g->wp.width) ||
2256                                 (yy1<0 && yy2<0) || (yy1>=g->wp.height && yy2>=g->wp.height)) {
2257                 debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
2258                                                                         xx1, yy1, xx2, yy2);
2259                 return;
2260         }
2261         if (xx2 > g->wp.width-1)
2262                 xx2 = g->wp.width-1;
2263         if (xx1 < 0)
2264                 xx1 = 0;
2265         if (yy2 > g->wp.height-1)
2266                 yy2 = g->wp.height-1;
2267         if (yy1 < 0)
2268                 yy1 = 0;
2269         debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", xx1, yy1, xx2,yy2);
2270
2271         g_assert(e->elment_color_p!=NULL);
2272
2273 #if GTK_CHECK_VERSION(2,22,0)
2274         cr = cairo_create (g->surface[1^g->displayed]);
2275 #else
2276         cr = gdk_cairo_create (g->pixmap[1^g->displayed]);
2277 #endif
2278         cairo_set_line_width (cr, 1.0);
2279         if(e->elment_color_p!=NULL){
2280                 gdk_cairo_set_source_color (cr, e->elment_color_p);
2281         }
2282         cairo_move_to(cr, xx1+0.5, yy1+0.5);
2283         cairo_line_to(cr, xx2+0.5, yy2+0.5);
2284         cairo_stroke(cr);
2285         cairo_destroy(cr);
2286 }
2287
2288 static void draw_element_ellipse (struct graph *g, struct element *e)
2289 {
2290 #if GTK_CHECK_VERSION(2,22,0)
2291         cairo_t *cr;
2292         gdouble w = e->p.ellipse.dim.width;
2293         gdouble h = e->p.ellipse.dim.height;
2294         gdouble x = e->p.ellipse.dim.x + g->geom.x - g->wp.x;
2295         gdouble y = g->geom.height-1 - e->p.ellipse.dim.y + g->geom.y - g->wp.y;
2296
2297         debug(DBS_GRAPH_DRAWING) printf ("ellipse: (x, y) -> (w, h): (%f, %f) -> (%f, %f)\n", x, y, w, h);
2298
2299         cr = cairo_create (g->surface[1^g->displayed]);
2300         cairo_translate (cr, x + w / 2., y + h / 2.);
2301         cairo_scale (cr, w / 2., h / 2.);
2302         cairo_arc (cr, 0., 0., 1., 0., 2 * G_PI);
2303         cairo_fill(cr);
2304         cairo_destroy(cr);
2305 #else
2306         int xx1, xx2, yy1, yy2;
2307
2308         xx1 = (int )rint (e->p.ellipse.dim.x + g->geom.x - g->wp.x);
2309         xx2 = (int )e->p.ellipse.dim.width;
2310         yy1 = (int )rint (g->geom.height-1 - e->p.ellipse.dim.y + g->geom.y - g->wp.y);
2311         yy2 = (int )e->p.ellipse.dim.height;
2312         if (xx1<-xx2 || xx1>=g->wp.width || yy1<-yy2 || yy1>=g->wp.height)
2313                 return;
2314         debug(DBS_GRAPH_DRAWING) printf ("ellipse: (%d,%d)->(%d,%d)\n", xx1, yy1, xx2, yy2);
2315
2316         gdk_draw_arc (g->pixmap[1^g->displayed], g->fg_gc, TRUE, xx1,
2317                                         yy1, xx2, yy2, 0, 23040);
2318 #endif
2319         /* NOTE the coordinates and angels needs to be recalculated as cairo_arc works differently */
2320 }
2321
2322 static void axis_pixmaps_create (struct axis *axis)
2323 {
2324         debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
2325 #if GTK_CHECK_VERSION(2,22,0)
2326         if(axis->surface[0]){
2327                 cairo_surface_destroy (axis->surface[0]);
2328                 axis->surface[0] = NULL;
2329         }
2330         if(axis->surface[1]){
2331                 cairo_surface_destroy (axis->surface[1]);
2332                 axis->surface[1] = NULL;
2333         }
2334         axis->surface[0] = gdk_window_create_similar_surface (gtk_widget_get_window(axis->drawing_area),
2335                         CAIRO_CONTENT_COLOR,
2336                         axis->p.width,
2337                         axis->p.height);
2338
2339         axis->surface[1] = gdk_window_create_similar_surface (gtk_widget_get_window(axis->drawing_area),
2340                         CAIRO_CONTENT_COLOR,
2341                         axis->p.width,
2342                         axis->p.height);
2343
2344         axis->displayed = 0;
2345 #else
2346         if (axis->pixmap[0])
2347                 g_object_unref (axis->pixmap[0]);
2348         if (axis->pixmap[1])
2349                 g_object_unref (axis->pixmap[1]);
2350
2351         axis->pixmap[0] = gdk_pixmap_new (gtk_widget_get_window(axis->drawing_area),
2352                                                         axis->p.width, axis->p.height, -1);
2353         axis->pixmap[1] = gdk_pixmap_new (gtk_widget_get_window(axis->drawing_area),
2354                                                         axis->p.width, axis->p.height, -1);
2355
2356         axis->displayed = 0;
2357 #endif
2358 }
2359
2360 static void axis_destroy (struct axis *axis)
2361 {
2362 #if GTK_CHECK_VERSION(2,22,0)
2363         if(axis->surface[0]){
2364                 cairo_surface_destroy (axis->surface[0]);
2365                 axis->surface[0] = NULL;
2366         }
2367         if(axis->surface[1]){
2368                 cairo_surface_destroy (axis->surface[1]);
2369                 axis->surface[1] = NULL;
2370         }
2371 #else
2372         g_object_unref (axis->pixmap[0]);
2373         g_object_unref (axis->pixmap[1]);
2374 #endif
2375         g_free ( (gpointer) (axis->label) );
2376 }
2377
2378 static void axis_display (struct axis *axis)
2379 {
2380         if (axis->flags & AXIS_ORIENTATION)
2381                 h_axis_pixmap_draw (axis);
2382         else
2383                 v_axis_pixmap_draw (axis);
2384         axis_pixmaps_switch (axis);
2385         axis_pixmap_display (axis);
2386 }
2387
2388 static void v_axis_pixmap_draw (struct axis *axis)
2389 {
2390         struct graph *g = axis->g;
2391         int i;
2392         double major_tick;
2393         int not_disp, rdigits, offset, imin, imax;
2394         double bottom, top, j, fl, corr;
2395         PangoLayout *layout;
2396         cairo_t *cr;
2397
2398         debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
2399         bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
2400                                         (double )g->geom.height * g->bounds.height;
2401         bottom += axis->min;
2402         top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
2403                                         (double )g->geom.height * g->bounds.height;
2404         top += axis->min;
2405         axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
2406
2407         j = axis->major - floor (axis->major);
2408         for (rdigits=0; rdigits<=6; rdigits++) {
2409                 j *= 10;
2410                 if (j<=0.000001)
2411                         break;
2412                 j = j - floor (j);
2413         }
2414
2415         not_disp = 1 ^ axis->displayed;
2416
2417 #if GTK_CHECK_VERSION(2,22,0)
2418         cr = cairo_create (axis->surface[not_disp]);
2419 #else
2420         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2421 #endif
2422         cairo_set_source_rgb (cr, 1, 1, 1);
2423         cairo_rectangle (cr, 0, 0, axis->p.width, axis->p.height);
2424         cairo_fill (cr);
2425         cairo_destroy (cr);
2426         cr = NULL;
2427
2428         /* axis */
2429 #if GTK_CHECK_VERSION(2,22,0)
2430         cr = cairo_create (axis->surface[not_disp]);
2431 #else
2432         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2433 #endif
2434         cairo_set_line_width (cr, 1.0);
2435         cairo_move_to(cr, axis->p.width - 1.5, (axis->p.height-axis->s.height)/2.0);
2436         cairo_line_to(cr, axis->s.width - 1.5, axis->p.height);
2437         cairo_stroke(cr);
2438         cairo_destroy(cr);
2439         cr = NULL;
2440
2441         offset = g->wp.y + (-g->geom.y);
2442         fl = floor (axis->min / axis->major) * axis->major;
2443         corr = rint ((axis->min - fl) * g->zoom.y);
2444
2445         /* major ticks */
2446         major_tick = axis->major * g->zoom.y;
2447         imin = (int) ((g->geom.height - offset + corr - g->wp.height) / major_tick + 1);
2448         imax = (int) ((g->geom.height - offset + corr) / major_tick);
2449         for (i=imin; i <= imax; i++) {
2450                 gint w, h;
2451                 char desc[32];
2452                 int y = (int) (g->geom.height-1 - (int )rint (i * major_tick) -
2453                                                 offset + corr + axis->s.y);
2454
2455                 debug(DBS_AXES_DRAWING) printf("%f @ %d\n",
2456                                                i*axis->major + fl, y);
2457                 if (y < 0 || y > axis->p.height)
2458                         continue;
2459
2460 #if GTK_CHECK_VERSION(2,22,0)
2461                 cr = cairo_create (axis->surface[not_disp]);
2462 #else
2463                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2464 #endif
2465                 cairo_set_line_width (cr, 1.0);
2466                 cairo_move_to(cr, axis->p.width - 15, y+0.5);
2467                 cairo_line_to(cr, axis->s.width - 1, y+0.5);
2468                 cairo_stroke(cr);
2469                 cairo_destroy(cr);
2470                 cr = NULL;
2471
2472                 g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2473         layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2474         pango_layout_get_pixel_size(layout, &w, &h);
2475 #if GTK_CHECK_VERSION(2,22,0)
2476                 cr = cairo_create (axis->surface[not_disp]);
2477 #else
2478                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2479 #endif
2480                 cairo_move_to (cr, axis->s.width-14-4-w, y - h/2);
2481                 pango_cairo_show_layout (cr, layout);
2482                 cairo_destroy (cr);
2483                 cr = NULL;
2484         g_object_unref(G_OBJECT(layout));
2485         }
2486         /* minor ticks */
2487         if (axis->minor) {
2488                 double minor_tick = axis->minor * g->zoom.y;
2489                 imin = (int) ((g->geom.height - offset + corr - g->wp.height)/minor_tick + 1);
2490                 imax = (int) ((g->geom.height - offset + corr) / minor_tick);
2491                 for (i=imin; i <= imax; i++) {
2492                         int y = (int) (g->geom.height-1 - (int )rint (i*minor_tick) -
2493                                                         offset + corr + axis->s.y);
2494
2495                         debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
2496                         if (y > 0 && y < axis->p.height)
2497 #if GTK_CHECK_VERSION(2,22,0)
2498                                 cr = cairo_create (axis->surface[not_disp]);
2499 #else
2500                                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2501 #endif
2502                                 cairo_set_line_width (cr, 1.0);
2503                                 cairo_move_to(cr, axis->s.width - 8, y+0.5);
2504                                 cairo_line_to(cr, axis->s.width - 1, y+0.5);
2505                                 cairo_stroke(cr);
2506                                 cairo_destroy(cr);
2507                                 cr = NULL;
2508                 }
2509         }
2510         for (i=0; axis->label[i]; i++) {
2511                 gint w, h;
2512         layout = gtk_widget_create_pango_layout(g->drawing_area,
2513                                                 axis->label[i]);
2514         pango_layout_get_pixel_size(layout, &w, &h);
2515 #if GTK_CHECK_VERSION(2,22,0)
2516                 cr = cairo_create (axis->surface[not_disp]);
2517 #else
2518         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2519 #endif
2520         cairo_move_to (cr, (axis->p.width - w)/2, TITLEBAR_HEIGHT-10 - i*(h+3) - h);
2521         pango_cairo_show_layout (cr, layout);
2522         cairo_destroy (cr);
2523         cr = NULL;
2524         g_object_unref(G_OBJECT(layout));
2525     }
2526 }
2527
2528 static void h_axis_pixmap_draw (struct axis *axis)
2529 {
2530         struct graph *g = axis->g;
2531         int i;
2532         double major_tick, minor_tick;
2533         int not_disp, rdigits, offset, imin, imax;
2534         double left, right, j, fl, corr;
2535         PangoLayout *layout;
2536         cairo_t *cr;
2537
2538         debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
2539         left = (g->wp.x-g->geom.x) /
2540                                         (double )g->geom.width * g->bounds.width;
2541         left += axis->min;
2542         right = (g->wp.x-g->geom.x+g->wp.width) /
2543                                         (double )g->geom.width * g->bounds.width;
2544         right += axis->min;
2545         axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
2546
2547         j = axis->major - floor (axis->major);
2548         for (rdigits=0; rdigits<=6; rdigits++) {
2549                 j *= 10;
2550                 if (j<=0.000001)
2551                         break;
2552                 j = j - floor (j);
2553         }
2554
2555         not_disp = 1 ^ axis->displayed;
2556
2557 #if GTK_CHECK_VERSION(2,22,0)
2558         cr = cairo_create (axis->surface[not_disp]);
2559 #else
2560         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2561 #endif
2562         cairo_set_source_rgb (cr, 1, 1, 1);
2563         cairo_rectangle (cr, 0, 0, axis->p.width, axis->p.height);
2564         cairo_fill (cr);
2565         cairo_destroy (cr);
2566         cr = NULL;
2567
2568         /* axis */
2569 #if GTK_CHECK_VERSION(2,22,0)
2570         cr = cairo_create (axis->surface[not_disp]);
2571 #else
2572         cr = gdk_cairo_create (axis->pixmap[not_disp]);
2573 #endif
2574         cairo_set_line_width (cr, 1.0);
2575         cairo_move_to(cr, 0, 0.5);
2576         cairo_line_to(cr, axis->s.width + (axis->p.width-axis->s.width)/2.0, 0.5);
2577         cairo_stroke(cr);
2578         cairo_destroy(cr);
2579         cr = NULL;
2580
2581         offset = g->wp.x - g->geom.x;
2582
2583         fl = floor (axis->min / axis->major) * axis->major;
2584         corr = rint ((axis->min - fl) * g->zoom.x);
2585
2586         /* major ticks */
2587         major_tick = axis->major*g->zoom.x;
2588         imin = (int) ((offset + corr) / major_tick + 1);
2589         imax = (int) ((offset + corr + axis->s.width) / major_tick);
2590         for (i=imin; i <= imax; i++) {
2591                 char desc[32];
2592                 int w, h;
2593                 int x = (int ) (rint (i * major_tick) - offset - corr);
2594
2595                 /* printf ("%f @ %d\n", i*axis->major + fl, x); */
2596                 if (x < 0 || x > axis->s.width)
2597                         continue;
2598 #if GTK_CHECK_VERSION(2,22,0)
2599                 cr = cairo_create (axis->surface[not_disp]);
2600 #else
2601                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2602 #endif
2603                 cairo_set_line_width (cr, 1.0);
2604                 cairo_move_to(cr, x+0.5, 0);
2605                 cairo_line_to(cr, x+0.5, 15);
2606                 cairo_stroke(cr);
2607                 cairo_destroy(cr);
2608                 cr = NULL;
2609
2610                 g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
2611         layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
2612         pango_layout_get_pixel_size(layout, &w, &h);
2613 #if GTK_CHECK_VERSION(2,22,0)
2614                 cr = cairo_create (axis->surface[not_disp]);
2615 #else
2616                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2617 #endif
2618                 cairo_move_to (cr,  x - w/2, 15+4);
2619                 pango_cairo_show_layout (cr, layout);
2620                 cairo_destroy (cr);
2621                 cr = NULL;
2622
2623         g_object_unref(G_OBJECT(layout));
2624         }
2625         if (axis->minor > 0) {
2626                 /* minor ticks */
2627                 minor_tick = axis->minor*g->zoom.x;
2628                 imin = (int) ((offset + corr) / minor_tick + 1);
2629                 imax = (int) ((offset + corr + g->wp.width) / minor_tick);
2630                 for (i=imin; i <= imax; i++) {
2631                         int x = (int) (rint (i * minor_tick) - offset - corr);
2632                         if (x > 0 && x < axis->s.width){
2633 #if GTK_CHECK_VERSION(2,22,0)
2634                                 cr = cairo_create (axis->surface[not_disp]);
2635 #else
2636                                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2637 #endif
2638                                 cairo_set_line_width (cr, 1.0);
2639                                 cairo_move_to(cr, x+0.5, 0);
2640                                 cairo_line_to(cr, x+0.5, 8);
2641                                 cairo_stroke(cr);
2642                                 cairo_destroy(cr);
2643                                 cr = NULL;
2644                         }
2645                 }
2646         }
2647         for (i=0; axis->label[i]; i++) {
2648                 gint w, h;
2649         layout = gtk_widget_create_pango_layout(g->drawing_area,
2650                                                 axis->label[i]);
2651         pango_layout_get_pixel_size(layout, &w, &h);
2652 #if GTK_CHECK_VERSION(2,22,0)
2653                 cr = cairo_create (axis->surface[not_disp]);
2654 #else
2655                 cr = gdk_cairo_create (axis->pixmap[not_disp]);
2656 #endif
2657                 cairo_move_to (cr,  axis->s.width - w - 50, 15+h+15 + i*(h+3));
2658                 pango_cairo_show_layout (cr, layout);
2659                 cairo_destroy (cr);
2660                 cr = NULL;
2661         g_object_unref(G_OBJECT(layout));
2662         }
2663 }
2664
2665 static void axis_pixmaps_switch (struct axis *axis)
2666 {
2667         axis->displayed = 1 ^ axis->displayed;
2668 }
2669
2670 static void axis_pixmap_display (struct axis *axis)
2671 {
2672         cairo_t *cr;
2673
2674         cr = gdk_cairo_create (gtk_widget_get_window(axis->drawing_area));
2675 #if GTK_CHECK_VERSION(2,22,0)
2676         cairo_set_source_surface (cr, axis->surface[axis->displayed], axis->p.x, axis->p.y);
2677 #else
2678         gdk_cairo_set_source_pixmap (cr, axis->pixmap[axis->displayed], axis->p.x, axis->p.y);
2679 #endif
2680         cairo_rectangle (cr, axis->p.x, axis->p.y, axis->p.width, axis->p.height);
2681         cairo_fill (cr);
2682         cairo_destroy (cr);
2683
2684 }
2685
2686 static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
2687 {
2688         int i, j, ii, jj, ms;
2689         double zoom, x, steps[3]={ 0.1, 0.5 };
2690         int dim, check_needed, diminished;
2691         double majthresh[2]={2.0, 3.0};
2692
2693         debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
2694         debug(DBS_AXES_TICKS)
2695                 printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
2696
2697         zoom = axis_zoom_get (axis, dir);
2698         x = xmax-x0;
2699         for (i=-9; i<=12; i++) {
2700                 if (x / pow (10, i) < 1)
2701                         break;
2702         }
2703         --i;
2704         ms = (int )(x / pow (10, i));
2705
2706         if (ms > 5) {
2707                 j = 0;
2708                 ++i;
2709         } else if (ms > 2)
2710                 j = 1;
2711         else
2712                 j = 0;
2713
2714         axis->major = steps[j] * pow (10, i);
2715
2716         debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
2717                         " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
2718
2719         /* let's compute minor ticks */
2720         jj = j;
2721         ii = i;
2722         axis_ticks_down (&ii, &jj);
2723         axis->minor = steps[jj] * pow (10, ii);
2724         /* we don't want minors if they would be less than 10 pixels apart */
2725         if (axis->minor*zoom < 10) {
2726                 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2727                                         "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2728                 axis->minor = 0;
2729         }
2730
2731         check_needed = TRUE;
2732         diminished = FALSE;
2733         while (check_needed) {
2734                 check_needed = FALSE;
2735                 dim = get_label_dim (axis, dir, xmax);
2736                 debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
2737                                 " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
2738                                 axis->major, axis->minor, axis->major*zoom/dim,
2739                                 axis->minor*zoom/dim);
2740
2741                 /* corrections: if majors are less than majthresh[dir] times label
2742                 * dimension apart, we need to use bigger ones */
2743                 if (axis->major*zoom / dim < majthresh[dir]) {
2744                         axis_ticks_up (&ii, &jj);
2745                         axis->minor = axis->major;
2746                         axis_ticks_up (&i, &j);
2747                         axis->major = steps[j] * pow (10, i);
2748                         check_needed = TRUE;
2749                         debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
2750                                                                                 axis->major);
2751                 }
2752                 /* if minor ticks are bigger than majthresh[dir] times label dimension,
2753                  * we could  promote them to majors as well */
2754                 if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
2755                         axis_ticks_down (&i, &j);
2756                         axis->major = axis->minor;
2757                         axis_ticks_down (&ii, &jj);
2758                         axis->minor = steps[jj] * pow (10, ii);
2759                         check_needed = TRUE;
2760                         diminished = TRUE;
2761
2762                         debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
2763                                                                                 axis->minor);
2764
2765                         if (axis->minor*zoom < 10) {
2766                                 debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
2767                                         "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
2768                                 axis->minor = 0;
2769                         }
2770                 }
2771         }
2772
2773         debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
2774                                                         "axis->minor == %.1f\n", axis->major, axis->minor);
2775 }
2776
2777 static void axis_ticks_up (int *i, int *j)
2778 {
2779         (*j)++;
2780         if (*j>1) {
2781                 (*i)++;
2782                 *j=0;
2783         }
2784 }
2785
2786 static void axis_ticks_down (int *i, int *j)
2787 {
2788         (*j)--;
2789         if (*j<0) {
2790                 (*i)--;
2791                 *j=1;
2792         }
2793 }
2794
2795 static int get_label_dim (struct axis *axis, int dir, double label)
2796 {
2797         double y;
2798         char str[32];
2799         int rdigits, dim;
2800         PangoLayout *layout;
2801
2802          /* First, let's compute how many digits to the right of radix
2803          * we need to print */
2804         y = axis->major - floor (axis->major);
2805         for (rdigits=0; rdigits<=6; rdigits++) {
2806                 y *= 10;
2807                 if (y<=0.000001)
2808                         break;
2809                 y = y - floor (y);
2810         }
2811         g_snprintf (str, sizeof(str), "%.*f", rdigits, label);
2812         switch (dir) {
2813         case AXIS_HORIZONTAL:
2814                 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2815                                                         str);
2816                 pango_layout_get_pixel_size(layout, &dim, NULL);
2817                 g_object_unref(G_OBJECT(layout));
2818                 break;
2819         case AXIS_VERTICAL:
2820                 layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
2821                                                         str);
2822                 pango_layout_get_pixel_size(layout, NULL, &dim);
2823                 g_object_unref(G_OBJECT(layout));
2824                 break;
2825         default:
2826                 puts ("initialize axis: an axis must be either horizontal or vertical");
2827                 return -1;
2828         }
2829         return dim;
2830 }
2831
2832 static double axis_zoom_get (struct axis *axis, int dir)
2833 {
2834         switch (dir) {
2835         case AXIS_HORIZONTAL:
2836                 return axis->g->zoom.x;
2837         case AXIS_VERTICAL:
2838                 return axis->g->zoom.y;
2839         default:
2840                 return -1;
2841         }
2842 }
2843
2844 static void graph_select_segment (struct graph *g, int x, int y)
2845 {
2846         struct element_list *list;
2847         struct element *e;
2848         guint num = 0;
2849
2850         debug(DBS_FENTRY) puts ("graph_select_segment()");
2851
2852         x -= g->geom.x;
2853         y = g->geom.height-1 - (y - g->geom.y);
2854
2855         set_busy_cursor (gtk_widget_get_window(g->drawing_area));
2856
2857         for (list=g->elists; list; list=list->next)
2858                 for (e=list->elements; e->type != ELMT_NONE; e++) {
2859                         switch (e->type) {
2860                         case ELMT_RECT:
2861                                 break;
2862                         case ELMT_LINE:
2863                                 if (line_detect_collision (e, x, y)) {
2864                                         num = e->parent->num;
2865                                 }
2866                                 break;
2867                         case ELMT_ELLIPSE:
2868                                 if (ellipse_detect_collision (e, x, y)) {
2869                                         num = e->parent->num;
2870                                 }
2871                                 break;
2872                         default:
2873                                 break;
2874                         }
2875                 }
2876
2877
2878         if (num) {
2879                 cf_goto_frame(&cfile, num);
2880         }
2881         unset_busy_cursor (gtk_widget_get_window(g->drawing_area));
2882 }
2883
2884 static int line_detect_collision (struct element *e, int x, int y)
2885 {
2886         int xx1, yy1, xx2, yy2;
2887
2888         if (e->p.line.dim.x1 < e->p.line.dim.x2) {
2889                 xx1 = (int )rint (e->p.line.dim.x1);
2890                 xx2 = (int )rint (e->p.line.dim.x2);
2891         } else {
2892                 xx1 = (int )rint (e->p.line.dim.x2);
2893                 xx2 = (int )rint (e->p.line.dim.x1);
2894         }
2895         if (e->p.line.dim.y1 < e->p.line.dim.y2) {
2896                 yy1 = (int )rint (e->p.line.dim.y1);
2897                 yy2 = (int )rint (e->p.line.dim.y2);
2898         } else {
2899                 yy1 = (int )rint (e->p.line.dim.y2);
2900                 yy2 = (int )rint (e->p.line.dim.y1);
2901         }
2902         /*
2903         printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
2904          */
2905         if ((xx1==x && xx2==x && yy1<=y && y<=yy2)||(yy1==y && yy2==y && xx1<=x && x<=xx2))
2906                 return TRUE;
2907         else
2908                 return FALSE;
2909 }
2910
2911 static int ellipse_detect_collision (struct element *e, int x, int y)
2912 {
2913         int xx1, yy1, xx2, yy2;
2914
2915         xx1 = (int )rint (e->p.ellipse.dim.x);
2916         xx2 = (int )rint (e->p.ellipse.dim.x + e->p.ellipse.dim.width);
2917         yy1 = (int )rint (e->p.ellipse.dim.y - e->p.ellipse.dim.height);
2918         yy2 = (int )rint (e->p.ellipse.dim.y);
2919         /*
2920         printf ("ellipse: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
2921          */
2922         if (xx1<=x && x<=xx2 && yy1<=y && y<=yy2)
2923                 return TRUE;
2924         else
2925                 return FALSE;
2926 }
2927
2928 static void cross_xor (struct graph *g, int x, int y)
2929 {
2930 #if GTK_CHECK_VERSION(3,0,0)
2931         GdkColor color_gray15 = {0x0, 0x2626, 0x2626, 0x2626};
2932         cairo_t *cr;
2933
2934         /* XXX Fix me: lines do not disapere */
2935         if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2936                                 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2937                 /* Draw horisontal line */
2938                 cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
2939                 cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
2940                 gdk_cairo_set_source_color (cr, &color_gray15);
2941                 cairo_set_line_width (cr, 1.0);
2942                 cairo_move_to(cr,  g->wp.x, y);
2943                 cairo_line_to(cr,  g->wp.x + g->wp.width, y);
2944                 cairo_stroke(cr);
2945                 cairo_destroy(cr);
2946                 /* draw vertical line */
2947                 cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
2948                 cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
2949                 gdk_cairo_set_source_color (cr, &color_gray15);
2950                 cairo_set_line_width (cr, 1.0);
2951                 cairo_move_to(cr,  x, g->wp.y);
2952                 cairo_line_to(cr,  x, g->wp.y + g->wp.height);
2953                 cairo_stroke(cr);
2954                 cairo_destroy(cr);
2955         }
2956
2957 #else
2958
2959         if (x > g->wp.x && x < g->wp.x+g->wp.width &&
2960                                 y >= g->wp.y && y < g->wp.y+g->wp.height) {
2961                 gdk_draw_line (gtk_widget_get_window(g->drawing_area), xor_gc, g->wp.x,
2962                                                 y, g->wp.x + g->wp.width, y);
2963                 gdk_draw_line (gtk_widget_get_window(g->drawing_area), xor_gc, x,
2964                                                 g->wp.y, x, g->wp.y + g->wp.height);
2965         }
2966 #endif
2967 }
2968
2969 static void cross_draw (struct graph *g, int x, int y)
2970 {
2971         cross_xor (g, x, y);
2972         g->cross.x = x;
2973         g->cross.y = y;
2974         g->cross.erase_needed = 1;
2975 }
2976
2977 static void cross_erase (struct graph *g)
2978 {
2979         cross_xor (g, g->cross.x, g->cross.y);
2980         g->cross.erase_needed = 0;
2981 }
2982
2983 static void magnify_create (struct graph *g, int x, int y)
2984 {
2985         struct graph *mg;
2986         struct element_list *list, *new_list;
2987         struct ipoint pos, offsetpos;
2988         GdkEvent *e=NULL;
2989
2990         mg = g->magnify.g = (struct graph * )g_malloc (sizeof (struct graph));
2991         memcpy ((void * )mg, (void * )g, sizeof (struct graph));
2992
2993         mg->toplevel = dlg_window_new("tcp graph magnify");
2994         mg->drawing_area = mg->toplevel;
2995         gtk_window_set_default_size(GTK_WINDOW(mg->toplevel), g->magnify.width, g->magnify.height);
2996         gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
2997                         /*              | GDK_ENTER_NOTIFY_MASK */
2998                         /*              | GDK_ALL_EVENTS_MASK   */
2999                                         );
3000
3001         mg->wp.x = 0;
3002         mg->wp.y = 0;
3003         mg->wp.width = g->magnify.width;
3004         mg->wp.height = g->magnify.height;
3005         mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
3006         mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
3007         mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
3008         mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
3009
3010         /* in order to keep original element lists intact we need our own */
3011         graph_element_lists_initialize (mg);
3012         list = g->elists->next;
3013         new_list = mg->elists;
3014         for ( ; list; list=list->next) {
3015                 new_list->next =
3016                                 (struct element_list * )g_malloc (sizeof (struct element_list));
3017                 new_list = new_list->next;
3018                 new_list->next = NULL;
3019                 new_list->elements = NULL;
3020         }
3021         graph_element_lists_make (mg);
3022
3023         gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &pos.x, &pos.y);
3024         g->magnify.x = pos.x + x - g->magnify.width/2;
3025         g->magnify.y = pos.y + y - g->magnify.height/2;
3026         offsetpos.x = g->magnify.x + g->magnify.offset.x;
3027         offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
3028         offsetpos.y = g->magnify.y + g->magnify.offset.y;
3029         offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
3030         gtk_window_set_position (GTK_WINDOW(mg->drawing_area), GTK_WIN_POS_NONE);
3031         magnify_get_geom (g, x, y);
3032
3033         gtk_widget_show (mg->drawing_area);
3034
3035         /* we need to wait for the first expose event before we start drawing */
3036         while (!gdk_events_pending ());
3037         do {
3038                 e = gdk_event_get ();
3039                 if (e) {
3040                         if (e->any.type == GDK_EXPOSE) {
3041                                 gdk_event_free (e);
3042                                 break;
3043                         }
3044                         gdk_event_free (e);
3045                 }
3046         } while (e);
3047
3048 #if GTK_CHECK_VERSION(2,22,0)
3049         mg->surface[0] = mg->surface[1] = NULL;
3050 #else
3051         mg->pixmap[0] = mg->pixmap[1] = NULL;
3052 #endif /* GTK_CHECK_VERSION(2,22,0) */
3053         graph_pixmaps_create (mg);
3054         magnify_draw (g);
3055         g->magnify.active = 1;
3056 }
3057
3058 static void magnify_move (struct graph *g, int x, int y)
3059 {
3060         struct ipoint pos, offsetpos;
3061
3062         gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &pos.x, &pos.y);
3063         g->magnify.x = pos.x + x - g->magnify.width/2;
3064         g->magnify.y = pos.y + y - g->magnify.height/2;
3065         offsetpos.x = g->magnify.x + g->magnify.offset.x;
3066         offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
3067         offsetpos.y = g->magnify.y + g->magnify.offset.y;
3068         offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
3069         magnify_get_geom (g, x, y);
3070         magnify_draw (g);
3071 }
3072
3073 static void magnify_destroy (struct graph *g)
3074 {
3075         struct element_list *list;
3076         struct graph *mg = g->magnify.g;
3077
3078         window_destroy (GTK_WIDGET (mg->drawing_area));
3079
3080 #if GTK_CHECK_VERSION(2,22,0)
3081         if(mg->surface[0]){
3082                  cairo_surface_destroy (mg->surface[0]);
3083         }
3084         if(mg->surface[1]){
3085                  cairo_surface_destroy (mg->surface[1]);
3086         }
3087 #else
3088         g_object_unref (mg->pixmap[0]);
3089         g_object_unref (mg->pixmap[1]);
3090 #endif /* GTK_CHECK_VERSION(2,22,0) */
3091         for (list=mg->elists; list; list=list->next)
3092                 g_free (list->elements);
3093
3094         if (mg->elists) {
3095     while (mg->elists->next) {
3096       list = mg->elists->next->next;
3097       g_free (mg->elists->next);
3098       mg->elists->next = list;
3099     }
3100         }
3101         g_free (g->magnify.g);
3102         g->magnify.active = 0;
3103 }
3104
3105 static void magnify_get_geom (struct graph *g, int x, int y)
3106 {
3107         int posx, posy;
3108
3109         gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &posx, &posy);
3110
3111         g->magnify.g->geom.x = g->geom.x;
3112         g->magnify.g->geom.y = g->geom.y;
3113
3114         g->magnify.g->geom.x -=
3115                                 (int )rint ((g->magnify.g->geom.width - g->geom.width) *
3116                                 ((x-g->geom.x)/(double )g->geom.width));
3117         g->magnify.g->geom.y -=
3118                                 (int )rint ((g->magnify.g->geom.height - g->geom.height) *
3119                                 ((y-g->geom.y)/(double )g->geom.height));
3120
3121         /* we have coords of origin of graph relative to origin of g->toplevel.
3122          * now we need them to relate to origin of magnify window */
3123         g->magnify.g->geom.x -= (g->magnify.x - posx);
3124         g->magnify.g->geom.y -= (g->magnify.y - posy);
3125 }
3126
3127 static void magnify_draw (struct graph *g)
3128 {
3129         cairo_t *cr;
3130         int not_disp = 1 ^ g->magnify.g->displayed;
3131
3132         graph_pixmap_draw (g->magnify.g);
3133         /* graph pixmap is almost ready, just add border */
3134 #if GTK_CHECK_VERSION(2,22,0)
3135         cr = cairo_create (g->magnify.g->surface[not_disp]);
3136 #else
3137         cr = gdk_cairo_create (g->magnify.g->pixmap[not_disp]);
3138 #endif /* GTK_CHECK_VERSION(2,22,0) */
3139         cairo_set_line_width (cr, 1.0);
3140         cairo_move_to(cr, 0, 0);
3141         cairo_line_to(cr, g->magnify.width - 1, 0);
3142         cairo_stroke(cr);
3143
3144         cairo_move_to(cr, g->magnify.width - 1, 0);
3145         cairo_line_to(cr, g->magnify.width - 1, g->magnify.height);
3146         cairo_stroke(cr);
3147
3148         cairo_move_to(cr, 0, 0);
3149         cairo_line_to(cr, 0,g->magnify.height - 1);
3150         cairo_stroke(cr);
3151
3152         cairo_move_to(cr, 0, g->magnify.height - 1);
3153         cairo_line_to(cr, g->magnify.width - 1, g->magnify.height - 1);
3154         cairo_stroke(cr);
3155         cairo_destroy(cr);
3156
3157         graph_pixmaps_switch (g->magnify.g);
3158         graph_pixmap_display (g->magnify.g);
3159
3160 }
3161
3162 static gboolean configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer user_data _U_)
3163 {
3164         struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3165         struct {
3166                 double x, y;
3167         } zoom;
3168         int cur_g_width, cur_g_height;
3169         int cur_wp_width, cur_wp_height;
3170
3171         debug(DBS_FENTRY) puts ("configure_event()");
3172
3173         cur_wp_width = g->wp.width;
3174         cur_wp_height = g->wp.height;
3175         g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
3176         g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
3177         g->x_axis->s.width = g->wp.width;
3178         g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
3179         g->y_axis->p.height = g->wp.height + g->wp.y;
3180         g->y_axis->s.height = g->wp.height;
3181         g->x_axis->p.y = g->y_axis->p.height;
3182         zoom.x = (double )g->wp.width / cur_wp_width;
3183         zoom.y = (double )g->wp.height / cur_wp_height;
3184         cur_g_width = g->geom.width;
3185         cur_g_height = g->geom.height;
3186         g->geom.width = (int )rint (g->geom.width * zoom.x);
3187         g->geom.height = (int )rint (g->geom.height * zoom.y);
3188         g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
3189         g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
3190         /* g->zoom.initial.x = g->zoom.x; */
3191         /* g->zoom.initial.y = g->zoom.y; */
3192
3193         g->geom.x = (int) (g->wp.x - (double )g->geom.width/cur_g_width *
3194                                                         (g->wp.x - g->geom.x));
3195         g->geom.y = (int) (g->wp.y - (double )g->geom.height/cur_g_height *
3196                                                         (g->wp.y - g->geom.y));
3197 #if 0
3198         printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
3199                                 "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
3200                                 g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
3201                                 g->zoom.x, g->zoom.y);
3202 #endif
3203
3204         update_zoom_spins (g);
3205         graph_element_lists_make (g);
3206         graph_pixmaps_create (g);
3207         graph_title_pixmap_create (g);
3208         axis_pixmaps_create (g->y_axis);
3209         axis_pixmaps_create (g->x_axis);
3210         /* we don't do actual drawing here; we leave it to expose handler */
3211         graph_pixmap_draw (g);
3212         graph_pixmaps_switch (g);
3213         graph_title_pixmap_draw (g);
3214         h_axis_pixmap_draw (g->x_axis);
3215         axis_pixmaps_switch (g->x_axis);
3216         v_axis_pixmap_draw (g->y_axis);
3217         axis_pixmaps_switch (g->y_axis);
3218         return TRUE;
3219 }
3220
3221 static gboolean expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data _U_)
3222 {
3223     struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3224     cairo_t *cr;
3225
3226         debug(DBS_FENTRY) puts ("expose_event()");
3227
3228         if (event->count)
3229                 return TRUE;
3230
3231         /* lower left corner */
3232         cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
3233         cairo_set_source_rgb (cr, 1, 1, 1);
3234         cairo_rectangle (cr, 0, g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
3235         cairo_fill (cr);
3236         cairo_destroy(cr);
3237         cr = NULL;
3238
3239         /* right margin */
3240         cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
3241         cairo_set_source_rgb (cr, 1, 1, 1);
3242         cairo_rectangle (cr, g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
3243         cairo_fill (cr);
3244         cairo_destroy(cr);
3245         cr = NULL;
3246
3247         graph_pixmap_display (g);
3248         graph_title_pixmap_display (g);
3249         axis_pixmap_display (g->x_axis);
3250         axis_pixmap_display (g->y_axis);
3251
3252         return TRUE;
3253 }
3254
3255 static void do_zoom_mouse (struct graph *g, GdkEventButton *event)
3256 {
3257         int cur_width = g->geom.width, cur_height = g->geom.height;
3258         struct { double x, y; } factor;
3259
3260         if (g->zoom.flags & ZOOM_OUT) {
3261                 if (g->zoom.flags & ZOOM_HLOCK)
3262                         factor.x = 1.0;
3263                 else
3264                         factor.x = 1 / g->zoom.step_x;
3265                 if (g->zoom.flags & ZOOM_VLOCK)
3266                         factor.y = 1.0;
3267                 else
3268                         factor.y = 1 / g->zoom.step_y;
3269         } else {
3270                 if (g->zoom.flags & ZOOM_HLOCK)
3271                         factor.x = 1.0;
3272                 else
3273                         factor.x = g->zoom.step_x;
3274                 if (g->zoom.flags & ZOOM_VLOCK)
3275                         factor.y = 1.0;
3276                 else
3277                         factor.y = g->zoom.step_y;
3278         }
3279
3280         g->geom.width = (int )rint (g->geom.width * factor.x);
3281         g->geom.height = (int )rint (g->geom.height * factor.y);
3282         if (g->geom.width < g->wp.width)
3283                 g->geom.width = g->wp.width;
3284         if (g->geom.height < g->wp.height)
3285                 g->geom.height = g->wp.height;
3286         g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3287         g->zoom.y = (g->geom.height- 1) / g->bounds.height;
3288
3289         g->geom.x -= (int )rint ((g->geom.width - cur_width) *
3290                         ((event->x-g->geom.x)/(double )cur_width));
3291         g->geom.y -= (int )rint ((g->geom.height - cur_height) *
3292                         ((event->y-g->geom.y)/(double )cur_height));
3293
3294         if (g->geom.x > g->wp.x)
3295                 g->geom.x = g->wp.x;
3296         if (g->geom.y > g->wp.y)
3297                 g->geom.y = g->wp.y;
3298         if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3299                 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3300         if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3301                 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3302 #if 0
3303         printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
3304                         "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
3305                         g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
3306                         g->wp.height, g->zoom.x, g->zoom.y);
3307 #endif
3308         graph_element_lists_make (g);
3309         g->cross.erase_needed = 0;
3310         graph_display (g);
3311         axis_display (g->y_axis);
3312         axis_display (g->x_axis);
3313         update_zoom_spins (g);
3314         if (g->cross.draw)
3315                 cross_draw (g, (int) event->x, (int) event->y);
3316 }
3317
3318 static void do_zoom_keyboard (struct graph *g)
3319 {
3320         int cur_width = g->geom.width, cur_height = g->geom.height;
3321         struct { double x, y; } factor;
3322         int pointer_x, pointer_y;
3323
3324         gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
3325
3326         if (g->zoom.flags & ZOOM_OUT) {
3327                 if (g->zoom.flags & ZOOM_HLOCK)
3328                         factor.x = 1.0;
3329                 else
3330                         factor.x = 1 / g->zoom.step_x;
3331                 if (g->zoom.flags & ZOOM_VLOCK)
3332                         factor.y = 1.0;
3333                 else
3334                         factor.y = 1 / g->zoom.step_y;
3335         } else {
3336                 if (g->zoom.flags & ZOOM_HLOCK)
3337                         factor.x = 1.0;
3338                 else
3339                         factor.x = g->zoom.step_x;
3340                 if (g->zoom.flags & ZOOM_VLOCK)
3341                         factor.y = 1.0;
3342                 else
3343                         factor.y = g->zoom.step_y;
3344         }
3345
3346         g->geom.width = (int )rint (g->geom.width * factor.x);
3347         g->geom.height = (int )rint (g->geom.height * factor.y);
3348         if (g->geom.width < g->wp.width)
3349                 g->geom.width = g->wp.width;
3350         if (g->geom.height < g->wp.height)
3351                 g->geom.height = g->wp.height;
3352         g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3353         g->zoom.y = (g->geom.height- 1) / g->bounds.height;
3354
3355         g->geom.x -= (int )rint ((g->geom.width - cur_width) *
3356                         ((pointer_x - g->geom.x)/(double )cur_width));
3357         g->geom.y -= (int )rint ((g->geom.height - cur_height) *
3358                         ((pointer_y - g->geom.y)/(double )cur_height));
3359
3360         if (g->geom.x > g->wp.x)
3361                 g->geom.x = g->wp.x;
3362         if (g->geom.y > g->wp.y)
3363                 g->geom.y = g->wp.y;
3364         if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3365                 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3366         if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3367                 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3368 #if 0
3369         printf ("key press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
3370                         "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
3371                         g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
3372                         g->wp.height, g->zoom.x, g->zoom.y);
3373 #endif
3374
3375         graph_element_lists_make (g);
3376         g->cross.erase_needed = 0;
3377         graph_display (g);
3378         axis_display (g->y_axis);
3379         axis_display (g->x_axis);
3380         update_zoom_spins (g);
3381         if (g->cross.draw)
3382                 cross_draw (g, pointer_x, pointer_y);
3383 }
3384
3385 static void do_zoom_in_keyboard (struct graph *g)
3386 {
3387         gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3388         do_zoom_keyboard (g);
3389 }
3390
3391 static void do_zoom_out_keyboard (struct graph *g)
3392 {
3393         gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
3394         do_zoom_keyboard (g);
3395         gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3396 }
3397
3398 static void do_select_segment (struct graph *g)
3399 {
3400         int pointer_x, pointer_y;
3401
3402         gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
3403         graph_select_segment (g, pointer_x, pointer_y);
3404 }
3405
3406 static void do_wscale_graph (struct graph *g)
3407 {
3408         gtk_toggle_button_set_active (g->gt.graph_wscale, TRUE);
3409 }
3410
3411 static void do_rtt_graph (struct graph *g)
3412 {
3413         gtk_toggle_button_set_active (g->gt.graph_rtt, TRUE);
3414 }
3415
3416 static void do_throughput_graph (struct graph *g)
3417 {
3418         gtk_toggle_button_set_active (g->gt.graph_tput, TRUE);
3419 }
3420
3421 static void do_ts_graph_stevens (struct graph *g)
3422 {
3423         gtk_toggle_button_set_active (g->gt.graph_tseqstevens, TRUE);
3424 }
3425
3426 static void do_ts_graph_tcptrace (struct graph *g)
3427 {
3428         gtk_toggle_button_set_active (g->gt.graph_tseqttrace, TRUE);
3429 }
3430
3431 static void do_magnify_create (struct graph *g)
3432 {
3433         int pointer_x, pointer_y;
3434
3435         gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
3436
3437         magnify_create (g, (int )rint (pointer_x), (int )rint (pointer_y));
3438 }
3439
3440 static void do_key_motion (struct graph *g)
3441 {
3442         if (g->geom.x > g->wp.x)
3443                 g->geom.x = g->wp.x;
3444         if (g->geom.y > g->wp.y)
3445                 g->geom.y = g->wp.y;
3446         if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3447                 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3448         if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3449                 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3450         g->cross.erase_needed = 0;
3451         graph_display (g);
3452         axis_display (g->y_axis);
3453         axis_display (g->x_axis);
3454         if (g->cross.draw) {
3455                 int pointer_x, pointer_y;
3456                 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
3457                 cross_draw (g, pointer_x, pointer_y);
3458         }
3459 }
3460
3461 static void do_key_motion_up (struct graph *g, int step)
3462 {
3463         g->geom.y += step;
3464         do_key_motion (g);
3465 }
3466
3467 static void do_key_motion_down (struct graph *g, int step)
3468 {
3469         g->geom.y -= step;
3470         do_key_motion (g);
3471 }
3472
3473 static void do_key_motion_left (struct graph *g, int step)
3474 {
3475         g->geom.x += step;
3476         do_key_motion (g);
3477 }
3478
3479 static void do_key_motion_right (struct graph *g, int step)
3480 {
3481         g->geom.x -= step;
3482         do_key_motion (g);
3483 }
3484
3485 static gboolean button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data _U_)
3486 {
3487         struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3488
3489         debug(DBS_FENTRY) puts ("button_press_event()");
3490
3491         if (event->button == MOUSE_BUTTON_RIGHT) {
3492                 if (event->state & GDK_CONTROL_MASK) {
3493                         magnify_create (g, (int )rint (event->x), (int )rint (event->y));
3494                 } else {
3495                         g->grab.x = (int )rint (event->x) - g->geom.x;
3496                         g->grab.y = (int )rint (event->y) - g->geom.y;
3497                         g->grab.grabbed = TRUE;
3498                 }
3499 #ifdef ORIGINAL_WIN32_BUTTONS
3500                                 /* Windows mouse control:        */
3501                                 /* [<ctrl>-left] - select packet */
3502                                 /* [left] - zoom in              */
3503                                 /* [<shift>-left] - zoom out     */
3504         } else if (event->button == MOUSE_BUTTON_LEFT) {
3505                 if (event->state & GDK_CONTROL_MASK) {
3506                         graph_select_segment (g, (int)event->x, (int)event->y);
3507                 } else {
3508 #else /* !ORIGINAL_WIN32_BUTTONS */
3509         } else if (event->button == MOUSE_BUTTON_MIDDLE) {
3510 #endif
3511                 do_zoom_mouse(g, event);
3512 #ifndef ORIGINAL_WIN32_BUTTONS
3513         } else if (event->button == MOUSE_BUTTON_LEFT) {
3514                 graph_select_segment (g, (int )event->x, (int )event->y);
3515 #else /* ORIGINAL_WIN32_BUTTONS*/
3516                 }
3517 #endif
3518         }
3519         return TRUE;
3520 }
3521
3522 static gboolean motion_notify_event (GtkWidget *widget, GdkEventMotion *event, gpointer user_data _U_)
3523 {
3524         struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3525         int x, y;
3526         GdkModifierType state;
3527
3528         /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
3529
3530         if (event->is_hint)
3531                 gdk_window_get_pointer (event->window, &x, &y, &state);
3532         else {
3533                 x = (int) event->x;
3534                 y = (int) event->y;
3535                 state = event->state;
3536         }
3537
3538         /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
3539          * is pressed while pointer is in motion, we will receive one more motion
3540          * notify *before* we get the button press. This last motion notify works
3541          * with stale grab coordinates */
3542         if (state & GDK_BUTTON3_MASK) {
3543                 if (g->grab.grabbed) {
3544                         g->geom.x = x-g->grab.x;
3545                         g->geom.y = y-g->grab.y;
3546
3547                         if (g->geom.x > g->wp.x)
3548                                 g->geom.x = g->wp.x;
3549                         if (g->geom.y > g->wp.y)
3550                                 g->geom.y = g->wp.y;
3551                         if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
3552                                 g->geom.x = g->wp.width + g->wp.x - g->geom.width;
3553                         if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
3554                                 g->geom.y = g->wp.height + g->wp.y - g->geom.height;
3555                         g->cross.erase_needed = 0;
3556                         graph_display (g);
3557                         axis_display (g->y_axis);
3558                         axis_display (g->x_axis);
3559                         if (g->cross.draw)
3560                                 cross_draw (g, x, y);
3561                 } else if (g->magnify.active)
3562                         magnify_move (g, x, y);
3563         } else if (state & GDK_BUTTON1_MASK) {
3564                 graph_select_segment (g, x, y);
3565                 if (g->cross.erase_needed)
3566                         cross_erase (g);
3567                 if (g->cross.draw)
3568                         cross_draw (g, x, y);
3569         } else {
3570                 if (g->cross.erase_needed)
3571                         cross_erase (g);
3572                 if (g->cross.draw)
3573                         cross_draw (g, x, y);
3574         }
3575
3576         return TRUE;
3577 }
3578
3579 static gboolean button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data _U_)
3580 {
3581         struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3582
3583         debug(DBS_FENTRY) puts ("button_release_event()");
3584
3585         if (event->button == MOUSE_BUTTON_RIGHT)
3586                 g->grab.grabbed = FALSE;
3587
3588         if (g->magnify.active)
3589                 magnify_destroy (g);
3590         return TRUE;
3591 }
3592
3593 static gboolean key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data _U_)
3594 {
3595         struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3596         int step;
3597
3598         debug(DBS_FENTRY) puts ("key_press_event()");
3599
3600         if((event->state & GDK_CONTROL_MASK) && (event->state & GDK_SHIFT_MASK))
3601                 step = 0;
3602         else if (event->state & GDK_CONTROL_MASK)
3603                 step = 1;
3604         else if (event->state & GDK_SHIFT_MASK)
3605                 step = 10;
3606         else
3607                 step = 100;
3608
3609         switch (event->keyval) {
3610         case ' ':
3611                 toggle_crosshairs (g);
3612                 break;
3613         case 't':
3614                 toggle_time_origin (g);
3615                 break;
3616         case 's':
3617                 toggle_seq_origin (g);
3618                 break;
3619         case 'r':
3620         case GDK_Home:
3621                 restore_initial_graph_view (g);
3622                 break;
3623         case 'i':
3624         case '+':
3625                 do_zoom_in_keyboard (g);
3626                 break;
3627         case 'o':
3628         case '-':
3629                 do_zoom_out_keyboard (g);
3630                 break;
3631         case 'm':
3632                 do_magnify_create (g);
3633                 break;
3634         case 'g':
3635                 do_select_segment (g);
3636                 break;
3637         case '1':
3638                 do_rtt_graph (g);
3639                 break;
3640         case '2':
3641                 do_throughput_graph (g);
3642                 break;
3643         case '3':
3644                 do_ts_graph_stevens (g);
3645                 break;
3646         case '4':
3647                 do_ts_graph_tcptrace (g);
3648                 break;
3649         case '5':
3650                 do_wscale_graph(g);
3651                 break;
3652         case GDK_Left:
3653                 do_key_motion_left (g, step);
3654                 break;
3655         case GDK_Up:
3656                 do_key_motion_up (g, step);
3657                 break;
3658         case GDK_Right:
3659                 do_key_motion_right (g, step);
3660                 break;
3661         case GDK_Down:
3662                 do_key_motion_down (g, step);
3663                 break;
3664         case GDK_F1:
3665                 callback_create_help (NULL, NULL);
3666                 break;
3667         default:
3668                 break;
3669         }
3670         return TRUE;
3671 }
3672
3673 static gboolean key_release_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data _U_)
3674 {
3675         struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3676
3677         debug(DBS_FENTRY) puts ("key_release_event()");
3678
3679         if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
3680                 /* g->zoom.flags &= ~ZOOM_OUT; */
3681                 gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
3682         }
3683         return TRUE;
3684 }
3685
3686 static gboolean leave_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_, gpointer user_data _U_)
3687 {
3688         struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3689
3690         if (g->cross.erase_needed)
3691                 cross_erase (g);
3692
3693         return TRUE;
3694 }
3695
3696 static gboolean enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_, gpointer user_data _U_)
3697 {
3698         struct graph *g = (struct graph *) g_object_get_data(G_OBJECT(widget), "graph");
3699
3700         /* graph_pixmap_display (g); */
3701         if (g->cross.draw) {
3702                 int x, y;
3703                 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
3704                 cross_draw (g, x, y);
3705         }
3706         return TRUE;
3707 }
3708
3709 static void toggle_crosshairs (struct graph *g)
3710 {
3711         g->cross.draw ^= 1;
3712 #if 0
3713         if (g->cross.draw) {
3714                 int x, y;
3715                 gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
3716                 cross_draw (g);
3717         } else if (g->cross.erase_needed) {
3718                 cross_erase (g);
3719         }
3720 #endif
3721         /* toggle buttons emit their "toggled" signals so don't bother doing
3722          * any real work here, it will be done in signal handlers */
3723         if (g->cross.draw)
3724                 gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
3725         else
3726                 gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
3727 }
3728
3729 static void toggle_time_origin (struct graph *g)
3730 {
3731         switch (g->type) {
3732         case GRAPH_TSEQ_STEVENS:
3733                 tseq_stevens_toggle_time_origin (g);
3734                 break;
3735         case GRAPH_TSEQ_TCPTRACE:
3736                 tseq_tcptrace_toggle_time_origin (g);
3737                 break;
3738         case GRAPH_THROUGHPUT:
3739                 tput_toggle_time_origin (g);
3740                 break;
3741         default:
3742                 break;
3743         }
3744         axis_display (g->x_axis);
3745 }
3746
3747 static void toggle_seq_origin (struct graph *g)
3748 {
3749         switch (g->type) {
3750         case GRAPH_TSEQ_STEVENS:
3751                 tseq_stevens_toggle_seq_origin (g);
3752                 axis_display (g->y_axis);
3753                 break;
3754         case GRAPH_TSEQ_TCPTRACE:
3755                 tseq_tcptrace_toggle_seq_origin (g);
3756                 axis_display (g->y_axis);
3757                 break;
3758         case GRAPH_RTT:
3759                 rtt_toggle_seq_origin (g);
3760                 axis_display (g->x_axis);
3761                 break;
3762         default:
3763                 break;
3764         }
3765 }
3766
3767 static void restore_initial_graph_view (struct graph *g)
3768 {
3769         g->geom.width = g->wp.width;
3770         g->geom.height = g->wp.height;
3771         g->geom.x = g->wp.x;
3772         g->geom.y = g->wp.y;
3773         graph_init_sequence (g);
3774 }
3775
3776 static int get_num_dsegs (struct graph *g)
3777 {
3778         int count;
3779         struct segment *tmp;
3780
3781         for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3782                 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3783                                    g->current->th_sport, g->current->th_dport,
3784                                    &tmp->ip_src, &tmp->ip_dst,
3785                                    tmp->th_sport, tmp->th_dport,
3786                                    COMPARE_CURR_DIR)) {
3787                         count++;
3788                 }
3789         }
3790         return count;
3791 }
3792
3793 static int get_num_acks (struct graph *g)
3794 {
3795         int count;
3796         struct segment *tmp;
3797
3798         for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
3799                 if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
3800                                    g->current->th_sport, g->current->th_dport,
3801                                    &tmp->ip_src, &tmp->ip_dst,
3802                                    tmp->th_sport, tmp->th_dport,
3803                                    COMPARE_CURR_DIR)) {
3804                         count++;
3805                 }
3806         }
3807         return count;
3808 }
3809
3810 /*
3811  * Stevens-style time-sequence grapH
3812  */
3813
3814 static void tseq_stevens_read_config (struct graph *g)
3815 {
3816         debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
3817
3818         g->s.tseq_stevens.seq_width = 4;
3819         g->s.tseq_stevens.seq_height = 4;
3820         g->s.tseq_stevens.flags = 0;
3821
3822         g->title = (const char ** )g_malloc (2 * sizeof (char *));
3823         g->title[0] = "Time/Sequence Graph (Stevens)";
3824         g->title[1] = NULL;
3825         g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
3826         g->y_axis->label[0] = "number[B]";
3827         g->y_axis->label[1] = "Sequence";
3828         g->y_axis->label[2] = NULL;
3829         g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
3830         g->x_axis->label[0] = "Time[s]";
3831         g->x_axis->label[1] = NULL;
3832 }
3833
3834 /* Used by both 'stevens' and 'tcptrace' */
3835 static void tseq_initialize (struct graph *g)
3836 {
3837         debug(DBS_FENTRY) puts ("tseq_initialize()");
3838         tseq_get_bounds (g);
3839
3840         g->x_axis->min = 0;
3841         g->y_axis->min = 0;
3842
3843         switch (g->type) {
3844         case GRAPH_TSEQ_STEVENS:
3845                 tseq_stevens_read_config(g);
3846                 break;
3847         case GRAPH_TSEQ_TCPTRACE:
3848                 tseq_tcptrace_read_config(g);
3849                 break;
3850         }
3851 }
3852
3853
3854 /* Determine "bounds"
3855  *  Essentially: look for lowest/highest time and seq in the list of segments
3856  *  Note that for tcptrace the "(ack + window) sequence number" would normally be expected
3857  *   to be the upper bound; However, just to be safe, include the data seg sequence numbers
3858  *   in the comparison for tcptrace
3859  *   (e.g. to handle the case of only data segments).
3860  */
3861
3862 /* ToDo: worry about handling cases such as trying to plot seq of just 1 frame  */
3863
3864 static void tseq_get_bounds (struct graph *g)
3865 {
3866         struct segment *tmp;
3867         double   tim;
3868         gboolean data_frame_seen=FALSE;
3869         double   data_tim_low=0;
3870         double   data_tim_high=0;
3871         guint32  data_seq_cur;
3872         guint32  data_seq_nxt;
3873         guint32  data_seq_low=0;
3874         guint32  data_seq_high=0;
3875         gboolean ack_frame_seen=FALSE;
3876         double   ack_tim_low=0;
3877         double   ack_tim_high=0;
3878         guint32  ack_seq_cur;
3879         guint32  ack_seq_low=0;
3880         guint32  win_seq_cur;
3881         guint32  win_seq_high=0;
3882
3883         /* go thru all segments to determine "bounds" */
3884         for (tmp=g->segments; tmp; tmp=tmp->next) {
3885                 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
3886                                    g->current->th_sport, g->current->th_dport,
3887                                    &tmp->ip_src, &tmp->ip_dst,
3888                                    tmp->th_sport, tmp->th_dport,
3889                                    COMPARE_CURR_DIR)) {
3890
3891                         /* "data" seg */
3892                         tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3893                         data_seq_cur = tmp->th_seq;
3894                         data_seq_nxt = data_seq_cur + tmp->th_seglen;
3895                         if (! data_frame_seen) {
3896                                 data_tim_low    = data_tim_high = tim;
3897                                 data_seq_low    = data_seq_cur;
3898                                 data_seq_high   = data_seq_nxt;
3899                                 data_frame_seen = TRUE;
3900                         }
3901                         if (tim          < data_tim_low)  data_tim_low  = tim;
3902                         if (tim          > data_tim_high) data_tim_high = tim;
3903                         if (data_seq_cur < data_seq_low)  data_seq_low  = data_seq_cur;
3904                         if (data_seq_nxt > data_seq_high) data_seq_high = data_seq_nxt;
3905                 }
3906                 else { /* ack seg */
3907                         /* skip ack processing if no ACK (e.g. in RST) */
3908                         if (TCP_ACK (tmp->th_flags)) {
3909                                 tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
3910                                 ack_seq_cur = tmp->th_ack;
3911                                 win_seq_cur = ack_seq_cur + tmp->th_win;
3912                                 if (! ack_frame_seen) {
3913                                         ack_tim_low  = ack_tim_high = tim;
3914                                         ack_seq_low  = ack_seq_cur;
3915                                         win_seq_high = win_seq_cur;
3916                                         ack_frame_seen = TRUE;
3917                                 }
3918                                 if (tim         < ack_tim_low)  ack_tim_low  = tim;
3919                                 if (tim         > ack_tim_high) ack_tim_high = tim;
3920                                 if (ack_seq_cur < ack_seq_low)  ack_seq_low  = ack_seq_cur;
3921                                 if (win_seq_cur > win_seq_high) win_seq_high = win_seq_cur;
3922                         }
3923                 }
3924         }
3925
3926         /* if 'stevens':  use only data segments to determine bounds         */
3927         /* if 'tcptrace': use both data and ack segments to determine bounds */
3928         switch (g->type) {
3929         case GRAPH_TSEQ_STEVENS:
3930                 g->bounds.x0     = data_tim_low;
3931                 g->bounds.width  = data_tim_high - data_tim_low;
3932                 g->bounds.y0     = data_seq_low;
3933                 g->bounds.height = data_seq_high - data_seq_low;
3934                 break;
3935         case GRAPH_TSEQ_TCPTRACE:
3936                 /* If (ack_frame_seen == false) -> use 'data' segments.
3937                  * Else If (data_frame_seen == false) -> use 'ack' segments.
3938                  * Else -> use both data and ack segments.
3939                  */
3940                 g->bounds.x0     =  ((data_tim_low <= ack_tim_low   && data_frame_seen) || (! ack_frame_seen)) ? data_tim_low  : ack_tim_low;
3941                 g->bounds.width  = (((data_tim_high >= ack_tim_high && data_frame_seen) || (! ack_frame_seen)) ? data_tim_high : ack_tim_high) - g->bounds.x0;
3942                 g->bounds.y0     =  ((data_seq_low <= ack_seq_low   && data_frame_seen) || (! ack_frame_seen)) ? data_seq_low  : ack_seq_low;
3943                 g->bounds.height = (((data_seq_high >= win_seq_high && data_frame_seen) || (! ack_frame_seen)) ? data_seq_high : win_seq_high) - g->bounds.y0;
3944                 break;
3945         }
3946
3947         g->zoom.x = (g->geom.width - 1) / g->bounds.width;
3948         g->zoom.y = (g->geom.height -1) / g->bounds.height;
3949 }
3950
3951
3952 static void tseq_stevens_make_elmtlist (struct graph *g)
3953 {
3954         struct segment *tmp;
3955         struct element *elements, *e;
3956         double xx0 = g->bounds.x0, yy0 = g->bounds.y0;
3957         guint32 seq_base = (guint32) yy0;
3958         guint32 seq_cur;
3959
3960         debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
3961         if (g->elists->elements == NULL) {
3962                 int n = 1 + get_num_dsegs (g);
3963                 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
3964         } else
3965                 e = elements = g->elists->elements;
3966
3967         for (tmp=g->segments; tmp; tmp=tmp->next) {
3968                 double secs, seqno;
3969
3970                 if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
3971                                    g->current->th_sport, g->current->th_dport,
3972                                    &tmp->ip_src, &tmp->ip_dst,
3973                                    tmp->th_sport, tmp->th_dport,
3974                                    COMPARE_CURR_DIR)) {
3975                         continue;
3976                 }
3977                 /* data seg */
3978                 seq_cur = tmp->th_seq - seq_base;
3979                 secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - xx0);
3980                 seqno = g->zoom.y * seq_cur;
3981
3982                 e->type = ELMT_ELLIPSE;
3983                 e->parent = tmp;
3984                 e->p.ellipse.dim.width = g->s.tseq_stevens.seq_width;
3985                 e->p.ellipse.dim.height = g->s.tseq_stevens.seq_height;
3986                 e->p.ellipse.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
3987                 e->p.ellipse.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
3988                 e++;
3989         }
3990         e->type = ELMT_NONE;
3991         g->elists->elements = elements;
3992 }
3993
3994 static void tseq_stevens_toggle_seq_origin (struct graph *g)
3995 {
3996         g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
3997
3998         if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
3999                 g->y_axis->min = g->bounds.y0;
4000         else            /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
4001                 g->y_axis->min = 0;
4002 }
4003
4004 static void tseq_stevens_toggle_time_origin (struct graph *g)
4005 {
4006         g->s.tseq_stevens.flags ^= TIME_ORIGIN;
4007
4008         if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
4009                 g->x_axis->min = g->bounds.x0;
4010         else            /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
4011                 g->x_axis->min = 0;
4012 }
4013
4014 /*
4015  * tcptrace-style time-sequence graph
4016  */
4017
4018 static void tseq_tcptrace_read_config (struct graph *g)
4019 {
4020
4021         /* Black */
4022         g->s.tseq_tcptrace.seq_color.pixel=0;
4023         g->s.tseq_tcptrace.seq_color.red=0;
4024         g->s.tseq_tcptrace.seq_color.green=0;
4025         g->s.tseq_tcptrace.seq_color.blue=0;
4026
4027         /* LightSlateGray */
4028         g->s.tseq_tcptrace.ack_color[0].pixel=0;
4029         g->s.tseq_tcptrace.ack_color[0].red=0x7777;
4030         g->s.tseq_tcptrace.ack_color[0].green=0x8888;
4031         g->s.tseq_tcptrace.ack_color[0].blue=0x9999;
4032
4033         /* LightGray */
4034         g->s.tseq_tcptrace.ack_color[1].pixel=0;
4035         g->s.tseq_tcptrace.ack_color[1].red=0xd3d3;
4036         g->s.tseq_tcptrace.ack_color[1].green=0xd3d3;
4037         g->s.tseq_tcptrace.ack_color[1].blue=0xd3d3;
4038
4039         g->s.tseq_tcptrace.flags = 0;
4040
4041         g->elists->next = (struct element_list * )
4042                                 g_malloc (sizeof (struct element_list));
4043         g->elists->next->next = NULL;
4044         g->elists->next->elements = NULL;
4045
4046         g->title = (const char ** )g_malloc (2 * sizeof (char *));
4047         g->title[0] = "Time/Sequence Graph (tcptrace)";
4048         g->title[1] = NULL;
4049         g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
4050         g->y_axis->label[0] = "number[B]";
4051         g->y_axis->label[1] = "Sequence";
4052         g->y_axis->label[2] = NULL;
4053         g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
4054         g->x_axis->label[0] = "Time[s]";
4055         g->x_axis->label[1] = NULL;
4056 }
4057
4058 static void tseq_tcptrace_make_elmtlist (struct graph *g)
4059 {
4060         struct segment *tmp;
4061         struct element *elements0, *e0;         /* list of elmts with prio 0 */
4062         struct element *elements1, *e1;         /* list of elmts with prio 1 */
4063         double xx0, yy0;
4064         double p_t = 0; /* ackno, window and time of previous segment */
4065         double p_ackno = 0, p_win = 0;
4066         gboolean ack_seen=FALSE;
4067         int toggle=0;
4068         guint32 seq_base;
4069         guint32 seq_cur;
4070
4071         debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
4072
4073         if (g->elists->elements == NULL) {
4074                 int n = 1 + 4*get_num_acks(g);
4075                 e0 = elements0 = (struct element * )g_malloc (n*sizeof (struct element));
4076         } else
4077                 e0 = elements0 = g->elists->elements;
4078
4079         if (g->elists->next->elements == NULL ) {
4080                 int n = 1 + 3*get_num_dsegs(g);
4081                 e1 = elements1 = (struct element * )g_malloc (n*sizeof (struct element));
4082         } else
4083                 e1 = elements1 = g->elists->next->elements;
4084
4085         xx0 = g->bounds.x0;
4086         yy0 = g->bounds.y0;
4087         seq_base = (guint32) yy0;
4088
4089         for (tmp=g->segments; tmp; tmp=tmp->next) {
4090                 double secs, data;
4091                 double x;
4092
4093                 secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4094                 x = secs - xx0;
4095                 x *= g->zoom.x;
4096                 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
4097                                    g->current->th_sport, g->current->th_dport,
4098                                    &tmp->ip_src, &tmp->ip_dst,
4099                                    tmp->th_sport, tmp->th_dport,
4100                                    COMPARE_CURR_DIR)) {
4101                         /* forward direction -> we need seqno and amount of data */
4102                         double yy1, yy2;
4103
4104                         seq_cur = tmp->th_seq - seq_base;
4105                         if (TCP_SYN (tmp->th_flags) || TCP_FIN (tmp->th_flags))
4106                                 data = 1;
4107                         else
4108                                 data = tmp->th_seglen;
4109
4110                         yy1 = g->zoom.y * (seq_cur);
4111                         yy2 = g->zoom.y * (seq_cur + data);
4112                         e1->type = ELMT_LINE;
4113                         e1->parent = tmp;
4114                         /* Set the drawing color */
4115                         e1->elment_color_p = &g->s.tseq_tcptrace.seq_color;
4116                         e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
4117                         e1->p.line.dim.y1 = yy1;
4118                         e1->p.line.dim.y2 = yy2;
4119                         e1++;
4120                         e1->type = ELMT_LINE;
4121                         e1->parent = tmp;
4122                         /* Set the drawing color */
4123                         e1->elment_color_p = &g->s.tseq_tcptrace.seq_color;
4124                         e1->p.line.dim.x1 = x - 1;
4125                         e1->p.line.dim.x2 = x + 1;
4126                         e1->p.line.dim.y1 = e1->p.line.dim.y2 = yy1;
4127                         e1++;
4128                         e1->type = ELMT_LINE;
4129                         e1->parent = tmp;
4130                         /* Set the drawing color */
4131                         e1->elment_color_p = &g->s.tseq_tcptrace.seq_color;
4132                         e1->p.line.dim.x1 = x + 1;
4133                         e1->p.line.dim.x2 = x - 1;
4134                         e1->p.line.dim.y1 = e1->p.line.dim.y2 = yy2;
4135                         e1++;
4136                 } else {
4137                         double ackno, win;
4138                         if (! TCP_ACK (tmp->th_flags))
4139                                 /* SYN's and RST's do not necessarily have ACK's*/
4140                                 continue;
4141                         /* backward direction -> we need ackno and window */
4142                         seq_cur = tmp->th_ack - seq_base;
4143                         ackno = seq_cur * g->zoom.y;
4144                         win = tmp->th_win * g->zoom.y;
4145
4146                         /* ack line */
4147                         if (ack_seen == TRUE) { /* don't plot the first ack */
4148                                 e0->type = ELMT_LINE;
4149                                 e0->parent = tmp;
4150                                 /* Set the drawing color */
4151                                 e0->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
4152                                 e0->p.line.dim.x1 = p_t;
4153                                 e0->p.line.dim.y1 = p_ackno;
4154                                 e0->p.line.dim.x2 = x;
4155                                 e0->p.line.dim.y2 = p_ackno;
4156                                 e0++;
4157                                 e0->type = ELMT_LINE;
4158                                 e0->parent = tmp;
4159                                 /* Set the drawing color */
4160                                 e0->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
4161                                 e0->p.line.dim.x1 = x;
4162                                 e0->p.line.dim.y1 = p_ackno;
4163                                 e0->p.line.dim.x2 = x;
4164                                 e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
4165                                 e0++;
4166                                 /* window line */
4167                                 e0->type = ELMT_LINE;
4168                                 e0->parent = tmp;
4169                                 /* Set the drawing color */
4170                                 e0->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
4171                                 e0->p.line.dim.x1 = p_t;
4172                                 e0->p.line.dim.y1 = p_win + p_ackno;
4173                                 e0->p.line.dim.x2 = x;
4174                                 e0->p.line.dim.y2 = p_win + p_ackno;
4175                                 e0++;
4176                                 e0->type = ELMT_LINE;
4177                                 e0->parent = tmp;
4178                                 /* Set the drawing color */
4179                                 e0->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
4180                                 e0->p.line.dim.x1 = x;
4181                                 e0->p.line.dim.y1 = p_win + p_ackno;
4182                                 e0->p.line.dim.x2 = x;
4183                                 e0->p.line.dim.y2 = win + ackno;
4184                                 e0++;
4185                                 toggle = 1^toggle;
4186                         }
4187                         ack_seen = TRUE;
4188                         p_ackno = ackno;
4189                         p_win = win;
4190                         p_t = x;
4191                 }
4192         }
4193         e0->type = ELMT_NONE;
4194         e1->type = ELMT_NONE;
4195         g->elists->elements = elements0;
4196         g->elists->next->elements = elements1;
4197 }
4198
4199 static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
4200 {
4201         g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
4202
4203         if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
4204                 g->y_axis->min = g->bounds.y0;
4205         else    /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
4206                 g->y_axis->min = 0;
4207 }
4208
4209 static void tseq_tcptrace_toggle_time_origin (struct graph *g)
4210 {
4211         g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
4212
4213         if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
4214                 g->x_axis->min = g->bounds.x0;
4215         else    /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
4216                 g->x_axis->min = 0;
4217 }
4218
4219 /*
4220  * throughput graph
4221  */
4222
4223 static void tput_make_elmtlist (struct graph *g)
4224 {
4225         struct segment *tmp, *oldest;
4226         struct element *elements, *e;
4227         int i, sum=0;
4228         double dtime, tput;
4229
4230         if (g->elists->elements == NULL) {
4231                 int n = 1 + get_num_dsegs (g);
4232                 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
4233         } else
4234                 e = elements = g->elists->elements;
4235
4236         for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
4237                 double time_val = tmp->rel_secs + tmp->rel_usecs/1000000.0;
4238                 dtime = time_val - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
4239                 if (i>g->s.tput.nsegs) {
4240                         sum -= oldest->th_seglen;
4241                         oldest=oldest->next;
4242                 }
4243                 sum += tmp->th_seglen;
4244                 tput = sum / dtime;
4245                 /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
4246
4247                 e->type = ELMT_ELLIPSE;
4248                 e->parent = tmp;
4249                 e->p.ellipse.dim.width = g->s.tput.width;
4250                 e->p.ellipse.dim.height = g->s.tput.height;
4251                 e->p.ellipse.dim.x = g->zoom.x*(time_val - g->bounds.x0) - g->s.tput.width/2.0;
4252                 e->p.ellipse.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
4253                 e++;
4254         }
4255         e->type = ELMT_NONE;
4256         g->elists->elements = elements;
4257 }
4258
4259 /* Purpose of <graph_type>_initialize functions:
4260  * - find maximum and minimum for both axes
4261  * - call setup routine for style struct */
4262 static void tput_initialize (struct graph *g)
4263 {
4264         struct segment *tmp, *oldest, *last;
4265         int i, sum=0;
4266         double dtime, tput, tputmax=0;
4267         double t, t0, tmax = 0, yy0, ymax;
4268
4269         debug(DBS_FENTRY) puts ("tput_initialize()");
4270
4271         tput_read_config(g);
4272
4273         for (last=g->segments; last->next; last=last->next);
4274         for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
4275                 dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
4276                                                 (oldest->rel_secs + oldest->rel_usecs/1000000.0);
4277                 if (i>g->s.tput.nsegs) {
4278                         sum -= oldest->th_seglen;
4279                         oldest=oldest->next;
4280                 }
4281                 sum += tmp->th_seglen;
4282                 tput = sum / dtime;
4283                 debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
4284                 if (tput > tputmax)
4285                         tputmax = tput;
4286                 t = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4287                 if (t > tmax)
4288                         tmax = t;
4289         }
4290
4291         t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
4292         yy0 = 0;
4293         ymax = tputmax;
4294
4295         g->bounds.x0 = t0;
4296         g->bounds.y0 = yy0;
4297         g->bounds.width = tmax - t0;
4298         g->bounds.height = ymax - yy0;
4299         g->zoom.x = (g->geom.width - 1) / g->bounds.width;
4300         g->zoom.y = (g->geom.height -1) / g->bounds.height;
4301 }
4302
4303 static void tput_read_config (struct graph *g)
4304 {
4305         debug(DBS_FENTRY) puts ("tput_read_config()");
4306
4307         g->s.tput.width = 4;
4308         g->s.tput.height = 4;
4309         g->s.tput.nsegs = 20;
4310
4311         g->title = (const char ** )g_malloc (2 * sizeof (char *));
4312         g->title[0] = "Throughput Graph";
4313         g->title[1] = NULL;
4314         g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
4315         g->y_axis->label[0] = "[B/s]";
4316         g->y_axis->label[1] = "Throughput";
4317         g->y_axis->label[2] = NULL;
4318         g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
4319         g->x_axis->label[0] = "Time[s]";
4320         g->x_axis->label[1] = NULL;
4321         g->s.tput.flags = 0;
4322 }
4323
4324 static void tput_toggle_time_origin (struct graph *g)
4325 {
4326         g->s.tput.flags ^= TIME_ORIGIN;
4327
4328         if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
4329                 g->x_axis->min = g->bounds.x0;
4330         else    /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
4331                 g->x_axis->min = 0;
4332 }
4333
4334 /* RTT graph */
4335
4336 static void rtt_read_config (struct graph *g)
4337 {
4338         debug(DBS_FENTRY) puts ("rtt_read_config()");
4339
4340         g->s.rtt.width = 4;
4341         g->s.rtt.height = 4;
4342         g->s.rtt.flags = 0;
4343
4344         g->title = (const char ** )g_malloc (2 * sizeof (char *));
4345         g->title[0] = "Round Trip Time Graph";
4346         g->title[1] = NULL;
4347         g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
4348         g->y_axis->label[0] = "RTT [s]";
4349         g->y_axis->label[1] = NULL;
4350         g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
4351         g->x_axis->label[0] = "Sequence Number[B]";
4352         g->x_axis->label[1] = NULL;
4353 }
4354
4355 static void rtt_initialize (struct graph *g)
4356 {
4357         struct segment *tmp, *first=NULL;
4358         struct unack *unack = NULL, *u;
4359         double rttmax=0;
4360         double xx0, yy0, ymax;
4361         guint32 xmax = 0;
4362         guint32 seq_base = 0;
4363
4364         debug(DBS_FENTRY) puts ("rtt_initialize()");
4365
4366         rtt_read_config (g);
4367
4368         for (tmp=g->segments; tmp; tmp=tmp->next) {
4369                 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
4370                                    g->current->th_sport, g->current->th_dport,
4371                                    &tmp->ip_src, &tmp->ip_dst,
4372                                    tmp->th_sport, tmp->th_dport,
4373                                    COMPARE_CURR_DIR)) {
4374                         guint32 seqno = tmp->th_seq;
4375
4376                         if (!first) {
4377                                 first= tmp;
4378                                 seq_base = seqno;
4379                         }
4380                         seqno -= seq_base;
4381                         if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
4382                                 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4383                                 u = rtt_get_new_unack (time_val, seqno);
4384                                 if (!u) return;
4385                                 rtt_put_unack_on_list (&unack, u);
4386                         }
4387
4388                         if (seqno + tmp->th_seglen > xmax)
4389                                 xmax = seqno + tmp->th_seglen;
4390                 } else if (first) {
4391                         guint32 ackno = tmp->th_ack -seq_base;
4392                         double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4393                         struct unack *v;
4394
4395                         for (u=unack; u; u=v)
4396                                 if (ackno > u->seqno) {
4397                                         double rtt = time_val - u->time;
4398                                         if (rtt > rttmax)
4399                                                 rttmax = rtt;
4400                                         v=u->next;
4401                                         rtt_delete_unack_from_list (&unack, u);
4402                                 } else
4403                                         v=u->next;
4404                 }
4405         }
4406
4407         xx0 = seq_base;
4408         yy0 = 0;
4409         ymax = rttmax;
4410
4411         g->bounds.x0 = xx0;
4412         g->bounds.y0 = yy0;
4413         g->bounds.width = xmax;
4414         g->bounds.height = ymax - yy0;
4415         g->zoom.x = g->geom.width / g->bounds.width;
4416         g->zoom.y = g->geom.height / g->bounds.height;
4417 }
4418
4419 static int rtt_is_retrans (struct unack *list, unsigned int seqno)
4420 {
4421         struct unack *u;
4422
4423         for (u=list; u; u=u->next)
4424                 if (u->seqno== seqno)
4425                         return TRUE;
4426
4427         return FALSE;
4428 }
4429
4430 static struct unack *rtt_get_new_unack (double time_val, unsigned int seqno)
4431 {
4432         struct unack *u;
4433
4434         u = (struct unack * )g_malloc (sizeof (struct unack));
4435         if (!u)
4436                 return NULL;
4437         u->next = NULL;
4438         u->time = time_val;
4439         u->seqno = seqno;
4440         return u;
4441 }
4442
4443 static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
4444 {
4445         struct unack *u, *list = *l;
4446
4447         for (u=list; u; u=u->next)
4448                 if (!u->next)
4449                         break;
4450
4451         if (u)
4452                 u->next = new;
4453         else
4454                 *l = new;
4455 }
4456
4457 static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
4458 {
4459         struct unack *u, *list = *l;
4460
4461         if (!dead || !list)
4462                 return;
4463
4464         if (dead==list) {
4465                 *l = list->next;
4466                 g_free (list);
4467         } else
4468                 for (u=list; u; u=u->next)
4469                         if (u->next == dead) {
4470                                 u->next = u->next->next;
4471                                 g_free (dead);
4472                                 break;
4473                         }
4474 }
4475
4476 static void rtt_make_elmtlist (struct graph *g)
4477 {
4478         struct segment *tmp;
4479         struct unack *unack = NULL, *u;
4480         struct element *elements, *e;
4481         guint32 seq_base = (guint32) g->bounds.x0;
4482
4483         debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
4484
4485         if (g->elists->elements == NULL) {
4486                 int n = 1 + get_num_dsegs (g);
4487                 e = elements = (struct element * )g_malloc (n*sizeof (struct element));
4488         } else {
4489                 e = elements = g->elists->elements;
4490         }
4491
4492         for (tmp=g->segments; tmp; tmp=tmp->next) {
4493                 if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
4494                                    g->current->th_sport, g->current->th_dport,
4495                                    &tmp->ip_src, &tmp->ip_dst,
4496                                    tmp->th_sport, tmp->th_dport,
4497                                    COMPARE_CURR_DIR)) {
4498                         guint32 seqno = tmp->th_seq -seq_base;
4499
4500                         if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
4501                                 double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4502                                 u = rtt_get_new_unack (time_val, seqno);
4503                                 if (!u) return;
4504                                 rtt_put_unack_on_list (&unack, u);
4505                         }
4506                 } else {
4507                         guint32 ackno = tmp->th_ack -seq_base;
4508                         double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
4509                         struct unack *v;
4510
4511                         for (u=unack; u; u=v)
4512                                 if (ackno > u->seqno) {
4513                                         double rtt = time_val - u->time;
4514
4515                                         e->type = ELMT_ELLIPSE;
4516                                         e->parent = tmp;
4517                                         e->p.ellipse.dim.width = g->s.rtt.width;
4518                                         e->p.ellipse.dim.height = g->s.rtt.height;
4519                                         e->p.ellipse.dim.x = g->zoom.x * u->seqno - g->s.rtt.width/2.0;
4520                                         e->p.ellipse.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
4521                                         e++;
4522
4523                                         v=u->next;
4524                                         rtt_delete_unack_from_list (&unack, u);
4525                                 } else
4526                                         v=u->next;
4527                 }
4528         }
4529         e->type = ELMT_NONE;
4530         g->elists->elements = elements;
4531 }
4532
4533 static void rtt_toggle_seq_origin (struct graph *g)
4534 {
4535         g->s.rtt.flags ^= SEQ_ORIGIN;
4536
4537         if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
4538                 g->x_axis->min = g->bounds.x0;
4539         else
4540                 g->x_axis->min = 0;
4541 }
4542
4543 /* WSCALE Graph */
4544
4545 static void wscale_read_config(struct graph* g)
4546 {
4547         debug(DBS_FENTRY) puts ("wscale_read_config()");
4548
4549         g->s.wscale.win_width = 4;
4550         g->s.wscale.win_height = 4;
4551         g->s.wscale.flags = 0;
4552
4553         g->title = (const char ** )g_malloc (2 * sizeof (char *));
4554         g->title[0] = "Window Scaling Graph";
4555         g->title[1] = NULL;
4556         g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
4557         g->y_axis->label[0] = "[bytes]";
4558         g->y_axis->label[1] = "Windowsize";
4559         g->y_axis->label[2] = NULL;
4560         g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
4561         g->x_axis->label[0] = "Time [s]";
4562         g->x_axis->label[1] = NULL;
4563 }
4564
4565 /*
4566     (1) Find maximum and minimum values for Window-Size(scaled) and seconds
4567     (2) call function to define window related values
4568 */
4569 static void wscale_initialize(struct graph* g)
4570 {
4571
4572         struct segment* segm = NULL;
4573         guint32 wsize_max = 0;
4574         guint32 wsize_min = 0;
4575         gdouble sec_max = 0.0;
4576         gdouble sec_base = -1.0;
4577
4578         wscale_read_config (g);
4579
4580         debug(DBS_FENTRY) puts ("wscale_initialize()");
4581
4582         for (segm = g->segments; segm; segm = segm->next)
4583         {
4584                 if (compare_headers(&g->current->ip_src, &g->current->ip_dst,
4585                                         g->current->th_sport, g->current->th_dport,
4586                                         &segm->ip_src, &segm->ip_dst,
4587                                         segm->th_sport, segm->th_dport,
4588                                         COMPARE_CURR_DIR))
4589                 {
4590                         gdouble sec = segm->rel_secs + ( segm->rel_usecs / 1000000.0 );
4591                         guint16 flags = segm->th_flags;
4592                         guint32 wsize = segm->th_win;
4593
4594                         /* only data segments */
4595                         if ( (flags & (TH_SYN|TH_RST)) == 0 )
4596                                 if ( wsize > wsize_max )
4597                                         wsize_max = wsize;
4598
4599                         /* remind time of first probe */
4600                         if ( sec_base < 0 && sec > 0 )
4601                                 sec_base = sec;
4602
4603                         if ( sec_max < sec )
4604                                 sec_max = sec;
4605
4606                 }
4607
4608         }
4609
4610         g->bounds.x0 = 0;
4611         g->bounds.y0 = wsize_min;
4612         g->bounds.width = sec_max - sec_base + 5;
4613         g->bounds.height = wsize_max + 5;
4614         g->zoom.x = g->geom.width / g->bounds.width;
4615         g->zoom.y = g->geom.height / g->bounds.height;
4616
4617 }
4618
4619 /*
4620    (1) Fill & allocate memory for segments times elements,
4621 */
4622 static void wscale_make_elmtlist(struct graph* g)
4623 {
4624         struct segment* segm = NULL;
4625         struct element* elements = NULL;
4626         struct element* e = NULL;
4627         gdouble sec_base = -1.0;
4628
4629         debug(DBS_FENTRY) puts ("wscale_make_elmtlist()");
4630
4631         /* Allocate memory for elements if not already done */
4632         if (g->elists->elements == NULL)
4633         {
4634                 int n = 1 + get_num_dsegs(g);
4635                 e = elements = (struct element*)g_malloc(n*sizeof(struct element));
4636         }
4637         else
4638                 e = elements = g->elists->elements;
4639
4640
4641         for ( segm = g->segments; segm; segm = segm->next )
4642         {
4643                 if (compare_headers(&g->current->ip_src, &g->current->ip_dst,
4644                                         g->current->th_sport, g->current->th_dport,
4645                                         &segm->ip_src, &segm->ip_dst,
4646                                         segm->th_sport, segm->th_dport,
4647                                         COMPARE_CURR_DIR))
4648                 {
4649                         gdouble sec = segm->rel_secs + (segm->rel_usecs / 1000000.0);
4650                         guint16 flags = segm->th_flags;
4651                         guint32 wsize = segm->th_win;
4652
4653                         /* remind time of first probe */
4654                         if ( sec_base < 0 && sec > 0 )
4655                                 sec_base = sec;
4656
4657                         /* only data or ack segments */
4658                         if ( (flags & (TH_SYN|TH_RST)) == 0 )
4659                         {
4660                                 e->type = ELMT_ELLIPSE;
4661                                 e->parent = segm;
4662                                 e->p.ellipse.dim.width = g->s.wscale.win_width;
4663                                 e->p.ellipse.dim.height = g->s.wscale.win_height;
4664                                 e->p.ellipse.dim.x = g->zoom.x * (sec - sec_base) - g->s.wscale.win_width / 2.0;
4665                                 e->p.ellipse.dim.y = g->zoom.y * wsize - g->s.wscale.win_height / 2.0;
4666                                 e++;
4667                         }
4668                 }
4669         }
4670         /* finished populating element list */
4671         e->type = ELMT_NONE;
4672         g->elists->elements = elements;
4673 }
4674
4675
4676 #if defined(_WIN32) && !defined(__MINGW32__)
4677 /* replacement of Unix rint() for Windows */
4678 static int rint (double x)
4679 {
4680         char *buf;
4681         int i,dec,sig;
4682
4683         buf = _fcvt(x, 0, &dec, &sig);
4684         i = atoi(buf);
4685         if(sig == 1) {
4686                 i = i * -1;
4687         }
4688         return(i);
4689 }
4690 #endif
4691
4692 #ifdef MAIN_MENU_USE_UIMANAGER
4693 gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt, gpointer callback_data _U_)
4694 #else
4695 static
4696 gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt, gpointer callback_data _U_)
4697 #endif
4698 {
4699     return current_frame != NULL ? (edt->pi.ipproto == IP_PROTO_TCP) : FALSE;
4700 }
4701
4702
4703 void
4704 register_tap_listener_tcp_graph(void)
4705 {
4706 #ifdef MAIN_MENU_USE_UIMANAGER
4707 #else
4708     register_stat_menu_item("TCP Stream Graph/Time-Sequence Graph (Stevens)", REGISTER_STAT_GROUP_UNSORTED,
4709         tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(0));
4710     register_stat_menu_item("TCP Stream Graph/Time-Sequence Graph (tcptrace)", REGISTER_STAT_GROUP_UNSORTED,
4711         tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(1));
4712     register_stat_menu_item("TCP Stream Graph/Throughput Graph", REGISTER_STAT_GROUP_UNSORTED,
4713         tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(2));
4714     register_stat_menu_item("TCP Stream Graph/Round Trip Time Graph", REGISTER_STAT_GROUP_UNSORTED,
4715         tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(3));
4716     register_stat_menu_item("TCP Stream Graph/Window Scaling Graph", REGISTER_STAT_GROUP_UNSORTED,
4717         tcp_graph_cb, tcp_graph_selected_packet_enabled, NULL, GINT_TO_POINTER(GRAPH_WSCALE));
4718 #endif
4719
4720 }