/* 
 * 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 "pixmaps.h"
#include "currenttime.h"
#include "debug.h"

char do_zoom_center=1;
char do_initial_zoom_fit=0;

void fix_wavehadj(void)
{
GtkAdjustment *hadj;
gfloat pageinc;

hadj=GTK_ADJUSTMENT(wave_hslider);
hadj->lower=tims.first;
hadj->upper=tims.last+2.0;

pageinc=(gfloat)(((gdouble)wavewidth)*nspx);
hadj->page_size=hadj->page_increment=(pageinc>=1.0)?pageinc:1.0;
hadj->step_increment=(nspx>=1.0)?nspx:1.0;

if(hadj->page_size >= (hadj->upper-hadj->lower)) hadj->value=hadj->lower;
if(hadj->value+hadj->page_size>hadj->upper)
	{
	hadj->value=hadj->upper-hadj->page_size;
	if(hadj->value<hadj->lower)
		hadj->value=hadj->lower;
	}

}

void service_zoom_left(GtkWidget *text, gpointer data)
{
GtkAdjustment *hadj;

if(helpbox_is_active)
        {
        help_text_bold("\n\nZoom To Start");
        help_text(
		" is used to jump scroll to the trace's beginning."
        );
        return;
        }

hadj=GTK_ADJUSTMENT(wave_hslider);
hadj->value=tims.timecache=tims.first;
time_update();
}

void service_zoom_right(GtkWidget *text, gpointer data)
{
GtkAdjustment *hadj;
TimeType ntinc;

if(helpbox_is_active)
        {
        help_text_bold("\n\nZoom To End");
        help_text(
		" is used to jump scroll to the trace's end."
        );
        return;
        }

ntinc=(TimeType)(((gdouble)wavewidth)*nspx);

tims.timecache=tims.last-ntinc+1;
        if(tims.timecache<tims.first) tims.timecache=tims.first;

hadj=GTK_ADJUSTMENT(wave_hslider);
hadj->value=tims.timecache;
time_update();
}

void service_zoom_out(GtkWidget *text, gpointer data)
{
TimeType middle=0, width;

if(helpbox_is_active)
        {
        help_text_bold("\n\nZoom Out");
        help_text(
		" is used to decrease the zoom factor."
#ifdef WAVE_USE_GTK2
		" Alt + Scrollwheel Up also performs this function."
#endif
        );
        return;
        }

if(do_zoom_center)
	{
	if((tims.marker<0)||(tims.marker<tims.first)||(tims.marker>tims.last))
		{
		if(tims.end>tims.last) tims.end=tims.last;
		middle=(tims.start/2)+(tims.end/2);
		if((tims.start&1)&&(tims.end&1)) middle++;
		}
		else
		{
		middle=tims.marker;
		}
	}

tims.prevzoom=tims.zoom;

tims.zoom--;
calczoom(tims.zoom);

if(do_zoom_center)
	{
	width=(TimeType)(((gdouble)wavewidth)*nspx);
	tims.start=time_trunc(middle-(width/2));
        if(tims.start+width>tims.last) tims.start=time_trunc(tims.last-width);
	if(tims.start<tims.first) tims.start=tims.first;
	GTK_ADJUSTMENT(wave_hslider)->value=tims.timecache=tims.start;
	}
	else
	{
	tims.timecache=0;
	}	

fix_wavehadj();

gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed"); /* force zoom update */
gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed"); /* force zoom update */

DEBUG(printf("Zoombuttons out\n"));
}

