/* 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   : Menus.c                                */
/*                                                 */
/* Purpose: Handle browser menus.                  */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 20-Nov-96: Created                     */
/***************************************************/

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

#include "swis.h"

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

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

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

#include "Browser.h"
#include "Choices.h"
#include "Fetch.h"    /* (For ISLINK macro) */
#include "FetchPage.h"
#include "Frames.h"
#include "Handlers.h"
#include "History.h"
#include "Hotlist.h"
#include "Images.h"
#include "ImgHistory.h"
#include "Multiuser.h"
#include "Object.h"
#include "OpenURL.h"
#include "Reformat.h"
#include "Save.h"     /* (For menu component IDs) */
#include "Toolbars.h"
#include "URLutils.h"
#include "Windows.h"

#include "Menus.h"

/* Local statics */

static browser_data * document_menu_browser     = NULL;
static HStream      * document_menu_opened_over = NULL;

/* Static function prototypes */

static void menus_toggle_bars       (IdBlock * idb);
static void menus_toggle_look       (IdBlock * idb);

static void menus_choices_bars      (IdBlock * idb);
static void menus_choices_other     (IdBlock * idb);

static void menus_history_selection (IdBlock * idb);
static void menus_main_selection    (IdBlock * idb);

/*************************************************/
/* menus_item_selected()                         */
/*                                               */
/* Following a Menu_Selection event from the     */
/* toolbox, work out what to do with it.         */
/*                                               */
/* Parameters are as for a standard Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_item_selected(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_item_selected: Called\n");
  #endif

  switch (idb->self_component)
  {
    case UtilsURLBar:
    case UtilsButtonBar:
    case UtilsStatusBar: menus_toggle_bars(idb);
    break;

    case UtilsFullScreen:
    case UtilsUnderlineLinks:
    case UtilsUseDocumentColours:
    case UtilsShowForegroundImages:
    case UtilsShowBackgroundImages: menus_toggle_look(idb);
    break;

    case ChoicesURLBar:
    case ChoicesButtonBar:
    case ChoicesStatusBar: menus_choices_bars(idb);
    break;

    case ChoicesFullScreen:
    case ChoicesUnderlineLinks:
    case ChoicesUseDocumentColours:
    case ChoicesShowForegroundImages:
    case ChoicesShowBackgroundImages: menus_choices_other(idb);
    break;

    case ChoicesSave: ChkError(save_save_choices(NULL));
    break;

    case HistoryEmptyLocal:
    case HistoryEmptyGlobal:
    case HistoryEmptyImage: menus_history_selection(idb);
    break;

    case MainLogOut:
    case MainCache: menus_main_selection(idb);
    break;

    default: return 0;
  }

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_item_selected: Successful\n");
  #endif

  return 1;
}

/*************************************************/
/* menus_toggle_tick()                           */
/*                                               */
/* Toggles the state of a given tick in a menu.  */
/*                                               */
/* Parameters: The object ID of the menu;        */
/*                                               */
/*             The component ID to alter.        */
/*                                               */
/* Returns:    1 if item is now ticked, else 0.  */
/*************************************************/

int menus_toggle_tick(ObjectId o, ComponentId c)
{
  int t = 0;

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

  menu_get_tick(0,o,c,&t);
  t ? (t = 0) : (t = 1);
  menu_set_tick(0,o,c,t);

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_toggle_tick: Successful\n");
  #endif

  return t;
}

/*************************************************/
/* menus_show_main()                             */
/*                                               */
/* Called before the main menu is showed from    */
/* from the icon bar.                            */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_show_main(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  unsigned int task_handle = 0;

  utils_get_task_handle(lookup_token("ProxyName:Acorn WebServe",0,0), &task_handle);

  if (!task_handle) menu_set_fade(0, idb->self_id, MainCache, 1);
  else              menu_set_fade(0, idb->self_id, MainCache, 0);

  #ifndef SINGLE_USER

    if (!logged_in)
    {
      menu_set_fade(0, idb->self_id, MainChoices,     1);
      menu_set_fade(0, idb->self_id, MainOpenURL,     1);
      menu_set_fade(0, idb->self_id, MainShowHotlist, 1);
      menu_set_fade(0, idb->self_id, MainHelp,        1);
      menu_set_fade(0, idb->self_id, MainLogOut,      1);
    }
    else
    {
      menu_set_fade(0, idb->self_id, MainChoices,     0);
      menu_set_fade(0, idb->self_id, MainOpenURL,     0);
      menu_set_fade(0, idb->self_id, MainShowHotlist, 0);
      menu_set_fade(0, idb->self_id, MainHelp,        0);
      menu_set_fade(0, idb->self_id, MainLogOut,      0);
    }

  #endif

  return 1;
}

