/* 
 * Copyright (c) Tony Bybell 1999-2005.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 */

#include "gtk12compat.h"
#include "currenttime.h"
#include "pixmaps.h"
#include "aet.h"
#include "bsearch.h"
#include "color.h"
#include "strace.h"
#include "debug.h"

#define _ISOC99_SOURCE 1
#include <math.h>

static void rendertimebar(void);
static void draw_hptr_trace(Trptr t, hptr h, int which, int dodraw, int kill_grid);
static void draw_hptr_trace_vector(Trptr t, hptr h, int which);
static void draw_vptr_trace(Trptr t, vptr v, int which);
static void rendertraces(void);
static void rendertimes(void);

static gint m1x, m2x;

char signalwindow_width_dirty=1;	/* prime it up for the 1st one at least.. */
char enable_ghost_marker=1;
char enable_horiz_grid=1;
char enable_vert_grid=1;
char use_big_fonts=0, use_nonprop_fonts=0;
char do_resize_signals=~0;
char constant_marker_update=0;
char use_roundcaps=0;
char show_base=~0;
char wave_scrolling=~0;
int vector_padding=4;
static int in_button_press=0;
char left_justify_sigs=0;
char zoom_pow10_snap=0;
int cursor_snap=0;

gfloat old_wvalue=-1.0;

struct blackout_region_t *blackout_regions = NULL;
TimeType zoom=0, scale=1, nsperframe=1;
gdouble pixelsperframe=1;
gdouble hashstep=1.0;
static TimeType prevtim=-1;

gdouble pxns=1.0, nspx=1.0;
gdouble zoombase=2.0;

Trptr topmost_trace=NULL;
int waveheight=1, wavecrosspiece;
int wavewidth=1;
GdkFont *wavefont=NULL;
GdkFont *wavefont_smaller=NULL;

GtkWidget *wavearea=NULL;
static GtkWidget *vscroll=NULL;
static GtkWidget *hscroll=NULL;
static GdkPixmap *wavepixmap = NULL;

GtkObject *wave_vslider=NULL, *wave_hslider=NULL;
TimeType named_markers[26];

static char made_gc_contexts=0;

static GdkGC    *gc_back  =NULL;
static GdkGC    *gc_baseline=NULL;
static GdkGC    *gc_grid  =NULL;
static GdkGC    *gc_time  =NULL;
static GdkGC    *gc_timeb =NULL;
static GdkGC	*gc_value =NULL;                 
static GdkGC	*gc_low   =NULL;
static GdkGC    *gc_high  =NULL;
static GdkGC    *gc_trans =NULL;
static GdkGC    *gc_mid   =NULL;
static GdkGC    *gc_xfill =NULL;
static GdkGC    *gc_x     =NULL;
static GdkGC    *gc_vbox  =NULL;
static GdkGC    *gc_vtrans=NULL;
static GdkGC    *gc_mark  =NULL;
static GdkGC    *gc_umark =NULL;

static GdkGC    *gc_0     =NULL;
static GdkGC    *gc_1     =NULL;
static GdkGC    *gc_ufill =NULL;
static GdkGC    *gc_u     =NULL;
static GdkGC    *gc_wfill =NULL;
static GdkGC    *gc_w     =NULL;
static GdkGC    *gc_dashfill =NULL;
static GdkGC    *gc_dash  =NULL;

static const GdkModifierType   bmask[4]= {0, GDK_BUTTON1_MASK, 0, GDK_BUTTON3_MASK };		        /* button 1, 3 press/rel encodings */
static const GdkModifierType m_bmask[4]= {0, GDK_BUTTON1_MOTION_MASK, 0, GDK_BUTTON3_MOTION_MASK };	/* button 1, 3 motion encodings */

/******************************************************************/

/*
 * gtk_draw_line() acceleration for win32 by doing draw combining
 */
#if defined __MINGW32__ || defined _MSC_VER

#define WAVE_SEG_BUF_CNT 1024

static int seg_trans_cnt=0, seg_low_cnt=0, seg_high_cnt=0, seg_mid_cnt=0;
static GdkSegment seg_trans[WAVE_SEG_BUF_CNT], seg_low[WAVE_SEG_BUF_CNT], seg_high[WAVE_SEG_BUF_CNT], seg_mid[WAVE_SEG_BUF_CNT];


static void wave_gdk_draw_line(GdkDrawable *drawable, GdkGC *gc, gint x1, gint y1, gint x2, gint y2)
{
GdkSegment *seg;
int *seg_cnt;

if(gc==gc_trans) 	{ seg = seg_trans; seg_cnt = &seg_trans_cnt; }
else if(gc==gc_low) 	{ seg = seg_low; seg_cnt = &seg_low_cnt; }
else if(gc==gc_high) 	{ seg = seg_high; seg_cnt = &seg_high_cnt; }
else if(gc==gc_mid) 	{ seg = seg_mid; seg_cnt = &seg_mid_cnt; }
else 			{ gdk_draw_line(drawable, gc, x1, y1, x2, y2); return; }

seg[*seg_cnt].x1 = x1;
seg[*seg_cnt].y1 = y1;
seg[*seg_cnt].x2 = x2;
seg[*seg_cnt].y2 = y2;
(*seg_cnt)++;
if(*seg_cnt == WAVE_SEG_BUF_CNT)
	{
	gdk_draw_segments(drawable, gc, seg, *seg_cnt);
	*seg_cnt = 0;
	}
}

static void wave_gdk_draw_line_flush(GdkDrawable *drawable)
{
if(seg_mid)
	{
	gdk_draw_segments(drawable, gc_mid, seg_mid, seg_mid_cnt);
	seg_mid_cnt = 0;
	}

if(seg_high)
	{
	gdk_draw_segments(drawable, gc_high, seg_high, seg_high_cnt);
	seg_high_cnt = 0;
	}

if(seg_low)
	{
	gdk_draw_segments(drawable, gc_low, seg_low, seg_low_cnt);
	seg_low_cnt = 0;
	}

if(seg_trans)
	{
	gdk_draw_segments(drawable, gc_trans, seg_trans, seg_trans_cnt);
	seg_trans_cnt = 0;
	}
}

#else

/* completely unnecessary for linux */

#define wave_gdk_draw_line(a,b,c,d,e,f) gdk_draw_line(a,b,c,d,e,f)
#define wave_gdk_draw_line_flush(x)

#endif

/******************************************************************/


/*
 * aldec-like "snap" feature...
 */
TimeType cook_markertime(TimeType marker, gint x, gint y)
{
int i, num_traces_displayable;                
Trptr t;
TimeType lft, rgh;
char lftinv, rghinv;
gdouble xlft, xrgh;
gdouble xlftd, xrghd;

if(!cursor_snap) return(marker);

num_traces_displayable=wavearea->allocation.height/(fontheight);
num_traces_displayable--;   /* for the time trace that is always there */

y-=fontheight;
if(y<0) y=0;
y/=fontheight;		    /* y now indicates the trace in question */
if(y>num_traces_displayable) y=num_traces_displayable;

t=topmost_trace;
for(i=0;i<y;i++)
	{
	if(!t) return(marker);
	t=GiveNextTrace(t);
	}

if((!t)||(t->flags&(TR_BLANK|TR_EXCLUDE))) return(marker);

if(t->flags & TR_ANALOG_BLANK_STRETCH)	/* seek to real analog trace is present... */
	{
	while((t) && (t = t->t_prev))
		{
		if(!(t->flags & TR_ANALOG_BLANK_STRETCH))
			{
			if(t->flags & TR_ANALOGMASK)
				{
				break; /* found it */
				}
				else
				{
				t = NULL;
				}
			}
		}
	}
if(!t) return(marker);

if(t->vector)
	{
	vptr v = bsearch_vector(t->n.vec, marker - t->shift);
	vptr v2 = v ? v->next : NULL;

	if((!v)||(!v2)) return(marker);	/* should never happen */

	lft = v->time;
	rgh = v2->time;
	}
	else
	{
	hptr h = bsearch_node(t->n.nd, marker - t->shift);
	hptr h2 = h ? h->next : NULL;

	if((!h)||(!h2)) return(marker);	/* should never happen */

	lft = h->time;
	rgh = h2->time;
	}

lftinv = (lft < (tims.start - t->shift))||(lft >= (tims.end - t->shift))||(lft >= (tims.last - t->shift));
rghinv = (rgh < (tims.start - t->shift))||(rgh >= (tims.end - t->shift))||(rgh >= (tims.last - t->shift));

xlft = (lft + t->shift - tims.start) * pxns;
xrgh = (rgh + t->shift - tims.start) * pxns;

xlftd = xlft - x;
if(xlftd<(gdouble)0.0) xlftd = ((gdouble)0.0) - xlftd;

xrghd = xrgh - x;
if(xrghd<(gdouble)0.0) xrghd = ((gdouble)0.0) - xrghd;

if(xlftd<=xrghd)
	{
	if((!lftinv)&&(xlftd<=cursor_snap)) marker = lft + t->shift;
	}
	else
	{
	if((!rghinv)&&(xrghd<=cursor_snap)) marker = rgh + t->shift;
	}

return(marker);
}


static void draw_named_markers(void)
{
gdouble pixstep;
gint xl, y;
int i;
TimeType t;

for(i=0;i<26;i++)
{
if((t=named_markers[i])!=-1)
	{
	if((t>=tims.start)&&(t<=tims.last)
		&&(t<=tims.end))
		{
		/* this needs to be here rather than outside the loop as gcc does some
		   optimizations that cause it to calculate slightly different from the marker if it's not here */
		pixstep=((gdouble)nsperframe)/((gdouble)pixelsperframe);

		xl=((gdouble)(t-tims.start))/pixstep;     /* snap to integer */
		if((xl>=0)&&(xl<wavewidth))
			{
			char nbuff[2];
			nbuff[0]='A'+i; nbuff[1]=0x00;

			for(y=fontheight-1;y<=waveheight-1;y+=8)
				{
				gdk_draw_line(wavepixmap,
					gc_mark,
        	        		xl, y, xl, y+5);
				}

			gdk_draw_string(wavepixmap, wavefont_smaller,
				gc_mark,
				xl-(gdk_string_measure(wavefont_smaller, nbuff)>>1), 
				fontheight-2, nbuff);
			}
		}
	}
}
}


static void sync_marker(void)
{
if((tims.prevmarker==-1)&&(tims.marker!=-1))
	{
	signalwindow_width_dirty=1;
	}
	else
if((tims.marker==-1)&&(tims.prevmarker!=-1))
	{
	signalwindow_width_dirty=1;
	}
tims.prevmarker=tims.marker;
}


