/************************************************************
**
** Application: UnitConv
**
** Title:       c.unitconv
**
*************************************************************/

/*
*
* Copyright (c) 2015, Chris Johnson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/* Include files */
/* from standard clib */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>

/* from oslib: */
#include "OSLib:menu.h"
#include "OSLib:radiobutton.h"
#include "OSLib:writablefield.h"
#include "OSLib:button.h"
#include "OSLib:window.h"
#include "OSLib:displayfield.h"
#include "OSLib:messagetrans.h"
#include "OSLib:gadget.h"
#include "OSLib:toolbox.h"
#include "OSLib:wimp.h"

/* from oslib support */
#include "OSLibSupport:event.h"


/* from CJLib */
#include "CJLib:message.h"
#include "CJLib:etc.h"
#include "CJLib:tbox.h"

/* from unitconv */
#include "gases.h"
#include "imagesize.h"
#include "binhex.h"
#include "iconbar.h"
#include "main.h"
#include "msglink.h"
#include "cjoslib.h"
#include "unitconv.h"


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


#define UNITS_TEMPLATE        ((toolbox_id)"Units")
#define UNITS_WRITE_BASE_ID   ((toolbox_c)0x00)
#define UNITS_LABEL_BASE_ID   ((toolbox_c)0x10)
#define UNITS_RADIO_BASE_ID   ((toolbox_c)0x20)
#define UNITS_GROUP_TITLE     ((toolbox_c)0x126)

#define uevent_UNITS_SET_1    0x050
#define uevent_UNITS_SET_2    0x051
#define uevent_UNITS_SET_3    0x052
#define uevent_UNITS_SET_4    0x053
#define uevent_UNITS_SET_5    0x054
#define uevent_UNITS_SET_6    0x055
#define uevent_UNITS_SET_7    0x056
#define uevent_UNITS_SET_8    0x057
#define uevent_UNITS_SET_9    0x058
#define uevent_UNITS_ABOUT_TO_OPEN     0x05E
#define uevent_UNITS_ABOUT_TO_CLOSE    0x05F

        // unitconv menu
#define UNITSMENU_TEMPLATE    ((toolbox_id)"UnitsM")
#define uevent_UNITSMENU_SIGFIGS_4      0x060
#define uevent_UNITSMENU_SIGFIGS_5      0x061
#define uevent_UNITSMENU_SIGFIGS_6      0x062
#define uevent_UNITSMENU_ABOUT_TO_OPEN  0x63
#define uevent_UNITSMENU_ABOUT_TO_CLOSE 0x64
#define uevent_UNITSMENU_SHOW_BIN       0x65
#define uevent_UNITSMENU_SHOW_IMAGE     0x66
#define uevent_UNITSMENU_SHOW_GASES     0x67
#define uevent_UNITSMENU_SHOW_HELP      0x68

/* Local defines */
#define STATE_ON                1
#define STATE_OFF               0

#define C_TO_KELVIN   273.15
#define ZERO_C_TO_F   32.0
#define FACTOR_C_TO_F 1.80
#define FACTOR_F_TO_C 0.555555556

#define TEXTBUFSIZE 20

/* Flags for sig fig options - also cmp_ids in menu */
#define SIG_FIGS_4 0
#define SIG_FIGS_5 1
#define SIG_FIGS_6 2


/************************************************************/
/* global variables */

toolbox_o Unitconv_winid;
static wimp_w    Unitconv_winhandle;
static toolbox_o Groupmenu_id;
static toolbox_o Unitconv_menuid;
static unsigned int Unitconv_sigfig_flag = SIG_FIGS_4;
static char *Unitconv_sigfig_str [] = { "%.4g", "%.5g", "%.6g" };

static toolbox_position_tag unitconv_position = toolbox_POSITION_CENTRED;


/************************************************************/
/* forward function declarations */

static osbool Unitconv_MenuOpeningHandler ( bits event_code,
                                            toolbox_action *action,
                                            toolbox_block *id_block,
                                            void *handle );



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

static void UnitconvSetupConvMenu ( void )
{
  bits flags=0 ;
  conv_group *group_ptr ;
  int i ;
  toolbox_c new;

  menu_entry_object menuitem;
  char title [MAX_LEN_GROUPNAME + 4];

  /* set up menu entry */
  menuitem.flags=0;
  strcpy (title,"Group title");
  menuitem.text = title;
  menuitem.text_limit = MAX_LEN_GROUPNAME + 1;
  menuitem.click_object_name=0;
  menuitem.sub_menu_object_name=0;
  menuitem.sub_menu_action=0;
  menuitem.click_action=0;
  menuitem.help=0;
  menuitem.help_limit=0;

  /* do we need to increase items in group menu */
  if ( uc_data.no_of_groups > 2 )
  {
    for ( i=2; i < uc_data.no_of_groups; i++ )
    {
      /* set component id */
      menuitem.cmp = (toolbox_c)i;
      new = menu_add_entry (0, Groupmenu_id, -2, &menuitem );
    }
  }
  /* copy menu item text */
  for ( i = 0; i < uc_data.no_of_groups; i++ )
  {
    int len;
    char buf [MAX_LEN_GROUPNAME + 2];

    group_ptr = uc_data.group [ i ] ;
    /* ensure group name will fit into menu item */
    /* take a copy of group name - means original name is preserved */
    strcpy(buf, group_ptr->group_name);
    len = strlen(buf);
    if (len > MAX_LEN_GROUPNAME) buf[MAX_LEN_GROUPNAME] = '\0';
    menu_set_entry_text ( flags, Groupmenu_id, ( toolbox_c ) i, buf ) ;
  }
  return ;
}




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

