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