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