/********************************************************/
/* OTS_GUI.c - OpenTaxSolver Graphical User Interface.	*/
/* Based on OpenToolKit (OTK).				*/
/*							*/
/* The OTS_GUI is designed to read-in standard OTS data	*/
/* files, and present the tax information on your 	*/
/* screen.  It allows entering the values, saving 	*/
/* return data, editing previously saved return sheets, */
/* and running the OTS tax-solver to compute your 	*/
/* taxes.						*/
/*							*/
/* The OTS tax-solver is a text program which can be	*/
/* used by itself. This GUI front-end simplifies using  */
/* OTS.  It walks you through the steps and invokes the	*/
/* regular OTS solver when you are ready.		*/
/*							*/
/* OTS data files, for example "tax_xx.dat", contain 	*/
/* the line numbers (or names) of the entries for a 	*/
/* given tax form, as well as any previously entered 	*/
/* values for each line.  Additional comments may 	*/
/* follow on any line.  				*/
/* A few example lines follow:				*/
/*							*/
/*	L15    	      ;  { Rental income }		*/
/*	L17    234.00 ;  { Savings interest }		*/
/*	L18     	 { Dividends }			*/
/*		23.00    {  Bank1 }			*/
/*		14.50 ;  {  Work loan }			*/
/*							*/
/* OTS_GUI reads these lines and places a label for 	*/
/* each line number/name, a text-box for filling in	*/
/* the value(s), and addtional labels for the comments.	*/
/* At the bottom are placed buttons to save and 	*/
/* calculate-taxes.					*/
/* 							*/
/* To compile this graphical program, you will need to  */
/* grab the Open graphics Tool-Kit (Otk). 		*/
/* Download Otk from:					*/
/*     https://sourceforge.net/projects/otk/		*/
/* It unpacks to a subdirectory called otk_lib.		*/
/*							*/
/* Compile:						*/
/*  cc -I/usr/X11R6/include -L/usr/X11R6/lib ots_gui.c  \
 	-lGLU -lGL -lXmu -lXext -lX11 -lm -o ots_gui	*/
/*							*/
/*							*/
/********************************************************/

float version=0.5;

#include "otk_lib/otk_lib.c"	/* Include the graphics library. */
int verbose=0;
FILE *infile;
int ots_column=0, ots_line=0;	/* Input file position. */
#define MaxFname 900
char directory_bin[MaxFname]=".", wildcards_bin[MaxFname]="", filename_exe[MaxFname]="";
char directory_dat[MaxFname]=".", wildcards_dat[MaxFname]="*.dat", filename_dat[MaxFname]="";
char title_line[900], *working_file=0, *invocation_path;
OtkWidget title_panel, title_label, main_panel, warningpopup=0, popup, commentbox;
OtkTabbedPanel *TabbedPanel;
void open_taxfile();
void save_taxfile();

#define VKIND_FLOAT   0
#define VKIND_INT     1
#define VKIND_TEXT    2
#define VKIND_COMMENT 3
#define VKIND_COLON   4

#define VALUE_LABEL 0
#define COMMENT     1
#define SEMICOLON   2
#define NOTHING	    10

struct value_list
 {
  int	    kind;	/* 0=float, 1=integer, 2=text, 3=comment. */
  float     value;
  char      *comment, *text;
  int       column, linenum;
  struct taxline_record *parent;
  OtkWidget box;
  struct value_list *nxt;
 };

struct taxline_record
 {
  char *linename;
  int linenum, format_offset;
  struct value_list *values_hd, *values_tl;	/* Head and tail list pointers for a tax-line-entry. */
  struct taxline_record *nxt;
 } *taxlines_hd=0, *taxlines_tl=0;		/* Head and tail list pointers for tax-form. */


 struct taxline_record * 
new_taxline( char *linename, int linenum )
{
 struct taxline_record *tmppt;

 tmppt = (struct taxline_record *)malloc(sizeof(struct taxline_record));
 tmppt->linename = strdup(linename);
 tmppt->linenum = linenum;
 tmppt->format_offset = 0;
 tmppt->values_hd = 0;
 tmppt->values_tl = 0;
 tmppt->nxt = 0;
 if (taxlines_hd==0) taxlines_hd = tmppt;
 else taxlines_tl->nxt = tmppt;
 taxlines_tl = tmppt;
 return tmppt;
}


 struct value_list * 
new_list_item_value( int kind, struct taxline_record *txline, void *x, int column, int linenum )
{ 
 struct value_list *tmppt;

 tmppt = (struct value_list *)malloc(sizeof(struct value_list));
 tmppt->kind = kind;
 tmppt->text = 0;
 tmppt->comment = 0;
 tmppt->column = column;
 tmppt->linenum = linenum;
 tmppt->box = 0;
 switch (kind)
  {
   case VKIND_FLOAT:   tmppt->value = *(float *)x; break;
   case VKIND_INT:     tmppt->value = *(int *)x; break;
   case VKIND_TEXT:    tmppt->text = strdup( (char *)x ); break;
   case VKIND_COMMENT: tmppt->comment = strdup( (char *)x ); break;
  }
 tmppt->parent = txline;
 tmppt->nxt = 0;
 if (txline==0) {printf("ERROR1:  called add_value %d before any line.\n",kind); return tmppt;}
 if (txline->values_hd==0) txline->values_hd = tmppt;  else  txline->values_tl->nxt = tmppt;
 txline->values_tl = tmppt;
 return tmppt;
}


