/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : Choices.c                              */
/*                                                 */
/* Purpose: Functions relating to the choices      */
/*          dialogue box and associated sub        */
/*          windows                                */
/*                                                 */
/* Author : D.T.A.Brown                            */
/*                                                 */
/* History: 23-Sep-97: Created.                    */
/***************************************************/

#include <stdlib.h>
#include <string.h>

#include "swis.h"

#include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */

#include "wimp.h"
#include "wimplib.h"
#include "event.h"

#include "toolbox.h"
#include "window.h"
#include "menu.h"
#include "ColourDbox.h"
#include "FontMenu.h"

#include "svcprint.h"
#include "Global.h"
#include "FromROSLib.h"
#include "Utils.h"

#include "Encoding.h"
#include "FetchPage.h"
#include "FontManage.h"
#include "History.h"
#include "Limits.h"
#include "Menus.h"
#include "NestWimp.h"
#include "Redraw.h"
#include "Reformat.h"
#include "Save.h"
#include "URLutils.h"
#include "Windows.h"

#include "Choices.h"

/* Local compile-time options */

#define CREATE_ONLY_ONCE

/* Locals */

static char consonant[]                = "bcdfghjklmnpqrstvwxyz";
static char vowel[]                    = "aeiou";
static int  rubbish_seed;

/* Used to prevent rounding errors when converting to/from font 16ths of a point to decimal */

static unsigned char todecimal  [] = {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9};
static unsigned char fromdecimal[] = {0, 2, 4, 5, 7, 8, 10, 12, 13, 15};

static ObjectId *    subwindows              = NULL;
static int           current_subwindow       = 0;
static ObjectId      colourdbox_id           = 0;

static int           save_ticked             = -1;
static int           disp_ticked             = -1;
static int           plug_ticked             = -1;
static int           time_ticked             = -1;
static int           bord_ticked             = -1;

static int           history_radio           = 0; /* 0 - page history, 1 - image history */

static int           expiry_age_greyed       = 0;
static int           max_size_greyed         = 0;
static int           image_expiry_age_greyed = 0;
static int           image_max_size_greyed   = 0;

static int           image_expiry_units      = -1;
static int           history_expiry_units    = -1;

static int           choices_modechanged     = 0;

static fm_typeface * new_typefaces           = NULL;

#ifdef CREATE_ONLY_ONCE

  static int         objects_created         = 0;

#endif

/* Static function prototypes */

static _kernel_oserror * choices_show_subwindow              (ObjectId choices_window, int subwindow);
static _kernel_oserror * choices_set_contents                (void);
static _kernel_oserror * choices_get_contents                (void);
static void              choices_set_expiry_age              (ObjectId window);
static void              choices_set_expiry_age_greyed       (ObjectId window, int state);
static void              choices_set_tables_greyed           (int state, ObjectId window);
static _kernel_oserror * choices_set_table_border_field      (ObjectId obj, ComponentId comp, int state);
static void              choices_set_fonts_greyed            (ObjectId window, int state);
static void              choices_set_max_size                (ObjectId window);
static void              choices_set_max_size_greyed         (ObjectId window, int state);
static void              choices_set_im_max_size             (ObjectId window);
static void              choices_set_im_max_size_greyed      (ObjectId window, int state);
int                      choices_clip_to_min_max             (int value, int min, int max);
static void              choices_set_im_expiry_age           (ObjectId window);
static void              choices_set_im_expiry_age_greyed    (ObjectId window, int state);
_kernel_oserror        * choices_set_timetype_tick           (int timetype);

_kernel_oserror        * choices_set_timetype_field          (int timetype, ComponentId comp);

static void              choices_modified_font               (char * orig, char * mod, char * buffer);

