/* 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 "svcprint.h"
#include "Global.h"
#include "FromROSLib.h"
#include "Utils.h"

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

#include "Choices.h"

/* Locals */

char *consonant = "bcdfghjklmnpqrstvwxyz";
char *vowel = "aeiou";

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

static int        save_ticked = -1;
static int        disp_ticked = -1;

static int        choices_modechanged = 0;

global_choices  * new_choices       = NULL;
ObjectId          choices_windowid  = 0;

/* 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 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_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 _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);

/*************************************************/
/* 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;

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

  if (choices_windowid) return 1;

  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));
  }

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

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

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

  e = toolbox_create_object(0, "ChSub0", subwindows + CDSubBrowseDefaults); if (e) subwindows[CDSubBrowseDefaults] = NULL;
  e = toolbox_create_object(0, "ChSub1", subwindows + CDSubWindows);        if (e) subwindows[CDSubWindows]        = NULL;
  e = toolbox_create_object(0, "ChSub2", subwindows + CDSubColours);        if (e) subwindows[CDSubColours]        = NULL;
  e = toolbox_create_object(0, "ChSub3", subwindows + CDSubFonts);          if (e) subwindows[CDSubFonts]          = NULL;
  e = toolbox_create_object(0, "ChSub4", subwindows + CDSubHotlist);        if (e) subwindows[CDSubHotlist]        = NULL;
  e = toolbox_create_object(0, "ChSub5", subwindows + CDSubHistory);        if (e) subwindows[CDSubHistory]        = NULL;
  e = toolbox_create_object(0, "ChSub6", subwindows + CDSubSub6);           if (e) subwindows[CDSubSub6]           = NULL;
  e = toolbox_create_object(0, "ChSub7", subwindows + CDSubSub7);           if (e) subwindows[CDSubSub7]           = NULL;
  e = toolbox_create_object(0, "ChSub8", subwindows + CDSubSub8);           if (e) subwindows[CDSubSub8]           = NULL;

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

  ChkError(radiobutton_set_state(0, idb->self_id, CDBrowseDefaults, 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.                                               */

  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(subwindows[CDSubBrowseDefaults], OptionButton_StateChanged, choices_option_state_handler, NULL);

  if (!e) e = event_register_wimp_handler(*(subwindows + CDSubColours), Wimp_ERedrawWindow, choices_redraw_fakepage_handler, NULL);
  if (!e) e = event_register_wimp_handler(idb->self_id, Wimp_EOpenWindow, choices_open_choice_window, NULL);
  /* 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(subwindows[CDSubBrowseDefaults], OptionButton_StateChanged, choices_option_state_handler, NULL);

    event_deregister_wimp_handler(*(subwindows + CDSubColours), Wimp_ERedrawWindow, choices_redraw_fakepage_handler, NULL);
    event_deregister_wimp_handler(idb->self_id, Wimp_EOpenWindow, choices_open_choice_window, NULL);

    ChkError(e);
  }

  return 1;
}

/*************************************************/
/* 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;
}

/*************************************************/
/* 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;

  /* Close subwindow */

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

  /* Delete all subwindow objects */

  choices_delete_subwindows();

  /* 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(subwindows[CDSubBrowseDefaults], OptionButton_StateChanged, choices_option_state_handler, NULL);

  event_deregister_wimp_handler(*(subwindows + CDSubColours), Wimp_ERedrawWindow, choices_redraw_fakepage_handler, NULL);
  event_deregister_wimp_handler(idb->self_id, Wimp_EOpenWindow, choices_open_choice_window, NULL);

  choices_windowid = 0;

  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)
{
  ObjectId window;

  switch(idb->self_component)
  {
    case CDBrowseDefaults: window = CDSubBrowseDefaults; break;
    case CDWindows:        window = CDSubWindows;        break;
    case CDColours:        window = CDSubColours;        break;
    case CDFonts:          window = CDSubFonts;          break;
    case CDHotlist:        window = CDSubHotlist;        break;
    case CDHistory:        window = CDSubHistory;        break;
    case CDSub6:           window = CDSubSub6;           break;
    case CDSub7:           window = CDSubSub7;           break;
    case CDSub8:           window = CDSubSub8;           break;

    default:               window = CDNoSubwindows;      break;
  }

  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)
{
  /* No error handling here, as we want to allow items to be missing */

  if (subwindows[CDSubBrowseDefaults])
  {
    writablefield_set_value(0, subwindows[CDSubBrowseDefaults], CD_Homepage, new_choices->home_page);

    optionbutton_set_state(0, subwindows[CDSubBrowseDefaults], CD_UnderlineLinks, new_choices->underline_links);
    optionbutton_set_state(0, subwindows[CDSubBrowseDefaults], CD_UseDocColours,  new_choices->use_source_cols);
    optionbutton_set_state(0, subwindows[CDSubBrowseDefaults], CD_ShowForeground, new_choices->show_foreground);
    optionbutton_set_state(0, subwindows[CDSubBrowseDefaults], CD_ShowBackground, new_choices->show_background);

    /* Get encoding name */

    choices_set_encoding_field();
  }

  if (subwindows[CDSubWindows])
  {
    optionbutton_set_state(0, subwindows[CDSubWindows], CD_URLBar,     new_choices->url_bar);
    optionbutton_set_state(0, subwindows[CDSubWindows], CD_StatusBar,  new_choices->status_bar);
    optionbutton_set_state(0, subwindows[CDSubWindows], CD_ButtonBar,  new_choices->button_bar);
    optionbutton_set_state(0, subwindows[CDSubWindows], CD_FullScreen, new_choices->full_screen);
  }

  if (subwindows[CDSubColours])
  {
    choices_colour_set_component(subwindows[CDSubColours], CD_BackColour, new_choices->background_colour);
    choices_colour_set_component(subwindows[CDSubColours], CD_TextColour, new_choices->text_colour);
    choices_colour_set_component(subwindows[CDSubColours], CD_LinkColour, new_choices->link_colour);
    choices_colour_set_component(subwindows[CDSubColours], CD_UsedColour, new_choices->used_colour);
    choices_colour_set_component(subwindows[CDSubColours], CD_FolwColour, new_choices->followed_colour);
    choices_colour_set_component(subwindows[CDSubColours], CD_SlctColour, new_choices->selected_colour);
  }

  if (subwindows[CDSubHotlist])
  {
    numberrange_set_value(0, subwindows[CDSubHotlist], CD_HlAutoOpen,   new_choices->auto_open_delay);
    numberrange_set_value(0, subwindows[CDSubHotlist], CD_HlAutoScroll, new_choices->auto_scroll_delay);

    choices_set_display_field(subwindows[CDSubHotlist], CD_HlDispDisp, new_choices->hotlist_show);

    choices_set_save_field(subwindows[CDSubHotlist], CD_HlSaveDisp, new_choices->save_hotlist);
  }

  if (subwindows[CDSubHistory])
  {
    numberrange_set_value(0, subwindows[CDSubHistory], CD_HiLines,   new_choices->v_hist_size);
    numberrange_set_value(0, subwindows[CDSubHistory], CD_HiMaxSize, new_choices->g_hist_size);

    choices_set_display_field(subwindows[CDSubHistory], CD_HiDispDisp, new_choices->show_urls);

    choices_set_save_field(subwindows[CDSubHistory], CD_HiSaveDisp, new_choices->save_history);
  }

  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;
  char                    * tempstring;
  int                       state;
  int                       reqsize;

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

  if (subwindows[CDSubBrowseDefaults])
  {
    e = writablefield_get_value(0, subwindows[CDSubBrowseDefaults], CD_Homepage, NULL, 0, &reqsize);

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

      if (tempstring)
      {
        e = writablefield_get_value(0, subwindows[CDSubBrowseDefaults], 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 (subwindows[CDSubWindows])
  {
    e = optionbutton_get_state(0, subwindows[CDSubWindows], CD_URLBar,     &state);
    if (!e) new_choices->url_bar     = !!state;

    e = optionbutton_get_state(0, subwindows[CDSubWindows], CD_StatusBar,  &state);
    if (!e) new_choices->status_bar  = !!state;

    e = optionbutton_get_state(0, subwindows[CDSubWindows], CD_ButtonBar,  &state);
    if (!e) new_choices->button_bar  = !!state;

    e = optionbutton_get_state(0, subwindows[CDSubWindows], CD_FullScreen, &state);
    if (!e) new_choices->full_screen = !!state;
  }

  if (subwindows[CDSubHotlist])
  {
    numberrange_get_value(0, subwindows[CDSubHotlist], CD_HlAutoOpen,   &new_choices->auto_open_delay);
    numberrange_get_value(0, subwindows[CDSubHotlist], CD_HlAutoScroll, &new_choices->auto_scroll_delay);
  }

  if (subwindows[CDSubHistory])
  {
    numberrange_get_value(0, subwindows[CDSubHistory], CD_HiLines,   &new_choices->v_hist_size);
    numberrange_get_value(0, subwindows[CDSubHistory], CD_HiMaxSize, &new_choices->g_hist_size);
  }

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

  /* Update browsers */

  b = last_browser;

  while (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.       */

    if (!b->use_source_cols)
    {
      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));
    }

    b = b->previous;
  }

  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;
  e = choices_get_contents();
  if (e) show_error_ret(e);

  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)
{
  _kernel_oserror *e = NULL;

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

  choices_set_contents();
  if (e) show_error_ret(e);

  return 1;
}

