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