static int               choices_radio_click_handler         (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_set_button_handler          (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_cancel_button_handler       (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_save_button_handler         (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_encoding_button_handler     (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_colour_button_handler       (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_colour_closed_handler       (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_colour_selected_handler     (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_display_m_button_handler    (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_save_m_button_handler       (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_display_m_click_handler     (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_save_m_click_handler        (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_option_state_handler        (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_font_button_handler         (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_font_closed_handler         (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_font_selected_handler       (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_redraw_fakepage_handler     (int eventcode, WimpPollBlock * event, IdBlock * id_block, void * handle);
static int               choices_open_choice_window          (int eventcode, WimpPollBlock * event, IdBlock * id_block, void * handle);
static int               choices_plug_m_button_handler       (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_plug_m_click_handler        (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_timetype_m_button_handler   (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_timetype_m_click_handler    (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_bord_m_button_handler       (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_bord_m_click_handler        (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int               choices_history_radio_handler       (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);

static _kernel_oserror * choices_colour_set_component        (ObjectId window, ComponentId component, int colour);
static _kernel_oserror * choices_set_save_field              (ObjectId obj, ComponentId comp, int state);
static _kernel_oserror * choices_set_display_field           (ObjectId obj, ComponentId comp, int state);
static ObjectId          choices_find_component              (ComponentId component);
_kernel_oserror        * choices_get_menu_entry_text         (char * menuname, ComponentId compid, char ** tempstring);

static _kernel_oserror * choices_displayfield_set_value      (unsigned int flags, ObjectId window, ComponentId writable, char *text);
static _kernel_oserror * choices_writablefield_set_value     (unsigned int flags, ObjectId window, ComponentId writable, char *text);
static _kernel_oserror * choices_button_set_validation       (unsigned int flags, ObjectId window, ComponentId writable, char *text);
static _kernel_oserror * choices_numberrange_set_value       (unsigned int flags, ObjectId window, ComponentId writable, int value);

static int               choices_return_appropriate_timetype (int seconds);
static int               choices_seconds_to_typed_time       (int secs, int timetype);
static int               choices_typed_time_to_seconds       (int time, int timetype);
static int               choices_get_range_of_typed_time     (int time);

/*************************************************/
/* choices_show_subwindow()                      */
/*                                               */
/* Shows a choice subwindow in the main choices  */
/* window.                                     � */
/*                                               */
/* Parameters: the ObjectId of the main choices  */
/*             window                            */
/*                                               */
/*             the number of the subwindow to    */
/*             show.  Defined in choices.h       */
/*             Use CDSubNone to remove           */
/*             the current subwindow.            */
/*                                               */
/* Returns:    pointer to _kernel_oserror        */
/*************************************************/

static _kernel_oserror * choices_show_subwindow(ObjectId choices_window, int subwindow)
{
  _kernel_oserror           * e;
  WindowShowObjectBlock       show_block;
  WimpGetWindowStateBlock     state;
  WimpGetWindowOutlineBlock   outline;
  int                         window_handle;
  int                         vwidth, hheight;

  if (current_subwindow != CDSubNone && current_subwindow != subwindow)
  {
    /* remove the current subwindow */

    RetError(toolbox_hide_object(0, subwindows[current_subwindow]));
    current_subwindow = CDSubNone;
  }

  if (subwindow == CDSubNone || !(subwindows[subwindow]))
  {
    /* Have not been asked to open a new subwindow */

    return NULL;
  }

  RetError(window_get_wimp_handle(0, choices_window, &window_handle));
  state.window_handle = window_handle;
  RetError(wimp_get_window_state(&state));
  RetError(gadget_get_bbox(0, choices_window, CDPlaceHolder, &show_block.visible_area));
  windows_return_tool_sizes(NULL, &hheight, &vwidth);

  show_block.visible_area.xmin = coords_x_toscreen(show_block.visible_area.xmin, (WimpRedrawWindowBlock *) &state);
  show_block.visible_area.xmax = coords_x_toscreen(show_block.visible_area.xmax, (WimpRedrawWindowBlock *) &state);
  show_block.visible_area.ymin = coords_y_toscreen(show_block.visible_area.ymin, (WimpRedrawWindowBlock *) &state);
  show_block.visible_area.ymax = coords_y_toscreen(show_block.visible_area.ymax, (WimpRedrawWindowBlock *) &state);

  /* Adjustments for different types of window */

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_show_subwindow: Wimp flags = %x\n", state.flags);
  #endif

  RetError(window_get_wimp_handle(0, subwindows[subwindow], &state.window_handle));
  RetError(wimp_get_window_state(&state));

  if (state.flags & WimpWindow_VScroll) show_block.visible_area.xmax -= vwidth;
  else vwidth  = 0;

  if (state.flags & WimpWindow_HScroll) show_block.visible_area.ymin += hheight;
  else hheight = 0;

  show_block.xscroll = 0;
  show_block.yscroll = 0;
  show_block.behind  = -1;

  show_block.window_flags    = 0;
  show_block.alignment_flags = 0;

  /* Requires wimp handle just to be awkward */

  show_block.parent_window_handle = window_handle;

  RetError(toolbox_show_object(Toolbox_ShowObject_AsSubWindow,
                               subwindows[subwindow],
                               Toolbox_ShowObject_FullSpec,
                               &show_block,
                               choices_window,
                               -1));

  outline.window_handle = state.window_handle;
  wimp_get_window_outline(&outline);

  if (outline.outline.xmin < show_block.visible_area.xmin)
  {
    show_block.visible_area.xmin += (show_block.visible_area.xmin - outline.outline.xmin);
  }
  if (outline.outline.ymin < (show_block.visible_area.ymin - hheight))
  {
    show_block.visible_area.ymin += (show_block.visible_area.ymin - outline.outline.ymin);
  }
  if (outline.outline.xmax > (show_block.visible_area.xmax + vwidth))
  {
    show_block.visible_area.xmax -= (outline.outline.xmax - show_block.visible_area.xmax);
  }
  if (outline.outline.ymax > show_block.visible_area.ymax)
  {
    show_block.visible_area.ymax -= (outline.outline.ymax - show_block.visible_area.ymax);
  }

  e = toolbox_show_object(Toolbox_ShowObject_AsSubWindow,
                          subwindows[subwindow],
                          Toolbox_ShowObject_FullSpec,
                          &show_block,
                          choices_window,
                          -1);

  if (!e) current_subwindow = subwindow;

  return e;
}

/*************************************************/
/* choices_to_be_shown()                         */
/*                                               */
/* Called when the ECDToBeShown event is         */
/* generated.                                    */
/*                                               */
/* Creates all the subwindow dialogues and fills */
/* them in with appropriate values.              */
/* Registers all additional event handlers used  */
/* while the choices dialogue is visible.        */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int choices_to_be_shown(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror       *e;
  ObjectId              window;
  int                   subwindow_tobeshown;

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_to_be_shown: Called\n");
  #endif

  /* In multiuser builds, need to be logged in - otherwise, close */
  /* the dialogue!                                                */

  #ifndef SINGLE_USER

    if (!logged_in)
    {
      toolbox_hide_object(0, idb->self_id);
      return 1;
    }

  #endif

  if (choices_windowid) return 1;

  _swix(OS_ReadMonotonicTime, _OUT(0), &rubbish_seed);

  choices_windowid = idb->self_id;

  /* Allocate block for holding subwindows ObjectIds */

  if (!subwindows)
  {
    subwindows = malloc(sizeof(ObjectId)*CDNoSubwindows);

    /* If hide object doesn't work, things are really screwed */

    if (!subwindows)
    {
      ChkError(toolbox_hide_object(0, idb->self_id));
      return 1;
    }
  }

  if (!new_choices)
  {
    new_choices = malloc(sizeof(global_choices));

    if (!new_choices)
    {
      ChkError(toolbox_hide_object(0, idb->self_id));
      return 1;
    }
    else
    {
      memcpy(new_choices, &choices, sizeof(global_choices));
    }
  }

  if (!new_typefaces)
  {
    new_typefaces = calloc(sizeof(fm_typeface), 3);

    if (!new_typefaces)
    {
      free(new_choices);
      new_choices = NULL;
      free(subwindows);
      subwindows  = NULL;
    }
    else
    {
      fm_typeface * tfptr;

      tfptr = fm_find_typeface("serif");
      memcpy(&new_typefaces[0], tfptr, sizeof(fm_typeface));
      tfptr = fm_find_typeface("sans");
      memcpy(&new_typefaces[1], tfptr, sizeof(fm_typeface));
      tfptr = fm_find_typeface("fixed");
      memcpy(&new_typefaces[2], tfptr, sizeof(fm_typeface));
    }
  }

  /* Create all choices subwindows.  Set their ObjectIds to NULL if they cannot be created */

  /* Might be worth changing this to a loop in the future */

  #ifdef CREATE_ONLY_ONCE
  if (!objects_created)
  {
  #endif
    e = toolbox_create_object(0, "ChSub0", &subwindows[0]); if (e) subwindows[0] = NULL;
    e = toolbox_create_object(0, "ChSub1", &subwindows[1]); if (e) subwindows[1] = NULL;
    e = toolbox_create_object(0, "ChSub2", &subwindows[2]); if (e) subwindows[2] = NULL;
    e = toolbox_create_object(0, "ChSub3", &subwindows[3]); if (e) subwindows[3] = NULL;
    e = toolbox_create_object(0, "ChSub4", &subwindows[4]); if (e) subwindows[4] = NULL;
    e = toolbox_create_object(0, "ChSub5", &subwindows[5]); if (e) subwindows[5] = NULL;
    e = toolbox_create_object(0, "ChSub6", &subwindows[6]); if (e) subwindows[6] = NULL;
    e = toolbox_create_object(0, "ChSub7", &subwindows[7]); if (e) subwindows[7] = NULL;
    e = toolbox_create_object(0, "ChSub8", &subwindows[8]); if (e) subwindows[8] = NULL;
  #ifdef CREATE_ONLY_ONCE
  }
  #endif

  subwindow_tobeshown = current_subwindow;
  current_subwindow   = CDSubNone;

  ChkError(choices_show_subwindow(idb->self_id, subwindow_tobeshown));

  ChkError(radiobutton_set_state(0, idb->self_id, CDFirstSubRadio + subwindow_tobeshown, 1));

  ChkError(choices_set_contents());

  /* Register choices event handlers. Make sure any additions here */
  /* are matched in the deregistration section below, and add to   */
  /* choices_hidden.                                               */
  #ifdef CREATE_ONLY_ONCE
  e = NULL;

  if (!objects_created)
  {
  #endif
    e         = event_register_toolbox_handler(-1, ECDRG2,                    choices_radio_click_handler,       NULL);
    if (!e) e = event_register_toolbox_handler(-1, CDSet,                     choices_set_button_handler,        NULL);
    if (!e) e = event_register_toolbox_handler(-1, CDCancel,                  choices_cancel_button_handler,     NULL);
    if (!e) e = event_register_toolbox_handler(-1, CDSaveButton,              choices_save_button_handler,       NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_EncodingMenuBt,        choices_encoding_button_handler,   NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_ColourButton,          choices_colour_button_handler,     NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_HlDispBt,              choices_display_m_button_handler,  NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_HiDispBt,              choices_display_m_button_handler,  NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_HlSaveBt,              choices_save_m_button_handler,     NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_HiSaveBt,              choices_save_m_button_handler,     NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECDSaveMenuClick,          choices_save_m_click_handler,      NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECDDispMenuClick,          choices_display_m_click_handler,   NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECDHiRadioClick,           choices_history_radio_handler,     NULL);
    if (!e) e = event_register_toolbox_handler(-1, OptionButton_StateChanged, choices_option_state_handler,      NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_FontButton,            choices_font_button_handler,       NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_ObjPlugBt,             choices_plug_m_button_handler,     NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECDPlugMenuClick,          choices_plug_m_click_handler,      NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_HiAgeTypeBt,           choices_timetype_m_button_handler, NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECDTimeMenuClick,          choices_timetype_m_click_handler,  NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_TabInnerBordBt,        choices_bord_m_button_handler,     NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECD_TabOuterBordBt,        choices_bord_m_button_handler,     NULL);
    if (!e) e = event_register_toolbox_handler(-1, ECDBordMenuClick,          choices_bord_m_click_handler,      NULL);
    if ((window = choices_find_component(CD_FakePage)) != 0)
    {
      if (!e) e = event_register_wimp_handler(window, Wimp_ERedrawWindow, choices_redraw_fakepage_handler, NULL);
    }
    if (!e) e = event_register_wimp_handler(idb->self_id, Wimp_EOpenWindow, choices_open_choice_window, NULL);
  #ifdef CREATE_ONLY_ONCE
  }
  #endif

  /* Deal with errors */

  if (e)
  {
    event_deregister_toolbox_handler(-1, ECDRG2,                    choices_radio_click_handler,      NULL);
    event_deregister_toolbox_handler(-1, CDSet,                     choices_set_button_handler,       NULL);
    event_deregister_toolbox_handler(-1, CDCancel,                  choices_cancel_button_handler,    NULL);
    event_deregister_toolbox_handler(-1, CDSaveButton,              choices_save_button_handler,      NULL);
    event_deregister_toolbox_handler(-1, ECD_EncodingMenuBt,        choices_encoding_button_handler,  NULL);
    event_deregister_toolbox_handler(-1, ECD_ColourButton,          choices_colour_button_handler,    NULL);
    event_deregister_toolbox_handler(-1, ECD_HlDispBt,              choices_display_m_button_handler, NULL);
    event_deregister_toolbox_handler(-1, ECD_HiDispBt,              choices_display_m_button_handler, NULL);
    event_deregister_toolbox_handler(-1, ECD_HlSaveBt,              choices_save_m_button_handler,    NULL);
    event_deregister_toolbox_handler(-1, ECD_HiSaveBt,              choices_save_m_button_handler,    NULL);
    event_deregister_toolbox_handler(-1, ECDSaveMenuClick,          choices_save_m_click_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECDDispMenuClick,          choices_display_m_click_handler,  NULL);
    event_deregister_toolbox_handler(-1, ECDHiRadioClick,           choices_history_radio_handler,     NULL);
    event_deregister_toolbox_handler(-1, OptionButton_StateChanged, choices_option_state_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECD_FontButton,            choices_font_button_handler,      NULL);
    event_deregister_toolbox_handler(-1, ECD_ObjPlugBt,             choices_plug_m_button_handler,    NULL);
    event_deregister_toolbox_handler(-1, ECDPlugMenuClick,          choices_plug_m_click_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECD_HiAgeTypeBt,           choices_timetype_m_button_handler, NULL);
    event_deregister_toolbox_handler(-1, ECDTimeMenuClick,          choices_timetype_m_click_handler,  NULL);
    event_deregister_toolbox_handler(-1, ECD_TabInnerBordBt,        choices_bord_m_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECD_TabOuterBordBt,        choices_bord_m_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECDBordMenuClick,          choices_bord_m_click_handler,      NULL);

    if ((window = choices_find_component(CD_FakePage)) != 0)
    {
      event_deregister_wimp_handler(window, Wimp_ERedrawWindow, choices_redraw_fakepage_handler, NULL);
    }

    event_deregister_wimp_handler(idb->self_id, Wimp_EOpenWindow, choices_open_choice_window, NULL);

    ChkError(e);
  }

  #ifdef CREATE_ONLY_ONCE
  objects_created = 1;
  #endif

  return 1;
}

#ifndef CREATE_ONLY_ONCE
  /*************************************************/
  /* choices_delete_subwindows()                   */
  /*                                               */
  /* Deletes all subwindows, reports any and all   */
  /* errors occuring while deleting them.          */
  /* Frees the structure holding the subwindows.   */
  /*************************************************/

  static void choices_delete_subwindows(void)
  {
    _kernel_oserror *e;
    int count;

    for(count = 0; count < CDNoSubwindows; count++)
    {
      if (subwindows[count])
      {
        e = toolbox_delete_object(0, subwindows[count]);
        if (e) show_error_ret(e);
        subwindows[count] = 0;
      }
    }
    free(subwindows);
    subwindows = NULL;
  }
#endif

/*************************************************/
/* choices_hidden()                              */
/*                                               */
/* Called when the ECDHidden event is generated. */
/* Deletes all the subwindow dialogues.          */
/* Deregisters all additional event handlers     */
/* used while the choices dialogue is visible.   */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int choices_hidden(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror * e;
  #ifndef CREATE_ONLY_ONCE
    ObjectId          window;
  #endif
  int               temp;

  temp = current_subwindow;

  /* Close subwindow */

  e = choices_show_subwindow(idb->self_id, CDSubNone);
  if (e) show_error_ret(e);

  #ifndef CREATE_ONLY_ONCE
    /* Delete all subwindow objects */

    choices_delete_subwindows();

    free(new_choices);
    new_choices = NULL;
    free(new_typefaces);
    new_typefaces = NULL;

    /* Deregister choices event handlers */
    event_deregister_toolbox_handler(-1, ECDRG2,                    choices_radio_click_handler,       NULL);
    event_deregister_toolbox_handler(-1, CDSet,                     choices_set_button_handler,        NULL);
    event_deregister_toolbox_handler(-1, CDCancel,                  choices_cancel_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, CDSaveButton,              choices_save_button_handler,       NULL);
    event_deregister_toolbox_handler(-1, ECD_EncodingMenuBt,        choices_encoding_button_handler,   NULL);
    event_deregister_toolbox_handler(-1, ECD_ColourButton,          choices_colour_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECD_HlDispBt,              choices_display_m_button_handler,  NULL);
    event_deregister_toolbox_handler(-1, ECD_HiDispBt,              choices_display_m_button_handler,  NULL);
    event_deregister_toolbox_handler(-1, ECD_HlSaveBt,              choices_save_m_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECD_HiSaveBt,              choices_save_m_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECDSaveMenuClick,          choices_save_m_click_handler,      NULL);
    event_deregister_toolbox_handler(-1, ECDDispMenuClick,          choices_display_m_click_handler,   NULL);
    event_deregister_toolbox_handler(-1, ECDHiRadioClick,   �       choices_history_radio_handler,     NULL);
    event_deregister_toolbox_handler(-1, OptionButton_StateChanged, choices_option_state_handler,      NULL);
    event_deregister_toolbox_handler(-1, ECD_FontButton,            choices_font_button_handler,       NULL);
    event_deregister_toolbox_handler(-1, ECD_ObjPlugBt,             choices_plug_m_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECDPlugMenuClick,          choices_plug_m_click_handler,      NULL);
    event_deregister_toolbox_handler(-1, ECD_HiAgeTypeBt,           choices_timetype_m_button_handler, NULL);
    event_deregister_toolbox_handler(-1, ECDTimeMenuClick,          choices_timetype_m_click_handler,  NULL);
    event_deregister_toolbox_handler(-1, ECD_TabInnerBordBt,        choices_bord_m_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECD_TabOuterBordBt,        choices_bord_m_button_handler,     NULL);
    event_deregister_toolbox_handler(-1, ECDBordMenuClick,          choices_bord_m_click_handler,      NULL);

    if ((window = choices_find_component(CD_FakePage)) != 0)
    {
      event_deregister_wimp_handler(window, Wimp_ERedrawWindow, choices_redraw_fakepage_handler, NULL);
    }
    event_deregister_wimp_handler(idb->self_id, Wimp_EOpenWindow, choices_open_choice_window, NULL);
  #endif

  choices_windowid = 0;
  current_subwindow = temp;

  return 1;
}

/*************************************************/
/* choices_radio_click_handler()                 */
/*                                               */
/* Shows the appropriate subwindow when a radio  */
/* button in the choices dialogue is clicked on  */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

static int choices_radio_click_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  int window, state;

  radiobutton_get_state(0, idb->self_id, idb->self_component, &state, NULL);

  /* Is this a radiobutton selected event? */

  if (state)
  {
    window = idb->self_component - CDFirstSubRadio;
    if (window > CDNoSubwindows) window = CDSubNone;
    choices_show_subwindow(idb->self_id, window);
  }

  return 1;
}

/*************************************************/
/* choices_set_contents()                        */
/*                                               */
/* Sets the contents of all choices subwindows   */
/* to reflect the current state of the global    */
/* choices.                                      */
/*                                               */
/* Returns:    pointer to _kernel_oserror.       */
/*************************************************/

static _kernel_oserror * choices_set_contents(void)
{
  ObjectId          window;
  unsigned int      temp, frac;

  /* No error handling here, as we want to allow items to be missing */

  if ((window = choices_find_component(CD_Homepage))       != 0) choices_writablefield_set_value(0, window, CD_Homepage,       new_choices->home_page);
  if ((window = choices_find_component(CD_UnderlineLinks)) != 0) optionbutton_set_state(0,  window, CD_UnderlineLinks, new_choices->underline_links);
  if ((window = choices_find_component(CD_UseDocColours))  != 0) optionbutton_set_state(0,  window, CD_UseDocColours,  new_choices->use_source_cols);
  if ((window = choices_find_component(CD_ShowForeground)) != 0) optionbutton_set_state(0,  window, CD_ShowForeground, new_choices->show_foreground);
  if ((window = choices_find_component(CD_ShowBackground)) != 0) optionbutton_set_state(0,  window, CD_ShowBackground, new_choices->show_background);

  /* Get encoding name */
  choices_set_encoding_field();

  if ((window = choices_find_component(CD_URLBar))         != 0) optionbutton_set_state(0, window, CD_URLBar,     new_choices->url_bar);
  if ((window = choices_find_component(CD_StatusBar))      != 0) optionbutton_set_state(0, window, CD_StatusBar,  new_choices->status_bar);
  if ((window = choices_find_component(CD_ButtonBar))      != 0) optionbutton_set_state(0, window, CD_ButtonBar , new_choices->button_bar);
  if ((window = choices_find_component(CD_FullScreen))     != 0) optionbutton_set_state(0, window, CD_FullScreen, new_choices->full_screen);

  if ((window = choices_find_component(CD_BackColour))     != 0) choices_colour_set_component(window, CD_BackColour, new_choices->background_colour);
  if ((window = choices_find_component(CD_TextColour))     != 0) choices_colour_set_component(window, CD_TextColour, new_choices->text_colour);
  if ((window = choices_find_component(CD_LinkColour))     != 0) choices_colour_set_component(window, CD_LinkColour, new_choices->link_colour);
  if ((window = choices_find_component(CD_UsedColour))     != 0) choices_colour_set_component(window, CD_UsedColour, new_choices->used_colour);
  if ((window = choices_find_component(CD_FolwColour))     != 0) choices_colour_set_component(window, CD_FolwColour, new_choices->followed_colour);
  if ((window = choices_find_component(CD_SlctColour))     != 0) choices_colour_set_component(window, CD_SlctColour, new_choices->selected_colour);

  if ((window = choices_find_component(CD_HlAutoOpen))     != 0) choices_numberrange_set_value(0,  window, CD_HlAutoOpen,   new_choices->auto_open_delay);
  if ((window = choices_find_component(CD_HlAutoScroll))   != 0) choices_numberrange_set_value(0,  window, CD_HlAutoScroll, new_choices->auto_scroll_delay);
  if ((window = choices_find_component(CD_HlDispDisp))     != 0) choices_set_display_field(window, CD_HlDispDisp,   new_choices->hotlist_show);
  if ((window = choices_find_component(CD_HlSaveDisp))     != 0) choices_set_save_field(   window, CD_HlSaveDisp,   new_choices->save_hotlist);

  if ((window = choices_find_component(CD_HiExpiryAge))    != 0) choices_set_expiry_age(window);
  if ((window = choices_find_component(CD_HiMaxSize))      != 0) choices_set_max_size(window);
  if ((window = choices_find_component(CD_HiDispDisp))     != 0) choices_set_display_field(window, CD_HiDispDisp, new_choices->show_urls);
  if ((window = choices_find_component(CD_HiSaveDisp))     != 0) choices_set_save_field(   window, CD_HiSaveDisp, new_choices->save_history);
  if ((window = choices_find_component(CD_HiImExpiryAge))  != 0) choices_set_im_expiry_age(window);
  if ((window = choices_find_component(CD_HiImMaxSize))    != 0) choices_set_im_max_size(window);

  if (history_radio)
  {
    if ((window = choices_find_component(CD_HiRadImage))    != 0)
    radiobutton_set_state(0, window, CD_HiRadImage, 1);
  }
  else
  {
    if ((window = choices_find_component(CD_HiRadPage))    != 0)
    radiobutton_set_state(0, window, CD_HiRadPage, 1);
  }

  if ((window = choices_find_component(CD_ClientPull))     != 0) optionbutton_set_state(0, window, CD_ClientPull,    new_choices->client_pull);
  if ((window = choices_find_component(CD_NetscapeEmu))    != 0) optionbutton_set_state(0, window, CD_NetscapeEmu,   new_choices->clone);
  if ((window = choices_find_component(CD_FramesSupport))  != 0) optionbutton_set_state(0, window, CD_FramesSupport, new_choices->support_frames);

  if ((window = choices_find_component(CD_FontsSystem))    != 0)
  {
    optionbutton_set_state(0, window, CD_FontsSystem, new_choices->system_font);
    choices_set_fonts_greyed(window, new_choices->system_font);
    /*set_gadget_state(window, CD_FontsGroup1,  new_choices->system_font);*/
  }
  if ((window = choices_find_component(CD_FontsTF1Disp))   != 0) choices_displayfield_set_value(0, window, CD_FontsTF1Disp, new_typefaces[0].fontnames[0]);
  if ((window = choices_find_component(CD_FontsTF2Disp))   != 0) choices_displayfield_set_value(0, window, CD_FontsTF2Disp, new_typefaces[1].fontnames[0]);
  if ((window = choices_find_component(CD_FontsTF3Disp))   != 0) choices_displayfield_set_value(0, window, CD_FontsTF3Disp, new_typefaces[2].fontnames[0]);

  temp = new_choices->font_size;
  frac = (temp & 0xf);
  temp = ((temp >> 4) * 10) + todecimal[frac];

  if ((window = choices_find_component(CD_FontsSize))      != 0) choices_numberrange_set_value(0,  window, CD_FontsSize, temp);
  if ((window = choices_find_component(CD_FontsAspect))    != 0) choices_numberrange_set_value(0,  window, CD_FontsAspect, new_choices->tt_aspect);

  if ((window = choices_find_component(CD_ObjHandle))      != 0) optionbutton_set_state(0,  window, CD_ObjHandle, new_choices->support_object);
  if ((window = choices_find_component(CD_ObjPlugDisp))    != 0)
  {
    set_gadget_state(window, CD_ObjPlugLabl, !new_choices->support_object);
    set_gadget_state(window, CD_ObjPlugBt,   !new_choices->support_object);
    set_gadget_state(window, CD_ObjPlugDisp, !new_choices->support_object);
    choices_set_plugin_field();
  }

  if ((window = choices_find_component(CD_TabSupport))     != 0)
  {
    optionbutton_set_state(0,  window, CD_TabSupport, new_choices->support_tables);
    choices_set_tables_greyed(!new_choices->support_tables,window);
  }

  if ((window = choices_find_component(CD_TabInnerBordDisp)) != 0)
  {
    choices_set_table_border_field(window, CD_TabInnerBordDisp, new_choices->table_inner);
  }

  if ((window = choices_find_component(CD_TabOuterBordDisp)) != 0)
  {
    choices_set_table_border_field(window, CD_TabOuterBordDisp, new_choices->table_outer);
  }

  if ((window = choices_find_component(CD_NetUseProxy))     != 0) optionbutton_set_state(0,  window, CD_NetUseProxy, new_choices->use_proxy);
  if ((window = choices_find_component(CD_NetProxyAddr))    != 0)
  {
    set_gadget_state(window, CD_NetProxyAddr, !new_choices->use_proxy);
    set_gadget_state(window, CD_NetProxyLabl, !new_choices->use_proxy);
    choices_writablefield_set_value(0, window, CD_NetProxyAddr, new_choices->proxy_address);
  }
  if ((window = choices_find_component(CD_NetLaunchProxy))   != 0) optionbutton_set_state(0,  window, CD_NetLaunchProxy, new_choices->start_proxy);
  if ((window = choices_find_component(CD_NetMaxImageFetch)) != 0) choices_numberrange_set_value(0, window, CD_NetMaxImageFetch, new_choices->maximages);

  return NULL;
}

/*************************************************/
/* choices_get_contents()                        */
/*                                               */
/* Sets the state of the global choices to       */
/* reflect the contents of all the choices       */
/* subwindows.                                   */
/*************************************************/

static _kernel_oserror * choices_get_contents(void)
{
  _kernel_oserror         * e;
  WimpGetWindowStateBlock   s;
  browser_data            * b;
  ObjectId                  window;
  char                    * tempstring;
  int                       reqsize;
  fm_typeface             * tfptr;
  int                       serif_changed   = 0;
  int                       sans_changed    = 0;
  int                       fixed_changed   = 0;
  int                       needs_redraw    = 0;
  int                       colours_changed = 0;
  int                       old_font_size, old_system_font, frac;
  int                       temp;

  /* The lack of error checking is to allow */
  /* items to be missing from the dialogue. */

  if ((window = choices_find_component(CD_Homepage)) != 0)
  {
    e = writablefield_get_value(0, window, CD_Homepage, NULL, 0, &reqsize);

    if (!e)
    {
      tempstring = malloc(reqsize + 1);

      if (tempstring)
      {
        e = writablefield_get_value(0, window, CD_Homepage, tempstring, reqsize, NULL);

        if (!e)
        {
          if (!strcmp(tempstring, new_choices->home_page))
          {
            free(tempstring);
          }
          else
          {
            free(new_choices->home_page);
            new_choices->home_page = tempstring;
          }
        }
      }
      else
      {
        show_error_ret(make_no_memory_error(17));
      }
    }
  }

  if ((window = choices_find_component(CD_HlAutoOpen))     != 0) numberrange_get_value(0, window, CD_HlAutoOpen,   &new_choices->auto_open_delay);
  if ((window = choices_find_component(CD_HlAutoScroll))   != 0) numberrange_get_value(0, window, CD_HlAutoScroll, &new_choices->auto_scroll_delay);

  if (expiry_age_greyed)
  {
    if (history_radio) new_choices->image_expiry_age = 0;
    else               new_choices->expiry_age = 0;
  }
  else
  {
    if ((window = choices_find_component(CD_HiExpiryAge))    != 0)
    {
      numberrange_get_value(0, window, CD_HiExpiryAge,  &temp);
      if (history_radio) new_choices->image_expiry_age = choices_typed_time_to_seconds(temp, history_expiry_units);
      else               new_choices->expiry_age = choices_typed_time_to_seconds(temp, history_expiry_units);
    }
  }

  if (max_size_greyed)
  {
    if (history_radio) new_choices->image_max_size = 0;
    else               new_choices->max_size = 0;
  }
  else
  {
    if ((window = choices_find_component(CD_HiMaxSize))      != 0)
    {
      numberrange_get_value(0, window, CD_HiMaxSize,    &temp);
      if (history_radio) new_choices->image_max_size = temp * 1024;
      else               new_choices->max_size = temp * 1024;
    }
  }

  if (image_expiry_age_greyed)
  {
    new_choices->image_expiry_age = 0;
  }
  else
  {
    if ((window = choices_find_component(CD_HiImExpiryAge))    != 0)
    {
      numberrange_get_value(0, window, CD_HiImExpiryAge,  &temp);
      new_choices->image_expiry_age = choices_typed_time_to_seconds(temp, image_expiry_units);
    }
  }

  if (image_max_size_greyed)
  {
    new_choices->image_max_size = 0;
  }
  else
  {
    if ((window = choices_find_component(CD_HiImMaxSize))      != 0)
    {
      numberrange_get_value(0, window, CD_HiImMaxSize,    &temp);
      new_choices->image_max_size = temp * 1024;
    }
  }


  if ((window = choices_find_component(CD_NetProxyAddr)) != 0)
  {
    e = writablefield_get_value(0, window, CD_NetProxyAddr, NULL, 0, &reqsize);

    if (!e)
    {
      tempstring = malloc(reqsize + 1);

      if (tempstring)
      {
        e = writablefield_get_value(0, window, CD_NetProxyAddr, tempstring, reqsize, NULL);

        if (!e)
        {
          if (!strcmp(tempstring, new_choices->proxy_address))
          {
            free(tempstring);
          }
          else
          {
            free(new_choices->proxy_address);
            new_choices->proxy_address = tempstring;
          }
        }
      }
      else
      {
        show_error_ret(make_no_memory_error(17));
      }
    }
  }

  if ((window = choices_find_component(CD_NetMaxImageFetch)) != 0)
  {
    numberrange_get_value(0, window, CD_NetMaxImageFetch,  &temp);
    new_choices->maximages = temp;
  }

  old_font_size = choices.font_size;
  old_system_font = choices.system_font;
  if ((window = choices_find_component(CD_FontsSize)) != 0)
  {
    numberrange_get_value(0, window, CD_FontsSize, &new_choices->font_size);
    frac = new_choices->font_size % 10;
    new_choices->font_size = ((new_choices->font_size / 10)<<4) + fromdecimal[frac];
  }

  if ((window = choices_find_component(CD_FontsAspect)) != 0)
  {
    numberrange_get_value(0, window, CD_FontsAspect, &new_choices->tt_aspect);
    if (choices.tt_aspect != new_choices->tt_aspect) fixed_changed = 1;
  }

  /* Have the default document colours changed? */

  if (
      choices.background_colour != new_choices->background_colour ||
      choices.text_colour       != new_choices->text_colour       ||
      choices.link_colour       != new_choices->link_colour       ||
      choices.used_colour       != new_choices->used_colour       ||
      choices.followed_colour   != new_choices->followed_colour   ||
      choices.selected_colour   != new_choices->selected_colour
     )
  {
    colours_changed = 1;
  }

  /* Do we need to redraw because certain special items have changed? */

  if (
       choices.table_inner != new_choices->table_inner ||
       choices.table_outer != new_choices->table_outer
     )
     needs_redraw = 1;

  /* Now start using the dialogue's new settings */

  memcpy(&choices, new_choices, sizeof(global_choices));

  /* Before we change fonts, we need to forget the basic */
  /* typeface definitions. First find out which have     */
  /* changed, if any.                                    */

  tfptr = fm_find_typeface("serif");

  if (tfptr)
  {
    if (memcmp(tfptr, &new_typefaces[0], sizeof(fm_typeface))) serif_changed = 1;
  }

  tfptr = fm_find_typeface("sans");

  if (tfptr)
  {
    if (memcmp(tfptr, &new_typefaces[1], sizeof(fm_typeface))) sans_changed = 1;
  }

  tfptr = fm_find_typeface("fixed");

  if (tfptr)
  {
    if (memcmp(tfptr, &new_typefaces[2], sizeof(fm_typeface))) fixed_changed = 1;
  }

  if (choices.system_font) choices.font_size = FM_Standard_Size;

  if (choices.font_size != old_font_size || choices.system_font != old_system_font)
  {
    serif_changed = 1;
    sans_changed  = 1;
    fixed_changed = 1;
    fm_set_system_font(choices.system_font);
    fm_init_sizes(choices.font_size);
  }

  /* If fonts have changed, get ready to update them */

  if (serif_changed || sans_changed || fixed_changed)
  {
    /* Dump *all* current fonts */

    fm_shutdown();

    /* Write the new typeface details into the existing structures */

    tfptr = fm_find_typeface("serif");
    if (tfptr) memcpy(tfptr, &new_typefaces[0], sizeof(fm_typeface));

    tfptr = fm_find_typeface("sans");
    if (tfptr) memcpy(tfptr, &new_typefaces[1], sizeof(fm_typeface));

    tfptr = fm_find_typeface("fixed");
    if (tfptr) memcpy(tfptr, &new_typefaces[2], sizeof(fm_typeface));
  }

  /* Update browsers - *must* call fm_lose_fonts for all */
  /* browsers if the above code called fm_shutdown.      */

  b = last_browser;

  while (b)
  {
    if (serif_changed || sans_changed || fixed_changed)
    {
      /* Get rid of the currently used fonts for this browser */

      fm_lose_fonts(b);

      /* Only reformat page if it has no children */

      if (!b->children) reformat_format_from(b, -1, 1, -1);
    }
    else
    {
      if (b->background_colour == -1)
      {
        b->antialias_colour = redraw_backcol(b);
      }

      /* The Choices are only used directly for browsers which are */
      /* set to override document colours. Otherwise defaults will */
      /* have been read locally and possibly overridden by HTML,   */
      /* and a reload will be needed to reflect the changes.       */
      /*                                                           */
      /* Alternatively, it may be the case that all browsers need  */
      /* redrawing (e.g. if the table border types change).        */

      if (
           needs_redraw ||
           (
             (!b->use_source_cols &&
              colours_changed
             ) ||
             (
               b->background_colour == -1 &&
               b->background_image  == -1
             )
           )
         )
      {
        s.window_handle = b->window_handle;
        RetError(wimp_get_window_state(&s));

        coords_box_toworkarea(&s.visible_area, (WimpRedrawWindowBlock *) &s);

        RetError(wimp_force_redraw(b->window_handle,
                                   s.visible_area.xmin,
                                   s.visible_area.ymin,
                                   s.visible_area.xmax,
                                   s.visible_area.ymax));

        if (b->background_colour == -1)
        {
          b->antialias_colour = choices.background_colour;
        }
      }
    }

    b = b->previous;
  }

  if (serif_changed || sans_changed || fixed_changed)
  {
    /* Reclaim basic typefaces - remember to claim at the *new* */
    /* font Choices size.                                       */

    fm_claim_basic_typefaces(choices.font_size);
  }

  return NULL;
}

/*************************************************/
/* choices_set_button_handler()                  */
/*                                               */
/* Called when the set button in the main        */
/* choices dialogue is clicked.                  */
/* Uses choices_get_contents to set the global   */
/* choices to reflect the choices set in the     */
/* choices subwindows                            */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

static int choices_set_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e = NULL;
  int             buttons;

  ChkError(window_get_pointer_info(0, NULL, NULL, &buttons, NULL, NULL));

  e = choices_get_contents();
  if (e) show_error_ret(e);

  if (buttons != 1)
  {
    ChkError(toolbox_hide_object(0, choices_windowid));
  }

  return 1;
}

/*************************************************/
/* choices_cancel_button_handler()               */
/*                                               */
/* Called when the cancel button in the main     */
/* choices dialogue is clicked.                  */
/* Uses choices_set_contents to restore the      */
/* subwindows to their previous state.           */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

static int choices_cancel_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  ObjectId        window;
  int             update_fake;
  fm_typeface   * tfptr;
  int             buttons;

  ChkError(window_get_pointer_info(0, NULL, NULL, &buttons, NULL, NULL));

  if (buttons == 1)
  {
    if ((choices.background_colour != new_choices->background_colour) ||
        (choices.text_colour       != new_choices->text_colour)       ||
        (choices.link_colour       != new_choices->link_colour)       ||
        (choices.used_colour       != new_choices->used_colour)       ||
        (choices.followed_colour   != new_choices->followed_colour)   ||
        (choices.selected_colour   != new_choices->selected_colour)   ||
        (choices.underline_links   != new_choices->underline_links))
    {
      update_fake = 1;
    }
    else
    {
      update_fake = 0;
    }

    memcpy(new_choices, &choices, sizeof(global_choices));

    tfptr = fm_find_typeface("serif");
    memcpy(&new_typefaces[0], tfptr, sizeof(fm_typeface));
    tfptr = fm_find_typeface("sans");
    memcpy(&new_typefaces[1], tfptr, sizeof(fm_typeface));
    tfptr = fm_find_typeface("fixed");
    memcpy(&new_typefaces[2], tfptr, sizeof(fm_typeface));

    choices_set_contents();

    /* Attempt to redraw fake page display */

    if (update_fake && (window = choices_find_component(CD_FakePage)) != 0) button_set_flags(0, window, CD_FakePage, 0, 0);
  }
  else
  {
    memcpy(new_choices, &choices, sizeof(global_choices));
    ChkError(toolbox_hide_object(0, choices_windowid));
  }

  return 1;
}

/*************************************************/
/* choices_close()                               */
/*                                               */
/* Close the Choices dialogue as if the 'Cancel' */
/* button had been activated with Select.        */
/*************************************************/

_kernel_oserror * choices_close(void)
{
  if (new_choices) memcpy(new_choices, &choices, sizeof(global_choices));

  if (choices_windowid) return toolbox_hide_object(0, choices_windowid);
  else                  return NULL;
}

/*************************************************/
/* choices_save_button_handler()                 */
/*                                               */
/* Called when the set button in the main        */
/* choices dialogue is clicked.                  */
/* Uses choices_get_contents to set the global   */
/* choices to reflect the choices set in the     */
/* choices subwindows and then saves the choices */
/* file.                                         */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

static int choices_save_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e = NULL;
  ObjectId        window;
  int             buttons;

  ChkError(window_get_pointer_info(0, NULL, NULL, &buttons, NULL, NULL));

  e = choices_get_contents();
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Attempt to redraw fake page display */

  if ((window = choices_find_component(CD_FakePage)) != 0) button_set_flags(0, window, CD_FakePage, 0, 0);

  e = save_save_choices(NULL);
  if (e) show_error_ret(e);

  if (buttons != 1)
  {
    ChkError(toolbox_hide_object(0, choices_windowid));
  }

  return 1;
}

/*************************************************/
/* choices_set_encoding_field()                  */
/*                                               */
/* Sets the encoding display field appropriately */
/* to reflect the current state of               */
/* new_choices->encoding                         */
/*************************************************/

_kernel_oserror * choices_set_encoding_field(void)
{
  _kernel_oserror * e;
  ObjectId          objid, destwind;
  ComponentId       compid;
  char            * textptr;
  int               sizereqd;

  /* Find which window the encoding display is in */

  destwind = choices_find_component(CD_EncodingDisply);
  if (!destwind) return NULL;

  /* Find menu item which contains encoding name */

  if (encoding_get_encoding_item(new_choices->encoding, &objid, &compid))
  {
    RetError(menu_get_entry_text(0, objid, compid, NULL, 0, &sizereqd));

    /* Add 1 to the buffer size just incase the sizereqd field is returned as string */
    /* length rather than the buffer size required.  Typical paranoid precautions    */
    /* when using the toolbox.                                                       */

    textptr = malloc(sizereqd+1);

    if (textptr)
    {
      e = menu_get_entry_text(0, objid, compid, textptr, sizereqd+1, NULL);
      if (e)
      {
        free(textptr);
        return e;
      }

      e = choices_displayfield_set_value(0, destwind, CD_EncodingDisply, textptr);

      free(textptr);
      return e;
    }
    else
    {
      RetError(make_no_memory_error(18));
    }
  }
  else
  {
    /* The encoding name could not be found in the menu structure */

    RetError(choices_displayfield_set_value(0, destwind, CD_EncodingDisply, "Unknown encoding")); //
  }

  return NULL;
}

/*************************************************/
/* choices_encoding_button_handler()             */
/*                                               */
/* Opens the encoding window with appropriate    */
/* values for the choices dialogue.              */
/*************************************************/

static int choices_encoding_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e;
  int  position[2];
  BBox box;
  WimpGetWindowStateBlock state;

  e = gadget_get_bbox(0, idb->self_id, idb->self_component, &box);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = window_get_wimp_handle(0, idb->self_id, &state.window_handle);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = wimp_get_window_state(&state);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Set open coordinates of encoding menu to top left of encoding button */

  position[0] = coords_x_toscreen(box.xmax, (WimpRedrawWindowBlock*)&state);
  position[1] = coords_y_toscreen(box.ymax, (WimpRedrawWindowBlock*)&state);

  show_error_ret(toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                     encoding_get_menuid(),
                                     Toolbox_ShowObject_TopLeft,
                                     position,
                                     choices_windowid,
                                     NULL));

  return 1;
}

/*************************************************/
/* choices_colour_button_handler()               */
/*                                               */
/* Opens a colour dialogue box with appropriate  */
/* settings for the current colour.              */
/*************************************************/

static int choices_colour_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e;
  int  position[2];
  BBox box;
  WimpGetWindowStateBlock state;
  int colour[2];

  /* Is the ColourDbox module in RMA? */

  e = _swix(OS_Module, _INR(0,1), 18, "ColourDbox");

  /* No, well try and load it from system */

  if (e)
  {
    e = _swix(OS_Module, _INR(0,1), 1, "System:Modules.Toolbox.ColourDbox");
  }

  /* That didn't work so try reinitialising it */

  if (e)
  {
    e = _swix(OS_Module, _INR(0,1), 3, "ColourDbox");
  }

  /* That didn't work either!  So it's tough you can't change the colours */

  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  switch(idb->self_component)
  {
    case CD_BackColourBt: colour[0] = new_choices->background_colour; break;
    case CD_TextColourBt: colour[0] = new_choices->text_colour;       break;
    case CD_LinkColourBt: colour[0] = new_choices->link_colour;       break;
    case CD_UsedColourBt: colour[0] = new_choices->used_colour;       break;
    case CD_FolwColourBt: colour[0] = new_choices->followed_colour;   break;
    case CD_SlctColourBt: colour[0] = new_choices->selected_colour;   break;
    default:
    return 1;
    break;
  }

  colour[1] = 0;

  e = gadget_get_bbox(0, idb->self_id, idb->self_component, &box);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = window_get_wimp_handle(0, idb->self_id, &state.window_handle);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = wimp_get_window_state(&state);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Set open coordinates of encoding menu to top left of encoding button */

  position[0] = coords_x_toscreen(box.xmax, (WimpRedrawWindowBlock*)&state);
  position[1] = coords_y_toscreen(box.ymax, (WimpRedrawWindowBlock*)&state);

  e = toolbox_create_object(0, "ColourDbox", &colourdbox_id);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = colourdbox_set_colour(0, colourdbox_id, colour);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  show_error_ret(toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                     colourdbox_id,
                                     Toolbox_ShowObject_TopLeft,
                                     position,
                                     idb->self_id,
                                     idb->self_component));

  e = event_register_toolbox_handler(colourdbox_id, ColourDbox_DialogueCompleted, choices_colour_closed_handler, NULL);
  if (e) {show_error_ret(e); return 1;}

  e = event_register_toolbox_handler(colourdbox_id, ColourDbox_ColourSelected, choices_colour_selected_handler, NULL);
  if (e) {show_error_ret(e); return 1;}

  return 1;
}

/*************************************************/
/* choices_colour_closed_handler()               */
/*                                               */
/* Called when the colour dialogue box is closed */
/* deregisters all events attached to it and     */
/* deletes the dbox object.                      */
/*************************************************/

static int choices_colour_closed_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e;

  e = event_deregister_toolbox_handler(idb->self_id, ColourDbox_DialogueCompleted, choices_colour_closed_handler, NULL);
  if (e) {show_error_ret(e); return 1;}

  e = event_deregister_toolbox_handler(colourdbox_id, ColourDbox_ColourSelected, choices_colour_selected_handler, NULL);
  if (e) {show_error_ret(e); return 1;}

  e = toolbox_delete_object(0, idb->self_id);
  if (e) {show_error_ret(e); return 1;}

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_colour_closed_handler: Colour DBox deleted\n");
  #endif

  return 1;
}

/*************************************************/
/* choices_colour_selected_handler()             */
/*                                               */
/* Called when the a colour is selected in the   */
/* colour dialogue box.                          */
/* Sets appropriate fields in the new_choices    */
/* structure and updates the window to reflect   */
/* the new colour chosen.                        */
/*************************************************/

static int choices_colour_selected_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  ColourDboxColourSelectedEvent *cevent = (ColourDboxColourSelectedEvent*)event;

  switch(idb->parent_component)
  {
    case CD_BackColourBt: new_choices->background_colour = cevent->colour_block[0]; break;
    case CD_TextColourBt: new_choices->text_colour       = cevent->colour_block[0]; break;
    case CD_LinkColourBt: new_choices->link_colour       = cevent->colour_block[0]; break;
    case CD_UsedColourBt: new_choices->used_colour       = cevent->colour_block[0]; break;
    case CD_FolwColourBt: new_choices->followed_colour   = cevent->colour_block[0]; break;
    case CD_SlctColourBt: new_choices->selected_colour   = cevent->colour_block[0]; break;
    default:
    return 1;
    break;
  }

  /* Don't bother with errors as colour display field might not exist */

  choices_colour_set_component(idb->parent_id, idb->parent_component-1, cevent->colour_block[0]);

  /* Don't bother with errors as fake page display might not exist */

  button_set_flags(0, idb->parent_id, CD_FakePage, 0, 0);

  return 1;
}

/*************************************************/
/* choices_colour_set_component()                */
/*                                               */
/* Sets the validation string of a button to be  */
/* slabbed in and have the background colour     */
/* specified.                                    */
/*                                               */
/* Parameters: window ObjectId                   */
/*                                               */
/*             button ComponentId                */
/*                                               */
/*             colour                            */
/*************************************************/

static _kernel_oserror *choices_colour_set_component(ObjectId window, ComponentId component, int colour)
{
  char            newvalidation[32];
  unsigned char * newcol;

  newcol = (unsigned char*)&colour;

  sprintf(newvalidation, "R2;C/%02.2x%02.2x%02.2x", *(newcol + 3), *(newcol + 2), *(newcol + 1));

  return choices_button_set_validation(0, window, component, newvalidation);
}

/*************************************************/
/* choices_display_button_handler()              */
/*                                               */
/* Opens the display menu with a value           */
/* appropriate to either the hotlist or history. */
/*************************************************/

static int choices_display_m_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e;
  int  position[2];
  BBox box;
  WimpGetWindowStateBlock state;
  ObjectId display;
  int new_tick;

  e = gadget_get_bbox(0, idb->self_id, idb->self_component, &box);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = window_get_wimp_handle(0, idb->self_id, &state.window_handle);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = wimp_get_window_state(&state);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Set open coordinates of display menu to top left of display button */

  position[0] = coords_x_toscreen(box.xmax, (WimpRedrawWindowBlock*)&state);
  position[1] = coords_y_toscreen(box.ymax, (WimpRedrawWindowBlock*)&state);

  e = toolbox_create_object(0, "ChDisplay", &display);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  switch(idb->self_component)
  {
    case CD_HlDispBt: new_tick = new_choices->hotlist_show; break;
    case CD_HiDispBt: new_tick = new_choices->show_urls;    break;
    default: new_tick = 0; break;
  }

  if (disp_ticked != new_tick)
  {
    if (disp_ticked != -1) menu_set_tick(0, display, disp_ticked, 0);
    menu_set_tick(0, display, new_tick, 1);
    disp_ticked = new_tick;
  }

  show_error_ret(toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                     display,
                                     Toolbox_ShowObject_TopLeft,
                                     position,
                                     idb->self_id,
                                     idb->self_component));

  return 1;
}

/*************************************************/
/* choices_save_m_button_handler()               */
/*                                               */
/* Opens the save menu with a value appropriate  */
/* to either the hotlist or history.             */
/*************************************************/

static int choices_save_m_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e;
  int  position[2];
  BBox box;
  WimpGetWindowStateBlock state;
  ObjectId display;
  int new_tick;

  e = gadget_get_bbox(0, idb->self_id, idb->self_component, &box);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = window_get_wimp_handle(0, idb->self_id, &state.window_handle);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = wimp_get_window_state(&state);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Set open coordinates of display menu to top left of display button */

  position[0] = coords_x_toscreen(box.xmax, (WimpRedrawWindowBlock*)&state);
  position[1] = coords_y_toscreen(box.ymax, (WimpRedrawWindowBlock*)&state);

  e = toolbox_create_object(0, "ChSave", &display);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  switch(idb->self_component)
  {
    case CD_HlSaveBt: new_tick = new_choices->save_hotlist; break;
    case CD_HiSaveBt: new_tick = new_choices->save_history; break;
    default: new_tick = 0; break;
  }

  if (save_ticked != new_tick)
  {
    if (save_ticked != -1) menu_set_tick(0, display, save_ticked, 0);
    menu_set_tick(0, display, new_tick, 1);
    save_ticked = new_tick;
  }

  show_error_ret(toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                     display,
                                     Toolbox_ShowObject_TopLeft,
                                     position,
                                     idb->self_id,
                                     idb->self_component));

  return 1;
}

/*************************************************/
/* choices_set_save_field()                      */
/*                                               */
/* Sets the passed save display field            */
/* appropriately to reflect the passed state.    */
/*************************************************/

static _kernel_oserror * choices_set_save_field(ObjectId obj, ComponentId comp, int state)
{
  _kernel_oserror *e;
  char *tempstring;

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_set_display_field: Called\n");
  #endif

  RetError(choices_get_menu_entry_text("ChSave", state, &tempstring));

  e = choices_displayfield_set_value(0, obj, comp, tempstring);

  free(tempstring);

  return e;
}

/*************************************************/
/* choices_set_display_field()                   */
/*                                               */
/* Sets the passed display display field         */
/* appropriately to reflect the passed state.    */
/*************************************************/

static _kernel_oserror * choices_set_display_field(ObjectId obj, ComponentId comp, int state)
{
  _kernel_oserror *e;
  char *tempstring;

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_set_display_field: Called\n");
  #endif

  RetError(choices_get_menu_entry_text("ChDisplay", state, &tempstring));

  e = choices_displayfield_set_value(0, obj, comp, tempstring);

  free(tempstring);

  return e;
}

/*************************************************/
/* choices_display_m_click_handler()             */
/*                                               */
/* Called when there is a selection in the       */
/* display menu.  Sets the selected menu item to */
/* be ticked, unticks the previously ticked      */
/* entry sets appropriate new_choices field and  */
/* updates the display field of the component    */
/* with component number 1 less than the menus   */
/* parent.                                       */
/*************************************************/

static int choices_display_m_click_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  if (disp_ticked != idb->self_component)
  {
    if (disp_ticked != -1) menu_set_tick(0, idb->self_id, disp_ticked, 0);
    menu_set_tick(0, idb->self_id, idb->self_component, 1);
    disp_ticked = idb->self_component;
  }

  switch(idb->parent_component)
  {
    case CD_HlDispBt:
    new_choices->hotlist_show = idb->self_component;
    break;
    case CD_HiDispBt:
    new_choices->show_urls    = idb->self_component;
    break;
  }

  choices_set_display_field(idb->parent_id, idb->parent_component - 1, idb->self_component);

  return 1;
}

/*************************************************/
/* choices_save_m_click_handler()                */
/*                                               */
/* Called when there is a selection in the save  */
/* menu.  Sets the selected menu item to be      */
/* ticked, unticks the previously ticked entry   */
/* sets appropriate new_choices field and        */
/* updates the display field of the component    */
/* with component number 1 less than the menus   */
/* parent.                                       */
/*************************************************/

static int choices_save_m_click_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  if (save_ticked != idb->self_component)
  {
    if (save_ticked != -1) menu_set_tick(0, idb->self_id, save_ticked, 0);
    menu_set_tick(0, idb->self_id, idb->self_component, 1);
    save_ticked = idb->self_component;
  }

  switch(idb->parent_component)
  {
    case CD_HlSaveBt:
    new_choices->save_hotlist = idb->self_component;
    break;
    case CD_HiSaveBt:
    new_choices->save_history = idb->self_component;
    break;
  }

  choices_set_save_field(idb->parent_id, idb->parent_component - 1, idb->self_component);

  return 1;
}

/*************************************************/
/* choices_draw_string()                         */
/*                                               */
/* Writes a string on the screen using the       */
/* passed font handle, it underlines the text if */
/* required.  It also returns the position to    */
/* paint the next string following this one.     */
/*                                               */
/* Parameters: font handle                       */
/*                                               */
/*             string to print                   */
/*                                               */
/*             xposition on screen (os coords)   */
/*                                               */
/*             yposition on screen (os coords)   */
/*                                               */
/*             0     - don't underline           */
/*             non 0 - underline                 */
/*                                               */
/*             foreground colour                 */
/*                                               */
/*             background colour                 */
/*                                               */
/*             pointer to int to return x        */
/*             position in                       */
/*************************************************/

static _kernel_oserror * choices_draw_string(fm_face h,
                                             char *string,
                                             int xpos,
                                             int ypos,
                                             int underline,
                                             int forecolour,
                                             int backcolour,
                                             int *newxpos)
{
  int width, nochars;
  fm_set_font_colour(h, forecolour, backcolour);
  fm_get_string_width(h, string, 0x1000000, strlen(string), -1, &nochars, &width);
  convert_to_os(width, &width);

  /* The next string must be plotted straight after this string */

  *newxpos = xpos + width;

  fm_puts(h, xpos, ypos, string, 1, 0);

  if (underline)
  {
    redraw_set_colour(forecolour);
    bbc_move(xpos, ypos - 7);
    bbc_draw(*newxpos, ypos - 7);
  }
  return NULL;
}

/*************************************************/
/* choices_write_rubbish()                       */
/*                                               */
/* Writes a string of readable rubbish on the    */
/* screen using the passed font handle.  It also */
/* returns the position to paint the next string */
/* following this one.                           */
/*                                               */
/* Parameters: font handle                       */
/*                                               */
/*             xposition on screen (os coords)   */
/*                                               */
/*             yposition on screen (os coords)   */
/*                                               */
/*             Approximate x position to stop    */
/*             printing at (os coords)           */
/*                                               */
/*             foreground colour                 */
/*                                               */
/*             background colour                 */
/*                                               */
/*             pointer to int to return x        */
/*             position in                       */
/*                                               */
/*             Random seed for string to print   */
/*             (If you pass the same seed the    */
/*             string should be the same)        */
/*************************************************/

static _kernel_oserror * choices_write_rubbish(fm_face h,
                                            int xpos,
                                            int ypos,
                                            int maxxish,
                                            int forecolour,
                                            int backcolour,
                                            int *newxpos,
                                            int seed)
{
  char ministring[5];
  int chars = 0;
  int spcwidth, width, nochars;

  fm_set_font_colour(h, forecolour, backcolour);

  srand(seed);

  fm_get_string_width(h, " ", 0x1000000, 1, -1, &nochars, &width);
  convert_to_os(width, &spcwidth);
  xpos += spcwidth;

  while(xpos < maxxish)
  {
    ministring[0] = consonant[rand()%(sizeof(consonant)-1)];
    ministring[1] = vowel[rand()%(sizeof(vowel)-1)];

    if (rand()%2)
    {
      ministring[2] = consonant[rand()%(sizeof(consonant)-1)];
      ministring[3] = vowel[rand()%(sizeof(vowel)-1)];
      ministring[4] = 0;
    }
    else
    {
      ministring[2] = 0;
    }

    fm_puts(h, xpos, ypos, ministring, 1, 0);
    fm_get_string_width(h, ministring, 0x1000000, strlen(ministring), -1, &nochars, &width);
    convert_to_os(width, &width);
    xpos += width;

    chars++;
    if (chars > rand()%2 || xpos >= maxxish)
    {
      chars = 0;
      xpos += spcwidth;
    }
  }

  *newxpos = xpos;
  return NULL;
}

/*************************************************/
/* choices_redraw_fakepage_handler()             */
/*                                               */
/* Redraws the fake page in the colour selection */
/* dbox.                                         */
/*************************************************/

static int choices_redraw_fakepage_handler(int eventcode, WimpPollBlock * event, IdBlock * idb, void * handle)
{
  _kernel_oserror       * e = NULL;
  WimpRedrawWindowBlock   block;
  WimpGetWindowStateBlock state;
  int                     more, ypos, xtarget, ptsize, gadsize, xstart, nolines;
  int                     fontheight = 0;
  int                     gotfont    = 0;
  BBox                    icon_coords, fbox;
  fm_face                 h = 0;
  char                    display_this[4];

  block.window_handle = event->redraw_window_request.window_handle;
  state.window_handle = event->redraw_window_request.window_handle;

  e = wimp_get_window_state(&state);
  show_error_ret(e);
  if (!e) e = gadget_get_bbox(0, idb->self_id, CD_FakePage, &icon_coords);

  gadsize = icon_coords.ymax - icon_coords.ymin;

  xstart = gadsize / 8;

  if (e)
  {
    /* Gadget doesn't exist or something strange happened trying */
    /* to get window state so just do a simple redraw loop to    */
    /* keep the wimp happy.                                      */

    ChkError(wimp_redraw_window(&block, &more));
    while (more && !e) e = wimp_get_rectangle(&block, &more);
    return 1;
  }

  coords_box_toscreen(&icon_coords, (WimpRedrawWindowBlock *) &state);

  /* Find out which lines to draw */

  for(nolines = 0; nolines < sizeof(display_this); nolines++) display_this[nolines] = 0;

  nolines = 1; /* Start with one line (half at top, half at bottom) */

  if (choices_find_component(CD_LinkColourBt))
  {
    nolines ++;
    display_this[0] = 1;
  }
  if (choices_find_component(CD_UsedColourBt))
  {
    nolines ++;
    display_this[1] = 1;
  }
  if (choices_find_component(CD_FolwColourBt))
  {
    nolines ++;
    display_this[2] = 1;
  }
  if (choices_find_component(CD_SlctColourBt))
  {
    nolines ++;
    display_this[3] = 1;
  }

  /* Start the redraw loop */

  ChkError(wimp_redraw_window(&block, &more));

  while (more && !e)
  {
    /* Clip the redraw area to only take in the gadget */

    if (set_graphics_intersection(&icon_coords, &block.redraw_area))
    {
      /* Only claim the font if it is required */

      if (!gotfont)
      {
        /* Find default browser font, the font manager will return system */
        /* font if it is configured.                                      */

        /* Find a 1000 subpoint (big) version of default browser font to scale against */

        h = fm_find_font(NULL, "serif", 1000, 1000, 0, 0);
        e = fm_font_box(h, &fbox);
        fm_lose_font(NULL, h);
        if (e) show_error_cont(e);
        fontheight = fbox.ymax - fbox.ymin;

        /* Find a version of the default browser font with a */
        /* point size which will allow an appropriate number */
        /* of lines in the fake page display.                */

        ptsize = ((gadsize * 1000 / nolines) / fontheight);

        h = fm_find_font(NULL, "serif", ptsize, ptsize, 0, 0);
        ChkError(fm_font_box(h, &fbox));
        fontheight = fbox.ymax - fbox.ymin;
        gotfont = 1;
      }

      /* Fake browser drawing code is here */

      /* Fill background with background colour */

      redraw_set_colour(new_choices->background_colour);
      ChkError(bbc_rectanglefill(icon_coords.xmin, icon_coords.ymin, icon_coords.xmax - icon_coords.xmin, icon_coords.ymax - icon_coords.ymin));

      xtarget = icon_coords.xmin - 32;
      ypos = icon_coords.ymax - fontheight / 3;

      /* Display the top line of the fake page display */

      ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 1));

      ypos -= fontheight;
      xtarget = icon_coords.xmin - 32;

      /* Display the new link line of the fake page display */

      if (display_this[0])
      {
        ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmin+xstart, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 2));
        ChkError(choices_draw_string(h, "new", xtarget, ypos, new_choices->underline_links, new_choices->link_colour, new_choices->background_colour, &xtarget));
        ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 3));

        ypos -= fontheight;
        xtarget = icon_coords.xmin - 32;
      }

      /* Display the new followed line of the fake page display */

      if (display_this[1])
      {
        ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmin+xstart, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 4));
        ChkError(choices_draw_string(h, "followed", xtarget, ypos, new_choices->underline_links, new_choices->used_colour, new_choices->background_colour, &xtarget));
        ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 5));

        ypos -= fontheight;
        xtarget = icon_coords.xmin - 32;
      }

      /* Display the highlighted link line of the fake page display */

      if (display_this[2])
      {
        ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmin+xstart, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 6));
        ChkError(choices_draw_string(h, "highlighted", xtarget, ypos, new_choices->underline_links, new_choices->followed_colour, new_choices->background_colour, &xtarget));
        ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 7));

        ypos -= fontheight;
        xtarget = icon_coords.xmin - 32;
      }

      /* Display the selected link line of the fake page display */
      if (display_this[3])
      {
        ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmin+xstart, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 8));
        ChkError(choices_draw_string(h, "selected", xtarget, ypos, new_choices->underline_links, new_choices->selected_colour, new_choices->background_colour, &xtarget));
        ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 9));

        ypos -= fontheight;
        xtarget = icon_coords.xmin - 32;
      }

      /* Display the bottom line of the fake page display */

      ChkError(choices_write_rubbish(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, rubbish_seed + 10));
    }

    restore_graphics_intersection(&block.redraw_area);

    /* Get the next redraw rectangle */

    if (!e) e = wimp_get_rectangle(&block, &more);
  }

  if (gotfont) fm_lose_font(NULL, h);

  return 1;
}

