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