/* menu.c: general menu widget
   Copyright (c) 2001-2016 Philip Kendall
   Copyright (c) 2015-2018 Sergio Baldoví

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

   Author contact information:

   E-mail: philip-fuse@shadowmagic.org.uk

*/

#include "config.h"

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "debugger/debugger.h"
#include "event.h"
#include "fuse.h"
#include "keyboard.h"
#include "machine.h"
#include "machines/specplus3.h"
#include "menu.h"
#include "peripherals/dck.h"
#include "peripherals/disk/beta.h"
#include "peripherals/joystick.h"
#include "psg.h"
#include "rzx.h"
#include "screenshot.h"
#include "settings.h"
#include "snapshot.h"
#include "tape.h"
#include "ui/uidisplay.h"
#include "utils.h"
#include "widget_internals.h"
#include "widget.h"

widget_menu_entry *menu;
static size_t highlight_line = 0;
static size_t count;
static int *current_settings[ 16 ];

#define GET_SET_KEY_FUNCTIONS( which ) \
\
static void \
set_key_for_button_ ## which ( int action ) \
{ \
  *current_settings[ which ] = action; \
  widget_end_all( WIDGET_FINISHED_OK ); \
} \
\
static const char* \
get_key_name_for_button_ ## which ( void ) \
{ \
  return keyboard_key_text( *current_settings[ which ] ); \
}

GET_SET_KEY_FUNCTIONS( 1 )
GET_SET_KEY_FUNCTIONS( 2 )
GET_SET_KEY_FUNCTIONS( 3 )
GET_SET_KEY_FUNCTIONS( 4 )
GET_SET_KEY_FUNCTIONS( 5 )
#ifdef USE_JOYSTICK
GET_SET_KEY_FUNCTIONS( 6 )
GET_SET_KEY_FUNCTIONS( 7 )
GET_SET_KEY_FUNCTIONS( 8 )
#ifndef GEKKO
GET_SET_KEY_FUNCTIONS( 9 )
GET_SET_KEY_FUNCTIONS( 10 )
GET_SET_KEY_FUNCTIONS( 11 )
GET_SET_KEY_FUNCTIONS( 12 )
GET_SET_KEY_FUNCTIONS( 13 )
GET_SET_KEY_FUNCTIONS( 14 )
GET_SET_KEY_FUNCTIONS( 15 )
#endif  /* #ifndef GEKKO */
#endif  /* #ifdef USE_JOYSTICK */