/*************************************************/
/* menus_show_utils()                            */
/*                                               */
/* Called when the utils menu is about to be     */
/* opened from a main menu in a browser window.  */
/* Handles turning ticks on and off in items.    */
/* Parameters are as standard for a Toolbox      */
/* event handler                                 */
/*************************************************/

int menus_show_utils(int eventcode, ToolboxEvent * event, IdBlock * idb, void *handle)
{
  browser_data * b;

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

  /* Find the browser_data structure for the browser (ancestor) window */

  ChkError(toolbox_get_client_handle(0,idb->ancestor_id,(void *) &b));

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_show_utils: For ancestor client handle %p\n",(void *) b);
  #endif

  /* Set ticks according to the current toolbar status. */
  /* This should be for an ancestor window, and not for */
  /* any child frames. Similarly, the Full Screen       */
  /* state comes from the ancestor browser.             */

  {
    browser_data * ancestor = utils_ancestor(b);

    menu_set_tick(0, idb->self_id, UtilsURLBar,     ancestor->url_bar);
    menu_set_tick(0, idb->self_id, UtilsButtonBar,  ancestor->button_bar);
    menu_set_tick(0, idb->self_id, UtilsStatusBar,  ancestor->status_bar);
    menu_set_tick(0, idb->self_id, UtilsFullScreen, ancestor->full_screen);
  }

  /* Set ticks according to general options. These are specific */
  /* to each window.                                            */

  menu_set_tick(0, idb->self_id, UtilsUnderlineLinks,       b->underline_links);
  menu_set_tick(0, idb->self_id, UtilsUseDocumentColours,   b->use_source_cols);
  menu_set_tick(0, idb->self_id, UtilsShowForegroundImages, b->show_foreground);
  menu_set_tick(0, idb->self_id, UtilsShowBackgroundImages, b->show_background);

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_show_utils: Successful\n");
  #endif

  return 1;
}

/*************************************************/
/* menus_show_file()                             */
/*                                               */
/* Called when the File submenu is about to be   */
/* opened from a browser window's main menu.     */
/* Deals with greying of entries as appropriate. */
/*************************************************/

int menus_show_file(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;
  browser_data * parent;
  browser_data * ancestor;
  char         * url;

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

  /* Find the browser_data structure for the browser (ancestor) window */

  ChkError(toolbox_get_client_handle(0,idb->ancestor_id,(void *) &b));

  if (!b) return 1;

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_show_file: For ancestor client handle %p\n",(void *) b);
  #endif

  parent = utils_parent(b);
  ancestor = utils_ancestor(b);
  if (ancestor == b) ancestor = NULL;

  /* If there is HTML source present, unfade the Save entry - else fade it out */

  if (b->source) menu_set_fade(0, idb->self_id, FileSaveFrame, 0);
  else           menu_set_fade(0, idb->self_id, FileSaveFrame, 1);

  /* Similarly, for a parent */

  if (parent && parent->source) menu_set_fade(0, idb->self_id, FileSaveParent, 0);
  else                          menu_set_fade(0, idb->self_id, FileSaveParent, 1);

  /* Lastly, the ancestor */

  if (ancestor && ancestor->source) menu_set_fade(0, idb->self_id, FileSaveAncestor, 0);
  else                              menu_set_fade(0, idb->self_id, FileSaveAncestor, 1);

  /* If the browser has children or no line list, you can't print from it */

  if (b->nchildren || !b->cell || !b->cell->nlines) menu_set_fade(0, idb->self_id, FilePrint, 1);
  else                                              menu_set_fade(0, idb->self_id, FilePrint, 0);

  /* Do we have a current location? */

  url = browser_current_url(b);

  if (url && *url) menu_set_fade(0, idb->self_id, FileSaveFrameLocation, 0);
  else             menu_set_fade(0, idb->self_id, FileSaveFrameLocation, 1);

  /* Does the parent have a current location? */

  url = parent ? browser_current_url(parent) : NULL;

  if (url && *url) menu_set_fade(0, idb->self_id, FileSaveParentLocation, 0);
  else             menu_set_fade(0, idb->self_id, FileSaveParentLocation, 1);

  /* Does the ancestor have a current location? */

  url = ancestor ? browser_current_url(ancestor) : NULL;

  if (url && *url) menu_set_fade(0, idb->self_id, FileSaveAncestorLocation, 0);
  else             menu_set_fade(0, idb->self_id, FileSaveAncestorLocation, 1);

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_show_file: Successful\n");
  #endif

  return 1;
}

