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