/*--------------------------------------------------------------*/
/* Get_Next_Entry - Reads next item from input file.		*/
/* Returns 0=VALUE_LABEL if reads data value or line-label. 	*/
/* Returns 1=COMMENT     if reads comment.			*/
/* Returns 2=SEMICOLON   if reads ';' entry-end character.	*/
/*								*/
/* Passes back the column and line number where the current     */
/* entry begins on the line in the input file.  		*/
/*--------------------------------------------------------------*/
int get_next_entry( char *word, int maxn, int *column, int *linenum, FILE *infile )
{
 int k=0;

 /* Get up to the next non-white-space character. */
 do 
  { 
   word[k] = getc(infile);
   if (word[k] == '\n') { ots_column = 0;  ots_line++; } else ots_column++;
  }
 while ((!feof(infile)) && ((word[k]==' ') || (word[k]=='	') || (word[k]=='\n') || (word[k]==13)));
 *column = ots_column;
 *linenum = ots_line;

 if (feof(infile)) {word[0] = '\0'; return NOTHING;}
 if (word[k]=='{')
  { /*get_comment*/
    do 
     {
      word[k++] = getc(infile);
      if (word[k-1] == '\n') { ots_column = 0;  ots_line++; } else ots_column++;
     }
    while ((!feof(infile)) && (word[k-1]!='}') && (k<maxn));
    word[k-1] = '\0';
    if (k>=maxn) {printf("Error: Character buffer overflow detected.\n"); exit(0);}
    return COMMENT;
  } /*get_comment*/
 else
  { /*get_value_or_linelabel*/
    k++;
    while ((!feof(infile)) && (word[k-1]!=' ') && (word[k-1]!='	') && 
	   (word[k-1]!='\n') && (word[k-1]!=13) && (word[k-1]!=';') && (k<maxn))
      { 
	word[k++] = getc(infile);
	if (word[k-1] == '\n') { ots_column = 0;  ots_line++; } else ots_column++;
      }
    if (k>=maxn) {printf("Error: Character buffer overflow detected.\n"); exit(0);}
    if (word[k-1]==';')
     { if (k==1) word[1] = '\0'; else { ungetc(word[k-1], infile); word[k-1] = '\0'; } return SEMICOLON; }
    else { word[k-1] = '\0';  return VALUE_LABEL; }
  } /*get_value_or_linelabel*/
}


void DisplayTaxInfo();



void Update_box_info()	/* Capture entries from form-boxes. */
{
 struct taxline_record *txline;
 struct value_list *tmppt;
 char text[100];

 txline = taxlines_hd;
 while (txline!=0)
  {
   tmppt = txline->values_hd;
   while (tmppt!=0)
    {
     if (tmppt->box != 0)
      {
       Otk_Get_Text( tmppt->box, text, 100 );	
       tmppt->kind = VKIND_TEXT;
       tmppt->text = strdup( text );
      }
     tmppt = tmppt->nxt;
    }
   txline = txline->nxt;
  }
}


void refresh()
{
 int pannum;

 /* Determine the current panel. */
 pannum = 0;
 while ((pannum < TabbedPanel->num) && (TabbedPanel->selection != TabbedPanel->selects[pannum].selection )) 
  pannum++;

 /* Remove and recreate the page-panels with the new line item. */
 Otk_RemoveObject( main_panel );
 main_panel =  OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 1, 7.5, 98, 87 );	/* Main Panel. */
 Otk_SetBorderThickness( main_panel, 0.25 );
 DisplayTaxInfo();

 /* Set to display the original page. */
 Otk_tabbed_panel_select( &(TabbedPanel->selects[pannum]) );
}


/*--------------------------------------------------------------*/
/* Add_New_Boxes - Callback for "+" button on form-boxes.	*/
/*  Adds new form-box(s) to the line item.			*/
/*--------------------------------------------------------------*/
void add_new_boxes( void *data, int num )
{
 struct taxline_record *txline;
 struct value_list *item, *lineitem, *newitem1, *newitem2, *oldtail;

 Update_box_info();

 item = (struct value_list *)data;
 oldtail = item->parent->values_tl;

 newitem1 = new_list_item_value( VKIND_TEXT, item->parent, "", 0, item->linenum + 1 );
 if (num>1)
  {
   new_list_item_value( VKIND_TEXT, item->parent, "", 0, item->linenum + 1 );
   new_list_item_value( VKIND_TEXT, item->parent, "", 0, item->linenum + 2 );
   newitem2 = new_list_item_value( VKIND_TEXT, item->parent, "", 0, item->linenum + 2 );
  }
 else newitem2 = newitem1;

 lineitem = item;	 /* Skip the items on the original line. */
 while ((lineitem->nxt!=newitem1) && (lineitem->nxt->linenum == item->linenum)) lineitem = lineitem->nxt;

 /* Insert in list and remove from tail, if not on end of list. */
 if (lineitem->nxt != newitem1)
  {
   newitem2->nxt = lineitem->nxt;
   lineitem->nxt = newitem1;
   oldtail->nxt = 0;  
   item->parent->values_tl = oldtail;   
  }

 /* Increment the effective file-line-number of all subsequent line entries. */
 lineitem = newitem2->nxt;
 while (lineitem!=0)		/* Now do remaining lines of this item. */
  {
   lineitem->linenum = lineitem->linenum + num;
   lineitem = lineitem->nxt;
  }
 txline = item->parent->nxt;	/* Next do remaining line items. */
 while (txline!=0)
  {
   txline->linenum = txline->linenum + num;
   lineitem = txline->values_hd;
   while (lineitem!=0)
    {
     lineitem->linenum = lineitem->linenum + num;
     lineitem = lineitem->nxt;
    }
   txline = txline->nxt;
  }

 refresh();
}


/*-------------------------------------------------------------------------*/
/* Add_new_capgain_boxes - Callback for "+" button on cap-gain form-boxes. */
/*  Adds a new form-boxes to the line item.				   */
/*-------------------------------------------------------------------------*/
void add_new_capgain_boxes( void *data )
{
 add_new_boxes( data, 2 ); 
}

void add_new_box_item( void *data )
{
 add_new_boxes( data, 1 );
}


void cancelpopup()
{ Otk_RemoveObject(popup); }

void acceptcomment( void *data )
{
 char comment[500];
 struct value_list *tmppt;

 tmppt = (struct value_list *)data;
 Otk_Get_Text( commentbox, comment, 500 );
 tmppt->comment = strdup(comment);
 cancelpopup();
 refresh();
}

void acceptcomment2( char *s, void *x ) { acceptcomment( x ); }