static void draw_marker(void)
{
gdouble pixstep;
gint xl;

if(!wavearea->window) return;

m1x=m2x=-1;

if(tims.baseline>=0)
	{
	if((tims.baseline>=tims.start)&&(tims.baseline<=tims.last)
		&&(tims.baseline<=tims.end))
		{
		pixstep=((gdouble)nsperframe)/((gdouble)pixelsperframe);
		xl=((gdouble)(tims.baseline-tims.start))/pixstep;     /* snap to integer */
		if((xl>=0)&&(xl<wavewidth))
			{
			gdk_draw_line(wavearea->window,
				gc_baseline,
                		xl, fontheight-1, xl, waveheight-1);
			}
		}
	}

if(tims.marker>=0)
	{
	if((tims.marker>=tims.start)&&(tims.marker<=tims.last)
		&&(tims.marker<=tims.end))
		{
		pixstep=((gdouble)nsperframe)/((gdouble)pixelsperframe);
		xl=((gdouble)(tims.marker-tims.start))/pixstep;     /* snap to integer */
		if((xl>=0)&&(xl<wavewidth))
			{
			gdk_draw_line(wavearea->window,
				gc_umark,
                		xl, fontheight-1, xl, waveheight-1);
			m1x=xl;
			}
		}
	}


if((enable_ghost_marker)&&(in_button_press)&&(tims.lmbcache>=0))
	{
	if((tims.lmbcache>=tims.start)&&(tims.lmbcache<=tims.last)
		&&(tims.lmbcache<=tims.end))
		{
		pixstep=((gdouble)nsperframe)/((gdouble)pixelsperframe);
		xl=((gdouble)(tims.lmbcache-tims.start))/pixstep;     /* snap to integer */
		if((xl>=0)&&(xl<wavewidth))
			{
			gdk_draw_line(wavearea->window,
				gc_umark,
                		xl, fontheight-1, xl, waveheight-1);
			m2x=xl;
			}
		}
	}

if(m1x>m2x)		/* ensure m1x <= m2x for partitioned refresh */
	{
	gint t;

	t=m1x;
	m1x=m2x;
	m2x=t;
	}

if(m1x==-1) m1x=m2x;	/* make both markers same if no ghost marker or v.v. */
}


static void draw_marker_partitions(void)
{
draw_marker();

if(m1x==m2x)
	{
	gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
	wavepixmap, m1x, 0, m1x, 0, 1, fontheight-2);

	if(m1x<0)
		{
		gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
		wavepixmap, 0, 0, 0, 0, wavewidth, waveheight);
		}
		else
		{
		if(m1x==0)
			{
			gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
			wavepixmap, 1, 0, 1, 0, wavewidth-1, waveheight);
			}
		else
		if(m1x==wavewidth-1)
			{

			gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
			wavepixmap, 0, 0, 0, 0, wavewidth-1, waveheight);
			}
		else
			{
			gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
			wavepixmap, 0, 0, 0, 0, m1x, waveheight);
			gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
			wavepixmap, m1x+1, 0, m1x+1, 0, wavewidth-m1x-1, waveheight);
			}
		}
	}
	else
	{
	gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
	wavepixmap, m1x, 0, m1x, 0, 1, fontheight-2);
	gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
	wavepixmap, m2x, 0, m2x, 0, 1, fontheight-2);

	if(m1x>0)
		{
		gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
		wavepixmap, 0, 0, 0, 0, m1x, waveheight);
		}

	if(m2x-m1x>1)
		{
		gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
		wavepixmap, m1x+1, 0, m1x+1, 0, m2x-m1x-1, waveheight);
		}

	if(m2x!=wavewidth-1)
		{
		gdk_draw_pixmap(wavearea->window, wavearea->style->fg_gc[GTK_WIDGET_STATE(wavearea)],
		wavepixmap, m2x+1, 0, m2x+1, 0, wavewidth-m2x-1, waveheight);
		}
	}

/* keep baseline from getting obliterated */
if(tims.baseline>=0)
	{
	draw_marker();
	}
}

static void renderblackout(void)
{
gfloat pageinc;
TimeType lhs, rhs, lclip, rclip;
struct blackout_region_t *bt = blackout_regions;

if(bt)
	{
	pageinc=(gfloat)(((gdouble)wavewidth)*nspx);
	lhs = tims.start;
	rhs = pageinc + lhs;

	while(bt)
		{
		if( ((bt->bstart <= lhs) && (bt->bend >= lhs)) || ((bt->bstart >= lhs) && (bt->bstart <= rhs)) )
			{
			lclip = bt->bstart; rclip = bt->bend;

			if(lclip < lhs) lclip = lhs;
				else if (lclip > rhs) lclip = rhs;

			if(rclip < lhs) rclip = lhs;

			lclip -= lhs;
			rclip -= lhs;
			if(rclip>(wavewidth+1)) rclip = wavewidth+1;
			
			gdk_draw_rectangle(wavepixmap, gc_xfill, TRUE, 
				(((gdouble)lclip)*pxns), fontheight,
				(((gdouble)(rclip-lclip))*pxns), waveheight-fontheight);
			}

		bt=bt->next;
		}
	}
}

static void     
service_hslider(GtkWidget *text, gpointer data)
{
DEBUG(printf("Wave HSlider Moved\n"));

if((wavepixmap)&&(wavewidth>1))
	{
	GtkAdjustment *hadj;

	hadj=GTK_ADJUSTMENT(wave_hslider);

	if(!tims.timecache)
		{
		tims.start=time_trunc(hadj->value);
		}
		else
		{
		tims.start=time_trunc(tims.timecache);
		tims.timecache=0;	/* reset */
		}

	if(tims.start<tims.first) tims.start=tims.first;
		else if(tims.start>tims.last) tims.start=tims.last;

	tims.laststart=tims.start;

	gdk_draw_rectangle(wavepixmap, gc_back, TRUE, 0, 0,
		wavewidth, waveheight);
	rendertimebar();
	}
}

static void     
service_vslider(GtkWidget *text, gpointer data)
{
GtkAdjustment *sadj, *hadj;
int trtarget;
int xsrc;

if(signalpixmap)
	{
	hadj=GTK_ADJUSTMENT(signal_hslider);
	sadj=GTK_ADJUSTMENT(wave_vslider);
	xsrc=(gint)hadj->value;

	trtarget=(int)(sadj->value);
	DEBUG(printf("Wave VSlider Moved to %d\n",trtarget));

		gdk_draw_rectangle(signalpixmap, 
			signalarea->style->bg_gc[GTK_STATE_PRELIGHT], TRUE, 0, 0,
	            	signal_fill_width, signalarea->allocation.height);
	
		sync_marker();
		RenderSigs(trtarget,(old_wvalue==sadj->value)?0:1);

	old_wvalue=sadj->value;

		draw_named_markers();
		gdk_draw_pixmap(signalarea->window, signalarea->style->fg_gc[GTK_WIDGET_STATE(signalarea)],
	                signalpixmap,
			xsrc, 0,
			0, 0,
	                signalarea->allocation.width, signalarea->allocation.height);
		draw_marker();
	}
}

static void button_press_release_common(void)
{
MaxSignalLength();
gdk_draw_rectangle(signalpixmap, 
	signalarea->style->bg_gc[GTK_STATE_PRELIGHT], TRUE, 0, 0,
        signal_fill_width, signalarea->allocation.height);
sync_marker();
RenderSigs((int)(GTK_ADJUSTMENT(wave_vslider)->value),0);
gdk_draw_pixmap(signalarea->window, signalarea->style->fg_gc[GTK_WIDGET_STATE(signalarea)],
      	signalpixmap,
	(gint)(GTK_ADJUSTMENT(signal_hslider)->value), 0,
	0, 0,
        signalarea->allocation.width, signalarea->allocation.height);
}

static void button_motion_common(gint xin, gint yin, int pressrel, int is_button_2) 
{
gdouble x,offset,pixstep;
TimeType newcurr;
if(xin<0) xin=0;
if(xin>(wavewidth-1)) xin=(wavewidth-1);

x=xin;	/* for pix time calc */

pixstep=((gdouble)nsperframe)/((gdouble)pixelsperframe);
newcurr=(TimeType)(offset=((gdouble)tims.start)+(x*pixstep));

if(offset-newcurr>0.5)	/* round to nearest integer ns */
	{
	newcurr++;
	}

if(newcurr>tims.last) 		/* sanity checking */
	{
	newcurr=tims.last;
	}
if(newcurr>tims.end)
	{
	newcurr=tims.end;
	}
if(newcurr<tims.start)
	{
	newcurr=tims.start;
	}

if(!is_button_2)
	{
	update_markertime(tims.marker=cook_markertime(time_trunc(newcurr), xin, yin));
	if(tims.lmbcache<0) tims.lmbcache=time_trunc(newcurr);

	draw_marker_partitions();

	if((pressrel)||(constant_marker_update)) 
		{
		button_press_release_common();
		}
	}
	else
	{
	tims.baseline = ((tims.baseline<0)||(is_button_2<0)) ? cook_markertime(time_trunc(newcurr), xin, yin) : -1;
	update_basetime(tims.baseline);
	update_markertime(tims.marker);
	wavearea_configure_event(wavearea, NULL);
	}
}