/*************************************************/
/* choices_option_state_handler()                */
/*                                               */
/* Called when an option button has its state    */
/* changed by being clicked on.  Sets or unsets  */
/* the relevant flag in new_choices and causes   */
/* redraws where necessary.                      */
/*************************************************/

static int choices_option_state_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  int               state;
  ObjectId          window;

  ChkError(optionbutton_get_state(0, idb->self_id, idb->self_component, &state));

  switch(idb->self_component)
  {
    case CD_UnderlineLinks:
    {
      new_choices->underline_links = !!state;

      /* Attempt to redraw fake page display */

      if ((window = choices_find_component(CD_FakePage)) != 0) button_set_flags(0, window, CD_FakePage, 0, 0);
      break;
    }

    case CD_UseDocColours:  new_choices->use_source_cols = !!state; break;
    case CD_ShowForeground: new_choices->show_foreground = !!state; break;
    case CD_ShowBackground: new_choices->show_background = !!state; break;

    case CD_URLBar:         new_choices->url_bar         = !!state; break;
    case CD_StatusBar:      new_choices->status_bar      = !!state; break;
    case CD_ButtonBar:      new_choices->button_bar      = !!state; break;
    case CD_FullScreen:     new_choices->full_screen     = !!state; break;

    case CD_ClientPull:     new_choices->client_pull     = !!state; break;
    case CD_NetscapeEmu:    new_choices->clone           = !!state; break;
    case CD_FramesSupport:  new_choices->support_frames  = !!state; break;

    case CD_FontsSystem:
    new_choices->system_font = !!state;
    if ((window = choices_find_component(CD_FontsTF1Labl)) != 0)
    {
      choices_set_fonts_greyed(window, new_choices->system_font);
      /*set_gadget_state(window, CD_FontsGroup1,  new_choices->system_font);*/
      if ((window = choices_find_component(CD_FontsSize)) != 0)
      {
        if (new_choices->system_font)
        {
          int frac, temp;
          numberrange_get_value(0, window, CD_FontsSize, &new_choices->font_size);
          frac = new_choices->font_size % 10;
          new_choices->font_size = ((new_choices->font_size / 10)<<4) + fromdecimal[frac];
          temp = FM_Standard_Size;
          frac = (temp & 0xf);
          temp = ((temp >> 4) * 10) + todecimal[frac];
          choices_numberrange_set_value(0, window, CD_FontsSize, temp);
          choices_numberrange_set_value(0, window, CD_FontsAspect, 100);
        }
        else
        {
          int temp, frac;
          temp = new_choices->font_size;
          frac = (temp & 0xf);
          temp = ((temp >> 4) * 10) + todecimal[frac];
          choices_numberrange_set_value(0, window, CD_FontsSize, temp);
          choices_numberrange_set_value(0, window, CD_FontsAspect, new_choices->tt_aspect);
        }
      }
    }
    break;

    case CD_ObjHandle:
    new_choices->support_object = !!state;

    if ((window = choices_find_component(CD_ObjPlugDisp)) != 0)
    {
      set_gadget_state(window, CD_ObjPlugLabl, !new_choices->support_object);
      set_gadget_state(window, CD_ObjPlugBt, !new_choices->support_object);
      set_gadget_state(window, CD_ObjPlugDisp, !new_choices->support_object);
      choices_set_plugin_field();
    }
    break;

    case CD_NetUseProxy:
    new_choices->use_proxy = !!state;
    if ((window = choices_find_component(CD_NetProxyAddr))    != 0)
    {
      set_gadget_state(window, CD_NetProxyAddr, !new_choices->use_proxy);
      set_gadget_state(window, CD_NetProxyLabl, !new_choices->use_proxy);
    }
    break;

    case CD_NetLaunchProxy: new_choices->start_proxy = !!state; break;

    case CD_HiDontAge:
    choices_set_expiry_age_greyed(idb->self_id, !state);
    expiry_age_greyed = !state;
    break;

    case CD_HiDontSize:
    choices_set_max_size_greyed(idb->self_id, !state);
    max_size_greyed = !state;
    break;

    case CD_HiImDontAge:
    choices_set_im_expiry_age_greyed(idb->self_id, !state);
    image_expiry_age_greyed = !state;
    break;

    case CD_HiImDontSize:
    choices_set_im_max_size_greyed(idb->self_id, !state);
    image_max_size_greyed = !state;
    break;

    case CD_TabSupport:
    new_choices->support_tables = !!state;
    choices_set_tables_greyed(!new_choices->support_tables,idb->self_id);
    break;

    /* Haven't recognised this option button event so pass it on */

    default: return 0;
  }

  return 1;
}