/*************************************************/
/* menus_show_navigate()                         */
/*                                               */
/* Called when the Navigate submenu is about to  */
/* be opened from a browser window's main menu.  */
/* Deals with greying of entries as appropriate. */
/*************************************************/

int menus_show_navigate(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;

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

  /* Find the browser_data structure for the browser (ancestor) window */

  ChkError(toolbox_get_client_handle(0,idb->ancestor_id,(void *) &b));

  if (!b) return 1;

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_show_navigate: For ancestor client handle %p\n",(void *) b);
  #endif

  /* Can only stop if there's activity of some sort */

  if (
       (b->anim_handler    && !b->anim_drift)      ||
       (b->meta_refresh_at && b->meta_refresh_url)
     )
     menu_set_fade(0, idb->self_id, NavigateStopAllFetches, 0);

  else menu_set_fade(0, idb->self_id, NavigateStopAllFetches, 1);

  /* Can only go back if we're not at the start of the history */
  /* and there's a history to go into.                         */

  if (!history_can_go_backwards(b)) menu_set_fade(0, idb->self_id, NavigateBack,    1);
  else                              menu_set_fade(0, idb->self_id, NavigateBack,    0);

  /* Can only go forward if we're in the history somewhere */

  if (!history_can_go_forwards(b))  menu_set_fade(0, idb->self_id, NavigateForward, 1);
  else                              menu_set_fade(0, idb->self_id, NavigateForward, 0);

  /* Only need to load images if delayed image loading is set  */
  /* in the local browser flags, or plain backgrounds are set. */

  if (
       (
         !b->show_foreground &&
         b->displayed != Display_External_Image
       )
       ||
       !b->show_background
     )
     menu_set_fade(0, idb->self_id, NavigateLoadAllImages, 0);

  else menu_set_fade(0, idb->self_id, NavigateLoadAllImages, 1);

  /* Finished */

  return 1;
}

/*************************************************/
/* menus_show_export()                           */
/*                                               */
/* Called before the Export menu is showed.      */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_show_export(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;
  HStream      * over;

  ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b));

  if (!is_known_browser(b))
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Browser %p is unknown in menus_show_export",
              b);

      ChkError(&erb);

    #endif

    return 0;
  }

  /* The rest are supported, but may not be valid depending on */
  /* what the menu was opened over.                            */

  over = menus_document_opened_over();

  /* Export a link */

  if (!over || !ISLINK(over)) menu_set_fade(0, idb->self_id, ExportLink, 1);
  else                        menu_set_fade(0, idb->self_id, ExportLink, 0);

  /* Export a foreground image */

  if (
       !over ||
       !
       (
         (over->style & IMG) ||
         (
           over->tagno == TAG_INPUT &&
           HtmlINPUTtype(over) == inputtype_IMAGE
         )
         ||
         (
           ISOBJECT(over) &&
           object_token_is_image(b, over)
         )
       )
     )
     menu_set_fade(0, idb->self_id, ExportPicture, 1);

  else
  {
    if (image_token_can_be_saved_as_sprite(b, over)) menu_set_fade(0, idb->self_id, ExportPicture, 0);
    else                                             menu_set_fade(0, idb->self_id, ExportPicture, 1);
  }

  /* Export a background image */

  if (b && b->background_image < 0) menu_set_fade(0, idb->self_id, ExportBackground, 1);
  else
  {
    if (image_token_can_be_saved_as_sprite(b, NULL)) menu_set_fade(0, idb->self_id, ExportBackground, 0);
    else                                             menu_set_fade(0, idb->self_id, ExportBackground, 1);
  }

  /* Export page as Draw or a text file */

  if (b && !b->nchildren && b->cell && b->cell->nlines)
  {
    menu_set_fade(0, idb->self_id, ExportAsText, 0);
    menu_set_fade(0, idb->self_id, ExportAsDraw, 0);
  }
  else
  {
    menu_set_fade(0, idb->self_id, ExportAsText, 1);
    menu_set_fade(0, idb->self_id, ExportAsDraw, 1);
  }

  /* Finished */

  return 1;
}