static gint motion_notify_event(GtkWidget *widget, GdkEventMotion *event)
{
gdouble x, y, pixstep, offset;
GdkModifierType state;
TimeType newcurr;
int scrolled;

#ifdef WAVE_USE_GTK2
gint xi, yi;
#endif

if(event->is_hint)   
        {
	WAVE_GDK_GET_POINTER(event->window, &x, &y, &xi, &yi, &state);
	WAVE_GDK_GET_POINTER_COPY;
        }
        else
        {
        x = event->x;
        y = event->y;
        state = event->state;
        }

do
	{
	scrolled=0;
	if(state&bmask[in_button_press]) /* needed for retargeting in AIX/X11 */
		{
		if(x<0)
			{ 
			if(wave_scrolling)
			if(tims.start>tims.first)
				{
				if(nsperframe<10) 
					{
					tims.start-=nsperframe;
					}
					else
					{
					tims.start-=(nsperframe/10);
					}
				if(tims.start<tims.first) tims.start=tims.first;
				GTK_ADJUSTMENT(wave_hslider)->value=
					tims.marker=time_trunc(tims.timecache=tims.start);
	
				gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed");
				gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed");
				scrolled=1;
				}	
			x=0; 
			}
		else
		if(x>wavewidth) 
			{ 
			if(wave_scrolling)
			if(tims.start!=tims.last)
				{
				gfloat pageinc;
		
				pageinc=(gfloat)(((gdouble)wavewidth)*nspx);
	
				if(nsperframe<10) 
					{
					tims.start+=nsperframe;
					}
					else
					{
					tims.start+=(nsperframe/10);
					}
	
				if(tims.start>tims.last-pageinc+1) tims.start=time_trunc(tims.last-pageinc+1);
				if(tims.start<tims.first) tims.start=tims.first;
	
				tims.marker=time_trunc(tims.start+pageinc);
				if(tims.marker>tims.last) tims.marker=tims.last;
		
				GTK_ADJUSTMENT(wave_hslider)->value=tims.timecache=tims.start;
	
				gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed");
				gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed");
				scrolled=1;
				}
			x=wavewidth-1; 
			}
		}	
	else if((state&GDK_BUTTON2_MASK)&&(tims.baseline>=0))
		{
		button_motion_common(x,y,0,-1); /* neg one says don't clear tims.baseline */
		}

	pixstep=((gdouble)nsperframe)/((gdouble)pixelsperframe);
	newcurr=tims.start+(offset=x*pixstep);
	if((offset-((int)offset))>0.5)  /* round to nearest integer ns */
	        {
	        newcurr++;
	        }
	
	if(newcurr>tims.last) newcurr=tims.last;
	
	if(newcurr!=prevtim)
		{
		update_currenttime(time_trunc(newcurr));
		prevtim=newcurr;
		}
	
	if(state&bmask[in_button_press])
		{
		button_motion_common(x,y,0,0);
		}

	/* warp selected signals if CTRL is pressed */
        if((event->state & GDK_CONTROL_MASK)&&(state&GDK_BUTTON1_MASK))
		{
	  	int warp = 0;
          	Trptr t = traces.first;
		TimeType gt, delta;

          	while ( t )
          		{
            		if ( t->flags & TR_HIGHLIGHT )
            			{
	      			warp++;

				if(!t->shift_drag_valid)
					{
					t->shift_drag = t->shift;
					t->shift_drag_valid = 1;
					}

              			gt = t->shift_drag + (tims.marker - tims.lmbcache);

		        	if(gt<0)
        		        	{
		                	delta=tims.first-tims.last;
		                	if(gt<delta) gt=delta;
		                	}
		       		else 
				if(gt>0)
		                	{
		                	delta=tims.last-tims.first;
		                	if(gt>delta) gt=delta;
		                	}
				t->shift = gt;
            			}

            		t = t->t_next;
          		}

	  	if( warp )
	  		{
/* commented out to reduce on visual noise...

            		signalwindow_width_dirty = 1;
            		MaxSignalLength(  );

...commented out to reduce on visual noise */

            		signalarea_configure_event( signalarea, NULL );
            		wavearea_configure_event( wavearea, NULL );
          		}
		}

	if(scrolled)	/* make sure counters up top update.. */
		{
		while(gtk_events_pending())
			{
			gtk_main_iteration();
			}
		}

	WAVE_GDK_GET_POINTER(event->window, &x, &y, &xi, &yi, &state);
	WAVE_GDK_GET_POINTER_COPY;

	} while((scrolled)&&(state&bmask[in_button_press]));

return(TRUE);
}

#ifdef WAVE_USE_GTK2
static        gint
scroll_event( GtkWidget * widget, GdkEventScroll * event )
{
  DEBUG(printf("Mouse Scroll Event\n"));
  switch ( event->direction )
  {
    case GDK_SCROLL_UP:
      if ( event->state & GDK_CONTROL_MASK )
        service_left_shift(NULL, 0);
      else if ( event->state & GDK_MOD1_MASK )
	service_zoom_out(NULL, 0);
      else
        service_left_page(NULL, 0);
      break;
    case GDK_SCROLL_DOWN:
      if ( event->state & GDK_CONTROL_MASK )
        service_right_shift(NULL, 0);
      else if ( event->state & GDK_MOD1_MASK )
	service_zoom_in(NULL, 0);
      else
        service_right_page(NULL, 0);
      break;

    default:
      break;
  }
  return(TRUE);
}
#endif

static gint button_press_event(GtkWidget *widget, GdkEventButton *event)
{
if((event->button==1)||((event->button==3)&&(!in_button_press)))
	{
	in_button_press=event->button;
	
	DEBUG(printf("Button Press Event\n"));
	button_motion_common(event->x,event->y,1,0);
	tims.timecache=tims.start;

	gdk_pointer_grab(widget->window, FALSE,
		m_bmask[in_button_press] | 				/* key up on motion for button pressed ONLY */
		GDK_POINTER_MOTION_HINT_MASK |
	      	GDK_BUTTON_RELEASE_MASK, NULL, NULL, event->time);
	
	if ((event->state & GDK_CONTROL_MASK) && (event->button==1))
		{
		Trptr t = traces.first;

		while(t)
			{
			if((t->flags & TR_HIGHLIGHT)&&(!t->shift_drag_valid))			
				{
				t->shift_drag = t->shift; /* cache old value */
				t->shift_drag_valid = 1;
				}
			t=t->t_next;
			}
		}
	}
else
if(event->button==2)
	{
	button_motion_common(event->x,event->y,1,1);
	}

return(TRUE);
}

static gint button_release_event(GtkWidget *widget, GdkEventButton *event)
{
if((event->button)&&(event->button==in_button_press))
	{
	in_button_press=0;

	DEBUG(printf("Button Release Event\n"));
	button_motion_common(event->x,event->y,1,0);

	/* warp selected signals if CTRL is pressed */
        if((event->button==1))
		{
	  	int warp = 0;
          	Trptr t = traces.first;

		if(event->state & GDK_CONTROL_MASK)
			{	
			TimeType gt, delta;

	          	while ( t )
	          		{
	            		if ( t->flags & TR_HIGHLIGHT )
	            			{
		      			warp++;
	              			gt = (t->shift_drag_valid ? t-> shift_drag : t->shift) + (tims.marker - tims.lmbcache);
	
			        	if(gt<0)
	        		        	{
			                	delta=tims.first-tims.last;
			                	if(gt<delta) gt=delta;
			                	}
			       		else 
					if(gt>0)
			                	{
			                	delta=tims.last-tims.first;
			                	if(gt>delta) gt=delta;
			                	}
					t->shift = gt;
	
	              			t->flags &= ( ~TR_HIGHLIGHT );
	            			}
	
				t->shift_drag_valid = 0;
	            		t = t->t_next;
	          		}
			}
			else	/* back out warp and keep highlighting */
			{
			while(t)
				{
				if(t->shift_drag_valid)
					{
					t->shift = t->shift_drag;
					t->shift_drag_valid = 0;
					warp++;
					}
				t=t->t_next;
				}
			}

	  	if( warp )
	  		{
            		signalwindow_width_dirty = 1;
            		MaxSignalLength(  );
            		signalarea_configure_event( signalarea, NULL );
            		wavearea_configure_event( wavearea, NULL );
          		}
		}

	tims.timecache=tims.start;

	gdk_pointer_ungrab(event->time);

	if(event->button==3)	/* oh yeah, dragzoooooooom! */
		{
		service_dragzoom(tims.lmbcache, tims.marker);
		}

	tims.lmbcache=-1;
	update_markertime(time_trunc(tims.marker));
	}

tims.timecache=0;
return(TRUE);
}

gint wavearea_configure_event(GtkWidget *widget, GdkEventConfigure *event)
{
DEBUG(printf("WaveWin Configure Event h: %d, w: %d\n",widget->allocation.height,
		widget->allocation.width));

if(wavepixmap)
	{
	if((wavewidth!=widget->allocation.width)||(waveheight!=widget->allocation.height))
		{
		gdk_pixmap_unref(wavepixmap);
		wavepixmap=gdk_pixmap_new(widget->window, wavewidth=widget->allocation.width,
			waveheight=widget->allocation.height, -1);
		}
	old_wvalue=-1.0;
	}
	else
	{
	wavepixmap=gdk_pixmap_new(widget->window, wavewidth=widget->allocation.width,
		waveheight=widget->allocation.height, -1);
	}

if(!made_gc_contexts)
	{
	gc_back   = alloc_color(wavearea, color_back, wavearea->style->white_gc);    
	gc_baseline = alloc_color(wavearea, color_baseline, wavearea->style->bg_gc[GTK_STATE_SELECTED]);    
	gc_grid   = alloc_color(wavearea, color_grid, wavearea->style->bg_gc[GTK_STATE_PRELIGHT]);
	gc_time   = alloc_color(wavearea, color_time, wavearea->style->black_gc);
	gc_timeb  = alloc_color(wavearea, color_timeb, wavearea->style->bg_gc[GTK_STATE_ACTIVE]);
	gc_value  = alloc_color(wavearea, color_value, wavearea->style->black_gc);
	gc_low    = alloc_color(wavearea, color_low, wavearea->style->black_gc);    
	gc_high   = alloc_color(wavearea, color_high, wavearea->style->black_gc);    
	gc_trans  = alloc_color(wavearea, color_trans, wavearea->style->black_gc);    
	gc_mid    = alloc_color(wavearea, color_mid, wavearea->style->black_gc);    
	gc_xfill  = alloc_color(wavearea, color_xfill, wavearea->style->bg_gc[GTK_STATE_PRELIGHT]);
	gc_x      = alloc_color(wavearea, color_x, wavearea->style->black_gc);
	gc_vbox   = alloc_color(wavearea, color_vbox, wavearea->style->black_gc);
	gc_vtrans = alloc_color(wavearea, color_vtrans, wavearea->style->black_gc);
	gc_mark   = alloc_color(wavearea, color_mark, wavearea->style->bg_gc[GTK_STATE_SELECTED]);
	gc_umark  = alloc_color(wavearea, color_umark, wavearea->style->bg_gc[GTK_STATE_SELECTED]);

        gc_0      = alloc_color(wavearea, color_0, wavearea->style->black_gc);
        gc_1      = alloc_color(wavearea, color_1, wavearea->style->black_gc);
        gc_ufill  = alloc_color(wavearea, color_ufill, wavearea->style->bg_gc[GTK_STATE_PRELIGHT]);
        gc_u      = alloc_color(wavearea, color_u, wavearea->style->black_gc);
        gc_wfill  = alloc_color(wavearea, color_wfill, wavearea->style->bg_gc[GTK_STATE_PRELIGHT]);
        gc_w      = alloc_color(wavearea, color_w, wavearea->style->black_gc);
        gc_dashfill= alloc_color(wavearea, color_dashfill, wavearea->style->bg_gc[GTK_STATE_PRELIGHT]);
        gc_dash   = alloc_color(wavearea, color_dash, wavearea->style->black_gc);

	made_gc_contexts=~0;
	}

if(wavewidth>1)
	{
	if(!do_initial_zoom_fit)
		{
		calczoom(tims.zoom);
		fix_wavehadj();
		gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed"); /* force zoom update */ 
		gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed"); /* force zoom update */
		}
		else
		{
		do_initial_zoom_fit=0;
		service_zoom_fit(NULL,NULL);
		}	
	}

/* tims.timecache=tims.laststart; */
return(TRUE);
}

