#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <signal.h>
#include <limits.h>

#include "kernel.h"

#include "wimpspriteop.h"
#include "window.h"
#include "event.h"

#include "options.h"

#include "ztypes.h"
#include "riscosio.h"
#include "memory.h"
#include "fileio.h"
#include "quetzal.h"
#include "text.h"
#include "osdepend.h"
#include "screenio.h"
#include "riscoswimp.h"
#include "v6.h"
#include "v6ro.h"
#include "pngro.h"

#include "osfile.h"
#include "osgbpb.h"
#include "osbyte.h"
#include "colourtrans.h"
#include "territory.h"
#include "fileinfo.h"
#include "proginfo.h"
#include "saveas.h"
#include "menu.h"
#include "quit.h"
#include "user.h"

#include "fontmenu.h"
#include "popup.h"
#include "stringset.h"
#include "actionbutton.h"
#include "displayfield.h"
#include "numberrange.h"
#include "optionbutton.h"
#include "button.h"
#include "user.h"

#ifdef INFIX
#include "debug.h"
#endif

/* Toolbox events */
#define action_Quit 1
#define action_GrabFkeys 2
#define action_LineEdit 3
#define action_CopyScreen 4
#define action_Border 5
#define action_CmdRecall 6
#define action_Recording 7
#define action_Keypad 8

#define Cmp_Fkeys 0
#define Cmp_LineEdit 1
#define Cmp_CmdRecall 2
#define Cmp_Recording 3
#define Cmp_Keypad 4

#define Cmp_Border 5

#define Cmp_Release 0
#define Cmp_Version 2

#define Cmp_QuickLoad 2
#define Cmp_PauseExit 3
#define Cmp_ShowCaret 4
#define Cmp_ConfirmQ 5
#define Cmp_InterpNum 7
#define Cmp_InterpMenu 8
#define Cmp_ForeMenu 11
#define Cmp_BackMenu 12
#define Cmp_WarnOverwrite 13
#define Cmp_SmartQuotes 14
#define Cmp_UndoSlots 16
#define Cmp_ScreenGamma 18
#define Cmp_Dithering 20
#define Cmp_UpdateOften 21

#define Cmp_StoryName 1
#define Cmp_StoryCopyright 3
#define Cmp_StoryAuthor 5
#define Cmp_StoryType 7
#define Cmp_StoryRelease 9
#define Cmp_StorySize 11
#define Cmp_StoryDate 13
#define Cmp_StoryAnno 15

unsigned h_file_size;
unsigned h_extension_table;

static messagetrans_control_block mfd;
static toolbox_block id_block;
static int wimpver;
int log2bpp;

#ifdef INFIX
wimp_t our_task;
#endif

int default_fg=2;
int default_bg=9;

toolbox_o mainmenu;
static toolbox_o Screen, StoryInfo;
wimp_w ScreenW;

static int pref_cols = 80;
static int pref_rows = 25;

static char Release[5+1+6+1];
char StoryName[1024];
#define KeyQueueLen 16
static int KeyQueue[KeyQueueLen];
static int KeyIn=0, KeyOut=0;
int last_menu, last_item;

int have_input_focus;
int caret_enabled;

static int scrwidth, scrheight;

static toolbox_o QuitObj;

bool hiresmode;
bool grab_fkeys;
static bool quickload;
bool pauseexit=TRUE;
bool showcaret=TRUE;
static bool confirmq=TRUE;
bool warn_overwrite;
static bool separate_keypad=TRUE;
#ifdef ALLOW_QUOTES
bool smartquotes=TRUE;
#endif
#ifdef ALLOW_RECALL
bool command_recall;
#endif
#ifdef ALLOW_EDITING
bool line_editing=TRUE;
#endif

static void savechoices(void);
static int mouse_null_handler(wimp_event_no event_code, wimp_block *event,
                              toolbox_block *id, void *handle);

static int enter_handler(wimp_event_no event_code, wimp_block *event,
                              toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    event_register_wimp_handler(event_ANY, wimp_NULL_REASON_CODE, mouse_null_handler, 0);
    return 1;
}

static int leave_handler(wimp_event_no event_code, wimp_block *event,
                              toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    event_deregister_wimp_handler(event_ANY, wimp_NULL_REASON_CODE, mouse_null_handler, 0);
    return 1;
}

void setup_mouse(int use_mouse)
{
    wimp_poll_flags mask;

    if (use_mouse)
    {
    	event_register_wimp_handler(Screen, wimp_POINTER_ENTERING_WINDOW, enter_handler, 0);
    	event_register_wimp_handler(Screen, wimp_POINTER_LEAVING_WINDOW, leave_handler, 0);
    	event_get_mask(&mask);
    	mask&=~(wimp_MASK_LEAVING|wimp_MASK_ENTERING|wimp_MASK_NULL);
    }
}

static char msgs_buffer[256];

char *msgs_lookup(const char *token)
{
    int used;

    messagetrans_lookup(&mfd, token, msgs_buffer, sizeof msgs_buffer,
                        NULL, NULL, NULL, NULL, &used);

    msgs_buffer[used]='\0';

    return msgs_buffer;
}

char *msgs_lookup_1(const char *token, const char *param)
{
    int used;

    messagetrans_lookup(&mfd, token, msgs_buffer, sizeof msgs_buffer,
                        param, NULL, NULL, NULL, &used);

    msgs_buffer[used]='\0';

    return msgs_buffer;
}

char *msgs_lookup_2(const char *token, const char *param0, const char *param1)
{
    int used;

    messagetrans_lookup(&mfd, token, msgs_buffer, sizeof msgs_buffer,
                        param0, param1, NULL, NULL, &used);

    msgs_buffer[used]='\0';

    return msgs_buffer;
}

zword_t *msgs_lookup_u(const char *token)
{
    int used, i;

    messagetrans_lookup(&mfd, token, msgs_buffer, sizeof msgs_buffer/2,
                        NULL, NULL, NULL, NULL, &used);

    msgs_buffer[used]='\0';

    for (i = used; i>=0; i--)
        ((zword_t *) msgs_buffer)[i] = msgs_buffer[i];

    return (zword_t *) msgs_buffer;
}

#if 0 // Not used at present
static char *xmsgs_lookup(const char *token)
{
    int used;
    os_error *e;

    e=xmessagetrans_lookup(&mfd, token, msgs_buffer, sizeof msgs_buffer,
                           NULL, NULL, NULL, NULL, NULL, &used);

    if (e)
        return 0;

    msgs_buffer[used]='\0';

    return msgs_buffer;
}
#endif

void illegal_write(unsigned addr)
{
    char buffer[16];
    sprintf(buffer, "%X", addr);
    fatal(msgs_lookup_1("BadWrite", buffer));
}

unsigned illegal_read(unsigned addr)
{
    char buffer[16];
    sprintf(buffer, "%X", addr);
    fatal(msgs_lookup_1(addr >= h_file_size * size_scaler ? "BadRead" : "BadRead2", buffer));
    return 0;
}

void fatal_lookup(const char *token)
{
    fatal(msgs_lookup(token));
}

void fatal_lookup_1(const char *token, const char *param)
{
    fatal(msgs_lookup_1(token, param));
}

void warning_lookup(const char *token)
{
    warning(msgs_lookup(token));
}

void warning_lookup_1(const char *token, const char *param)
{
    warning(msgs_lookup_1(token, param));
}

void warning_lookup_2(const char *token, const char *param0, const char *param1)
{
    warning(msgs_lookup_2(token, param0, param1));
}

void info_lookup(const char *token)
{
    info(msgs_lookup(token));
}

/*
 * fatal
 *
 * Display message and stop interpreter.
 *
 */

void fatal(const char *s)
{
    os_error err;

    if (h_type==V6)
    	switchoutput(-100);

    err.errnum=1;
    if (pc)
    	sprintf(err.errmess, "Fatal error: %s (PC=&%X)", s, pc-datap);
    else
    	sprintf(err.errmess, "Fatal error: %s", s);

    if (wimpver >= 322)
        wimp_report_error_by_category(&err,
                wimp_ERROR_BOX_CATEGORY_ERROR << wimp_ERROR_BOX_CATEGORY_SHIFT,
                msgs_lookup("_TaskName"), "!zip2000", wimpspriteop_AREA, "Quit");
    else
        wimp_report_error(&err, wimp_ERROR_BOX_CANCEL_ICON, msgs_lookup("_TaskName"));

    reset_screen();

    exit(1);
}

