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

#include "Browser.h"
#include "Frames.h"
#include "Images.h"
#include "Reformat.h"
#include "Save.h" /* (For menu component IDs) */
#include "Toolbars.h"
#include "Windows.h"

#include "Menus.h"

/* Local statics */

static browser_data * main_menu_browser     = NULL;
static HStream      * main_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);

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

  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_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, URLbar,     ancestor->url_bar);
    menu_set_tick(0, idb->self_id, ButtonBar,  ancestor->button_bar);
    menu_set_tick(0, idb->self_id, StatusBar,  ancestor->status_bar);
    menu_set_tick(0, idb->self_id, FullScreen, ancestor->full_screen);
  }

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

  menu_set_tick(0, idb->self_id, Underline,    b->underlinelks);
  menu_set_tick(0, idb->self_id, DocumentCols, b->sourcecolours);
  menu_set_tick(0, idb->self_id, ShowImages,   b->displayimages);
  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) menu_set_fade(0, idb->self_id, SaveAsHTML, 0);
  else           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) menu_set_fade(0, idb->self_id, SaveSetAsHTML, 0);
  else             menu_set_fade(0, idb->self_id, SaveSetAsHTML, 1);

  /* If the browser has children, you can't print from it */

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

  #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.              */
/*                                               */
/* 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,CURLbar,choices.url_bar);
  menu_set_tick(0,idb->self_id,CButtonBar,choices.button_bar);
  menu_set_tick(0,idb->self_id,CStatusBar,choices.status_bar);

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

  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_show_main()                             */
/*                                               */
/* Called before the main menu is showed from a  */
/* document window.                              */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int menus_show_main(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;

  main_menu_browser     = b;
  main_menu_opened_over = browser_get_pointer_token(b, &info, NULL, NULL);

  return 1;
}

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

int menus_hide_main(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  main_menu_opened_over = NULL;
  main_menu_browser     = NULL;

  return 1;
}

/*************************************************/
/* menus_close_main_if_mine()                    */
/*                                               */
/* If a main 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_main_if_mine(browser_data * b)
{
  if (b && b == main_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 (!fixed.swapbars) 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 URLbar:    b->url_bar    = t, toolbars_set_presence(b, InternalTopLeft);    break;
    case ButtonBar: b->button_bar = t, toolbars_set_presence(b, InternalTopLeft);    break;
    case StatusBar: 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 (!fixed.swapbars)
  {
    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 Underline:
    {
      ChkError(browser_set_look(b, idb->self_id, t, -1, -1, -1));
    }
    break;

    case DocumentCols:
    {
      ChkError(browser_set_look(b, idb->self_id, -1, t, -1, -1));
    }
    break;

    case ShowImages:
    {
      ChkError(browser_set_look(b, idb->self_id, -1, -1, t, -1));
    }
    break;

    case ShowBackgrds:
    {
      ChkError(browser_set_look(b, idb->self_id, -1, -1, -1, !t));
    }
    break;

    case FullScreen:
    {
      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 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
}

/*************************************************/
/* menus_main_opened_over()                      */
/*                                               */
/* When the main menu is opened, it tries to see */
/* if the pointer was over a specific token.     */
/* If so, it records this information for        */
/* future use (see menus_show_main). 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_main_opened_over(void)
{
  return main_menu_opened_over;
}

/*************************************************/
/* menus_main_over_browser()                     */
/*                                               */
/* Companion function to menus_main_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_main_over_browser(void)
{
  return main_menu_browser;
}