/*************************************************/
/* choices_mode_change()                         */
/*                                               */
/* Called on every mode change event.  Records   */
/* that a mode change has taken place for use    */
/* with the choices_open_choice_window function. */
/*************************************************/

_kernel_oserror * choices_mode_change(void)
{
  choices_modechanged = 1;

  return NULL;
}

/*************************************************/
/* choices_open_choice_window()                  */
/*                                               */
/* Called whenever the choices window needs to   */
/* be moved and specifically on mode changes so  */
/* the current sub window can be positioned      */
/* correctly dealing with rounding errors        */
/* between modes of different aspect ratio.      */
/*************************************************/

static int choices_open_choice_window(int eventcode, WimpPollBlock * event, IdBlock * idb, void * handle)
{
  toolbox_show_object(0, idb->self_id, Toolbox_ShowObject_FullSpec, &(event->open_window_request.visible_area), idb->parent_id, idb->parent_component);

  if (choices_modechanged)
  {
    /* Reshow the subwindow at possibly new location */

    choices_show_subwindow(idb->self_id, current_subwindow);
    choices_modechanged = 0;
  }

  return 1;
}

/*************************************************/
/* choices_find_component()                      */
/*                                               */
/* Scans all the choices subwindows for the      */
/* requested component and returns the id of the */
/* first window in which it was seen.            */
/*                                               */
/* Parameters: The component to find.            */
/*                                               */
/* Returns:    Where to return the object id to. */
/*             0 if the component was not found  */
/*************************************************/