/*
 * warning
 *
 * Display warning message.
 *
 */
void warning(const char *s)
{
    os_error err;

    if (h_type==V6)
        switchoutput(-100);

    err.errnum=1;
    if (pc)
    	sprintf(err.errmess, "Warning: %s (PC=%x)", s, pc-datap);
    else
    	sprintf(err.errmess, "Warning: %s", s);

    wimp_report_error_by_category(&err,
                wimp_ERROR_BOX_OK_ICON |
                wimp_ERROR_BOX_CATEGORY_ERROR << wimp_ERROR_BOX_CATEGORY_SHIFT,
                msgs_lookup("_TaskName"), "!zip2000", wimpspriteop_AREA, 0);

    if (h_type==V6)
    	switchoutput(+1);
}

/*
 * info
 *
 * Display warning message.
 *
 */
void info(const char *s)
{
    os_error err;

    if (h_type==V6)
        switchoutput(-100);

    err.errnum=1;
    strcpy(err.errmess, s);

    wimp_report_error_by_category(&err,
                wimp_ERROR_BOX_OK_ICON | wimp_ERROR_BOX_NO_BEEP |
                wimp_ERROR_BOX_CATEGORY_INFO << wimp_ERROR_BOX_CATEGORY_SHIFT,
                msgs_lookup("_TaskName"), "!zip2000", wimpspriteop_AREA, 0);

    if (h_type==V6)
    	switchoutput(+1);
}


/*
 * warn_bad_use
 *
 * Warn about use of opcode in wrong version
 *
 */
void warn_bad_use(const char *s)
{
    char buffer[256];

    sprintf(buffer, msgs_lookup("BadUse"), s, h_type);

    warning(buffer);
}

const char *GameTitle()
{
    static char temp[256];
    char *guess;
    int used;
    os_error *e;

    if (blorb_name_s)
        return blorb_name_s;

    memcpy(temp, datap+H_SERIAL_NUMBER, 6);
    temp[6]='\0';

    sprintf(Release, "%d.%s", get_word_priv(H_RELEASE), temp);

    e=xmessagetrans_lookup(&mfd, Release, temp, sizeof temp,
                           NULL, NULL, NULL, NULL, &guess, &used);
    if (!e)
        temp[used]='\0';
    else
    {
        guess=strrchr(StoryName, '.');
        if (guess)
            guess++;
        else
            guess=StoryName;

        strcpy(temp, guess);
        *temp=toupper(*temp);
        guess=temp;
    }

    return guess;
}

static void sprintnum_formatted(char *out, unsigned num)
{
    struct lconv *l = localeconv();
    char buffer[64];
    char buffer2[64];
    char *p = buffer2 + sizeof buffer2;
    char *p2;
    const char *sep = l->thousands_sep;
    const char *group = l->grouping;
    int seplen = strlen(sep);
    int n;

    n = sprintf(buffer, "%u", num);
    p2 = buffer + n - 1;

    n = *group;
    *--p = '\0';

    while (p2 >= buffer)
    {
        n--;
        if (n < 0)
        {
            memcpy(p-seplen, sep, seplen);
            p-=seplen;
            n = *++group;
            if (n == 0)
                n = *--group;
        }
        *--p = *p2--;
    }
    strcpy(out, p);
}

void SetTitle()
{
    const char *tit = GameTitle();
    char buffer[260];
    char buffer2[30];
    int i;
    os_date_and_time t;
    bits hi, lo;

    if (h_type==V6)
        switchoutput(-1);

    window_set_title(NONE, Screen, tit);
    button_set_value(NONE, StoryInfo, Cmp_StoryName, tit);

    window_set_title(NONE, StoryInfo, StoryName);
    if (blorb_copyright)
    {
        /*
         * Yes, we check whether the copyright symbol is available in the
         * current system alphabet...
         */
        i = unicode_to_system(0x00A9, 0);
        if (i)
            sprintf(buffer, "%c %s", i, blorb_copyright);
        else
            sprintf(buffer, "(c) %s", blorb_copyright);
        button_set_value(NONE, StoryInfo, Cmp_StoryCopyright, buffer);
    }
    if (blorb_author)
        button_set_value(NONE, StoryInfo, Cmp_StoryAuthor, blorb_author);
    if (blorb_anno)
        button_set_value(NONE, StoryInfo, Cmp_StoryAnno, blorb_anno);
    sprintf(buffer, "%d", h_type);
    button_set_value(NONE, StoryInfo, Cmp_StoryType,
                     msgs_lookup_1(zcode_in_blorb ? "BlorbV" :
                                   blorb_map ? "VersionB" : "Version", buffer));
    sprintf(buffer, "%d.%n", h_release, &i);
    memcpy(buffer + i, datap+H_SERIAL_NUMBER, 6);
    if (blorb_release)
        sprintf(buffer+i+6, " / %d", blorb_release);
    else if (GFile)
        sprintf(buffer+i+6, " / %d", GHeader.version);
    else
        buffer[i+6]='\0';
    button_set_value(NONE, StoryInfo, Cmp_StoryRelease, buffer);

    osfile_read_stamped(StoryName, &hi, &lo, NULL, NULL, NULL);
    *((int *)t)=lo; t[4]=hi;
    territory_convert_standard_date_and_time(territory_CURRENT, &t, buffer, sizeof buffer);
    button_set_value(NONE, StoryInfo, Cmp_StoryDate, buffer);

    sprintnum_formatted(buffer, h_file_size*size_scaler);
    if (blorb_map)
    {
        fseek(bfp, 4L, SEEK_SET);
        sprintnum_formatted(buffer2, fget32(bfp) + 8);
        button_set_value(NONE, StoryInfo, Cmp_StorySize,
                         msgs_lookup_2(zcode_in_blorb ? "SizeIn" : "SizePlus",
                                        buffer, buffer2));
    }
    else
        button_set_value(NONE, StoryInfo, Cmp_StorySize, buffer);

    if (h_type==V6)
        switchoutput(+1);
}


static void ExpandWindow(toolbox_show_flags flags, toolbox_o window,
                         toolbox_o parent_obj, toolbox_c parent_cmp)
{
    int width, height, scrwidth, scrheight, xeig, yeig;
    os_box extent;
    toolbox_position pos;

    window_get_extent(NONE, window, &extent);

    width=extent.x1-extent.x0;
    height=extent.y1-extent.y0;

    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XWIND_LIMIT, &scrwidth);
    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YWIND_LIMIT, &scrheight);
    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR, &xeig);
    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR, &yeig);

    scrwidth+=1; scrheight+=1;
    scrwidth<<=xeig; scrheight<<=yeig;

    pos.full.visible.x0=(scrwidth-width)/2;
    pos.full.visible.x1=pos.full.visible.x0+width;
    pos.full.visible.y0=(scrheight-height)/2;
    pos.full.visible.y1=pos.full.visible.y0+height;
    pos.full.xscroll=-32;
    pos.full.yscroll=32;
    pos.full.next=wimp_TOP;

    /* Keep clear of the icon bar */
    if (pos.full.visible.y0 < 128)
    {
        pos.full.visible.y1 += 128 - pos.full.visible.y0;
        pos.full.visible.y0 = 128;
    }

    toolbox_show_object(flags, window, toolbox_POSITION_FULL, &pos,
                        parent_obj, parent_cmp);
}

/*
 * This general-purpose routine will work for objects of class Window, DCS,
 * FileInfo, FontDbox, PrintDbox, ProgInfo, Quit, SaveAs, Scale, and indeed
 * any other object that supports Toolbox_MiscOp 0 as XXX_GetWindowId, and
 * for which Toolbox_ShowObject_FullSpec takes the same block as the Window
 * class.
 */