static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
{
gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
		wavepixmap, 
		event->area.x, event->area.y,
		event->area.x, event->area.y,
		event->area.width, event->area.height);
draw_marker();

return(FALSE);
}

GtkWidget *
create_wavewindow(void)
{
GtkWidget *table;
GtkWidget *frame;
GtkAdjustment *hadj, *vadj;

table = gtk_table_new(10, 10, FALSE);
wavearea=gtk_drawing_area_new();
gtk_widget_show(wavearea);

gtk_widget_set_events(wavearea,
#ifdef WAVE_USE_GTK2
		GDK_SCROLL_MASK |
#endif
                GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
                GDK_BUTTON_RELEASE_MASK |
                GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
                );

gtk_signal_connect(GTK_OBJECT(wavearea), "configure_event",
                        GTK_SIGNAL_FUNC(wavearea_configure_event), NULL);
gtk_signal_connect(GTK_OBJECT(wavearea), "expose_event",
                        GTK_SIGNAL_FUNC(expose_event), NULL);
gtk_signal_connect(GTK_OBJECT(wavearea), "motion_notify_event",
                        GTK_SIGNAL_FUNC(motion_notify_event), NULL);
gtk_signal_connect(GTK_OBJECT(wavearea), "button_press_event",
                        GTK_SIGNAL_FUNC(button_press_event), NULL);
gtk_signal_connect(GTK_OBJECT(wavearea), "button_release_event",
                        GTK_SIGNAL_FUNC(button_release_event), NULL);

#ifdef WAVE_USE_GTK2
gtk_signal_connect(GTK_OBJECT(wavearea), "scroll_event",
                        GTK_SIGNAL_FUNC(scroll_event), NULL);
GTK_WIDGET_SET_FLAGS( wavearea, GTK_CAN_FOCUS );
#endif

gtk_table_attach (GTK_TABLE (table), wavearea, 0, 9, 0, 9,
                        GTK_FILL | GTK_EXPAND,
                        GTK_FILL | GTK_EXPAND | GTK_SHRINK, 3, 3);

wave_vslider=gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
vadj=GTK_ADJUSTMENT(wave_vslider);
gtk_signal_connect(GTK_OBJECT(wave_vslider), "value_changed",
                        GTK_SIGNAL_FUNC(service_vslider), NULL);
vscroll=gtk_vscrollbar_new(vadj);
GTK_WIDGET_SET_FLAGS(vscroll, GTK_CAN_FOCUS);
gtk_widget_show(vscroll);
gtk_table_attach (GTK_TABLE (table), vscroll, 9, 10, 0, 9,
                        GTK_FILL,
                        GTK_FILL | GTK_SHRINK, 3, 3);

wave_hslider=gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
hadj=GTK_ADJUSTMENT(wave_hslider);
gtk_signal_connect(GTK_OBJECT(wave_hslider), "value_changed",
                        GTK_SIGNAL_FUNC(service_hslider), NULL);
hscroll=gtk_hscrollbar_new(hadj);
GTK_WIDGET_SET_FLAGS(hscroll, GTK_CAN_FOCUS);

gtk_widget_show(hscroll);
gtk_table_attach (GTK_TABLE (table), hscroll, 0, 9, 9, 10,
                        GTK_FILL,
                        GTK_FILL | GTK_SHRINK, 3, 3);
gtk_widget_show(table);

frame=gtk_frame_new("Waves");
gtk_container_border_width(GTK_CONTAINER(frame),2);

gtk_container_add(GTK_CONTAINER(frame),table);
return(frame);
}
   

/**********************************************/

void RenderSigs(int trtarget, int update_waves)
{
Trptr t;
int i, trwhich;
int num_traces_displayable;
GtkAdjustment *hadj;
int xsrc;

hadj=GTK_ADJUSTMENT(signal_hslider);
xsrc=(gint)hadj->value;

num_traces_displayable=signalarea->allocation.height/(fontheight);
num_traces_displayable--;   /* for the time trace that is always there */

gdk_draw_rectangle(signalpixmap, signalarea->style->bg_gc[GTK_STATE_ACTIVE], TRUE, 
		0, -1, signal_fill_width, fontheight); 
gdk_draw_line(signalpixmap, signalarea->style->white_gc, 
		0, fontheight-1, signal_fill_width-1, fontheight-1);
gdk_draw_string(signalpixmap, signalfont, 
	signalarea->style->fg_gc[0], 3+xsrc, fontheight-4, "Time");

t=traces.first;
trwhich=0;
while(t)
        {
        if((trwhich<trtarget)&&(GiveNextTrace(t)))
                {
		trwhich++;
                t=GiveNextTrace(t);
                }
                else
                {
                break;
                }
        }

topmost_trace=t;
if(t)
        {
        for(i=0;(i<num_traces_displayable)&&(t);i++)
                {
		RenderSig(t, i, 0);
                t=GiveNextTrace(t);
                }
        }

if((wavepixmap)&&(update_waves))
	{
	gdk_draw_rectangle(wavepixmap, gc_back, TRUE, 0, fontheight-1,
	                        wavewidth, waveheight-fontheight+1);

	if(display_grid) rendertimes();
	rendertraces();
	}
}


int RenderSig(Trptr t, int i, int dobackground)
{
int texty, liney;
int retval;
char buf[65];
int bufxlen;

UpdateSigValue(t); /* in case it's stale on nonprop */
if((t->name)&&(t->shift))
	{
	buf[0]='`';
	reformat_time(buf+1, t->shift, time_dimension);
	strcpy(buf+strlen(buf+1)+1,"\'");
	bufxlen=gdk_string_measure(signalfont, buf);
	}
	else
	{
	bufxlen=0;
	}

liney=((i+2)*fontheight)-2;
texty=liney-(signalfont->descent);

retval=liney-fontheight+1;

if(!(t->flags&TR_HIGHLIGHT)) 
	{
	if(dobackground)	/* for the highlight routines in signalwindow.c */
		{
		if(dobackground==2)
			{
			gdk_draw_rectangle(signalpixmap, signalarea->style->bg_gc[GTK_STATE_NORMAL], TRUE, 
				0, retval,
		            	signal_fill_width, fontheight-1);
			}
			else
			{
			gdk_draw_rectangle(signalpixmap, signalarea->style->bg_gc[GTK_STATE_PRELIGHT], TRUE, 
				0, retval,
		            	signal_fill_width, fontheight-1);
			}
		}

	gdk_draw_line(signalpixmap, 
		signalarea->style->white_gc,
		0, liney,
		signal_fill_width-1, liney);

	if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
		{
		if(t->name)
			{
			if(bufxlen)
				{
				int baselen=gdk_string_measure(signalfont, t->name);
				int combined=baselen+bufxlen;

				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->fg_gc[0],
				        left_justify_sigs?3:3+max_signal_name_pixel_width-
						combined, 
					texty,
				        t->name);
				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->fg_gc[0],
				        left_justify_sigs?3+baselen:3+max_signal_name_pixel_width-
						bufxlen, 
					texty,
				        buf);
				}
				else
				{
				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->fg_gc[0],
				        left_justify_sigs?3:3+max_signal_name_pixel_width-
						gdk_string_measure(signalfont, t->name), 
					texty,
				        t->name);
				}
			}

		if((t->asciivalue)&&(!(t->flags&TR_EXCLUDE)))
			gdk_draw_string(signalpixmap,
				signalfont,
	        		signalarea->style->fg_gc[0],
	        		max_signal_name_pixel_width+6,
				texty,
	        		t->asciivalue);
		}
		else
		{
		if(t->name)
			{
			int comment_color = (t->flags&TR_COLLAPSED) ? GTK_STATE_INSENSITIVE : GTK_STATE_SELECTED;

			if(bufxlen)
				{
				int baselen=gdk_string_measure(signalfont, t->name);
				int combined=baselen+bufxlen;

				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->bg_gc[comment_color],
				        left_justify_sigs?3:3+max_signal_name_pixel_width-
						combined, 
					texty,
				        t->name);
				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->bg_gc[comment_color],
				        left_justify_sigs?3+baselen:3+max_signal_name_pixel_width-
						bufxlen, 
					texty,
				        buf);
				}
				else
				{
				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->bg_gc[comment_color],
				        left_justify_sigs?3:3+max_signal_name_pixel_width-
						gdk_string_measure(signalfont, t->name), 
					texty,
				        t->name);
				}
			}
		}
	}
	else
	{
	gdk_draw_rectangle(signalpixmap, signalarea->style->bg_gc[GTK_STATE_SELECTED], TRUE, 
		0, retval,
            	signal_fill_width, fontheight-1);
	gdk_draw_line(signalpixmap, 
		signalarea->style->white_gc,
		0, liney,
		signal_fill_width-1, liney);

	if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
		{
		if(t->name)
			{
			if(bufxlen)
				{
				int baselen=gdk_string_measure(signalfont, t->name);
				int combined=baselen+bufxlen;

				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->white_gc,
				        left_justify_sigs?3:3+max_signal_name_pixel_width-
						combined, 
					texty,
				        t->name);
				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->white_gc,
				        left_justify_sigs?3+baselen:3+max_signal_name_pixel_width-
						bufxlen, 
					texty,
				        buf);
				}
				else
				{
				gdk_draw_string(signalpixmap,
					signalfont,
				        signalarea->style->white_gc,
				        left_justify_sigs?3:3+max_signal_name_pixel_width-
						gdk_string_measure(signalfont, t->name), 
					texty,
				        t->name);
				}
			}

		if((t->asciivalue)&&(!(t->flags&TR_EXCLUDE)))
			gdk_draw_string(signalpixmap,
		      	signalfont,
		       	signalarea->style->white_gc,
		        max_signal_name_pixel_width+6,
			texty,
		        t->asciivalue);
		}
		else
		{
		if(t->name)
		gdk_draw_string(signalpixmap,
		      	signalfont,
		        (dobackground==2)?signalarea->style->bg_gc[GTK_STATE_PRELIGHT]:signalarea->style->bg_gc[GTK_STATE_ACTIVE],
		        left_justify_sigs?3:3+max_signal_name_pixel_width-
				gdk_string_measure(signalfont, t->name), 
			texty,
  		        t->name);
		}
	} 

