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