static void ShowCentred(toolbox_show_flags flags, toolbox_o obj,
                        toolbox_o parent_obj, toolbox_c parent_cmp)
{
    wimp_window_state state;
    int width, height;
    toolbox_o window;
    toolbox_class objclass;
    toolbox_position pos;

    objclass=toolbox_get_object_class(NONE, obj);
    if (objclass==class_WINDOW)
      window=obj;
    else
     window=fileinfo_get_window_id(NONE, obj);

    state.w=window_get_wimp_handle(NONE, window);

    wimp_get_window_state(&state);

    width=state.visible.x1-state.visible.x0;
    height=state.visible.y1-state.visible.y0;

    pos.top_left.x=(scrwidth-width)/2;
    pos.top_left.y=(scrheight+height)/2;

    /* Keep clear of the icon bar */
    if (pos.top_left.y - height < 128)
        pos.top_left.y = 128 + height;

    toolbox_show_object(flags, obj, toolbox_POSITION_TOP_LEFT, &pos,
                        parent_obj, parent_cmp);
}

static int quitmenu_handler(bits event_code, toolbox_action *event,
                            toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    caret_enabled=FALSE;
    place_caret();

    if (h_type==V6)
    	switchoutput(-1);

    reset_screen();

    exit(EXIT_SUCCESS);

    return 1;
}

static int iconise_handler(wimp_message *message, void *handle)
{
    wimp_message_window_info *info = (wimp_message_window_info *) &message->data;
    char *p;

    NOT_USED(handle);

    strcpy(info->sprite_name, "zip2000");

    p=strrchr(StoryName, '.');

    strncpy(info->title, p ? p+1 : StoryName, sizeof info->title);

    message->size=48;
    message->your_ref=message->my_ref;

    wimp_send_message(wimp_USER_MESSAGE, message, message->sender);

    return 1;
}

static int quit_handler(wimp_message *message, void *handle)
{
    NOT_USED(message); NOT_USED(handle);

    caret_enabled=FALSE;
    place_caret();

    if (h_type==V6)
    	switchoutput(-1);

    reset_screen();

    exit(EXIT_SUCCESS);

    return 1;
}

static int grabfkeys_handler(bits event_code, toolbox_action *event,
                             toolbox_block *id, void *handle)
{
    bool on;

    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    on=menu_get_tick(NONE, id->this_obj, id->this_cmp);

    menu_set_tick(NONE, id->this_obj, id->this_cmp, !on);

    grab_fkeys=!on;

    return 1;
}

#if defined(ALLOW_EDITING) || defined(ALLOW_RECALL)
static int lineedit_handler(bits event_code, toolbox_action *event,
                            toolbox_block *id, void *handle)
{
    bool on;

    NOT_USED(event_code); NOT_USED(event);

    on=menu_get_tick(NONE, id->this_obj, id->this_cmp);

    menu_set_tick(NONE, id->this_obj, id->this_cmp, !on);

    *(bool *)handle=!on;

    return 1;
}
#endif

#ifdef HOT_RECORD
static int recording_handler(bits event_code, toolbox_action *event,
                             toolbox_block *id, void *handle)
{
    bool on;

    on=menu_get_tick(NONE, id->this_obj, id->this_cmp);

    menu_set_tick(NONE, id->this_obj, id->this_cmp, !on);

    if (on)
    	z_output_stream(1, -4, 0, 0);
    else
        z_output_stream(1, 4, 0, 0);

    return 1;
}
#endif

static int error_handler(bits event_code, toolbox_action *event,
                         toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(id); NOT_USED(handle);

    wimp_report_error_by_category((os_error *) &event->data.error,
        wimp_ERROR_BOX_OK_ICON |
        wimp_ERROR_BOX_CATEGORY_ERROR << wimp_ERROR_BOX_CATEGORY_SHIFT,
        msgs_lookup("_TaskName"),
        "!zip2000",
        wimpspriteop_AREA,
        0);

    return 1;
}

void do_update_window(os_box *area)
{
    wimp_draw r;
    bool more;

    if (!ScreenW)
    	return;

    r.w=ScreenW;
    r.box=*area;

    more=wimp_update_window(&r);

    while (more)
    {
        redraw_window(&r);
        more=wimp_get_rectangle(&r);
    }

    return;
}

static void InsertKey(int c)
{
    KeyQueue[KeyIn++]=c;

    if (KeyIn==KeyQueueLen)
    	KeyIn=0;
}

int NextKey(void)
{
    int c;

    if (KeyIn==KeyOut)
    	return 0;

    c=KeyQueue[KeyOut++];

    if (KeyOut==KeyQueueLen)
    	KeyOut=0;

    return c;
}

static int update_pointer_pos(int x, int y, int buttons)
{
    wimp_window_state state;

    if (!h_extension_table)
    	return 0;

    state.w=ScreenW;
    wimp_get_window_state(&state);

    x=x+state.xscroll-state.visible.x0;
    y=y+state.yscroll-state.visible.y1;

    if (h_type!=V6)
    {
        x*=400; y*=-400;
        if (x<0 || y<0 || x>SWidth || y<-SHeight)
            return 0;

        x/=char_width;
        y/=line_height;
        x++; y++;
    }
    else
    {
        x/=V6_PIX_X; x++;
        y/=-V6_PIX_Y; y++;

    	if (mouse_window==-1)
    	{
    	    if (x<1 || y<1 || x>h_screen_width || y>h_screen_height)
    	    	return 0;
    	}
    	else
    	{
            if (x<wind_prop[mouse_window][WPROP_X] ||
            	y<wind_prop[mouse_window][WPROP_Y] ||
            	x>=wind_prop[mouse_window][WPROP_X]+wind_prop[mouse_window][WPROP_X_WIDTH] ||
            	y>=wind_prop[mouse_window][WPROP_Y]+wind_prop[mouse_window][WPROP_Y_HEIGHT])
            	return 0;
        }
    }

    set_word(h_extension_table+2, x);
    set_word(h_extension_table+4, y);

    mouse_x=x;
    mouse_y=y;
    mouse_butt=(buttons & wimp_CLICK_SELECT ? 2 : 0) |
               (buttons & wimp_CLICK_ADJUST ? 1 : 0);

    return 1;
}

static int mouse_null_handler(wimp_event_no event_code, wimp_block *event,
                              toolbox_block *id, void *handle)
{
    wimp_pointer p;

    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    wimp_get_pointer_info(&p);

    update_pointer_pos(p.pos.x, p.pos.y, p.buttons);

    return 0;
}

static int click_handler(wimp_event_no event_code, wimp_block *event,
                         toolbox_block *id, void *handle)
{
    wimp_pointer *p=&event->pointer;
    int ok;
    int b;

    NOT_USED(event_code); NOT_USED(id); NOT_USED(handle);

    if (p->buttons == wimp_CLICK_MENU)
    	return 0;

    if (!have_input_focus)
    {
    	have_input_focus=TRUE;
    	place_caret();
    	if (h_type==V6)
    	    switchoutput(-1);
    }

    if (!using_mouse)
    	return 1;

    if (p->buttons!=wimp_SINGLE_SELECT && p->buttons!=wimp_DOUBLE_SELECT &&
        p->buttons!=wimp_SINGLE_ADJUST && p->buttons!=wimp_DOUBLE_ADJUST)
        return 1;

    /* Righty-ho - we have a mouse click to send to the interpreter! */

    if (p->buttons==wimp_SINGLE_SELECT || p->buttons==wimp_DOUBLE_SELECT)
    	b=wimp_CLICK_SELECT;
    else
    	b=wimp_CLICK_ADJUST;

    ok=update_pointer_pos(p->pos.x, p->pos.y, b);

    if (ok)
    {
    	if (h_type==V6)
    	    InsertKey((p->buttons == wimp_DOUBLE_SELECT ||
    	               p->buttons == wimp_DOUBLE_ADJUST) ? 253 : 254);
    	else
    	    InsertKey(254);
    }

    return 1;
}

int palettechange_handler(wimp_message *message, void *handle)
{
    NOT_USED(message); NOT_USED(handle);

    if (h_type==V6)
    {
        if (v6_trans_tab)
        {
            free(v6_trans_tab);
            v6_trans_tab=NULL;
        }

        os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_LOG2_BPP, &log2bpp);

        /*don't need a table if going into 16 or 32bpp*/

        if (log2bpp <= 3)
        {
            int bufsize=colourtrans_generate_table_for_sprite(v6_screen_area,
                               (osspriteop_id) v6_screen,
                               os_CURRENT_MODE,
                               colourtrans_CURRENT_PALETTE, NULL,
                               colourtrans_GIVEN_SPRITE, NULL, NULL);

            v6_trans_tab=malloc(bufsize);
            colourtrans_generate_table_for_sprite(v6_screen_area,
                               (osspriteop_id) v6_screen,
                               os_CURRENT_MODE,
                               colourtrans_CURRENT_PALETTE, v6_trans_tab,
                               colourtrans_GIVEN_SPRITE, NULL, NULL);
        }
    }

    return 0;
}

