/*
    This file is part of the FElt finite element analysis package.
    Copyright (C) 1993 Jason I. Gobat and Darren C. Atkinson

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*****************************************************************************
* File:		text_form.c
*
* Description:	This file contains routines for the creation and 
*		management of text entry forms, i.e. a list of label,
*		text widget pairs for filling in a series of blanks
*		and okay/cancel buttons.
*
* Notes:	In several places I warp the pointer into the text entry
*		boxes.  There is no science to this!  So if you change
*		the font, figure it out yourself!
*
* History:	by Jason Gobat and Darren Atkinson
*
****************************************************************************/

# include <stdio.h>
# include <X11/Xlib.h>
# include <X11/Intrinsic.h>
# include <X11/StringDefs.h>
# include <X11/Shell.h>
# include <X11/Xaw/AsciiText.h>
# include <X11/Xaw/Dialog.h>
# include <X11/Xaw/Form.h>
# include <X11/Xaw/Label.h>
# include "forms.h"

# define max(x,y)	((x) > (y) ? (x) : (y))
# define min(x,y)	((x) < (y) ? (x) : (y))

# define FONTSIZE      14
# define STARTX_OFFSET 16
# define STARTY_OFFSET 40

double strtod ();

static void SetButtonState ();

void CheckBuffer     ();
void ClearBuffer     ();
void MovePointerUp   ();
void MovePointerDown ();

static unsigned selected;
 
static char text_translations [ ] = 
   "<Key>Return:     MovePointerDown()\n\
    <Key>Down:       MovePointerDown()\n\
    <Key>Up:	     MovePointerUp()\n\
    <Key>Tab:        MovePointerDown()\n\
    <Btn3Down>:      ClearBuffer()";

static char no_translations [ ] =
   "<Key>Return:     MovePointerDown()\n\
    <Key>Down:       MovePointerDown()\n\
    <Key>Up:	     MovePointerUp()\n\
    <Key>Tab:        MovePointerDown()\n\
    <Btn1Down>:	     no-op()\n\
    <Btn1Motion>:    no-op()\n\
    <Btn1Up>:        no-op()\n\
    <Btn2Down>:      no-op()\n\
    <Btn3Down>:      no-op()\n\
    <Btn3Motion>:    no-op()\n\
    <Btn3Up>:        no-op()";

static char numeric_translations [ ] = 
   "<Key>Return:     MovePointerDown()\n\
    <Key>Down:       MovePointerDown()\n\
    <Key>Up:	     MovePointerUp()\n\
    <Key>Tab:        MovePointerDown()\n\
    Shift<Key>Tab:   MovePointerUp()\n\
    <Btn3Down>:      ClearBuffer()\n\
    None <LeaveWindow>:    CheckBuffer()";

static XtActionsRec actiontable [ ] = {
   {"ClearBuffer",     ClearBuffer},
   {"CheckBuffer",     CheckBuffer},
   {"MovePointerDown", MovePointerDown},
   {"MovePointerUp",   MovePointerUp},
};

#define Okay   1
#define Cancel 2

static struct {
   String	name;
   unsigned	flag;
} buttons [ ] = {
   {"okay",	Okay},
   {"cancel",	Cancel},
};


