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