int modechange_handler(wimp_message *message, void *handle)
{
    const
    static os_VDU_VAR_LIST(5) vdulist={os_MODEVAR_XWIND_LIMIT,
                                       os_MODEVAR_YWIND_LIMIT,
                                       os_MODEVAR_XEIG_FACTOR,
                                       os_MODEVAR_YEIG_FACTOR,
                                       -1};
    struct
    {
        int scrw, scrh, xeig, yeig;
    } vdu;

    NOT_USED(message); NOT_USED(handle);

    if (h_type==V6)
    	wimp_read_pix_trans(osspriteop_PTR, v6_screen_area,
    	                    (osspriteop_id) v6_screen,
    	                    &factors,
    	                    NULL);
    else if (gfx_area)
        wimp_read_pix_trans(osspriteop_USER_AREA,
                            gfx_area,
                            (osspriteop_id) "20",
                            &factors,
                            NULL);

    os_read_vdu_variables((const os_vdu_var_list *) &vdulist, (int *) &vdu);

    scrwidth  = (vdu.scrw + 1) << vdu.xeig;
    scrheight = (vdu.scrh + 1) << vdu.yeig;

    if (h_type!=V6)
        find_fonts();

    palettechange_handler(NULL, NULL);

    return 0;
}

#ifdef ALLOW_FONTS
#if 0
static int fontshow_handler(bits event_code, toolbox_action *event,
                         toolbox_block *id, void *handle)
{
    fontmenu_set_font(NONE, id->this_obj, handle);

    return 1;
}
#endif


static int textfont_choose(bits event_code, toolbox_action *event,
                           toolbox_block *id, void *handle)

{
    fontmenu_action_selection *fontsel=(fontmenu_action_selection *)&event->data;
    os_box redraw;

    NOT_USED(event_code); NOT_USED(id);

    if (font_find_field(fontsel->font_identifier, 'E', NULL))
        font_apply_fields(fontsel->font_identifier, "\\E", (char *) handle, font_NAME_LIMIT);
    else
        strcpy((char *) handle, fontsel->font_identifier);

    if (h_type==V6)
    	switchoutput(+1);

    find_fonts();

    if (h_type==V6)
    	switchoutput(-1);

    if (h_type!=V6)
    {
        redraw.x0=0;
        redraw.x1=SWidthOS;
        redraw.y0=-SHeightOS;
        redraw.y1=0;

        window_force_redraw(NONE, Screen, &redraw);
    }

    return 1;
}

static int max_alphabet_cmp;

static void tick_alphabet_entry(toolbox_o o)
{
    int i;
    char buffer[14];

    for (i=0; i<=max_alphabet_cmp; i++)
    {
        menu_get_entry_text(NONE, o, i, buffer, sizeof buffer);
        menu_set_tick(NONE,o, i, strcmp(buffer, encoding_name+2)==0);
    }
}

static int alphabet_choose(bits event_code, toolbox_action *event,
                           toolbox_block *id, void *handle)

{
    char buffer[14];

    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    menu_get_entry_text(NONE, id->this_obj, id->this_cmp, buffer, sizeof buffer);

    if (strcmp(buffer, encoding_name+2))
    {
        os_box redraw;

        strcpy(encoding_name+2, buffer);

        if (h_type==V6)
        	switchoutput(+1);

        find_fonts();

        setup_mapping_tables();

        if (h_type==V6)
        	switchoutput(-1);

        if (h_type!=V6)
        {
            redraw.x0=0;
            redraw.x1=SWidthOS;
            redraw.y0=-SHeightOS;
            redraw.y1=0;

            window_force_redraw(NONE, Screen, &redraw);
        }

        tick_alphabet_entry(id->this_obj);
    }

    return 1;
}

static int alphabet_show(bits event_code, toolbox_action *event,
                         toolbox_block *id, void *handle)

{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    tick_alphabet_entry(id->this_obj);

    return 1;
}

#endif

static void open_parent(const char *file)
{
    char temp[FILENAME_MAX+20];
    char *p;

    sprintf(temp, "Filer_OpenDir %s", file);

    p=strrchr(temp, '.');
    if (!p)
    	return;

    *p='\0';

    os_cli(temp);
}

static void initiate_quit(void)
{
    if (confirmq)
    	ShowCentred(toolbox_SHOW_AS_MENU, QuitObj, toolbox_NULL_OBJECT,
                                               	toolbox_NULL_COMPONENT);
    else
    	quit_handler(0,0);
}

static int caret_handler(wimp_event_no event_code, wimp_block *event,
                         toolbox_block *id, void *handle)
{
    NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    have_input_focus=event_code==wimp_GAIN_CARET;

    return 0;
}

static int close_handler(wimp_event_no event_code, wimp_block *event,
                         toolbox_block *id, void *handle)
{
    wimp_pointer pointer;

    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    wimp_get_pointer_info(&pointer);

    if (pointer.buttons & wimp_CLICK_SELECT)
    	initiate_quit();
    else
    {
        open_parent(StoryName);
        if (!osbyte1(129, 0xFF, 0xFF))
            initiate_quit();
    }

    return 1;
}

static int redraw_handler(wimp_event_no event_code, wimp_block *event,
                          toolbox_block *id, void *handle)
{
    wimp_draw *r=(wimp_draw *) event;
    bool more;

    NOT_USED(event_code); NOT_USED(id); NOT_USED(handle);

    more=wimp_redraw_window(r);

    while (more)
    {
        redraw_window(r);
        more=wimp_get_rectangle(r);
    }

    return 1;
}

#define Cmp_Cols 0
#define Cmp_Rows 1

static int showsize_handler(bits event_code, toolbox_action *event,
                            toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    numberrange_set_value(NONE, id->this_obj, Cmp_Cols, h_screen_cols);
    numberrange_set_value(NONE, id->this_obj, Cmp_Rows, h_screen_rows);
    numberrange_set_bounds(numberrange_BOUND_UPPER, id->this_obj, Cmp_Cols,
                            0, (scrwidth-(border?64:0))*400/char_width, 0, 0);
    numberrange_set_bounds(numberrange_BOUND_UPPER, id->this_obj, Cmp_Rows,
                            0, (scrheight-(border?128:0))*400/line_height, 0, 0);

    return 1;
}

static int showchoice_handler(bits event_code, toolbox_action *event,
                              toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    optionbutton_set_state(NONE, id->this_obj, Cmp_QuickLoad, quickload);
    optionbutton_set_state(NONE, id->this_obj, Cmp_PauseExit, pauseexit);
    optionbutton_set_state(NONE, id->this_obj, Cmp_ShowCaret, showcaret);
    optionbutton_set_state(NONE, id->this_obj, Cmp_ConfirmQ, confirmq);
    optionbutton_set_state(NONE, id->this_obj, Cmp_WarnOverwrite, warn_overwrite);
    optionbutton_set_state(NONE, id->this_obj, Cmp_Dithering, dithering);
    optionbutton_set_state(NONE, id->this_obj, Cmp_UpdateOften, update_often);
    if (h_type != V6 || bitmap_depth > 3)
    {
        gadget_flags flags=gadget_get_flags(NONE, id->this_obj, Cmp_Dithering);
        gadget_set_flags(NONE, id->this_obj, Cmp_Dithering, flags | gadget_FADED);
    }
    if (h_type != V6)
    {
        gadget_flags flags=gadget_get_flags(NONE, id->this_obj, Cmp_UpdateOften);
        gadget_set_flags(NONE, id->this_obj, Cmp_UpdateOften, flags | gadget_FADED);
    }
    numberrange_set_value(NONE, id->this_obj, Cmp_ScreenGamma, screen_gamma);
    #ifdef ALLOW_QUOTES
    optionbutton_set_state(NONE, id->this_obj, Cmp_SmartQuotes, smartquotes);
    #endif
    #ifdef ALLOW_EXTRAS
    numberrange_set_value(NONE, id->this_obj, Cmp_UndoSlots, option_undo_slots);
    numberrange_set_value(NONE, id->this_obj, Cmp_InterpNum, h_interpreter);
    stringsetsetselected_index(NONE, id->this_obj, Cmp_ForeMenu, default_fg-2);
    stringsetsetselected_index(NONE, id->this_obj, Cmp_BackMenu, default_bg-2);
    #endif

    return 1;
}