return(retval);
}

/***************************************************************************/

void MaxSignalLength(void)
{
Trptr t;
int len=0,maxlen=0;
int vlen=0, vmaxlen=0;
char buf[65];
int bufxlen;

DEBUG(printf("signalwindow_width_dirty: %d\n",signalwindow_width_dirty));

if((!signalwindow_width_dirty)&&(use_nonprop_fonts)) return;

signalwindow_width_dirty=0;

t=traces.first;
while(t)
{
if(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH))	/* for "comment" style blank traces */
	{
	if(t->name)
		{
		len=gdk_string_measure(signalfont, t->name);
		if(len>maxlen) maxlen=len;
		}
	t=GiveNextTrace(t);
	}
else
if(t->name)
	{
	if((shift_timebase=t->shift))
        	{
        	buf[0]='`';
        	reformat_time(buf+1, t->shift, time_dimension);
        	strcpy(buf+strlen(buf+1)+1,"\'");
        	bufxlen=gdk_string_measure(signalfont, buf);
        	}
        	else
        	{
        	bufxlen=0;
        	}

	len=gdk_string_measure(signalfont, t->name)+bufxlen;
	if(len>maxlen) maxlen=len;

	if((tims.marker!=-1)&&(!(t->flags&TR_EXCLUDE)))
		{
		t->asciitime=tims.marker;
		if(t->asciivalue) free_2(t->asciivalue);

		if(t->vector)
			{
			char *str, *str2;
			vptr v;

                        v=bsearch_vector(t->n.vec,tims.marker);
                        str=convert_ascii(t,v);
			if(str)
				{
				str2=(char *)malloc_2(strlen(str)+2);
				*str2='=';
				strcpy(str2+1,str);
				free_2(str);

				vlen=gdk_string_measure(signalfont,str2);
				t->asciivalue=str2;
				}
				else
				{
				vlen=0;
				t->asciivalue=NULL;
				}

			}
			else
			{
			char *str;
			hptr hptr;
			if((hptr=bsearch_node(t->n.nd,tims.marker)))
				{
				if(!t->n.nd->ext)
					{
					str=(char *)calloc_2(1,3*sizeof(char));
					str[0]='=';
					if(t->flags&TR_INVERT)
						{
						str[1]=AN_STR_INV[hptr->v.h_val];
						}
						else
						{
						str[1]=AN_STR[hptr->v.h_val];
						}
					t->asciivalue=str;
					vlen=gdk_string_measure(signalfont,str);
					}
					else
					{
					char *str2;

					if(hptr->flags&HIST_REAL)
						{
						if(!(hptr->flags&HIST_STRING))
							{
							str=convert_ascii_real((double *)hptr->v.h_vector);
							}
							else
							{
							str=convert_ascii_string((char *)hptr->v.h_vector);
							}
						}
						else
						{
		                        	str=convert_ascii_vec(t,hptr->v.h_vector);
						}

					if(str)
						{
						str2=(char *)malloc_2(strlen(str)+2);
						*str2='=';
						strcpy(str2+1,str);

						free_2(str); 

						vlen=gdk_string_measure(signalfont,str2);
						t->asciivalue=str2;
						}
						else
						{
						vlen=0;
						t->asciivalue=NULL;
						}
					}
				}
				else
				{
				vlen=0;
				t->asciivalue=NULL;
				}
			}

		if(vlen>vmaxlen)
			{
			vmaxlen=vlen;
			}
		}

	t=GiveNextTrace(t);
	}
	else
	{
	t=GiveNextTrace(t);
	}
}

max_signal_name_pixel_width = maxlen;
signal_pixmap_width=maxlen+6; 		/* 2 * 3 pixel pad */
if(tims.marker!=-1)
	{
	signal_pixmap_width+=(vmaxlen+6);
	}

if(signal_pixmap_width<60) signal_pixmap_width=60;

if(!in_button_press)
	{
	if(!do_resize_signals)
		{
		int os;
		os=48;
		gtk_widget_set_usize(GTK_WIDGET(signalwindow), 
				os+30, -1);
		}
	else
	if((do_resize_signals)&&(signalwindow))
		{
		int oldusize;
	
		oldusize=signalwindow->allocation.width;
		if(oldusize!=max_signal_name_pixel_width)
			{
			int os;
			os=max_signal_name_pixel_width;
			os=(os<48)?48:os;
			gtk_widget_set_usize(GTK_WIDGET(signalwindow), 
					os+30, -1);
			}
		}
	}
}
/***************************************************************************/

void UpdateSigValue(Trptr t)
{
if(!t) return;
if((t->asciivalue)&&(t->asciitime==tims.marker))return;

if((t->name)&&(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH))))
	{
	shift_timebase=t->shift;
	DEBUG(printf("UpdateSigValue: %s\n",t->name));

	if((tims.marker!=-1)&&(!(t->flags&TR_EXCLUDE)))
		{
		t->asciitime=tims.marker;
		if(t->asciivalue) free_2(t->asciivalue);

		if(t->vector)
			{
			char *str, *str2;
			vptr v;

                        v=bsearch_vector(t->n.vec,tims.marker);
                        str=convert_ascii(t,v);
			if(str)
				{
				str2=(char *)malloc_2(strlen(str)+2);
				*str2='=';
				strcpy(str2+1,str);
				free_2(str);

				t->asciivalue=str2;
				}
				else
				{
				t->asciivalue=NULL;
				}

			}
			else
			{
			char *str;
			hptr hptr;
			if((hptr=bsearch_node(t->n.nd,tims.marker)))
				{
				if(!t->n.nd->ext)
					{
					str=(char *)calloc_2(1,3*sizeof(char));
					str[0]='=';
					if(t->flags&TR_INVERT)
						{
						str[1]=AN_STR_INV[hptr->v.h_val];
						}
						else
						{
						str[1]=AN_STR[hptr->v.h_val];
						}
					t->asciivalue=str;
					}
					else
					{
					char *str2;

					if(hptr->flags&HIST_REAL)
						{
						if(!(hptr->flags&HIST_STRING))
							{
							str=convert_ascii_real((double *)hptr->v.h_vector);
							}
							else
							{
							str=convert_ascii_string((char *)hptr->v.h_vector);
							}
						}
						else
						{
		                        	str=convert_ascii_vec(t,hptr->v.h_vector);
						}

					if(str)
						{
						str2=(char *)malloc_2(strlen(str)+2);
						*str2='=';
						strcpy(str2+1,str);
						free_2(str);

						t->asciivalue=str2;
						}
						else
						{
						t->asciivalue=NULL;
						}
					}
				}
				else
				{
				t->asciivalue=NULL;
				}
			}
		}
	}
}

/***************************************************************************/

void calczoom(gdouble z0)
{
gdouble ppf, frame;
ppf=((gdouble)(pixelsperframe=200));
frame=pow(zoombase,-z0);

if(frame>((gdouble)MAX_HISTENT_TIME/(gdouble)4.0))
	{
	nsperframe=((gdouble)MAX_HISTENT_TIME/(gdouble)4.0);
	}
	else
	if(frame<(gdouble)1.0)
	{
	nsperframe=1.0;
	}
	else
	{
	nsperframe=frame;
	}

hashstep=10.0;

if(zoom_pow10_snap)
if(nsperframe>10.0)
	{
	TimeType nsperframe2;
	gdouble p=10.0;
	gdouble scale;
	int l;
	l=(int)((log(nsperframe)/log(p))+0.5);	/* nearest power of 10 */
	nsperframe2=pow(p, (gdouble)l);

	scale = (gdouble)nsperframe2 / (gdouble)nsperframe;
	ppf *= scale;
	pixelsperframe = ppf;

	nsperframe = nsperframe2;
	hashstep = ppf / 10.0;
	}

nspx=nsperframe/ppf;
pxns=ppf/nsperframe;

time_trunc_set();	/* map nspx to rounding value */

DEBUG(printf("Zoom: %e Pixelsperframe: %lld, nsperframe: %e\n",z0, pixelsperframe,(float)nsperframe));
}

static void renderhash(int x, TimeType tim)
{
TimeType rborder;
int fhminus2;
int rhs;
gdouble dx;
gdouble hashoffset;
int iter = 0;

fhminus2=fontheight-2;

gdk_draw_line(wavepixmap, 
		gc_grid,
		x, 0,
		x, ((!timearray)&&(display_grid)&&(enable_vert_grid))?waveheight:fhminus2);

if(tim==tims.last) return;

rborder=(tims.last-tims.start)*pxns;
DEBUG(printf("Rborder: %lld, Wavewidth: %d\n", rborder, wavewidth));
if(rborder>wavewidth) rborder=wavewidth;
if((rhs=x+pixelsperframe)>rborder) rhs=rborder;

gdk_draw_line(wavepixmap, 
		gc_grid,
		x, wavecrosspiece,
		rhs, wavecrosspiece);

dx = x + (hashoffset=hashstep);
x  = dx;

while((hashoffset<pixelsperframe)&&(x<=rhs)&&(iter<9))
	{
	gdk_draw_line(wavepixmap, 
		gc_grid,
		x, wavecrosspiece,
		x, fhminus2);

	hashoffset+=hashstep;
	dx=dx+hashstep;
	if((pixelsperframe!=200)||(hashstep!=10.0)) iter++; /* fix any roundoff errors */
	x = dx;
	} 

}

static void rendertimes(void)
{
int lastx = -1000; /* arbitrary */
int x, lenhalf;
TimeType tim, rem;
char timebuff[32];
char prevover=0;
gdouble realx;

renderblackout();

tim=tims.start;
tims.end=tims.start+(((gdouble)wavewidth)*nspx);

/***********/
if(timearray)
	{
	int pos, pos2;
	TimeType *t, tm;
	int y=fontheight+2;
	int oldx=-1;

	pos=bsearch_timechain(tims.start);
	top:
	if((pos>=0)&&(pos<timearray_size))
		{
		t=timearray+pos;
		for(;pos<timearray_size;t++, pos++)
			{
			tm=*t;
			if(tm>=tims.start)
				{
				if(tm<=tims.end)
					{
					x=(tm-tims.start)*pxns;
					if(oldx==x) 
						{
						pos2=bsearch_timechain(tims.start+(((gdouble)(x+1))*nspx));
						if(pos2>pos) { pos=pos2; goto top; } else continue;
						}
					oldx=x;
					gdk_draw_line(wavepixmap, gc_grid, x, y, x, waveheight);
					}
					else
					{
					break;
					}
				}
			}
		}
	}
/***********/


DEBUG(printf("Ruler Start time: "TTFormat", Finish time: "TTFormat"\n",tims.start, tims.end));

x=0;
realx=0;
if(tim)
	{
	rem=tim%((TimeType)nsperframe);
	if(rem)
		{
		tim=tim-nsperframe-rem;
		x=-pixelsperframe-((rem*pixelsperframe)/nsperframe);
		realx=-pixelsperframe-((rem*pixelsperframe)/nsperframe);
		}
	}

for(;;)
	{
	renderhash(realx, tim);

	if(tim)
		{
		reformat_time(timebuff, time_trunc(tim), time_dimension);
		}
		else
		{
		strcpy(timebuff, "0");
		}

	lenhalf=gdk_string_measure(wavefont, timebuff) >> 1;

	if((x-lenhalf >= lastx) || (pixelsperframe >= 200))
		{
		gdk_draw_string(wavepixmap,
			wavefont,
		       	gc_time,
			x-lenhalf, wavefont->ascent-1,
		        timebuff);

		lastx = x+lenhalf;
		}

	tim+=nsperframe;
	x+=pixelsperframe;
	realx+=pixelsperframe;
	if((prevover)||(tim>tims.last)) break;
	if(x>=wavewidth) prevover=1;
	}
}