void edit_line_comment( void *data )
{
 char comment[1000];
 struct value_list *tmppt;

 tmppt = (struct value_list *)data;
 strcpy(comment,tmppt->comment);
 popup = OtkMakeWindow( Otk_Raised, Otk_Blue, OtkSetColor(0.8,0.8,0.8), 10.0, 10.0, 80.0, 25.0 );
 OtkMakeTextLabel( popup, "Edit Line Comment", Otk_Black, /*scale=*/ 1.5, /*weight=*/ 1, /*x=*/ 5, /*y=*/ 10 );
 commentbox = OtkMakeTextFormBox( popup, comment, 60,  8, 35, 90, 22, acceptcomment2, tmppt );
 OtkMakeButton( popup, 15, 75.0, 10, 14, " Ok ", acceptcomment, tmppt );	
 OtkMakeButton( popup, 80, 75.0, 10, 14, "Cancel", cancelpopup, 0 );	
}


/* Check if entry looks like a date. */
/* If so, return 1, else return 0.  */
int datecheck( char *word )
{
 int j, k=0;

 j = strlen(word) - 1;
 while (j>0)
  {
   if ((word[j]=='-') || (word[j]=='/')) k++;
   else if (word[j]>'9') return 0;
   j--;
  }
 if (k==2) return 1; else return 0;
}


#if (PLATFORM_KIND!=Posix_Platform) 
 char slashchr='\\';
#else
 char slashchr='/';
#endif


/***********************/
/* Read Tax Data File. */
/***********************/
void Read_Tax_File( char *fname )
{
 int j, k, kind, state=0, column=0, linenum=0, linecnt=0, lastline=0, newentry=0;
 char word[10000], *tmpstr, tmpstr2[100], tmpstr3[100];
 struct taxline_record *txline=0;
 struct value_list *tmppt, *newitem, *oldtail;

 /* Read the Tax Data Form File. */
 working_file = strdup(fname);
 taxlines_hd = 0;
 /* Accept the form's Title line.  (Must be first line!) */
 fgets(word, 200, infile);
 strcpy(title_line, word);
 j = strlen(word);
 if (j>0) word[j-1] = '\0';
 // printf("Title: '%s'\n", word);
 if (strstr(word,"Title:")==word) tmpstr = &(word[6]); else tmpstr = &(word[0]);
 k = strlen(tmpstr);	/* Pad to center if title is too short. */
 if (k<20)
  { for (j=0; j<(20-k)/2; j++) tmpstr2[k]=' '; tmpstr2[(20-k)/2] = '\0'; 
    strcpy(tmpstr3,tmpstr2); strcat(tmpstr3,tmpstr); strcpy(tmpstr,tmpstr3); strcat(tmpstr,tmpstr2);
  }
 Otk_Modify_Text( title_label, tmpstr );
 Otk_FitTextInPanel( title_label );

 kind = get_next_entry( word, 10000, &column, &linenum, infile );
 if (linenum > lastline) { lastline = linenum;  if (newentry) linecnt++;  newentry = 0; }
 while (!feof(infile))
  {
   if (column==1) state = 0;
   if (verbose) printf("%d: state=%d: col=%d: lnum=%d:  '%s'\n", kind, state, column, linenum, word);
   switch (kind)
    {
     case VALUE_LABEL: 
	 if (state==0) 
	  {
	   if (verbose) printf(" LineLabel:	%s\n", word);
	   state = 1;
	   txline = new_taxline( word, linecnt );
	  }
	 else
	  {
	   if (verbose) printf(" Value:	%s\n", word);
	   if (strcasecmp(txline->linename, "Status")==0)
	    {
	     new_list_item_value( VKIND_TEXT, txline, word, column, linecnt );
	     state = 0;
	    }
	   else
//	   if (datecheck(word))
	    {
	     new_list_item_value( VKIND_TEXT, txline, word, column, linecnt );
	    }
//	   else
//	    {
//	     if (sscanf(word, "%f",&x)!=1) printf("Error: reading '%s'\n",word);
//	     new_list_item_value( VKIND_FLOAT, txline, &x, column, linecnt );
//	    }
	  }
	newentry++;
	break;
     case COMMENT: if (verbose) printf(" Comment:	%s\n", word);
	if (txline==0) txline = new_taxline("", linecnt);
	new_list_item_value( VKIND_COMMENT, txline, word, column, linecnt );
	newentry++;
	break;
     case SEMICOLON: if (verbose) printf(" End:	%s\n", word);
	state = 0;
	new_list_item_value( VKIND_COLON, txline, word, column, linecnt );
	break;
    }
   column = column + strlen(word);
   kind = get_next_entry( word, 10000, &column, &linenum, infile );
   if (linenum > lastline) { lastline = linenum;  if (newentry) linecnt++;  newentry = 0; }
  }
 fclose(infile);

 /* Check for missing entries. */
 txline = taxlines_hd;
 while (txline!=0)
  {
   tmppt = txline->values_hd;  state = 0;
   while (tmppt!=0)
    {
     if ((tmppt->kind==VKIND_FLOAT) || (tmppt->kind==VKIND_TEXT) || (tmppt->kind==VKIND_INT)) state = 1;
     tmppt = tmppt->nxt;
    }
   if ((state==0) && (strlen(txline->linename)>0))	/* Place empty formbox on any line having no entries. */
    {
      oldtail = txline->values_tl;
      newitem = new_list_item_value( VKIND_TEXT, txline, "", 0, txline->linenum );
      if (newitem!=txline->values_hd)
       {
	newitem->nxt = txline->values_hd;
	txline->values_hd = newitem;
	txline->values_tl = oldtail;
	oldtail->nxt = 0;
       }
    }
   txline = txline->nxt;
  }

 DisplayTaxInfo();
}




void advance_panel( void *x )
{
 int pannum=0, direction;

 /* Determine the current panel. */
 while ((pannum < TabbedPanel->num) && (TabbedPanel->selection != TabbedPanel->selects[pannum].selection )) 
  pannum++;

 direction = (long)x;
 pannum = pannum + direction;
 if (pannum >= TabbedPanel->num) return;
 if (pannum < 0) return;

 /* Set to display the original page. */
 Otk_tabbed_panel_select( &(TabbedPanel->selects[pannum]) );
}