static ObjectId choices_find_component(ComponentId component)
{
  int          findwindow;
  unsigned int flags;

  /* Uses lazy evaluation to not call gadget_get_flags unless */
  /* subwindows[findwindow] contains an objectid.             */

  for(findwindow = 0; findwindow < CDNoSubwindows; findwindow++)
    if (subwindows[findwindow] && !gadget_get_flags(0, subwindows[findwindow], component, &flags)) return subwindows[findwindow];

  return 0;
}

/*************************************************/
/* choices_writablefield_set_value()             */
/*                                               */
/* Does as writablefield_set_value but will only */
/* update it if the text is different to that    */
/* currently in the display field.               */
/*************************************************/

static _kernel_oserror *choices_writablefield_set_value(unsigned int flags, ObjectId window, ComponentId writable, char *text)
{
  int               reqdsize;
  char            * oldtext;
  _kernel_oserror * e = NULL;

  RetError(writablefield_get_value(0, window, writable, NULL, 0, &reqdsize));

  oldtext = malloc(reqdsize+1);
  if (!oldtext) return make_no_memory_error(100);

  writablefield_get_value(0, window, writable, oldtext, reqdsize, NULL);

  if (strcmp(text, oldtext))
  {
    e = writablefield_set_value(flags, window, writable, text);
  }

  free(oldtext);

  return e;
}

