Ethereal-dev: [Ethereal-dev] Re: TCP graphs for ethereal

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Guy Harris <gharris@xxxxxxxxxxxx>
Date: Sat, 24 Feb 2001 15:07:01 -0800
On Sat, Feb 24, 2001 at 09:47:13PM +0100, Kostra wrote:
> Hello,
> 
> I hope you are the right person to submit patches to so I'm sending you a
> patch that adds some rudimentary TCP graphing capabilities to the wonderful
> program ethereal. Needless to say, it is incomplete, the code is not very
> well structured (sometimes it's downright ugly) and it really needs to be
> worked on. My intention was to do it myself but recently it turned out that
> during the next couple of months I won't be able to devote much time to it
> so I figured out that someone could be interested in playing with it and
> coming up with ideas/code in the meantime. Besides, since some basic
> features are working I've been able to use it frequently while debugging
> networks so someone else could find it useful too.
> 
> What I've got so far is limited to TCP stream analysis and is basically a
> partial reimplementation of tcptrace-style time-sequence, throughput and
> RTT graphs. It can do zooming in and out, crosshairs, a kind of "magnifying
> glass" where you temporarily magnify portion of graph, you can select
> packets in ethereal's packet list by clicking on segments on graph and so
> on. Unfortunately, I used ethereal-0.8.11 as base distribution during
> development because 0.8.11 was the latest release at the time I started
> coding. This means that the patch I'm sending in doesn't work with 0.8.15
> although this would be very easy to correct. I apologize for the
> Makefile.{in,am} mess, I don't know how to use automake and I was barely
> able to add my files to Makefile.
> 
> If you decide to give it a try you can test it by checking out 0.8.11,
> running patch -p1 from the root of the distribution and then compiling
> normally. The patch adds an entry to the 'Tools' menu. Load a suitable dump
> acquired on ethernet or ppp network (this is important) that contains some
> interesting tcp connections (like ftp transfers), select a data segment
> (graph of 1000 ack's would not be very interesting ;-) and fire up
> tcptrace-style time-sequence graph. You should able to figure it out from
> the gui quite easily. Click on 'Help' button in the control panel, there's
> a couple of lines describing usage of mouse buttons and so on.
> 
> I've never submitted a large patch like this to a free software project yet
> so please be patient with me if the patch is not generated correctly or if
> I unknowingly break some other rules.
> 
> 	pvl

For a patch of this size that adds a new feature to Ethereal, I'm
probably *not* the right person to whom to submit it - in fact, there
probably is no right *person*; it should probably be sent to
"ethereal-dev@xxxxxxxxxxxx" for review by the developer community as a
whole (or should be made available from an FTP or Web site and the URL
sent to "ethereal-dev").

I've attached the patch to this message.
diff -r -u -N -I $Id: --exclude=CVS ethereal-0.8.11/gtk/Makefile.am ethereal/gtk/Makefile.am
--- ethereal-0.8.11/gtk/Makefile.am	Thu Aug  3 03:56:52 2000
+++ ethereal/gtk/Makefile.am	Sat Feb 24 20:45:08 2001
@@ -75,6 +75,7 @@
 	stream_prefs.h	\
 	summary_dlg.c   \
 	summary_dlg.h   \
+	tcp_graph.c	\
 	ui_util.c
 
 EXTRA_DIST = \
diff -r -u -N -I $Id: --exclude=CVS ethereal-0.8.11/gtk/Makefile.in ethereal/gtk/Makefile.in
--- ethereal-0.8.11/gtk/Makefile.in	Thu Aug  3 04:09:32 2000
+++ ethereal/gtk/Makefile.in	Sat Feb 24 20:45:08 2001
@@ -140,7 +140,7 @@
 CLEANFILES =  	libui.a			*~
 
 
-libui_a_SOURCES =  	capture_dlg.c		capture_dlg.h		color_dlg.c		color_dlg.h		colors.c		colors.h		column_prefs.c		column_prefs.h		display_opts.c		display_opts.h		dlg_utils.c		dlg_utils.h		file_dlg.c		file_dlg.h		filter_prefs.c		filter_prefs.h		find_dlg.c		find_dlg.h		goto_dlg.c		goto_dlg.h		gtkclist.c		gtkclist.h		gtkglobals.h		gui_prefs.c		gui_prefs.h		keys.h			main.c			main.h			menu.c			menu.h			packet_win.c		packet_win.h		plugins_dlg.c		prefs_dlg.c		prefs_dlg.h		print_dlg.c		print_prefs.c   	print_prefs.h		progress_dlg.c		proto_draw.c		proto_draw.h		simple_dialog.c		stream_prefs.c		stream_prefs.h		summary_dlg.c   	summary_dlg.h   	ui_util.c
+libui_a_SOURCES =  	capture_dlg.c		capture_dlg.h		color_dlg.c		color_dlg.h		colors.c		colors.h		column_prefs.c		column_prefs.h		display_opts.c		display_opts.h		dlg_utils.c		dlg_utils.h		file_dlg.c		file_dlg.h		filter_prefs.c		filter_prefs.h		find_dlg.c		find_dlg.h		goto_dlg.c		goto_dlg.h		gtkclist.c		gtkclist.h		gtkglobals.h		gui_prefs.c		gui_prefs.h		keys.h			main.c			main.h			menu.c			menu.h			packet_win.c		packet_win.h		plugins_dlg.c		prefs_dlg.c		prefs_dlg.h		print_dlg.c		print_prefs.c   	print_prefs.h		progress_dlg.c		proto_draw.c		proto_draw.h		simple_dialog.c		stream_prefs.c		stream_prefs.h		summary_dlg.c   	summary_dlg.h   	tcp_graph.c		ui_util.c
 
 
 EXTRA_DIST =  	Makefile.nmake
@@ -160,7 +160,8 @@
 display_opts.o dlg_utils.o file_dlg.o filter_prefs.o find_dlg.o \
 goto_dlg.o gtkclist.o gui_prefs.o main.o menu.o packet_win.o \
 plugins_dlg.o prefs_dlg.o print_dlg.o print_prefs.o progress_dlg.o \
-proto_draw.o simple_dialog.o stream_prefs.o summary_dlg.o ui_util.o
+proto_draw.o simple_dialog.o stream_prefs.o summary_dlg.o tcp_graph.o \
+ui_util.o
 AR = ar
 CFLAGS = @CFLAGS@
 COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -174,6 +175,14 @@
 
 TAR = tar
 GZIP_ENV = --best
+DEP_FILES =  .deps/capture_dlg.P .deps/color_dlg.P .deps/colors.P \
+.deps/column_prefs.P .deps/display_opts.P .deps/dlg_utils.P \
+.deps/file_dlg.P .deps/filter_prefs.P .deps/find_dlg.P .deps/goto_dlg.P \
+.deps/gtkclist.P .deps/gui_prefs.P .deps/main.P .deps/menu.P \
+.deps/packet_win.P .deps/plugins_dlg.P .deps/prefs_dlg.P \
+.deps/print_dlg.P .deps/print_prefs.P .deps/progress_dlg.P \
+.deps/proto_draw.P .deps/simple_dialog.P .deps/stream_prefs.P \
+.deps/summary_dlg.P .deps/tcp_graph.P .deps/ui_util.P
 SOURCES = $(libui_a_SOURCES)
 OBJECTS = $(libui_a_OBJECTS)
 
@@ -181,9 +190,9 @@
 .SUFFIXES:
 .SUFFIXES: .S .c .lo .o .s
 $(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
-	cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps gtk/Makefile
+	cd $(top_srcdir) && $(AUTOMAKE) --gnu gtk/Makefile
 
-Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status
+Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status $(BUILT_SOURCES)
 	cd $(top_builddir) \
 	  && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
 
@@ -197,9 +206,6 @@
 
 maintainer-clean-noinstLIBRARIES:
 
-.c.o:
-	$(COMPILE) -c $<
-
 .s.o:
 	$(COMPILE) -c $<
 
@@ -216,9 +222,6 @@
 
 maintainer-clean-compile:
 
-.c.lo:
-	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
-
 .s.lo:
 	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
 
@@ -274,6 +277,11 @@
 subdir = gtk
 
 distdir: $(DISTFILES)
+	here=`cd $(top_builddir) && pwd`; \
+	top_distdir=`cd $(top_distdir) && pwd`; \
+	distdir=`cd $(distdir) && pwd`; \
+	cd $(top_srcdir) \
+	  && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu gtk/Makefile
 	@for file in $(DISTFILES); do \
 	  d=$(srcdir); \
 	  if test -d $$d/$$file; then \
@@ -284,123 +292,38 @@
 	    || cp -p $$d/$$file $(distdir)/$$file || :; \
 	  fi; \
 	done
-capture_dlg.o: capture_dlg.c ../config.h gtkclist.h ../getopt.h \
-	../capture.h ../globals.h ../packet.h ../wiretap/wtap.h \
-	../proto.h ../ipv4.h ../tvbuff.h ../exceptions.h ../except.h \
-	../file.h ../dfilter.h colors.h ../print.h ../timestamp.h \
-	main.h ../ui_util.h capture_dlg.h filter_prefs.h \
-	../simple_dialog.h dlg_utils.h ../util.h
-color_dlg.o: color_dlg.c ../config.h gtkclist.h main.h ../globals.h \
-	../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	../print.h ../getopt.h ../timestamp.h colors.h color_dlg.h \
-	../simple_dialog.h dlg_utils.h ../ui_util.h
-colors.o: colors.c ../config.h gtkclist.h main.h ../globals.h \
-	../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	../print.h ../getopt.h ../timestamp.h colors.h \
-	../simple_dialog.h ../util.h
-column_prefs.o: column_prefs.c ../config.h ../globals.h ../packet.h \
-	../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	gtkclist.h ../print.h ../getopt.h ../timestamp.h column_prefs.h \
-	gtkglobals.h prefs_dlg.h ../prefs.h ../column.h
-display_opts.o: display_opts.c ../config.h gtkclist.h ../getopt.h \
-	../globals.h ../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h \
-	../tvbuff.h ../exceptions.h ../except.h ../file.h ../dfilter.h \
-	colors.h ../print.h ../timestamp.h display_opts.h ../ui_util.h \
-	dlg_utils.h
-dlg_utils.o: dlg_utils.c ../config.h gtkclist.h gtkglobals.h
-file_dlg.o: file_dlg.c ../config.h ../getopt.h ../globals.h ../packet.h \
-	../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	gtkclist.h ../print.h ../timestamp.h gtkglobals.h keys.h \
-	filter_prefs.h ../ui_util.h ../simple_dialog.h menu.h \
-	file_dlg.h dlg_utils.h ../util.h
-filter_prefs.o: filter_prefs.c ../config.h gtkclist.h ../getopt.h main.h \
-	../globals.h ../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h \
-	../tvbuff.h ../exceptions.h ../except.h ../file.h ../dfilter.h \
-	colors.h ../print.h ../timestamp.h filter_prefs.h ../util.h \
-	dlg_utils.h ../ui_util.h prefs_dlg.h ../prefs.h
-find_dlg.o: find_dlg.c ../config.h gtkclist.h ../getopt.h ../proto.h \
-	../ipv4.h ../tvbuff.h ../exceptions.h ../except.h ../dfilter.h \
-	../globals.h ../packet.h ../wiretap/wtap.h ../file.h colors.h \
-	../print.h ../timestamp.h ../ui_util.h find_dlg.h \
-	filter_prefs.h ../simple_dialog.h dlg_utils.h
-goto_dlg.o: goto_dlg.c ../config.h gtkclist.h ../getopt.h ../proto.h \
-	../ipv4.h ../tvbuff.h ../exceptions.h ../except.h ../dfilter.h \
-	../globals.h ../packet.h ../wiretap/wtap.h ../file.h colors.h \
-	../print.h ../timestamp.h goto_dlg.h prefs_dlg.h ../prefs.h \
-	../simple_dialog.h dlg_utils.h
-gtkclist.o: gtkclist.c ../config.h gtkclist.h
-gui_prefs.o: gui_prefs.c ../config.h ../globals.h ../packet.h \
-	../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	gtkclist.h ../print.h ../getopt.h ../timestamp.h gui_prefs.h \
-	gtkglobals.h prefs_dlg.h ../prefs.h
-main.o: main.c ../config.h gtkclist.h ../getopt.h main.h ../globals.h \
-	../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	../print.h ../timestamp.h ../capture.h ../summary.h menu.h \
-	../menu.h filter_prefs.h prefs_dlg.h ../prefs.h ../column.h \
-	../resolv.h ../follow.h ../util.h ../simple_dialog.h \
-	proto_draw.h keys.h packet_win.h gtkglobals.h ../plugins.h
-menu.o: menu.c ../config.h gtkclist.h ../menu.h main.h ../globals.h \
-	../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	../print.h ../getopt.h ../timestamp.h menu.h capture_dlg.h \
-	color_dlg.h file_dlg.h filter_prefs.h find_dlg.h goto_dlg.h \
-	summary_dlg.h display_opts.h prefs_dlg.h ../prefs.h \
-	packet_win.h ../follow.h keys.h ../plugins.h
-packet_win.o: packet_win.c ../config.h gtkclist.h ../getopt.h main.h \
-	../globals.h ../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h \
-	../tvbuff.h ../exceptions.h ../except.h ../file.h ../dfilter.h \
-	colors.h ../print.h ../timestamp.h ../capture.h ../summary.h \
-	menu.h ../menu.h prefs_dlg.h ../prefs.h ../column.h ../resolv.h \
-	../follow.h ../util.h packet_win.h ../simple_dialog.h \
-	../ui_util.h proto_draw.h keys.h gtkglobals.h ../plugins.h
-plugins_dlg.o: plugins_dlg.c ../config.h ../globals.h ../packet.h \
-	../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	gtkclist.h ../print.h ../getopt.h ../timestamp.h ../plugins.h \
-	keys.h dlg_utils.h prefs_dlg.h ../prefs.h ../simple_dialog.h
-prefs_dlg.o: prefs_dlg.c ../config.h gtkclist.h ../getopt.h main.h \
-	../globals.h ../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h \
-	../tvbuff.h ../exceptions.h ../except.h ../file.h ../dfilter.h \
-	colors.h ../print.h ../timestamp.h ../prefs.h column_prefs.h \
-	prefs_dlg.h print_prefs.h stream_prefs.h gui_prefs.h ../util.h \
-	../ui_util.h dlg_utils.h ../simple_dialog.h ../prefs-int.h
-print_dlg.o: print_dlg.c ../config.h ../globals.h ../packet.h \
-	../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	gtkclist.h ../print.h ../getopt.h ../timestamp.h keys.h \
-	prefs_dlg.h ../prefs.h ../simple_dialog.h ../ui_util.h \
-	dlg_utils.h
-print_prefs.o: print_prefs.c ../config.h ../globals.h ../packet.h \
-	../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	gtkclist.h ../print.h ../getopt.h ../timestamp.h print_prefs.h \
-	keys.h prefs_dlg.h ../prefs.h ../util.h ../ui_util.h \
-	dlg_utils.h
-progress_dlg.o: progress_dlg.c ../config.h gtkglobals.h gtkclist.h \
-	dlg_utils.h ../ui_util.h
-proto_draw.o: proto_draw.c ../config.h gtkclist.h main.h ../globals.h \
-	../packet.h ../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	../print.h ../getopt.h ../timestamp.h ../util.h ../prefs.h \
-	proto_draw.h gtkglobals.h
-simple_dialog.o: simple_dialog.c ../config.h gtkclist.h gtkglobals.h \
-	../simple_dialog.h dlg_utils.h ../image/icon-excl.xpm \
-	../image/icon-ethereal.xpm
-stream_prefs.o: stream_prefs.c ../config.h ../globals.h ../packet.h \
-	../wiretap/wtap.h ../proto.h ../ipv4.h ../tvbuff.h \
-	../exceptions.h ../except.h ../file.h ../dfilter.h colors.h \
-	gtkclist.h ../print.h ../getopt.h ../timestamp.h stream_prefs.h \
-	keys.h prefs_dlg.h ../prefs.h ../util.h
-summary_dlg.o: summary_dlg.c ../config.h gtkclist.h ../wiretap/wtap.h \
-	../summary.h summary_dlg.h dlg_utils.h
-ui_util.o: ui_util.c ../config.h gtkclist.h gtkglobals.h ../ui_util.h
 
+DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
+
+-include $(DEP_FILES)
+
+mostlyclean-depend:
+
+clean-depend:
+
+distclean-depend:
+	-rm -rf .deps
+
+maintainer-clean-depend:
+
+%.o: %.c
+	@echo '$(COMPILE) -c $<'; \
+	$(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+	@-cp .deps/$(*F).pp .deps/$(*F).P; \
+	tr ' ' '\012' < .deps/$(*F).pp \
+	  | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+	    >> .deps/$(*F).P; \
+	rm .deps/$(*F).pp
+
+%.lo: %.c
+	@echo '$(LTCOMPILE) -c $<'; \
+	$(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+	@-sed -e 's/^\([^:]*\)\.o[ 	]*:/\1.lo \1.o :/' \
+	  < .deps/$(*F).pp > .deps/$(*F).P; \
+	tr ' ' '\012' < .deps/$(*F).pp \
+	  | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+	    >> .deps/$(*F).P; \
+	rm -f .deps/$(*F).pp
 info-am:
 info: info-am
 dvi-am:
@@ -438,27 +361,27 @@
 
 maintainer-clean-generic:
 mostlyclean-am:  mostlyclean-noinstLIBRARIES mostlyclean-compile \
-		mostlyclean-libtool mostlyclean-tags \
+		mostlyclean-libtool mostlyclean-tags mostlyclean-depend \
 		mostlyclean-generic
 
 mostlyclean: mostlyclean-am
 
 clean-am:  clean-noinstLIBRARIES clean-compile clean-libtool clean-tags \
-		clean-generic mostlyclean-am
+		clean-depend clean-generic mostlyclean-am
 
 clean: clean-am
 
 distclean-am:  distclean-noinstLIBRARIES distclean-compile \
-		distclean-libtool distclean-tags distclean-generic \
-		clean-am
+		distclean-libtool distclean-tags distclean-depend \
+		distclean-generic clean-am
 	-rm -f libtool
 
 distclean: distclean-am
 
 maintainer-clean-am:  maintainer-clean-noinstLIBRARIES \
 		maintainer-clean-compile maintainer-clean-libtool \
-		maintainer-clean-tags maintainer-clean-generic \
-		distclean-am
+		maintainer-clean-tags maintainer-clean-depend \
+		maintainer-clean-generic distclean-am
 	@echo "This command is intended for maintainers to use;"
 	@echo "it deletes files that may require special tools to rebuild."
 
@@ -469,12 +392,14 @@
 mostlyclean-compile distclean-compile clean-compile \
 maintainer-clean-compile mostlyclean-libtool distclean-libtool \
 clean-libtool maintainer-clean-libtool tags mostlyclean-tags \
-distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
-dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
-install-exec install-data-am install-data install-am install \
-uninstall-am uninstall all-redirect all-am all installdirs \
-mostlyclean-generic distclean-generic clean-generic \
-maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+distclean-tags clean-tags maintainer-clean-tags distdir \
+mostlyclean-depend distclean-depend clean-depend \
+maintainer-clean-depend info-am info dvi-am dvi check check-am \
+installcheck-am installcheck install-exec-am install-exec \
+install-data-am install-data install-am install uninstall-am uninstall \
+all-redirect all-am all installdirs mostlyclean-generic \
+distclean-generic clean-generic maintainer-clean-generic clean \
+mostlyclean distclean maintainer-clean
 
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
diff -r -u -N -I $Id: --exclude=CVS ethereal-0.8.11/gtk/menu.c ethereal/gtk/menu.c
--- ethereal-0.8.11/gtk/menu.c	Thu Aug  3 03:56:53 2000
+++ ethereal/gtk/menu.c	Sat Feb 24 20:45:08 2001
@@ -56,6 +56,7 @@
 #include "follow.h"
 #include "keys.h"
 #include "plugins.h"
+#include "tcp_graph.h"
 
 GtkWidget *popup_menu_object;
 
@@ -128,6 +129,11 @@
 #endif
   {"/Tools/_Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
 /*  {"/Tools/Graph", NULL, NULL, 0, NULL}, future use */
+  {"/_Tools/TCP Stream Analysis", NULL, NULL, 0, "<Branch>" },
+  {"/_Tools/TCP Stream Analysis/Time-Sequence Graph (Stevens)", NULL, GTK_MENU_FUNC (tcp_graph), 0, NULL},
+  {"/_Tools/TCP Stream Analysis/Time-Sequence Graph (tcptrace)", NULL, GTK_MENU_FUNC (tcp_graph), 1, NULL},
+  {"/_Tools/TCP Stream Analysis/Throughput Graph", NULL, GTK_MENU_FUNC (tcp_graph), 2, NULL},
+  {"/_Tools/TCP Stream Analysis/RTT Graph", NULL, GTK_MENU_FUNC (tcp_graph), 3, NULL},
   {"/Tools/_Summary", NULL, GTK_MENU_FUNC(summary_open_cb), 0, NULL},
   {"/_Help", NULL, NULL, 0, "<LastBranch>" },
   {"/Help/_About Ethereal...", NULL, GTK_MENU_FUNC(about_ethereal), 0, NULL}
Binary files ethereal-0.8.11/gtk/menu.o and ethereal/gtk/menu.o differ
Binary files ethereal-0.8.11/gtk/packet_win.o and ethereal/gtk/packet_win.o differ
Binary files ethereal-0.8.11/gtk/plugins_dlg.o and ethereal/gtk/plugins_dlg.o differ
Binary files ethereal-0.8.11/gtk/prefs_dlg.o and ethereal/gtk/prefs_dlg.o differ
Binary files ethereal-0.8.11/gtk/print_dlg.o and ethereal/gtk/print_dlg.o differ
Binary files ethereal-0.8.11/gtk/print_prefs.o and ethereal/gtk/print_prefs.o differ
Binary files ethereal-0.8.11/gtk/progress_dlg.o and ethereal/gtk/progress_dlg.o differ
Binary files ethereal-0.8.11/gtk/proto_draw.o and ethereal/gtk/proto_draw.o differ
Binary files ethereal-0.8.11/gtk/simple_dialog.o and ethereal/gtk/simple_dialog.o differ
Binary files ethereal-0.8.11/gtk/stream_prefs.o and ethereal/gtk/stream_prefs.o differ
Binary files ethereal-0.8.11/gtk/summary_dlg.o and ethereal/gtk/summary_dlg.o differ
diff -r -u -N -I $Id: --exclude=CVS ethereal-0.8.11/gtk/tcp_graph.c ethereal/gtk/tcp_graph.c
--- ethereal-0.8.11/gtk/tcp_graph.c	Thu Jan  1 01:00:00 1970
+++ ethereal/gtk/tcp_graph.c	Sat Feb 24 20:40:01 2001
@@ -0,0 +1,3494 @@
+
+/* $Id: tcp_graph.c,v 1.1 2001/02/24 19:40:01 pvl Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <math.h>			/* rint() */
+
+#include <net/ethernet.h>	/* struct ether_header & comp. */
+#include <netinet/ip.h>		/* struct iphdr & comp. */
+#include <netinet/tcp.h>	/* struct tcphdr & comp. */
+#include <netinet/in.h>		/* ntohs(), IPPROTO_TCP */
+#include <arpa/inet.h>		/* inet_ntoa() */
+#include <X11/keysym.h>
+
+#include "globals.h" 		/* cfile */
+#include "packet.h"			/* frame_data */
+#include "prefs_dlg.h"		/* prefs */
+#include "gtkglobals.h"		/* set_scrollbar_placement_srollw() and
+							 * remember_scrolled_window() */
+
+#define TXT_WIDTH	850
+#define TXT_HEIGHT	550
+
+/* for compare_headers() */
+/* segment went the same direction as the currently selected one */
+#define COMPARE_CURR_DIR	0
+#define COMPARE_ANY_DIR		1
+
+/* initalize_axis() */
+#define AXIS_HORIZONTAL		0
+#define AXIS_VERTICAL		1
+
+struct segment {
+	struct segment *next;
+	guint32 num;
+	guint32 rel_secs;
+	guint32 rel_usecs;
+	guint32 abs_secs;
+	guint32 abs_usecs;
+	struct iphdr iphdr;
+	struct tcphdr tcphdr;
+	int data;				/* amount of data in this segment */
+};
+
+struct rect {
+	double x, y, width, height;
+};
+
+struct line {
+	double x1, y1, x2, y2;
+};
+
+struct irect {
+	int x, y, width, height;
+};
+
+struct ipoint {
+	int x, y;
+};
+
+typedef enum {
+	ELMT_NONE=0,
+	ELMT_RECT=1,
+	ELMT_LINE=2,
+	ELMT_ARC=3
+} ElementType;
+
+struct rect_params {
+	struct rect dim;
+	gint filled;
+};
+
+struct line_params {
+	struct line dim;
+};
+
+struct arc_params {
+	struct rect dim;
+	gint filled;
+	gint angle1, angle2;
+};
+
+struct element {
+	ElementType type;
+	GdkGC *gc;
+	struct segment *parent;
+	union {
+		struct arc_params arc;
+		struct rect_params rect;
+		struct line_params line;
+	} p;
+};
+
+struct element_list {
+	struct element_list *next;
+	struct element *elements;
+};
+
+struct axis {
+	struct graph *g;			/* which graph we belong to */
+	GtkWidget *drawing_area;
+	GdkPixmap *pixmap[2];
+	int displayed;
+#define AXIS_ORIENTATION	1 << 0
+	int flags;
+	/* dim and orig (relative to origin of window) of axis' pixmap */
+	struct irect p;
+	/* dim and orig (relative to origin of axis' pixmap) of scale itself */
+	struct irect s;
+	gdouble min, max;
+	gdouble major, minor;		/* major and minor ticks */
+	char **label;
+};
+
+#define HAXIS_INIT_HEIGHT	70
+#define VAXIS_INIT_WIDTH	100
+#define TITLEBAR_HEIGHT		50
+#define RMARGIN_WIDTH	30
+
+struct style_tseq_tcptrace {
+	GdkGC *gc_seq;
+	GdkGC *gc_ack[2];
+	int flags;
+};
+
+struct style_tseq_stevens {
+	int seq_width;
+	int seq_height;
+	int flags;
+};
+
+struct style_tput {
+	int width, height;
+	int nsegs;
+	int flags;
+};
+
+struct style_rtt {
+	int width, height;
+	int flags;
+};
+
+/* style flags */
+#define SEQ_ORIGIN			0x1
+/* show absolute sequence numbers (not differences from isn) */
+#define SEQ_ORIGIN_ZERO		0x1
+#define SEQ_ORIGIN_ISN		0x0
+#define TIME_ORIGIN			0x10
+/* show time from beginning of capture as opposed to time from beginning
+ * of the connection */
+#define TIME_ORIGIN_CAP		0x10
+#define TIME_ORIGIN_CONN	0x0
+
+/* this is used by rtt module only */
+struct unack {
+	struct unack *next;
+	double time;
+	unsigned int seqno;
+};
+
+struct cross {
+	int x, y;
+	int draw;			/* indicates whether we should draw cross at all */
+	int erase_needed;
+	GtkToggleButton *on_toggle;
+	GtkToggleButton *off_toggle;
+};
+
+struct bounds {
+	double x0, y0, width, height;
+};
+
+struct zoom {
+	double x, y;
+};
+
+struct zooms {
+	double x, y;
+	double step_x, step_y;
+	struct zoom initial;
+#define ZOOM_OUT				(1 << 0)
+#define ZOOM_HLOCK				(1 << 1)
+#define ZOOM_VLOCK				(1 << 2)
+#define ZOOM_STEPS_SAME			(1 << 3)
+#define ZOOM_STEPS_KEEP_RATIO	(1 << 4)
+	int flags;
+	/* unfortunately, we need them both because gtk_toggle_button_set_active ()
+	 * with second argument FALSE doesn't do anything, somehow */
+	struct {
+		GtkToggleButton *in_toggle;
+		GtkToggleButton *out_toggle;
+		GtkEntry *h_zoom;
+		GtkEntry *v_zoom;
+		GtkSpinButton *h_step;
+		GtkSpinButton *v_step;
+	} widget;
+};
+
+struct grab {
+	int grabbed;
+	int x, y;
+};
+
+struct magnify {
+	int active;
+	int x, y;
+	struct ipoint offset;
+	int width, height;
+	struct zoom zoom;
+	struct graph *g;
+#define MAGZOOMS_SAME		(1 << 0)
+#define MAGZOOMS_SAME_RATIO	(1 << 1)
+#define MAGZOOMS_IGNORE		(1 << 31)
+	int flags;
+	struct {
+		GtkSpinButton *h_zoom, *v_zoom;
+	} widget;
+};
+
+struct graph {
+	struct graph *next;
+#define GRAPH_TSEQ_STEVENS	0
+#define GRAPH_TSEQ_TCPTRACE	1
+#define GRAPH_THROUGHPUT	2
+#define GRAPH_RTT			3
+	int type;
+#define GRAPH_DESTROYED				(1 << 0)
+#define GRAPH_INIT_ON_TYPE_CHANGE	(1 << 1)
+	int flags;
+	GtkWidget *toplevel;	/* keypress handler needs this */
+	GtkWidget *drawing_area;
+	GtkWidget *text;		/* text widget for seg list - probably temporary */
+	GdkFont *font;			/* font used for annotations etc. */
+	GdkGC *fg_gc;
+	GdkGC *bg_gc;
+	GdkPixmap *title_pixmap;
+	GdkPixmap *pixmap[2];
+	int displayed;			/* which of both pixmaps is on screen right now */
+	struct {
+		GtkWidget *control_panel;
+		/* this belongs to style structs of graph types that make use of it */
+		GtkToggleButton *time_orig_conn, *seq_orig_isn;
+	} gui;
+	char **title;
+	/* Next 4 attribs describe the graph in natural units, before any scaling.
+	 * For example, if we want to display graph of TCP conversation that
+	 * started 112.309845 s after beginning of the capture and ran until
+	 * 479.093582 s, 237019 B went through the connection (in one direction)
+	 * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
+	 * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
+	struct bounds bounds;
+	/* dimensions and position of the graph, both expressed already in pixels.
+	 * x and y give the position of upper left corner of the graph relative
+	 * to origin of the graph window, size is basically bounds*zoom */
+	struct irect geom;
+	/* viewport (=graph window area which is reserved for graph itself), its
+	 * size and position relative to origin of the graph window */
+	struct irect wp;
+	struct grab grab;
+	/* If we need to display 237019 sequence numbers (=bytes) onto say 500
+	 * pixels, we have to scale the graph down by factor of 0.002109. This
+	 * number would be zoom.y. Obviously, both directions have separate zooms.*/
+	struct zooms zoom;
+	struct cross cross;
+	struct magnify magnify;
+	struct axis *x_axis, *y_axis;
+	struct segment *segments;
+	struct segment *current;
+	struct element_list *elists;		/* element lists */
+	union {
+		struct style_tseq_stevens tseq_stevens;
+		struct style_tseq_tcptrace tseq_tcptrace;
+		struct style_tput tput;
+		struct style_rtt rtt;
+	} s;
+};
+
+static struct graph *graphs = NULL;
+static GdkGC *xor_gc = NULL;
+
+#define debug(section) if (debugging & section)
+/* print function entry points */
+#define DBS_FENTRY			(1 << 0)
+#define DBS_AXES_TICKS		(1 << 1)
+#define DBS_AXES_DRAWING	(1 << 2)
+#define DBS_GRAPH_DRAWING	(1 << 3)
+#define DBS_TPUT_ELMTS		(1 << 4)
+//int debugging = DBS_FENTRY;
+int debugging = 0;
+//int debugging = DBS_AXES_TICKS;
+//int debugging = DBS_AXES_DRAWING;
+//int debugging = DBS_GRAPH_DRAWING;
+//int debugging = DBS_TPUT_ELMTS;
+
+static void create_gui (struct graph * );
+static void create_text_widget (struct graph * );
+static void display_text (struct graph * );
+static void create_drawing_area (struct graph * );
+static void control_panel_create (struct graph * );
+static GtkWidget *control_panel_create_zoom_group (struct graph * );
+static GtkWidget *control_panel_create_magnify_group (struct graph * );
+static GtkWidget *control_panel_create_cross_group (struct graph * );
+static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
+static GtkWidget *control_panel_create_graph_type_group (struct graph * );
+static void callback_toplevel_destroy (GtkWidget * , gpointer );
+static void callback_close (GtkWidget * , gpointer );
+static void callback_time_origin (GtkWidget * , gpointer );
+static void callback_seq_origin (GtkWidget * , gpointer );
+static void callback_zoomlock_h (GtkWidget * , gpointer );
+static void callback_zoomlock_v (GtkWidget * , gpointer );
+static void callback_zoom_inout (GtkWidget * , gpointer );
+static void callback_zoom_step (GtkWidget * , gpointer );
+static void callback_zoom_flags (GtkWidget * , gpointer );
+static void callback_cross_on_off (GtkWidget * , gpointer );
+static void callback_mag_width (GtkWidget * , gpointer );
+static void callback_mag_height (GtkWidget * , gpointer );
+static void callback_mag_x (GtkWidget * , gpointer );
+static void callback_mag_y (GtkWidget * , gpointer );
+static void callback_mag_zoom (GtkWidget * , gpointer );
+static void callback_mag_flags (GtkWidget * , gpointer );
+static void callback_graph_type (GtkWidget * , gpointer );
+static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
+static void callback_create_help (GtkWidget * , gpointer );
+static void callback_close_help (GtkWidget * , gpointer );
+static void update_zoom_spins (struct graph * );
+static int get_headers (char * , struct segment * );
+static int compare_headers (struct segment * , struct segment * , int );
+static int get_num_dsegs (struct graph * );
+static int get_num_acks (struct graph * );
+static void graph_type_dependent_initialize (struct graph * );
+static void graph_put (struct graph * );
+static struct graph *graph_new (void);
+static void graph_destroy (struct graph * );
+static void graph_initialize_values (struct graph * );
+static void graph_init_sequence (struct graph * );
+static void draw_element_line (struct graph * , struct element * );
+static void draw_element_arc (struct graph * , struct element * );
+static void graph_display (struct graph * );
+static void graph_pixmaps_create (struct graph * );
+static void graph_pixmaps_switch (struct graph * );
+static void graph_pixmap_draw (struct graph * );
+static void graph_pixmap_display (struct graph * );
+static void graph_element_lists_make (struct graph * );
+static void graph_element_lists_free (struct graph * );
+static void graph_element_lists_initialize (struct graph * );
+static void graph_title_pixmap_create (struct graph * );
+static void graph_title_pixmap_draw (struct graph * );
+static void graph_title_pixmap_display (struct graph * );
+static void graph_segment_list_get (struct graph * );
+static void graph_segment_list_free (struct graph * );
+static void graph_select_segment (struct graph * , int , int );
+static int line_detect_collision (struct element * , int , int );
+static int arc_detect_collision (struct element * , int , int );
+static void update_packet_list (int );
+static void axis_pixmaps_create (struct axis * );
+static void axis_pixmaps_switch (struct axis * );
+static void axis_display (struct axis * );
+static void v_axis_pixmap_draw (struct axis * );
+static void h_axis_pixmap_draw (struct axis * );
+static void axis_pixmap_display (struct axis * );
+static void axis_compute_ticks (struct axis * , double , double , int );
+static double axis_zoom_get (struct axis * , int );
+static void axis_ticks_up (int * , int * );
+static void axis_ticks_down (int * , int * );
+static void axis_destroy (struct axis * );
+static int get_label_dim (struct axis * , int , double );
+static void toggle_time_origin (struct graph * );
+static void toggle_seq_origin (struct graph * );
+static void cross_xor (struct graph * , int , int );
+static void cross_draw (struct graph * , int , int );
+static void cross_erase (struct graph * );
+static void magnify_create (struct graph * , int , int );
+static void magnify_move (struct graph * , int , int );
+static void magnify_destroy (struct graph * );
+static void magnify_draw (struct graph * );
+static void magnify_get_geom (struct graph * , int , int );
+static gint configure_event (GtkWidget * , GdkEventConfigure * );
+static gint expose_event (GtkWidget * , GdkEventExpose * );
+static gint button_press_event (GtkWidget * , GdkEventButton * );
+static gint button_release_event (GtkWidget * , GdkEventButton * );
+static gint motion_notify_event (GtkWidget * , GdkEventMotion * );
+static gint key_press_event (GtkWidget * , GdkEventKey * );
+static gint key_release_event (GtkWidget * , GdkEventKey * );
+static gint leave_notify_event (GtkWidget * , GdkEventCrossing * );
+static gint enter_notify_event (GtkWidget * , GdkEventCrossing * );
+static void tseq_stevens_initialize (struct graph * );
+static void tseq_stevens_get_bounds (struct graph * );
+static void tseq_stevens_read_config (struct graph * );
+static void tseq_stevens_make_elmtlist (struct graph * );
+static void tseq_stevens_toggle_seq_origin (struct graph * );
+static void tseq_stevens_toggle_time_origin (struct graph * );
+static void tseq_tcptrace_read_config (struct graph * );
+static void tseq_tcptrace_make_elmtlist (struct graph * );
+static void tseq_tcptrace_toggle_seq_origin (struct graph * );
+static void tseq_tcptrace_toggle_time_origin (struct graph * );
+static void tput_initialize (struct graph * );
+static void tput_read_config (struct graph * );
+static void tput_make_elmtlist (struct graph * );
+static void tput_toggle_time_origin (struct graph * );
+static void rtt_read_config (struct graph * );
+static void rtt_initialize (struct graph * );
+static int rtt_is_retrans (struct unack * , unsigned int );
+static struct unack *rtt_get_new_unack (double , unsigned int );
+static void rtt_put_unack_on_list (struct unack ** , struct unack * );
+static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
+static void rtt_make_elmtlist (struct graph * );
+static void rtt_toggle_seq_origin (struct graph * );
+
+static char helptext[] =
+"Here's what you can do:\n
+- Left Mouse Button moves the graph (if zoomed in)
+- <ctrl>-Left Mouse Button displays a portion of graph magnified
+- Middle Mouse Button zooms in
+- <shift>-Middle Button zooms out
+- Right Mouse Button selects segment in ethereal's packet list
+- Space toggles crosshairs\n
+";
+
+void tcp_graph (GtkWidget *w, gpointer data, guint graph_type)
+{
+	struct segment current;
+	struct graph *g;
+
+	debug(DBS_FENTRY) puts ("tcp_graph()");
+
+	if (! (g = graph_new()))
+		return;
+
+	graph_initialize_values (g);
+	graph_put (g);
+
+	g->type = graph_type;
+	if (!get_headers (cfile.pd, &current)) {
+		/* currently selected packet is neither TCP over IP over Ethernet II
+		 * nor TCP over IP alone (= IP over PPP) - should display some
+		 * kind of warning dialog */
+		printf ("packet selected is not a TCP segment\n");
+		return;
+	}
+
+	graph_segment_list_get(g);
+	create_gui(g);
+//	display_text(g);
+	graph_init_sequence(g);
+}
+
+static void create_gui (struct graph *g)
+{
+	debug(DBS_FENTRY) puts ("create_gui()");
+//	create_text_widget(g);
+	control_panel_create (g);
+	create_drawing_area(g);
+}
+
+static void create_text_widget (struct graph *g)
+{
+	GtkWidget *streamwindow, *txt_scrollw, *box;
+
+	debug(DBS_FENTRY) puts ("create_text_widget()");
+	streamwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	gtk_widget_set_name (streamwindow, "Packet chain");
+	gtk_widget_set_usize (GTK_WIDGET (streamwindow), TXT_WIDTH, TXT_HEIGHT);
+	gtk_container_border_width (GTK_CONTAINER(streamwindow), 2);
+
+	box = gtk_vbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (streamwindow), box);
+	gtk_widget_show (box);
+
+	txt_scrollw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_box_pack_start (GTK_BOX (box), txt_scrollw, TRUE, TRUE, 0);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (txt_scrollw),
+					GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
+	set_scrollbar_placement_scrollw (txt_scrollw, prefs.gui_scrollbar_on_right);
+	remember_scrolled_window (txt_scrollw);
+	gtk_widget_show (txt_scrollw);
+
+	g->text = gtk_text_new (NULL, NULL);
+	gtk_text_set_editable (GTK_TEXT (g->text), FALSE);
+	gtk_container_add (GTK_CONTAINER (txt_scrollw), g->text);
+	gtk_widget_show (g->text);
+	gtk_widget_show (streamwindow);
+}
+
+static void display_text (struct graph *g)
+{
+	char *line[256];
+	struct segment *ptr;
+	double first_time, prev_time;
+	unsigned int isn_this=0, isn_opposite=0, seq_this_prev, seq_opposite_prev;
+	GdkColor color, *c;
+
+	debug(DBS_FENTRY) puts ("display_text()");
+	gdk_color_parse ("SlateGray", &color);
+	gtk_text_freeze (GTK_TEXT (g->text));
+	snprintf ((char * )line, 256, "%10s%15s%15s%15s%15s%15s%15s%10s\n",
+					"pkt num", "time", "delta first", "delta prev",
+					"seqno", "delta first", "delta prev", "data (B)");
+	gtk_text_insert (GTK_TEXT (g->text), g->font, NULL, NULL,
+							(const char *)line, -1);
+
+	first_time = g->segments->rel_secs + g->segments->rel_usecs/1000000.0;
+	prev_time = first_time;
+	/* we have to find Initial Sequence Number for both ends of connection */
+	for (ptr=g->segments; ptr; ptr=ptr->next) {
+		if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
+			isn_this = ntohl (ptr->tcphdr.seq);
+			break;
+		}
+	}
+	for (ptr=g->segments; ptr; ptr=ptr->next) {
+		if (!compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
+			isn_opposite = ntohl (ptr->tcphdr.seq);
+			break;
+		}
+	}
+	seq_this_prev = isn_this;
+	seq_opposite_prev = isn_opposite;
+	for (ptr=g->segments; ptr; ptr=ptr->next) {
+		double time=ptr->rel_secs + ptr->rel_usecs/1000000.0;
+		unsigned int seq = ntohl (ptr->tcphdr.seq);
+		int seq_delta_isn, seq_delta_prev;
+
+		if (compare_headers (g->current, ptr, COMPARE_CURR_DIR)) {
+			seq_delta_isn = seq - isn_this;
+			seq_delta_prev = seq - seq_this_prev;
+			seq_this_prev = seq;
+			c = NULL;
+		} else {
+			seq_delta_isn = seq - isn_opposite;
+			seq_delta_prev = seq - seq_opposite_prev;
+			seq_opposite_prev = seq;
+			c = &color;
+		}
+		snprintf ((char *)line, 256, "%10d%15.6f%15.6f%15.6f%15u%15d%15d%10u\n",
+						ptr->num, time, time-first_time, time-prev_time,
+						seq, seq_delta_isn, seq_delta_prev,
+						ntohs (ptr->iphdr.tot_len) - 4*ptr->iphdr.ihl -
+						4*ptr->tcphdr.doff);
+		gtk_text_insert (GTK_TEXT (g->text), g->font, c, NULL,
+						(const char * )line, -1);
+		prev_time = time;
+	}
+	gtk_text_thaw (GTK_TEXT (g->text));
+}
+
+static void create_drawing_area (struct graph *g)
+{
+	GdkColormap *colormap;
+	GdkColor color;
+	GtkWidget *frame, *box;
+
+	debug(DBS_FENTRY) puts ("create_drawing_area()");
+//	g->font = gdk_font_load ("-sony-fixed-medium-r-normal--16-150-75-75"
+//							"-c-80-iso8859-2");
+	g->font = gdk_font_load ("-biznet-fotinostypewriter-medium-r-normal-*-*-120"
+							"-*-*-m-*-iso8859-2");
+	gdk_font_ref (g->font);
+
+	g->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	gtk_widget_set_name (g->toplevel, "Test Graph");
+
+	/* Create the drawing area */
+	g->drawing_area = gtk_drawing_area_new ();
+	g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
+	gtk_drawing_area_size (GTK_DRAWING_AREA (g->drawing_area),
+					g->wp.width + g->wp.x + RMARGIN_WIDTH,
+					g->wp.height + g->wp.y + g->x_axis->s.height);
+	gtk_widget_show (g->drawing_area);
+
+	gtk_signal_connect (GTK_OBJECT (g->drawing_area), "expose_event",
+							(GtkSignalFunc )expose_event, NULL);
+//	gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event",
+//							(GtkSignalFunc )configure_event, NULL);
+	gtk_signal_connect (GTK_OBJECT (g->drawing_area), "motion_notify_event",
+                      	(GtkSignalFunc )motion_notify_event, NULL);
+	gtk_signal_connect (GTK_OBJECT (g->drawing_area), "button_press_event",
+                    	(GtkSignalFunc )button_press_event, NULL);
+	gtk_signal_connect (GTK_OBJECT (g->drawing_area), "button_release_event",
+                    	(GtkSignalFunc )button_release_event, NULL);
+	gtk_signal_connect (GTK_OBJECT (g->drawing_area), "leave_notify_event",
+                    	(GtkSignalFunc )leave_notify_event, NULL);
+	gtk_signal_connect (GTK_OBJECT (g->drawing_area), "enter_notify_event",
+                    	(GtkSignalFunc )enter_notify_event, NULL);
+	gtk_signal_connect (GTK_OBJECT (g->toplevel), "destroy",
+                    	(GtkSignalFunc )callback_toplevel_destroy, g);
+	/* why doesn't drawing area send key_press_signals? */
+	gtk_signal_connect (GTK_OBJECT (g->toplevel), "key_press_event",
+                    	(GtkSignalFunc )key_press_event, NULL);
+	gtk_signal_connect (GTK_OBJECT (g->toplevel), "key_release_event",
+                    	(GtkSignalFunc )key_release_event, NULL);
+	gtk_widget_set_events (g->toplevel,GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
+
+	gtk_widget_set_events (g->drawing_area, GDK_EXPOSURE_MASK
+									| GDK_LEAVE_NOTIFY_MASK
+									| GDK_ENTER_NOTIFY_MASK
+									| GDK_BUTTON_PRESS_MASK
+									| GDK_BUTTON_RELEASE_MASK
+									| GDK_POINTER_MOTION_MASK
+									| GDK_POINTER_MOTION_HINT_MASK);
+
+#if 0
+	frame = gtk_frame_new (NULL);
+	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
+	gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
+
+	box = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (box), g->gui.control_panel, FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
+	gtk_container_add (GTK_CONTAINER (g->toplevel), box);
+	gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
+	gtk_widget_show (frame);
+	gtk_widget_show (box);
+#endif
+
+	gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
+	gtk_widget_show (g->toplevel);
+
+	/* in case we didn't get what we asked for */
+	g->wp.width = GTK_WIDGET (g->drawing_area)->allocation.width -
+						g->wp.x - RMARGIN_WIDTH;
+	g->wp.height = GTK_WIDGET (g->drawing_area)->allocation.height -
+						g->wp.y - g->x_axis->s.height;
+
+	colormap = gdk_window_get_colormap (g->drawing_area->window);
+	if (!xor_gc) {
+		xor_gc = gdk_gc_new (g->drawing_area->window);
+		gdk_gc_set_function (xor_gc, GDK_XOR);
+		gdk_color_parse ("gray15", &color);
+		gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+		gdk_gc_set_foreground (xor_gc, &color);
+	}
+	g->fg_gc = gdk_gc_new (g->drawing_area->window);
+	g->bg_gc = gdk_gc_new (g->drawing_area->window);
+	gdk_color_parse ("white", &color);
+	gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+	gdk_gc_set_foreground (g->bg_gc, &color);
+
+	/* this is probably quite an ugly way to get rid of the first configure
+	 * event
+	 * immediatelly after gtk_widget_show (window) drawing_area gets a configure
+	 * event which is handled during the next return to gtk_main which is
+	 * probably the gdk_gc_new() call. configure handler calls
+	 * graph_element_lists_make() which is not good because the graph struct is
+	 * not fully set up yet - namely we're not sure about actual geometry
+	 * and we don't have the GC's at all. so we just postpone installation
+	 * of configure handler until we're ready to deal with it.
+	 *
+	 * !!! NEM�LO BY TO B�T NA KONCI graph_init_sequence()? !!! 
+	 *
+	 */
+	gtk_signal_connect (GTK_OBJECT(g->drawing_area),"configure_event",
+							(GtkSignalFunc )configure_event, NULL);
+
+//	puts ("exiting create_drawing_area()");
+}
+
+static void callback_toplevel_destroy (GtkWidget *widget, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	if (!(g->flags & GRAPH_DESTROYED)) {
+		g->flags |= GRAPH_DESTROYED;
+		graph_destroy ((struct graph * )data);
+	}
+}
+
+static void control_panel_create (struct graph *g)
+{
+	GtkWidget *toplevel;
+	GtkWidget *table;
+	GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
+	GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
+	GtkWidget *zoom_frame;
+	GtkWidget *zoom_lock_frame;
+	GtkWidget *cross_frame;
+	GtkWidget *help, *close, *button_box;
+	GtkWidget *mag_frame, *graph_type_frame;
+
+	debug(DBS_FENTRY) puts ("control_panel_create()");
+
+	/* time origin box */
+	time_orig_cap =
+			gtk_radio_button_new_with_label (NULL, "beginning of capture");
+	time_orig_conn = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (time_orig_cap)), "beginning of this TCP connection");
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
+	time_orig_box = gtk_vbox_new (TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
+	time_orig_frame = gtk_frame_new ("Time origin");
+	gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
+
+	/* sequence number origin group */
+	seq_orig_isn =
+			gtk_radio_button_new_with_label (NULL, "initial sequence number");
+	seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_group (
+			GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
+	seq_orig_box = gtk_vbox_new (TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
+	seq_orig_frame = gtk_frame_new ("Sequence number origin");
+	gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
+
+	g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
+	g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
+
+	zoom_frame = control_panel_create_zoom_group (g);
+	cross_frame = control_panel_create_cross_group (g);
+	zoom_lock_frame = control_panel_create_zoomlock_group (g);
+	mag_frame = control_panel_create_magnify_group (g);
+	graph_type_frame = control_panel_create_graph_type_group (g);
+
+	/* bottom buttons group */
+	help = gtk_button_new_with_label ("Help");
+	close = gtk_button_new_with_label ("Close");
+	button_box = gtk_hbox_new (TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (button_box), help, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (button_box), close, TRUE, TRUE, 0);
+
+	toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+//	gtk_signal_connect (GTK_OBJECT (toplevel), "delete_event",
+//					GTK_SIGNAL_FUNC (XXXXXXXXXXXXXXXXX), NULL);
+
+	table = gtk_table_new (8, 1,  FALSE);
+	gtk_container_add (GTK_CONTAINER (toplevel), table);
+
+	gtk_table_attach (GTK_TABLE (table), time_orig_frame, 0, 1, 0, 1,
+						GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+	gtk_table_attach (GTK_TABLE (table), seq_orig_frame, 0, 1, 1, 2,
+						GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+	gtk_table_attach (GTK_TABLE (table), graph_type_frame, 0, 1, 2, 3,
+						GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+	gtk_table_attach (GTK_TABLE (table), zoom_frame, 0, 1, 3, 4,
+						GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+	gtk_table_attach (GTK_TABLE (table), zoom_lock_frame, 0, 1, 4, 5,
+						GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+	gtk_table_attach (GTK_TABLE (table), mag_frame, 0, 1, 5, 6,
+						GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+	gtk_table_attach (GTK_TABLE (table), cross_frame, 0, 1, 6, 7,
+						GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+	gtk_table_attach (GTK_TABLE (table), button_box, 0, 1, 7, 8,
+						GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+	gtk_signal_connect (GTK_OBJECT (time_orig_conn), "toggled",
+                    	(GtkSignalFunc )callback_time_origin, g);
+	gtk_signal_connect (GTK_OBJECT (seq_orig_isn), "toggled",
+                    	(GtkSignalFunc )callback_seq_origin, g);
+	gtk_signal_connect (GTK_OBJECT (close), "clicked",
+                    	(GtkSignalFunc )callback_close, g);
+	gtk_signal_connect (GTK_OBJECT (help), "clicked",
+                    	(GtkSignalFunc )callback_create_help, g);
+
+//	gtk_widget_show_all (table);
+//	g->gui.control_panel = table;
+	gtk_widget_show_all (toplevel);
+	g->gui.control_panel = toplevel;
+}
+
+static void callback_close (GtkWidget *widget, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	if (!(g->flags & GRAPH_DESTROYED)) {
+		g->flags |= GRAPH_DESTROYED;
+		graph_destroy ((struct graph * )data);
+	}
+}
+
+static void callback_create_help (GtkWidget *widget, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+	GtkWidget *toplevel, *box, *text, *scroll, *close;
+
+	toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	gtk_widget_set_usize (toplevel, 500, 400);
+	box = gtk_vbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (toplevel), box);
+	scroll = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+						GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);
+	text = gtk_text_new (NULL, NULL);
+	gtk_text_set_editable (GTK_TEXT (text), FALSE);
+	gtk_text_set_line_wrap (GTK_TEXT (text), FALSE);
+	gtk_text_set_word_wrap (GTK_TEXT (text), FALSE);
+	gtk_text_insert (GTK_TEXT (text), g->font, NULL, NULL, helptext, -1);
+	gtk_container_add (GTK_CONTAINER (scroll), text);
+	close = gtk_button_new_with_label ("Close");
+	gtk_box_pack_start (GTK_BOX (box), close, FALSE, FALSE, 0);
+	gtk_signal_connect (GTK_OBJECT (close), "clicked",
+                    	(GtkSignalFunc )callback_close_help, toplevel);
+	
+	gtk_widget_show_all (toplevel);
+}
+
+static void callback_close_help (GtkWidget *widget, gpointer data)
+{
+	gtk_widget_destroy ((GtkWidget * )data);
+}
+
+static void callback_time_origin (GtkWidget *toggle, gpointer data)
+{
+	toggle_time_origin ((struct graph * )data);
+}
+
+static void callback_seq_origin (GtkWidget *toggle, gpointer data)
+{
+	toggle_seq_origin ((struct graph * )data);
+}
+
+static GtkWidget *control_panel_create_zoom_group (struct graph *g)
+{
+	GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
+	GtkAdjustment *zoom_h_adj, *zoom_v_adj;
+	GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
+	GtkWidget *zoom_v_step_label, *zoom_v_step;
+	GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
+	GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
+	GtkWidget *zoom_h_entry, *zoom_v_entry;
+	GtkWidget *zoom_h_label, *zoom_v_label;
+
+	zoom_in = gtk_radio_button_new_with_label (NULL, "in");
+	zoom_out = gtk_radio_button_new_with_label (
+					gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_in)), "out");
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
+	zoom_inout_box = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
+	gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
+
+	zoom_separator1 = gtk_hseparator_new ();
+
+	zoom_h_entry = gtk_entry_new ();
+	gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
+	gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
+	zoom_h_label = gtk_label_new ("Horizontal:");
+
+	zoom_v_entry = gtk_entry_new ();
+	gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
+	gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
+	zoom_v_label = gtk_label_new ("Vertical:");
+
+	g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
+	g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
+
+	zoom_table = gtk_table_new (2, 2,  FALSE);
+	gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+	zoom_separator2 = gtk_hseparator_new ();
+
+	zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0);
+	zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
+	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
+	zoom_h_step_label = gtk_label_new ("Horizontal step:");
+
+	zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new (1.2, 1.0, 5, 0.1, 1, 0);
+	zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
+	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
+	zoom_v_step_label = gtk_label_new ("Vertical step:");
+
+	g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
+	g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
+
+	zoom_same_toggle = gtk_check_button_new_with_label ("Keep them the same");
+	zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
+	gtk_object_set_data (GTK_OBJECT (zoom_same_toggle), "flag",
+						(gpointer )ZOOM_STEPS_SAME);
+	gtk_object_set_data (GTK_OBJECT (zoom_ratio_toggle), "flag",
+						(gpointer )ZOOM_STEPS_KEEP_RATIO);
+	gtk_signal_connect (GTK_OBJECT (zoom_same_toggle), "clicked",
+		               	(GtkSignalFunc )callback_zoom_flags, g);
+	gtk_signal_connect (GTK_OBJECT (zoom_ratio_toggle), "clicked",
+		               	(GtkSignalFunc )callback_zoom_flags, g);
+
+	zoom_step_table = gtk_table_new (4, 2,  FALSE);
+	gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+	zoom_box = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
+	zoom_frame = gtk_frame_new ("Zoom");
+	gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
+
+	gtk_object_set_data (GTK_OBJECT (zoom_h_step), "direction", (gpointer )0);
+	gtk_object_set_data (GTK_OBJECT (zoom_v_step), "direction", (gpointer )1);
+
+	gtk_signal_connect (GTK_OBJECT (zoom_in), "toggled",
+		               	(GtkSignalFunc )callback_zoom_inout, g);
+	gtk_signal_connect (GTK_OBJECT (zoom_h_step), "changed",
+		               	(GtkSignalFunc )callback_zoom_step, g);
+	gtk_signal_connect (GTK_OBJECT (zoom_v_step), "changed",
+		               	(GtkSignalFunc )callback_zoom_step, g);
+
+	g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
+	g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
+	return zoom_frame;
+}
+
+static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	if (GTK_TOGGLE_BUTTON (toggle)->active)
+		g->zoom.flags &= ~ZOOM_OUT;
+	else
+		g->zoom.flags |= ZOOM_OUT;
+}
+
+static void callback_zoom_step (GtkWidget *spin, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+	float value;
+	int direction;
+	double *zoom_this, *zoom_other;
+	GtkSpinButton *widget_this, *widget_other;
+	double old_this;
+
+	direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction");
+	value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
+
+	if (direction) {
+		zoom_this = &g->zoom.step_y;
+		zoom_other = &g->zoom.step_x;
+		widget_this = g->zoom.widget.v_step;
+		widget_other = g->zoom.widget.h_step;
+	} else {
+		zoom_this = &g->zoom.step_x;
+		zoom_other = &g->zoom.step_y;
+		widget_this = g->zoom.widget.h_step;
+		widget_other = g->zoom.widget.v_step;
+	}
+
+	old_this = *zoom_this;
+	*zoom_this = value;
+	if (g->zoom.flags & ZOOM_STEPS_SAME) {
+		*zoom_other = value;
+		gtk_spin_button_set_value (widget_other, *zoom_other);
+	} else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
+		double old_other = *zoom_other;
+		*zoom_other *= value / old_this;
+		if (*zoom_other < 1.0) {
+			*zoom_other = 1.0;
+			*zoom_this = old_this * 1.0 / old_other;
+			gtk_spin_button_set_value (widget_this, *zoom_this);
+		} else if (*zoom_other > 5.0) {
+			*zoom_other = 5.0;
+			*zoom_this = old_this * 5.0 / old_other;
+			gtk_spin_button_set_value (widget_this, *zoom_this);
+		}
+		gtk_spin_button_set_value (widget_other, *zoom_other);
+	}
+}
+
+static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+	int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag");
+
+	if (GTK_TOGGLE_BUTTON (toggle)->active)
+		g->zoom.flags |= flag;	
+	else
+		g->zoom.flags &= ~flag;	
+}
+
+static void update_zoom_spins (struct graph *g)
+{
+	char s[32];
+
+	snprintf (s, 32, "%.3f", g->zoom.x / g->zoom.initial.x);
+	gtk_entry_set_text (g->zoom.widget.h_zoom, s);
+	snprintf (s, 32, "%.3f", g->zoom.y / g->zoom.initial.y);
+	gtk_entry_set_text (g->zoom.widget.v_zoom, s);
+}
+
+static GtkWidget *control_panel_create_magnify_group (struct graph *g)
+{
+	GtkWidget *mag_width_label, *mag_width;
+	GtkWidget *mag_height_label, *mag_height;
+	GtkWidget *mag_x_label, *mag_x;
+	GtkWidget *mag_y_label, *mag_y;
+	GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
+	GtkWidget *mag_h_zoom_label, *mag_h_zoom;
+	GtkWidget *mag_v_zoom_label, *mag_v_zoom;
+	GtkWidget *mag_zoom_same, *mag_zoom_ratio;
+	GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
+	GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
+	GtkWidget *mag_box, *mag_frame;
+
+	mag_width_label = gtk_label_new ("Width:");
+	mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
+	mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
+
+	mag_height_label = gtk_label_new ("Height:");
+	mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
+	mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
+
+	mag_x_label = gtk_label_new ("X:");
+	mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
+	mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
+
+	mag_y_label = gtk_label_new ("Y:");
+	mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
+	mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
+
+	mag_wh_table = gtk_table_new (4, 2, FALSE);
+	gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+	gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+	mag_h_zoom_label = gtk_label_new ("Horizontal:");
+	mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0);
+	mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
+
+	mag_v_zoom_label = gtk_label_new ("Vertical:");
+	mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0,1.0,25.0,0.1,1,0);
+	mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
+
+	mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
+	mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
+
+	mag_zoom_table = gtk_table_new (4, 2, FALSE);
+	gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+	gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+	gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+	gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+	gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+	gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
+				GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+
+	mag_zoom_frame = gtk_frame_new ("Magnify zoom");
+	gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
+	gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
+
+	mag_box = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
+	mag_frame = gtk_frame_new ("Magnify");
+	gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
+
+	g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
+	g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
+	gtk_object_set_data (GTK_OBJECT (mag_h_zoom), "direction", (gpointer )0);
+	gtk_object_set_data (GTK_OBJECT (mag_v_zoom), "direction", (gpointer )1);
+	gtk_object_set_data (GTK_OBJECT (mag_zoom_same), "flag",
+										(gpointer )MAGZOOMS_SAME);
+	gtk_object_set_data (GTK_OBJECT (mag_zoom_ratio), "flag",
+										(gpointer )MAGZOOMS_SAME_RATIO);
+
+	gtk_signal_connect (GTK_OBJECT (mag_width), "changed",
+		               	(GtkSignalFunc )callback_mag_width, g);
+	gtk_signal_connect (GTK_OBJECT (mag_height), "changed",
+		               	(GtkSignalFunc )callback_mag_height, g);
+	gtk_signal_connect (GTK_OBJECT (mag_x), "changed",
+		               	(GtkSignalFunc )callback_mag_x, g);
+	gtk_signal_connect (GTK_OBJECT (mag_y), "changed",
+		               	(GtkSignalFunc )callback_mag_y, g);
+	gtk_signal_connect (GTK_OBJECT (mag_h_zoom), "changed",
+		               	(GtkSignalFunc )callback_mag_zoom, g);
+	gtk_signal_connect (GTK_OBJECT (mag_v_zoom), "changed",
+		               	(GtkSignalFunc )callback_mag_zoom, g);
+	gtk_signal_connect (GTK_OBJECT (mag_zoom_same), "clicked",
+		               	(GtkSignalFunc )callback_mag_flags, g);
+	gtk_signal_connect (GTK_OBJECT (mag_zoom_ratio), "clicked",
+		               	(GtkSignalFunc )callback_mag_flags, g);
+
+	return mag_frame;
+}
+
+static void callback_mag_width (GtkWidget *spin, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_height (GtkWidget *spin, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_x (GtkWidget *spin, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_y (GtkWidget *spin, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_zoom (GtkWidget *spin, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+	float value;
+	int direction;
+	double *zoom_this, *zoom_other;
+	GtkSpinButton *widget_this, *widget_other;
+	double old_this;
+
+	if (g->magnify.flags & MAGZOOMS_IGNORE) {
+		printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
+		g->magnify.flags &= ~MAGZOOMS_IGNORE;
+		return;
+	}
+	direction = (int )gtk_object_get_data (GTK_OBJECT (spin), "direction");
+	value = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (spin));
+
+	if (direction) {
+		zoom_this = &g->magnify.zoom.y;
+		zoom_other = &g->magnify.zoom.x;
+		widget_this = g->magnify.widget.v_zoom;
+		widget_other = g->magnify.widget.h_zoom;
+	} else {
+		zoom_this = &g->magnify.zoom.x;
+		zoom_other = &g->magnify.zoom.y;
+		widget_this = g->magnify.widget.h_zoom;
+		widget_other = g->magnify.widget.v_zoom;
+	}
+
+	old_this = *zoom_this;
+	*zoom_this = value;
+	if (g->magnify.flags & MAGZOOMS_SAME) {
+		*zoom_other = value;
+//		g->magnify.flags |= MAGZOOMS_IGNORE;
+		gtk_spin_button_set_value (widget_other, *zoom_other);
+	} else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
+		double old_other = *zoom_other;
+		*zoom_other *= value / old_this;
+		if (*zoom_other < 1.0) {
+			*zoom_other = 1.0;
+			*zoom_this = old_this * 1.0 / old_other;
+			// g->magnify.flags |= MAGZOOMS_IGNORE;
+			gtk_spin_button_set_value (widget_this, *zoom_this);
+		} else if (*zoom_other > 25.0) {
+			*zoom_other = 25.0;
+			*zoom_this = old_this * 25.0 / old_other;
+			// g->magnify.flags |= MAGZOOMS_IGNORE;
+			gtk_spin_button_set_value (widget_this, *zoom_this);
+		}
+		// g->magnify.flags |= MAGZOOMS_IGNORE;
+		gtk_spin_button_set_value (widget_other, *zoom_other);
+	}
+}
+
+static void callback_mag_flags (GtkWidget *toggle, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+	int flag = (int )gtk_object_get_data (GTK_OBJECT (toggle), "flag");
+
+	if (GTK_TOGGLE_BUTTON (toggle)->active)
+		g->magnify.flags |= flag;	
+	else
+		g->magnify.flags &= ~flag;	
+}
+
+static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
+{
+	GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
+	GtkWidget *zoom_lock_frame;
+
+	zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
+	zoom_lock_h = gtk_radio_button_new_with_label (
+					gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
+					"horizontal");
+	zoom_lock_v = gtk_radio_button_new_with_label (
+					gtk_radio_button_group (GTK_RADIO_BUTTON (zoom_lock_none)),
+					"vertical");
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
+	zoom_lock_box = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_none, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
+	zoom_lock_frame = gtk_frame_new ("Zoom lock:");
+	gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
+
+	gtk_signal_connect (GTK_OBJECT (zoom_lock_h), "toggled",
+                    	(GtkSignalFunc )callback_zoomlock_h, g);
+	gtk_signal_connect (GTK_OBJECT (zoom_lock_v), "toggled",
+                    	(GtkSignalFunc )callback_zoomlock_v, g);
+
+	return zoom_lock_frame;
+}
+
+static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	if (GTK_TOGGLE_BUTTON (toggle)->active)
+		g->zoom.flags |= ZOOM_HLOCK;	
+	else
+		g->zoom.flags &= ~ZOOM_HLOCK;	
+}
+
+static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	if (GTK_TOGGLE_BUTTON (toggle)->active)
+		g->zoom.flags |= ZOOM_VLOCK;	
+	else
+		g->zoom.flags &= ~ZOOM_VLOCK;	
+}
+
+static GtkWidget *control_panel_create_cross_group (struct graph *g)
+{
+	GtkWidget *cross_on, *cross_off, *cross_box, *cross_frame;
+
+	cross_off = gtk_radio_button_new_with_label (NULL, "off");
+	cross_on = gtk_radio_button_new_with_label (
+				gtk_radio_button_group (GTK_RADIO_BUTTON (cross_off)), "on");
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cross_off), TRUE);
+	cross_box = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (cross_box), cross_off, FALSE, FALSE, 10);
+	gtk_box_pack_start (GTK_BOX (cross_box), cross_on, FALSE, FALSE, 0);
+	cross_frame = gtk_frame_new ("Cross:");
+	gtk_container_add (GTK_CONTAINER (cross_frame), cross_box);
+
+	gtk_signal_connect (GTK_OBJECT (cross_on), "toggled",
+		               	(GtkSignalFunc )callback_cross_on_off, g);
+
+	g->cross.on_toggle = (GtkToggleButton * )cross_on;
+	g->cross.off_toggle = (GtkToggleButton * )cross_off;
+
+	return cross_frame;
+}
+static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
+{
+	struct graph *g = (struct graph * )data;
+
+	if (GTK_TOGGLE_BUTTON (toggle)->active) {
+		int x, y;
+		g->cross.draw = TRUE;
+		gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
+		cross_draw (g, x, y);
+	} else {
+		g->cross.draw = FALSE;
+		cross_erase (g);
+	}
+}
+
+static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
+{
+	GtkWidget *graph_tseqttrace, *graph_tseqstevens;
+	GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
+	GtkWidget *graph_frame;
+
+	graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
+	graph_tseqttrace = gtk_radio_button_new_with_label (
+					gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
+					"Time/Sequence (tcptrace-style)");
+	graph_tseqstevens = gtk_radio_button_new_with_label (
+					gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
+					"Time/Sequence (Stevens'-style)");
+	graph_rtt = gtk_radio_button_new_with_label (
+					gtk_radio_button_group (GTK_RADIO_BUTTON (graph_tput)),
+					"Round-trip Time");
+	switch (g->type) {
+	case GRAPH_TSEQ_STEVENS:
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
+		break;
+	case GRAPH_TSEQ_TCPTRACE:
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
+		break;
+	case GRAPH_THROUGHPUT:
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
+		break;
+	case GRAPH_RTT:
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
+		break;
+	}
+	graph_init = gtk_check_button_new_with_label ("Init on change");
+	graph_sep = gtk_hseparator_new ();
+	graph_box = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
+	graph_frame = gtk_frame_new ("Graph type:");
+	gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
+
+	gtk_object_set_data (GTK_OBJECT (graph_tseqstevens), "new-graph-type",
+							(gpointer )0);
+	gtk_object_set_data (GTK_OBJECT (graph_tseqttrace), "new-graph-type",
+							(gpointer )1);
+	gtk_object_set_data (GTK_OBJECT (graph_tput), "new-graph-type", 
+							(gpointer )2);
+	gtk_object_set_data (GTK_OBJECT (graph_rtt), "new-graph-type", 
+							(gpointer )3);
+
+	gtk_signal_connect (GTK_OBJECT (graph_tseqttrace), "toggled",
+	                   	(GtkSignalFunc )callback_graph_type, g);
+	gtk_signal_connect (GTK_OBJECT (graph_tseqstevens), "toggled",
+	                   	(GtkSignalFunc )callback_graph_type, g);
+	gtk_signal_connect (GTK_OBJECT (graph_tput), "toggled",
+	                   	(GtkSignalFunc )callback_graph_type, g);
+	gtk_signal_connect (GTK_OBJECT (graph_rtt), "toggled",
+	                   	(GtkSignalFunc )callback_graph_type, g);
+	gtk_signal_connect (GTK_OBJECT (graph_init), "toggled",
+	                   	(GtkSignalFunc )callback_graph_init_on_typechg, g);
+
+	return graph_frame;
+}
+
+static void callback_graph_type (GtkWidget *toggle, gpointer data)
+{
+	int old_type, new_type;
+	struct graph *g = (struct graph * )data;
+
+	new_type = (int )gtk_object_get_data (GTK_OBJECT (toggle),"new-graph-type");
+
+	if (!GTK_TOGGLE_BUTTON (toggle)->active)
+		return;
+
+	old_type = g->type;
+	g->type = new_type;
+
+	graph_element_lists_free (g);
+	graph_element_lists_initialize (g);
+
+	if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
+		/* throughput graph uses differently constructed segment list so we
+		 * need to recreate it */
+		graph_segment_list_free (g);
+		graph_segment_list_get (g);
+	}
+
+	if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
+		g->geom.width = g->wp.width;
+		g->geom.height = g->wp.height;
+		g->geom.x = g->wp.x;
+		g->geom.y = g->wp.y;
+	}
+	g->x_axis->min = g->y_axis->min = 0;
+	gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
+	gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
+	graph_init_sequence (g);
+}
+
+static void callback_graph_init_on_typechg (GtkWidget *toggle, gpointer data)
+{
+	((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
+}
+
+static struct graph *graph_new (void)
+{
+	struct graph *g;
+
+	g = (struct graph * )calloc (1, sizeof (struct graph));
+	graph_element_lists_initialize (g);
+
+	g->x_axis = (struct axis * )calloc (1, sizeof (struct axis));
+	g->y_axis = (struct axis * )calloc (1, sizeof (struct axis));
+	g->x_axis->g = g;
+	g->x_axis->flags = 0;
+	g->x_axis->flags |= AXIS_ORIENTATION;
+	g->x_axis->s.x = g->x_axis->s.y = 0;
+	g->x_axis->s.height = HAXIS_INIT_HEIGHT;
+	g->x_axis->p.x = VAXIS_INIT_WIDTH;
+	g->x_axis->p.height = HAXIS_INIT_HEIGHT;
+	g->y_axis->g = g;
+	g->y_axis->flags = 0;
+	g->y_axis->flags &= ~AXIS_ORIENTATION;
+	g->y_axis->p.x = g->y_axis->p.y = 0;
+	g->y_axis->p.width = VAXIS_INIT_WIDTH;
+	g->y_axis->s.x = 0;
+	g->y_axis->s.y = TITLEBAR_HEIGHT;
+	g->y_axis->s.width = VAXIS_INIT_WIDTH;
+
+	return g;
+}
+
+static void graph_initialize_values (struct graph *g)
+{
+	g->geom.width = g->wp.width = 750;
+	g->geom.height = g->wp.height = 550;
+	g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
+	g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
+	g->flags = 0;
+//	g->zoom.x = g->zoom.y = 1.0;
+	g->zoom.step_x = g->zoom.step_y = 1.2;
+	g->zoom.flags = 0;
+	g->cross.draw = g->cross.erase_needed = 0;
+	g->grab.grabbed = 0;
+	g->magnify.active = 0;
+	g->magnify.offset.x = g->magnify.offset.y = 0;
+	g->magnify.width = g->magnify.height = 250;
+	g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
+	g->magnify.flags = 0;
+}
+
+static void graph_put (struct graph *graph)
+{
+	struct graph *g;
+	if (graphs) {
+		for (g=graphs; g->next; g=g->next);
+		g->next = graph;
+	} else
+		graphs = graph;
+}
+
+static void graph_init_sequence (struct graph *g)
+{
+	debug(DBS_FENTRY) puts ("graph_init_sequence()");
+
+	graph_type_dependent_initialize (g);
+	g->zoom.initial.x = g->zoom.x;
+	g->zoom.initial.y = g->zoom.y;
+	graph_element_lists_make (g);
+	g->x_axis->s.width = g->wp.width;
+	g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
+	g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
+	g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
+	g->y_axis->s.height = g->wp.height;
+	g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
+	graph_pixmaps_create (g);
+	axis_pixmaps_create (g->y_axis);
+	axis_pixmaps_create (g->x_axis);
+	graph_title_pixmap_create (g);
+	graph_title_pixmap_draw (g);
+	graph_title_pixmap_display (g);
+	graph_display (g);
+	axis_display (g->y_axis);
+	axis_display (g->x_axis);
+}
+
+static void graph_type_dependent_initialize (struct graph *g)
+{
+	switch (g->type) {
+	case GRAPH_TSEQ_STEVENS:
+	case GRAPH_TSEQ_TCPTRACE:
+		tseq_stevens_initialize (g);
+		break;
+	case GRAPH_THROUGHPUT:
+		tput_initialize (g);
+		break;
+	case GRAPH_RTT:
+		rtt_initialize (g);
+		break;
+	default:
+		break;
+	}
+}
+
+static void graph_destroy (struct graph *g)
+{
+	struct graph *gtmp;
+	struct graph *p=NULL;
+//	struct graph *tmp;
+
+	debug(DBS_FENTRY) puts ("graph_destroy()");
+
+	for (gtmp=graphs; gtmp; p=gtmp, gtmp=gtmp->next)
+		if (gtmp == g)
+			break;
+
+	axis_destroy (g->x_axis);
+	axis_destroy (g->y_axis);
+//	gtk_widget_destroy (g->drawing_area);
+	gtk_widget_destroy (g->gui.control_panel);
+	gtk_widget_destroy (g->toplevel);
+//	gtk_widget_destroy (g->text);
+	gdk_gc_unref (g->fg_gc);
+	gdk_gc_unref (g->bg_gc);
+	gdk_font_unref (g->font);
+	gdk_pixmap_unref (g->pixmap[0]);
+	gdk_pixmap_unref (g->pixmap[1]);
+	free (g->x_axis);
+	free (g->y_axis);
+	free (g->title);
+	graph_segment_list_free (g);
+	graph_element_lists_free (g);
+//	for (tmp=graphs; tmp; tmp=tmp->next)
+//		printf ("%p next: %p\n", tmp, tmp->next);
+//	printf ("p=%p, g=%p, p->next=%p, g->next=%p\n",
+//									p, g, p ? p->next : NULL, g->next);
+	if (g==graphs)
+		graphs = g->next;
+	else
+		p->next = g->next;
+	free (g);
+//	for (tmp=graphs; tmp; tmp=tmp->next)
+//		printf ("%p next: %p\n", tmp, tmp->next);
+}
+
+/* here we collect all the external data we will ever need */
+static void graph_segment_list_get (struct graph *g)
+{
+	frame_data *ptr;
+	char pd[4096];
+	struct segment *segment=NULL, *last=NULL;
+	struct segment current;
+	int condition;
+
+	debug(DBS_FENTRY) puts ("graph_segment_list_get()");
+	get_headers (cfile.pd, &current);
+	if (g->type == GRAPH_THROUGHPUT)
+		condition = COMPARE_CURR_DIR;
+	else
+		condition = COMPARE_ANY_DIR;
+
+	for (ptr=cfile.plist; ptr; ptr=ptr->next) {
+		wtap_seek_read (cfile.wth, ptr->file_off, &cfile.pseudo_header,
+							pd, 4096);
+		if (!segment)
+			segment = (struct segment * )malloc (sizeof (struct segment));
+			if (!segment)
+				perror ("malloc failed");
+		if (!get_headers (pd, segment))
+			continue;	/* not TCP over IP over Ethernet II */
+		if (compare_headers (&current, segment, condition)) {
+			segment->next = NULL;
+			segment->num = ptr->num;
+			segment->rel_secs = ptr->rel_secs;
+			segment->rel_usecs = ptr->rel_usecs;
+			segment->abs_secs = ptr->abs_secs;
+			segment->abs_usecs = ptr->abs_usecs;
+			segment->data = ntohs (segment->iphdr.tot_len) -
+								4*segment->iphdr.ihl - 4*segment->tcphdr.doff;
+			if (g->segments) {
+				last->next = segment;
+			} else {
+				g->segments = segment;
+			}
+			last = segment;
+			if (ptr==cfile.current_frame)
+				g->current = segment;
+		}
+		segment = NULL;
+	}
+}
+
+static int get_headers (char *pd, struct segment *hdrs)
+{
+	struct ether_header *e;
+	struct iphdr *ip;
+	struct tcphdr *tcp;
+
+	e = (struct ether_header * )pd;
+	if (ntohs (e->ether_type) == ETHERTYPE_IP) {
+		ip = (struct iphdr * )((struct ether_header * )pd + 1);
+	} else if (((struct iphdr *)e)->protocol == IPPROTO_TCP) {
+		ip = (struct iphdr *)e;
+	} else {
+//		printf ("not IP over Ethernet II or PPP\n");
+		return FALSE;
+	}
+	if (ip->protocol != IPPROTO_TCP) {
+//		printf ("transport protocol not TCP: %#1x\n", ip->protocol);
+		return FALSE;
+	}
+	tcp = (struct tcphdr * )((char * )ip + 4*ip->ihl);
+
+	hdrs->iphdr = *ip;
+	hdrs->tcphdr = *tcp;
+	return TRUE;
+}
+
+static int compare_headers (struct segment *h1, struct segment *h2, int dir)
+{
+	if (dir == COMPARE_CURR_DIR)
+		return h1->iphdr.saddr == h2->iphdr.saddr &&
+			   h1->iphdr.daddr == h2->iphdr.daddr &&
+			   h1->tcphdr.source == h2->tcphdr.source &&
+			   h1->tcphdr.dest == h2->tcphdr.dest;
+	else
+		return (h1->iphdr.saddr == h2->iphdr.saddr &&
+			   h1->iphdr.daddr == h2->iphdr.daddr &&
+			   h1->tcphdr.source == h2->tcphdr.source &&
+			   h1->tcphdr.dest == h2->tcphdr.dest) ||
+			   (h1->iphdr.saddr == h2->iphdr.daddr &&
+			   h1->iphdr.daddr == h2->iphdr.saddr &&
+			   h1->tcphdr.source == h2->tcphdr.dest &&
+			   h1->tcphdr.dest == h2->tcphdr.source);
+}
+
+static void graph_segment_list_free (struct graph *g)
+{
+	struct segment *segment;
+
+	while (g->segments) {
+		segment = g->segments->next;
+		free (g->segments);
+		g->segments = segment;
+	}
+	g->segments = NULL;
+}
+
+static void graph_element_lists_initialize (struct graph *g)
+{
+	g->elists = (struct element_list *)calloc (1, sizeof (struct element_list));
+}
+
+static void graph_element_lists_make (struct graph *g)
+{
+	debug(DBS_FENTRY) puts ("graph_element_lists_make()");
+
+	switch (g->type) {
+	case GRAPH_TSEQ_STEVENS:
+		tseq_stevens_make_elmtlist (g);
+		break;
+	case GRAPH_TSEQ_TCPTRACE:
+		tseq_tcptrace_make_elmtlist (g);
+		break;
+	case GRAPH_THROUGHPUT:
+		tput_make_elmtlist (g);
+		break;
+	case GRAPH_RTT:
+		rtt_make_elmtlist (g);
+		break;
+	default:
+		printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
+		break;
+	}
+}
+
+static void graph_element_lists_free (struct graph *g)
+{
+	struct element_list *list, *next_list;
+
+//	for (list=g->elists; list; list=list->next)
+//		free (list->elements);
+//	while (g->elists->next) {
+//		list = g->elists->next->next;
+//		free (g->elists->next);
+//		g->elists->next = list;
+//	}
+
+	for (list=g->elists; list; list=next_list) {
+		free (list->elements);
+		next_list = list->next;
+		free (list);
+	}
+	g->elists = NULL;	/* just to make debugging easier */
+}
+
+static void graph_title_pixmap_create (struct graph *g)
+{
+	if (g->title_pixmap)
+		gdk_pixmap_unref (g->title_pixmap);
+
+	g->title_pixmap = gdk_pixmap_new (g->drawing_area->window,
+							g->x_axis->p.width, g->wp.y, -1);
+}
+
+static void graph_title_pixmap_draw (struct graph *g)
+{
+	int i;
+
+	gdk_draw_rectangle (g->title_pixmap, g->bg_gc, TRUE, 0, 0,
+							g->x_axis->p.width, g->wp.y);
+	for (i=0; g->title[i]; i++) {
+		gint w, h;
+		w = gdk_string_width (g->font, g->title[i]);
+		h = gdk_string_height (g->font, g->title[i]);
+		gdk_draw_string (g->title_pixmap, g->font, g->fg_gc,
+					g->wp.width/2 - w/2, 20+h + i*(h+3), g->title[i]);
+	}
+}
+
+static void graph_title_pixmap_display (struct graph *g)
+{
+	gdk_draw_pixmap (g->drawing_area->window, g->fg_gc, g->title_pixmap,
+						0, 0, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
+}
+
+static void graph_pixmaps_create (struct graph *g)
+{
+	debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
+
+	if (g->pixmap[0])
+		gdk_pixmap_unref (g->pixmap[0]);
+	if (g->pixmap[1])
+		gdk_pixmap_unref (g->pixmap[1]);
+
+	g->pixmap[0] = gdk_pixmap_new (g->drawing_area->window,
+									g->wp.width, g->wp.height, -1);
+	g->pixmap[1] = gdk_pixmap_new (g->drawing_area->window,
+									g->wp.width, g->wp.height, -1);
+
+	g->displayed = 0;
+}
+
+static void graph_display (struct graph *g)
+{
+	graph_pixmap_draw (g);
+	graph_pixmaps_switch (g);
+	graph_pixmap_display (g);
+}
+
+static void graph_pixmap_display (struct graph *g)
+{
+    gdk_draw_pixmap (g->drawing_area->window, g->fg_gc,
+					g->pixmap[g->displayed], 0, 0, g->wp.x, g->wp.y,
+					g->wp.width, g->wp.height);
+}
+
+static void graph_pixmaps_switch (struct graph *g)
+{
+	g->displayed = 1 ^ g->displayed;
+}
+
+static void graph_pixmap_draw (struct graph *g)
+{
+	struct element_list *list;
+	struct element *e;
+	int not_disp;
+
+	debug(DBS_FENTRY) puts ("graph_display()");
+	not_disp = 1 ^ g->displayed;
+
+	gdk_draw_rectangle (g->pixmap[not_disp], g->bg_gc, TRUE,
+							0, 0, g->wp.width, g->wp.height);
+
+	for (list=g->elists; list; list=list->next)
+		for (e=list->elements; e->type != ELMT_NONE; e++) {
+			switch (e->type) {
+			case ELMT_RECT:
+				break;
+			case ELMT_LINE:
+				draw_element_line (g, e);
+				break;
+			case ELMT_ARC:
+				draw_element_arc (g, e);
+				break;
+			default:
+				break;
+			}
+		}
+}
+
+static void draw_element_line (struct graph *g, struct element *e)
+{
+	int x1, x2, y1, y2;
+
+	debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
+				"seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
+				e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
+	x1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
+	x2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
+	y1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
+	y2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
+	if (x1 > x2) {
+		int tmp=x2;
+		x2=x1;
+		x1=tmp;
+	}
+	if (y1 > y2) {
+		int tmp=y2;
+		y2=y1;
+		y1=tmp;
+	}
+	if ((x1<0 && x2<0) || (x1>=g->wp.width && x2>=g->wp.width) ||
+				(y1<0 && y2<0) || (y1>=g->wp.height && y2>=g->wp.height)) {
+		debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
+									x1, y1, x2, y2);
+		return;
+	}
+	if (x2 > g->wp.width-1)
+		x2 = g->wp.width-1;
+	if (x1 < 0)
+		x1 = 0;
+	if (y2 > g->wp.height-1)
+		y2 = g->wp.height-1;
+	if (y1 < 0)
+		y1 = 0;
+	debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", x1, y1, x2,y2);
+	gdk_draw_line (g->pixmap[1^g->displayed], e->gc, x1, y1, x2, y2);
+}
+
+static void draw_element_arc (struct graph *g, struct element *e)
+{
+	int x1, x2, y1, y2;
+
+	x1 = (int )rint (e->p.arc.dim.x + g->geom.x - g->wp.x);
+	x2 = e->p.arc.dim.width;
+	y1 = (int )rint (g->geom.height-1 - e->p.arc.dim.y + g->geom.y - g->wp.y);
+	y2 = e->p.arc.dim.height;
+	if (x1<-x2 || x1>=g->wp.width || y1<-y2 || y1>=g->wp.height)
+		return;
+	debug(DBS_GRAPH_DRAWING) printf ("arc: (%d,%d)->(%d,%d)\n", x1, y1, x2, y2);
+	gdk_draw_arc (g->pixmap[1^g->displayed], e->gc, e->p.arc.filled, x1,
+					y1, x2, y2, e->p.arc.angle1, e->p.arc.angle2);
+}
+
+static void axis_pixmaps_create (struct axis *axis)
+{
+	debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
+	if (axis->pixmap[0])
+		gdk_pixmap_unref (axis->pixmap[0]);
+	if (axis->pixmap[1])
+		gdk_pixmap_unref (axis->pixmap[1]);
+
+	axis->pixmap[0] = gdk_pixmap_new (axis->drawing_area->window,
+							axis->p.width, axis->p.height, -1);
+	axis->pixmap[1] = gdk_pixmap_new (axis->drawing_area->window,
+							axis->p.width, axis->p.height, -1);
+
+	axis->displayed = 0;
+}
+
+static void axis_destroy (struct axis *axis)
+{
+	gdk_pixmap_unref (axis->pixmap[0]);
+	gdk_pixmap_unref (axis->pixmap[1]);
+	free (axis->label);
+}
+
+static void axis_display (struct axis *axis)
+{
+	if (axis->flags & AXIS_ORIENTATION)
+		h_axis_pixmap_draw (axis);
+	else
+		v_axis_pixmap_draw (axis);
+	axis_pixmaps_switch (axis);
+	axis_pixmap_display (axis);
+}
+
+static void v_axis_pixmap_draw (struct axis *axis)
+{
+	struct graph *g = axis->g;
+	int i;
+	double major_tick;
+	int not_disp, rdigits, offset, imin, imax;
+	double bottom, top, j, fl, corr;
+
+	debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
+	bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
+					(double )g->geom.height * g->bounds.height;
+	bottom += axis->min;
+	top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
+					(double )g->geom.height * g->bounds.height;
+	top += axis->min;
+	axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
+
+	j = axis->major - floor (axis->major);
+	for (rdigits=0; rdigits<=6; rdigits++) {
+		j *= 10;
+		if (j<=0.000001)
+			break;
+		j = j - floor (j);
+	}
+
+	not_disp = 1 ^ axis->displayed;
+	gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
+					axis->p.width, axis->p.height);
+	/* axis */
+	gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, axis->p.width - 1,
+			(axis->p.height-axis->s.height)/2.0, axis->s.width - 1,
+			axis->p.height);
+
+	offset = g->wp.y + (-g->geom.y);
+	fl = floor (axis->min / axis->major) * axis->major;
+	corr = rint ((axis->min - fl) * g->zoom.y);
+
+	/* major ticks */
+	major_tick = axis->major * g->zoom.y;
+	imin = (g->geom.height - offset + corr - g->wp.height) / major_tick + 1;
+	imax = (g->geom.height - offset + corr) / major_tick;
+	for (i=imin; i <= imax; i++) {
+		gint w, h;
+		char desc[32];
+		int y = g->geom.height-1 - (int )rint (i * major_tick) -
+						offset + corr + axis->s.y;
+
+		debug(DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->major + fl, y);
+		if (y < 0 || y > axis->p.height)
+			continue;
+		gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
+						axis->s.width - 15, y, axis->s.width - 1, y);
+		snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
+		w = gdk_string_width (g->font, desc);
+		h = gdk_string_height (g->font, desc);
+		gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
+					axis->s.width-15-4-w, y + h/2, desc);
+	}
+	/* minor ticks */
+	if (axis->minor) {
+		double minor_tick = axis->minor * g->zoom.y;
+ 		imin = (g->geom.height - offset + corr - g->wp.height)/minor_tick + 1;
+		imax = (g->geom.height - offset + corr) / minor_tick;
+		for (i=imin; i <= imax; i++) {
+			int y = g->geom.height-1 - (int )rint (i*minor_tick) -
+							offset + corr + axis->s.y;
+
+			debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
+			if (y > 0 && y < axis->p.height)
+				gdk_draw_line (axis->pixmap[not_disp], g->fg_gc,
+							axis->s.width - 8, y, axis->s.width - 1, y);
+		}
+	}
+	for (i=0; axis->label[i]; i++) {
+		gint w, h;
+		w = gdk_string_width (g->font, axis->label[i]);
+		h = gdk_string_height (g->font, axis->label[i]);
+		gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
+					(axis->p.width - w)/2 , TITLEBAR_HEIGHT-15 - i*(h+3),
+					axis->label[i]);
+	}
+}
+
+static void h_axis_pixmap_draw (struct axis *axis)
+{
+	struct graph *g = axis->g;
+	int i;
+	double major_tick, minor_tick;
+	int not_disp, rdigits, offset, imin, imax;
+	double left, right, j, fl, corr;
+
+	debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
+	left = (g->wp.x-g->geom.x) /
+					(double )g->geom.width * g->bounds.width;
+	left += axis->min;
+	right = (g->wp.x-g->geom.x+g->wp.width) /
+					(double )g->geom.width * g->bounds.width;
+	right += axis->min;
+	axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
+
+	j = axis->major - floor (axis->major);
+	for (rdigits=0; rdigits<=6; rdigits++) {
+		j *= 10;
+		if (j<=0.000001)
+			break;
+		j = j - floor (j);
+	}
+
+	not_disp = 1 ^ axis->displayed;
+	gdk_draw_rectangle (axis->pixmap[not_disp], g->bg_gc, TRUE, 0, 0,
+					axis->p.width, axis->p.height);
+	/* axis */
+	gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, 0, 0,	
+						axis->s.width + (axis->p.width-axis->s.width)/2.0, 0);
+	offset = g->wp.x - g->geom.x;
+
+	fl = floor (axis->min / axis->major) * axis->major;
+	corr = rint ((axis->min - fl) * g->zoom.x);
+
+	/* major ticks */
+	major_tick = axis->major*g->zoom.x;
+	imin = (offset + corr) / major_tick + 1;
+	imax = (offset + corr + axis->s.width) / major_tick;
+	for (i=imin; i <= imax; i++) {
+		char desc[32];
+		int w, h;
+		int x = (int )rint (i * major_tick) - offset - corr;
+
+//		printf ("%f @ %d\n", i*axis->major + fl, x);
+		if (x < 0 || x > axis->s.width)
+			continue;
+		gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 15);
+		snprintf (desc, 32, "%.*f", rdigits, i*axis->major + fl);
+		w = gdk_string_width (g->font, desc);
+		h = gdk_string_height (g->font, desc);
+		gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
+						x - w/2, 15+h+4, desc);
+	}
+	if (axis->minor > 0) {
+		/* minor ticks */
+		minor_tick = axis->minor*g->zoom.x;
+		imin = (offset + corr) / minor_tick + 1;
+		imax = (offset + corr + g->wp.width) / minor_tick;
+		for (i=imin; i <= imax; i++) {
+			int x = (int )rint (i * minor_tick) - offset - corr;
+			if (x > 0 && x < axis->s.width)
+				gdk_draw_line (axis->pixmap[not_disp], g->fg_gc, x, 0, x, 8);
+		}
+	}
+	for (i=0; axis->label[i]; i++) {
+		gint w, h;
+		w = gdk_string_width (g->font, axis->label[i]);
+		h = gdk_string_height (g->font, axis->label[i]);
+		gdk_draw_string (axis->pixmap[not_disp], g->font, g->fg_gc,
+					axis->s.width - w - 50, 15+2*h+15 + i*(h+3),
+					axis->label[i]);
+	}
+}
+
+static void axis_pixmaps_switch (struct axis *axis)
+{
+	axis->displayed = 1 ^ axis->displayed;
+}
+
+static void axis_pixmap_display (struct axis *axis)
+{
+	gdk_draw_pixmap (axis->drawing_area->window, axis->g->fg_gc,
+			axis->pixmap[axis->displayed], 0, 0, axis->p.x, axis->p.y,
+			axis->p.width, axis->p.height);
+}
+
+static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
+{
+	int i, j, ii, jj, ms;
+	double zoom, x, steps[3]={ 0.1, 0.5 };
+	int dim, check_needed, diminished;
+	double majthresh[2]={2.0, 3.0};
+
+	debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
+	debug(DBS_AXES_TICKS)
+		printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
+
+	zoom = axis_zoom_get (axis, dir);
+	x = xmax-x0;
+	for (i=-9; i<=12; i++) {
+		if (x / pow (10, i) < 1)
+			break;
+	}
+	--i;
+	ms = (int )(x / pow (10, i));
+
+	if (ms > 5) {
+		j = 0;
+		++i;
+	} else if (ms > 2)
+		j = 1;
+	else
+		j = 0;
+
+	axis->major = steps[j] * pow (10, i);
+
+	debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
+			" axis->major=%f\n", zoom, x, i, ms, j, axis->major);
+
+	/* let's compute minor ticks */
+	jj = j;
+	ii = i;
+	axis_ticks_down (&ii, &jj);
+	axis->minor = steps[jj] * pow (10, ii);
+	/* we don't want minors if they would be less than 10 pixels apart */
+	if (axis->minor*zoom < 10) {
+		debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
+					"axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
+		axis->minor = 0;
+	}
+
+	check_needed = TRUE;
+	diminished = FALSE;
+	while (check_needed) {
+		check_needed = FALSE;
+		dim = get_label_dim (axis, dir, xmax);
+		debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
+				" axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
+				axis->major, axis->minor, axis->major*zoom/dim,
+				axis->minor*zoom/dim);
+
+		/* corrections: if majors are less than majthresh[dir] times label
+	 	* dimension apart, we need to use bigger ones */
+		if (axis->major*zoom / dim < majthresh[dir]) {
+			axis_ticks_up (&ii, &jj);
+			axis->minor = axis->major;
+			axis_ticks_up (&i, &j);
+			axis->major = steps[j] * pow (10, i);
+			check_needed = TRUE;
+			debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
+										axis->major);
+		}
+		/* if minor ticks are bigger than majthresh[dir] times label dimension,
+		 * we could  promote them to majors as well */
+		if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
+			axis_ticks_down (&i, &j);
+			axis->major = axis->minor;
+			axis_ticks_down (&ii, &jj);
+			axis->minor = steps[jj] * pow (10, ii);
+			check_needed = TRUE;
+			diminished = TRUE;
+	
+			debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
+										axis->minor);
+	
+			if (axis->minor*zoom < 10) {
+				debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
+					"axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
+				axis->minor = 0;
+			}
+		}
+	}
+
+	debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
+							"axis->minor == %.1f\n", axis->major, axis->minor);
+}
+
+static void axis_ticks_up (int *i, int *j)
+{
+	(*j)++;
+	if (*j>1) {
+		(*i)++;
+		*j=0;
+	}
+}
+
+static void axis_ticks_down (int *i, int *j)
+{
+	(*j)--;
+	if (*j<0) {
+		(*i)--;
+		*j=1;
+	}
+}
+
+static int get_label_dim (struct axis *axis, int dir, double label)
+{
+	double y;
+	char str[32];
+	int rdigits, dim;
+
+	 /* First, let's compute how many digits to the right of radix
+	 * we need to print */
+	y = axis->major - floor (axis->major);
+	for (rdigits=0; rdigits<=6; rdigits++) {
+		y *= 10;
+		if (y<=0.000001)
+			break;
+		y = y - floor (y);
+	}
+	snprintf (str, 32, "%.*f", rdigits, label);
+	switch (dir) {
+	case AXIS_HORIZONTAL:
+		dim = gdk_string_width (axis->g->font, str);
+		break;
+	case AXIS_VERTICAL:
+		dim = gdk_string_height (axis->g->font, str);
+		break;
+	default:
+		puts ("initialize axis: an axis must be either horizontal or vertical");
+		return -1;
+		break;
+	}
+	return dim;
+}
+
+static double axis_zoom_get (struct axis *axis, int dir)
+{
+	switch (dir) {
+	case AXIS_HORIZONTAL:
+		return axis->g->zoom.x;
+		break;
+	case AXIS_VERTICAL:
+		return axis->g->zoom.y;
+		break;
+	default:
+		return -1;
+		break;
+	}
+}
+
+static void graph_select_segment (struct graph *g, int x, int y)
+{
+	struct element_list *list;
+	struct element *e;
+
+	debug(DBS_FENTRY) puts ("graph_select_segment()");
+
+	x -= g->geom.x;
+	y = g->geom.height-1 - (y - g->geom.y);
+
+	for (list=g->elists; list; list=list->next)
+		for (e=list->elements; e->type != ELMT_NONE; e++) {
+			switch (e->type) {
+			case ELMT_RECT:
+				break;
+			case ELMT_LINE:
+				if (line_detect_collision (e, x, y)) {
+					int row = e->parent->num - 1;
+					update_packet_list (row);
+				}
+				break;
+			case ELMT_ARC:
+				if (arc_detect_collision (e, x, y)) {
+					int row = e->parent->num - 1;
+					update_packet_list (row);
+				}
+				break;
+			default:
+				break;
+			}
+		}
+}
+
+static int line_detect_collision (struct element *e, int x, int y)
+{
+	int x1, y1, x2, y2;
+
+	if (e->p.line.dim.x1 < e->p.line.dim.x2) {
+		x1 = (int )rint (e->p.line.dim.x1);
+		x2 = (int )rint (e->p.line.dim.x2);
+	} else {
+		x1 = (int )rint (e->p.line.dim.x2);
+		x2 = (int )rint (e->p.line.dim.x1);
+	}
+	if (e->p.line.dim.y1 < e->p.line.dim.y2) {
+		y1 = (int )rint (e->p.line.dim.y1);
+		y2 = (int )rint (e->p.line.dim.y2);
+	} else {
+		y1 = (int )rint (e->p.line.dim.y2);
+		y2 = (int )rint (e->p.line.dim.y1);
+	}
+//	printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
+	if ((x1==x && x2==x && y1<=y && y<=y2)||(y1==y && y2==y && x1<=x && x<=x2))
+		return TRUE;
+	else
+		return FALSE;
+}
+
+static int arc_detect_collision (struct element *e, int x, int y)
+{
+	int x1, y1, x2, y2;
+
+	x1 = (int )rint (e->p.arc.dim.x);
+	x2 = (int )rint (e->p.arc.dim.x + e->p.arc.dim.width);
+	y1 = (int )rint (e->p.arc.dim.y - e->p.arc.dim.height);
+	y2 = (int )rint (e->p.arc.dim.y);
+//	printf ("arc: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", x1, y1, x2, y2, x, y);
+	if (x1<=x && x<=x2 && y1<=y && y<=y2)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+static void update_packet_list (int row)
+{
+	select_packet (&cfile, row);
+	if (gtk_clist_row_is_visible(GTK_CLIST(packet_list), row) !=
+				GTK_VISIBILITY_FULL)
+		gtk_clist_moveto(GTK_CLIST(packet_list), row, -1, 0.5, 0.5);
+	GTK_CLIST(packet_list)->focus_row = row;
+	gtk_clist_select_row(GTK_CLIST(packet_list), row, -1);
+}
+
+static void cross_xor (struct graph *g, int x, int y)
+{
+	if (x > g->wp.x && x < g->wp.x+g->wp.width &&
+				y >= g->wp.y && y < g->wp.y+g->wp.height) {
+		gdk_draw_line (g->drawing_area->window, xor_gc, g->wp.x,
+						y, g->wp.x + g->wp.width, y);
+		gdk_draw_line (g->drawing_area->window, xor_gc, x,
+						g->wp.y, x, g->wp.y + g->wp.height);
+	}
+}
+
+static void cross_draw (struct graph *g, int x, int y)
+{
+	cross_xor (g, x, y);
+	g->cross.x = x;
+	g->cross.y = y;
+	g->cross.erase_needed = 1;
+}
+
+static void cross_erase (struct graph *g)
+{
+	cross_xor (g, g->cross.x, g->cross.y);
+	g->cross.erase_needed = 0;
+}
+
+static void magnify_create (struct graph *g, int x, int y)
+{
+	struct graph *mg;
+	struct element_list *list, *new_list;
+	struct ipoint pos, offsetpos;
+	GdkEvent *e=NULL;
+
+	mg = g->magnify.g = (struct graph * )malloc (sizeof (struct graph));
+	memcpy ((void * )mg, (void * )g, sizeof (struct graph));
+
+	mg->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
+	mg->drawing_area = mg->toplevel;
+	gtk_widget_set_usize (mg->toplevel, g->magnify.width, g->magnify.height);
+//	gtk_signal_connect (GTK_OBJECT (mg->drawing_area),
+//			"expose_event", (GtkSignalFunc )magnify_expose_event, NULL);
+//	gtk_signal_connect (GTK_OBJECT (mg->drawing_area),
+//			"enter_notify_event", (GtkSignalFunc )magnify_expose_event, NULL);
+	gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
+//					| GDK_ENTER_NOTIFY_MASK
+//					| GDK_ALL_EVENTS_MASK
+					);
+
+	mg->wp.x = 0;
+	mg->wp.y = 0;
+	mg->wp.width = g->magnify.width;
+	mg->wp.height = g->magnify.height;
+	mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
+	mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
+	mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
+	mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
+
+	/* in order to keep original element lists intact we need our own */
+	graph_element_lists_initialize (mg);
+	list = g->elists->next;
+	new_list = mg->elists;
+	for ( ; list; list=list->next) {
+		new_list->next =
+				(struct element_list * )malloc (sizeof (struct element_list));
+		new_list = new_list->next;
+		new_list->next = NULL;
+		new_list->elements = NULL;
+	}
+	graph_element_lists_make (mg);
+
+	gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
+	g->magnify.x = pos.x + x - g->magnify.width/2;
+	g->magnify.y = pos.y + y - g->magnify.height/2;
+	offsetpos.x = g->magnify.x + g->magnify.offset.x;
+	offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
+	offsetpos.y = g->magnify.y + g->magnify.offset.y;
+	offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
+	gtk_widget_set_uposition (mg->drawing_area, offsetpos.x, offsetpos.y);
+	magnify_get_geom (g, x, y);
+
+	gtk_widget_show (mg->drawing_area);
+
+	/* we need to wait for the first expose event before we start drawing */
+	while (!gdk_events_pending ());
+	do {
+		e = gdk_event_get ();
+		if (e) {
+			if (e->any.type == GDK_EXPOSE) {
+				gdk_event_free (e);
+				break;
+			}
+			gdk_event_free (e);
+		}
+	} while (e);
+
+	mg->pixmap[0] = mg->pixmap[1] = NULL;
+	graph_pixmaps_create (mg);
+	magnify_draw (g);
+	g->magnify.active = 1;
+}
+
+static void magnify_move (struct graph *g, int x, int y)
+{
+	struct ipoint pos, offsetpos;
+
+	gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &pos.x, &pos.y);
+	g->magnify.x = pos.x + x - g->magnify.width/2;
+	g->magnify.y = pos.y + y - g->magnify.height/2;
+	offsetpos.x = g->magnify.x + g->magnify.offset.x;
+	offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
+	offsetpos.y = g->magnify.y + g->magnify.offset.y;
+	offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
+	magnify_get_geom (g, x, y);
+	gtk_widget_set_uposition (g->magnify.g->drawing_area, offsetpos.x,
+								offsetpos.y);
+	magnify_draw (g);
+}
+
+static void magnify_destroy (struct graph *g)
+{
+	struct element_list *list;
+	struct graph *mg = g->magnify.g;
+
+	gtk_widget_destroy (GTK_WIDGET (mg->drawing_area));
+	gdk_pixmap_unref (mg->pixmap[0]);
+	gdk_pixmap_unref (mg->pixmap[1]);
+	for (list=mg->elists; list; list=list->next)
+		free (list->elements);
+	while (mg->elists->next) {
+		list = mg->elists->next->next;
+		free (mg->elists->next);
+		mg->elists->next = list;
+	}
+	free (g->magnify.g);
+	g->magnify.active = 0;
+}
+
+static void magnify_get_geom (struct graph *g, int x, int y)
+{
+	int posx, posy;
+
+	gdk_window_get_position (GTK_WIDGET (g->toplevel)->window, &posx, &posy);
+
+	g->magnify.g->geom.x = g->geom.x;
+	g->magnify.g->geom.y = g->geom.y;
+
+	g->magnify.g->geom.x -=
+				(int )rint ((g->magnify.g->geom.width - g->geom.width) *
+				((x-g->geom.x)/(double )g->geom.width));
+	g->magnify.g->geom.y -=
+				(int )rint ((g->magnify.g->geom.height - g->geom.height) *
+				((y-g->geom.y)/(double )g->geom.height));
+
+	/* we have coords of origin of graph relative to origin of g->toplevel.
+	 * now we need them to relate to origin of magnify window */
+	g->magnify.g->geom.x -= (g->magnify.x - posx);
+	g->magnify.g->geom.y -= (g->magnify.y - posy);
+}
+
+static void magnify_draw (struct graph *g)
+{
+	int not_disp = 1 ^ g->magnify.g->displayed;
+
+	graph_pixmap_draw (g->magnify.g);
+	/* graph pixmap is almost ready, just add border */
+	gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
+						g->magnify.width - 1, 0);
+	gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc,
+			g->magnify.width - 1, 0, g->magnify.width - 1, g->magnify.height);
+	gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0, 0,
+						0, g->magnify.height - 1);
+	gdk_draw_line (g->magnify.g->pixmap[not_disp], g->fg_gc, 0,
+			g->magnify.height - 1, g->magnify.width - 1, g->magnify.height - 1);
+
+	graph_pixmaps_switch (g->magnify.g);
+	graph_pixmap_display (g->magnify.g);
+
+}
+
+static gint configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+	struct graph *g;
+	struct {
+		double x, y;
+	} zoom;
+	int cur_g_width, cur_g_height;
+	int cur_wp_width, cur_wp_height;
+
+	debug(DBS_FENTRY) puts ("configure_event()");
+
+	for (g=graphs; g; g=g->next)
+		if (g->drawing_area == widget)
+			break;
+
+	cur_wp_width = g->wp.width;
+	cur_wp_height = g->wp.height;
+	g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
+	g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
+	g->x_axis->s.width = g->wp.width;
+	g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
+	g->y_axis->p.height = g->wp.height + g->wp.y;
+	g->y_axis->s.height = g->wp.height;
+	g->x_axis->p.y = g->y_axis->p.height;
+	zoom.x = (double )g->wp.width / cur_wp_width;
+	zoom.y = (double )g->wp.height / cur_wp_height;
+	cur_g_width = g->geom.width;
+	cur_g_height = g->geom.height;
+	g->geom.width = (int )rint (g->geom.width * zoom.x);
+	g->geom.height = (int )rint (g->geom.height * zoom.y);
+	g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
+	g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
+//	g->zoom.initial.x = g->zoom.x;
+//	g->zoom.initial.y = g->zoom.y;
+
+	g->geom.x = g->wp.x - (double )g->geom.width/cur_g_width *
+							(g->wp.x - g->geom.x);
+	g->geom.y = g->wp.y - (double )g->geom.height/cur_g_height *
+							(g->wp.y - g->geom.y);
+//	printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
+//				"zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
+//				g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
+//				g->zoom.x, g->zoom.y);
+
+	update_zoom_spins (g);
+	graph_element_lists_make (g);
+	graph_pixmaps_create (g);
+	graph_title_pixmap_create (g);
+	axis_pixmaps_create (g->y_axis);
+	axis_pixmaps_create (g->x_axis);
+	/* we don't do actual drawing here; we leave it to expose handler */
+	graph_pixmap_draw (g);
+	graph_pixmaps_switch (g);
+	graph_title_pixmap_draw (g);
+	h_axis_pixmap_draw (g->x_axis);
+	axis_pixmaps_switch (g->x_axis);
+	v_axis_pixmap_draw (g->y_axis);
+	axis_pixmaps_switch (g->y_axis);
+	return TRUE;
+}
+
+static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+	struct graph *g;
+
+	debug(DBS_FENTRY) puts ("expose_event()");
+
+	if (event->count)
+		return TRUE;
+
+	for (g=graphs; g; g=g->next)
+		if (g->drawing_area == widget)
+			break;
+
+	/* lower left corner */
+	gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE, 0,
+			g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
+	/* right margin */
+	gdk_draw_rectangle (g->drawing_area->window, g->bg_gc, TRUE,
+			g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
+
+	graph_pixmap_display (g);
+	graph_title_pixmap_display (g);
+	axis_pixmap_display (g->x_axis);
+	axis_pixmap_display (g->y_axis);
+
+	return TRUE;
+}
+
+static gint button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+	struct graph *g;
+
+	debug(DBS_FENTRY) puts ("button_press_event()");
+
+	for (g=graphs; g; g=g->next)
+		if (g->drawing_area == widget)
+			break;
+
+	if (event->button == 1) {
+		if (event->state & GDK_CONTROL_MASK)
+			magnify_create (g, (int )rint (event->x), (int )rint (event->y));
+		else {
+			g->grab.x = (int )rint (event->x) - g->geom.x;
+			g->grab.y = (int )rint (event->y) - g->geom.y;
+			g->grab.grabbed = TRUE;
+		}
+	} else if (event->button == 2) {
+		int cur_width = g->geom.width, cur_height = g->geom.height;
+		struct { double x, y; } factor;
+
+		if (g->zoom.flags & ZOOM_OUT) {
+			if (g->zoom.flags & ZOOM_HLOCK)
+				factor.x = 1.0;
+			else
+				factor.x = 1 / g->zoom.step_x;
+			if (g->zoom.flags & ZOOM_VLOCK)
+				factor.y = 1.0;
+			else
+				factor.y = 1 / g->zoom.step_y;
+		} else {
+			if (g->zoom.flags & ZOOM_HLOCK)
+				factor.x = 1.0;
+			else
+				factor.x = g->zoom.step_x;
+			if (g->zoom.flags & ZOOM_VLOCK)
+				factor.y = 1.0;
+			else
+				factor.y = g->zoom.step_y;
+		}
+
+		g->geom.width = (int )rint (g->geom.width * factor.x);
+		g->geom.height = (int )rint (g->geom.height * factor.y);
+		if (g->geom.width < g->wp.width)
+			g->geom.width = g->wp.width;
+		if (g->geom.height < g->wp.height)
+			g->geom.height = g->wp.height;
+		g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+		g->zoom.y = (g->geom.height- 1) / g->bounds.height;
+
+		g->geom.x -= (int )rint ((g->geom.width - cur_width) *
+						((event->x-g->geom.x)/(double )cur_width));
+		g->geom.y -= (int )rint ((g->geom.height - cur_height) *
+						((event->y-g->geom.y)/(double )cur_height));
+
+		if (g->geom.x > g->wp.x)
+			g->geom.x = g->wp.x;
+		if (g->geom.y > g->wp.y)
+			g->geom.y = g->wp.y;
+		if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
+			g->geom.x = g->wp.width + g->wp.x - g->geom.width;
+		if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
+			g->geom.y = g->wp.height + g->wp.y - g->geom.height;
+//		printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
+//				"(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
+//				g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
+//				g->wp.height, g->zoom.x, g->zoom.y);
+		graph_element_lists_make (g);
+		graph_display (g);
+		axis_display (g->y_axis);
+		axis_display (g->x_axis);
+		update_zoom_spins (g);
+		if (g->cross.draw)
+			cross_draw (g, event->x, event->y);
+	} else if (event->button == 3) {
+		graph_select_segment (g, (int )event->x, (int )event->y);
+	}
+	return TRUE;
+}
+
+static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+	struct graph *g;
+	int x, y;
+	GdkModifierType state;
+
+//	debug(DBS_FENTRY) puts ("motion_notify_event()");
+//
+	for (g=graphs; g; g=g->next)
+		if (g->drawing_area == widget)
+			break;
+
+	if (event->is_hint)
+		gdk_window_get_pointer (event->window, &x, &y, &state);
+	else {
+		x = event->x;
+		y = event->y;
+		state = event->state;
+	}
+    
+	/* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
+	 * is pressed while pointer is in motion, we will receive one more motion
+	 * notify *before* we get the button press. This last motion notify works
+	 * with stale grab coordinates */
+	if (state & GDK_BUTTON1_MASK) {
+		if (g->grab.grabbed) {
+			g->geom.x = x-g->grab.x;
+			g->geom.y = y-g->grab.y;
+
+			if (g->geom.x > g->wp.x)
+				g->geom.x = g->wp.x;
+			if (g->geom.y > g->wp.y)
+				g->geom.y = g->wp.y;
+			if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
+				g->geom.x = g->wp.width + g->wp.x - g->geom.width;
+			if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
+				g->geom.y = g->wp.height + g->wp.y - g->geom.height;
+			graph_display (g);
+			axis_display (g->y_axis);
+			axis_display (g->x_axis);
+			if (g->cross.draw)
+				cross_draw (g, x, y);
+		} else if (g->magnify.active)
+			magnify_move (g, x, y);
+	} else if (state & GDK_BUTTON3_MASK) {
+		graph_select_segment (g, x, y);
+		if (g->cross.erase_needed)
+			cross_erase (g);
+		if (g->cross.draw)
+			cross_draw (g, x, y);
+	} else {
+		if (g->cross.erase_needed)
+			cross_erase (g);
+		if (g->cross.draw)
+			cross_draw (g, x, y);
+	}
+  
+	return TRUE;
+}
+
+static gint button_release_event (GtkWidget *widget, GdkEventButton *event)
+{
+	struct graph *g;
+
+	debug(DBS_FENTRY) puts ("button_release_event()");
+
+	for (g=graphs; g; g=g->next)
+		if (g->drawing_area == widget)
+			break;
+
+	if (event->button == 1)
+		g->grab.grabbed = FALSE;
+
+	if (g->magnify.active)
+		magnify_destroy (g);
+	return TRUE;
+}
+
+static gint key_press_event (GtkWidget *widget, GdkEventKey *event)
+{
+	struct graph *g;
+
+	debug(DBS_FENTRY) puts ("key_press_event()");
+
+	for (g=graphs; g; g=g->next)
+		if (g->toplevel == widget)
+			break;
+
+	if (event->keyval == 32 /*space*/) {
+		g->cross.draw ^= 1;
+//		if (g->cross.draw) {
+//			int x, y;
+//			gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
+//			cross_draw (g);
+//		} else if (g->cross.erase_needed) {
+//			cross_erase (g);
+//		}
+		/* toggle buttons emit their "toggled" signals so don't bother doing
+		 * any real work here, it will be done in signal handlers */
+		if (g->cross.draw)
+			gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
+		else
+			gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
+	} else if (event->keyval == 't')
+		toggle_time_origin (g);
+	else if (event->keyval == 's')
+		toggle_seq_origin (g);
+	else if (event->keyval == XK_Shift_L) {
+//		g->zoom.flags |= ZOOM_OUT;
+		gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
+	}
+	return TRUE;
+}
+
+static gint key_release_event (GtkWidget *widget, GdkEventKey *event)
+{
+	struct graph *g;
+
+	debug(DBS_FENTRY) puts ("key_release_event()");
+
+	for (g=graphs; g; g=g->next)
+		if (g->toplevel == widget)
+			break;
+
+	if (event->keyval == XK_Shift_L || event->keyval == XK_ISO_Prev_Group) {
+//		g->zoom.flags &= ~ZOOM_OUT;
+		gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
+	}
+	return TRUE;
+}
+
+static gint leave_notify_event (GtkWidget *widget, GdkEventCrossing *event)
+{
+	struct graph *g;
+
+	for (g=graphs; g; g=g->next)
+		if (g->drawing_area == widget)
+			break;
+
+	if (g->cross.erase_needed)
+		cross_erase (g);
+
+	return TRUE;
+}
+
+static gint enter_notify_event (GtkWidget *widget, GdkEventCrossing *event)
+{
+	struct graph *g;
+
+	for (g=graphs; g; g=g->next)
+		if (g->drawing_area == widget)
+			break;
+
+//	graph_pixmap_display (g);
+	if (g->cross.draw) {
+		int x, y;
+		gdk_window_get_pointer (g->drawing_area->window, &x, &y, 0);
+		cross_draw (g, x, y);
+	}
+	return TRUE;
+}
+
+static void toggle_time_origin (struct graph *g)
+{
+	switch (g->type) {
+	case GRAPH_TSEQ_STEVENS:
+		tseq_stevens_toggle_time_origin (g);
+		break;
+	case GRAPH_TSEQ_TCPTRACE:
+		tseq_tcptrace_toggle_time_origin (g);
+		break;
+	case GRAPH_THROUGHPUT:
+		tput_toggle_time_origin (g);
+		break;
+	default:
+		break;
+	}
+	axis_display (g->x_axis);
+}
+
+static void toggle_seq_origin (struct graph *g)
+{
+	switch (g->type) {
+	case GRAPH_TSEQ_STEVENS:
+		tseq_stevens_toggle_seq_origin (g);
+		axis_display (g->y_axis);
+		break;
+	case GRAPH_TSEQ_TCPTRACE:
+		tseq_tcptrace_toggle_seq_origin (g);
+		axis_display (g->y_axis);
+		break;
+	case GRAPH_RTT:
+		rtt_toggle_seq_origin (g);
+		axis_display (g->x_axis);
+		break;
+	default:
+		break;
+	}
+}
+
+static int get_num_dsegs (struct graph *g)
+{
+	int count;
+	struct segment *tmp;
+
+	for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
+		if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+			count++;
+		}
+	}
+	return count;
+}
+
+static int get_num_acks (struct graph *g)
+{
+	int count;
+	struct segment *tmp;
+
+	for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
+		if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+			count++;
+		}
+	}
+	return count;
+}
+
+/*
+ * Stevens-style time-sequence grapH
+ */
+
+static void tseq_stevens_read_config (struct graph *g)
+{
+	debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
+
+	g->s.tseq_stevens.seq_width = 4;
+	g->s.tseq_stevens.seq_height = 4;
+	g->s.tseq_stevens.flags = 0;
+
+	g->title = (char ** )malloc (2 * sizeof (char *));
+	g->title[0] = "Time/Sequence Graph";
+	g->title[1] = NULL;
+	g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
+	g->y_axis->label[0] = "number[B]";
+	g->y_axis->label[1] = "Sequence";
+	g->y_axis->label[2] = NULL;
+	g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
+	g->x_axis->label[0] = "Time[s]";
+	g->x_axis->label[1] = NULL;
+}
+
+static void tseq_stevens_initialize (struct graph *g)
+{
+	debug(DBS_FENTRY) puts ("tseq_stevens_initialize()");
+	tseq_stevens_get_bounds (g);
+
+	g->x_axis->min = 0;
+	g->y_axis->min = 0;
+
+	switch (g->type) {
+	case GRAPH_TSEQ_STEVENS:
+		tseq_stevens_read_config(g);
+		break;
+	case GRAPH_TSEQ_TCPTRACE:
+		tseq_tcptrace_read_config(g);
+		break;
+	}
+}
+
+static void tseq_stevens_get_bounds (struct graph *g)
+{
+	struct segment *tmp, *last, *first;
+	double t0, tmax, y0, ymax;
+
+	for (first=g->segments; first->next; first=first->next) {
+		if (compare_headers (g->current, first, COMPARE_CURR_DIR))
+			break;
+	}
+	last = NULL;
+	ymax = 0;
+	for (tmp=g->segments; tmp; tmp=tmp->next) {
+		unsigned int highest_byte_num;
+		last = tmp;
+		if (compare_headers (g->current, tmp, COMPARE_CURR_DIR))
+			highest_byte_num = ntohl (tmp->tcphdr.seq) + tmp->data;
+		else
+			highest_byte_num = ntohl (tmp->tcphdr.ack_seq);
+		if (highest_byte_num > ymax)
+			ymax = highest_byte_num;
+	}
+	if (!last) {
+		puts ("tseq_stevens_get_bounds: segment list corrupted!");
+		return;
+	}
+
+	t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
+	tmax = last->rel_secs + last->rel_usecs / 1000000.0;
+	y0 = ntohl (first->tcphdr.seq);
+
+	g->bounds.x0 = t0;
+	g->bounds.y0 = y0;
+	g->bounds.width = tmax - t0;
+	g->bounds.height = ymax - y0;
+	g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+	g->zoom.y = (g->geom.height -1) / g->bounds.height;
+}
+
+static void tseq_stevens_make_elmtlist (struct graph *g)
+{
+	struct segment *tmp;
+	struct element *elements, *e;
+	double x0 = g->bounds.x0, y0 = g->bounds.y0;
+
+	debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
+	if (g->elists->elements == NULL) {
+		int n = 1 + get_num_dsegs (g);
+		e = elements = (struct element * )malloc (n*sizeof (struct element));
+	} else
+		e = elements = g->elists->elements;
+
+	for (tmp=g->segments; tmp; tmp=tmp->next) {
+		double secs, seqno;
+
+		if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
+			continue;
+
+		secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - x0);
+		seqno = g->zoom.y * (ntohl (tmp->tcphdr.seq) - y0);
+
+		e->type = ELMT_ARC;
+		e->parent = tmp;
+		e->gc = g->fg_gc;
+		e->p.arc.dim.width = g->s.tseq_stevens.seq_width;
+		e->p.arc.dim.height = g->s.tseq_stevens.seq_height;
+		e->p.arc.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
+		e->p.arc.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
+		e->p.arc.filled = TRUE;
+		e->p.arc.angle1 = 0;
+		e->p.arc.angle2 = 23040;
+		e++;
+	}
+	e->type = ELMT_NONE;
+	g->elists->elements = elements;
+}
+
+static void tseq_stevens_toggle_seq_origin (struct graph *g)
+{
+	g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
+
+	if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+		g->y_axis->min = g->bounds.y0;
+	else		/* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
+		g->y_axis->min = 0;
+}
+
+static void tseq_stevens_toggle_time_origin (struct graph *g)
+{
+	g->s.tseq_stevens.flags ^= TIME_ORIGIN;
+
+	if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+		g->x_axis->min = g->bounds.x0;
+	else		/* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+		g->x_axis->min = 0;
+}
+
+/*
+ * tcptrace-style time-sequence graph
+ */
+
+static void tseq_tcptrace_read_config (struct graph *g)
+{
+	GdkColormap *colormap;
+	GdkColor color;
+
+	g->s.tseq_tcptrace.flags = 0;
+	g->s.tseq_tcptrace.gc_seq = gdk_gc_new (g->drawing_area->window);
+	g->s.tseq_tcptrace.gc_ack[0] = gdk_gc_new (g->drawing_area->window);
+	g->s.tseq_tcptrace.gc_ack[1] = gdk_gc_new (g->drawing_area->window);
+	colormap = gdk_window_get_colormap (g->drawing_area->window);
+	gdk_color_parse ("black", &color);
+	gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+	gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_seq, &color);
+	gdk_color_parse ("LightSlateGray", &color);
+	gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+	gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[0], &color);
+	gdk_color_parse ("LightGray", &color);
+	gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+	gdk_gc_set_foreground (g->s.tseq_tcptrace.gc_ack[1], &color);
+
+	g->elists->next = (struct element_list * )
+										malloc (sizeof (struct element_list));
+	g->elists->next->next = NULL;
+	g->elists->next->elements = NULL;
+
+	g->title = (char ** )malloc (2 * sizeof (char *));
+	g->title[0] = "Time/Sequence Graph";
+	g->title[1] = NULL;
+	g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
+	g->y_axis->label[0] = "number[B]";
+	g->y_axis->label[1] = "Sequence";
+	g->y_axis->label[2] = NULL;
+	g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
+	g->x_axis->label[0] = "Time[s]";
+	g->x_axis->label[1] = NULL;
+}
+
+static void tseq_tcptrace_make_elmtlist (struct graph *g)
+{
+	struct segment *tmp;
+	struct element *elements0, *e0;		/* list of elmts with prio 0 */
+	struct element *elements1, *e1;		/* list of elmts with prio 1 */
+	double x0, y0;
+	double p_t; /* ackno, window and time of previous segment */
+	double p_ackno, p_win;
+	int toggle=0;
+
+	debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
+
+	if (g->elists->elements == NULL) {
+		int n = 1 + 4*get_num_acks(g);
+		e0 = elements0 = (struct element * )malloc (n*sizeof (struct element));
+	} else
+		e0 = elements0 = g->elists->elements;
+
+	if (g->elists->next->elements == NULL ) {
+		int n = 1 + 3*get_num_dsegs(g);
+		e1 = elements1 = (struct element * )malloc (n*sizeof (struct element));
+	} else
+		e1 = elements1 = g->elists->next->elements;
+
+	x0 = g->bounds.x0;
+	y0 = g->bounds.y0;
+	/* initialize "previous" values */
+	for (tmp=g->segments; tmp; tmp=tmp->next)
+		if (!compare_headers (g->current, tmp, COMPARE_CURR_DIR))
+			break;
+//	p_ackno = (unsigned int )(g->zoom.y * (ntohl (tmp->tcphdr.ack_seq) - y0));
+	p_ackno = 0;
+	p_win = g->zoom.y * ntohs (tmp->tcphdr.window);
+	p_t = g->segments->rel_secs + g->segments->rel_usecs/1000000.0 - x0;
+	for (tmp=g->segments; tmp; tmp=tmp->next) {
+		double secs, seqno, data;
+		double x;
+
+		secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+		x = secs - x0;
+		x *= g->zoom.x;
+		if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+			/* forward direction -> we need seqno and amount of data */
+			double y1, y2;
+
+			seqno = ntohl (tmp->tcphdr.seq);
+			if (tmp->tcphdr.syn)
+				data = 1;
+			else
+				data = tmp->data;
+
+			y1 = g->zoom.y * (seqno - y0);
+			y2 = g->zoom.y * (seqno - y0 + data);
+			e1->type = ELMT_LINE;
+			e1->parent = tmp;
+			e1->gc = g->s.tseq_tcptrace.gc_seq;
+			e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
+			e1->p.line.dim.y1 = y1;
+			e1->p.line.dim.y2 = y2;
+			e1++;
+			e1->type = ELMT_LINE;
+			e1->parent = tmp;
+			e1->gc = g->s.tseq_tcptrace.gc_seq;
+			e1->p.line.dim.x1 = x - 1;
+			e1->p.line.dim.x2 = x + 1;
+			e1->p.line.dim.y1 = e1->p.line.dim.y2 = y1;
+			e1++;
+			e1->type = ELMT_LINE;
+			e1->parent = tmp;
+			e1->gc = g->s.tseq_tcptrace.gc_seq;
+			e1->p.line.dim.x1 = x + 1;
+			e1->p.line.dim.x2 = x - 1;
+			e1->p.line.dim.y1 = e1->p.line.dim.y2 = y2;
+			e1++;
+		} else {
+			double ackno, win;
+			if (tmp->tcphdr.syn && !tmp->tcphdr.ack)
+				/* SYN's have ACK==0 and are useless here */
+				continue;
+			/* backward direction -> we need ackno and window */
+			ackno = (ntohl (tmp->tcphdr.ack_seq) - y0) * g->zoom.y;
+			win = ntohs (tmp->tcphdr.window) * g->zoom.y;
+
+			/* ack line */
+			e0->type = ELMT_LINE;
+			e0->parent = tmp;
+			e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
+			e0->p.line.dim.x1 = p_t;
+			e0->p.line.dim.y1 = p_ackno;
+			e0->p.line.dim.x2 = x;
+			e0->p.line.dim.y2 = p_ackno;
+			e0++;
+			e0->type = ELMT_LINE;
+			e0->parent = tmp;
+			e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
+			e0->p.line.dim.x1 = x;
+			e0->p.line.dim.y1 = p_ackno;
+			e0->p.line.dim.x2 = x;
+			e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
+			e0++;
+			/* window line */
+			e0->type = ELMT_LINE;
+			e0->parent = tmp;
+			e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
+			e0->p.line.dim.x1 = p_t;
+			e0->p.line.dim.y1 = p_win + p_ackno;
+			e0->p.line.dim.x2 = x;
+			e0->p.line.dim.y2 = p_win + p_ackno;
+			e0++;
+			e0->type = ELMT_LINE;
+			e0->parent = tmp;
+			e0->gc = g->s.tseq_tcptrace.gc_ack[toggle];
+			e0->p.line.dim.x1 = x;
+			e0->p.line.dim.y1 = p_win + p_ackno;
+			e0->p.line.dim.x2 = x;
+			e0->p.line.dim.y2 = win + ackno;
+			e0++;
+			p_ackno = ackno;
+			p_win = win;
+			p_t = x;
+			toggle = 1^toggle;
+		}
+	}
+	e0->type = ELMT_NONE;
+	e1->type = ELMT_NONE;
+	g->elists->elements = elements0;
+	g->elists->next->elements = elements1;
+}
+
+static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
+{
+	g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
+
+	if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+		g->y_axis->min = g->bounds.y0;
+	else	/* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
+		g->y_axis->min = 0;
+}
+
+static void tseq_tcptrace_toggle_time_origin (struct graph *g)
+{
+	g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
+
+	if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+		g->x_axis->min = g->bounds.x0;
+	else	/* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+		g->x_axis->min = 0;
+}
+
+/*
+ * throughput graph
+ */
+
+static void tput_make_elmtlist (struct graph *g)
+{
+	struct segment *tmp, *oldest;
+	struct element *elements, *e;
+	int i, sum=0;
+	double dtime, tput;
+
+	if (g->elists->elements == NULL) {
+		int n = 1 + get_num_dsegs (g);
+		e = elements = (struct element * )malloc (n*sizeof (struct element));
+	} else
+		e = elements = g->elists->elements;
+
+	for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
+		double time = tmp->rel_secs + tmp->rel_usecs/1000000.0;
+		dtime = time - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
+		if (i>g->s.tput.nsegs) {
+			sum -= oldest->data;
+			oldest=oldest->next;
+		}
+		sum += tmp->data;
+		tput = sum / dtime;
+//		debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
+
+		e->type = ELMT_ARC;
+		e->parent = tmp;
+		e->gc = g->fg_gc;
+		e->p.arc.dim.width = g->s.tput.width;
+		e->p.arc.dim.height = g->s.tput.height;
+		e->p.arc.dim.x = g->zoom.x*(time - g->bounds.x0) - g->s.tput.width/2.0;
+		e->p.arc.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
+		e->p.arc.filled = TRUE;
+		e->p.arc.angle1 = 0;
+		e->p.arc.angle2 = 23040;
+		e++;
+	}
+	e->type = ELMT_NONE;
+	g->elists->elements = elements;
+}
+
+/* Purpose of <graph_type>_initialize functions:
+ * - find maximum and minimum for both axes
+ * - call setup routine for style struct */
+static void tput_initialize (struct graph *g)
+{
+	struct segment *tmp, *oldest, *last;
+	int i, sum=0;
+	double dtime, tput, tputmax=0;
+	double t0, tmax, y0, ymax;
+
+	debug(DBS_FENTRY) puts ("tput_initialize()");
+
+	tput_read_config(g);
+
+	for (last=g->segments; last->next; last=last->next);
+	for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
+		dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
+						(oldest->rel_secs + oldest->rel_usecs/1000000.0);
+		if (i>g->s.tput.nsegs) {
+			sum -= oldest->data;
+			oldest=oldest->next;
+		}
+		sum += tmp->data;
+		tput = sum / dtime;
+		debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
+		if (tput > tputmax)
+			tputmax = tput;
+	}
+
+	t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
+	tmax = last->rel_secs + last->rel_usecs / 1000000.0;
+	y0 = 0;
+	ymax = tputmax;
+
+	g->bounds.x0 = t0;
+	g->bounds.y0 = y0;
+	g->bounds.width = tmax - t0;
+	g->bounds.height = ymax - y0;
+	g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+	g->zoom.y = (g->geom.height -1) / g->bounds.height;
+}
+
+static void tput_read_config (struct graph *g)
+{
+	debug(DBS_FENTRY) puts ("tput_read_config()");
+
+	g->s.tput.width = 4;
+	g->s.tput.height = 4;
+	g->s.tput.nsegs = 20;
+
+	g->title = (char ** )malloc (2 * sizeof (char *));
+	g->title[0] = "Throughput Graph";
+	g->title[1] = NULL;
+	g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
+	g->y_axis->label[0] = "[B/s]";
+	g->y_axis->label[1] = "Throughput";
+	g->y_axis->label[2] = NULL;
+	g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
+	g->x_axis->label[0] = "Time[s]";
+	g->x_axis->label[1] = NULL;
+	g->s.tput.flags = 0;
+}
+
+static void tput_toggle_time_origin (struct graph *g)
+{
+	g->s.tput.flags ^= TIME_ORIGIN;
+
+	if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+		g->x_axis->min = g->bounds.x0;
+	else 	/* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+		g->x_axis->min = 0;
+}
+
+/* RTT graph */
+
+static void rtt_read_config (struct graph *g)
+{
+	debug(DBS_FENTRY) puts ("rtt_read_config()");
+
+	g->s.rtt.width = 4;
+	g->s.rtt.height = 4;
+	g->s.rtt.flags = 0;
+
+	g->title = (char ** )malloc (2 * sizeof (char *));
+	g->title[0] = "Round Trip Time Graph";
+	g->title[1] = NULL;
+	g->y_axis->label = (char ** )malloc (3 * sizeof (char * ));
+	g->y_axis->label[0] = "RTT [s]";
+	g->y_axis->label[1] = NULL;
+	g->x_axis->label = (char ** )malloc (2 * sizeof (char * ));
+	g->x_axis->label[0] = "Sequence Number[B]";
+	g->x_axis->label[1] = NULL;
+}
+
+static void rtt_initialize (struct graph *g)
+{
+	struct segment *tmp, *first=NULL;
+	struct unack *unack = NULL, *u;
+	double rttmax=0;
+	double x0, xmax=0, y0, ymax;
+
+	debug(DBS_FENTRY) puts ("rtt_initialize()");
+
+	rtt_read_config (g);
+
+	for (tmp=g->segments; tmp; tmp=tmp->next) {
+		if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+			unsigned int seqno = ntohl (tmp->tcphdr.seq);
+			
+			if (!first)
+				first= tmp;
+
+			if (tmp->data && !rtt_is_retrans (unack, seqno)) {
+				double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+				u = rtt_get_new_unack (time, seqno);
+				if (!u) return;
+				rtt_put_unack_on_list (&unack, u);
+			}
+
+			if (seqno + tmp->data > xmax)
+				xmax = seqno + tmp->data;
+		} else {
+			unsigned int ackno = ntohl (tmp->tcphdr.ack_seq);
+			double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+			struct unack *v;
+
+			for (u=unack; u; u=v)
+				if (ackno > u->seqno) {
+					double rtt = time - u->time;
+					if (rtt > rttmax)
+						rttmax = rtt;
+					v=u->next;
+					rtt_delete_unack_from_list (&unack, u);
+				} else
+					v=u->next;
+		}
+	}
+
+	x0 = ntohl (first->tcphdr.seq);
+	y0 = 0;
+	ymax = rttmax;
+
+	g->bounds.x0 = x0;
+	g->bounds.y0 = y0;
+	g->bounds.width = xmax - x0;
+	g->bounds.height = ymax - y0;
+	g->zoom.x = g->geom.width / g->bounds.width;
+	g->zoom.y = g->geom.height / g->bounds.height;
+}
+
+static int rtt_is_retrans (struct unack *list, unsigned int seqno)
+{
+	struct unack *u;
+
+	for (u=list; u; u=u->next)
+		if (u->seqno == seqno)
+			return TRUE;
+
+	return FALSE;
+}
+
+static struct unack *rtt_get_new_unack (double time, unsigned int seqno)
+{
+	struct unack *u;
+
+	u = (struct unack * )malloc (sizeof (struct unack));
+	if (!u)
+		return NULL;
+	u->next = NULL;
+	u->time = time;
+	u->seqno = seqno;
+	return u;
+}
+
+static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
+{
+	struct unack *u, *list = *l;
+
+	for (u=list; u; u=u->next)
+		if (!u->next)
+			break;
+
+	if (u)
+		u->next = new;
+	else
+		*l = new;
+}
+
+static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
+{
+	struct unack *u, *list = *l;
+
+	if (!dead || !list)
+		return;
+
+	if (dead==list) {
+		*l = list->next;
+		free (list);
+	} else
+		for (u=list; u; u=u->next)
+			if (u->next == dead) {
+				u->next = u->next->next;
+				free (dead);
+				break;
+			}
+}
+
+static void rtt_make_elmtlist (struct graph *g)
+{
+	struct segment *tmp;
+	struct unack *unack = NULL, *u;
+	struct element *elements, *e;
+
+	debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
+
+	if (g->elists->elements == NULL) {
+		int n = 1 + get_num_dsegs (g);
+		e = elements = (struct element * )malloc (n*sizeof (struct element));
+	} else
+		e = elements = g->elists->elements;
+
+	for (tmp=g->segments; tmp; tmp=tmp->next) {
+		if (compare_headers (g->current, tmp, COMPARE_CURR_DIR)) {
+			unsigned int seqno = ntohl (tmp->tcphdr.seq);
+
+			if (tmp->data && !rtt_is_retrans (unack, seqno)) {
+				double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+				u = rtt_get_new_unack (time, seqno);
+				if (!u) return;
+				rtt_put_unack_on_list (&unack, u);
+			}
+		} else {
+			unsigned int ackno = ntohl (tmp->tcphdr.ack_seq);
+			double time = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+			struct unack *v;
+
+			for (u=unack; u; u=v)
+				if (ackno > u->seqno) {
+					double rtt = time - u->time;
+
+					e->type = ELMT_ARC;
+					e->parent = tmp;
+					e->gc = g->fg_gc;
+					e->p.arc.dim.width = g->s.rtt.width;
+					e->p.arc.dim.height = g->s.rtt.height;
+					e->p.arc.dim.x = g->zoom.x * (u->seqno - g->bounds.x0)
+														- g->s.rtt.width/2.0;
+					e->p.arc.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
+					e->p.arc.filled = TRUE;
+					e->p.arc.angle1 = 0;
+					e->p.arc.angle2 = 23040;
+					e++;
+
+					v=u->next;
+					rtt_delete_unack_from_list (&unack, u);
+				} else
+					v=u->next;
+		}
+	}
+	e->type = ELMT_NONE;
+	g->elists->elements = elements;
+}
+
+static void rtt_toggle_seq_origin (struct graph *g)
+{
+	g->s.rtt.flags ^= SEQ_ORIGIN;
+
+	if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+		g->x_axis->min = g->bounds.x0;
+	else
+		g->x_axis->min = 0;
+}
diff -r -u -N -I $Id: --exclude=CVS ethereal-0.8.11/gtk/tcp_graph.h ethereal/gtk/tcp_graph.h
--- ethereal-0.8.11/gtk/tcp_graph.h	Thu Jan  1 01:00:00 1970
+++ ethereal/gtk/tcp_graph.h	Sat Feb 24 20:40:01 2001
@@ -0,0 +1,5 @@
+
+/* $Id: tcp_graph.h,v 1.1 2001/02/24 19:40:01 pvl Exp $ */
+
+extern void tcp_graph (GtkWidget * , gpointer);
+