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