void UnitconvSetupItems ( void )
{
  bits flags=0 ;
  conv_group *group_ptr ;
  int items ;
  int i ;
  char buffer [ MAX_LEN_UCWINTITLE + 5 ] ;
  int len ;
  toolbox_c gad, gad2 ;
  wimp_w win ;
  wimp_i *icons ;
  int size = 32 ;
  int written ;
  char temp[ 256 ] ;

  group_ptr = uc_data.group [ uc_data.current_group ] ;
  items = group_ptr->set[ uc_data.current_set ].no_of_items ;
  win = window_get_wimp_handle ( flags, Unitconv_winid ) ;
  icons = (wimp_i *) temp ;

  for ( i=0 ; i < items ; i++ )
  {
    gad = ( toolbox_c ) i + UNITS_WRITE_BASE_ID ;
    gad2 = ( toolbox_c ) i + UNITS_LABEL_BASE_ID ;
    /* undelete the icons */
    written = gadget_get_icon_list ( flags ,
                           Unitconv_winid,
                           gad ,
                           icons ,
                           size ) ;
    CJL_GadgetUnfade ( Unitconv_winid, gad ) ;
    wimp_set_icon_state ( win, icons[0] , 0, wimp_ICON_DELETED ) ;

    CJL_GadgetUnfade ( Unitconv_winid, gad2 ) ;
    button_set_value ( 0,
                       Unitconv_winid,
                       gad2,
                       group_ptr->set[ uc_data.current_set ].item [ i ].unit_name );
    writablefield_set_value ( 0,
                              Unitconv_winid,
                              gad,
                              "" ) ;
  }
  for ( i = items; i < MAX_UC_UNITITEMS; i++ )
  {
    gad2 = ( toolbox_c ) i + UNITS_LABEL_BASE_ID ;
    gad = ( toolbox_c ) i + UNITS_WRITE_BASE_ID ;
    button_set_value ( flags,
                       Unitconv_winid,
                       gad2,
                       "" ) ;
    writablefield_set_value ( flags,
                              Unitconv_winid,
                              gad,
                              "" ) ;

    /* mark as deleted the icons */
    written = gadget_get_icon_list ( flags ,
                           Unitconv_winid,
                           gad ,
                           icons ,
                           size ) ;

    CJL_GadgetFade ( Unitconv_winid,  gad ) ;
    wimp_set_icon_state ( win, icons[0] ,
                           wimp_ICON_DELETED, wimp_ICON_DELETED ) ;
    CJL_GadgetFade ( Unitconv_winid,  gad2 ) ;
  }
  /* now change window title */
  len = sprintf ( buffer, "Converting %s units",
            group_ptr->set[ uc_data.current_set ].set_name ) ;
  if ( len >= MAX_LEN_UCWINTITLE )
  {
    buffer [ MAX_LEN_UCWINTITLE ] = 0x00 ;
  }

  window_set_title ( flags,
                     Unitconv_winid,
                     buffer ) ;

  return ;
}



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