void check_comments()	/* Make sure every line has a comment field. */
{
 struct taxline_record *txline;
 struct value_list *tmppt, *npt, *tail;
 int ncomments;

 txline = taxlines_hd;
 while (txline!=0)
  {
   ncomments = 0;
   tmppt = txline->values_hd;
   while (tmppt!=0)
    {
     if (tmppt->kind==VKIND_COMMENT) ncomments++;
     if ((tmppt->nxt==0) || (tmppt->linenum != tmppt->nxt->linenum))
      {
       if (ncomments==0)
        {
         new_list_item_value( VKIND_COMMENT, txline, "", 50, tmppt->linenum);
	 if (tmppt->nxt != txline->values_tl)
	  {
	   npt = txline->values_tl;
	   tail = tmppt->nxt;
	   while (tail->nxt != npt) tail = tail->nxt;
	   tail->nxt = 0;
 	   txline->values_tl = tail;
	   npt->nxt = tmppt->nxt;
	   tmppt->nxt = npt;
	 }
        }
       ncomments = 0;
      }
     tmppt = tmppt->nxt;
    }
   txline = txline->nxt;
  }
}



void status_choice_S( void *x )
{ struct value_list *tmppt=(struct value_list *)x;
  Otk_Modify_Text( tmppt->box, "Single" );
}

void status_choice_MJ( void *x )
{ struct value_list *tmppt=(struct value_list *)x;
  Otk_Modify_Text( tmppt->box, "Married/Joint" );
}

void status_choice_MS( void *x )
{ struct value_list *tmppt=(struct value_list *)x;
  Otk_Modify_Text( tmppt->box, "Married/Sep" );
}

void status_choice_HH( void *x )
{ struct value_list *tmppt=(struct value_list *)x;
  Otk_Modify_Text( tmppt->box, "Head_of_Household" );
}

void status_choice_W( void *x )
{ struct value_list *tmppt=(struct value_list *)x;
  Otk_Modify_Text( tmppt->box, "Widow(er)" );
}


#define lines_per_page 10