/***************************************************************************/

static void rendertimebar(void)
{
gdk_draw_rectangle(wavepixmap, gc_timeb, TRUE,
		0, -1, wavewidth, fontheight); 
rendertimes();
rendertraces();
}

static void rendertraces(void)
{
if(!topmost_trace)
	{
	topmost_trace=traces.first;
	}

if(topmost_trace)
	{
	Trptr t;
	hptr h;
	vptr v;
	int i, num_traces_displayable;

	num_traces_displayable=wavearea->allocation.height/(fontheight);
	num_traces_displayable--;   /* for the time trace that is always there */

	t=topmost_trace;
	for(i=0;((i<num_traces_displayable)&&(t));i++)
		{
		if(!(t->flags&(TR_EXCLUDE|TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
			{
			shift_timebase=t->shift;
			if(!t->vector)
				{
				h=bsearch_node(t->n.nd, tims.start);
				DEBUG(printf("Bit Trace: %s, %s\n", t->name, t->n.nd->nname));
				DEBUG(printf("Start time: "TTFormat", Histent time: "TTFormat"\n", tims.start,(h->time+shift_timebase)));

				if(!t->n.nd->ext)
					{
					draw_hptr_trace(t,h,i,1,0);
					}
					else
					{
					draw_hptr_trace_vector(t,h,i);
					}
				}
				else
				{
				v=bsearch_vector(t->n.vec, tims.start);
				DEBUG(printf("Vector Trace: %s, %s\n", t->name, t->n.vec->name));
				DEBUG(printf("Start time: "TTFormat", Vectorent time: "TTFormat"\n", tims.start,(v->time+shift_timebase)));
				draw_vptr_trace(t,v,i);
				}
			}
			else
			{
			int kill_dodraw_grid = t->flags & TR_ANALOG_BLANK_STRETCH;

			if(kill_dodraw_grid)
				{
				if(!(t->t_next)) 
					{
					kill_dodraw_grid = 0;
					}
				else
				if(!(t->t_next->flags & TR_ANALOG_BLANK_STRETCH))
					{
					kill_dodraw_grid = 0;
					}
				}
			
			draw_hptr_trace(NULL,NULL,i,0,kill_dodraw_grid);
			}
		t=GiveNextTrace(t);
		}
	}


draw_named_markers();
draw_marker_partitions();
}


/*
 * draw single traces and use this for rendering the grid lines
 * for "excluded" traces
 */
static void draw_hptr_trace(Trptr t, hptr h, int which, int dodraw, int kill_grid)
{
TimeType x0, x1, newtime;
int y0, y1, yu, liney, ytext;
TimeType tim, h2tim;
hptr h2, h3;
char hval, h2val, invert;
GdkGC    *c;
GdkGC    *gcx, *gcxf;
char identifier_str[2];

tims.start-=shift_timebase;
tims.end-=shift_timebase;

liney=((which+2)*fontheight)-2;
if((t)&&(t->flags&TR_INVERT))
	{
	y0=((which+1)*fontheight)+2;	
	y1=liney-2;
	invert=1;
	}
	else
	{
	y1=((which+1)*fontheight)+2;	
	y0=liney-2;
	invert=0;
	}
yu=(y0+y1)/2;
ytext=yu-(wavefont->ascent/2)+wavefont->ascent;


if((display_grid)&&(enable_horiz_grid)&&(!kill_grid))
	{
	gdk_draw_line(wavepixmap, 
		gc_grid,
		(tims.start<tims.first)?(tims.first-tims.start)*pxns:0, liney,
		(tims.last<=tims.end)?(tims.last-tims.start)*pxns:wavewidth-1, liney);
	}

if((h)&&(tims.start==h->time))
	if (h->v.h_val != AN_Z) {

	switch(h->v.h_val)
		{
		case AN_X:	c = gc_x; break;
		case AN_U:	c = gc_u; break;
		case AN_W:	c = gc_w; break;
		case AN_DASH:	c = gc_dash; break;
		default:	c = (h->v.h_val == AN_X) ? gc_x: gc_trans;
		}
	gdk_draw_line(wavepixmap, c, 0, y0, 0, y1);
	}

if(dodraw)
for(;;)
{
if(!h) break;
tim=(h->time);
if((tim>tims.end)||(tim>tims.last)) break;

x0=(tim - tims.start) * pxns;
if(x0<-1) 
	{ 
	x0=-1; 
	}
	else
if(x0>wavewidth)
	{
	break;
	}

h2=h->next;
if(!h2) break;
h2tim=tim=(h2->time);
if(tim>tims.last) tim=tims.last;
	else if(tim>tims.end+1) tim=tims.end+1;
x1=(tim - tims.start) * pxns;
if(x1<-1) 
	{ 
	x1=-1; 
	}
	else
if(x1>wavewidth)
	{
	x1=wavewidth;
	}

if(x0!=x1)
	{
	hval=h->v.h_val;
	h2val=h2->v.h_val;

	switch(h2val)
		{
		case AN_X:	c = gc_x; break;
		case AN_U:	c = gc_u; break;
		case AN_W:	c = gc_w; break;
		case AN_DASH:	c = gc_dash; break;
		default:	c = (hval == AN_X) ? gc_x: gc_trans;
		}

	switch(hval)
		{
		case AN_0:	/* 0 */
		case AN_L:	/* L */
		wave_gdk_draw_line(wavepixmap, 
			(hval==AN_0) ? gc_0 : gc_low,
			x0, y0,
			x1, y0);

		if(h2tim<=tims.end)
		switch(h2val)
			{
			case AN_0:
			case AN_L:	break;

			case AN_Z:	wave_gdk_draw_line(wavepixmap, c, x1, y0, x1, yu); break;
			default:	wave_gdk_draw_line(wavepixmap, c, x1, y0, x1, y1); break;
			}
		break;
	
		case AN_X: /* X */
		case AN_W: /* W */
		case AN_U: /* U */
		case AN_DASH: /* - */

		identifier_str[1] = 0;
		switch(hval)
			{
			case AN_X:	c = gcx = gc_x; gcxf = gc_xfill; identifier_str[0] = 0; break;
			case AN_W:	c = gcx = gc_w; gcxf = gc_wfill; identifier_str[0] = 'W'; break;
			case AN_U:	c = gcx = gc_u; gcxf = gc_ufill; identifier_str[0] = 'U'; break;
			default:	c = gcx = gc_dash; gcxf = gc_dashfill; identifier_str[0] = '-'; break;
			}

		if(invert)
			{
			gdk_draw_rectangle(wavepixmap, 
				gcx, TRUE,
				x0+1, y0, x1-x0, y1-y0+1); 
			}
			else
			{
			gdk_draw_rectangle(wavepixmap, 
				gcxf, TRUE,
				x0+1, y1, x1-x0, y0-y1+1); 
			}

		if(identifier_str[0])
			{
			int x0_new = (x0>=0) ? x0 : 0;
			int width;

			if((width=x1-x0_new)>vector_padding)
				{
				if((x1>=wavewidth)||(gdk_string_measure(wavefont, identifier_str)+vector_padding<=width))
					{
		                        gdk_draw_string(wavepixmap,
		                                wavefont,  
		                                gc_value,  
		                                x0+2,ytext,
		                                identifier_str);
					}
				}
			}

		wave_gdk_draw_line(wavepixmap, 
			gcx,
			x0, y0,
			x1, y0);
		wave_gdk_draw_line(wavepixmap, 
			gcx,
			x0, y1,
			x1, y1);
		if(h2tim<=tims.end) wave_gdk_draw_line(wavepixmap, c, x1, y0, x1, y1);
		break;
		
		case AN_Z: /* Z */
		wave_gdk_draw_line(wavepixmap, 
			gc_mid,
			x0, yu,
			x1, yu);
		if(h2tim<=tims.end)
		switch(h2val)
			{
			case AN_0:
			case AN_L: 	
					wave_gdk_draw_line(wavepixmap, c, x1, yu, x1, y0); break;
			case AN_1:
			case AN_H:
					wave_gdk_draw_line(wavepixmap, c, x1, yu, x1, y1); break;
			default:	wave_gdk_draw_line(wavepixmap, c, x1, y0, x1, y1); break;
			}
		break;
		
		case AN_1: /* 1 */
		case AN_H: /* 1 */
		wave_gdk_draw_line(wavepixmap, 
			(hval==AN_1) ? gc_1 : gc_high,
			x0, y1,
			x1, y1);
		if(h2tim<=tims.end)
		switch(h2val)
			{
			case AN_1:
			case AN_H:	break;

			case AN_0: 	
			case AN_L:
					wave_gdk_draw_line(wavepixmap, c, x1, y1, x1, y0); break;
			case AN_Z:	wave_gdk_draw_line(wavepixmap, c, x1, y1, x1, yu); break;
			default:	wave_gdk_draw_line(wavepixmap, c, x1, y0, x1, y1); break;
			}
		break;

		default:
		break;
		}
	}
	else
	{
	wave_gdk_draw_line(wavepixmap, gc_trans, x1, y0, x1, y1);		
	newtime=(((gdouble)(x1+WAVE_OPT_SKIP))*nspx)+tims.start+shift_timebase;	/* skip to next pixel */
	h3=bsearch_node(t->n.nd,newtime);
	if(h3->time>h->time)
		{
		h=h3;
		continue;
		}
	}

h=h->next;
}

wave_gdk_draw_line_flush(wavepixmap); /* clear out state */

tims.start+=shift_timebase;
tims.end+=shift_timebase;
}

/********************************************************************************************************/

static void draw_hptr_trace_vector_analog(Trptr t, hptr h, int which, int num_extension)
{
TimeType x0, x1, newtime, width;
int y0, y1, yu, liney, yt0, yt1;
TimeType tim, h2tim;
hptr h2, h3;
int endcnt = 0;
int type;
int lasttype=-1;
GdkGC    *c;
double mynan = strtod("NaN", NULL);
double tmin = mynan, tmax = mynan, tv, tv2;

liney=((which+2+num_extension)*fontheight)-2;
y1=((which+1)*fontheight)+2;	
y0=liney-2;
yu=(y0+y1)/2;

h2 = h;
for(;;)
{
if(!h2) break;
tim=(h2->time);
if(tim>tims.end) { endcnt++; if(endcnt==2) break; }
if(tim>tims.last) break;
x0=(tim - tims.start) * pxns;
if((x0>wavewidth)&&(endcnt==2))
	{
	break;
	}
h3=h2;
h2 = h2->next;
if (!h2) break;
tim=(h2->time);
x1=(tim - tims.start) * pxns;
if(x1<0)
	continue;
tv = mynan;
if(h3->flags&HIST_REAL)
	{
	if(!(h3->flags&HIST_STRING) && h3->v.h_vector)
		tv = *(double *)h3->v.h_vector;
	}
	else
	{
	tv=convert_real_vec(t,h3->v.h_vector);
	}
if (!isnan(tv))
	{
	if (isnan(tmin) || tv < tmin)
		tmin = tv;
	if (isnan(tmax) || tv > tmax)
		tmax = tv;
	}
}
if (isnan(tmin) || isnan(tmax))
	tmin = tmax = 0;
if ((tmax - tmin) < 1e-20)
	{
	tmax = 1;
	tmin -= 0.5 * (y1 - y0);
	}
	else
	{
	tmax = (y1 - y0) / (tmax - tmin);
	}

h3 = NULL;
for(;;)
{
if(!h) break;
tim=(h->time);
if((tim>tims.end)||(tim>tims.last)) break;

x0=(tim - tims.start) * pxns;
if(x0<-1) 
	{ 
	x0=-1; 
	}
	else
if(x0>wavewidth)
	{
	break;
	}

h2=h->next;
if(!h2) break;
h2tim=tim=(h2->time);
if(tim>tims.last) tim=tims.last;
	else if(tim>tims.end+1) tim=tims.end+1;
x1=(tim - tims.start) * pxns;
if(x1<-1) 
	{ 
	x1=-1; 
	}
	else
if(x1>wavewidth)
	{
	x1=wavewidth;
	}

/* draw trans */
type = (!(h->flags&(HIST_REAL|HIST_STRING))) ? vtype(t,h->v.h_vector) : AN_0;
tv = tv2 = mynan;
if(h->flags&HIST_REAL)
	{
	if(!(h->flags&HIST_STRING) && h->v.h_vector)
		tv = *(double *)h->v.h_vector;
	}
	else
	{
	tv=convert_real_vec(t,h->v.h_vector);
	}
if(h2->flags&HIST_REAL)
	{
	if(!(h2->flags&HIST_STRING) && h2->v.h_vector)
		tv2 = *(double *)h2->v.h_vector;
	}
	else
	{
	tv2=convert_real_vec(t,h2->v.h_vector);
	}
if(isnan(tv))
	{
	yt0 = yu;
	}
	else
	{
	yt0 = y0 + (tv - tmin) * tmax;
	}
if(isnan(tv2))
	{
	yt1 = yu;
	}
	else
	{
	yt1 = y0 + (tv2 - tmin) * tmax;
	}
if(x0!=x1)
	{
	if(type == AN_0) 
		{
		c = gc_vbox; 
		} 
		else 
		{
		c = gc_x;
		}
	if(t->flags & TR_ANALOG_STEP)
		{
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0, yt0,
			x1, yt0);
		wave_gdk_draw_line(wavepixmap, 
			c,
			x1, yt0,
			x1, yt1);
		}
		else
		{
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0, yt0,
			x1, yt1);
		}
	}
	else
	{
	newtime=(((gdouble)(x1+WAVE_OPT_SKIP))*nspx)+tims.start+shift_timebase;	/* skip to next pixel */
	h3=bsearch_node(t->n.nd,newtime);
	if(h3->time>h->time)
		{
		h=h3;
		lasttype=type;
		continue;
		}
	}

h=h->next;
lasttype=type;
}

wave_gdk_draw_line_flush(wavepixmap);

tims.start+=shift_timebase;
tims.end+=shift_timebase;
}

/*
 * draw hptr vectors (integer+real)
 */
static void draw_hptr_trace_vector(Trptr t, hptr h, int which)
{
TimeType x0, x1, newtime, width;
int y0, y1, yu, liney, ytext;
TimeType tim, h2tim;
hptr h2, h3;
char *ascii=NULL;
int type;
int lasttype=-1;
GdkGC    *c;

tims.start-=shift_timebase;
tims.end-=shift_timebase;

liney=((which+2)*fontheight)-2;
y1=((which+1)*fontheight)+2;	
y0=liney-2;
yu=(y0+y1)/2;
ytext=yu-(wavefont->ascent/2)+wavefont->ascent;

if((display_grid)&&(enable_horiz_grid))
	{
	if(!(t->flags & TR_ANALOGMASK))
		{
		gdk_draw_line(wavepixmap, 
			gc_grid,
			(tims.start<tims.first)?(tims.first-tims.start)*pxns:0, liney,
			(tims.last<=tims.end)?(tims.last-tims.start)*pxns:wavewidth-1, liney);
		}
	}

if((t->flags & TR_ANALOGMASK) && (!(h->flags&HIST_STRING) || !(h->flags&HIST_REAL)))
	{
	Trptr te = t->t_next;
	int ext = 0;

	while(te)
		{
		if(te->flags & TR_ANALOG_BLANK_STRETCH)
			{
			ext++;
			te = te->t_next;
			}
			else
			{
			break;
			}
		}	

	draw_hptr_trace_vector_analog(t, h, which, ext);
	return;
	}

for(;;)
{
if(!h) break;
tim=(h->time);
if((tim>tims.end)||(tim>tims.last)) break;

x0=(tim - tims.start) * pxns;
if(x0<-1) 
	{ 
	x0=-1; 
	}
	else
if(x0>wavewidth)
	{
	break;
	}

h2=h->next;
if(!h2) break;
h2tim=tim=(h2->time);
if(tim>tims.last) tim=tims.last;
	else if(tim>tims.end+1) tim=tims.end+1;
x1=(tim - tims.start) * pxns;
if(x1<-1) 
	{ 
	x1=-1; 
	}
	else
if(x1>wavewidth)
	{
	x1=wavewidth;
	}

/* draw trans */
type = (!(h->flags&(HIST_REAL|HIST_STRING))) ? vtype(t,h->v.h_vector) : AN_0;
if(x0>-1)
if(use_roundcaps)
	{
	if (type == AN_Z) {
		if (lasttype != -1) {
		wave_gdk_draw_line(wavepixmap, 
			(lasttype==AN_X? gc_x:gc_vtrans),
			x0-1, y0,
			x0,   yu);
		wave_gdk_draw_line(wavepixmap, 
			(lasttype==AN_X? gc_x:gc_vtrans),
			x0, yu,
			x0-1, y1);
		}
	} else
	if (lasttype==AN_Z) {
		wave_gdk_draw_line(wavepixmap, 
			(type==AN_X? gc_x:gc_vtrans),
			x0+1, y0,
			x0,   yu);
		wave_gdk_draw_line(wavepixmap, 
			(type==AN_X? gc_x:gc_vtrans),
			x0, yu,
			x0+1, y1);
	} else {
		if (lasttype != type) {
		wave_gdk_draw_line(wavepixmap, 
			(lasttype==AN_X? gc_x:gc_vtrans),
			x0-1, y0,
			x0,   yu);
		wave_gdk_draw_line(wavepixmap, 
			(lasttype==AN_X? gc_x:gc_vtrans),
			x0, yu,
			x0-1, y1);
		wave_gdk_draw_line(wavepixmap, 
			(type==AN_X? gc_x:gc_vtrans),
			x0+1, y0,
			x0,   yu);
		wave_gdk_draw_line(wavepixmap, 
			(type==AN_X? gc_x:gc_vtrans),
			x0, yu,
			x0+1, y1);
		} else {
	wave_gdk_draw_line(wavepixmap, 
		(type==AN_X? gc_x:gc_vtrans),
		x0-2, y0,
		x0+2, y1);
	wave_gdk_draw_line(wavepixmap, 
		(type==AN_X? gc_x:gc_vtrans),
		x0+2, y0,
		x0-2, y1);
		}
	}
	}
	else
	{
	wave_gdk_draw_line(wavepixmap, 
		(type==AN_X? gc_x:gc_vtrans),
		x0, y0,
		x0, y1);
	}

		
if(x0!=x1)
	{
	if (type == AN_Z) 
		{
		if(use_roundcaps)
			{
			wave_gdk_draw_line(wavepixmap, 
				gc_mid,
				x0+1, yu,
				x1-1, yu);
			} 
			else 
			{
			wave_gdk_draw_line(wavepixmap, 
				gc_mid,
				x0, yu,
				x1, yu);
			}
		} 
		else 
		{
		if(type == AN_0) 
			{
			c = gc_vbox; 
			} 
			else 
			{
			c = gc_x;
			}
	
	if(use_roundcaps)
		{
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0+2, y0,
			x1-2, y0);
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0+2, y1,
			x1-2, y1);
		}
		else
		{
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0, y0,
			x1, y0);
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0, y1,
			x1, y1);
		}

if(x0<0) x0=0;	/* fixup left margin */

	if((width=x1-x0)>vector_padding)
		{
		if(h->flags&HIST_REAL)
			{
			if(!(h->flags&HIST_STRING))
				{
				ascii=convert_ascii_real((double *)h->v.h_vector);
				}
				else
				{
				ascii=convert_ascii_string((char *)h->v.h_vector);
				}
			}
			else
			{
			ascii=convert_ascii_vec(t,h->v.h_vector);
			}

		if((x1>=wavewidth)||(gdk_string_measure(wavefont, ascii)+vector_padding<=width))
			{
			gdk_draw_string(wavepixmap,
				wavefont,
			       	gc_value,
				x0+2,ytext,
			        ascii);
			}
		else
			{
			char *mod;

			mod=bsearch_trunc(ascii,width-vector_padding);
			if(mod)
				{
				*mod='+';
				*(mod+1)=0;

				gdk_draw_string(wavepixmap,
					wavefont,
				       	gc_value,
					x0+2,ytext,
				        ascii);
				}
			}

		}
	    }
	}
	else
	{
	newtime=(((gdouble)(x1+WAVE_OPT_SKIP))*nspx)+tims.start+shift_timebase;	/* skip to next pixel */
	h3=bsearch_node(t->n.nd,newtime);
	if(h3->time>h->time)
		{
		h=h3;
		lasttype=type;
		continue;
		}
	}