static int setchoice_handler(bits event_code, toolbox_action *event,
                             toolbox_block *id, void *handle)
{
    if (event->flags & actionbutton_SELECTED_DEFAULT)
    {
        #ifdef ALLOW_EXTRAS
        int temp_slots;
        #endif

        quickload=optionbutton_get_state(NONE, id->this_obj, Cmp_QuickLoad);
        pauseexit=optionbutton_get_state(NONE, id->this_obj, Cmp_PauseExit);
        showcaret=optionbutton_get_state(NONE, id->this_obj, Cmp_ShowCaret);
        confirmq =optionbutton_get_state(NONE, id->this_obj, Cmp_ConfirmQ);
        warn_overwrite=optionbutton_get_state(NONE, id->this_obj, Cmp_WarnOverwrite);
        dithering=optionbutton_get_state(NONE, id->this_obj, Cmp_Dithering);
        update_often=optionbutton_get_state(NONE, id->this_obj, Cmp_UpdateOften);
        screen_gamma=numberrange_get_value(NONE, id->this_obj, Cmp_ScreenGamma);
        build_gamma_tables(screen_gamma);
        #ifdef ALLOW_QUOTES
        smartquotes=optionbutton_get_state(NONE, id->this_obj, Cmp_SmartQuotes);
        #endif
        #ifdef ALLOW_EXTRAS
        temp_slots=numberrange_get_value(NONE, id->this_obj, Cmp_UndoSlots);
        if (option_undo_slots != temp_slots)
        {
            info_lookup("WUndoSlots");
            option_undo_slots = temp_slots;
        }
        h_interpreter=numberrange_get_value(NONE, id->this_obj, Cmp_InterpNum);
        default_fg=stringsetgetselected_index(NONE, id->this_obj, Cmp_ForeMenu)+2;
        default_bg=stringsetgetselected_index(NONE, id->this_obj, Cmp_BackMenu)+2;
        #endif
        set_byte_priv(H_INTERPRETER, h_interpreter);
    	savechoices();
    }
    else if (event->flags & actionbutton_SELECTED_CANCEL)
    	showchoice_handler(event_code, event, id, handle);

    return 1;
}

static int hidechoice_handler(bits event_code, toolbox_action *event,
                              toolbox_block *id, void *handle)
{
    wimp_caret c;

    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    wimp_get_caret_position(&c);

    /* The window has been closed by this stage, so if we had the caret
       it's now in the background window */
    if (c.w == wimp_BACKGROUND)
    {
        have_input_focus=TRUE;
        place_caret();
        if (h_type==V6)
            switchoutput(-1);
    }

    return 1;
}

static int border_handler(bits event_code, toolbox_action *event,
                           toolbox_block *id, void *handle)
{
    os_box extent;
    int border_width;

    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    border=!menu_get_tick(NONE, id->this_obj, id->this_cmp);

    menu_set_tick(NONE, id->this_obj, id->this_cmp, border);

    border_width=border?32:0;
    extent.x0=-border_width;
    extent.x1=SWidthOS+border_width;
    extent.y0=-SHeightOS-border_width;
    extent.y1=border_width;
    window_set_extent(NONE, Screen, &extent);
    place_caret();
    if (h_type==V6)
    	switchoutput(-1);

    ExpandWindow(NONE, Screen, toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);

    return 1;
}

static int setsize_handler(bits event_code, toolbox_action *event,
                           toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(handle);

    if (event->flags & actionbutton_SELECTED_DEFAULT)
    {
        os_box extent;
        int border_width;

        resize_window(numberrange_get_value(NONE, id->this_obj, Cmp_Cols),
                      numberrange_get_value(NONE, id->this_obj, Cmp_Rows));

    	border_width=border?32:0;
        extent.x0=-border_width;
        extent.x1=SWidthOS+border_width;
        extent.y0=-SHeightOS-border_width;
        extent.y1=border_width;
        window_set_extent(NONE, Screen, &extent);
    	place_caret();
        window_force_redraw(NONE, Screen, &extent);

        ExpandWindow(NONE, Screen, toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);
    }

    numberrange_set_value(NONE, id->this_obj, Cmp_Cols, h_screen_cols);
    numberrange_set_value(NONE, id->this_obj, Cmp_Rows, h_screen_rows);

    return 1;
}

static void ProcessDigit(int digit)
{
    static const char keycode[10]={149,148,131,147,133,132,229,228,213,212};

    if (separate_keypad && osbyte1(129, keycode[digit], 0xFF))
    	InsertKey(145+digit);
    else
    	InsertKey('0'+digit);
}

static int gamemenu_handler(bits event_code, toolbox_action *event,
                            toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    last_menu=id->parent_cmp;
    last_item=id->this_cmp;
    InsertKey(252);

    return 1;
}

static int input_handler(wimp_event_no event_code, wimp_block *event,
                         toolbox_block *id, void *handle)
{
    wimp_key_no key=event->key.c;
    int alphabet;

    NOT_USED(event_code); NOT_USED(id); NOT_USED(handle);

    /* Spot alphabet changes */
    alphabet = osbyte1(osbyte_ALPHABET_NUMBER, 127, 0);
    if (alphabet != system_alphabet_no)
    	setup_mapping_tables();

    if (key>='0' && key <='9')
    	ProcessDigit(key-'0');
    else if (key <= 255)
    {
        if (system_to_zscii_table[key])
            InsertKey(system_to_zscii_table[key]);
        else
            return 0;
    }
    else if (key == 0x18F)   /* Up arrow */
    	InsertKey(129);
    else if (key == 0x18E)   /* Down arrow */
        InsertKey(130);
    else if (key == 0x18C)   /* Left arrow */
        InsertKey(131);
    else if (key == 0x18D)   /* Right arrow */
        InsertKey(132);
    else if (key == 0x19C)
    	InsertKey(key_SHIFT_LEFT);
    else if (key == 0x19D)
    	InsertKey(key_SHIFT_RIGHT);
    else if (key == 0x1AC)
    	InsertKey(key_CTRL_LEFT);
    else if (key == 0x1AD)
    	InsertKey(key_CTRL_RIGHT);
    else if (key == 0x18B)
    	InsertKey(key_COPY);
    else if (key == 0x1AB)
    	InsertKey(key_CTRL_COPY);
    else if (key >= 0x181 && key <= 0x189)  /* f1-f9 */
        InsertKey(key-0x181+133);
    else if (key >= 0x1CA && key <= 0x1CB) /* f10-f11 */
        InsertKey(key-0x1CA+142);
    else if (grab_fkeys && key == 0x1CC) /* f12 */
        InsertKey(144);
    else
        return 0;

    return 1;
}

static int key_handler(wimp_event_no event_code, wimp_block *event,
                          toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(id); NOT_USED(handle);

    wimp_process_key(event->key.c);

    return 1;
}

static int saveas_handler(bits event_code, toolbox_action *event,
                            toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    saveas_set_file_name(NONE, id->this_obj, "Screen");
    saveas_set_file_size(NONE, id->this_obj, v6_screen_area->size-4);
    saveas_set_data_address(NONE, id->this_obj, (byte *)v6_screen_area+4,
                                                v6_screen_area->size-4,
                                                NULL,
                                                0);
    return 1;
}

static int memdump_saveas_handler(bits event_code, toolbox_action *event,
                                  toolbox_block *id, void *handle)
{
    int file_size=h_file_size*size_scaler;

    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    saveas_set_file_name(NONE, id->this_obj, "MemDump");
    saveas_set_file_size(NONE, id->this_obj, file_size);
    saveas_set_data_address(NONE, id->this_obj, (byte *) datap,
                                                file_size,
                                                NULL,
                                                0);
    return 1;
}