/*************************************************/
/* 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;
  e = choices_get_contents();
  if (e)
  {
    show_error_ret(e);
    return 1;
  }

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

  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;
  ComponentId compid;
  char *textptr;
  int sizereqd;

  /* 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 = displayfield_set_value(0, subwindows[CDSubBrowseDefaults], CD_EncodingDisply, textptr);

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

    RetError(displayfield_set_value(0, subwindows[CDSubBrowseDefaults], 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 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;
  ObjectId menu_id;
  int reqdsize;
  char *tempstring;

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

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

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

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

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

  e = 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;
  ObjectId menu_id;
  int reqdsize;
  char *tempstring;

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

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

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

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

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

  e = 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_crap()                          */
/*                                               */
/* 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_crap(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)];
    ministring[1] = vowel[rand()%sizeof(vowel)];

    if (rand()%2)
    {
      ministring[2] = consonant[rand()%sizeof(consonant)];
      ministring[3] = vowel[rand()%sizeof(vowel)];
      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;
  int                     fontheight = 0;
  int                     gotfont    = 0;
  BBox                    icon_coords, fbox;
  fm_face                 h = 0;

  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);

  /* 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);
        ChkError(fm_font_box(h, &fbox));
        fm_lose_font(NULL, h);
        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.                */

        #ifdef ChoiceUseSelected
          ptsize = ((gadsize * 1000 / 5) / fontheight);
        #else
          ptsize = ((gadsize * 1000 / 4) / fontheight);
        #endif

        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_crap(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, 1));

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

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

      ChkError(choices_write_crap(h, xtarget, ypos, icon_coords.xmin+xstart, new_choices->text_colour, new_choices->background_colour, &xtarget, 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_crap(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, 3));

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

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

      ChkError(choices_write_crap(h, xtarget, ypos, icon_coords.xmin+xstart, new_choices->text_colour, new_choices->background_colour, &xtarget, 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_crap(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, 5));

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

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

      ChkError(choices_write_crap(h, xtarget, ypos, icon_coords.xmin+xstart, new_choices->text_colour, new_choices->background_colour, &xtarget, 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_crap(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, 7));

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

      /* Display the selected link line of the fake page display */
      #ifdef ChoiceUseSelected
        ChkError(choices_write_crap(h, xtarget, ypos, icon_coords.xmin+xstart, new_choices->text_colour, new_choices->background_colour, &xtarget, 12));
        ChkError(choices_draw_string(h, "selected", xtarget, ypos, new_choices->underline_links, new_choices->selected_colour, new_choices->background_colour, &xtarget));
        ChkError(choices_write_crap(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, 9));

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

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

      ChkError(choices_write_crap(h, xtarget, ypos, icon_coords.xmax, new_choices->text_colour, new_choices->background_colour, &xtarget, 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;
  ChkError(optionbutton_get_state(0, idb->self_id, idb->self_component, &state));

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

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

      if (subwindows[CDSubColours])
      {
        button_set_flags(0, subwindows[CDSubColours], 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;
  }

  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), 0, -1);

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

  return 1;
}