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