/*************************************************/
/* choices_displayfield_set_value()              */
/*                                               */
/* Does as displayfield_set_value but will only  */
/* update it if the text is different to that    */
/* currently in the display field.               */
/*************************************************/

static _kernel_oserror *choices_displayfield_set_value(unsigned int flags, ObjectId window, ComponentId writable, char *text)
{
  int               reqdsize;
  char            * oldtext;
  _kernel_oserror * e = NULL;

  RetError(displayfield_get_value(0, window, writable, NULL, 0, &reqdsize));

  oldtext = malloc(reqdsize+1);
  if (!oldtext) return make_no_memory_error(100);

  displayfield_get_value(0, window, writable, oldtext, reqdsize, NULL);

  if (strcmp(text, oldtext))
  {
    e = displayfield_set_value(flags, window, writable, text);
  }

  free(oldtext);

  return e;
}

/*************************************************/
/* choices_button_set_validation()               */
/*                                               */
/* Does as button_set_validation but will only   */
/* update it if the text is different to that    */
/* currently in the button.                      */
/*************************************************/

static _kernel_oserror *choices_button_set_validation(unsigned int flags, ObjectId window, ComponentId writable, char *text)
{
  int               reqdsize;
  char            * oldtext;
  _kernel_oserror * e = NULL;

  RetError(button_get_validation(0, window, writable, NULL, 0, &reqdsize));

  oldtext = malloc(reqdsize+1);
  if (!oldtext) return make_no_memory_error(100);

  button_get_validation(0, window, writable, oldtext, reqdsize, NULL);

  if (strcmp(text, oldtext))
  {
    e = button_set_validation(flags, window, writable, text);
  }

  free(oldtext);

  return e;
}

/*************************************************/
/* choices_numberrange_set_value()               */
/*                                               */
/* Does as numberrange_set_value but will only   */
/* update it if the number is different to that  */
/* currently in the display.                     */
/*************************************************/

static _kernel_oserror *choices_numberrange_set_value(unsigned int flags, ObjectId window, ComponentId writable, int value)
{
  int               oldvalue;
  _kernel_oserror * e = NULL;

  RetError(numberrange_get_value(0, window, writable, &oldvalue));

  if (oldvalue != value)
  {
    e = numberrange_set_value(flags, window, writable, value);
  }

  return e;
}

/*************************************************/
/* choices_modified_font()                       */
/*                                               */
/* Finds a version of a font with a specified    */
/* modification eg. bold or italic.              */
/*                                               */
/* Parameters: pointer to the original font name */
/*                                               */
/*             pointer to a space separated and  */
/*             null terminated list of           */
/*             modifications in order of         */
/*             preference                        */
/*                                               */
/*             pointer to a buffer to contain    */
/*             the name of the new font.         */
/*************************************************/

static void choices_modified_font(char * orig, char * mod, char * buffer)
{
  _kernel_oserror * e = NULL;
  char              origname[Limits_FontName];
  int               f=-1;
  char            * p, * p2;
  char            * lastdot;
  BOOL              found;

  /* Find the fontname field */

  _swix(Font_FindField, _INR(1,2)|_OUTR(1,2), orig, 'F', &p, &found);

  /* If the field could not be found assume that the entire */
  /* string is the font name.                               */

  if (!found) p = orig;

  p2 = origname;

  /* Extract the font name.                                       */
  /* Copys the string pointed to by p to p2 until either a \ or a */
  /* character with an ascii value less than 32 is encountered.   */

  while (*p > 32 && *p != '\\') *p2++ = *p++;

  /* NULL terminate the new string */

  *p2 = '\0';

  lastdot = strrchr(origname, '.');

  /* Get first of possibly several possible modifications */

  p = strtok(mod, " ");

  while (p)
  {
    sprintf(buffer, "%s.%s", origname, p);
    e=_swix(Font_FindFont, _INR(1,5)|_OUT(0), buffer, 12*16, 12*16, 90, 90, &f);

    /* If there was no error then the font must have been found */

    if (!e) break;

    if (lastdot)
    {
      *lastdot = '\0';
      sprintf(buffer, "%s.%s", origname, p);
      *lastdot = '.';
      e=_swix(Font_FindFont, _INR(1,5)|_OUT(0), buffer, 12*16, 12*16, 90, 90, &f);
      if (e==NULL) break;
    }

    /* Get the next modification */

    p=strtok(NULL, " ");
  }

  /* If a font has been 'found' we need to lose it again */

  if (f >= 0) _swix(Font_LoseFont, _IN(0), f);

  /* If an error was encountered the best we can do is */
  /* to return the original font.                      */

  if (e) strcpy(buffer, orig);
}

/*************************************************/
/* choices_font_button_handler()                 */
/*                                               */
/* Opens a font menu box with appropriate        */
/* settings for the current font.                */
/*************************************************/

static int choices_font_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e;
  int  position[2];
  BBox box;
  WimpGetWindowStateBlock state;
  ObjectId                fontmenu_id;
  int colour[2];
  char * fontname;

  switch(idb->self_component)
  {
    case CD_FontsTF1Bt: break;
    case CD_FontsTF2Bt: break;
    case CD_FontsTF3Bt: break;

    default:
    return 1;
    break;
  }

  colour[1] = 0;

  e = gadget_get_bbox(0, idb->self_id, idb->self_component, &box);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = window_get_wimp_handle(0, idb->self_id, &state.window_handle);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = wimp_get_window_state(&state);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Set open coordinates of encoding menu to top left of encoding button */

  position[0] = coords_x_toscreen(box.xmax, (WimpRedrawWindowBlock*)&state);
  position[1] = coords_y_toscreen(box.ymax, (WimpRedrawWindowBlock*)&state);

  e = toolbox_create_object(0, "FontMenu", &fontmenu_id);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  switch(idb->self_component)
  {
    case CD_FontsTF1Bt: fontname = new_typefaces[0].fontnames[0]; break;
    case CD_FontsTF2Bt: fontname = new_typefaces[1].fontnames[0]; break;
    case CD_FontsTF3Bt: fontname = new_typefaces[2].fontnames[0]; break;
    default: fontname = ""; break;
  }

  fontmenu_set_font(0, fontmenu_id, fontname);

  show_error_ret(toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                     fontmenu_id,
                                     Toolbox_ShowObject_TopLeft,
                                     position,
                                     idb->self_id,
                                     idb->self_component));

  e = event_register_toolbox_handler(fontmenu_id, FontMenu_HasBeenHidden, choices_font_closed_handler, NULL);
  if (e) {show_error_ret(e); return 1;}

  e = event_register_toolbox_handler(fontmenu_id, FontMenu_Selection, choices_font_selected_handler, NULL);
  if (e) {show_error_ret(e); return 1;}

  return 1;
}

/*************************************************/
/* choices_font_closed_handler()                 */
/*                                               */
/* Called when the font menu is closed           */
/* deregisters all events attached to it and     */
/* deletes the menu object.                      */
/*************************************************/

static int choices_font_closed_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e;

  e = event_register_toolbox_handler(idb->self_id, FontMenu_HasBeenHidden, choices_font_closed_handler, NULL);
  if (e) {show_error_ret(e); return 1;}

  e = event_register_toolbox_handler(idb->self_id, FontMenu_Selection, choices_font_selected_handler, NULL);
  if (e) {show_error_ret(e); return 1;}

  e = toolbox_delete_object(0, idb->self_id);
  if (e) {show_error_ret(e); return 1;}

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_font_closed_handler: FontMenu deleted\n");
  #endif

  return 1;
}

/*************************************************/
/* choices_font_selected_handler()               */
/*                                               */
/* Called when the a font is selected in the     */
/* font menu.                                    */
/*************************************************/