/*************************************************************************/
/* Display the Tax Info - This routine constructs, lays-out and poulates */
/*  the panels.  Called after initial read-in and on updates.		 */
/*************************************************************************/
void DisplayTaxInfo()
{
 struct taxline_record *txline;
 struct value_list *tmppt;
 char **panelnames;
 OtkWidget pagept, label, button, cbutton;
 OtkTabbedPanel *Panels;
 char messg[500];
 int j, nlines=1, npanels, page, linecnt=0, nchars=14, linenum, pagenum;
 int lastline, olinecnt, olinenum, offset=0, tchars, capgtoggle;
 float pos_x, pos_y, twidth, theight, x, leftmargin=0.0;
 float box_x=11.0, box_width=18.0, box_height=5.0;

 check_comments();

 /* First count the number of lines to display. */
  txline = taxlines_hd;
  while (txline!=0)
   {
    if (linecnt < txline->linenum + offset) linecnt = txline->linenum + offset;
    linenum = linecnt % lines_per_page;
    olinecnt = linecnt;
    tmppt = txline->values_hd;
    while (tmppt!=0)
     {
      if (linecnt < tmppt->linenum + offset) linecnt = tmppt->linenum + offset;
      tmppt = tmppt->nxt;
     }

    /* Don't start a new multi-line entry at bottom of a page. */
    if ((linenum==lines_per_page-1) && (linecnt > olinecnt))
     {
      txline->format_offset = 1;
      offset++;
      linecnt++;
     }
    else txline->format_offset = 0;

    txline = txline->nxt;
   }
 nlines = linecnt;

 /* Now create enough tabbed-panels to hold all the lines. */
  npanels = nlines / lines_per_page + 1;
  panelnames = (char **)malloc( (npanels+1) * sizeof(char *));
  for (j=0; j<npanels; j++) {panelnames[j] = (char *)malloc(30); sprintf( panelnames[j], "Page %d", j+1); }
  Panels = Otk_Tabbed_Panel_New( main_panel, npanels, panelnames, Otk_LightGray, 1, 1, 98, 98, 5 );
  TabbedPanel = Panels;

 /* Now place the form-data onto the pages. */
 page = 0;  linecnt = 0;  pagept = Panels->panels[0];  offset = 0;
 OtkMakeTextLabel( pagept, panelnames[page], Otk_Black, 1.0, 1.0, 45.0, 2.0 );
 txline = taxlines_hd;
 while (txline!=0)
  {
   offset = offset + txline->format_offset;
   pagenum = (txline->linenum + offset) / lines_per_page;
   linenum = (txline->linenum + offset) % lines_per_page;
   if (page < pagenum)	/* Check to turn page. */
    { 
     page++;
     if (page>=npanels) {printf("Too many pages!\n"); exit(0);}
     pagept = Panels->panels[page];
     OtkMakeTextLabel( pagept, panelnames[page], Otk_Black, 1.0, 1.0, 45.0, 2.0 );
    }

   /* Place the line label. */
   pos_x = 2.0;
   pos_y = (float)linenum * 9.0 + 8.0;
   olinenum = linenum;
   // printf("%d: Label %s\n", linenum, txline->linename );
   label = OtkMakeTextLabel( pagept, txline->linename, Otk_Black, 1.0, 1.0, pos_x, pos_y );
   if (strlen(txline->linename)>8)  /* Squeeze size if too long. */
    {
     x = 7.8 / (float)strlen(txline->linename);
     Otk_Modify_Text_Aspect( label, x*sqrt(x) );
     Otk_Modify_Text_Scale( label, sqrt(sqrt(x)) );
    }

   tmppt = txline->values_hd;
   lastline = -1;  button = 0;  capgtoggle = 0;
   while (tmppt!=0)
    {
     pagenum = (tmppt->linenum + offset) / lines_per_page;
     linenum = (tmppt->linenum + offset) % lines_per_page;
     if (page < pagenum)	/* Check to turn page. */
      { 
       page++;
       if (page>=npanels) {printf("Too many pages!\n"); exit(0);}
       pagept = Panels->panels[page];
       OtkMakeTextLabel( pagept, panelnames[page], Otk_Black, 1.0, 1.0, 45.0, 2.0 );
      }
     if (lastline != linenum) leftmargin = box_x;

     switch (tmppt->kind)
      {
       case VKIND_FLOAT: 
		sprintf(messg, "%12.2f", tmppt->value ); 
                // printf("Formbox: '%s'\n", messg);
		pos_x = leftmargin;
		leftmargin = leftmargin + box_width + 1.5;
		pos_y = (float)linenum * 9.0 + 6.5;
		tmppt->box = 
		 OtkMakeTextFormBox( pagept, messg, nchars, pos_x, pos_y, box_width, box_height, 0, 0 );
		button = OtkMakeButton( pagept, pos_x+box_width, pos_y+4.0, 1.5, 1.5, "+", add_new_box_item, tmppt );   /* Add another box - button */
		Otk_Modify_Text_Scale( button->children, 0.8 );
		lastline = linenum;
		break;

       case VKIND_INT:  // printf(">>>Int: %d	", (int)(tmppt->value) ); 
		break;

       case VKIND_TEXT: // printf(">>>Text: '%s'\n", tmppt->text ); 
		pos_x = leftmargin;
		pos_y = (float)linenum * 9.0 + 6.5;
		twidth = box_width;
		tchars = nchars;
		if (lastline == linenum) 
		 { /* Cap-gains. */
		  twidth = 0.75 * box_width;  
		  tchars = 8;
		  pos_x = pos_x + 1.0;
		  if (button!=0) Otk_RemoveObject(button);  button = 0;
		  if (!capgtoggle) OtkMakeTextLabel( pagept, "Date Bought", Otk_Black, 0.75, 1.0, pos_x+1.0, pos_y-1.9 );
		  else OtkMakeTextLabel( pagept, "Date Sold", Otk_Black, 0.75, 1.0, pos_x+2.0, pos_y-1.9 );
		  capgtoggle = !capgtoggle;
		 }
		leftmargin = leftmargin + twidth + 1.5;
		tmppt->box = 
		 OtkMakeTextFormBox( pagept, tmppt->text, tchars, pos_x, pos_y, twidth, box_height, 0, 0 );
		if (capgtoggle==0)
		 {
		  if (strcmp(txline->linename,"Status")!=0)
		   {
		    if (lastline == linenum)
		     button = OtkMakeButton( pagept, pos_x+twidth, pos_y+4.0, 1.5, 1.5, "+", add_new_capgain_boxes, tmppt );   /* Add another box - button */
		    else
		     button = OtkMakeButton( pagept, pos_x+twidth, pos_y+4.0, 1.5, 1.5, "+", add_new_box_item, tmppt );   /* Add another box - button */
		    Otk_Modify_Text_Scale( button->children, 0.8 );
		   }
		  else
		   {
		    button = Otk_Make_Menu( pagept, pos_x+box_width, pos_y+4.0, 2.0, 1.5, "??" );
		    Otk_Add_Menu_Item( button, "Single", status_choice_S, tmppt);
		    Otk_Add_Menu_Item( button, "Married/Joint", status_choice_MJ, tmppt);
		    Otk_Add_Menu_Item( button, "Married/Sep", status_choice_MS, tmppt);
		    Otk_Add_Menu_Item( button, "Head_of_Household", status_choice_HH, tmppt);
		    Otk_Add_Menu_Item( button, "Widow(er)", status_choice_W, tmppt);
		   }
		 }
		lastline = linenum;
		break;

       case VKIND_COMMENT: // printf("%d: Comment {%s}\n", linenum, tmppt->comment ); 
		pos_x = leftmargin;
		pos_y = (float)linenum * 9.0 + 8.0;
		label = OtkMakeTextLabel( pagept, tmppt->comment, Otk_Black, 1.0, 1.0, pos_x, pos_y );
		Otk_Get_Text_Size( label, &twidth, &theight );
		if (twidth > (100.0-leftmargin-3.5))	/* Shrink width to fit on page if too long. */
		 {
		  x = (100.0-leftmargin-3.5)/twidth;
		  Otk_Modify_Text_Aspect( label, x * x );
		 }
		/* Add edit_line_comment button */
		cbutton = OtkMakeButton( pagept, 97.0, (float)linenum * 9.0 + 10.5, 1.4, 1.4, "*", edit_line_comment, tmppt );
	   	Otk_Modify_Text_Scale( cbutton->children, 0.9 );
		break;
      }
     tmppt = tmppt->nxt;
    }
   txline = txline->nxt;
  }

 /* Add the 'advance' and go-back' paging buttons to bottom of each page. */
 for (page=0; page<npanels; page++)
  {
   pagept = Panels->panels[page];
   if (page>0)
    {
     button = OtkMakeButton( pagept, 1.5, 96.0, 2.5, 2.5, "<", advance_panel, (void *)-1 );
     Otk_Modify_Text_Scale( button->children, 1.1 );
    }
   if (page<npanels-1)
    {
     button = OtkMakeButton( pagept, 96.0, 96.0, 2.5, 2.5, ">", advance_panel, (void *)1 );
     Otk_Modify_Text_Scale( button->children, 1.1 );
    }
  }
}




void dump_taxinfo()
{
 struct taxline_record *txline;
 struct value_list *tmppt;

 printf("\n======================================\n");
 txline = taxlines_hd;
 while (txline!=0)
  {
   printf("\n%d: %s\n", txline->linenum, txline->linename );
   tmppt = txline->values_hd;
   while (tmppt!=0)
    {
     switch (tmppt->kind)
      {
       case VKIND_FLOAT:   printf("%d,F: %6.2f\n", tmppt->linenum, tmppt->value ); break;
       case VKIND_INT:     printf("%d,I: %d\n", tmppt->linenum, (int)(tmppt->value) ); break;
       case VKIND_TEXT:    printf("%d,T: %s\n", tmppt->linenum, tmppt->text ); break;
       case VKIND_COMMENT: printf("%d,C: {%s}\n", tmppt->linenum, tmppt->comment ); break;
      }
     tmppt = tmppt->nxt;
    }
   txline = txline->nxt;
  }
 printf("\n");
}