void service_zoom_in(GtkWidget *text, gpointer data)
{
if(helpbox_is_active)
        {
        help_text_bold("\n\nZoom In");
        help_text(
                " is used to increase the zoom factor."  
#ifdef WAVE_USE_GTK2
		" Alt + Scrollwheel Down also performs this function."
#endif
        );
        return;
        }

if(tims.zoom<0)		/* otherwise it's ridiculous and can cause */
	{		/* overflow problems in the scope          */
	TimeType middle=0, width;

	if(do_zoom_center)
		{
		if((tims.marker<0)||(tims.marker<tims.first)||(tims.marker>tims.last))
			{
			if(tims.end>tims.last) tims.end=tims.last;
			middle=(tims.start/2)+(tims.end/2);
			if((tims.start&1)&&(tims.end&1)) middle++;
			}
			else
			{
			middle=tims.marker;
			}
		}

	tims.prevzoom=tims.zoom;

	tims.zoom++;
	calczoom(tims.zoom);

	if(do_zoom_center)
		{
		width=(TimeType)(((gdouble)wavewidth)*nspx);
		tims.start=time_trunc(middle-(width/2));
	        if(tims.start+width>tims.last) tims.start=time_trunc(tims.last-width);
		if(tims.start<tims.first) tims.start=tims.first;
		GTK_ADJUSTMENT(wave_hslider)->value=tims.timecache=tims.start;
		}
		else
		{
		tims.timecache=0;
		}	

	fix_wavehadj();
	
	gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed"); /* force zoom update */
	gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed"); /* force zoom update */
	
	DEBUG(printf("Zoombuttons in\n"));
	}
}

void service_zoom_undo(GtkWidget *text, gpointer data)
{
gdouble temp;

if(helpbox_is_active)
        {
        help_text_bold("\n\nZoom Undo");
        help_text(
                " is used to revert to the previous zoom value used.  Undo"  
		" only works one level deep."
        );
        return;
        }


temp=tims.zoom;
tims.zoom=tims.prevzoom;
tims.prevzoom=temp;
tims.timecache=0;
calczoom(tims.zoom);
fix_wavehadj();

gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed"); /* force zoom update */
gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed"); /* force zoom update */

DEBUG(printf("Zoombuttons Undo\n"));
}

void service_zoom_fit(GtkWidget *text, gpointer data)
{
gdouble estimated;
int fixedwidth;

if(helpbox_is_active)
        {
        help_text_bold("\n\nZoom Best Fit");
        help_text(
		" attempts a \"best fit\" to get the whole trace onscreen."
		"  Note that the trace may be more or less than a whole screen since"
		" this isn't a \"perfect fit.\" Also, if the middle button baseline"
		" marker is nailed down, the zoom instead of getting the whole trace"
		" onscreen will use the part of the trace between the primary"
		" marker and the baseline marker."
        );
        return;
        }

if((tims.baseline>=0)&&(tims.marker>=0))
	{
	service_dragzoom(tims.baseline, tims.marker); /* new semantics added to zoom between the two */
	}
	else
	{
	if(wavewidth>4) { fixedwidth=wavewidth-4; } else { fixedwidth=wavewidth; }
	estimated=-log(((gdouble)(tims.last-tims.first+1))/((gdouble)fixedwidth)*((gdouble)200.0))/log(zoombase);
	if(estimated>((gdouble)(0.0))) estimated=((gdouble)(0.0));
	
	tims.prevzoom=tims.zoom;
	tims.timecache=0;
	
	calczoom(estimated);
	tims.zoom=estimated;

	fix_wavehadj();
	
	gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed"); /* force zoom update */
	gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed"); /* force zoom update */
	}
	
DEBUG(printf("Zoombuttons Fit\n"));
}

void service_zoom_full(GtkWidget *text, gpointer data)
{
gdouble estimated;
int fixedwidth;

if(helpbox_is_active)
        {
        help_text_bold("\n\nZoom Full");
        help_text(
		" attempts a \"best fit\" to get the whole trace onscreen."
		"  Note that the trace may be more or less than a whole screen since"
		" this isn't a \"perfect fit.\""
        );
        return;
        }

if(wavewidth>4) { fixedwidth=wavewidth-4; } else { fixedwidth=wavewidth; }
estimated=-log(((gdouble)(tims.last-tims.first+1))/((gdouble)fixedwidth)*((gdouble)200.0))/log(zoombase);
if(estimated>((gdouble)(0.0))) estimated=((gdouble)(0.0));
	
tims.prevzoom=tims.zoom;
tims.timecache=0;
	
calczoom(estimated);
tims.zoom=estimated;

fix_wavehadj();
	
gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed"); /* force zoom update */
gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed"); /* force zoom update */
	
DEBUG(printf("Zoombuttons Full\n"));
}