static int fileinfo_handler(bits event_code, toolbox_action *event,
                            toolbox_block *id, void *handle)
{
    int type=get_byte_priv(H_TYPE);
    int size=h_file_size*size_scaler;
    char buffer[32];

    NOT_USED(event); NOT_USED(event_code); NOT_USED(handle);

    sprintf(buffer, "%d", type);
    displayfield_set_value(NONE, fileinfo_get_window_id(NONE, id->this_obj),
                           Cmp_Release, Release);
    displayfield_set_value(NONE, fileinfo_get_window_id(NONE, id->this_obj),
                           Cmp_Version, msgs_lookup_1("Version", buffer));
    fileinfo_set_file_size(NONE, id->this_obj, size);

    return 1;
}

static void savechoices()
{
    FILE *f;

    f=fopen("<Zip2000$ChoicesFile>", "w");

    if (!f)
    	return;

    fprintf(f, "grab_fkeys:%d\n"
               "screen_cols:%d\n"
               "screen_rows:%d\n"
               "pause_exit:%d\n"
               "quick_load:%d\n"
               "show_caret:%d\n"
               "confirm_quit:%d\n"
               "warn_overwrite:%d\n"
               "border:%d\n"
               "separate_keypad:%d\n"
               "screen_gamma:%d\n"
               "dithering:%d\n"
               "update_often:%d\n",
               grab_fkeys,
               h_type == V6 ? pref_cols : h_screen_cols,
               h_type == V6 ? pref_rows : h_screen_rows,
               pauseexit, quickload, showcaret, confirmq,
               warn_overwrite, border, separate_keypad,
               screen_gamma, dithering, update_often);
    #ifdef ALLOW_EXTRAS
    fprintf(f, "undo_slots:%d\n"
    	       "interpreter_no:%d\n"
    	       "default_fg:%d\n"
    	       "default_bg:%d\n",
    	       option_undo_slots, h_interpreter, default_fg, default_bg);
    #endif
    #ifdef ALLOW_RECALL
    fprintf(f, "command_recall:%d\n", command_recall);
    #endif
    #ifdef ALLOW_EDITING
    fprintf(f, "line_editing:%d\n", line_editing);
    #endif
    #ifdef ALLOW_QUOTES
    fprintf(f, "smart_quotes:%d\n", smartquotes);
    #endif
    #ifdef ALLOW_FONTS
    fprintf(f, "text_font:%s\n"
    	       "fixed_font:%s\n"
    	       "alphabet:%s\n",
    	       textfont_name, fixedfont_name, encoding_name);
    #endif

    fclose(f);
}

void gasp(void)
{
    wimp_poll_flags flags;

    event_get_mask(&flags);
    event_set_mask(flags&~wimp_MASK_NULL);
    poll(0,0,0);
    event_set_mask(flags);
}

static void get_choice(messagetrans_control_block *cb, const char *tok, int *v)
{
    char buf[32];
    int used;

    if (xmessagetrans_lookup(cb, tok, buf, sizeof buf, 0, 0, 0, 0, NULL, &used))
    	return;

    buf[used]='\0';

    *v=atoi(buf);
}

#ifdef ALLOW_FONTS
static void get_choice_s(messagetrans_control_block *cb, const char *tok, char *v)
{
    int used;

    if (xmessagetrans_lookup(cb, tok, v, 216, 0, 0, 0, 0, NULL, &used))
    	return;

    v[used]='\0';
}
#endif

static void load_choices(void)
{
    messagetrans_control_block cb;
    os_error *e;
    #ifdef ALLOW_EXTRAS
    int temp;
    #endif
    int dim, eig;

    e=xmessagetrans_open_file(&cb, "<Zip2000$ChoicesFile>", 0);

    if (e) return;

    get_choice(&cb, "grab_fkeys", &grab_fkeys);
    get_choice(&cb, "screen_cols", &pref_cols); h_screen_cols=pref_cols;
    get_choice(&cb, "screen_rows", &pref_rows); h_screen_rows=pref_rows;
    get_choice(&cb, "pause_exit", &pauseexit);
    get_choice(&cb, "quick_load", &quickload);
    get_choice(&cb, "show_caret", &showcaret);
    get_choice(&cb, "warn_overwrite", &warn_overwrite);
    get_choice(&cb, "confirm_quit", &confirmq);
    get_choice(&cb, "border", &border);
    get_choice(&cb, "separate_keypad", &separate_keypad);
    get_choice(&cb, "screen_gamma", &screen_gamma);
    get_choice(&cb, "dithering", &dithering);
    get_choice(&cb, "update_often", &update_often);
    #ifdef ALLOW_QUOTES
    get_choice(&cb, "smart_quotes", &smartquotes);
    #endif
    #ifdef ALLOW_EXTRAS
    get_choice(&cb, "undo_slots", &option_undo_slots);
    temp=-1;
    get_choice(&cb, "interpreter_no", &temp);
    if (temp!=-1)
    	h_interpreter=temp;
    get_choice(&cb, "default_fg", &default_fg);
    get_choice(&cb, "default_bg", &default_bg);
    #endif
    #ifdef ALLOW_RECALL
    get_choice(&cb, "command_recall", &command_recall);
    #endif
    #ifdef ALLOW_EDITING
    get_choice(&cb, "line_editing", &line_editing);
    #endif
    #ifdef ALLOW_FONTS
    get_choice_s(&cb, "text_font", textfont_name);
    get_choice_s(&cb, "fixed_font", fixedfont_name);
    get_choice_s(&cb, "alphabet", encoding_name);
    #endif

    xmessagetrans_close_file(&cb);

    build_gamma_tables(screen_gamma);

    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XWIND_LIMIT, &dim);
    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR, &eig);
    dim+=1;
    dim<<=eig;
    if (h_screen_cols > (dim-(border?64:0))/16)
    	h_screen_cols = (dim-(border?64:0))/16;

    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YWIND_LIMIT, &dim);
    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR, &eig);
    dim+=1;
    dim<<=eig;
    if (h_screen_rows > (dim-(border?64:0))/32)
    	h_screen_rows = (dim-(border?64:0))/32;
}

#if 0
static showwin_handler(bits event_code, toolbox_action *event,
                          toolbox_block *id, void *handle)
{
    remove_caret();

    event_deregister_toolbox_handler(id->this_obj, action_WINDOW_ABOUT_TO_BE_SHOWN,
    	                            showwin_handler, 0);
    return 1;
}
#endif

static int confirmed_quit_handler(bits event_code, toolbox_action *event,
                                  toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    quit_handler(0, 0);

    return 1;
}

#ifdef ALLOW_EXTRAS
static int interpmenu_handler(bits event_code, toolbox_action *event,
                                  toolbox_block *id, void *handle)
{
    toolbox_c i;
    int n;
    popup_action_about_to_be_shown *popup=(popup_action_about_to_be_shown *) &event->data;

    NOT_USED(event_code); NOT_USED(handle);

    if (id->this_cmp != Cmp_InterpMenu)
    	return 0;

    n=numberrange_get_value(NONE, id->this_obj, Cmp_InterpNum);

    for (i=1; i<=11; i++)
    	menu_set_tick(NONE, popup->menu, i, i==n);

    return 1;
}

static int interpmenu_selection(bits event_code, toolbox_action *event,
                                  toolbox_block *id, void *handle)
{
    toolbox_c i;

    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    numberrange_set_value(NONE, id->parent_obj, Cmp_InterpNum, id->this_cmp);

    for (i=1; i<=11; i++)
    	menu_set_tick(NONE, id->this_obj, i, i==id->this_cmp);

    return 1;
}
#endif

static int quit_cancelled_handler(bits event_code, toolbox_action *event,
                                  toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(handle);

    if (!(toolbox_get_object_info(NONE, id->this_obj) & toolbox_INFO_SHOWING))
    {
    	have_input_focus=TRUE;
    	place_caret();
    }

    if (h_type==V6)
    	switchoutput(-1);

    return 1;
}