/*************************************************/
/* menus_show_choices()                          */
/*                                               */
/* Called when the choices menu is about to be   */
/* opened from the icon bar menu. Handles        */
/* turning ticks on and off depending on the     */
/* Messages file's choices entries.              */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_show_choices(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_show_choices: Called\n");
  #endif

  menu_set_tick(0, idb->self_id, ChoicesURLBar,               choices.url_bar);
  menu_set_tick(0, idb->self_id, ChoicesButtonBar,            choices.button_bar);
  menu_set_tick(0, idb->self_id, ChoicesStatusBar,            choices.status_bar);

  menu_set_tick(0, idb->self_id, ChoicesUnderlineLinks,       choices.underline_links);
  menu_set_tick(0, idb->self_id, ChoicesUseDocumentColours,   choices.use_source_cols);
  menu_set_tick(0, idb->self_id, ChoicesShowForegroundImages, choices.show_foreground);
  menu_set_tick(0, idb->self_id, ChoicesShowBackgroundImages, choices.show_background);

  menu_set_tick(0, idb->self_id, ChoicesFullScreen,           choices.full_screen);

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_show_choices: Successful\n");
  #endif

  return 1;
}

/*************************************************/
/* menus_show_document()                         */
/*                                               */
/* Called before the document menu is showed     */
/* from a browser window.                        */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_show_document(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  WimpGetPointerInfoBlock   info;
  browser_data            * b;

  ChkError(wimp_get_pointer_info(&info));
  ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b));

  if (!is_known_browser(b)) return 0;

  document_menu_browser     = b;
  document_menu_opened_over = browser_get_pointer_token(b, &info, NULL, NULL);

  return 1;
}

/*************************************************/
/* menus_show_history()                          */
/*                                               */
/* Called before the History menu is displayed.  */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_show_history(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;

  ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b));

  if (!is_known_browser(b) || history_empty(b))
  {
    menu_set_fade(0, idb->self_id, HistorySaveLocal,  1);
    menu_set_fade(0, idb->self_id, HistoryEmptyLocal, 1);
  }
  else
  {
    menu_set_fade(0, idb->self_id, HistorySaveLocal,  0);
    menu_set_fade(0, idb->self_id, HistoryEmptyLocal, 0);
  }

  if (history_empty(NULL))
  {
    menu_set_fade(0, idb->self_id, HistorySaveGlobal,  1);
    menu_set_fade(0, idb->self_id, HistoryEmptyGlobal, 1);
  }
  else
  {
    menu_set_fade(0, idb->self_id, HistorySaveGlobal,  0);
    menu_set_fade(0, idb->self_id, HistoryEmptyGlobal, 0);
  }

  if (imghistory_empty()) menu_set_fade(0, idb->self_id, HistoryEmptyImage, 1);
  else                    menu_set_fade(0, idb->self_id, HistoryEmptyImage, 0);

  return 1;
}

/*************************************************/
/* menus_hide_document()                         */
/*                                               */
/* Called before the document menu is hidden.    */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_hide_document(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  document_menu_opened_over = NULL;
  document_menu_browser     = NULL;

  /* Pass on - others (eg Encoding.c) may be interested */
  return 0;
}

/*************************************************/
/* menus_close_document_if_mine()                */
/*                                               */
/* If a document menu is open and its origin was */
/* the given browser, close it.                  */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the menu.             */
/*                                               */
/* Returns:    1 if the menu was closed, else 0. */
/*************************************************/

int menus_close_document_if_mine(browser_data * b)
{
  if (b && b == document_menu_browser)
  {
    _swix(Wimp_CreateMenu,
          _IN(1),

          -1);

    return 1;
  }

  return 0;
}

