/*
 * Copyright (c) Tony Bybell 2008-2014.
 *
 * 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 <config.h>
#include "globals.h"
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "gtk23compat.h"
#include "analyzer.h"
#include "tree.h"
#include "symbol.h"
#include "vcd.h"
#include "lx2.h"
#include "busy.h"
#include "debug.h"
#include "hierpack.h"
#include "menu.h"
#include "tcl_helper.h"
#include "tcl_support_commands.h"

#if !defined __MINGW32__
#include <sys/types.h>
#include <unistd.h>
#endif

#if defined(HAVE_LIBTCL)
#include <tcl.h>
#endif



/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
/* XXX functions for embedding TCL interpreter XXX */
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

#if defined(HAVE_LIBTCL)

static int gtkwavetcl_badNumArgs(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int expected)
{
(void)clientData;

Tcl_Obj *aobj;
char reportString[1024];

sprintf(reportString, "* wrong number of arguments for '%s': %d expected, %d encountered", Tcl_GetString(objv[0]), expected, objc-1);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);
return(TCL_ERROR);
}

static int gtkwavetcl_nop(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
(void)clientData;
(void)interp;
(void)objc;
(void)objv;

/* nothing, this is simply to call gtk's main loop */
gtkwave_main_iteration();
return(TCL_OK);
}