void Save_Tax_File( char *filename )
{
 struct taxline_record *txline;
 struct value_list *tmppt;
 int lastline=-1, semicolon;
 FILE *outfile;

 /* Update the data structure(s) by getting the form fields. */
 Update_box_info();

  working_file = strdup(filename);
  outfile = fopen(filename,"w");
  if (outfile==0) 
  {
   printf("ERROR: Output file '%s' could not be opened for writing.\n", filename);
   save_taxfile();
   return;
  }
 working_file = strdup(filename);
 fprintf(outfile,"%s\n", title_line);
 txline = taxlines_hd;
 while (txline!=0)
  {
   fprintf(outfile,"\n%s", txline->linename );
   semicolon = 0;
   lastline = txline->linenum;
   tmppt = txline->values_hd;
   while (tmppt!=0)
    {
     if (tmppt->linenum != lastline)
      { fprintf(outfile,"\n");  lastline = tmppt->linenum; }
     switch (tmppt->kind)
      {
       case VKIND_FLOAT:   fprintf(outfile,"	%6.2f	", tmppt->value ); break;
       case VKIND_INT:     fprintf(outfile,"	%d	", (int)(tmppt->value) ); break;
       case VKIND_TEXT:    fprintf(outfile,"	%s	", tmppt->text ); break;
       case VKIND_COMMENT: if (strlen(tmppt->comment)>0) fprintf(outfile," {%s}", tmppt->comment ); break;
       case VKIND_COLON:   semicolon = 1; break;
      }
     tmppt = tmppt->nxt;
    }
   if (semicolon) fprintf(outfile,"\n		;");
   txline = txline->nxt;
  }

 fclose(outfile);
 printf("\nWrote form-data to file %s\n.", filename);
}



void read_file( char *filename )
{
 infile = fopen(filename,"r");
 if (infile==0) 
  {
   printf("ERROR: Input file '%s' could not be opened.\n", filename);
   open_taxfile();
  }
 else Read_Tax_File(filename);
} 

void open_taxfile()
{
 Otk_Browse_Files( "File to Open:", MaxFname, directory_dat, wildcards_dat, filename_dat, read_file );
}


void save_taxfile()
{
 Otk_Browse_Files( "File to Save:", MaxFname, directory_dat, wildcards_dat, filename_dat, Save_Tax_File );
}


void predict_output_filename(char *indatafile, char *outfname)
{
 int j;
 /* Base name of output file on input file. */
 strcpy(outfname,indatafile);
 j = strlen(outfname)-1;
 while ((j>=0) && (outfname[j]!='.')) j--;
 if (j<0) strcat(outfname,".out"); else strcpy(&(outfname[j]),".out");
}


void taxsolve();
char *taxsolvecmd=0, taxsolvestrng[MaxFname]="";

void settxslvcmd( char *filename )
{
 strcpy(taxsolvestrng,filename);
 taxsolve();
}

void findtscmd()
{
 Otk_RemoveObject(warningpopup);  
 Otk_Browse_Files( "Select TaxSolver:", MaxFname, directory_bin, wildcards_bin, taxsolvestrng, settxslvcmd);
}

void canceltxslvr()
{
 Otk_RemoveObject(warningpopup);  
}

void taxsolve()
{
 char cmd[MaxFname], outfname[MaxFname];

 if (working_file==0) 
  {
   printf("No tax file opened.\n");  
   warningpopup = OtkMakeWindow( Otk_Raised, Otk_Blue, OtkSetColor(0.8,0.8,0.8), 10.0, 10.0, 80.0, 30.0 );
   OtkMakeTextLabel( warningpopup, "No tax file selected.", Otk_Red, /*scale=*/ 1.8, /*weight=*/ 2, /*x=*/ 3, /*y=*/ 10 );
   OtkMakeTextLabel( warningpopup, "First select a tax file.", Otk_Red, /*scale=*/ 1.8, /*weight=*/ 2, /*x=*/ 10, /*y=*/ 25 );
   OtkMakeButton( warningpopup, 20, 48.0, 60, 15, "Select Tax File Now", open_taxfile, 0 );	
   OtkMakeButton( warningpopup, 42.5, 75.0, 15, 15, " Cancel ", canceltxslvr, 0 );	
   return;
  }
 if (strlen(taxsolvestrng)>0) taxsolvecmd = taxsolvestrng;
 if (taxsolvecmd==0) taxsolvecmd = getenv("taxsolvecmd");
 if (taxsolvecmd==0)
  {
   printf("The 'taxsolvecmd' environment variable is not set.\n");   
   warningpopup = OtkMakeWindow( Otk_Raised, Otk_Blue, OtkSetColor(0.8,0.8,0.8), 10.0, 10.0, 80.0, 30.0 );
   OtkMakeTextLabel( warningpopup, "The 'taxsolvecmd' environment", Otk_Red, /*scale=*/ 1.8, /*weight=*/ 2, /*x=*/ 3, /*y=*/ 10 );
   OtkMakeTextLabel( warningpopup, "variable is not set.", Otk_Red, /*scale=*/ 1.8, /*weight=*/ 2, /*x=*/ 10, /*y=*/ 25 );
   OtkMakeButton( warningpopup, 20, 48.0, 60, 15, "Select TaxSolver Now", findtscmd, 0 );	
   OtkMakeButton( warningpopup, 42.5, 75.0, 15, 15, " Cancel ", canceltxslvr, 0 );	
   return;   
  }
 if (PLATFORM_KIND==Posix_Platform)
  sprintf(cmd,"%s %s &", taxsolvecmd, working_file );
 else
  sprintf(cmd,"%s %s", taxsolvecmd, working_file );

 printf("Invoking '%s'\n", cmd );
 system(cmd);	/* Invoke the TaxSolver. */

 /* Make a popup message telling where the results are. */
 predict_output_filename(working_file, outfname);
 warningpopup = OtkMakeWindow( Otk_Raised, Otk_Blue, OtkSetColor(0.8,0.8,0.6), 10.0, 10.0, 80.0, 30.0 );
 OtkMakeTextLabel( warningpopup, "Results written to file:", Otk_Black, /*scale=*/ 1.8, /*weight=*/ 2, /*x=*/ 3, /*y=*/ 10 );
 OtkMakeTextLabel( warningpopup, outfname, Otk_Blue, /*scale=*/ 1.2, /*weight=*/ 1.2, /*x=*/ 5, /*y=*/ 35 );
 OtkMakeButton( warningpopup, 42.5, 75.0, 15, 15, " Dismiss ", canceltxslvr, 0 );	
}



