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