static int gtkwavetcl_printInteger(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int intVal)
{
(void)clientData;
(void)objc;
(void)objv;

Tcl_Obj *aobj;
char reportString[33];

sprintf(reportString, "%d", intVal);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_printTimeType(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], TimeType ttVal)
{
(void)clientData;
(void)objc;
(void)objv;

Tcl_Obj *aobj;
char reportString[65];

sprintf(reportString, TTFormat, ttVal);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_printTraceFlagsType(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], TraceFlagsType ttVal)
{
(void)clientData;
(void)objc;
(void)objv;

Tcl_Obj *aobj;
char reportString[65];

sprintf(reportString, "%"TRACEFLAGSPRIuFMT, ttVal);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_printDouble(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], double dVal)
{
(void)clientData;
(void)objc;
(void)objv;

Tcl_Obj *aobj;
char reportString[65];

sprintf(reportString, "%e", dVal);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_printString(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const char *reportString)
{
(void)clientData;
(void)objc;
(void)objv;

Tcl_Obj *aobj;

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static char *extractFullTraceName(Trptr t)
{
char *name = NULL;

 if(HasWave(t))
   {
     if (HasAlias(t))
       {
	 name = strdup_2(t->name_full);
       }
     else if (t->vector)
       {
	 name = strdup_2(t->n.vec->bvname);
       }
     else
       {
	 int flagged = HIER_DEPACK_ALLOC;

	 name = hier_decompress_flagged(t->n.nd->nname, &flagged);
	 if(!flagged)
	   {
	     name = strdup_2(name);
	   }
       }
   }
 return(name);
}


/* tcl interface functions */

char *get_Tcl_string(Tcl_Obj *obj) {
  char *s = Tcl_GetString(obj) ;
  if (*s == '{') {		/* braced string */
    char *p = strrchr(s, '}') ;
    if(p) {
      if(GLOBALS->previous_braced_tcl_string)
		{
		free_2(GLOBALS->previous_braced_tcl_string);
		}

      GLOBALS->previous_braced_tcl_string = strdup_2(s);
      GLOBALS->previous_braced_tcl_string[p-s] = 0;
      s = GLOBALS->previous_braced_tcl_string + 1;
    }
  }
  return s ;
}

static int gtkwavetcl_getNumFacs(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->numfacs;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getLongestName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->longestname;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getFacName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
Tcl_Obj *aobj;

if(objc == 2)
	{
	char *s = get_Tcl_string(objv[1]);
	int which = atoi(s);

	if((which >= 0) && (which < GLOBALS->numfacs))
		{
		int was_packed = HIER_DEPACK_ALLOC;
        	char *hfacname = NULL;

        	hfacname = hier_decompress_flagged(GLOBALS->facs[which]->name, &was_packed);

		aobj = Tcl_NewStringObj(hfacname, -1);
		Tcl_SetObjResult(interp, aobj);
		if(was_packed) free_2(hfacname);
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_getFacDir(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
Tcl_Obj *aobj;

if(objc == 2)
	{
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if((which >= 0) && (which < GLOBALS->numfacs))
                {
                WAVE_NODEVARDIR_STR
                int vardir = GLOBALS->facs[which]->n->vardir; /* two bit already chops down to 0..3, but this doesn't hurt */
                if((vardir < 0) || (vardir > ND_DIR_MAX))
                        {
                        vardir = 0;
                        }

                aobj = Tcl_NewStringObj(vardir_strings[vardir], -1);
                Tcl_SetObjResult(interp, aobj);
                }
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_getFacVtype(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
Tcl_Obj *aobj;

if(objc == 2)
        {
	char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if((which >= 0) && (which < GLOBALS->numfacs))
                {
                WAVE_NODEVARTYPE_STR
                WAVE_NODEVARDATATYPE_STR
                unsigned int varxt;
                char *varxt_pnt;
                int vartype;
                int vardt;

                varxt = GLOBALS->facs[which]->n->varxt;
                varxt_pnt = varxt ? varxt_fix(GLOBALS->subvar_pnt[varxt]) : NULL;

                vartype = GLOBALS->facs[which]->n->vartype;
                if((vartype < 0) || (vartype > ND_VARTYPE_MAX))
                        {
                        vartype = 0;
                        }

                vardt = GLOBALS->facs[which]->n->vardt;
                if((vardt < 0) || (vardt > ND_VDT_MAX))
                        {
                        vardt = 0;
                        }

                aobj = Tcl_NewStringObj( (((GLOBALS->supplemental_datatypes_encountered) && (!GLOBALS->supplemental_vartypes_encountered)) ?
                                                        (varxt ? varxt_pnt : vardatatype_strings[vardt]) : vartype_strings[vartype]), -1);
                Tcl_SetObjResult(interp, aobj);
                }
        }
	else
        {
	return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_getFacDtype(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
Tcl_Obj *aobj;

if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if((which >= 0) && (which < GLOBALS->numfacs))
                {
                WAVE_NODEVARTYPE_STR
                WAVE_NODEVARDATATYPE_STR
                unsigned int varxt;
                char *varxt_pnt;
                int vardt;

                varxt = GLOBALS->facs[which]->n->varxt;
                varxt_pnt = varxt ? varxt_fix(GLOBALS->subvar_pnt[varxt]) : NULL;

                vardt = GLOBALS->facs[which]->n->vardt;
                if((vardt < 0) || (vardt > ND_VDT_MAX))
                        {
                        vardt = 0;
                        }

                aobj = Tcl_NewStringObj( varxt ? varxt_pnt : vardatatype_strings[vardt], -1);
                Tcl_SetObjResult(interp, aobj);
                }
        }
        else
        {
	return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_getMinTime(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
TimeType value = GLOBALS->min_time;
return(gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getMaxTime(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
TimeType value = GLOBALS->max_time;
return(gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getTimeZero(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
TimeType value = GLOBALS->global_time_offset;
return(gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getTimeDimension(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
(void)clientData;
(void)objc;
(void)objv;

Tcl_Obj *aobj;
char reportString[2];

reportString[0] = GLOBALS->time_dimension;
reportString[1] = 0;

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_getArgv(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
(void)clientData;
(void)objc;
(void)objv;

if(GLOBALS->argvlist)
	{
	Tcl_Obj *aobj = Tcl_NewStringObj(GLOBALS->argvlist, -1);
	Tcl_SetObjResult(interp, aobj);
	}

return(TCL_OK);
}

static int gtkwavetcl_getBaselineMarker(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
TimeType value = GLOBALS->tims.baseline;
return(gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getMarker(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
TimeType value = GLOBALS->tims.marker;
return(gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getWindowStartTime(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
TimeType value = GLOBALS->tims.start;
return(gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getWindowEndTime(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
TimeType value = GLOBALS->tims.end;
return(gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getDumpType(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
(void)clientData;
(void)objc;
(void)objv;

Tcl_Obj *aobj;
char *reportString = "UNKNOWN";

if(GLOBALS->is_vcd)
        {
        if(GLOBALS->partial_vcd)
                {
                reportString = "PVCD";
                }
                else
                {
                reportString = "VCD";
                }
        }
else
if(GLOBALS->is_lxt)
        {
	reportString = "LXT";
        }
else
if(GLOBALS->is_ghw)
        {
	reportString = "GHW";
        }
else
if(GLOBALS->is_lx2)
        {
        switch(GLOBALS->is_lx2)
                {
                case LXT2_IS_LXT2: reportString = "LXT2"; break;
                case LXT2_IS_AET2: reportString = "AET2"; break;
                case LXT2_IS_VZT:  reportString = "VZT"; break;
                case LXT2_IS_VLIST:reportString = "VCD"; break;
                case LXT2_IS_FST:  reportString = "FST"; break;
                case LXT2_IS_FSDB:  reportString = "FSDB"; break;
                }
        }

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}


static int gtkwavetcl_getNamedMarker(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
	{
	char *s = get_Tcl_string(objv[1]);
	int which = -1;

	if((s[0]>='A')&&(s[0]<='Z'))
		{
		which = bijective_marker_id_string_hash(s);
		}
	else
	if((s[0]>='a')&&(s[0]<='z'))
		{
		which = bijective_marker_id_string_hash(s);
		}
	else
		{
		which = atoi(s); 
		}

	if((which >= 0) && (which < WAVE_NUM_NAMED_MARKERS))
		{
		TimeType value = GLOBALS->named_markers[which];
		return(gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_getWaveHeight(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->waveheight;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getWaveWidth(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->wavewidth;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getPixelsUnitTime(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
double value = GLOBALS->pxns;
return(gtkwavetcl_printDouble(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getUnitTimePixels(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
double value = GLOBALS->nspx;
return(gtkwavetcl_printDouble(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getZoomFactor(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
double value = GLOBALS->tims.zoom;
return(gtkwavetcl_printDouble(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getDumpFileName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
char *value = GLOBALS->loaded_file_name;
return(gtkwavetcl_printString(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getVisibleNumTraces(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->traces.visible;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getTotalNumTraces(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->traces.total;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getTraceNameFromIndex(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
	{
	char *s = get_Tcl_string(objv[1]);
	int which = atoi(s);

	if((which >= 0) && (which < GLOBALS->traces.total))
		{
		Trptr t = GLOBALS->traces.first;
		int i = 0;
		while(t)
			{
			if(i == which)
				{
				if(t->name)
					{
					return(gtkwavetcl_printString(clientData, interp, objc, objv, t->name));
					}
					else
					{
					break;
					}
				}

			i++;
			t = t->t_next;
			}
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
        }

return(TCL_OK);
}

static int gtkwavetcl_getTraceFlagsFromIndex(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
	{
	char *s = get_Tcl_string(objv[1]);
	int which = atoi(s);

	if((which >= 0) && (which < GLOBALS->traces.total))
		{
		Trptr t = GLOBALS->traces.first;
		int i = 0;
		while(t)
			{
			if(i == which)
				{
				return(gtkwavetcl_printTraceFlagsType(clientData, interp, objc, objv, t->flags));
				}

			i++;
			t = t->t_next;
			}
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_getTraceValueAtMarkerFromIndex(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
	{
	char *s = get_Tcl_string(objv[1]);
	int which = atoi(s);

	if((which >= 0) && (which < GLOBALS->traces.total))
		{
		Trptr t = GLOBALS->traces.first;
		int i = 0;
		while(t)
			{
			if(i == which)
				{
				if(t->asciivalue)
					{
					char *pnt = t->asciivalue;
					if(*pnt == '=') pnt++;

					return(gtkwavetcl_printString(clientData, interp, objc, objv, pnt));
					}
					else
					{
					break;
					}
				}

			i++;
			t = t->t_next;
			}
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_getTraceValueAtMarkerFromName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
	{
	char *s = get_Tcl_string(objv[1]);
	Trptr t = GLOBALS->traces.first;

	while(t)
		{
		if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
			{
			char *name = extractFullTraceName(t);
			if(!strcmp(name, s))
				{
				free_2(name);
				break;
				}
			free_2(name);
			}
		t = t-> t_next;
		}

	if(t)
		{
		if(t->asciivalue)
			{
			char *pnt = t->asciivalue;
			if(*pnt == '=') pnt++;
			return(gtkwavetcl_printString(clientData, interp, objc, objv, pnt));
			}
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}


static int gtkwavetcl_getTraceValueAtNamedMarkerFromName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 3)
	{
	char *sv = get_Tcl_string(objv[1]);
	int which = -1;
	TimeType oldmarker = GLOBALS->tims.marker;
	TimeType value = LLDescriptor(-1);

	if((sv[0]>='A')&&(sv[0]<='Z'))
		{
		which = bijective_marker_id_string_hash(sv);
		}
	else
	if((sv[0]>='a')&&(sv[0]<='z'))
		{
		which = bijective_marker_id_string_hash(sv);
		}
	else
		{
		which = atoi(sv);
		}

	if((which >= 0) && (which < WAVE_NUM_NAMED_MARKERS))
		{
		char *s = get_Tcl_string(objv[2]);
		Trptr t = GLOBALS->traces.first;

		value = GLOBALS->named_markers[which];

		while(t)
			{
			if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
				{
				char *name = extractFullTraceName(t);
				if(!strcmp(name, s))
					{
					free_2(name);
					break;
					}
				free_2(name);
				}
			t = t-> t_next;
			}

		if(t && (value >= LLDescriptor(0)))
			{
			GLOBALS->tims.marker = value;
		        GLOBALS->signalwindow_width_dirty=1;
		        MaxSignalLength();
		        signalarea_configure_event(GLOBALS->signalarea, NULL);
		        wavearea_configure_event(GLOBALS->wavearea, NULL);
			gtkwave_main_iteration();

			if(t->asciivalue)
				{
				Tcl_Obj *aobj;
				char *pnt = t->asciivalue;
				if(*pnt == '=') pnt++;

				aobj = Tcl_NewStringObj(pnt, -1);
				Tcl_SetObjResult(interp, aobj);

				GLOBALS->tims.marker = oldmarker;
				update_markertime(GLOBALS->tims.marker);
			        GLOBALS->signalwindow_width_dirty=1;
			        MaxSignalLength();
			        signalarea_configure_event(GLOBALS->signalarea, NULL);
			        wavearea_configure_event(GLOBALS->wavearea, NULL);
				gtkwave_main_iteration();

				return(TCL_OK);
				}

			GLOBALS->tims.marker = oldmarker;
			update_markertime(GLOBALS->tims.marker);
		        GLOBALS->signalwindow_width_dirty=1;
		        MaxSignalLength();
		        signalarea_configure_event(GLOBALS->signalarea, NULL);
		        wavearea_configure_event(GLOBALS->wavearea, NULL);
			gtkwave_main_iteration();
			}
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_getHierMaxLevel(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->hier_max_level;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getFontHeight(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->fontheight;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getLeftJustifySigs(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = (GLOBALS->left_justify_sigs != 0);
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getSaveFileName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
char *value = GLOBALS->filesel_writesave;
if(value)
	{
	return(gtkwavetcl_printString(clientData, interp, objc, objv, value));
	}

return(TCL_OK);
}

static int gtkwavetcl_getStemsFileName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
char *value = GLOBALS->stems_name;
if(value)
	{
	return(gtkwavetcl_printString(clientData, interp, objc, objv, value));
	}

return(TCL_OK);
}

static int gtkwavetcl_getTraceScrollbarRowValue(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
GtkAdjustment *wadj=GTK_ADJUSTMENT(GLOBALS->wave_vslider);
int value = (int)gtk_adjustment_get_value(wadj);

return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}



static int gtkwavetcl_setMarker(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        TimeType mrk = unformat_time(s, GLOBALS->time_dimension);

	if((mrk >= GLOBALS->min_time) && (mrk <= GLOBALS->max_time))
		{
		GLOBALS->tims.marker = mrk;
		}
		else
		{
		GLOBALS->tims.marker = LLDescriptor(-1);
		}

        update_markertime(GLOBALS->tims.marker);
        GLOBALS->signalwindow_width_dirty=1;
        MaxSignalLength();
        signalarea_configure_event(GLOBALS->signalarea, NULL);
        wavearea_configure_event(GLOBALS->wavearea, NULL);

	gtkwave_main_iteration();
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}


static int gtkwavetcl_setBaselineMarker(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        TimeType mrk = unformat_time(s, GLOBALS->time_dimension);

	if((mrk >= GLOBALS->min_time) && (mrk <= GLOBALS->max_time))
		{
		GLOBALS->tims.baseline = mrk;
		}
		else
		{
		GLOBALS->tims.baseline = LLDescriptor(-1);
		}

        update_markertime(GLOBALS->tims.marker);
        GLOBALS->signalwindow_width_dirty=1;
        MaxSignalLength();
        signalarea_configure_event(GLOBALS->signalarea, NULL);
        wavearea_configure_event(GLOBALS->wavearea, NULL);

	gtkwave_main_iteration();
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}


static int gtkwavetcl_setWindowStartTime(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);

	if(s)
	        {
	        TimeType gt;
	        char timval[40];
	        GtkAdjustment *hadj;
	        TimeType pageinc;

	        gt=unformat_time(s, GLOBALS->time_dimension);

	        if(gt<GLOBALS->tims.first) gt=GLOBALS->tims.first;
	        else if(gt>GLOBALS->tims.last) gt=GLOBALS->tims.last;

	        hadj=GTK_ADJUSTMENT(GLOBALS->wave_hslider);
	        gtk_adjustment_set_value(hadj, gt);

	        pageinc=(TimeType)(((gdouble)GLOBALS->wavewidth)*GLOBALS->nspx);
	        if(gt<(GLOBALS->tims.last-pageinc+1))
	                GLOBALS->tims.timecache=gt;
	                else
	                {
	                GLOBALS->tims.timecache=GLOBALS->tims.last-pageinc+1;
	                if(GLOBALS->tims.timecache<GLOBALS->tims.first) GLOBALS->tims.timecache=GLOBALS->tims.first;
	                }

	        reformat_time(timval,GLOBALS->tims.timecache,GLOBALS->time_dimension);

	        time_update();
	        }

        signalarea_configure_event(GLOBALS->signalarea, NULL);
        wavearea_configure_event(GLOBALS->wavearea, NULL);
	gtkwave_main_iteration();
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_setZoomFactor(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        float f;

        sscanf(s, "%f", &f);
        if(f>0.0)
                {
                f=0.0; /* in case they try to go out of range */
                }
        else
        if(f<-62.0)
                {
                f=-62.0; /* in case they try to go out of range */
                }

        GLOBALS->tims.prevzoom=GLOBALS->tims.zoom;
        GLOBALS->tims.zoom=(gdouble)f;
        calczoom(GLOBALS->tims.zoom);
        fix_wavehadj();

        g_signal_emit_by_name (XXX_GTK_OBJECT (GTK_ADJUSTMENT(GLOBALS->wave_hslider)), "changed");
        g_signal_emit_by_name (XXX_GTK_OBJECT (GTK_ADJUSTMENT(GLOBALS->wave_hslider)), "value_changed");

	gtkwave_main_iteration();
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_setZoomRangeTimes(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 3)
        {
        char *s, *t;
	TimeType time1, time2;
	TimeType oldmarker = GLOBALS->tims.marker;

	s = get_Tcl_string(objv[1]);
	time1 = unformat_time(s, GLOBALS->time_dimension);
        t = get_Tcl_string(objv[2]);
	time2 = unformat_time(t, GLOBALS->time_dimension);

	if(time1 < GLOBALS->tims.first) { time1 = GLOBALS->tims.first; }
	if(time1 > GLOBALS->tims.last)  { time1 = GLOBALS->tims.last; }
	if(time2 < GLOBALS->tims.first) { time2 = GLOBALS->tims.first; }
	if(time2 > GLOBALS->tims.last)  { time2 = GLOBALS->tims.last; }

	service_dragzoom(time1, time2);
	GLOBALS->tims.marker = oldmarker;

        GLOBALS->signalwindow_width_dirty=1;
        MaxSignalLength();
        signalarea_configure_event(GLOBALS->signalarea, NULL);
        wavearea_configure_event(GLOBALS->wavearea, NULL);

	gtkwave_main_iteration();
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_setLeftJustifySigs(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        TimeType val = atoi_64(s);
	GLOBALS->left_justify_sigs = (val != LLDescriptor(0)) ? ~0 : 0;

        MaxSignalLength();
        signalarea_configure_event(GLOBALS->signalarea, NULL);

	gtkwave_main_iteration();
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_setNamedMarker(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if((objc == 3)||(objc == 4))
        {
        char *s = get_Tcl_string(objv[1]);
	int which = -1;

        if((s[0]>='A')&&(s[0]<='Z'))
                {
		which = bijective_marker_id_string_hash(s);
                }
        else
        if((s[0]>='a')&&(s[0]<='z'))
                {
		which = bijective_marker_id_string_hash(s);
                }
	else
		{
	        which = atoi(s);
		}

        if((which >= 0) && (which < WAVE_NUM_NAMED_MARKERS))
                {
	        char *t = get_Tcl_string(objv[2]);
		TimeType gt=unformat_time(t, GLOBALS->time_dimension);

                GLOBALS->named_markers[which] = gt;

		if(GLOBALS->marker_names[which])
			{
			free_2(GLOBALS->marker_names[which]);
			GLOBALS->marker_names[which] = NULL;
			}

		if(objc == 4)
			{
			char *u = get_Tcl_string(objv[3]);

			GLOBALS->marker_names[which] = strdup_2(u);
			}

	        wavearea_configure_event(GLOBALS->wavearea, NULL);
		gtkwave_main_iteration();
                }
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
        }

return(TCL_OK);
}


static int gtkwavetcl_setTraceScrollbarRowValue(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        int target = atoi(s);

 	SetTraceScrollbarRowValue(target, 0);

/*         GtkAdjustment *wadj=GTK_ADJUSTMENT(GLOBALS->wave_vslider); */

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

/* 	if(target > GLOBALS->traces.visible - num_traces_displayable) target = GLOBALS->traces.visible - num_traces_displayable; */

/* 	if(target < 0) target = 0; */

/* 	wadj->value = target; */

/*         g_signal_emit_by_name (XXX_GTK_OBJECT (wadj), "changed"); /\* force bar update *\/ */
/*         g_signal_emit_by_name (XXX_GTK_OBJECT (wadj), "value_changed"); /\* force text update *\/ */
/*	gtkwave_main_iteration(); */
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_addCommentTracesFromList(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
char reportString[33];
Tcl_Obj *aobj;

if(objc==2)
	{
	char *s = Tcl_GetString(objv[1]);
        char** elem = NULL;
        int i, l = 0;

        elem = zSplitTclList(s, &l);
	if(elem)
		{
		for(i=0;i<l;i++)
			{
			InsertBlankTrace(elem[i], 0);
			}
		free_2(elem);
		}

	GLOBALS->signalwindow_width_dirty=1;
	MaxSignalLength();
	signalarea_configure_event(GLOBALS->signalarea, NULL);
	wavearea_configure_event(GLOBALS->wavearea, NULL);

	sprintf(reportString, "%d", l);

	aobj = Tcl_NewStringObj(reportString, -1);
	Tcl_SetObjResult(interp, aobj);
	}
	else
	{
	return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
	}

return(TCL_OK);
}


static int gtkwavetcl_addSignalsFromList(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int i;
char *one_entry = NULL, *mult_entry = NULL;
unsigned int mult_len = 0;
int num_found = 0;
char reportString[33];
Tcl_Obj *aobj;

if(objc==2)
	{
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces! */
	char** elem = NULL;
	int l = 0;

	elem = zSplitTclList(s, &l);

	if(elem)
        	{
		for(i=0;i<l;i++)
			{
			one_entry = make_single_tcl_list_name(elem[i], NULL, 0, 1); /* keep range */
			WAVE_OE_ME
			}
                free_2(elem);
                elem = NULL;
		if(mult_entry)
			{
			num_found = process_tcl_list(mult_entry, FALSE);
			free_2(mult_entry);
			}
		if(num_found)
        		{
        		MaxSignalLength();
        		signalarea_configure_event(GLOBALS->signalarea, NULL);
        		wavearea_configure_event(GLOBALS->wavearea, NULL);
			gtkwave_main_iteration();
        		}
                }
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

sprintf(reportString, "%d", num_found);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_processTclList(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int num_found = 0;
char reportString[33];
Tcl_Obj *aobj;

if(objc==2)
	{
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces! */
	if(s)
        	{
		num_found = process_tcl_list(s, FALSE);
		if(num_found)
        		{
        		MaxSignalLength();
        		signalarea_configure_event(GLOBALS->signalarea, NULL);
        		wavearea_configure_event(GLOBALS->wavearea, NULL);
			gtkwave_main_iteration();
        		}
                }
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

sprintf(reportString, "%d", num_found);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_deleteSignalsFromList(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int i;
int num_found = 0;
char reportString[33];
Tcl_Obj *aobj;

if(objc==2)
	{
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces */
	char** elem = NULL;
	int l = 0;

	elem = zSplitTclList(s, &l);

	if(elem)
        	{
		Trptr t = GLOBALS->traces.first;
		while(t)
			{
			t->cached_flags = t->flags;
			t->flags &= (~TR_HIGHLIGHT);
			t = t->t_next;
			}

		for(i=0;i<l;i++)
			{
			t = GLOBALS->traces.first;
			while(t)
				{
				if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH|TR_HIGHLIGHT)))
					{
					char *name = extractFullTraceName(t);
					if(name)
						{
						int len_name = strlen(name);
						int len_elem = strlen(elem[i]);
						int brackmatch = (len_name > len_elem) && (name[len_elem] == '[');

						if(((len_name == len_elem) && (!strcmp(name, elem[i])))
							|| (brackmatch && !strncmp(name, elem[i], len_elem)))
							{
							t->flags |= TR_HIGHLIGHT;
							num_found++;
							break;
							}
						free_2(name);
						}
					}
				t = t->t_next;
				}
			}

                free_2(elem);
                elem = NULL;

		if(num_found)
        		{
			CutBuffer();
			}

		t = GLOBALS->traces.first;
		while(t)
			{
			t->flags = t->cached_flags;
			t->cached_flags = 0;
			t = t-> t_next;
			}

		if(num_found)
        		{
        		MaxSignalLength();
        		signalarea_configure_event(GLOBALS->signalarea, NULL);
        		wavearea_configure_event(GLOBALS->wavearea, NULL);
			gtkwave_main_iteration();
        		}
                }
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

sprintf(reportString, "%d", num_found);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_deleteSignalsFromListIncludingDuplicates(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int i;
int num_found = 0;
char reportString[33];
Tcl_Obj *aobj;

if(objc==2)
	{
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces */
	char** elem = NULL;
	int l = 0;

	elem = zSplitTclList(s, &l);

	if(elem)
        	{
		Trptr t = GLOBALS->traces.first;
		while(t)
			{
			t->cached_flags = t->flags;
			t->flags &= (~TR_HIGHLIGHT);

			if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
				{
				char *name = extractFullTraceName(t);
				if(name)
					{
					for(i=0;i<l;i++)
						{
						int len_name = strlen(name);
						int len_elem = strlen(elem[i]);
						int brackmatch = (len_name > len_elem) && (name[len_elem] == '[');

						if(((len_name == len_elem) && (!strcmp(name, elem[i])))
							|| (brackmatch && !strncmp(name, elem[i], len_elem)))
							{
							t->flags |= TR_HIGHLIGHT;
							num_found++;
							break;
							}
						}
					free_2(name);
					}
				}

			t = t-> t_next;
			}

                free_2(elem);
                elem = NULL;

		if(num_found)
        		{
			CutBuffer();
			}

		t = GLOBALS->traces.first;
		while(t)
			{
			t->flags = t->cached_flags;
			t->cached_flags = 0;
			t = t-> t_next;
			}

		if(num_found)
        		{
        		MaxSignalLength();
        		signalarea_configure_event(GLOBALS->signalarea, NULL);
        		wavearea_configure_event(GLOBALS->wavearea, NULL);
			gtkwave_main_iteration();
        		}
                }
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

sprintf(reportString, "%d", num_found);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_highlightSignalsFromList(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int i;
int num_found = 0;
char reportString[33];
Tcl_Obj *aobj;

if(objc==2)
	{
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces */
	char** elem = NULL;
	int l = 0;

	elem = zSplitTclList(s, &l);

	if(elem)
        	{
		Trptr t = GLOBALS->traces.first;
		while(t)
			{
			if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
				{
				char *name = extractFullTraceName(t);
				if(name)
					{
					for(i=0;i<l;i++)
						{
						if(!strcmp(name, elem[i]))
							{
							t->flags |= TR_HIGHLIGHT;
							num_found++;
							break;
							}
						}
					free_2(name);
					}
				}

			t = t-> t_next;
			}

                free_2(elem);
                elem = NULL;

		if(num_found)
        		{
        		MaxSignalLength();
        		signalarea_configure_event(GLOBALS->signalarea, NULL);
        		wavearea_configure_event(GLOBALS->wavearea, NULL);
			gtkwave_main_iteration();
        		}
                }
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

sprintf(reportString, "%d", num_found);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}

static int gtkwavetcl_unhighlightSignalsFromList(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int i;
int num_found = 0;
char reportString[33];
Tcl_Obj *aobj;

if(objc==2)
	{
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces */
	char** elem = NULL;
	int l = 0;

	elem = zSplitTclList(s, &l);

	if(elem)
        	{
		Trptr t = GLOBALS->traces.first;
		while(t)
			{
			if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
				{
				char *name = extractFullTraceName(t);
				if(name)
					{
					for(i=0;i<l;i++)
						{
						if(!strcmp(name, elem[i]))
							{
							t->flags &= (~TR_HIGHLIGHT);
							num_found++;
							break;
							}
						}
					free_2(name);
					}
				}

			t = t-> t_next;
			}

                free_2(elem);
                elem = NULL;

		if(num_found)
        		{
        		MaxSignalLength();
        		signalarea_configure_event(GLOBALS->signalarea, NULL);
        		wavearea_configure_event(GLOBALS->wavearea, NULL);
			gtkwave_main_iteration();
        		}
                }
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

sprintf(reportString, "%d", num_found);

aobj = Tcl_NewStringObj(reportString, -1);
Tcl_SetObjResult(interp, aobj);

return(TCL_OK);
}


static int gtkwavetcl_setTraceHighlightFromIndex(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 3)
	{
	char *s = get_Tcl_string(objv[1]);
	int which = atoi(s);
	char *ts = get_Tcl_string(objv[2]);
	int onoff = atoi_64(ts);

	if((which >= 0) && (which < GLOBALS->traces.total))
		{
		Trptr t = GLOBALS->traces.first;
		int i = 0;
		while(t)
			{
			if(i == which)
				{
				if(onoff)
					{
					t->flags |= TR_HIGHLIGHT;
					}
					else
					{
					t->flags &= (~TR_HIGHLIGHT);
					}
	        		signalarea_configure_event(GLOBALS->signalarea, NULL);
				gtkwave_main_iteration();
				break;
				}

			i++;
			t = t->t_next;
			}
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
        }

return(TCL_OK);
}

static int gtkwavetcl_setTraceHighlightFromNameMatch(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 3)
	{
	char *s = get_Tcl_string(objv[1]);
	int which = atoi(s);
	char *ts = get_Tcl_string(objv[2]);
	int onoff = atoi_64(ts);
	int mat = 0;

	if((which >= 0) && (which < GLOBALS->traces.total))
		{
		Trptr t = GLOBALS->traces.first;
		int i = 0;
		while(t)
			{
			if(t->name && !strcmp(t->name, s))
				{
				if(onoff)
					{
					t->flags |= TR_HIGHLIGHT;
					}
					else
					{
					t->flags &= (~TR_HIGHLIGHT);
					}
				mat++;
				}

			i++;
			t = t->t_next;
			}

		if(mat)
			{
        		signalarea_configure_event(GLOBALS->signalarea, NULL);
			gtkwave_main_iteration();
			}

		return(gtkwavetcl_printInteger(clientData, interp, objc, objv, mat));
		}
	}
	else
	{
	return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
	}

return(TCL_OK);
}


static int gtkwavetcl_signalChangeList(ClientData clientData, Tcl_Interp *interp,
				       int objc, Tcl_Obj *const objv[]) {
(void) clientData;

    int dir = STRACE_FORWARD ;
    TimeType start_time = 0 ;
    TimeType end_time = MAX_HISTENT_TIME ;
    int max_elements = 0x7fffffff ;
    char *sig_name = NULL ;
    int i ;
    char *str_p, *str1_p ;
    int error = 0 ;
    llist_p *l_head ;
    Tcl_Obj *l_obj ;
    Tcl_Obj *obj ;
    llist_p *p, *p1 ;

    for(i=1; i<objc; i++) {
      str_p = Tcl_GetStringFromObj(objv[i], NULL) ;
      if (*str_p != '-') {
	sig_name = str_p ;
      } else {
        if(i == (objc-1)) { /* loop overflow on i check */
	  error++;
	  break;
	  }
	str1_p = Tcl_GetStringFromObj(objv[++i], NULL) ;
	switch(str_p[1]) {
	case 's': /* start time */
	  if(!strstr("-start_time", str_p))
	    error++ ;
	  else {
	    if((start_time = atoi_64(str1_p)) < 0)
	      start_time = 0 ;
	    dir = (start_time > end_time) ? STRACE_BACKWARD : STRACE_FORWARD ;
	  }
	  break ;
	case 'e': /* end time */
	  if(!strstr("-end_time", str_p))
	    error++ ;
	  else {
	    end_time = atoi_64(str1_p) ;
	    dir = (start_time > end_time) ? STRACE_BACKWARD : STRACE_FORWARD ;
	  }
	  break ;
	case 'm': /* max */
	  if(!strstr("-max", str_p))
	    error++ ;
	  else {
	    max_elements = atoi(str1_p) ;
	  }
	  break ;
	case 'd': /* dir */
	  if(!strstr("-dir", str_p))
	    error++ ;
	  else {
	    if(strstr("forward", str1_p))
	      dir = STRACE_FORWARD ;
	    else
	      if(strstr("backward", str1_p))
		dir = STRACE_BACKWARD ;
	      else
		error++ ;
	  }
	  break ;
	default:
	  error++ ;
	}
      }
    }
    /* consistancy check */
    if(dir == STRACE_FORWARD) {
      if(start_time > end_time) error++ ;
    } else {
      if(start_time < end_time) {
	if(end_time == MAX_HISTENT_TIME)
	  end_time = 0 ;
	else
	  error ++ ;
      }
    }
    if(error) {
      Tcl_SetObjResult
	(interp,
	 Tcl_NewStringObj("Usage: signal_change_list ?name? ?-start time? ?-end time? ?-max size? ?-dir forward|backward?", -1)) ;
      return TCL_ERROR;
    }
    l_head = signal_change_list(sig_name, dir, start_time, end_time, max_elements) ;
    l_obj = Tcl_NewListObj(0, NULL) ;
    p = l_head;
    while(p) {
      obj = Tcl_NewWideIntObj((Tcl_WideInt )p->u.tt) ;
      p1= p->next ;
      free_2(p) ;
      Tcl_ListObjAppendElement(interp, l_obj, obj) ;
      obj = Tcl_NewStringObj(p1->u.str,-1) ;
      Tcl_ListObjAppendElement(interp, l_obj, obj) ;
      p = p1->next ;
      free_2(p1->u.str) ;
      free_2(p1) ;
    }
    Tcl_SetObjResult(interp, l_obj) ;
    return TCL_OK ;
}

static int gtkwavetcl_findNextEdge(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
edge_search(STRACE_FORWARD);
gtkwave_main_iteration();
return(gtkwavetcl_getMarker(clientData, interp, objc, objv));
}


static int gtkwavetcl_findPrevEdge(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
edge_search(STRACE_BACKWARD);
gtkwave_main_iteration();
return(gtkwavetcl_getMarker(clientData, interp, objc, objv));
}


int SST_open_node(char *name) ;
static int gtkwavetcl_forceOpenTreeNode(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
  int rv = -100;		/* Tree does not exist */
  char *s = NULL ;
  if(objc == 2)
    s = get_Tcl_string(objv[1]);

  if(s && (strlen(s) > 1)) {	/* exclude empty strings */
    int len = strlen(s);
    if(s[len-1]!=GLOBALS->hier_delimeter)
      {
	rv = SST_open_node(s);
      }
    else {
      rv = SST_open_node(s);
    }
  } else {
    if (GLOBALS->selected_hierarchy_name) {
      rv = SST_NODE_CURRENT ;
    }
    gtkwave_main_iteration(); /* check if this is needed */
  }
  if (rv == -100) {
    return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
  }
  Tcl_SetObjResult(GLOBALS->interp, (rv == SST_NODE_CURRENT) ?
		   Tcl_NewStringObj(GLOBALS->selected_hierarchy_name,
				    strlen(GLOBALS->selected_hierarchy_name)) :
		   Tcl_NewIntObj(rv)) ;

  return(TCL_OK);
}

static int gtkwavetcl_setFromEntry(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);

	if(s)
		{
		gtk_entry_set_text(GTK_ENTRY(GLOBALS->from_entry),s);
		from_entry_callback(NULL, GLOBALS->from_entry);
		}

	gtkwave_main_iteration();
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}


static int gtkwavetcl_setToEntry(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);

	if(s)
		{
		gtk_entry_set_text(GTK_ENTRY(GLOBALS->to_entry),s);
		to_entry_callback(NULL, GLOBALS->to_entry);
		}

	gtkwave_main_iteration();
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}


static int gtkwavetcl_getFromEntry(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
const char *value = gtk_entry_get_text(GTK_ENTRY(GLOBALS->from_entry));
if(value)
        {
        return(gtkwavetcl_printString(clientData, interp, objc, objv, value));
        }

return(TCL_OK);
}


static int gtkwavetcl_getToEntry(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
const char *value = gtk_entry_get_text(GTK_ENTRY(GLOBALS->to_entry));
if(value)
        {
        return(gtkwavetcl_printString(clientData, interp, objc, objv, value));
        }

return(TCL_OK);
}


static int gtkwavetcl_getDisplayedSignals(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 1)
        {
	char *rv = add_traces_from_signal_window(TRUE);
	int rc = gtkwavetcl_printString(clientData, interp, objc, objv, rv);

	free_2(rv);
	return(rc);
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }
}


static int gtkwavetcl_getTraceFlagsFromName(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
	{
	char *s = get_Tcl_string(objv[1]);
	Trptr t = GLOBALS->traces.first;

	while(t)
		{
		if(!(t->flags&(TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
			{
			char *name = extractFullTraceName(t);
			if(!strcmp(name, s))
				{
				free_2(name);
				break;
				}
			free_2(name);
			}
		t = t-> t_next;
		}

	if(t)
		{
		return(gtkwavetcl_printTraceFlagsType(clientData, interp, objc, objv, t->flags));
		}
	}
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

return(TCL_OK);
}

static int gtkwavetcl_loadFile(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
  if(objc == 2)
    {
      char *s = get_Tcl_string(objv[1]);

      if(!GLOBALS->in_tcl_callback)
        {
	/* wave_gconf_client_set_string("/current/savefile", s); */
        /*	read_save_helper(s, NULL, NULL, NULL, NULL); */
        process_url_file(s);
        /*	process_url_list(s); */
        /*	gtkwave_main_iteration(); */
        }
	else
	{
	gtkwavetcl_setvar_nonblocking(WAVE_TCLCB_ERROR,"gtkwave::loadFile prohibited in callback",WAVE_TCLCB_ERROR_FLAGS);
	}
    }
  else
    {
      return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

  return(TCL_OK);
}

static int gtkwavetcl_reLoadFile(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{

  if(objc == 1)
    {
      if(!GLOBALS->in_tcl_callback)
	{
      	reload_into_new_context();
	}
	else
	{
	gtkwavetcl_setvar_nonblocking(WAVE_TCLCB_ERROR,"gtkwave::reLoadFile prohibited in callback",WAVE_TCLCB_ERROR_FLAGS);
	}
    }
  else
    {
      return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 0));
    }
  return(TCL_OK);
}

static int gtkwavetcl_presentWindow(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{

  if(objc == 1)
    {
      gtk_window_present(GTK_WINDOW(GLOBALS->mainwindow));
    }
  else
    {
      return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 0));
    }
  return(TCL_OK);
}

static int gtkwavetcl_showSignal(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
  if(objc == 3)
    {
      char *s0 = get_Tcl_string(objv[1]);
      char *s1;
      int row;
      unsigned location;

      sscanf(s0, "%d", &row);
      if (row < 0) { row = 0; };

      s1 = get_Tcl_string(objv[2]);
      sscanf(s1, "%u", &location);

      SetTraceScrollbarRowValue(row, location);
    }
  else
    {
      return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
    }

  return(TCL_OK);
}


/*
 * swap to a given context based on tab number (from Tcl)
 */
static gint switch_to_tab_number(unsigned int i)
{
if(i < GLOBALS->num_notebook_pages)
        {
        struct Global *g_old = GLOBALS;
        /* printf("Switching to: %d\n", i); */

        set_GLOBALS((*GLOBALS->contexts)[i]);

        GLOBALS->lxt_clock_compress_to_z = g_old->lxt_clock_compress_to_z;
        GLOBALS->autoname_bundles = g_old->autoname_bundles;
        GLOBALS->autocoalesce_reversal = g_old->autocoalesce_reversal;
        GLOBALS->autocoalesce = g_old->autocoalesce;
        GLOBALS->hier_grouping = g_old->hier_grouping;
        GLOBALS->wave_scrolling = g_old->wave_scrolling;
        GLOBALS->constant_marker_update = g_old->constant_marker_update;
        GLOBALS->do_zoom_center = g_old->do_zoom_center;
        GLOBALS->use_roundcaps = g_old->use_roundcaps;
        GLOBALS->do_resize_signals = g_old->do_resize_signals;
        GLOBALS->alt_wheel_mode = g_old->alt_wheel_mode;
	GLOBALS->initial_signal_window_width = g_old->initial_signal_window_width;
        GLOBALS->use_full_precision = g_old->use_full_precision;
        GLOBALS->show_base = g_old->show_base;
        GLOBALS->display_grid = g_old->display_grid;
        GLOBALS->highlight_wavewindow = g_old->highlight_wavewindow;
        GLOBALS->disable_mouseover = g_old->disable_mouseover;
        GLOBALS->zoom_pow10_snap = g_old->zoom_pow10_snap;

        GLOBALS->scale_to_time_dimension = g_old->scale_to_time_dimension;
        GLOBALS->zoom_dyn = g_old->zoom_dyn;
        GLOBALS->zoom_dyne = g_old->zoom_dyne;

        gtk_notebook_set_current_page(GTK_NOTEBOOK(GLOBALS->notebook), GLOBALS->this_context_page);
        return(TRUE);
        }

return(FALSE);
}


static int gtkwavetcl_setTabActive(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
	gint rc;

	if(!GLOBALS->in_tcl_callback)
		{
        	char *s = get_Tcl_string(objv[1]);
        	unsigned int tabnum = atoi(s);
		rc = switch_to_tab_number(tabnum);

        	MaxSignalLength();
        	signalarea_configure_event(GLOBALS->signalarea, NULL);

        	gtkwave_main_iteration();
		}
		else
		{
		gtkwavetcl_setvar_nonblocking(WAVE_TCLCB_ERROR,"gtkwave::setTabActive prohibited in callback",WAVE_TCLCB_ERROR_FLAGS);
		rc = -1;
		}

	return(gtkwavetcl_printInteger(clientData, interp, objc, objv, rc));
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

}


static int gtkwavetcl_getNumTabs(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
int value = GLOBALS->num_notebook_pages;
return(gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}


static int gtkwavetcl_installFileFilter(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        unsigned int which = atoi(s);
	gint rc = install_file_filter(which);

        gtkwave_main_iteration();
	return(gtkwavetcl_printInteger(clientData, interp, objc, objv, rc));
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

}


static int gtkwavetcl_setCurrentTranslateFile(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
	set_current_translate_file(s);

	return(gtkwavetcl_printInteger(clientData, interp, objc, objv, GLOBALS->current_translate_file));
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

}


static int gtkwavetcl_setCurrentTranslateEnums(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
	set_current_translate_enums(s);

	return(gtkwavetcl_printInteger(clientData, interp, objc, objv, GLOBALS->current_translate_file));
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

}


static int gtkwavetcl_installProcFilter(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        unsigned int which = atoi(s);
	gint rc = install_proc_filter(which);

        gtkwave_main_iteration();
	return(gtkwavetcl_printInteger(clientData, interp, objc, objv, rc));
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

}


static int gtkwavetcl_setCurrentTranslateProc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
	set_current_translate_proc(s);

	return(gtkwavetcl_printInteger(clientData, interp, objc, objv, GLOBALS->current_translate_proc));
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

}


static int gtkwavetcl_installTransFilter(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
        unsigned int which = atoi(s);
	gint rc = install_ttrans_filter(which);

        gtkwave_main_iteration();
	return(gtkwavetcl_printInteger(clientData, interp, objc, objv, rc));
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

}


static int gtkwavetcl_setCurrentTranslateTransProc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
if(objc == 2)
        {
        char *s = get_Tcl_string(objv[1]);
	set_current_translate_ttrans(s);

	return(gtkwavetcl_printInteger(clientData, interp, objc, objv, GLOBALS->current_translate_ttrans));
        }
        else
        {
        return(gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
        }

}


tcl_cmdstruct gtkwave_commands[] =
	{
	{"addCommentTracesFromList",		gtkwavetcl_addCommentTracesFromList},
	{"addSignalsFromList",			gtkwavetcl_addSignalsFromList},
	{"deleteSignalsFromList",		gtkwavetcl_deleteSignalsFromList},
	{"deleteSignalsFromListIncludingDuplicates", gtkwavetcl_deleteSignalsFromListIncludingDuplicates},
	{"findNextEdge",			gtkwavetcl_findNextEdge},
	{"findPrevEdge",			gtkwavetcl_findPrevEdge},
	{"forceOpenTreeNode",			gtkwavetcl_forceOpenTreeNode},
	{"getArgv",				gtkwavetcl_getArgv},
	{"getBaselineMarker",			gtkwavetcl_getBaselineMarker},
	{"getDisplayedSignals",			gtkwavetcl_getDisplayedSignals},
	{"getDumpFileName",			gtkwavetcl_getDumpFileName},
	{"getDumpType", 			gtkwavetcl_getDumpType},
        {"getFacDir",                           gtkwavetcl_getFacDir},
        {"getFacDtype",                         gtkwavetcl_getFacDtype},
        {"getFacName",                          gtkwavetcl_getFacName},
        {"getFacVtype",                         gtkwavetcl_getFacVtype},
	{"getFontHeight",			gtkwavetcl_getFontHeight},
	{"getFromEntry",			gtkwavetcl_getFromEntry},
	{"getHierMaxLevel",			gtkwavetcl_getHierMaxLevel},
	{"getLeftJustifySigs",			gtkwavetcl_getLeftJustifySigs},
	{"getLongestName", 			gtkwavetcl_getLongestName},
	{"getMarker",				gtkwavetcl_getMarker},
	{"getMaxTime", 				gtkwavetcl_getMaxTime},
	{"getMinTime", 				gtkwavetcl_getMinTime},
	{"getNamedMarker", 			gtkwavetcl_getNamedMarker},
	{"getNumFacs", 				gtkwavetcl_getNumFacs},
	{"getNumTabs",				gtkwavetcl_getNumTabs},
	{"getPixelsUnitTime", 			gtkwavetcl_getPixelsUnitTime},
	{"getSaveFileName",			gtkwavetcl_getSaveFileName},
	{"getStemsFileName",			gtkwavetcl_getStemsFileName},
	{"getTimeDimension", 			gtkwavetcl_getTimeDimension},
	{"getTimeZero", 			gtkwavetcl_getTimeZero},
	{"getToEntry",				gtkwavetcl_getToEntry},
	{"getTotalNumTraces",  			gtkwavetcl_getTotalNumTraces},
	{"getTraceFlagsFromIndex", 		gtkwavetcl_getTraceFlagsFromIndex},
	{"getTraceFlagsFromName",		gtkwavetcl_getTraceFlagsFromName},
	{"getTraceNameFromIndex", 		gtkwavetcl_getTraceNameFromIndex},
	{"getTraceScrollbarRowValue", 		gtkwavetcl_getTraceScrollbarRowValue},
	{"getTraceValueAtMarkerFromIndex", 	gtkwavetcl_getTraceValueAtMarkerFromIndex},
	{"getTraceValueAtMarkerFromName",	gtkwavetcl_getTraceValueAtMarkerFromName},
	{"getTraceValueAtNamedMarkerFromName",	gtkwavetcl_getTraceValueAtNamedMarkerFromName},
	{"getUnitTimePixels", 			gtkwavetcl_getUnitTimePixels},
	{"getVisibleNumTraces", 		gtkwavetcl_getVisibleNumTraces},
	{"getWaveHeight", 			gtkwavetcl_getWaveHeight},
	{"getWaveWidth", 			gtkwavetcl_getWaveWidth},
	{"getWindowEndTime", 			gtkwavetcl_getWindowEndTime},
	{"getWindowStartTime", 			gtkwavetcl_getWindowStartTime},
	{"getZoomFactor",			gtkwavetcl_getZoomFactor},
	{"highlightSignalsFromList",		gtkwavetcl_highlightSignalsFromList},
	{"installFileFilter",			gtkwavetcl_installFileFilter},
	{"installProcFilter",			gtkwavetcl_installProcFilter},
	{"installTransFilter",			gtkwavetcl_installTransFilter},
	{"loadFile",			        gtkwavetcl_loadFile},
   	{"nop", 				gtkwavetcl_nop},
	{"presentWindow",			gtkwavetcl_presentWindow},
	{"processTclList",			gtkwavetcl_processTclList}, /* not for general-purpose use */
	{"reLoadFile",			        gtkwavetcl_reLoadFile},
	{"setBaselineMarker",			gtkwavetcl_setBaselineMarker},
	{"setCurrentTranslateEnums",		gtkwavetcl_setCurrentTranslateEnums},
	{"setCurrentTranslateFile",		gtkwavetcl_setCurrentTranslateFile},
	{"setCurrentTranslateProc",		gtkwavetcl_setCurrentTranslateProc},
	{"setCurrentTranslateTransProc",	gtkwavetcl_setCurrentTranslateTransProc},
	{"setFromEntry",			gtkwavetcl_setFromEntry},
	{"setLeftJustifySigs",			gtkwavetcl_setLeftJustifySigs},
	{"setMarker",				gtkwavetcl_setMarker},
	{"setNamedMarker",			gtkwavetcl_setNamedMarker},
   	{"setTabActive",			gtkwavetcl_setTabActive},
	{"setToEntry",				gtkwavetcl_setToEntry},
	{"setTraceHighlightFromIndex",		gtkwavetcl_setTraceHighlightFromIndex},
	{"setTraceHighlightFromNameMatch",	gtkwavetcl_setTraceHighlightFromNameMatch},
	{"setTraceScrollbarRowValue", 		gtkwavetcl_setTraceScrollbarRowValue},
	{"setWindowStartTime",			gtkwavetcl_setWindowStartTime},
	{"setZoomFactor",			gtkwavetcl_setZoomFactor},
	{"setZoomRangeTimes",			gtkwavetcl_setZoomRangeTimes},
	{"showSignal",         			gtkwavetcl_showSignal},
	{"signalChangeList",                    gtkwavetcl_signalChangeList},	/* changed from signal_change_list for consistency! */
	{"unhighlightSignalsFromList",		gtkwavetcl_unhighlightSignalsFromList},
   	{"", 					NULL} /* sentinel */
	};

#else

static void dummy_function(void)
{
/* nothing */
}

#endif