void UnitconvSetupWindow ( void )
{
  bits flags = 0 ;
  conv_group *group_ptr ;
  int i ;
  toolbox_c gad ;
  wimp_w win ;
  wimp_i *icons ;
  int size = 32 ;
  int written ;
  char temp [ 256 ] ;

  group_ptr = uc_data.group [ uc_data.current_group ] ;

  /* show group title in display field */
  displayfield_set_value ( flags, Unitconv_winid,
                           UNITS_GROUP_TITLE, group_ptr->group_name ) ;
  win = window_get_wimp_handle ( flags, Unitconv_winid ) ;
  icons = (wimp_i *) temp ;

  /* setup the radio icons */
  for ( i = 0 ; i < group_ptr->no_of_sets ; i++ )
  {
    gad = ( toolbox_c ) i + UNITS_RADIO_BASE_ID ;
    /* undelete the icons */
    written = gadget_get_icon_list ( flags, Unitconv_winid, gad, icons, size ) ;
    wimp_set_icon_state ( win, icons[0] , 0, wimp_ICON_DELETED ) ;
    wimp_set_icon_state ( win, icons[1] , 0, wimp_ICON_DELETED ) ;

    radiobutton_set_label ( flags, Unitconv_winid,
                            gad, group_ptr->set[i].set_name ) ;
  }

  /* clear the rest of the radio icons */
  for ( i = group_ptr->no_of_sets; i < MAX_UC_UNITSETS; i++ )
  {
    radiobutton_set_label ( flags, Unitconv_winid,
                            ( toolbox_c ) i + UNITS_RADIO_BASE_ID, "" ) ;


    gad = ( toolbox_c ) i + UNITS_RADIO_BASE_ID ;

    /* mark as deleted the icons */
    written = gadget_get_icon_list ( flags, Unitconv_winid, gad, icons, size ) ;

    wimp_set_icon_state ( win, icons[0], wimp_ICON_DELETED, wimp_ICON_DELETED ) ;
    wimp_set_icon_state ( win, icons[1], wimp_ICON_DELETED, wimp_ICON_DELETED ) ;

  }
  /* make sure first radiobutton is selected */
  radiobutton_set_state ( flags, Unitconv_winid, UNITS_RADIO_BASE_ID, STATE_ON ) ;

  /* write the unit titles and clear the values */
  UnitconvSetupItems ( ) ;
  return ;
}






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

static void UnitconvClearAll ( void )
{
  int i ;


  for ( i = 0; i < MAX_UC_UNITITEMS; i++ )
  {
    writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) i + UNITS_WRITE_BASE_ID,
                              "" ) ;
  }
  return ;
}





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

static void Unitconv_ConvGasmark ( double value)
{
  char text[12];
  int degF;
  double degC, degK;
  int i;

  if (!app.ch.show_gasmark) return;

  if ((value > 9) || (value < 0))
  {
    Msg_Warn ( GASMARK );
    /* ...and clear the other writables */
    for ( i = 0; i < 3; i++)
    {
      writablefield_set_value ( 0, Unitconv_winid, UNITS_WRITE_BASE_ID + i, "" );
    }
    return;
  }
  if (value >= 0.5)
  {
    degF = (int)value * 25 + 250;
  }
  else
  {
    if (value >= 0.25)
      degF = 225;
    else degF = 0;
  }
  if (degF == 0)
  {
    text[0] = '\0';
    for (i = 0; i < 3; i++)
    {
      writablefield_set_value ( 0, Unitconv_winid, UNITS_WRITE_BASE_ID + i, text );
    }
  }
  else
  {
    degC = (degF - ZERO_C_TO_F ) * FACTOR_F_TO_C ;
    degK = degC + C_TO_KELVIN ;
    sprintf ( text,"%d", (int)degK ) ;
    writablefield_set_value ( 0, Unitconv_winid, UNITS_WRITE_BASE_ID, text ) ;
    sprintf ( text,"%d", (int)degC ) ;
    writablefield_set_value ( 0, Unitconv_winid, UNITS_WRITE_BASE_ID + 1, text );
    sprintf ( text,"%d", degF ) ;
    writablefield_set_value ( 0, Unitconv_winid, UNITS_WRITE_BASE_ID + 2, text );
  }
  return;
}





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

static void Unitconv_DoGasmark ( double tempF )
{
  char text[12];


  if (!app.ch.show_gasmark) return;

  /* value passed is in fahrenheit */
  /* initialise text buffer to zero string */
  text[0] = '\0';

  /* now find the gas mark equivalent */
  if ((tempF < 225) || (tempF > 525))
  {
    /* out of range */
    strcpy(text,"n/a");
  }
  else
  {
    if (tempF >= 475) strcpy(text,"9");
    else
      if (tempF >= 450) strcpy(text,"8");
    else
      if (tempF >= 425) strcpy(text,"7");
    else
      if (tempF >= 400) strcpy(text,"6");
    else
      if (tempF >= 375) strcpy(text,"5");
    else
      if (tempF >= 350) strcpy(text,"4");
    else
      if (tempF >= 325) strcpy(text,"3");
    else
      if (tempF >= 300) strcpy(text,"2");
    else
      if (tempF >= 275) strcpy(text,"1");
    else
      if (tempF >= 250) strcpy(text,"1/2");
    else
      if (tempF >= 225) strcpy(text,"1/4");
  }
  writablefield_set_value ( 0,
                            Unitconv_winid,
                            (toolbox_c)3 + UNITS_WRITE_BASE_ID,
                            text ) ;
  return;
}





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

