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