TextEntryForm *CreateTextEntryForm (parent_w, entries)
   Widget	parent_w;
   EntryRecord	entries;
{
   unsigned		i;
   unsigned		longest;
   Arg			arglist[12];
   Cardinal		count;
   char			label_name[120],
			entry_name[120]; 
   TextEntryForm	*text_form;
   XtTranslations	textrans,
			numtrans,
			notrans;
   unsigned		width,
			max_width;
   XFontStruct		*font_struct;

   max_width = 0; 
   longest = 0;

   for (i = 0 ; i < entries.number ; i++) {
       width = strlen (entries.info [i].label);
       if (width > max_width) {
          max_width = width;
          longest = i;
       }
   }

   font_struct = XLoadQueryFont (XtDisplay (parent_w), 
                          "*lucida-bold-i-normal-sans-14*");

   if (!font_struct)   
      width = FONTSIZE*max_width*0.85;
   else
      width = XTextWidth (font_struct, entries.info[longest].label, 
                          max_width) + FONTSIZE;

   text_form = XtNew (TextEntryForm);

   XtSetArg (arglist [0], XtNallowShellResize, FALSE);

   text_form -> shellwidget = XtCreatePopupShell ("entryShell", 
                                transientShellWidgetClass, parent_w, 
                                arglist, 1);

   text_form -> formwidget = XtCreateManagedWidget ("entryForm", 
                                formWidgetClass, text_form -> shellwidget, 
                                NULL, 0);

   text_form -> labelwidget = (Widget *) XtMalloc 
                                           (sizeof(Widget)*entries.number);
   text_form -> entrywidget = (Widget *) XtMalloc 
                                           (sizeof(Widget)*entries.number);

   XtAppAddActions (XtWidgetToApplicationContext (text_form -> shellwidget),
		actiontable, XtNumber (actiontable));

   numtrans = XtParseTranslationTable (numeric_translations);
   textrans = XtParseTranslationTable (text_translations);
   notrans  = XtParseTranslationTable (no_translations);

   for (i = 0 ; i < entries.number ; i++) {
   
      sprintf (label_name,"%sLabel",entries.info [i].label);
      sprintf (entry_name,"%sEntry",entries.info [i].label);

      count = 0;
      XtSetArg (arglist [count], XtNlabel, entries.info [i].label); count++;
      XtSetArg (arglist [count], XtNborderWidth, 0); count++;
      XtSetArg (arglist [count], XtNheight, 22); count++;
      XtSetArg (arglist [count], XtNwidth, width); count++;
      XtSetArg (arglist [count], XtNjustify, XtJustifyLeft); count++;
      XtSetArg (arglist [count], XtNtop, XawChainTop); count++;
      XtSetArg (arglist [count], XtNbottom, XawChainTop); count++;
      XtSetArg (arglist [count], XtNleft, XawChainLeft); count++;
      XtSetArg (arglist [count], XtNright, XawChainLeft); count++;

      if (i > 0) {
         XtSetArg (arglist [count], XtNfromVert, 
                   text_form -> labelwidget [i-1]); count++;
      } 

      text_form -> labelwidget [i] = XtCreateManagedWidget (label_name, 
		        labelWidgetClass, text_form -> formwidget, 
                        arglist, count);

      count = 0;
      if (entries.info [i].type == READ_ONLY) {
         XtSetArg (arglist [count], XtNeditType, XawtextRead); count++; 
      } else {
         XtSetArg (arglist [count], XtNeditType, XawtextEdit); count++;
      }
      XtSetArg (arglist [count], XtNleft, XawChainLeft); count++;
      XtSetArg (arglist [count], XtNright, XawChainRight); count++;
      XtSetArg (arglist [count], XtNtop, XawChainTop); count++;
      XtSetArg (arglist [count], XtNbottom, XawChainTop); count++;
      XtSetArg (arglist [count], XtNfromHoriz, 
                text_form -> labelwidget [i]); count++;

      if (i > 0) {
         XtSetArg (arglist [count], XtNfromVert, 
                   text_form -> entrywidget [i-1]); count++;
      }

      text_form -> entrywidget [i] = XtCreateManagedWidget (entry_name, 
			asciiTextWidgetClass, text_form -> formwidget, 
                        arglist, count);

      if (entries.info [i].type == NUMERIC_ONLY)
         XtOverrideTranslations (text_form -> entrywidget [i], numtrans);
      else if (entries.info [i].type == TEXT_AND_NUMERIC)
         XtOverrideTranslations (text_form -> entrywidget [i], textrans);
      else if (entries.info [i].type == READ_ONLY) {
         XtOverrideTranslations (text_form -> entrywidget [i], notrans);
         XawTextDisplayCaret (text_form -> entrywidget [i], False);
      }
   }

   count = 0;
   XtSetArg (arglist [count], XtNlabel, ""); count++;
   XtSetArg (arglist [count], XtNborderWidth, 0); count++;
   XtSetArg (arglist [count], XtNtop, XawChainTop); count++;
   XtSetArg (arglist [count], XtNbottom, XawChainTop); count++;
   XtSetArg (arglist [count], XtNleft, XawChainLeft); count++;
   XtSetArg (arglist [count], XtNright, XawChainLeft); count++;
   XtSetArg (arglist [count], XtNfromVert, 
             text_form -> labelwidget [i-1]); count++;

   text_form -> buttonwidget = XtCreateManagedWidget ("buttons", 
                                 dialogWidgetClass, text_form -> formwidget, 
                                 arglist, count); 

   for (i = 0 ; i < XtNumber (buttons) ; i++)
      XawDialogAddButton (text_form -> buttonwidget, buttons [i].name, 
                             SetButtonState, &buttons [i].flag);

   text_form -> topwidget = parent_w;

   return text_form;
}