static void Unitconv_ConvertTemperatures ( int gadget_id , double value )
{
  char  text [ TEXTBUFSIZE ] ;
  int len ;
  double temp1, temp2 ;


  switch ( gadget_id )
  {
  case 0:
    /* kelvin value input */
    if ( value >= 0 )
    {
      temp1 = value - C_TO_KELVIN ;
      len = sprintf ( text, Unitconv_sigfig_str [Unitconv_sigfig_flag], temp1 ) ;
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) 1 + UNITS_WRITE_BASE_ID,
                              text ) ;
      temp2 = temp1 * FACTOR_C_TO_F + ZERO_C_TO_F ;
      len = sprintf ( text, Unitconv_sigfig_str [Unitconv_sigfig_flag], temp2 ) ;
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) 2 + UNITS_WRITE_BASE_ID,
                              text ) ;
      Unitconv_DoGasmark (temp2);
    }
    else
    {
      /* warning neg abs temp */
      Msg_Warn ( ABSTLOW );
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) 1 + UNITS_WRITE_BASE_ID,
                              "" ) ;
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) 2 + UNITS_WRITE_BASE_ID,
                              "" ) ;
    }
    break ;

  case 1:
    /* C value input */
    if ( value >= -C_TO_KELVIN )
    {
      temp1 = value + C_TO_KELVIN ;
      len = sprintf ( text, Unitconv_sigfig_str [Unitconv_sigfig_flag], temp1 ) ;
      writablefield_set_value ( 0, Unitconv_winid, UNITS_WRITE_BASE_ID, text ) ;
      temp2 = value * FACTOR_C_TO_F + ZERO_C_TO_F ;
      len = sprintf ( text, Unitconv_sigfig_str [Unitconv_sigfig_flag], temp2 ) ;
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) 2 + UNITS_WRITE_BASE_ID,
                              text ) ;
      Unitconv_DoGasmark (temp2);
    }
    else
    {
      /* warning neg abs temp */
      Msg_Warn ( CTLOW );
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              UNITS_WRITE_BASE_ID,
                              "" ) ;
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) 2 + UNITS_WRITE_BASE_ID,
                              "" ) ;
    }
    break ;

  case 2:
    /* F value input */
    temp1 = (value - ZERO_C_TO_F ) * FACTOR_F_TO_C ;
    if ( temp1 >= -C_TO_KELVIN )
    {
      temp2 = temp1 + C_TO_KELVIN ;
      len = sprintf ( text, Unitconv_sigfig_str [Unitconv_sigfig_flag], temp2 ) ;
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              UNITS_WRITE_BASE_ID,
                              text ) ;
      len = sprintf ( text, Unitconv_sigfig_str [Unitconv_sigfig_flag], temp1 ) ;
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) 1 + UNITS_WRITE_BASE_ID,
                              text ) ;
      Unitconv_DoGasmark (temp2);
    }
    else
    {
      /* warning neg abs temp */
      Msg_Warn ( FTLOW );
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              UNITS_WRITE_BASE_ID,
                              "" ) ;
      writablefield_set_value ( 0,
                              Unitconv_winid,
                              ( toolbox_c ) 1 + UNITS_WRITE_BASE_ID,
                              "" ) ;
    }
    break ;

  case 3:
    /* gas mark input */
    Unitconv_ConvGasmark (value);
    break;
  }
  return ;
}






/* Functions to process all the unit conversion data - if there is a problem
 * the function returns FALSE, which will invoke the hard coded defaults
 */



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

static void Unitconv_ProcessDatafileGetline ( unitconv_data_pointers * p )
{

  do
  {
    /* set s and e ptrs to new start value */
    p->end_of_line ++ ;
    p->start_of_line = p->end_of_line ;
    /* look for end of line char */
    while ( p->end_of_line [0] != '\n' )
    {
      p->end_of_line ++ ;
    }
  }
  while ( ((p->end_of_line - p->start_of_line) < 2) || (p->start_of_line[0] == '#') ) ;

  p->end_of_line[0] = 0x00 ;
  /* this should discard empty lines and commented lines */
  return ;
}



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

static void Unitconv_DiscardToken ( unitconv_data_pointers * p )
{
  p->start_of_line = strchr (p->start_of_line , ':');

  p->start_of_line ++ ;
  while (( p->start_of_line[0] == ' ' )  || ( p->start_of_line[0] == '\t' ))
  {
    p->start_of_line ++ ;
  }
  return ;
}


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

static void Unitconv_SplitAtComma ( unitconv_data_pointers * p )
{
  p->start2 = strchr (p->start_of_line, ',') ;

  p->start2 [0] = 0x00 ;
  p->start2 ++ ;
  /* discard leading spaces */
  while (( p->start2 [0] == ' ' ) || ( p->start2 [0] == '\t' ))
  {
    p->start2 ++ ;
  }
  return ;
}




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

