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