OtkWidget printpopup=0, printerformbox, printresultstog;
char printer_command[MaxFname], wrkingfname[MaxFname];
#if (PLATFORM_KIND==POSIX_PLATFORM)
 char base_printer_command[]="lpr ";
#else
 char base_printer_command[]="print ";
#endif

void cancelprintpopup0()
{ printpopup = 0; }

void cancelprintpopup()
{ 
 Otk_RemoveObject(printpopup); 
 printpopup = 0;
}

void togprntcmd_in(void *x)
{ 
 sprintf(printer_command,"%s %s", base_printer_command, wrkingfname);
 Otk_Modify_Text( printerformbox, printer_command );
}

void togprntcmd_out(void *x)
{ char tmpstr[MaxFname];
  int k;
 predict_output_filename(wrkingfname,tmpstr);
 sprintf(printer_command,"%s %s", base_printer_command, tmpstr);
 Otk_Modify_Text( printerformbox, printer_command );
}

void acceptprinter_command( void *data )
{
 Otk_Get_Text( printerformbox, printer_command, MaxFname );
 printf("Issuing: %s\n", printer_command);
 system(printer_command);
 cancelprintpopup();
 refresh();
}

void acceptprinter_command2( char *s, void *x ) { acceptcomment( x ); }

void printout( void *data )
{
 if (printpopup!=0) return;
 printpopup = OtkMakeWindow( Otk_Raised, Otk_Blue, OtkSetColor(0.8,0.8,0.8), 10.0, 10.0, 80.0, 35.0 );
 OtkMakeTextLabel( printpopup, "Print Return Data:", Otk_Black, /*scale=*/ 1.9, /*weight=*/ 1, /*x=*/ 2, /*y=*/ 3 );

 OtkMakeTextLabel( printpopup, "Print Input Data", Otk_Black, /*scale=*/ 1.5, /*weight=*/ 1, /*x=*/ 20, /*y=*/ 20.0 );
 printresultstog = OtkMakeRadioButton( printpopup, 53.0, 20.0, 10.0, 6.0, togprntcmd_in, 0 );
 OtkMakeTextLabel( printpopup, "Print Output Results", Otk_Black, /*scale=*/ 1.5, /*weight=*/ 1, /*x=*/ 20, /*y=*/ 35.0 );
 OtkMakeRadioButton( printresultstog, 53.0, 35.0, 10.0, 6.0, togprntcmd_out, 0 );
 Otk_Add_BoundingBox( printpopup, Otk_Blue, 1.0, 18.0, 16.0, 63.0, 47.0 );

 if (working_file==0) strcpy(wrkingfname,filename_dat); else strcpy(wrkingfname,working_file);
 sprintf(printer_command,"%s %s", base_printer_command, wrkingfname);

 OtkMakeTextLabel( printpopup, "Print Command:", Otk_Black, /*scale=*/ 1.5, /*weight=*/ 1, /*x=*/ 4, /*y=*/ 57 );
 printerformbox = OtkMakeTextFormBox( printpopup, printer_command, 60,  28.5, 55, 68, 18, acceptprinter_command2, 0 );

 OtkMakeButton( printpopup, 10, 80.0, 22, 12, " Print It", acceptprinter_command, 0 );	
 OtkMakeButton( printpopup, 80, 81.0, 14, 11, " Cancel ", cancelprintpopup, 0 );	
 Otk_RegisterWindowKillEventFunction( printpopup, cancelprintpopup0, 0 );
}





void slcttxprog( void *data )
{
 char *strg=(char *)data, tmpstr[MaxFname];
 int k;

 printf("Selected '%s'\n", strg);
 if (strcmp(strg,"Other")==0)
  {
   Otk_Browse_Files( "Select TaxSolver:", MaxFname, directory_bin, wildcards_bin, taxsolvestrng, settxslvcmd);
   return;
  }
 else
  {
   sprintf(tmpstr,"%s%s", invocation_path, strg);
   printf("Setting Tax Program to be: '%s'\n", tmpstr);
   taxsolvecmd = strdup(tmpstr);
   strcpy(taxsolvestrng,tmpstr);

   strcpy(tmpstr,invocation_path);
   k = strlen(tmpstr)-1;
   while ((k>0) && (tmpstr[k]!=slashchr)) k--;
   if (k>0) k--;
   while ((k>0) && (tmpstr[k]!=slashchr)) k--;
   if (tmpstr[k]==slashchr)  tmpstr[k+1] = '\0';
   else  {sprintf(tmpstr,".%c", slashchr);}
   sprintf(directory_dat,"%sexamples_and_templates%c", tmpstr, slashchr);
   /* CA_540  MA_1  NC_400  NJ_1040  NY_IT201  OH_1040  PA_40  US_1040  US_1040_Sched_C  VA_760 */
   if (strstr(strg,"CA_540")) strcat(directory_dat,"CA_540"); else
   if (strstr(strg,"MA_1")) strcat(directory_dat,"MA_1"); else
   if (strstr(strg,"NC_D400")) strcat(directory_dat,"NC_400"); else
   if (strstr(strg,"NJ_1040")) strcat(directory_dat,"NJ_1040"); else
   if (strstr(strg,"NY_IT201")) strcat(directory_dat,"NY_IT201"); else
   if (strstr(strg,"OH_IT1040")) strcat(directory_dat,"OH_1040"); else
   if (strstr(strg,"PA_40")) strcat(directory_dat,"PA_40"); else
   if (strstr(strg,"US_1040_Sched_C")) strcat(directory_dat,"US_1040_Sched_C"); else
   if (strstr(strg,"US_1040")) strcat(directory_dat,"US_1040"); else
   if (strstr(strg,"VA_760")) strcat(directory_dat,"VA_760"); 
   printf("Setting Tax Data Directory to be: '%s'\n", directory_dat);
  }
}


void Ok2Quit()
{
 printf("OTS Exiting.\n");
 exit(0);
}

void QuitCallback( void *x )
{
 popup = OtkMakeWindow( Otk_Raised, Otk_Blue, OtkSetColor(0.8,0.8,0.8), 10.0, 10.0, 80.0, 25.0 );
 OtkMakeTextLabel( popup, "Ok to QUIT ?", Otk_Red, /*scale=*/ 3.0, /*weight=*/ 2, /*x=*/ 20, /*y=*/ 20 );
 OtkMakeButton( popup, 15, 70.0, 15, 18, " Ok Quit ", Ok2Quit, 0);	
 OtkMakeButton( popup, 75, 70.0, 15, 18, "Cancel", cancelpopup, 0 );	
}