/*************************************************/
/* menus_toggle_bars()                           */
/*                                               */
/* Called when one of the Utils menu items that  */
/* turns info/toolbars on and off is selected.   */
/*                                               */
/* The item is ticked or unticked as necessary   */
/* and the relevant toolbar turned on or off.    */
/*                                               */
/* Parameters: Pointer to an ID block, as given  */
/*             by the Toolbox when it raises an  */
/*             event.                            */
/*************************************************/

static void menus_toggle_bars(IdBlock * idb)
{
  int            t = 0, height;
  browser_data * b;

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

  /* Find the browser_data structure for the browser (ancestor) window */

  ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b));

  /* Bars are only attached to the ancestor browser_data struct, */
  /* not to any child frames. So want to point b to this.        */

  b = utils_ancestor(b);

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_toggle_bars: For ancestor client handle %p\n",(void *) b);
  #endif

  /* Swap the menu's tick state */

  t = menus_toggle_tick(idb->self_id, idb->self_component);

  /* Remember height of top toolbar */

  if (!controls.swap_bars) height = toolbars_button_height(b) + toolbars_url_height(b);
  else                     height = toolbars_status_height(b);

  /* For URL and Button bars, need to move the page contents up and down, */
  /* so remember the total bar height before removing or adding one of    */
  /* those bars. For the status bar it doesn't matter.                    */

  switch (idb->self_component)
  {
    case UtilsURLBar:    b->url_bar    = t, toolbars_set_presence(b, InternalTopLeft);    break;
    case UtilsButtonBar: b->button_bar = t, toolbars_set_presence(b, InternalTopLeft);    break;
    case UtilsStatusBar: b->status_bar = t, toolbars_set_presence(b, InternalBottomLeft); break;

    #ifdef TRACE
      default:
      {
        _kernel_oserror e;
        e.errnum = Utils_Error_Custom_Normal;
        strncpy(e.errmess,"Toolbar toggle handle not understood in menus_toggle_bars.",252);
        show_error_cont(&e);
      }
    #endif
  }

  /* Move any data inside the page as necessary */

  if (!controls.swap_bars)
  {
    ChkError(reformat_shift_vertically(b, 0, b->cell->nlines - 1, height - toolbars_url_height(b) - toolbars_button_height(b)));
  }
  else
  {
    ChkError(reformat_shift_vertically(b, 0, b->cell->nlines - 1, height - toolbars_status_height(b)));
  }

  ChkError(reformat_check_extent(b));

  /* If there are any frames on the page, resize them */

  if (b->nchildren)
  {
    WimpGetWindowStateBlock state;

    state.window_handle = b->window_handle;
    ChkError(wimp_get_window_state(&state));

    frames_resize_frameset(b, &state.visible_area);
  }

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_toggle_bars: Successful\n");
  #endif
}

/*************************************************/
/* menus_toggle_look()                           */
/*                                               */
/* Called when one of the Utils menu items not   */
/* related to toolbars is selected.              */
/*                                               */
/* The item is ticked or unticked as necessary   */
/* and the relevant visual aspect of the browser */
/* window turned on or off.                      */
/*                                               */
/* Any child frames at this frame level or       */
/* higher will inherit the new characteristics.  */
/*                                               */
/* Parameters: Pointer to an ID block, as given  */
/*             by the Toolbox when it raises an  */
/*             event.                            */
/*************************************************/

