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