if(ascii) { free_2(ascii); ascii=NULL; }
h=h->next;
lasttype=type;
}

wave_gdk_draw_line_flush(wavepixmap);

tims.start+=shift_timebase;
tims.end+=shift_timebase;
}

/********************************************************************************************************/

static void draw_vptr_trace_analog(Trptr t, vptr v, int which, int num_extension)
{
TimeType x0, x1, newtime, width;
int y0, y1, yu, liney, yt0, yt1;
TimeType tim, h2tim;
vptr h, h2, h3;
int type;
int lasttype=-1;
GdkGC    *c;
double mynan = strtod("NaN", NULL);
double tmin = mynan, tmax = mynan, tv, tv2;

h=v;
liney=((which+2+num_extension)*fontheight)-2;
y1=((which+1)*fontheight)+2;	
y0=liney-2;
yu=(y0+y1)/2;

h2 = h;
for(;;)
{
if(!h2) break;
tim=(h2->time);
if((tim>tims.end)||(tim>tims.last)) break;
x0=(tim - tims.start) * pxns;
if(x0>wavewidth)
	{
	break;
	}
h3=h2;
h2 = h2->next;
if (!h2) break;
tim=(h2->time);
x1=(tim - tims.start) * pxns;
if(x1<0)
	continue;
tv=convert_real(t,h3);
if (!isnan(tv))
	{
	if (isnan(tmin) || tv < tmin)
		tmin = tv;
	if (isnan(tmax) || tv > tmax)
		tmax = tv;
	}
}
if (isnan(tmin) || isnan(tmax))
	tmin = tmax = 0;
if ((tmax - tmin) < 1e-20)
	{
	tmax = 1;
	tmin -= 0.5 * (y1 - y0);
	}
	else
	{
	tmax = (y1 - y0) / (tmax - tmin);
	}

h3 = NULL;
for(;;)
{
if(!h) break;
tim=(h->time);
if((tim>tims.end)||(tim>tims.last)) break;

x0=(tim - tims.start) * pxns;
if(x0<-1) 
	{ 
	x0=-1; 
	}
	else
if(x0>wavewidth)
	{
	break;
	}

h2=h->next;
if(!h2) break;
h2tim=tim=(h2->time);
if(tim>tims.last) tim=tims.last;
	else if(tim>tims.end+1) tim=tims.end+1;
x1=(tim - tims.start) * pxns;
if(x1<-1) 
	{ 
	x1=-1; 
	}
	else
if(x1>wavewidth)
	{
	x1=wavewidth;
	}

/* draw trans */
type = vtype2(t,h);
tv=convert_real(t,h);
tv2=convert_real(t,h2);
if(isnan(tv))
	{
	yt0 = yu;
	}
	else
	{
	yt0 = y0 + (tv - tmin) * tmax;
	}
if(isnan(tv2))
	{
	yt1 = yu;
	}
	else
	{
	yt1 = y0 + (tv2 - tmin) * tmax;
	}
if(x0!=x1)
	{
	if(type == AN_0) 
		{
		c = gc_vbox; 
		} 
		else 
		{
		c = gc_x;
		}
	if(t->flags & TR_ANALOG_STEP)
		{
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0, yt0,
			x1, yt0);
		wave_gdk_draw_line(wavepixmap, 
			c,
			x1, yt0,
			x1, yt1);
		}
		else
		{
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0, yt0,
			x1, yt1);
		}
	}
	else
	{
	newtime=(((gdouble)(x1+WAVE_OPT_SKIP))*nspx)+tims.start+shift_timebase;	/* skip to next pixel */
	h3=bsearch_vector(t->n.vec,newtime);
	if(h3->time>h->time)
		{
		h=h3;
		lasttype=type;
		continue;
		}
	}