osbool Unitconv_ProcessDatafile ( char *buffer, unitconv_data *uc_data )
{
  unitconv_data_pointers uc_ptrs ;
  int compare ;
  int groups ;
  int g ;
  int sets;
  int s ;
  int items ;
  int i;
  conv_group *ptr ;


  /* first check fileheader */
  uc_ptrs.start_of_line = buffer ;
  uc_ptrs.end_of_line = buffer ;
  while ( !iscntrl (uc_ptrs.end_of_line [0]) )
  {
    uc_ptrs.end_of_line ++ ;
  }
  uc_ptrs.end_of_line[0] = 0x00 ;
  if ( (compare = strncmp ( uc_ptrs.start_of_line,
                            "#UnitConv:version ", 18 )) != 0 )
  {
    /* header not recognised */
    Msg_Warn ( INVALHEAD ) ;
    return (FALSE) ;
  }
  if ( uc_ptrs.start_of_line [18] != '1' )
  {
    /* version too new */
    Msg_Warn ( NOTV1 ) ;
    return (FALSE) ;
  }
  Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
  if ( (compare = strncmp ( uc_ptrs.start_of_line,
                            "total_groups:", 13 )) != 0 )
  {
    /* string not recognised */
    Msg_Warn ( NOSTR ) ;
    return (FALSE) ;
  }
  groups = atoi ( uc_ptrs.start_of_line + 13 ) ;
  /* check number of groups is within allowable range */
  if ( groups > MAX_UC_MENU_ENTRIES )
  {
    /* when max number of groups have been processed, reading the units
     * data file will simply terminate
     */
    uc_data->no_of_groups = MAX_UC_MENU_ENTRIES;
  }
  else
  {
    uc_data->no_of_groups = groups;
  }

  /* now get the groups */
  for ( g=0 ; g < uc_data->no_of_groups ; g++ )
  {
    /* allocate memory for group data */
    ptr = ( conv_group * ) malloc ( sizeof ( struct conv_group ) ) ;
    if ( ptr == NULL )
    {
      /* flag error and return */
      Msg_Warn ( LOADNOMEM2 ) ;
      return (FALSE) ;
    }
    /* save ptr in the data struct */
    /* get group title */
    Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
    Unitconv_DiscardToken ( &uc_ptrs ) ;
    strcpy ( ptr->group_name, uc_ptrs.start_of_line ) ;
    /* number of sets */
    Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
    Unitconv_DiscardToken ( &uc_ptrs ) ;
    sets = atoi ( uc_ptrs.start_of_line ) ;
    /* check number of sets do not exceed permitted number */
    ptr->no_of_sets = ROOF ( sets, MAX_UC_UNITSETS );
    /* only process data up to permitted number of sets */
    for ( s=0 ; s < ptr->no_of_sets ; s++ )
    {
      Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
      compare = strcmp ( uc_ptrs.start_of_line, "group_item:" ) ;
      if (compare == 0 )
      {
        Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
        Unitconv_SplitAtComma ( &uc_ptrs ) ;
        strcpy ( ptr->set[s].set_name, uc_ptrs.start_of_line ) ;
        items = atoi ( uc_ptrs.start2 ) ;
        /* check items do not exceed permitted number */
        ptr->set[s].no_of_items = ROOF ( items, MAX_UC_UNITITEMS );
        /* only process data up to permitted number of items */
        for (i = 0 ; i < ptr->set[s].no_of_items ; i++ )
        {
          Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
          Unitconv_SplitAtComma ( &uc_ptrs ) ;
          strcpy ( ptr->set[s].item[i].unit_name, uc_ptrs.start_of_line ) ;
          strncpy ( ptr->set[s].item[i].factor_txt, uc_ptrs.start2, MAX_LEN_FACTOR - 1);
          ptr->set[s].item[i].factor_txt[MAX_LEN_FACTOR - 1] = 0x00;
          /* do we have a flag for a reciprocal relationship?
           * If so, step the pointer past it */
          if ( *uc_ptrs.start2 == '/' )
          {
            uc_ptrs.start2++ ;
          }
          ptr->set[s].item[i].factor = atof ( uc_ptrs.start2 ) ;
        }
        /* is there an excess number of items? */
        if ( items > ptr->set[s].no_of_items )
        {
          /* there are!!! - must discard them and try to carry on */
          for (i = ptr->set[s].no_of_items ; i < items ; i++ )
          {
            Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
          }
          CJL_MessageF (WARNING, "TOOMANYITEMS", NULL,
                           msg_data[TOOMANYITEMS].defmsg, ptr->set[s].set_name);
        }
      }
      else
      {
        /* Data file error - can't find group */
        Msg_Warn ( NOITEM ) ;
        /* release the memory for this group */
        free ( ptr ) ;
        return (FALSE) ;
      }
    }
    /* is there an excess number of sets? */
    if ( sets > ptr->no_of_sets )
    {
      /* there are!!! - must discard them and try to carry on */
      for ( s=ptr->no_of_sets ; s < sets ; s++ )
      {
        Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
        compare = strcmp ( uc_ptrs.start_of_line, "group_item:" ) ;
        if (compare == 0 )
        {
          Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
          Unitconv_SplitAtComma ( &uc_ptrs ) ;
          items = atoi ( uc_ptrs.start2 ) ;
          /* now discard the items with update of all the pointers */
          for (i=0; i<items; i++ )
          {
            Unitconv_ProcessDatafileGetline ( &uc_ptrs ) ;
          }
        }
        CJL_MessageF (WARNING, "TOOMANYSETS", NULL,
                           msg_data[TOOMANYSETS].defmsg, ptr->group_name);

      }

    }

    /* if we have reached here then the data for this group
       must be ok. Write the pointer */
    uc_data->group [g] = ptr ;

  }
  if (groups > uc_data->no_of_groups)
  {
    /* display error warning */
    Msg_Warn ( TOOMANYGROUP ) ;

  }
  return (TRUE);
}