static void menus_toggle_look(IdBlock * idb)
{
  int            t = 0;
  ObjectId       browser;
  browser_data * b;
  browser_data * ancestor;

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

  /* Find the browser_data structure for the browser (ancestor) window */

  browser = idb->ancestor_id;

  ChkError(toolbox_get_client_handle(0, browser, (void *) &b));

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_toggle_look: For ancestor client handle %p\n",(void *) b);
  #endif

  ancestor = utils_ancestor(b);

  /* Swap the menu's tick state */

  t = menus_toggle_tick(idb->self_id, idb->self_component);

  /* Reflect changes in ancestor and children */

  /* (Un)Set the relevant flag inside the browser_data structure */

  switch (idb->self_component)
  {
    case UtilsUnderlineLinks:       ChkError(browser_set_look(b, idb->self_id, t, -1, -1, -1)); break;
    case UtilsUseDocumentColours:   ChkError(browser_set_look(b, idb->self_id, -1, t, -1, -1)); break;
    case UtilsShowForegroundImages: ChkError(browser_set_look(b, idb->self_id, -1, -1, t, -1)); break;
    case UtilsShowBackgroundImages: ChkError(browser_set_look(b, idb->self_id, -1, -1, -1, t)); break;

    case UtilsFullScreen:
    {
      browser_data * ancestor = utils_ancestor(b);

      /* Can only go Full Screen for ancestor objects */

      ancestor->full_screen = t;

      /* This opens full screen, remembering the previous window size */

      ChkError(windows_open_full_screen(ancestor,
                                        t,
                                        !t,
                                        choices.v_scroll,
                                        choices.h_scroll));

      /* Reformat the page */

      ChkError(reformat_format_from(ancestor, -1, 1, -1));
    }
    break;

    /* Report error for default case in TRACE builds as a warning */
    /* to the programmer only.                                    */

    #ifdef TRACE

      default:
      {
        _kernel_oserror e;

        if (b->ancestor) return; /* Avoid giving error for all the child windows */

        e.errnum = Utils_Error_Custom_Normal;
        strncpy(e.errmess,"Menu selection not understood in menus_toggle_look.",252);
        show_error_cont(&e);
      }

    #endif
  }

  /* Update the ancestor buttons */

  ChkError(toolbars_set_button_states(ancestor));

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_toggle_look: Successful\n");
  #endif
}

/*************************************************/
/* menus_choices_bars()                          */
/*                                               */
/* Called when one of the Choices menu items     */
/* relating to toolbars is selected. Toggles the */
/* menu item's tick and (un)sets the relevant    */
/* bit in the global Choices.                    */
/*                                               */
/* Parameters: Pointer to an ID block, as given  */
/*             by the Toolbox when it raises an  */
/*             event.                            */
/*************************************************/

static void menus_choices_bars(IdBlock * idb)
{
  int t;

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

  t = menus_toggle_tick(idb->self_id,idb->self_component);

  switch (idb->self_component)
  {
    case ChoicesURLBar:    choices.url_bar    = t;
    break;
    case ChoicesButtonBar: choices.button_bar = t;
    break;
    case ChoicesStatusBar: choices.status_bar = t;
    break;

    #ifdef TRACE
      default:
      {
        _kernel_oserror e;
        e.errnum = Utils_Error_Custom_Normal;
        strncpy(e.errmess,"Toolbar toggle handle not understood in menus_choices_bars.",252);
        show_error_cont(&e);
      }
    #endif
  }

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_choices_bars: Successful\n");
  #endif
}

/*************************************************/
/* menus_choices_other()                         */
/*                                               */
/* Called when one of the Choices menu items     */
/* relating to something other than toolbars is  */
/* selected. Toggles the menu item's tick and    */
/* (un)sets the relevant bit in the global       */
/* Choices.                                      */
/*                                               */
/* Parameters: Pointer to an ID block, as given  */
/*             by the Toolbox when it raises an  */
/*             event.                            */
/*************************************************/

static void menus_choices_other(IdBlock * idb)
{
  int t;

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

  t = menus_toggle_tick(idb->self_id,idb->self_component);

  switch (idb->self_component)
  {
    case ChoicesUnderlineLinks:       choices.underline_links = t; break;
    case ChoicesUseDocumentColours:   choices.use_source_cols = t; break;
    case ChoicesShowForegroundImages: choices.show_foreground = t; break;
    case ChoicesShowBackgroundImages: choices.show_background = t; break;

    case ChoicesFullScreen:           choices.full_screen     = t; break;

    #ifdef TRACE
      default:
      {
        _kernel_oserror e;
        e.errnum = Utils_Error_Custom_Normal;
        strncpy(e.errmess,"Menu selection item not understood in menus_choices_other.",252);
        show_error_cont(&e);
      }
    #endif
  }

  toolbars_set_all_button_states();

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_choices_other: Successful\n");
  #endif
}