#define SUBMENU_KEY_SELECTIONS( which ) \
\
static widget_menu_entry submenu_select_number_for_button_ ## which [] = { \
  { "Select a key" }, \
  { "\0120\011", INPUT_KEY_0, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_0 }, \
  { "\0121\011", INPUT_KEY_1, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_1 }, \
  { "\0122\011", INPUT_KEY_2, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_2 }, \
  { "\0123\011", INPUT_KEY_3, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_3 }, \
  { "\0124\011", INPUT_KEY_4, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_4 }, \
  { "\0125\011", INPUT_KEY_5, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_5 }, \
  { "\0126\011", INPUT_KEY_6, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_6 }, \
  { "\0127\011", INPUT_KEY_7, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_7 }, \
  { "\0128\011", INPUT_KEY_8, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_8 }, \
  { "\0129\011", INPUT_KEY_9, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_9 }, \
  { NULL } \
}; \
\
static widget_menu_entry submenu_select_letters1_for_button_ ## which [] = { \
  { "Select a key" }, \
  { "\012A\011", INPUT_KEY_a, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_a }, \
  { "\012B\011", INPUT_KEY_b, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_b }, \
  { "\012C\011", INPUT_KEY_c, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_c }, \
  { "\012D\011", INPUT_KEY_d, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_d }, \
  { "\012E\011", INPUT_KEY_e, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_e }, \
  { "\012F\011", INPUT_KEY_f, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_f }, \
  { "\012G\011", INPUT_KEY_g, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_g }, \
  { "\012H\011", INPUT_KEY_h, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_h }, \
  { "\012I\011", INPUT_KEY_i, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_i }, \
  { "\012J\011", INPUT_KEY_j, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_j }, \
  { "\012K\011", INPUT_KEY_k, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_k }, \
  { "\012L\011", INPUT_KEY_l, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_l }, \
  { "\012M\011", INPUT_KEY_m, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_m }, \
  { NULL } \
}; \
\
static widget_menu_entry submenu_select_letters2_for_button_ ## which [] = { \
  { "Select a key" }, \
  { "\012N\011", INPUT_KEY_n, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_n }, \
  { "\012O\011", INPUT_KEY_o, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_o }, \
  { "\012P\011", INPUT_KEY_p, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_p }, \
  { "\012Q\011", INPUT_KEY_q, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_q }, \
  { "\012R\011", INPUT_KEY_r, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_r }, \
  { "\012S\011", INPUT_KEY_s, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_s }, \
  { "\012T\011", INPUT_KEY_t, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_t }, \
  { "\012U\011", INPUT_KEY_u, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_u }, \
  { "\012V\011", INPUT_KEY_v, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_v }, \
  { "\012W\011", INPUT_KEY_w, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_w }, \
  { "\012X\011", INPUT_KEY_x, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_x }, \
  { "\012Y\011", INPUT_KEY_y, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_y }, \
  { "\012Z\011", INPUT_KEY_z, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_z }, \
  { NULL } \
}; \
\
static widget_menu_entry submenu_select_key_for_button_ ## which [] = { \
  { "Select a key" }, \
  { "\012J\011oystick fire", INPUT_KEY_j, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_JOYSTICK_FIRE }, \
  { "\012N\011umbers...", INPUT_KEY_n, submenu_select_number_for_button_ ## which , NULL, NULL, 0 }, \
  { "\012A\011-M...", INPUT_KEY_a, submenu_select_letters1_for_button_ ## which , NULL, NULL, 0 }, \
  { "N-\012Z\011...", INPUT_KEY_z, submenu_select_letters2_for_button_ ## which , NULL, NULL, 0 }, \
  { "\012S\011pace", INPUT_KEY_s, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_space }, \
  { "\012E\011nter", INPUT_KEY_e, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_Enter }, \
  { "\012C\011aps Shift", INPUT_KEY_c, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_Caps }, \
  { "S\012y\011mbol Shift", INPUT_KEY_y, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_Symbol }, \
  { "N\012o\011thing", INPUT_KEY_o, NULL, set_key_for_button_ ## which, NULL, KEYBOARD_NONE }, \
  { NULL } \
};

SUBMENU_KEY_SELECTIONS( 1 )
SUBMENU_KEY_SELECTIONS( 2 )
SUBMENU_KEY_SELECTIONS( 3 )
SUBMENU_KEY_SELECTIONS( 4 )
SUBMENU_KEY_SELECTIONS( 5 )
#ifdef USE_JOYSTICK
SUBMENU_KEY_SELECTIONS( 6 )
SUBMENU_KEY_SELECTIONS( 7 )
SUBMENU_KEY_SELECTIONS( 8 )
#ifndef GEKKO
SUBMENU_KEY_SELECTIONS( 9 )
SUBMENU_KEY_SELECTIONS( 10 )
SUBMENU_KEY_SELECTIONS( 11 )
SUBMENU_KEY_SELECTIONS( 12 )
SUBMENU_KEY_SELECTIONS( 13 )
SUBMENU_KEY_SELECTIONS( 14 )
SUBMENU_KEY_SELECTIONS( 15 )
#endif  /* #ifndef GEKKO */
#endif  /* #ifdef USE_JOYSTICK */

#ifdef USE_JOYSTICK
static widget_menu_entry submenu_joystick_buttons[] = {
  { "Select joystick button" },
#ifndef GEKKO
  { "Button \0121\011", INPUT_KEY_1, submenu_select_key_for_button_1, NULL, get_key_name_for_button_1, 0 },
  { "Button \0122\011", INPUT_KEY_2, submenu_select_key_for_button_2, NULL, get_key_name_for_button_2, 0 },
  { "Button \0123\011", INPUT_KEY_3, submenu_select_key_for_button_3, NULL, get_key_name_for_button_3, 0 },
  { "Button \0124\011", INPUT_KEY_4, submenu_select_key_for_button_4, NULL, get_key_name_for_button_4, 0 },
  { "Button \0125\011", INPUT_KEY_5, submenu_select_key_for_button_5, NULL, get_key_name_for_button_5, 0 },
  { "Button \0126\011", INPUT_KEY_6, submenu_select_key_for_button_6, NULL, get_key_name_for_button_6, 0 },
  { "Button \0127\011", INPUT_KEY_7, submenu_select_key_for_button_7, NULL, get_key_name_for_button_7, 0 },
  { "Button \0128\011", INPUT_KEY_8, submenu_select_key_for_button_8, NULL, get_key_name_for_button_8, 0 },
  { "Button \0129\011", INPUT_KEY_9, submenu_select_key_for_button_9, NULL, get_key_name_for_button_9, 0 },
  { "Button 1\0120\011", INPUT_KEY_0, submenu_select_key_for_button_10, NULL, get_key_name_for_button_10, 0 },
  { "Button 11(\012a\011)", INPUT_KEY_a, submenu_select_key_for_button_11, NULL, get_key_name_for_button_11, 0 },
  { "Button 12(\012b\011)", INPUT_KEY_b, submenu_select_key_for_button_12, NULL, get_key_name_for_button_12, 0 },
  { "Button 13(\012c\011)", INPUT_KEY_c, submenu_select_key_for_button_13, NULL, get_key_name_for_button_13, 0 },
  { "Button 14(\012d\011)", INPUT_KEY_d, submenu_select_key_for_button_14, NULL, get_key_name_for_button_14, 0 },
  { "Button 15(\012e\011)", INPUT_KEY_e, submenu_select_key_for_button_15, NULL, get_key_name_for_button_15, 0 },
#else  /* #ifndef GEKKO */
  { "Button \0121\011", INPUT_KEY_1, submenu_select_key_for_button_1, NULL, get_key_name_for_button_1, 0 },
  { "Button \0122\011", INPUT_KEY_2, submenu_select_key_for_button_2, NULL, get_key_name_for_button_2, 0 },
  { "Button \012A\011", INPUT_KEY_a, submenu_select_key_for_button_3, NULL, get_key_name_for_button_3, 0 },
  { "Button \012B\011", INPUT_KEY_b, submenu_select_key_for_button_4, NULL, get_key_name_for_button_4, 0 },
  { "Button \012P\011lus", INPUT_KEY_p, submenu_select_key_for_button_5, NULL, get_key_name_for_button_5, 0 },
  { "Button \012M\011inus", INPUT_KEY_m, submenu_select_key_for_button_6, NULL, get_key_name_for_button_6, 0 },
  { "Button \012Z\011 on Nunchuck", INPUT_KEY_z, submenu_select_key_for_button_7, NULL, get_key_name_for_button_7, 0 },
  { "Button \012C\011 on Nunchuck", INPUT_KEY_c, submenu_select_key_for_button_8, NULL, get_key_name_for_button_8, 0 },
#endif  /* #ifndef GEKKO */
  { NULL }
};
#endif  /* #ifdef USE_JOYSTICK */

static widget_menu_entry submenu_keyboard_buttons[] = {
  { "Select keyboard key" },
  { "Button \012U\011p", INPUT_KEY_u, submenu_select_key_for_button_1, NULL, get_key_name_for_button_1, 0 },
  { "Button \012D\011own", INPUT_KEY_d, submenu_select_key_for_button_2, NULL, get_key_name_for_button_2, 0 },
  { "Button \012L\011eft", INPUT_KEY_l, submenu_select_key_for_button_3, NULL, get_key_name_for_button_3, 0 },
  { "Button \012R\011ight", INPUT_KEY_r, submenu_select_key_for_button_4, NULL,	get_key_name_for_button_4, 0 },
  { "Button \012F\011ire", INPUT_KEY_f, submenu_select_key_for_button_5, NULL, get_key_name_for_button_5, 0 },
  { NULL }
};

#define MAX_JOYSTICK_TYPES 8
/* joystick types + title of the window + NULL */
static widget_menu_entry submenu_types[ MAX_JOYSTICK_TYPES + 2 ];
static char joystick_names[ MAX_JOYSTICK_TYPES ][ 100 ];
void set_joystick_type( int action );

#define SUBMENU_DEVICE_SELECTIONS( device ) \
\
static widget_menu_entry submenu_type_and_mapping_for_ ## device [] = { \
  { "Select type or map buttons" }, \
  { "\012T\011ype", INPUT_KEY_t, submenu_types, NULL, NULL, 0 }, \
  { "\012B\011utton Mapping", INPUT_KEY_b, submenu_ ## device ## _buttons, NULL, NULL, 0 }, \
  { NULL } \
};

#ifdef USE_JOYSTICK
SUBMENU_DEVICE_SELECTIONS( joystick )
#endif  /* #ifdef USE_JOYSTICK */
SUBMENU_DEVICE_SELECTIONS( keyboard )

static void
print_items( void )
{
  int i;
  char buffer[128];
  size_t height = 24;
  int width = widget_calculate_menu_width(menu);
  int menu_left_edge_x = (DISPLAY_WIDTH_COLS/2-width/2)*8+1;

  for( i = 0; i < count; i++ ) {
    int colour;
    if( !menu[i+1].text[0] ) { height += 4; continue; }

    snprintf( buffer, sizeof (buffer), "%s", menu[i+1].text );
    colour = menu[i+1].inactive ?
	     WIDGET_COLOUR_DISABLED :
	     WIDGET_COLOUR_FOREGROUND;

    if( i == highlight_line ) {
      widget_rectangle( menu_left_edge_x, height, width*8-2, 1*8,
                        WIDGET_COLOUR_HIGHLIGHT );
    } else {
      widget_rectangle( menu_left_edge_x, height, width*8-2, 1*8,
                        WIDGET_COLOUR_BACKGROUND );
    }

    widget_printstring( menu_left_edge_x+8, height, colour, buffer );

    if( menu[i+1].submenu ) {
      widget_draw_submenu_arrow(DISPLAY_BORDER_ASPECT_WIDTH+menu_left_edge_x+
                                width*8-9, i*8+49, colour);
    }

    if( menu[i+1].detail ) {
      size_t detail_width = widget_stringwidth( menu[i+1].detail() );
      int x = menu_left_edge_x + (width-1)*8 - detail_width - 2;
      widget_printstring( x, height, WIDGET_COLOUR_DISABLED, menu[i+1].detail() );
    }

    height += 8;
  }

  widget_display_lines( 2, count + 2 );
}

int widget_menu_draw( void *data )
{
  widget_menu_entry *ptr;
  size_t menu_entries, width, height = 0;
  int menu_left_edge_x;
  char buffer[128];
  highlight_line = 0;

  menu = (widget_menu_entry*)data;

  /* How many menu items do we have? */
  for( ptr = &menu[1]; ptr->text; ptr++ )
    height += ptr->text[0] ? 2 : 1;
  menu_entries = ptr - &menu[1];
  count = menu_entries;
  width = widget_calculate_menu_width(menu);
  menu_left_edge_x = DISPLAY_WIDTH_COLS/2-width/2;
  widget_dialog_with_border( menu_left_edge_x, 2, width, 2 + height / 2 );

  snprintf( buffer, sizeof( buffer ), "%s", menu->text );
  widget_printstring( menu_left_edge_x*8+2, 16, WIDGET_COLOUR_TITLE, buffer );

  print_items();
  return 0;
}

void
widget_menu_keyhandler( input_key key )
{
  widget_menu_entry *ptr;
  int new_highlight_line = 0;
  int cursor_pressed = 0;

  switch( key ) {

#if 0
  case INPUT_KEY_Resize:	/* Fake keypress used on window resize */
    widget_menu_draw( menu );
    break;
#endif
    
  case INPUT_KEY_Escape:
  case INPUT_JOYSTICK_FIRE_2:
    widget_end_widget( WIDGET_FINISHED_CANCEL );
    return;

  case INPUT_KEY_Return:
  case INPUT_KEY_KP_Enter:
  case INPUT_JOYSTICK_FIRE_1:
    ptr=&menu[1 + highlight_line];
    if(!ptr->inactive) {
      if( ptr->submenu ) {
        widget_do_menu( ptr->submenu );
      } else {
        ptr->callback( ptr->action );
      }
    }
    return;

  case INPUT_KEY_Up:
  case INPUT_KEY_7:
  case INPUT_JOYSTICK_UP:
    if ( highlight_line ) {
      new_highlight_line = highlight_line - 1;
      cursor_pressed = 1;
    }
    break;

  case INPUT_KEY_Down:
  case INPUT_KEY_6:
  case INPUT_JOYSTICK_DOWN:
    if ( highlight_line + 1 < (ptrdiff_t)count ) {
      new_highlight_line = highlight_line + 1;
      cursor_pressed = 1;
    }
    break;

  case INPUT_KEY_Home:
    new_highlight_line = 0;
    ptr = &menu[1 + new_highlight_line];
    while( new_highlight_line < (ptrdiff_t)count - 1 && ptr->inactive ) {
      new_highlight_line += 1;
      ptr = &menu[1 + new_highlight_line];
    }
    cursor_pressed = 1;
    break;

  case INPUT_KEY_End:
    new_highlight_line = (ptrdiff_t)count - 1;
    ptr = &menu[1 + new_highlight_line];
    while( new_highlight_line > 0 && ptr->inactive ) {
      new_highlight_line -= 1;
      ptr = &menu[1 + new_highlight_line];
    }
    cursor_pressed = 1;
    break;

  default:	/* Keep gcc happy */
    break;

  }

  if( cursor_pressed ) {
    highlight_line = new_highlight_line;
    print_items();
    return;
  }

  for( ptr=&menu[1]; ptr->text; ptr++ ) {
    if( !ptr->inactive && key == ptr->key ) {

      if( ptr->submenu ) {
        widget_do_menu( ptr->submenu );
      } else {
        ptr->callback( ptr->action );
      }

      break;
    }
  }
}

/* General callbacks */

scaler_type
menu_get_scaler( scaler_available_fn selector )
{
  size_t count, i;
  const char *options[ SCALER_NUM ];
  widget_select_t info;
  int error;

  count = 0; info.current = 0;

  for( i = 0; i < SCALER_NUM; i++ )
    if( selector( i ) ) {
      if( current_scaler == i ) info.current = count;
      options[ count++ ] = scaler_name( i );
    }

  info.title = "Select scaler";
  info.options = options;
  info.count = count;
  info.finish_all = 1;

  error = widget_do_select( &info );
  if( error ) return SCALER_NUM;

  if( info.result == -1 ) return SCALER_NUM;

  for( i = 0; i < SCALER_NUM; i++ )
    if( selector( i ) && !info.result-- ) return i;

  ui_error( UI_ERROR_ERROR, "widget_select_scaler: ran out of scalers" );
  fuse_abort();
}

void
menu_file_exit( int action )
{
  static int menu_exit_open = 0;

  if( menu_exit_open ) return;

  menu_exit_open = 1;
  if( widget_do_query( "Exit Fuse?" ) || !widget_query.confirm ) {
    menu_exit_open = 0;
    return;
  }

  if( menu_check_media_changed() ) {
    menu_exit_open = 0;
    return;
  }

  fuse_exiting = 1;
  menu_exit_open = 0;

  widget_end_all( WIDGET_FINISHED_OK );
}

void
menu_options_general( int action )
{
  widget_do_general();
}

void
menu_options_media( int action )
{
  widget_do_media();
}

void
menu_options_peripherals_general( int action )
{
  widget_do_peripherals_general();
}

void
menu_options_peripherals_sound( int action )
{
  widget_do_peripherals_sound();
}

void
menu_options_peripherals_disk( int action )
{
  widget_do_peripherals_disk();
}

void
menu_options_sound( int action )
{
  widget_do_sound();
}

void
menu_options_rzx( int action )
{
  widget_do_rzx();
}

void
menu_options_movie( int action )
{
  widget_do_movie();
}

void
menu_options_diskoptions( int action )
{
  widget_do_diskoptions();
}

void
menu_options_joysticks_select( int action )
{
  int error = 0;
  int i;

  switch( action - 1 ) {

#ifdef USE_JOYSTICK
  case 0:
    current_settings[ 0 ] = &( settings_current.joystick_1_output );
    current_settings[ 1 ] = &( settings_current.joystick_1_fire_1 );
    current_settings[ 2 ] = &( settings_current.joystick_1_fire_2 );
    current_settings[ 3 ] = &( settings_current.joystick_1_fire_3 );
    current_settings[ 4 ] = &( settings_current.joystick_1_fire_4 );
    current_settings[ 5 ] = &( settings_current.joystick_1_fire_5 );
    current_settings[ 6 ] = &( settings_current.joystick_1_fire_6 );
    current_settings[ 7 ] = &( settings_current.joystick_1_fire_7 );
    current_settings[ 8 ] = &( settings_current.joystick_1_fire_8 );
    current_settings[ 9 ] = &( settings_current.joystick_1_fire_9 );
    current_settings[ 10 ] = &( settings_current.joystick_1_fire_10 );
    current_settings[ 11 ] = &( settings_current.joystick_1_fire_11 );
    current_settings[ 12 ] = &( settings_current.joystick_1_fire_12 );
    current_settings[ 13 ] = &( settings_current.joystick_1_fire_13 );
    current_settings[ 14 ] = &( settings_current.joystick_1_fire_14 );
    current_settings[ 15 ] = &( settings_current.joystick_1_fire_15 );
    submenu_type_and_mapping_for_joystick[ 1 ].detail = menu_joystick_1_detail;
    break;
  case 1:
    current_settings[ 0 ] = &( settings_current.joystick_2_output );
    current_settings[ 1 ] = &( settings_current.joystick_2_fire_1 );
    current_settings[ 2 ] = &( settings_current.joystick_2_fire_2 );
    current_settings[ 3 ] = &( settings_current.joystick_2_fire_3 );
    current_settings[ 4 ] = &( settings_current.joystick_2_fire_4 );
    current_settings[ 5 ] = &( settings_current.joystick_2_fire_5 );
    current_settings[ 6 ] = &( settings_current.joystick_2_fire_6 );
    current_settings[ 7 ] = &( settings_current.joystick_2_fire_7 );
    current_settings[ 8 ] = &( settings_current.joystick_2_fire_8 );
    current_settings[ 9 ] = &( settings_current.joystick_2_fire_9 );
    current_settings[ 10 ] = &( settings_current.joystick_2_fire_10 );
    current_settings[ 11 ] = &( settings_current.joystick_2_fire_11 );
    current_settings[ 12 ] = &( settings_current.joystick_2_fire_12 );
    current_settings[ 13 ] = &( settings_current.joystick_2_fire_13 );
    current_settings[ 14 ] = &( settings_current.joystick_2_fire_14 );
    current_settings[ 15 ] = &( settings_current.joystick_2_fire_15 );
    submenu_type_and_mapping_for_joystick[ 1 ].detail = menu_joystick_2_detail;
    break;
#endif  /* #ifdef USE_JOYSTICK */

  case JOYSTICK_KEYBOARD:
    current_settings[ 0 ] = &( settings_current.joystick_keyboard_output );
    current_settings[ 1 ] = &( settings_current.joystick_keyboard_up );
    current_settings[ 2 ] = &( settings_current.joystick_keyboard_down );
    current_settings[ 3 ] = &( settings_current.joystick_keyboard_left );
    current_settings[ 4 ] = &( settings_current.joystick_keyboard_right );
    current_settings[ 5 ] = &( settings_current.joystick_keyboard_fire );
    submenu_type_and_mapping_for_keyboard[ 1 ].detail = menu_keyboard_joystick_detail;
    break;
  }

  /* Populate joystick names */
  if( JOYSTICK_TYPE_COUNT > MAX_JOYSTICK_TYPES )
    ui_error( UI_ERROR_ERROR, "Not all joystick types are displayed" );

  submenu_types[ 0 ].text = "Select joystick type";
  for( i = 0; ( i < JOYSTICK_TYPE_COUNT ) && ( i < MAX_JOYSTICK_TYPES ); i++ ) {
    char shortcut[ 2 ] = { 'A' + i, '\0' };
    snprintf( ( char * ) joystick_names[ i ], 100, "\012%s\011 %s", shortcut,
              joystick_name[ i ] );
    submenu_types[ i + 1 ].text = joystick_names[ i ];
    submenu_types[ i + 1 ].key = INPUT_KEY_a + i;
    submenu_types[ i + 1 ].callback = set_joystick_type;
    submenu_types[ i + 1 ].action = i;
  }
  submenu_types[ i + 1 ].text = NULL;

  if( action - 1 == JOYSTICK_KEYBOARD ) 
    error = widget_do_menu( submenu_type_and_mapping_for_keyboard );

#ifdef USE_JOYSTICK
  else
    error = widget_do_menu( submenu_type_and_mapping_for_joystick );
#endif  /* #ifdef USE_JOYSTICK */

  if( error ) return;
}

void
set_joystick_type( int action )
{
  *current_settings[ 0 ] = action;
  widget_end_all( WIDGET_FINISHED_OK );
}

/* Options/Select ROMs/<type> */
int
menu_select_roms_with_title( const char *title, size_t start, size_t count,
			     int is_peripheral )
{
  widget_roms_info info;

  info.title = title;
  info.start = start;
  info.count = count;
  info.is_peripheral = is_peripheral;
  info.initialised = 0;

  return widget_do_rom( &info );
}

void
menu_machine_reset( int action )
{
  int hard_reset = action;
  const char *message = "Reset?";

  if( hard_reset )
    message = "Hard reset?";

  if( widget_do_query( message ) ||
      !widget_query.confirm )
    return;

  widget_end_all( WIDGET_FINISHED_OK );

  /* Stop any ongoing RZX */
  rzx_stop_recording();
  rzx_stop_playback( 1 );

  machine_reset( hard_reset );
}

void
menu_machine_select( int action )
{
  widget_select_t info;
  char **options, *buffer;
  size_t i;
  int error;
  libspectrum_machine new_machine;

  options = malloc( machine_count * sizeof( const char * ) );
  if( !options ) {
    ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ );
    return;
  }

  buffer = malloc( 40 * machine_count );
  if( !buffer ) {
    ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ );
    free( options );
    return;
  }

  for( i = 0; i < machine_count; i++ ) {
    options[i] = &buffer[ i * 40 ];
    snprintf( options[i], 40, "%s",
              libspectrum_machine_name( machine_types[i]->machine ) );
    if( machine_current->machine == machine_types[i]->machine )
      info.current = i;
  }

  info.title = "Select machine";
  info.options = (const char**)options;
  info.count = machine_count;
  info.finish_all = 1;

  error = widget_do_select( &info );
  free( buffer ); free( options );
  if( error ) return;

  if( info.result == -1 ) return;

  new_machine = machine_types[ info.result ]->machine;

  if( machine_current->machine != new_machine ) machine_select( new_machine );
}

void
menu_machine_debugger( int action )
{
  debugger_mode = DEBUGGER_MODE_HALTED;
  widget_do_debugger();
}

void
menu_machine_pokememory( int action )
{
  widget_do_pokemem();
}

void
menu_machine_pokefinder( int action )
{
  widget_do_pokefinder();
}

void
menu_machine_memorybrowser( int action )
{
  widget_do_memorybrowser();
}

void
menu_media_tape_browse( int action )
{
  widget_do_browse();
}

void
menu_help_keyboard( int action )
{
  utils_file screen;
  widget_picture_data info;

  static const char * const filename = "keyboard.scr";

  if( utils_read_screen( filename, &screen ) ) {
    return;
  }

  info.filename = filename;
  info.screen = screen.buffer;
  info.border = 0;

  widget_do_picture( &info );

  utils_close_file( &screen );
}

void
menu_help_about( int action )
{
  widget_do_about();
}

static int
set_active( struct widget_menu_entry *menu, const char *path, int active )
{
  if( *path == '/' ) path++;

  /* Skip the menu title */
  menu++;

  for( ; menu->text; menu++ ) {

    const char *p = menu->text, *q = path;

    /* Compare the two strings, but skip hotkey-delimiter characters */
    do {
      if( *p == 9 || *p == 10 ) p++;
    } while( *p && *p++ == *q++ );

    if( *p ) continue;		/* not matched */

    /* match, but with a submenu */
    if( *q == '/' ) return set_active( menu->submenu, q, active );

    if( *q ) continue;		/* not matched */

    /* we have a match */
    menu->inactive = !active;
    return 0; 
  }

  return 1;
}

int
ui_menu_item_set_active( const char *path, int active )
{
  return set_active( widget_menu, path, active );
}
