/* 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: Menu-related functions for the browser */
/* 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 "TBEvents.h"
#include "Utils.h"

#include "Frames.h"
#include "Images.h"
#include "Reformat.h"
#include "Toolbars.h"
#include "Windows.h"

#include "Menus.h"

/* Static function prototypes */

static void menus_toggle_bars   (IdBlock * idb);

static void menus_toggle_look   (IdBlock * idb);
static void menus_toggle_look_r (browser_data * b, ComponentId c, int t);

static void menus_choices_bars  (IdBlock * idb);
static void menus_choices_other (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 URLbar:
    case ButtonBar:
    case StatusBar: menus_toggle_bars(idb);
    break;

    case FullScreen:
    case Underline:
    case DocumentCols:
    case ShowImages:
    case ShowBackgrds: menus_toggle_look(idb);
    break;

    case CURLbar:
    case CButtonBar:
    case CStatusBar: menus_choices_bars(idb);
    break;

    case DelayImages:
    case Backgrounds:
    case UnderlineLks:
    case OverrideCols:
    case CFullScreen: menus_choices_other(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

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

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

  return t;
}

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

    if (b->ancestor) ancestor = b->ancestor;
    else             ancestor = b;

    ChkTrace(menu_set_tick(0, idb->self_id, URLbar,     ancestor->url_bar));
    ChkTrace(menu_set_tick(0, idb->self_id, ButtonBar,  ancestor->button_bar));
    ChkTrace(menu_set_tick(0, idb->self_id, StatusBar,  ancestor->status_bar));

    ChkTrace(menu_set_tick(0, idb->self_id, FullScreen, ancestor->full_screen));
  }

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

  ChkTrace(menu_set_tick(0,idb->self_id,Underline,b->underlinelks));
  ChkTrace(menu_set_tick(0,idb->self_id,DocumentCols,b->sourcecolours));
  ChkTrace(menu_set_tick(0,idb->self_id,ShowImages,b->displayimages));
  ChkTrace(menu_set_tick(0,idb->self_id,ShowBackgrds,!b->plainback));

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

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

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

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

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

  /* If there is a parent with HTML source, unfade the Save frames entry; else */
  /* fade it out.                                                              */

  if (b->ancestor) ChkTrace(menu_set_fade(0, idb->self_id, SaveSetAsHTML, 0));
  else             ChkTrace(menu_set_fade(0, idb->self_id, SaveSetAsHTML, 1));

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

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

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

  ChkTrace(menu_set_tick(0,idb->self_id,CURLbar,choices.url_bar));
  ChkTrace(menu_set_tick(0,idb->self_id,CButtonBar,choices.button_bar));
  ChkTrace(menu_set_tick(0,idb->self_id,CStatusBar,choices.status_bar));

  ChkTrace(menu_set_tick(0,idb->self_id,UnderlineLks,choices.underlinelks));
  ChkTrace(menu_set_tick(0,idb->self_id,OverrideCols,choices.overridecols));
  ChkTrace(menu_set_tick(0,idb->self_id,DelayImages,choices.delayimages));
  ChkTrace(menu_set_tick(0,idb->self_id,Backgrounds,choices.displaybgs));

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

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

  return 1;
}

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

  if (b->ancestor) b = b->ancestor;

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

  height = toolbars_url_height(b) + toolbars_button_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 URLbar:    b->url_bar    = t, toolbars_set_presence(idb->ancestor_id, InternalTopLeft);    break;
    case ButtonBar: b->button_bar = t, toolbars_set_presence(idb->ancestor_id, InternalTopLeft);    break;
    case StatusBar: b->status_bar = t, toolbars_set_presence(idb->ancestor_id, 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
  }

  #ifdef TRACE
    if (tl & (1u<<4)) Printf("menus_toggle_bars: Successful, and exitting through reformat_shift_vertically()\n");
  #endif

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

  ChkError(reformat_shift_vertically(b, 0, b->cell->nlines - 1, height - toolbars_url_height(b) - toolbars_button_height(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, (WimpOpenWindowBlock *) &state);
  }
}

/*************************************************/
/* 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 = b->ancestor;
  if (!ancestor) ancestor = b;

  /* Swap the menu's tick state */

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

  /* Reflect changes in ancestor and children */

  menus_toggle_look_r(ancestor, idb->self_component, t);

  /* Update the ancestor buttons */

  toolbars_set_button_states(ancestor);

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

/*************************************************/
/* menus_toggle_look_r()                         */
/*                                               */
/* Recursive back-end to menus_toggle_took.      */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             which is the menu's ancestor for  */
/*             a top level call from             */
/*             menus_toggle_look, or else a      */
/*             child structure if calling itself */
/*             recursively;                      */
/*                                               */
/*             Component ID selected in menu;    */
/*                                               */
/*             1 if the item is now on, 0 if it  */
/*             is now off.                       */
/*************************************************/

static void menus_toggle_look_r(browser_data * b, ComponentId c, int t)
{
  int child;

  /* Scan the child tree */

  if (b->nchildren)
  {
    for (child = 0; child < b->nchildren; child++)
    {
      menus_toggle_look_r(b->children[child], c, t);
    }
  }

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

  switch (c)
  {
    case Underline: b->underlinelks = t;
    break;
    case DocumentCols: b->sourcecolours = t;
    break;
    case ShowImages:
    {
      b->displayimages = t;

      if (t) image_restart_fetches(b);
    }
    break;
    case ShowBackgrds: b->plainback = !t;
    break;
    case FullScreen:
    {
      /* Can only go Full Screen for ancestor objects */

      if (b->ancestor) return; /* (So exit if this is a child) */

      b->full_screen = t;

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

      windows_open_full_screen(b,
                               t,
                               !t,
                               find_behind(b->window_handle),
                               choices.v_scroll,
                               choices.h_scroll);

      /* Reformat the page */

      reformat_format_from(b, -1, 1, -1);
    }
    break;

    #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
  }

  /* Redraw the browser window to reflect the changes */

  {
    WimpGetWindowStateBlock s;
    s.window_handle = b->window_handle;
    ChkError(wimp_get_window_state(&s));

    ChkError(wimp_force_redraw(-1,
                               s.visible_area.xmin,
                               s.visible_area.ymin + toolbars_status_height(b),
                               s.visible_area.xmax,
                               s.visible_area.ymax - toolbars_button_height(b) - toolbars_url_height(b)));
  }
}

/*************************************************/
/* 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 CURLbar:choices.url_bar = t;
    break;
    case CButtonBar:choices.button_bar = t;
    break;
    case CStatusBar: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 OverrideCols: choices.overridecols = t;
    break;
    case UnderlineLks: choices.underlinelks = t;
    break;
    case Backgrounds: choices.displaybgs = t;
    break;
    case DelayImages: choices.delayimages = t;
    break;
    case CFullScreen: 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
}

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