static int choices_font_selected_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  char font[4] [Limits_FontName], *p, *p2;
  int found;
  fm_typeface * target;

  #ifdef TRACE
    if (tl & (1u<<29))
    {
      Printf("choices_font_selected_handler: called\n");
      Printf("Selected font = %s\n", ((FontMenuSelectionEvent*)event)->font_id);
    }
  #endif

  p2 = font[0];

  /* Assume that the F field will be found because the fontmenu */
  /* returns the font in the correct format.                    */

  ChkError(_swix(Font_FindField, _INR(1,2)|_OUTR(1,2), ((FontMenuSelectionEvent*)event)->font_id, 'F', &p, &found));

  /* Extract the font name */

  while (*p > 32 && *p != '\\') *p2++ = *p++;
  *p2 = '\0';

  choices_modified_font(font[0], getenv("Font$Italic"), font[1]); /* Get italic variant of base font */
  choices_modified_font(font[0], getenv("Font$Bold"),   font[2]); /* Get bold version of base font   */
  choices_modified_font(font[2], getenv("Font$Italic"), font[3]); /* Get italic version of bold font */

  #ifdef TRACE
    if (tl & (1u<<29))
    {
      Printf("Base font           = %s\n", font[0]);
      Printf("Italic version      = %s\n", font[1]);
      Printf("Bold version        = %s\n", font[2]);
      Printf("Bold-Italic version = %s\n", font[3]);
    }
  #endif

  /* Set display field to name of new font */
  /* Ignore error as the display isn't     */
  /* required.                             */

  choices_displayfield_set_value(0, idb->parent_id, idb->parent_component - 1, font[0]);

  switch(idb->parent_component)
  {
    case CD_FontsTF1Bt: target = &new_typefaces[0]; break;
    case CD_FontsTF2Bt: target = &new_typefaces[1]; break;
    case CD_FontsTF3Bt: target = &new_typefaces[2]; break;

    default:
    return 1;
    break;
  }

  strcpy(target->fontnames[0], font[0]);
  strcpy(target->fontnames[1], font[1]);
  strcpy(target->fontnames[2], font[2]);
  strcpy(target->fontnames[3], font[3]);

  return 1;
}

/*************************************************/
/* choices_set_fonts_greyed()                    */
/*                                               */
/* Sets the font name and font size gadgets to   */
/* greyed/ungreyed.                              */
/*                                               */
/* Parameters: ObjectId of the window which      */
/*             contains the icons.               */
/*                                               */
/*             0 to ungrey, any other value to   */
/*             to grey.                          */
/*************************************************/

static void choices_set_fonts_greyed(ObjectId window, int state)
{
  set_gadget_state(window, CD_FontsTF1Labl,     !!state);
  set_gadget_state(window, CD_FontsTF1Disp,     !!state);
  set_gadget_state(window, CD_FontsTF1Bt,       !!state);
  set_gadget_state(window, CD_FontsTF2Labl,     !!state);
  set_gadget_state(window, CD_FontsTF2Disp,     !!state);
  set_gadget_state(window, CD_FontsTF2Bt,       !!state);
  set_gadget_state(window, CD_FontsTF3Labl,     !!state);
  set_gadget_state(window, CD_FontsTF3Disp,     !!state);
  set_gadget_state(window, CD_FontsTF3Bt,       !!state);
  set_gadget_state(window, CD_FontsSize,        !!state);
  set_gadget_state(window, CD_FontsSzLabl1,     !!state);
  set_gadget_state(window, CD_FontsSzLabl2,     !!state);
  set_gadget_state(window, CD_FontsAspect,      !!state);
  set_gadget_state(window, CD_FontsAspectLabl1, !!state);
  set_gadget_state(window, CD_FontsAspectLabl2, !!state);
}

/*************************************************/
/* choices_set_plugin_field()                    */
/*                                               */
/* Sets the plugin field appropriately to        */
/* reflect the current state of                  */
/* new_choices->plugin_control                   */
/*************************************************/

_kernel_oserror * choices_set_plugin_field(void)
{
  _kernel_oserror * e;
  ObjectId          destwind;
  char            * tempstring;

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_set_plugin_field: Called\n");
  #endif

  /* Find which window the encoding display is in */

  destwind = choices_find_component(CD_ObjPlugDisp);
  if (!destwind) return NULL;

  RetError(choices_get_menu_entry_text("ChPlugin", new_choices->plugin_control, &tempstring));

  e = choices_displayfield_set_value(0, destwind, CD_ObjPlugDisp, tempstring);

  free(tempstring);

  return NULL;
}

/*************************************************/
/* choices_plug_m_click_handler()                */
/*                                               */
/* Called when there is a selection in the       */
/* plugin menu.  Sets the selected menu item to  */
/* be ticked, unticks the previously ticked      */
/* entry sets appropriate new_choices field and  */
/* updates the display field of the component    */
/* with component number 1 less than the menus   */
/* parent.                                       */
/*************************************************/

static int choices_plug_m_click_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  if (plug_ticked != idb->self_component)
  {
    if (plug_ticked != -1) menu_set_tick(0, idb->self_id, plug_ticked, 0);
    menu_set_tick(0, idb->self_id, idb->self_component, 1);
    plug_ticked = idb->self_component;
  }

  new_choices->plugin_control = idb->self_component;

  choices_set_plugin_field();

  return 1;
}

/*************************************************/
/* choices_plug_m_button_handler()               */
/*                                               */
/* Opens the save menu with a value appropriate  */
/* to either the hotlist or history.             */
/*************************************************/

static int choices_plug_m_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror         * e;
  int                       position[2];
  BBox                      box;
  WimpGetWindowStateBlock   state;
  ObjectId                  display;
  int                       new_tick;

  e = gadget_get_bbox(0, idb->self_id, idb->self_component, &box);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = window_get_wimp_handle(0, idb->self_id, &state.window_handle);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = wimp_get_window_state(&state);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Set open coordinates of display menu to top left of display button */

  position[0] = coords_x_toscreen(box.xmax, (WimpRedrawWindowBlock*)&state);
  position[1] = coords_y_toscreen(box.ymax, (WimpRedrawWindowBlock*)&state);

  e = toolbox_create_object(0, "ChPlugin", &display);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  new_tick = new_choices->plugin_control;

  if (plug_ticked != new_tick)
  {
    if (plug_ticked != -1) menu_set_tick(0, display, plug_ticked, 0);
    menu_set_tick(0, display, new_tick, 1);
    plug_ticked = new_tick;
  }

  show_error_ret(toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                     display,
                                     Toolbox_ShowObject_TopLeft,
                                     position,
                                     idb->self_id,
                                     idb->self_component));

  return 1;
}

/*************************************************/
/* choices_history_radio_handler()               */
/*                                               */
/* Handles radio button state changes.  Swaps    */
/* between page and image history settings.      */
/*************************************************/

static int choices_history_radio_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  int state, temp;
  ObjectId window;

  radiobutton_get_state(0, idb->self_id, idb->self_component, &state, NULL);

  /* Is this a radiobutton selected event? */

  if (state)
  {
    if (expiry_age_greyed)
    {
      if (history_radio) new_choices->image_expiry_age = 0;
      else               new_choices->expiry_age = 0;
    }
    else
    {
      if ((window = choices_find_component(CD_HiExpiryAge))    != 0)
      {
        numberrange_get_value(0, window, CD_HiExpiryAge,  &temp);
        if (history_radio) new_choices->image_expiry_age = choices_typed_time_to_seconds(temp, history_expiry_units);
        else               new_choices->expiry_age = choices_typed_time_to_seconds(temp, history_expiry_units);
      }
    }

    if (max_size_greyed)
    {
      if (history_radio) new_choices->image_max_size = 0;
      else               new_choices->max_size = 0;
    }
    else
    {
      if ((window = choices_find_component(CD_HiMaxSize))      != 0)
      {
        numberrange_get_value(0, window, CD_HiMaxSize,    &temp);
        if (history_radio) new_choices->image_max_size = temp * 1024;
        else               new_choices->max_size = temp * 1024;
      }
    }

    switch(idb->self_component)
    {
      case CD_HiRadPage:
      history_radio = 0;
      if ((window = choices_find_component(CD_HiExpiryAge))    != 0) choices_set_expiry_age(window);
      if ((window = choices_find_component(CD_HiMaxSize))      != 0) choices_set_max_size(window);
      break;

      case CD_HiRadImage:
      history_radio = 1;
      if ((window = choices_find_component(CD_HiExpiryAge))    != 0) choices_set_expiry_age(window);
      if ((window = choices_find_component(CD_HiMaxSize))      != 0) choices_set_max_size(window);
      break;
    }
  }

  return 1;
}

/*************************************************/
/* choices_set_expiry_age()                      */
/*                                               */
/* Sets up the expiry age fields and greys/      */
/* ungreys them as appropriate.                  */
/*                                               */
/* Parameters: ObjectId of the window containing */
/*             the gadgets.                      */
/*************************************************/

static void choices_set_expiry_age(ObjectId window)
{
  int temp;
  int rangetop;
  int time_scale;
  int age;

  age = history_radio ? new_choices->image_expiry_age : new_choices->expiry_age;

  if (age == 0)
  {
    temp       = 1;
    time_scale = CD_TimeDays;
    rangetop  = 7;
  }
  else
  {
    time_scale = choices_return_appropriate_timetype(age);
    temp       = choices_seconds_to_typed_time(age, time_scale);
    rangetop   = choices_get_range_of_typed_time(time_scale);
  }

  expiry_age_greyed = !age;
  choices_set_expiry_age_greyed(window, !age);
  optionbutton_set_state(0, window, CD_HiDontAge, !!age);

  choices_numberrange_set_value(0, window, CD_HiExpiryAge, temp);
  choices_set_timetype_field(time_scale, CD_HiAgeTypeDisp);

  history_expiry_units = time_scale;

  numberrange_set_bounds(15, window, CD_HiExpiryAge, 1, rangetop, 1, 0);
}

/*************************************************/
/* choices_set_expiry_age_greyed()               */
/*                                               */
/* Sets the expiry age gadgets to greyed/        */
/* ungreyed.                                     */
/*                                               */
/* Parameters: ObjectId of the window which      */
/*             contains the icons.               */
/*                                               */
/*             0 to ungrey, any other value to   */
/*             to grey.                          */
/*************************************************/

static void choices_set_expiry_age_greyed(ObjectId window, int state)
{
  set_gadget_state(window, CD_HiExpiryAgeLabl, !!state);
  set_gadget_state(window, CD_HiExpiryAge,     !!state);
  set_gadget_state(window, CD_HiAgeTypeDisp,   !!state);
  set_gadget_state(window, CD_HiAgeTypeBt,     !!state);
}

/*************************************************/
/* choices_set_im_expiry_age()                   */
/*                                               */
/* Sets up the image expiry age fields and greys */
/* /ungreys them as appropriate.                 */
/*                                               */
/* Parameters: ObjectId of the window containing */
/*             the gadgets.                      */
/*************************************************/

static void choices_set_im_expiry_age(ObjectId window)
{
  int temp;
  int rangetop;
  int time_scale;

  if (new_choices->image_expiry_age == 0)
  {
    temp       = 1;
    time_scale = CD_TimeDays;
    rangetop  = 7;
  }
  else
  {
    time_scale = choices_return_appropriate_timetype(new_choices->image_expiry_age);
    temp       = choices_seconds_to_typed_time(new_choices->image_expiry_age, time_scale);
    rangetop   = choices_get_range_of_typed_time(time_scale);
  }

  image_expiry_age_greyed = !new_choices->image_expiry_age;
  choices_set_im_expiry_age_greyed(window, !new_choices->image_expiry_age);
  optionbutton_set_state(0, window, CD_HiImDontAge, !!new_choices->image_expiry_age);
  choices_numberrange_set_value(0, window, CD_HiImExpiryAge, temp);
  choices_set_timetype_field(time_scale, CD_HiImAgeTypeDisp);

  image_expiry_units = time_scale;

  numberrange_set_bounds(15, window, CD_HiImExpiryAge, 1, rangetop, 1, 0);
}

/*************************************************/
/* choices_set_im_expiry_age_greyed()            */
/*                                               */
/* Sets the image expiry age gadgets to greyed/  */
/* ungreyed.                                     */
/*                                               */
/* Parameters: ObjectId of the window which      */
/*             contains the icons.               */
/*                                               */
/*             0 to ungrey, any other value to   */
/*             to grey.                          */
/*************************************************/

static void choices_set_im_expiry_age_greyed(ObjectId window, int state)
{
  set_gadget_state(window, CD_HiImExpiryAgeLabl, !!state);
  set_gadget_state(window, CD_HiImExpiryAge,     !!state);
  set_gadget_state(window, CD_HiImAgeTypeDisp,   !!state);
  set_gadget_state(window, CD_HiImAgeTypeBt,     !!state);
}

/*************************************************/
/* choices_set_max_size()                        */
/*                                               */
/* Sets up the max size field and greys or       */
/* ungreys it as appropriate.                    */
/*                                               */
/* Parameters: The ObjectId of the window which  */
/*             contains the gadgets.             */
/*************************************************/

static void choices_set_max_size(ObjectId window)
{
  int size;

  size = history_radio ? new_choices->image_max_size : new_choices->max_size;

  optionbutton_set_state(0, window, CD_HiDontSize, !!size);
  max_size_greyed = !size;
  choices_set_max_size_greyed(window, max_size_greyed);

  choices_numberrange_set_value(0,
                                window,
                                CD_HiMaxSize,
                                !size ? 32 : size/1024);
}

/*************************************************/
/* choices_set_max_size_greyed()                 */
/*                                               */
/* Sets the max size gadgets to greyed/ungreyed. */
/*                                               */
/* Parameters: ObjectId of the window which      */
/*             contains the icons.               */
/*                                               */
/*             0 to ungrey, any other value to   */
/*             to grey.                          */
/*************************************************/

static void choices_set_max_size_greyed(ObjectId window, int state)
{
  set_gadget_state(window, CD_HiMaxSizeLabl1, !!state);
  set_gadget_state(window, CD_HiMaxSizeLabl2, !!state);
  set_gadget_state(window, CD_HiMaxSize,      !!state);
}

/*************************************************/
/* choices_set_im_max_size()                     */
/*                                               */
/* Sets up the image max size field and greys or */
/* ungreys it as appropriate.                    */
/*                                               */
/* Parameters: The ObjectId of the window which  */
/*             contains the gadgets.             */
/*************************************************/

static void choices_set_im_max_size(ObjectId window)
{
  optionbutton_set_state(0, window, CD_HiImDontSize, !!new_choices->image_max_size);
  image_max_size_greyed = !new_choices->image_max_size;
  choices_set_im_max_size_greyed(window, image_max_size_greyed);

  choices_numberrange_set_value(0,
                                window,
                                CD_HiImMaxSize,
                                !new_choices->image_max_size ? 32 : new_choices->image_max_size/1024);
}