/*************************************************/
/* menus_history_selection()                     */
/*                                               */
/* Called when one of the History menu items     */
/* is selected.                                  */
/*                                               */
/* Parameters: Pointer to an ID block, as given  */
/*             by the Toolbox when it raises an  */
/*             event.                            */
/*************************************************/

static void menus_history_selection(IdBlock * idb)
{
  browser_data * b;

  if (toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b)) b = NULL;
  if (!is_known_browser(b)) b = NULL;

  switch (idb->self_component)
  {
    // Not really that useful? Amount of memory tied up in local
    //�histories is typically pretty small.

    case HistoryEmptyLocal:
    {
      if (!b) return;

      history_remove(b, NULL);

      toolbars_set_all_button_states();
    }
    break;

    // Yikes! Perhaps we should have some confirmation here...

    case HistoryEmptyGlobal:
    {
      history_limit(0);

      toolbars_set_all_button_states();
    }
    break;

    case HistoryEmptyImage:
    {
      imghistory_limit(0);
    }
    break;
  }

  return;
}

/*************************************************/
/* menus_main_selection()                        */
/*                                               */
/* Called when one of the main menu items is     */
/* selected.                                     */
/*                                               */
/* Parameters: Pointer to an ID block, as given  */
/*             by the Toolbox when it raises an  */
/*             event.                            */
/*************************************************/

static void menus_main_selection(IdBlock * idb)
{
  switch (idb->self_component)
  {
    case MainCache:
    {
      unsigned int handle = 0;
      WimpMessage  msg;

      /* First, get WebServe's task handle */

      utils_get_task_handle(lookup_token("ProxyName:Acorn WebServe",0,0), &handle);

      #ifdef TRACE

        if (!handle)
        {
          /* Didn't find WebServe, so complain and exit */

          erb.errnum = Utils_Error_Custom_Message;

          StrNCpy0(erb.errmess, "WebServe is not present");

          show_error_ret(&erb);
        }

      #else

        if (!handle)
        {
          /* Didn't find WebServe, so exit */

          return;
        }

      #endif

      /* If WebServe is present, send the message */

      msg.hdr.size        = 32;
      msg.hdr.sender      = task_handle;
      msg.hdr.my_ref      = 0;
      msg.hdr.your_ref    = 0;
      msg.hdr.action_code = Wimp_MAppControl;

      msg.data.app_control.version = 1;
      msg.data.app_control.flags   = Wimp_MAppControl_ImmediateAction;
      msg.data.app_control.reason  = Wimp_MAppControl_Configure;

      ChkError(wimp_send_message(Wimp_EUserMessage, &msg, handle, -1, NULL));
    }
    break;

    case MainLogOut:
    {
      browser_data * current;

      /* Close all open browser windows */

      while (last_browser)
      {
        /* Only close ancestor browsers */

        current = last_browser;

        while (current && current->ancestor) current = current->previous;

        if (current) handle_close_browser(0, NULL, NULL, current);
      }

      /* Close the OpenURL dialogue, in case it's open... */

      openurl_close(0, 0);

      #ifndef REMOTE_HOTLIST

        /* Make sure the hotlist window is closed */

        show_error_ret(hotlist_close());

      #endif

      /* Make sure the Choices are closed too */

      show_error_ret(choices_close());

      /* Finally, log the user out and bring back the */
      /* log in prompt.                               */

      show_error_ret(multiuser_logout());
      ChkError(multiuser_login());
    }
    break;
  }
}

/*************************************************/
/* menus_document_opened_over()                  */
/*                                               */
/* When the document menu is opened, this sees   */
/* if the pointer was over a specific token.     */
/* If so, it records this information for        */
/* future use (see menus_show_document). This    */
/* function returns that token address, or NULL  */
/* if the menu isn't open / no token could be    */
/* determined.                                   */
/*                                               */
/* Returns:    Pointer to the token the pointer  */
/*             was over when the main menu was   */
/*             opened, or NULL if the menu is no */
/*             longer open / no token could be   */
/*             determined when it was opened.    */
/*************************************************/

HStream * menus_document_opened_over(void)
{
  return document_menu_opened_over;
}