static int create_handler(bits event_code, toolbox_action *event,
                          toolbox_block *id, void *handle)
{
    char *name=event->data.created.name;

    NOT_USED(event_code); NOT_USED(handle);

    if (strcmp(name, "Screen")==0)
    {
        os_box extent;
        int border_width;

        Screen=id->this_obj;
        ScreenW=window_get_wimp_handle(NONE, Screen);
        event_register_wimp_handler(id->this_obj, wimp_REDRAW_WINDOW_REQUEST,
                                    redraw_handler, 0);
        event_register_wimp_handler(id->this_obj, wimp_MOUSE_CLICK,
                                    click_handler, 0);
        event_register_wimp_handler(id->this_obj, wimp_KEY_PRESSED,
                                    input_handler, 0);
        event_register_wimp_handler(id->this_obj, wimp_CLOSE_WINDOW_REQUEST,
                                    close_handler, 0);
        event_register_wimp_handler(id->this_obj, wimp_GAIN_CARET,
                                    caret_handler, 0);
        event_register_wimp_handler(id->this_obj, wimp_LOSE_CARET,
                                    caret_handler, 0);
    	/*event_register_toolbox_handler(id->this_obj, action_WINDOW_ABOUT_TO_BE_SHOWN,
    	                            showwin_handler, 0);*/
        border_width=border?32:0;
        extent.x0=-border_width;
        extent.x1=SWidthOS+border_width;
        extent.y0=-SHeightOS-border_width;
        extent.y1=border_width;
        window_set_extent(NONE, id->this_obj, &extent);
        ExpandWindow(NONE, id->this_obj, toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);
        have_input_focus=TRUE;
        place_caret();
        if (h_type==V6)
            switchoutput(-1);
    }
    else if (strcmp(name, "FileInfo")==0)
    {
        bits hi, lo;
        os_date_and_time time;

        fileinfo_set_file_name(NONE, id->this_obj, StoryName);
        osfile_read_stamped(StoryName, &hi, &lo, NULL, NULL, NULL);
        *((int *)time)=lo; time[4]=hi;
        fileinfo_set_date(NONE, id->this_obj, &time);
    }
    else if (strcmp(name, "UtilsMenu")==0)
    {
        menu_set_tick(NONE, id->this_obj, Cmp_Fkeys, grab_fkeys);
        #ifdef ALLOW_EDITING
        menu_set_tick(NONE, id->this_obj, Cmp_LineEdit, line_editing);
        #endif
        #ifdef ALLOW_RECALL
        menu_set_tick(NONE, id->this_obj, Cmp_CmdRecall, command_recall);
        #endif
        menu_set_tick(NONE, id->this_obj, Cmp_Keypad, separate_keypad);
    }
    else if (strcmp(name, "StyleMenu")==0)
    {
        menu_set_fade(NONE, id->this_obj, 2, h_type==V6);
    	menu_set_tick(NONE, id->this_obj, Cmp_Border, border);
    }
    else if (strcmp(name, "EditMenu")==0)
    {
        menu_set_fade(NONE, id->this_obj, 0, h_type!=V6);
    }
    else if (strcmp(name, "ExportMenu")==0)
    {
        menu_set_fade(NONE, id->this_obj, 0, h_type!=V6);
    }
    else if (strcmp(name, "ScreenSize")==0)
    {
    	event_register_toolbox_handler(id->this_obj, action_ACTION_BUTTON_SELECTED,
    	                                        setsize_handler, 0);
    	event_register_toolbox_handler(id->this_obj, action_WINDOW_ABOUT_TO_BE_SHOWN,
    	                                        showsize_handler, 0);
    }
    else if (strcmp(name, "Choices")==0)
    {
    	event_register_toolbox_handler(id->this_obj, action_ACTION_BUTTON_SELECTED,
    	                                        setchoice_handler, 0);
    	event_register_toolbox_handler(id->this_obj, action_WINDOW_ABOUT_TO_BE_SHOWN,
    	                                        showchoice_handler, 0);
    	event_register_toolbox_handler(id->this_obj, action_WINDOW_DIALOGUE_COMPLETED,
    	                                        hidechoice_handler, 0);
    	#ifdef ALLOW_EXTRAS
    	event_register_toolbox_handler(id->this_obj, action_POP_UP_ABOUT_TO_BE_SHOWN,
    	                                        interpmenu_handler, 0);
    	#endif
    	if (h_type==V6)
    	{
    	    gadget_flags flags=gadget_get_flags(NONE, id->this_obj, Cmp_ShowCaret);
    	    gadget_set_flags(NONE, id->this_obj, Cmp_ShowCaret, flags | gadget_FADED);
    	}
    }
    #ifdef ALLOW_EXTRAS
    else if (strcmp(name, "Interpreter")==0)
    {
        event_register_toolbox_handler(id->this_obj, action_MENU_SELECTION,
                                                interpmenu_selection, 0);
    }
    #endif
    else if (strcmp(name, "MainMenu")==0)
        mainmenu=id->this_obj;

    else if (strcmp(name, "Quit")==0)
    {
    	event_register_toolbox_handler(id->this_obj, action_QUIT_QUIT,
    	                                        confirmed_quit_handler, 0);
    	event_register_toolbox_handler(id->this_obj, action_QUIT_DIALOGUE_COMPLETED,
    	                                        quit_cancelled_handler, 0);
    	QuitObj=id->this_obj;
    }
    #ifdef ALLOW_FONTS
    else if (strcmp(name, "TextFont")==0)
    {
        fontmenu_set_font(NONE, id->this_obj, textfont_name);
    	/*event_register_toolbox_handler(id->this_obj, action_FONT_MENU_ABOUT_TO_BE_SHOWN,
    	                                         fontshow_handler, textfont_name);*/
    	event_register_toolbox_handler(id->this_obj, action_FONT_MENU_SELECTION,
    	                                         textfont_choose, textfont_name);
    }
    else if (strcmp(name, "FixedFont")==0)
    {
        fontmenu_set_font(NONE, id->this_obj, fixedfont_name);
    	/*event_register_toolbox_handler(id->this_obj, action_FONT_MENU_ABOUT_TO_BE_SHOWN,
    	                                         fontshow_handler, fixedfont_name);*/
    	event_register_toolbox_handler(id->this_obj, action_FONT_MENU_SELECTION,
    	                                         textfont_choose, fixedfont_name);
    }
    #endif
    else if (strcmp(name, "SaveScreen")==0)
    {
        event_register_toolbox_handler(id->this_obj, action_SAVE_AS_ABOUT_TO_BE_SHOWN,
                                       saveas_handler, 0);
    }
    else if (strcmp(name, "SaveMemDump")==0)
    {
        event_register_toolbox_handler(id->this_obj, action_SAVE_AS_ABOUT_TO_BE_SHOWN,
                                       memdump_saveas_handler, 0);
    }
    else if (strcmp(name, "Alphabet")==0)
    {
        char name_list[256];
        int index = 0, read;
        toolbox_c cmp=-1;
        menu_entry_object entry;

        entry.flags = 0;
        entry.click_object_name = NULL;
        entry.sub_menu_object_name = NULL;
        entry.sub_menu_action = 0;
        entry.click_action = 0;
        entry.help = msgs_lookup("AlphHelp");
        entry.help_limit = strlen(entry.help)+1;

        do
        {
            index = osgbpb_dir_entries("<Zip2000$Dir>.Resources.Encodings",
    	                               (osgbpb_string_list *) name_list,
                                       1, index, 256, "*", &read);
            if (read)
            {
                entry.cmp = max_alphabet_cmp = cmp + 1;
                entry.text = name_list;
                entry.text_limit = strlen(name_list)+1;
                menu_add_entry(0, id->this_obj, cmp++, &entry);
            }
        }
        while (index != -1);

        event_register_toolbox_handler(id->this_obj, action_MENU_SELECTION,
                                       alphabet_choose, 0);
        event_register_toolbox_handler(id->this_obj, action_MENU_ABOUT_TO_BE_SHOWN,
                                       alphabet_show, 0);
    }
    else if (strcmp(name, "StoryInfo")==0)
    {
        StoryInfo = id->this_obj;
    }

    return 1;
}

#pragma no_check_stack