h=h->next;
lasttype=type;
}

wave_gdk_draw_line_flush(wavepixmap);

tims.start+=shift_timebase;
tims.end+=shift_timebase;
}

/*
 * draw vector traces
 */
static void draw_vptr_trace(Trptr t, vptr v, int which)
{
TimeType x0, x1, newtime, width;
int y0, y1, yu, liney, ytext;
TimeType tim, h2tim;
vptr h, h2, h3;
char *ascii=NULL;
int type;
int lasttype=-1;
GdkGC    *c;

tims.start-=shift_timebase;
tims.end-=shift_timebase;

h=v;
liney=((which+2)*fontheight)-2;
y1=((which+1)*fontheight)+2;	
y0=liney-2;
yu=(y0+y1)/2;
ytext=yu-(wavefont->ascent/2)+wavefont->ascent;

if((display_grid)&&(enable_horiz_grid))
	{
	gdk_draw_line(wavepixmap, 
		gc_grid,
		(tims.start<tims.first)?(tims.first-tims.start)*pxns:0, liney,
		(tims.last<=tims.end)?(tims.last-tims.start)*pxns:wavewidth-1, liney);
	}

if(t->flags & TR_ANALOGMASK)
	{
        Trptr te = t->t_next;
        int ext = 0;
                 
        while(te)
                {
                if(te->flags & TR_ANALOG_BLANK_STRETCH)
                        {
                        ext++;
                        te = te->t_next;
                        }
                        else
                        {
                        break;
                        }
                }

	draw_vptr_trace_analog(t, v, which, ext);
	return;
	}

for(;;)
{
if(!h) break;
tim=(h->time);
if((tim>tims.end)||(tim>tims.last)) break;

x0=(tim - tims.start) * pxns;
if(x0<-1) 
	{ 
	x0=-1; 
	}
	else
if(x0>wavewidth)
	{
	break;
	}

h2=h->next;
if(!h2) break;
h2tim=tim=(h2->time);
if(tim>tims.last) tim=tims.last;
	else if(tim>tims.end+1) tim=tims.end+1;
x1=(tim - tims.start) * pxns;
if(x1<-1) 
	{ 
	x1=-1; 
	}
	else
if(x1>wavewidth)
	{
	x1=wavewidth;
	}

/* draw trans */
type = vtype2(t,h);

if(x0>-1)
if(use_roundcaps)
	{
	if (type == AN_Z) 
		{
		if (lasttype != -1) 
			{
			wave_gdk_draw_line(wavepixmap, 
				(lasttype==AN_X? gc_x:gc_vtrans),
				x0-1, y0,
				x0,   yu);
			wave_gdk_draw_line(wavepixmap, 
				(lasttype==AN_X? gc_x:gc_vtrans),
				x0, yu,
				x0-1, y1);
			}
		} 
		else
		if (lasttype==AN_Z) 
			{
			wave_gdk_draw_line(wavepixmap, 
				(type==AN_X? gc_x:gc_vtrans),
				x0+1, y0,
				x0,   yu);
			wave_gdk_draw_line(wavepixmap, 
				(type==AN_X? gc_x:gc_vtrans),
				x0, yu,
				x0+1, y1);
			} 
			else 
			{
			if (lasttype != type) 
				{
				wave_gdk_draw_line(wavepixmap, 
					(lasttype==AN_X? gc_x:gc_vtrans),
					x0-1, y0,
					x0,   yu);
				wave_gdk_draw_line(wavepixmap, 
					(lasttype==AN_X? gc_x:gc_vtrans),
					x0, yu,
					x0-1, y1);
				wave_gdk_draw_line(wavepixmap, 
					(type==AN_X? gc_x:gc_vtrans),
					x0+1, y0,
					x0,   yu);
				wave_gdk_draw_line(wavepixmap, 
					(type==AN_X? gc_x:gc_vtrans),
					x0, yu,
					x0+1, y1);
				} 
				else 
				{
				wave_gdk_draw_line(wavepixmap, 
					(type==AN_X? gc_x:gc_vtrans),
					x0-2, y0,
					x0+2, y1);
				wave_gdk_draw_line(wavepixmap, 
					(type==AN_X? gc_x:gc_vtrans),
					x0+2, y0,
					x0-2, y1);
				}
			}
		}
		else
		{
		wave_gdk_draw_line(wavepixmap, 
			(type==AN_X? gc_x:gc_vtrans),
			x0, y0,
			x0, y1);
		}

if(x0!=x1)
	{
	if (type == AN_Z) 
		{
		if(use_roundcaps)
			{
			wave_gdk_draw_line(wavepixmap, 
				gc_mid,
				x0+1, yu,
				x1-1, yu);
			} 
			else 
			{
			wave_gdk_draw_line(wavepixmap, 
				gc_mid,
				x0, yu,
				x1, yu);
			}
		} 
		else 
		{
		if(type == AN_0) 
			{
			c = gc_vbox; 
			} 
			else 
			{
			c = gc_x;
			}
	
	if(use_roundcaps)
		{
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0+2, y0,
			x1-2, y0);
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0+2, y1,
			x1-2, y1);
		}
		else
		{
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0, y0,
			x1, y0);
		wave_gdk_draw_line(wavepixmap, 
			c,
			x0, y1,
			x1, y1);
		}


if(x0<0) x0=0;	/* fixup left margin */

	if((width=x1-x0)>vector_padding)
		{
		ascii=convert_ascii(t,h);
		if((x1>=wavewidth)||(gdk_string_measure(wavefont, ascii)+vector_padding<=width))
			{
			gdk_draw_string(wavepixmap,
				wavefont,
			       	gc_value,
				x0+2,ytext,
			        ascii);
			}
		else
			{
			char *mod;

			mod=bsearch_trunc(ascii,width-vector_padding);
			if(mod)
				{
				*mod='+';
				*(mod+1)=0;

				gdk_draw_string(wavepixmap,
					wavefont,
				       	gc_value,
					x0+2,ytext,
				        ascii);
				}
			}

		}
	}
	}
	else
	{
	newtime=(((gdouble)(x1+WAVE_OPT_SKIP))*nspx)+tims.start+shift_timebase;	/* skip to next pixel */
	h3=bsearch_vector(t->n.vec,newtime);
	if(h3->time>h->time)
		{
		h=h3;
		lasttype=type;
		continue;
		}
	}

if(ascii) { free_2(ascii); ascii=NULL; }
lasttype=type;
h=h->next;
}

wave_gdk_draw_line_flush(wavepixmap);

tims.start+=shift_timebase;
tims.end+=shift_timebase;
}
