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