static void sighandler(int sig)
{
    char Quit[32];
    messagetrans_control_block cb;

    NOT_USED(sig);

    if (h_type==V6)
    	switchoutput(-100);

    if (sig == SIGILL || sig == SIGSEGV || sig == SIGFPE || sig == SIGOSERROR)
    {
        unsigned int *regdump, *os_regdump;

        os_change_environment(os_HANDLER_CALL_BACK, 0, 0, 0,
                              0, (byte **) &regdump);
        os_regdump = (unsigned int *)
                     os_change_environment(os_HANDLER_EXCEPTION_REGISTERS, 0, 0, 0,
                                           0, 0);
        memcpy(os_regdump, regdump, 4*16);
    }

    messagetrans_open_file(&cb, "WindowManager:Messages", 0);
    messagetrans_lookup(&cb, "Quit:Quit", Quit, sizeof Quit, 0, 0, 0, 0, 0);
    messagetrans_close_file(&cb);

    if (wimpver >= 322)
    	wimp_report_error_by_category((os_error *) _kernel_last_oserror(),
                wimp_ERROR_BOX_CATEGORY_ERROR << wimp_ERROR_BOX_CATEGORY_SHIFT,
                msgs_lookup("_TaskName"), "!zip2000", wimpspriteop_AREA, Quit);
    else
    	wimp_report_error((os_error *) _kernel_last_oserror(),
    	    	    	      wimp_ERROR_BOX_CANCEL_ICON,
    	    	    	      msgs_lookup("_TaskName"));

    reset_screen();
    exit(1);
}

#pragma check_stack

static void close_menu(void)
{
    wimp_create_menu(wimp_CLOSE_MENU, 0, 0);
}

void pre_initialise_wimp(void)
{
    const
    static wimp_MESSAGE_LIST(5) messages={message_PALETTE_CHANGE,
                                          message_MODE_CHANGE,
    	    	    	    	    	  message_WINDOW_INFO,
    	    	    	    	    	  #ifdef INFIX
    	    	    	    	    	  message_INFIX_TRANSFER,
    	    	    	    	    	  #endif
                                          0};

    const
    static toolbox_ACTION_LIST(23) action_nos={action_ERROR,
    	    	    	    	    	       action_OBJECT_AUTO_CREATED,
    	    	    	    	    	       action_WINDOW_ABOUT_TO_BE_SHOWN,
    	    	    	    	    	       action_WINDOW_DIALOGUE_COMPLETED,
    	    	    	    	    	       action_ACTION_BUTTON_SELECTED,
    	    	    	    	    	       action_FILE_INFO_ABOUT_TO_BE_SHOWN,
    	    	    	    	    	       action_PROG_INFO_ABOUT_TO_BE_SHOWN,
    	    	    	    	    	       action_QUIT_QUIT,
    	    	    	    	    	       action_QUIT_DIALOGUE_COMPLETED,
    	    	    	    	    	       action_SAVE_AS_ABOUT_TO_BE_SHOWN,
    	    	    	    	    	       action_POP_UP_ABOUT_TO_BE_SHOWN,
    	    	    	    	    	       action_MENU_SELECTION,
    	    	    	    	    	       action_MENU_ABOUT_TO_BE_SHOWN,
    	    	    	    	    	       action_FONT_MENU_SELECTION,
    	    	    	    	    	       action_FONT_MENU_ABOUT_TO_BE_SHOWN,
                                               action_Quit,
                                               action_GrabFkeys,
                                               action_LineEdit,
                                               action_CmdRecall,
                                               action_Border,
                                               action_GameMenu,
                                               /*action_Recording,*/
                                               action_Keypad,
                                               0};

    int i;

    setlocale(LC_ALL, "");

    for (i=os_HANDLER_UNDEFINED_INSTRUCTION; i<=os_HANDLER_ADDRESS_EXCEPTION; i++)
    {
        void *h;
        byte *a, *b;

    	h=os_read_default_handler(i, &a, &b);
    	os_change_environment(i, h, a, b, 0, 0);
    }

    signal(SIGFPE, sighandler);
    signal(SIGILL, sighandler);
    signal(SIGSEGV, sighandler);
    signal(SIGSTAK, sighandler);
    signal(SIGOSERROR, sighandler);

    toolbox_initialise(NONE, wimp_VERSION_RO3, (wimp_message_list *) &messages,
                       (toolbox_action_list *) &action_nos,
                       getenv("Zip2000$Dir"), &mfd, &id_block, &wimpver, 0);

    #ifdef INFIX
    our_task=toolboxgetsysinfo_task();
    #endif

    load_choices();

    // Nasty Toolbox 1.36 bug - if the game quits while one of our menus is up
    // the whole of RISC OS will crash
    atexit(close_menu);

    #if SIGABRT != 1 || SIGSTAK != 7
    #error "Signals have been pissed around with!"
    #endif

    for (i=SIGABRT; i<=SIGSTAK; i++)
    	signal(i, sighandler);

    signal(SIGOSERROR, sighandler);
}

void initialise_wimp(void)
{
    wimp_event_no code=-1;

    event_initialise(&id_block);

    event_register_toolbox_handler(event_ANY, action_ERROR,
                                                    error_handler, 0);

    event_register_toolbox_handler(event_ANY, action_Quit,
                                                    quitmenu_handler, 0);

    event_register_toolbox_handler(event_ANY, action_GrabFkeys,
                                                    grabfkeys_handler, 0);

    #ifdef ALLOW_EDITING
    event_register_toolbox_handler(event_ANY, action_LineEdit,
                                                    lineedit_handler, &line_editing);
    #endif
    #ifdef ALLOW_RECALL
    event_register_toolbox_handler(event_ANY, action_CmdRecall,
                                                    lineedit_handler, &command_recall);
    #endif
    #ifdef HOT_RECORD
    event_register_toolbox_handler(event_ANY, action_Recording,
                                                    recording_handler, 0);
    #endif
    event_register_toolbox_handler(event_ANY, action_Keypad,
                                                    lineedit_handler, &separate_keypad);

    event_register_message_handler(message_QUIT, quit_handler, 0);

    event_register_message_handler(message_WINDOW_INFO, iconise_handler, 0);

    #ifdef INFIX
    event_register_message_handler(message_INFIX_TRANSFER, debug_transfer, 0);
    #endif

    event_register_toolbox_handler(event_ANY, action_OBJECT_AUTO_CREATED,
                                                   create_handler, 0);

    event_register_toolbox_handler(event_ANY, action_FILE_INFO_ABOUT_TO_BE_SHOWN,
                                                   fileinfo_handler, 0);

    event_register_toolbox_handler(event_ANY, action_PROG_INFO_ABOUT_TO_BE_SHOWN,
                                                   proginfo_handler, 0);

    event_register_toolbox_handler(event_ANY, action_Border,
                                                   border_handler, 0);

    event_register_toolbox_handler(event_ANY, action_GameMenu,
                                                   gamemenu_handler, 0);

    event_register_message_handler(message_MODE_CHANGE, modechange_handler, 0);

    event_register_message_handler(message_PALETTE_CHANGE, palettechange_handler, 0);

    event_register_wimp_handler(event_ANY, wimp_KEY_PRESSED, key_handler, 0);

    event_set_mask(0);

    if (quickload)
    {
        while (!Screen)
            poll(0, 0, 0);
    }
    else
    {
        while (code != wimp_NULL_REASON_CODE)
            poll(&code, 0, 0);
    }
    event_set_mask(wimp_MASK_NULL | wimp_MASK_LEAVING | wimp_MASK_ENTERING);
}

void poll(wimp_event_no *event_code, wimp_block *poll_block,
      void *poll_word)
{
    if (h_type==V6)
        switchoutput(-1);

    event_poll(event_code, poll_block, poll_word);

    if (h_type==V6)
    	switchoutput(+1);
}

void poll_idle(wimp_event_no *event_code, wimp_block *poll_block,
                    os_t earliest, void *poll_word)
{
    if (h_type==V6)
        switchoutput(-1);

    event_poll_idle(event_code, poll_block, earliest, poll_word);

    if (h_type==V6)
    	switchoutput(+1);
}

static int null_count;

int claim_null_events(void)
{
    wimp_poll_flags oldmask;

    if (null_count == 0)
    {
        event_get_mask(&oldmask);
        event_set_mask(oldmask &~ wimp_MASK_NULL);
    }

    return null_count++;
}

int release_null_events(void)
{
    wimp_poll_flags oldmask;

    if (null_count == 1)
    {
        event_get_mask(&oldmask);
        event_set_mask(oldmask | wimp_MASK_NULL);
    }

    return null_count--;
}