void service_dragzoom(TimeType time1, TimeType time2)	/* the function you've been waiting for... */
{
gdouble estimated;
int fixedwidth;
TimeType temp;
GtkAdjustment *hadj;
Trptr t;

if(time2<time1)
	{
	temp=time1;
	time1=time2;
	time2=temp;
	}

if(time2>time1)	/* ensure at least 1 tick */
	{
	if(wavewidth>4) { fixedwidth=wavewidth-4; } else { fixedwidth=wavewidth; }
	estimated=-log(((gdouble)(time2-time1+1))/((gdouble)fixedwidth)*((gdouble)200.0))/log(zoombase);
	if(estimated>((gdouble)(0.0))) estimated=((gdouble)(0.0));

	tims.prevzoom=tims.zoom;
	tims.timecache=tims.laststart=tims.start=time_trunc(time1);

        for(t=traces.first;t;t=t->t_next) /* have to nuke string refs so printout is ok! */
                {
                if(t->asciivalue) { free_2(t->asciivalue); t->asciivalue=NULL; }
                }

        for(t=traces.buffer;t;t=t->t_next)
                {
                if(t->asciivalue) { free_2(t->asciivalue); t->asciivalue=NULL; }
                }

	if(!((tims.baseline>=0)&&(tims.marker>=0)))
		{
	        update_markertime(tims.marker=-1);
		}
        signalwindow_width_dirty=1;
        MaxSignalLength();


        hadj=GTK_ADJUSTMENT(wave_hslider);
        hadj->value=time1;

	calczoom(estimated);
	tims.zoom=estimated;

	fix_wavehadj();

	gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "changed"); /* force zoom update */
	gtk_signal_emit_by_name (GTK_OBJECT (GTK_ADJUSTMENT(wave_hslider)), "value_changed"); /* force zoom update */

	DEBUG(printf("Drag Zoom\n"));
	}
}