/* Unitconv dialogue event handlers */




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

static double Unitconv_EvalQuotient ( char *tx, char *sl )
{
  double n, d, v = 0;
  char c;

  c = *sl;
  *sl = 0x00;
  n = atof (tx);
  d = atof (sl+1);
  if (d != 0)
  {
    v = n / d;
  }
  *sl = c;
  return ( v );
}



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

static void Unitconv_WritableFieldValueChanged ( char *text1, toolbox_c cmp_id)
{
  char  text2 [ TEXTBUFSIZE ] ;
  double value, absvalue ;
  conv_group *group_ptr ;
  int gadget_id ;
  osbool remove_minus = TRUE ;
  double temp1, temp2 ;
  char *ptr ;
  int i ;
  osbool zerodivide = FALSE;




  /* now check if there is any text */
  if ( strlen (text1) == 0 )
  {
    /* null string written - null all the strings */
    UnitconvClearAll ( ) ;
  }
  else
  {
    /* Do calculations and write new values */
    /* find value of entry text */
    /* check whether value has been entered as a quotient, eg 1/2 */
    ptr = strchr ( text1, '/');
    if (ptr)
    {
      /* Entered as a quotient */
      value = Unitconv_EvalQuotient ( text1, ptr );
    }
    else
    {
      /* normal decimal number */
      value = atof ( text1 ) ;
    }
    absvalue = fabs (value) ;
    group_ptr = uc_data.group [ uc_data.current_group ] ;
    if ( uc_data.current_group == ( uc_data.no_of_groups - 1 ))
    {
      /* must be special conversions */
      /* get gadget number */
      gadget_id = (int) ( cmp_id - UNITS_WRITE_BASE_ID ) ;
      switch ( uc_data.current_set )
      {
        case 0:
          /* temperature */
          Unitconv_ConvertTemperatures ( gadget_id , value ) ;
          /* temperatures can be negative */
          remove_minus = FALSE ;
          break ;
      }
    }
    else
    {
      /* normal conversions */
      /* Check if we are dealing with a reciprocal relationship */
      if (group_ptr->set[uc_data.current_set]
                .item[(int)(cmp_id - UNITS_WRITE_BASE_ID)].factor_txt[0] == '/' )
      {
        if ( absvalue == 0 )
        {
          /* set error flag */
          zerodivide = TRUE;
        }
        else
        {
          temp1 = group_ptr->set[uc_data.current_set]
                  .item[(int)(cmp_id - UNITS_WRITE_BASE_ID)].factor / absvalue ;
        }
      }
      else
      {
        temp1 = absvalue / group_ptr->set[uc_data.current_set]
                .item [(int)(cmp_id - UNITS_WRITE_BASE_ID)].factor ;
      }

      if (zerodivide)
      {
        /* blank all the other fields */
        for ( i = 0; i < group_ptr->set[ uc_data.current_set ].no_of_items; i++ )
        {
          /* We don't want to change the text in the input field */
          if ( ( i + UNITS_WRITE_BASE_ID) != (int) cmp_id )
          {
            writablefield_set_value ( 0,
                                      Unitconv_winid,
                                      ( toolbox_c ) i + UNITS_WRITE_BASE_ID,
                                      "" ) ;
          }
        }
      }
      else
      {
        for ( i = 0; i < group_ptr->set[ uc_data.current_set ].no_of_items; i++ )
        {
          /* We don't want to change the text in the input field */
          if ( ( i + UNITS_WRITE_BASE_ID) != (int) cmp_id )
          {
            if (group_ptr->set[uc_data.current_set]
                      .item[(int)(i + UNITS_WRITE_BASE_ID)].factor_txt[0] == '/')
            {
              if ( temp1 == 0 )
              {
                /* set error flag */
                zerodivide = TRUE;
              }
              else
              {
                temp2 = group_ptr->set[uc_data.current_set]
                        .item[(int)(i + UNITS_WRITE_BASE_ID)].factor / temp1 ;
              }
            }
            else
            {
              temp2 = temp1 * group_ptr->set[uc_data.current_set]
                      .item[(int)(i + UNITS_WRITE_BASE_ID)].factor ;
            }
            if ( zerodivide )
            {
              writablefield_set_value ( 0,
                                        Unitconv_winid,
                                        ( toolbox_c ) i + UNITS_WRITE_BASE_ID,
                                        "" ) ;
              /* reset flag for next pass */
              zerodivide = FALSE;
            }
            else
            {
              sprintf ( text2, Unitconv_sigfig_str [Unitconv_sigfig_flag], temp2 ) ;
              writablefield_set_value ( 0,
                                        Unitconv_winid,
                                        ( toolbox_c ) i + UNITS_WRITE_BASE_ID,
                                        text2 ) ;
            }

          }
        }
      }
    }
  }

  /* remove any leading minus if necessary */
  if ( remove_minus )
  {
      if ( text1 [0] == '-')
      {
        ptr = text1 + 1 ;
        writablefield_set_value ( 0, Unitconv_winid, cmp_id, ptr ) ;
      }
  }


  return;

}





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

