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