/* Create actual buttons */
GtkWidget *
create_zoom_buttons (void)
{
GtkWidget *table;
GtkWidget *table2;
GtkWidget *frame;
GtkWidget *main_vbox;
GtkWidget *b1;
GtkWidget *b2;
GtkWidget *b3;
GtkWidget *b4;
GtkWidget *b5;
GtkWidget *b6;
GtkWidget *pixmapzout, *pixmapzin, *pixmapzfit, *pixmapzundo;
GtkWidget *pixmapzleft, *pixmapzright;

GtkTooltips *tooltips;

tooltips=gtk_tooltips_new_2();
gtk_tooltips_set_delay_2(tooltips,1500);

pixmapzin=gtk_pixmap_new(zoomin_pixmap, zoomin_mask);
gtk_widget_show(pixmapzin);
pixmapzout=gtk_pixmap_new(zoomout_pixmap, zoomout_mask);
gtk_widget_show(pixmapzout);
pixmapzfit=gtk_pixmap_new(zoomfit_pixmap, zoomfit_mask);
gtk_widget_show(pixmapzfit);
pixmapzundo=gtk_pixmap_new(zoomundo_pixmap, zoomundo_mask);
gtk_widget_show(pixmapzundo);

pixmapzleft=gtk_pixmap_new(zoom_larrow_pixmap, zoom_larrow_mask);
gtk_widget_show(pixmapzleft);
pixmapzright=gtk_pixmap_new(zoom_rarrow_pixmap, zoom_rarrow_mask);
gtk_widget_show(pixmapzright);


/* Create a table to hold the text widget and scrollbars */
table = gtk_table_new (1, 1, FALSE);

main_vbox = gtk_vbox_new (FALSE, 1);
gtk_container_border_width (GTK_CONTAINER (main_vbox), 1);
gtk_container_add (GTK_CONTAINER (table), main_vbox);

frame = gtk_frame_new ("Zoom ");
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
gtk_widget_show (main_vbox);

table2 = gtk_table_new (2, 3, FALSE);

b1 = gtk_button_new();
gtk_container_add(GTK_CONTAINER(b1), pixmapzin);
gtk_table_attach (GTK_TABLE (table2), b1, 0, 1, 0, 1,
		      	GTK_FILL | GTK_EXPAND,
		      	GTK_FILL | GTK_EXPAND | GTK_SHRINK, 1, 1);
gtk_signal_connect_object (GTK_OBJECT (b1), "clicked",
			GTK_SIGNAL_FUNC(service_zoom_out), GTK_OBJECT (table2));
gtk_tooltips_set_tip_2(tooltips, b1, "Zoom Out", NULL);
gtk_widget_show(b1);

b2 = gtk_button_new();
gtk_container_add(GTK_CONTAINER(b2), pixmapzout);
gtk_table_attach (GTK_TABLE (table2), b2, 0, 1, 1, 2,
		      	GTK_FILL | GTK_EXPAND,
		      	GTK_FILL | GTK_EXPAND | GTK_SHRINK, 1, 1);
gtk_signal_connect_object (GTK_OBJECT (b2), "clicked",
			GTK_SIGNAL_FUNC(service_zoom_in), GTK_OBJECT (table2));
gtk_tooltips_set_tip_2(tooltips, b2, "Zoom In", NULL);
gtk_widget_show(b2);

b3 = gtk_button_new();
gtk_container_add(GTK_CONTAINER(b3), pixmapzfit);
gtk_table_attach (GTK_TABLE (table2), b3, 1, 2, 0, 1,
		      	GTK_FILL | GTK_EXPAND,
		      	GTK_FILL | GTK_EXPAND | GTK_SHRINK, 1, 1);
gtk_signal_connect_object (GTK_OBJECT (b3), "clicked",
			GTK_SIGNAL_FUNC(service_zoom_fit), GTK_OBJECT (table2));
gtk_tooltips_set_tip_2(tooltips, b3, "Zoom Best Fit", NULL);
gtk_widget_show(b3);

b4 = gtk_button_new();
gtk_container_add(GTK_CONTAINER(b4), pixmapzundo);
gtk_table_attach (GTK_TABLE (table2), b4, 1, 2, 1, 2,
		      	GTK_FILL | GTK_EXPAND,
		      	GTK_FILL | GTK_EXPAND | GTK_SHRINK, 1, 1);
gtk_signal_connect_object (GTK_OBJECT (b4), "clicked",
			GTK_SIGNAL_FUNC(service_zoom_undo), GTK_OBJECT (table2));
gtk_tooltips_set_tip_2(tooltips, b4, "Undo Last Zoom", NULL);
gtk_widget_show(b4);

b5 = gtk_button_new();
gtk_container_add(GTK_CONTAINER(b5), pixmapzleft);
gtk_table_attach (GTK_TABLE (table2), b5, 2, 3, 0, 1,
		      	GTK_FILL | GTK_EXPAND,
		      	GTK_FILL | GTK_EXPAND | GTK_SHRINK, 1, 1);
gtk_signal_connect_object (GTK_OBJECT (b5), "clicked",
			GTK_SIGNAL_FUNC(service_zoom_left), GTK_OBJECT (table2));
gtk_tooltips_set_tip_2(tooltips, b5, "Zoom To Start", NULL);
gtk_widget_show(b5);

b6 = gtk_button_new();
gtk_container_add(GTK_CONTAINER(b6), pixmapzright);
gtk_table_attach (GTK_TABLE (table2), b6, 2, 3, 1, 2,
		      	GTK_FILL | GTK_EXPAND,
		      	GTK_FILL | GTK_EXPAND | GTK_SHRINK, 1, 1);
gtk_signal_connect_object (GTK_OBJECT (b6), "clicked",
			GTK_SIGNAL_FUNC(service_zoom_right), GTK_OBJECT (table2));
gtk_tooltips_set_tip_2(tooltips, b6, "Zoom To End", NULL);
gtk_widget_show(b6);


gtk_container_add (GTK_CONTAINER (frame), table2);

gtk_widget_show(table2);

return table;
}