/*************************************************/
/* choices_set_im_max_size_greyed()              */
/*                                               */
/* Sets the image max size gadgets to greyed/    */
/* ungreyed.                                     */
/*                                               */
/* Parameters: ObjectId of the window which      */
/*             contains the icons.               */
/*                                               */
/*             0 to ungrey, any other value to   */
/*             to grey.                          */
/*************************************************/

static void choices_set_im_max_size_greyed(ObjectId window, int state)
{
  set_gadget_state(window, CD_HiImMaxSizeLabl1, !!state);
  set_gadget_state(window, CD_HiImMaxSizeLabl2, !!state);
  set_gadget_state(window, CD_HiImMaxSize,      !!state);
}

/*************************************************/
/* choices_set_timetype_field()                  */
/*                                               */
/* Sets the time field appropriately to reflect  */
/* the passed state.  Also ticks the appropriate */
/* item in the menu.                             */
/*                                               */
/* Parameters: timetype - see defined values in  */
/*                        choices.h              */
/*************************************************/

_kernel_oserror * choices_set_timetype_field(int timetype, ComponentId comp)
{
  _kernel_oserror * e;
  ObjectId          destwind;
  ObjectId          menu_id;
  char            * tempstring;

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_set_plugin_field: Called\n");
  #endif

  /* Find which window the encoding display is in */

  destwind = choices_find_component(comp);
  if (!destwind) return NULL;

  RetError(toolbox_create_object(0, "ChTime", &menu_id));

  RetError(choices_get_menu_entry_text("ChTime", timetype, &tempstring));

  RetError(choices_set_timetype_tick(timetype));

  e = choices_displayfield_set_value(0, destwind, comp, tempstring);

  free(tempstring);

  return NULL;
}

/*************************************************/
/* choices_set_timetype_tick()                   */
/*                                               */
/* Ticks the appropriate item in the ChTime menu */
/*                                               */
/* Parameters: timetype - see defined values in  */
/*                        choices.h              */
/*************************************************/

_kernel_oserror * choices_set_timetype_tick(int timetype)
{
  ObjectId menu_id;

  RetError(toolbox_create_object(0, "ChTime", &menu_id));

  if (time_ticked != -1)
  {
    RetError(menu_set_tick(0, menu_id, time_ticked, 0));
  }

  RetError(menu_set_tick(0, menu_id, timetype, 1));

  time_ticked = timetype;

  return NULL;
}

/*************************************************/
/* choices_timetype_m_button_handler()           */
/*                                               */
/* Displays the time units menu when the         */
/* appropriate toolaction button is pressed.     */
/*************************************************/

static int choices_timetype_m_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror         * e;
  int                       position[2];
  BBox                      box;
  WimpGetWindowStateBlock   state;
  ObjectId                  display;

  e = gadget_get_bbox(0, idb->self_id, idb->self_component, &box);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = window_get_wimp_handle(0, idb->self_id, &state.window_handle);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = wimp_get_window_state(&state);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Set open coordinates of display menu to top left of display button */

  position[0] = coords_x_toscreen(box.xmax, (WimpRedrawWindowBlock*)&state);
  position[1] = coords_y_toscreen(box.ymax, (WimpRedrawWindowBlock*)&state);

  switch(idb->self_component)
  {
    case CD_HiAgeTypeBt:
    choices_set_timetype_tick(history_expiry_units);
    break;

    case CD_HiImAgeTypeBt:
    choices_set_timetype_tick(image_expiry_units);
    break;
  }

  e = toolbox_create_object(0, "ChTime", &display);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  show_error_ret(toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                     display,
                                     Toolbox_ShowObject_TopLeft,
                                     position,
                                     idb->self_id,
                                     idb->self_component));

  return 1;
}

/*************************************************/
/* choices_timetype_m_click_handler()            */
/*                                               */
/* Handles clicks in the time units menu.        */
/* Translates the current expiry time to the new */
/* units and places the name of the units in the */
/* appropriate display field.                    */
/*************************************************/

static int choices_timetype_m_click_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  ObjectId window;
  int temptime;

  window = idb->parent_id;

  switch(idb->parent_component)
  {
    case CD_HiAgeTypeBt:
    {
      numberrange_get_value(0, window, CD_HiExpiryAge, &temptime);

      temptime = choices_typed_time_to_seconds(temptime, history_expiry_units);

      choices_set_timetype_field(idb->self_component, CD_HiAgeTypeDisp);

      history_expiry_units = idb->self_component;

      numberrange_set_value(0, window, CD_HiExpiryAge, choices_seconds_to_typed_time(temptime, history_expiry_units));
      numberrange_set_bounds(15, window, CD_HiExpiryAge, 1, choices_get_range_of_typed_time(history_expiry_units), 1, 0);

      break;
    }

    case CD_HiImAgeTypeBt:
    {
      numberrange_get_value(0, window, CD_HiImExpiryAge, &temptime);

      temptime = choices_typed_time_to_seconds(temptime, image_expiry_units);

      choices_set_timetype_field(idb->self_component, CD_HiImAgeTypeDisp);

      image_expiry_units = idb->self_component;

      numberrange_set_value(0, window, CD_HiImExpiryAge, choices_seconds_to_typed_time(temptime, image_expiry_units));
      numberrange_set_bounds(15, window, CD_HiImExpiryAge, 1, choices_get_range_of_typed_time(image_expiry_units), 1, 0);

      break;
    }
  }

  return 1;
}

/*************************************************/
/* choices_typed_time_to_seconds()               */
/*                                               */
/* Converts a time in the passed units to        */
/* seconds.                                      */
/*                                               */
/* Parameters: Time in passed units;             */
/*                                               */
/*             units which the time was passed   */
/*             as (defined in choices.h);        */
/*                                               */
/* Returns:    time in seconds.                  */
/*************************************************/

static int choices_typed_time_to_seconds(int time, int timetype)
{
  switch(timetype)
  {
    case CD_TimeMinutes: return time * 60;
    case CD_TimeHours:   return time * 60*60;
    case CD_TimeDays:    return time * 60*60*24;
    case CD_TimeWeeks:   return time * 60*60*24*7;
    case CD_TimeMonths:  return time * 60*60*24*7*4;
    default:             return 0;
  }
}

/*************************************************/
/* choices_clip_to_min_max()                     */
/*                                               */
/* Clips a number to a minimum and maximum value */
/*                                               */
/* Parameters: initial value;                    */
/*                                               */
/*             minimum;                          */
/*                                               */
/*             maximum.                          */
/*                                               */
/* Returns:    clipped value.                    */
/*************************************************/

int choices_clip_to_min_max(int value, int min, int max)
{
  return value > max ? max : (value < min ? min : value);
}

/*************************************************/
/* choices_seconds_to_typed_time()               */
/*                                               */
/* Converts a time in seconds to a time in the   */
/* passed units.                                 */
/*                                               */
/* Parameters: time in seconds;                  */
/*                                               */
/*             the units to return time in as    */
/*             defined in choices.h;             */
/*                                               */
/* Returns:    the time in the passed units.     */
/*************************************************/

static int choices_seconds_to_typed_time(int secs, int timetype)
{
  switch(timetype)
  {
    case CD_TimeMinutes: return choices_clip_to_min_max(secs/(60),           1, 60);
    case CD_TimeHours:   return choices_clip_to_min_max(secs/(60*60),        1, 24);
    case CD_TimeDays:    return choices_clip_to_min_max(secs/(60*60*24),     1, 7);
    case CD_TimeWeeks:   return choices_clip_to_min_max(secs/(60*60*24*7),   1, 4);
    case CD_TimeMonths:  return choices_clip_to_min_max(secs/(60*60*24*7*4), 1, 12);
    default:             return 1;
  }
}

/*************************************************/
/* choices_return_appropriate_timetype()         */
/*                                               */
/* Given a time in seconds returns the most      */
/* appropriate set of units to display that time */
/* in.                                           */
/*                                               */
/* Parameters: time in seconds.                  */
/*                                               */
/* Returns:    the type of the appropriate units */
/*             as defined in choices.h           */
/*************************************************/

static int choices_return_appropriate_timetype(int seconds)
{
  if (seconds <= 60*60)        return CD_TimeMinutes;
  if (seconds <= 60*60*24)     return CD_TimeHours;
  if (seconds <= 60*60*24*7)   return CD_TimeDays;
  if (seconds <= 60*60*24*7*4) return CD_TimeWeeks;

  return CD_TimeMonths;
}

/*************************************************/
/* choices_get_range_of_typed_time()             */
/*                                               */
/* Returns the max acceptable value of the       */
/* passed time units type.                       */
/*                                               */
/* Parameters: the type of time units. (defined  */
/*             in choices.h)                     */
/*                                               */
/* Returns:    the maximum acceptable value.     */
/*************************************************/

static int choices_get_range_of_typed_time(int timetype)
{
  switch(timetype)
  {
    case CD_TimeMinutes: return 60;
    case CD_TimeHours:   return 24;
    case CD_TimeDays:    return 7;
    case CD_TimeWeeks:   return 4;
    case CD_TimeMonths:  return 12;
    default:             return 0;
  }
}

/*************************************************/
/* choices_set_tables_greyed()                   */
/*                                               */
/* Sets the table border gadgets to greyed/      */
/* ungreyed.                                     */
/*                                               */
/* Parameters: ObjectId of the window which      */
/*             contains the icons.               */
/*                                               */
/*             0 to ungrey, any other value to   */
/*             to grey.                          */
/*************************************************/

static void choices_set_tables_greyed(int state, ObjectId window)
{
  set_gadget_state(window, CD_TabInnerBordLabl, !!state);
  set_gadget_state(window, CD_TabInnerBordDisp, !!state);
  set_gadget_state(window, CD_TabInnerBordBt,   !!state);
  set_gadget_state(window, CD_TabOuterBordLabl, !!state);
  set_gadget_state(window, CD_TabOuterBordDisp, !!state);
  set_gadget_state(window, CD_TabOuterBordBt,   !!state);
}

/*************************************************/
/* choices_set_table_border_field()              */
/*                                               */
/* Sets the passed display display field         */
/* appropriately to reflect the passed state.    */
/*************************************************/

static _kernel_oserror * choices_set_table_border_field(ObjectId obj, ComponentId comp, int state)
{
  _kernel_oserror *e;
  char *tempstring;

  #ifdef TRACE
    if (tl & (1u<<29)) Printf("choices_set_table_border_field: Called\n");
  #endif

  RetError(choices_get_menu_entry_text("ChTabBord", state, &tempstring));

  e = choices_displayfield_set_value(0, obj, comp, tempstring);

  free(tempstring);

  return e;
}

/*************************************************/
/* choices_bord_m_button_handler()               */
/*                                               */
/* Opens the bord menu with a value appropriate  */
/* to either the inner or outer table border.    */
/*************************************************/

static int choices_bord_m_button_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  _kernel_oserror *e;
  int  position[2];
  BBox box;
  WimpGetWindowStateBlock state;
  ObjectId display;
  int new_tick;

  e = gadget_get_bbox(0, idb->self_id, idb->self_component, &box);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = window_get_wimp_handle(0, idb->self_id, &state.window_handle);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  e = wimp_get_window_state(&state);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  /* Set open coordinates of display menu to top left of display button */

  position[0] = coords_x_toscreen(box.xmax, (WimpRedrawWindowBlock*)&state);
  position[1] = coords_y_toscreen(box.ymax, (WimpRedrawWindowBlock*)&state);

  e = toolbox_create_object(0, "ChTabBord", &display);
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

  switch(idb->self_component)
  {
    case CD_TabInnerBordBt: new_tick = new_choices->table_inner; break;
    case CD_TabOuterBordBt: new_tick = new_choices->table_outer; break;
    default: new_tick = 0; break;
  }

  if (bord_ticked != new_tick)
  {
    if (bord_ticked != -1) menu_set_tick(0, display, bord_ticked, 0);
    menu_set_tick(0, display, new_tick, 1);
    bord_ticked = new_tick;
  }

  show_error_ret(toolbox_show_object(Toolbox_ShowObject_AsMenu,
                                     display,
                                     Toolbox_ShowObject_TopLeft,
                                     position,
                                     idb->self_id,
                                     idb->self_component));

  return 1;
}

/*************************************************/
/* choices_bord_m_click_handler()                */
/*                                               */
/* Handles clicks in the border type menu.  Sets */
/* the associated display field and field in the */
/* new_choices structure.                        */
/*************************************************/

static int choices_bord_m_click_handler(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  if (bord_ticked != idb->self_component)
  {
    if (bord_ticked != -1) menu_set_tick(0, idb->self_id, bord_ticked, 0);
    menu_set_tick(0, idb->self_id, idb->self_component, 1);
    bord_ticked = idb->self_component;
  }

  switch(idb->parent_component)
  {
    case CD_TabInnerBordBt:
    new_choices->table_inner = idb->self_component;
    break;
    case CD_TabOuterBordBt:
    new_choices->table_outer = idb->self_component;
    break;
  }

  choices_set_table_border_field(idb->parent_id, idb->parent_component - 1, idb->self_component);

  return 1;
}

/*************************************************/
/* choices_get_menu_entry_text()                 */
/*                                               */
/* Gets a pointer to a piece of text acquired    */
/* from a menu.  The pointer is to a malloc      */
/* block and so must be freed after use.         */
/*                                               */
/* Parameters: pointer to the menu name          */
/*                                               */
/*             menu componentid                  */
/*                                               */
/*             pointer to char* to use for       */
/*             the malloc block                  */
/*                                               */
/* Assumes:    the menu is a shared object.      */
/*************************************************/

_kernel_oserror * choices_get_menu_entry_text(char * menuname, ComponentId compid, char ** tempstring)
{
  ObjectId menu_id;
  int      reqdsize;

  RetError(toolbox_create_object(0, menuname, &menu_id));

  RetError(menu_get_entry_text(0, menu_id, compid, NULL, 0, &reqdsize));

  *tempstring = malloc(reqdsize+1);
  if (!*tempstring) return make_no_memory_error(20);

  RetError(menu_get_entry_text(0, menu_id, compid, *tempstring, reqdsize+1, NULL));

  return NULL;
}