/*----------------------------------------------------------------------------*/
/* Main -								      */
/*----------------------------------------------------------------------------*/

int main(int argc, char *argv[] )
{
 int argn, k;
 char vrsnmssg[50], tmpstr[MaxFname];
 OtkWidget subpanel, txprogstog;
 float x, y, dy, fontsz;
 FILE *testfile;

 /* Decode any command-line arguments. */
 argn = 1;  k=1;
 while (argn < argc)
 {
  if (strcmp(argv[argn],"-verbose")==0)  { verbose = 1;  argv[argn][0] = '\0'; }
  else
  if (k==1)
   {
    working_file = strdup(argv[argn]);
    infile = fopen(working_file,"r");
    if (infile==0) {printf("ERROR: Parameter file '%s' could not be opened.\n", argv[argn]); exit(1);}
    k = 2;
   }
  else
   {printf("Unknown command-line parameter '%s'\n", argv[argn]); /* exit(1); */ }
  argn = argn + 1;
 }
 sprintf(vrsnmssg,"OTS GUI v%1.2f", version);  printf("%s\n\n",vrsnmssg);
 invocation_path = strdup(argv[0]);
 k = strlen(invocation_path)-1;
 while ((k>0) && (invocation_path[k]!=slashchr)) k--;
 if (invocation_path[k]==slashchr) k++;
 invocation_path[k] = '\0';
 printf("Invocation path = '%s'\n", invocation_path);
 strcpy(directory_bin,invocation_path);

 OtkInitWindow( 650, 740, argc, argv );	 /* Open a window. */

 title_panel = 
 OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 1, 0.75, 98, 6 );	/* Title Panel. */
 Otk_SetBorderThickness( title_panel, 0.7 );
 title_label = OtkMakeTextLabel( title_panel, "Open-Tax-Solver", Otk_Blue, /*scale=*/ 3, /*weight=*/ 2, /*x=*/ 27, /*y=*/ 10 );

 main_panel = 
 OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 1, 7.5, 98, 87 );	/* Main Panel. */
 Otk_SetBorderThickness( main_panel, 0.25 );
 sprintf(tmpstr,"%s..%csrc%cGUI%cotslogo.ppm", invocation_path, slashchr, slashchr, slashchr );
 subpanel = OtkMakePanel( main_panel, Otk_Raised, Otk_LightGray, 20, 2, 60, 20 );
 testfile = fopen(tmpstr,"r");
 if (testfile!=0)
  { /* Only attempt to show image if image-file is where we expect to find it. */
   fclose(testfile);
   OtkMakeImagePanel( subpanel, tmpstr, 1.5, 5.0, 96, 88 );	/* Logo Image. */
  }

 taxsolvecmd = getenv("taxsolvecmd");
 if (taxsolvecmd==0)
  {
   OtkMakeTextLabel( main_panel, "Select Tax Program:", Otk_Black, /*scale=*/ 2, /*weight=*/ 1, /*x=*/ 3, /*y=*/ 27 );
   x = 8.0;  y = 33.5;  dy = 5.5;  fontsz = 1.4;
   txprogstog = OtkMakeRadioButton( main_panel, x, y, 6.0, 3.0, slcttxprog, "taxsolve_US_1040_2005" );
   OtkMakeTextLabel( main_panel, "US 1040 (w/Scheds A,B,D)", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, x+7.0, y+0.6 );
   slcttxprog("taxsolve_US_1040_2005");	 /* Set as default solver. */
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_US_1040_Sched_C_2005" );
   OtkMakeTextLabel( main_panel, "US 1040 Sched C", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_CA_540_2005" );
   OtkMakeTextLabel( main_panel, "CA State 540", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_MA_1_2005" );
   OtkMakeTextLabel( main_panel, "MA State 1", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_NC_D400_2005" );
   OtkMakeTextLabel( main_panel, "NC State DC400", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_NJ_1040_2005.c" );
   OtkMakeTextLabel( main_panel, "NJ State 1040", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   x = 60.0;  y = 33.5;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_NY_IT201_2005" );
   OtkMakeTextLabel( main_panel, "NY State IT201", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_OH_IT1040_2005" );
   OtkMakeTextLabel( main_panel, "OH State IT1040", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_PA_40_2005" );
   OtkMakeTextLabel( main_panel, "PA STATE 40", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "taxsolve_VA_760_2005" );
   OtkMakeTextLabel( main_panel, "VA State 760", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );
   y = y + dy;
   OtkMakeRadioButton( txprogstog, x, y, 6.0, 3.0, slcttxprog, "Other" );
   OtkMakeTextLabel( main_panel, "Other", Otk_Black, /*scale=*/ fontsz, /*weight=*/ 1, /*x=*/ x+7.0, y+0.6 );

   Otk_Add_BoundingBox( main_panel, Otk_Blue, 1.0, 6.0, 32.0, 95.0, 67.0 );
  }

 OtkMakeButton( OtkOuterWindow, 6, 95.3, 15, 4, "Save ", save_taxfile, 0 );	/* Save Button. */
 OtkMakeButton( OtkOuterWindow, 27, 95.3, 21, 4, "Compute Tax ", taxsolve, 0 );	/* TaxSolve Button. */
 OtkMakeButton( OtkOuterWindow, 55, 95.3, 15, 4, "Print", printout, 0 );	/* Print Button. */
 OtkMakeButton( OtkOuterWindow, 82, 95.3, 15, 4, "Exit", QuitCallback, 0 );	/* Exit Button. */

 if (infile==0) 
  {
   OtkMakeButton( main_panel, 10, 75, 80, 10, "Open a Tax File (or Template)",  open_taxfile, 0 );
   OtkMakeTextLabel( main_panel, vrsnmssg, Otk_Black, /*scale=*/ 1, /*weight=*/ 1, /*x=*/ 42, /*y=*/ 95 );
  }
 else
   Read_Tax_File(working_file);

 OtkMainLoop();
 return 0;
}
