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