unsigned GetEntryFormValues (text_form, entries, result)
   TextEntryForm	*text_form;
   EntryRecord		entries;
   String		*result;
{
   unsigned 	i;
   Widget	shell;
   Arg		arglist[4];
   Cardinal	count;
   XEvent	event;
   Dimension    formwidth, formheight,
                topwidth, topheight,
                borderwidth;
   Position     formx,formy,
		topx,topy,
		startx,starty;

   shell = text_form -> shellwidget;

   XtRealizeWidget (shell);

   count = 0;
   XtSetArg(arglist[count], XtNx, &topx); count++;
   XtSetArg(arglist[count], XtNy, &topy); count++;
   XtSetArg(arglist[count], XtNwidth, &topwidth); count++;
   XtSetArg(arglist[count], XtNheight, &topheight); count++;
   XtGetValues(text_form -> topwidget, arglist, count);

   count = 0;
   XtSetArg(arglist[count], XtNwidth, &formwidth); count++;
   XtSetArg(arglist[count], XtNheight, &formheight); count++;
   XtSetArg(arglist[count], XtNborderWidth, &borderwidth); count++;
   XtGetValues(shell, arglist, count);

   formx = max(0, 
       min(topx + ((Position)topwidth - (Position)formwidth) / 2, 
           (Position)DisplayWidth(XtDisplay(shell), 
	    DefaultScreen(XtDisplay(shell))) -
            (Position)formwidth - 2 * (Position)borderwidth));
   formy = max(0, 
       min(topy + ((Position)topheight - (Position)formheight) / 2,
           (Position)DisplayHeight(XtDisplay(shell), 
	    DefaultScreen(XtDisplay(shell))) -
            (Position)formheight - 2 * (Position)borderwidth));

   count = 0;
   XtSetArg(arglist[count], XtNx, formx); count++;
   XtSetArg(arglist[count], XtNy, formy); count++;
   XtSetValues(shell, arglist, count);

   count = 0;
   XtSetArg(arglist[count], XtNx, &startx); count++;
   XtSetArg(arglist[count], XtNy, &starty); count++;
   XtGetValues(text_form -> entrywidget [0], arglist, count);

   XtPopup (shell, XtGrabExclusive);

   XWarpPointer(XtDisplay(shell), 
                XtWindow (text_form -> topwidget), 
                XtWindow(text_form -> formwidget),
                0, 0, topwidth, topheight, 
                startx + STARTX_OFFSET, starty + STARTY_OFFSET);

   selected = 0;
   while (!(selected & (Okay | Cancel))) {
      XtAppNextEvent (XtWidgetToApplicationContext (text_form -> shellwidget),
								&event);
      XtDispatchEvent (&event);
   }

   XtPopdown (shell); 

   if (selected == Okay) {
      for (i = 0 ; i < entries.number ; i++) {
         XtSetArg (arglist [0], XtNstring, &result [i]);
         XtGetValues (text_form -> entrywidget [i], arglist, 1);
      }
      return 1;
   }
   return 0; 
}

void FillTextBuffers (text_form, entries, suggestion)
   TextEntryForm	*text_form;
   EntryRecord		entries;
   String		*suggestion; 
{
   unsigned 		i;
   Arg			arglist [2];

   for (i = 0 ; i < entries.number ; i++) {
      XtSetArg (arglist [0], XtNstring, suggestion [i]);
      XtSetValues (text_form -> entrywidget [i], arglist, 1);
   }
}

static void SetButtonState (w, client_data, call_data)
   Widget 	w;
   XtPointer	client_data,
		call_data;
{
   selected = *(unsigned *) client_data;
}

void CheckBuffer (w) 
   Widget	w;
{
    
   Arg	    	arglist [1];
   String	result;
   int		length;
   double	result_value;
   char 	*ptr;

   XtSetArg (arglist [0], XtNstring, &result); 
   XtGetValues (w, arglist, 1);

   length = strlen (result);

   if (length > 0) {
      result_value = strtod (result, &ptr);
      if (*ptr != 0) {
         XBell (XtDisplay (w), 20);
         ClearBuffer (w); 
      }
   }
}

void ClearBuffer (w)
   Widget 	w;
{
   Arg	    	arglist [1];

   XtSetArg (arglist [0], XtNstring, "");
   XtSetValues (w, arglist, 1);
}
 
void MovePointerDown (w)
   Widget (w);
{
   Arg	    	arglist [4];
   Cardinal	count;
   Dimension	height,
		width;
   Position	x,
		y,
		newy,
		newx;

   count = 0;
   XtSetArg (arglist [count], XtNwidth, &width); count++;
   XtSetArg (arglist [count], XtNheight, &height); count++;
   XtSetArg (arglist [count], XtNx, &x); count++;
   XtSetArg (arglist [count], XtNy, &y); count++;
   XtGetValues (w, arglist, count);

   newy = y + 2*height - 2;
   newx = x + 4;
  
   XWarpPointer (XtDisplay (w), XtWindow (XtParent (w)), 
                 XtWindow (XtParent (w)), x, y, width, height, newx,newy);
}

void MovePointerUp (w)
   Widget (w);
{
   Arg	    	arglist [4];
   Cardinal	count;
   Dimension	height,
		width;
   Position	x,
		y,
		newy,
		newx;

   count = 0;
   XtSetArg (arglist [count], XtNheight, &height); count++;
   XtSetArg (arglist [count], XtNwidth, &width); count++;
   XtSetArg (arglist [count], XtNx, &x); count++;
   XtSetArg (arglist [count], XtNy, &y); count++;
   XtGetValues (w, arglist, count);

   newy = y - FONTSIZE - 2;
   newx = x + 4;
  
   XWarpPointer (XtDisplay (w), XtWindow (XtParent (w)), 
                 XtWindow (XtParent (w)), x, y, width, height, newx,newy);
}