static osbool Unitconv_KeyPress ( wimp_event_no event,
                                  wimp_block * blk,
                                  toolbox_block * id_block,
                                  void * handle )
{
  wimp_key *wkey;

  wkey = (wimp_key*)blk;
  if (wkey->c == wimp_KEY_F1)
    IconbarShowHelp();
  else
  if (( wkey->c < 32 ) || ( wkey->c > 126 ))
    wimp_process_key (wkey->c);

  return (TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (event);
}





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

static osbool Unitconv_MainHandler (bits event_code,
                                 toolbox_action *action,
                                 toolbox_block *id_block,
                                 void *handle )
{
  char *text;

  switch (event_code)
  {
    case action_WRITABLE_FIELD_VALUE_CHANGED:
      text = (char *)&action->data.reserved;
      Unitconv_WritableFieldValueChanged ( text, id_block->this_cmp);
      break;


    /* Setting the set of conversions after radio button click */
    case uevent_UNITS_SET_1:
    case uevent_UNITS_SET_2:
    case uevent_UNITS_SET_3:
    case uevent_UNITS_SET_4:
    case uevent_UNITS_SET_5:
    case uevent_UNITS_SET_6:
    case uevent_UNITS_SET_7:
    case uevent_UNITS_SET_8:
    case uevent_UNITS_SET_9:
      uc_data.current_set = (int)event_code - (int)uevent_UNITS_SET_1;
      UnitconvSetupItems ();
      break;

    case uevent_UNITS_ABOUT_TO_CLOSE:
      event_deregister_toolbox_handler ( Unitconv_winid,
                                   action_ANY,
                                   Unitconv_MainHandler,
                                   NULL);
      event_deregister_wimp_handler ( Unitconv_winid,
                                    wimp_KEY_PRESSED,
                                    Unitconv_KeyPress,
                                    NULL );
      event_deregister_toolbox_handler ( Unitconv_menuid,
                                     uevent_UNITSMENU_ABOUT_TO_OPEN,
                                     Unitconv_MenuOpeningHandler,
                                     NULL);
      toolbox_delete_object (0, Unitconv_menuid);
      Unitconv_menuid = toolbox_NULL_OBJECT;
      break;

  }

  return(TRUE);

  /* for compiler */
  IGNORE (handle);
}





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

static osbool Unitconv_GroupmenuSelect (bits event_code,
                                 toolbox_action *action,
                                 toolbox_block *id_block,
                                 void *handle )
{
  uc_data.current_group = id_block->this_cmp;
  uc_data.current_set = 0 ;
  UnitconvSetupWindow ();
  return(TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (action);
  IGNORE (event_code);
}




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

static osbool Unitconv_MenuMainHandler (bits event_code,
                                 toolbox_action *action,
                                 toolbox_block *id_block,
                                 void *handle )
{

  switch (event_code)
  {
    case uevent_UNITSMENU_SIGFIGS_4:
      /* In case adjust was used just untick current choice */
      menu_set_tick (0, Unitconv_menuid, Unitconv_sigfig_flag, FALSE);
      /* Set new flag value */
      Unitconv_sigfig_flag = SIG_FIGS_4;
      /* Now tick this choice */
      menu_set_tick (0, Unitconv_menuid, Unitconv_sigfig_flag, TRUE);
      break;

    case uevent_UNITSMENU_SIGFIGS_5:
      menu_set_tick (0, Unitconv_menuid, Unitconv_sigfig_flag, FALSE);
      Unitconv_sigfig_flag = SIG_FIGS_5;
      menu_set_tick (0, Unitconv_menuid, Unitconv_sigfig_flag, TRUE);
     break;

    case uevent_UNITSMENU_SIGFIGS_6:
      menu_set_tick (0, Unitconv_menuid, Unitconv_sigfig_flag, FALSE);
      Unitconv_sigfig_flag = SIG_FIGS_6;
      menu_set_tick (0, Unitconv_menuid, Unitconv_sigfig_flag, TRUE);
      break;

    case uevent_UNITSMENU_SHOW_BIN:
      Binhex_Open ();
      break;

    case uevent_UNITSMENU_SHOW_IMAGE:
      Imagesize_Open ();
      break;

    case uevent_UNITSMENU_SHOW_GASES:
      Gases_Open ();
      break;

    case uevent_UNITSMENU_SHOW_HELP:
      IconbarShowHelp ();
      break;

    case uevent_UNITSMENU_ABOUT_TO_CLOSE:
      event_deregister_toolbox_handler ( Unitconv_menuid,
                                   action_ANY,
                                   Unitconv_MenuMainHandler,
                                   NULL);
      break;
  }

  return(TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (action);
}





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

static osbool Unitconv_MenuOpeningHandler (bits event_code,
                                 toolbox_action *action,
                                 toolbox_block *id_block,
                                 void *handle )
{
  int i;


  event_register_toolbox_handler ( Unitconv_menuid,
                                   action_ANY,
                                   Unitconv_MenuMainHandler,
                                   NULL);
  /* Clear any tick and then tick the current sig fig item */
  for (i = 0; i < 3; i++)
  {
    menu_set_tick (0, Unitconv_menuid, i, FALSE);
  }
  menu_set_tick (0, Unitconv_menuid, Unitconv_sigfig_flag, TRUE);

  return(TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (action);
  IGNORE (event_code);
}





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

static osbool Unitconv_AboutToBeShown ( bits event_code,
                                        toolbox_action *action,
                                        toolbox_block *id_block,
                                        void *handle )
{
  event_register_toolbox_handler ( Unitconv_winid,
                                   action_ANY,
                                   Unitconv_MainHandler,
                                   NULL);
  event_register_wimp_handler ( Unitconv_winid,
                                wimp_KEY_PRESSED,
                                Unitconv_KeyPress,
                                NULL );
  /* Also initialise the attached menu */
  if (Unitconv_menuid == toolbox_NULL_OBJECT)
  {
    Unitconv_menuid = toolbox_create_object (0, UNITSMENU_TEMPLATE);
    event_register_toolbox_handler ( Unitconv_menuid,
                                     uevent_UNITSMENU_ABOUT_TO_OPEN,
                                     Unitconv_MenuOpeningHandler,
                                     NULL);
  }
  return(TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (action);
  IGNORE (event_code);
}




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

void Unitconv_TrashWindow (void)
{
  /* Deregister event handlers */
  event_deregister_toolbox_handler ( Unitconv_winid,
                                     uevent_UNITS_ABOUT_TO_OPEN,
                                     Unitconv_AboutToBeShown,
                                     NULL);
  event_deregister_toolbox_handler ( Groupmenu_id,
                                     action_MENU_SELECTION,
                                     Unitconv_GroupmenuSelect,
                                     NULL);

  toolbox_hide_object (0, Unitconv_winid);
  CJL_DeleteObject (&Unitconv_winid);
  CJL_DeleteObject (&Groupmenu_id);
  Unitconv_winhandle = 0;
  return;
}





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

void Unitconv_Open (void)
{
  toolbox_show_object ( 0,
                        Unitconv_winid,
                        unitconv_position,
                        0,
                        toolbox_NULL_OBJECT,
                        toolbox_NULL_COMPONENT ) ;
  /* Reset position after first open */
  if (unitconv_position == toolbox_POSITION_CENTRED)
    unitconv_position = toolbox_POSITION_DEFAULT;
  return;
}



/* Create the unit conv window and register the event handlers */

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

void Unitconv_Initialise (void)
{
  /* We create this window at the start, and it will remain in existence
  ** throughout the lifetime of the session, unless the custom definitions
  ** are edited and resaved. In that case the window and its dependencies
  ** are trashed, and then this function is called to recreate the window. */

  Unitconv_winid = toolbox_create_object (0, UNITS_TEMPLATE);
  Unitconv_winhandle = window_get_wimp_handle ( 0, Unitconv_winid );
  /* now register the event handlers */
  event_register_toolbox_handler ( Unitconv_winid,
                                   uevent_UNITS_ABOUT_TO_OPEN,
                                   Unitconv_AboutToBeShown,
                                   NULL );
  Groupmenu_id=toolbox_create_object ( 0, GROUPMENU_TEMPLATE ) ;
  event_register_toolbox_handler ( Groupmenu_id,
                                   action_MENU_SELECTION,
                                   Unitconv_GroupmenuSelect,
                                   NULL );

  UnitconvSetupConvMenu ();
  uc_data.current_group = 0 ;
  uc_data.current_set = 0 ;
  UnitconvSetupWindow ();
  return;
}


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





/*************  End of c.unitconv  ***********************/