/*************************************************/
/* menus_document_over_browser()                 */
/*                                               */
/* Companion function to                         */
/* menus_document_opened_over, so see that for   */
/* details.                                      */
/*                                               */
/* Returns:    Pointer to the browser_data       */
/*             struct representing the browser   */
/*             the main menu was opened from, if */
/*             any (NULL if not).                */
/*************************************************/

browser_data * menus_document_over_browser(void)
{
  return document_menu_browser;
}

/*************************************************/
/* menus_help_from_help_string()                 */
/*                                               */
/* Visit a URL encoded in the selected menu      */
/* item's Interactive Help string.               */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_help_from_help_string(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;
  char           help[Limits_URL];

  /* Find the browser_data structure for the browser (ancestor) window */

  if (toolbox_get_client_handle(0,idb->ancestor_id,(void *) &b)) b = NULL;

  if (!is_known_browser(b)) b = NULL;
  else                      b = utils_ancestor(b);

  if (
       menu_get_entry_help_message(0,
                                   idb->self_id,
                                   idb->self_component,
                                   help,
                                   sizeof(help),
                                   NULL)
     )
     return 0;

  if (b) ChkError(fetchpage_new(b, help, 1, 0));
  else   ChkError(windows_create_browser(help, NULL, NULL, NULL, 0));

  return 1;
}

/*************************************************/
/* menus_help_release_notes()                    */
/*                                               */
/* Visit a URL encoded in the selected menu      */
/* item's Interactive Help string, appended by   */
/* a filename based on the browser version.      */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_help_release_notes(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;
  char         * p;
  char           help[Limits_URL];

  /* Find the browser_data structure for the browser (ancestor) window */

  if (toolbox_get_client_handle(0,idb->ancestor_id,(void *) &b)) b = NULL;

  if (!is_known_browser(b)) b = NULL;
  else                      b = utils_ancestor(b);

  /* Get the Help string */

  if (
       menu_get_entry_help_message(0,
                                   idb->self_id,
                                   idb->self_component,
                                   help,
                                   sizeof(help),
                                   NULL)
     )
     return 0;

  /* Append the version */

  *tokens = '\0';
  lookup_token("Version",1,0);

  if (strlen(tokens) + strlen(help) + sizeof(".html") > sizeof(help)) return 0;

  /* Convert spaces to underscores and dots to commas */

  p = tokens;

  while (*p)
  {
    if (*p == ' ') *p = '_';
    if (*p == '.') *p = ',';

    p++;
  }

  /* Append '.html' */

  strcat(tokens, ".html");
  strcat(help, tokens);

  /* Fetch the page */

  if (b) ChkError(fetchpage_new(b, help, 1, 0));
  else   ChkError(windows_create_browser(help, NULL, NULL, NULL, 0));

  return 1;
}

/*************************************************/
/* menus_help_about_page()                       */
/*                                               */
/* Go to the 'about:' page.                      */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_help_about_page(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;

  /* Find the browser_data structure for the browser (ancestor) window */

  if (toolbox_get_client_handle(0,idb->ancestor_id,(void *) &b)) b = NULL;

  if (!is_known_browser(b)) b = NULL;
  else                      b = utils_ancestor(b);

  if (b) ChkError(fetchpage_new(b, AboutMethod, 1, 0));
  else   ChkError(windows_create_browser(AboutMethod, NULL, NULL, NULL, 0));

  return 1;
}

/*************************************************/
/* menus_help_miscellaneous()                    */
/*                                               */
/* Go to a page looked up from Controls under    */
/* the name 'JumpToxx' where 'xx' is a 2 digit   */
/* hex number obtained from the given parameter. */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             in which to fetch the item, or    */
/*             NULL for a new window;            */
/*                                               */
/*             Number of token to look for (see  */
/*             above).                           */
/*************************************************/

void menus_help_miscellaneous(browser_data * b, int which)
{
  char   name[9];
  char * url;

  /* Sanity check */

  if (which < 0 || which > 0x7f) return;

  /* Build the Choices token name */

  sprintf(name, "JumpTo%02x", which);

  /* Read it - exit if not found */

  url = lookup_control(name, 1, NULL);
  if (!strcmp(url, "!")) return;

  /* Fetch the URL */

  if (b) ChkError(fetchpage_new(b, url, 1, 0));
  else   ChkError(windows_create_browser(url, NULL, NULL, NULL, 0));

  return;
}