/*
 * Only For Your Eyes : Wimp Iface Frank Lyonnet 1993
 */

#define FYEO_VERSIOND "2.05e (05-Mar-2004)"
#define FYEO_VERSION "2.05e"
#define FYEO_PURPOSE "Picture Viewer"
#define FYEO_AUTHOR "Arm's Tech"
#define FYEO_AUTHOR1 "Frank Lyonnet"
#define FYEO_AUTHOR2 "Frdric Elisei"
#define FYEO_AUTHOR3 "Jrme Mathevet"
#define FYEO_AUTHOR4 "Rick Murray"
#define FYEO_COPYRIGHT "Public Domain"

/**********************/
/* RiscOSLib includes */
/**********************/

#include "RISC_OSLib:wimp.h"
#include "RISC_OSLib:wimpt.h"
#include "RISC_OSLib:win.h"
#include "RISC_OSLib:event.h"
extern int event__current_menu;
extern int event__current_menu_window;
#include "RISC_OSLib:baricon.h"
#include "RISC_OSLib:res.h"
#include "RISC_OSLib:resspr.h"
#include "RISC_OSLib:template.h"
#include "RISC_OSLib:dbox.h"
#include "RISC_OSLib:xferrecv.h"
#include "RISC_OSLib:saveas.h"
#include "RISC_OSLib:flex.h"         /* Used mainly by MySprite.h, init currently called in this module */
#include "RISC_OSLib:visdelay.h"     /* Used mainly by MySprite.h, init currently called in this module */
#include "RISC_OSLib:magnify.h"
#include "RISC_OSLib:trace.h"
#include "RISC_OSLib:msgs.h"
#include "RISC_OSLib:os.h"
#include "RISC_OSLib:help.h"
#include "RISC_OSLib:coords.h"
#include "kernel.h"                  // Rick 2004/03/06; used in signal trapping
#include "signal.h"                  // Rick 2004/03/06; ...

/*********************/
/* Standard includes */
/*********************/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>

/********************/
/* Special includes */
/********************/

#include "Risc_OSLib:akbd.h"
#include "WimpUtils.h"
#include "Erreur.h"
#include "Filetypes.h"
#include "C:kernel.h"
#include "C:swis.h"
#include "MyThread.h"
#include "JHJFIF.h"
#include "JHGIF.h"
#include "JHBMP.h"
#include "JHPPM.h"
#include "JHTarga.h"
#include "Display.h"
#include "Process.h"
#include "FYEOPaths.h"
#include "Settings.h"
#include "Modes.h"
#include "MathUtils.h"
#include "Range.h"
#include "MySprite.h"

/** Templates */
#include "Ressources.h"

/*********************/
/* Menus definitions */
/*********************/

/** Icon bar menu*/
#define ICON_MENU_INFO 1
#define ICON_MENU_CHOICES 2
#define ICON_MENU_QUIT 3

/** Pic menu */
#define PIC_MENU_INFO 1
#define PIC_MENU_ZOOM 2
#define PIC_MENU_SAVE 3

/** Pic menu */
#define DITHER_MENU_NONE 1
#define DITHER_MENU_SIMPLE 2
#define DITHER_MENU_FLOYD 3

/********************/
/* Misc definitions */
/********************/

/* Processing status */
typedef enum {
    ACTIVE, WAITING
}    FYEO_status;

/* Pic Min Size */
#define PIC_MIN_WIDTH 64
#define PIC_MIN_HEIGHT 64

/* Output box */
#define OUTPUT_BUFFER 32
typedef struct OUTPUT_SETTINGS_BOX {
   dbox Box;
   char OutputBuffer[OUTPUT_BUFFER];
   output_settings * OldSettingsPtr;
   output_settings NewSettings;
} output_settings_box;

/* Input box */
typedef struct INPUT_SETTINGS_BOX {
   dbox Box;
   input_settings * OldSettingsPtr;
   input_settings NewSettings;
} input_settings_box;

/* Misc box */
typedef struct MISC_SETTINGS_BOX {
   dbox Box;
   misc_settings * OldSettingsPtr;
   misc_settings NewSettings;
} misc_settings_box;

/* Global box */
typedef struct GLOBAL_SETTINGS_BOX {
   FYEO_settings * SettingsPtr;
   dbox Box;
} global_settings_box;

/* Global choices */
global_settings_box * GlobalSettingsBox = NULL;
input_settings_box * InputSettingsBox = NULL;
misc_settings_box * MiscSettingsBox = NULL;
output_settings_box * OutputSettingsBox = NULL;


static dbox jpegtransify = NULL; // RICK 2004/01/29


/*********************/
/* Module prototypes */
/*********************/
#include "WimpIface.h"

/***************************/
/* Module Global variables */
/***************************/

/* Status */
static FYEO_status Status;

/* Settings */
FYEO_settings Settings, NewSettings;

/* Current process */
static FYEO_process Process;

/* Pic on display */
int Displays;

/* Icon bar menu */
static menu Icone_Menu;

/* Dither menu */
menu DitherMenu;
wimp_menustr * DMPtr;

/* Lines currently processed */
int FYEO_lines_processed;

/* The global string to allow FYEO to fail gracefully in case of errors */
char TheMess[256];

/* To remember which Preferences window was last selected */
wimp_w FrontWindow = -1;

/***************/
/* ToDo System */
/***************/
// In its current form, FYEO swallows all input if in ACTIVE mode. What we
// can do is hold the filenames that were fed at an early stage and process
// them later on when FYEO reverts back to WAITING mode.

#define ToDoSize 10 // Could be increased within reason

// Respectively Head and Tail of the ToDoList circular buffer.
// In practice, the number of unprocessed files is:
// Head-Tail if Head>=Tail
// Head-Tail+ToDoSize otherwise.
int ToDoHead=0,ToDoTail=0;

/* Array to stock Filenames for future processing when in ACTIVE mode */
char ToDoList[ToDoSize][256];

/****************************/
/* Window position handling */
/****************************/

static wimp_box
       CurrentBox,      /* Current window */
       StandardBox,     /* Standard window */
       DefaultBox;      /* Default window */

/* Initialisation */
static void FYEO_init_boxes(void) {
    DefaultBox.x0 = 300;
    DefaultBox.y0 = 300;
    DefaultBox.x1 = DefaultBox.x0 + 512;
    DefaultBox.y1 = DefaultBox.y0 + 512;
    StandardBox = DefaultBox;
}

/* Inform that next window opened will be same as Display window */
static void FYEO_current_is_display_box(FYEO_display * Display) {
wimp_winfo Info;
    Info.w = Display -> WinHandle;
    wimp_get_wind_info(&Info);
    CurrentBox = Info.info.box;
}

/* Inform that next window opened will be the standard one */
static void FYEO_current_is_standard_box(void) {
    if (Displays == 0)
        StandardBox = DefaultBox;
    else {
        StandardBox.x0 += 32;
        StandardBox.x1 += 32;
        StandardBox.y0 -= 32;
        StandardBox.y1 -= 32;
    };
    CurrentBox = StandardBox;
}

/*************************************/
/* Linked list for display structure */
/*************************************/

/* Simple linked list */
typedef struct DLIST {
    FYEO_display *Display;
    struct DLIST *Next;
}     dlist;

/* The head */
static dlist HeadList;

/* Pointer to head */
static dlist *TheDisplay;

/* Linked list initialisation */
static void FYEO_init_dlist(void) {
    HeadList.Display = NULL;
    HeadList.Next = NULL;
    TheDisplay = &HeadList;
    Displays = 0;
}

/* Put Display at the end of the linked list */
static void FYEO_insert_in_dlist(FYEO_display * Display) {
 dlist *New, *Current;
    New = (dlist *) malloc(sizeof(dlist));
    New -> Display = Display;
    New -> Next = NULL;

    Current = TheDisplay;

    while (Current -> Next != NULL)
        Current = Current -> Next;
    Current -> Next = New;
    Displays++;
}

/* Remove Display from the linked list */
// Rem: We're absolutely sure Display belongs to the linked list of displays
// Except if Display==NULL ?
static void FYEO_remove_from_dlist(FYEO_display * Display) {
 dlist *Current, *Last;

    Last = TheDisplay;
    Current = TheDisplay -> Next;

    while ((Current != NULL) && (Current -> Display != Display)) {
        Last = Current;
        Current = Current -> Next;
    }
    /* Check if we've got a buggy linked list */
    if (Current == NULL)
        erreur_fatal(msgs_lookup("NEDispE"));
    Last -> Next = Current -> Next;
    free(Current);
    Displays--;
}

/**********************************************/
/* Special allocation for FYEO_display struct */
/**********************************************/

/* Display allocation */
static FYEO_display *FYEO_alloc_display(void) {
 FYEO_display *Display;
    Display = (FYEO_display *) malloc(sizeof(FYEO_display));
    FYEO_insert_in_dlist(Display);
    return (Display);
}

/* Free Display */
static void FYEO_free_display(FYEO_display * Display) {
    FYEO_remove_from_dlist(Display);
    free(Display);
}

/*******************************/
/* Sprite management functions */
/*******************************/

/*
 * Sprite creation and allocation Returns TRUE if succed -> need to invoque destroy Returns FALSE
 * otherwise -> no need to invoque destroy
 */
static BOOL FYEO_create_sprite(FYEO_display * Display) {
  return((Display -> Sprite = mysprite_create(Display -> SpriteWidth, Display -> SpriteHeight,
    Display -> Bpp, Display -> Mode,
    Display -> XPos, Display -> YPos,
    Display -> PixWidth, Display -> PixHeight)) != NULL);
}

/* Sprite destruction */
static BOOL FYEO_destroy_sprite(FYEO_display * Display) {
  mysprite_destroy(Display -> Sprite);
  return(TRUE);
}

/*******************************/
/* Window management functions */
/*******************************/

/* Set 'Processing' window title */
static void FYEO_title_process(FYEO_display * Display) {
  char Title [256]; /* No need to incorporate it into Display structure*/

    sprintf(Title, "Processing %s %dx%d", strrchr(Display -> PicName, '.') +1,
            Display -> PixWidth, Display -> PixHeight);
    win_settitle(Display -> WinHandle, Title);
}

/* Set 'Waiting' window title */
static void FYEO_title_waiting(FYEO_display * Display) {
 char Title [256]; /* Same remark as above */

    sprintf(Title, "%s %dx%d", strrchr(Display -> PicName, '.')+1,
            Display -> PixWidth, Display -> PixHeight);
    win_settitle(Display -> WinHandle, Title);
}

/* Update window extent */
static void FYEO_resize_window(FYEO_display * Display) {
 wimp_redrawstr R;

    wimpt_checkmode();
    Display -> OSWidth = Display -> SpriteWidth * ( 180 / get_mode_xdpi(Display -> Mode));
    Display -> OSHeight = Display -> SpriteHeight * ( 180 / get_mode_ydpi(Display -> Mode));

    Display -> ScaledOSWidth = (Display -> OSWidth * Display -> MagWidth) / Display -> DivWidth;
    Display -> ScaledOSHeight = (Display -> OSHeight * Display -> MagHeight) / Display -> DivHeight;

    /* Resize sprite */
    mysprite_resize_sprite(Display -> Sprite, Display -> MagWidth, Display -> MagHeight,
      Display -> DivWidth, Display -> DivHeight);

    R.w = Display -> WinHandle;
    R.box.x0 = 0;
    R.box.x1 = Display -> ScaledOSWidth;
    R.box.y0 = -Display -> ScaledOSHeight;
    R.box.y1 = 0;
    wimp_set_extent(&R);
}

/* Redraw the specified part of the window */
static void FYEO_redo_window(FYEO_display * Display, wimp_redrawstr R) {
    mysprite_put_scaled(Display -> Sprite, R, Display -> ScaledOSHeight);
 }

/* Inform wimp that window must be redrawn when lines has been processed */
static void FYEO_update_window(FYEO_display * Display) {
 wimp_redrawstr R;
 BOOL More;
    R.w = Display -> WinHandle;
    R.box.x0 = 0;
    R.box.x1 = Display -> ScaledOSWidth;
    R.box.y0 = -(FYEO_lines_processed * Display -> ScaledOSHeight) / Display -> SpriteHeight;
    R.box.y1 = -((Display -> LinesProcessed) * Display -> ScaledOSHeight) / Display -> SpriteHeight;
    Display -> LinesProcessed = FYEO_lines_processed;

    wimp_update_wind(&R, &More);
    while (More) {
        FYEO_redo_window(Display, R);
        wimp_get_rectangle(&R, &More);
    }
}

/* Display the window */
static void FYEO_display_window(FYEO_display * Display) {
 wimp_wstate State;
 wimp_redrawstr R;
    wimp_get_wind_state(Display -> WinHandle, &State);
    /* open window in front */
    State.o.behind = -1;

    R.w = ((FYEO_display *) Display) -> WinHandle;
    R.box.x0 = 0;
    R.box.x1 = ((FYEO_display *) Display) -> ScaledOSWidth;
    R.box.y0 = -((FYEO_display *) Display) -> ScaledOSHeight;
    R.box.y1 = 0;

    wimp_open_wind(&State.o);
    wimp_force_redraw(&R);
}

/*
 * Window creation Returns TRUE if succed -> need to invoque destroy Returns FALSE otherwise -> no
 * need to invoque destroy
 */
static BOOL FYEO_create_window(FYEO_display * Display) {
    /* Create and alloc a new template, attention double malloc !! */
    Display -> Template = template_copy(template_find("PicWindow"));
    /* Set his extent */
    Display -> Template -> window.box = CurrentBox;
    /* Create the window */
    wimp_create_wind(&Display -> Template -> window, &Display -> WinHandle);

    Display -> MagWidth = 1;
    Display -> MagHeight = 1;
    Display -> DivWidth = 1;
    Display -> DivHeight = 1;

    /* Register event handler */
    win_register_event_handler(Display -> WinHandle, FYEO_window_event_handler, (void *) Display);

    /* Register unknown event processor */
    win_add_unknown_event_processor(FYEO_window_unknown_handler, (void *) Display);

    /* Claim idle events */
    win_claim_idle_events(Display -> WinHandle);

    /* Create the pic menu */
    FYEO_create_window_menu(Display);

    /* And attach it */
    event_attachmenu(Display -> WinHandle, Display -> Menu, FYEO_window_menu_proc, (void *) Display);

    mysprite_notify_display_change(Display -> Sprite);

    return (TRUE);
}

/* Window destruction */
static BOOL FYEO_destroy_window(FYEO_display * Display) {
    /* Detach menu */
    event_attachmenu(Display -> WinHandle, 0, FYEO_window_menu_proc,(void *) Display);

    /* And destroy it */
    FYEO_destroy_window_menu(Display);

    /* Remove unknown event processor */
    win_remove_unknown_event_processor(FYEO_window_unknown_handler, (void *) Display);

    /* And event handler */
    win_register_event_handler(Display -> WinHandle, 0, 0);

    /* Delete the window */
    wimp_delete_wind(Display -> WinHandle);

    /* Free the template, double free .. */

    /* RICK 2004/01/29

       There is a fuck-up someplace that results in this returning "heap overwritten" when we
       try to free the workspace, so we'll instead simply NOT free it, losing 64 bytes a time.
       It's the easy option, not the correct one, okay?... :-)

    {
       char temporary[256];

       sprintf(temporary, "Workspace size = %d bytes", Display->Template->workspacesize);
       erreur(temporary);
    }
    free(Display -> Template -> workspace); */

    free(Display -> Template);

    return (TRUE);
}

/****************************************************************************************************/
/* Display                                                                                          */
/****************************************************************************************************/
/*
 * Display creation Returns TRUE if succed -> need to invoque destroy Returns FALSE otherwise -> no
 * need to invoque destroy
 */
BOOL FYEO_init_display(FYEO_process * Process) {
 int OldMul,OldDiv,PixH,PixW;

    /* Alloc the display struct */
    Process -> Display = FYEO_alloc_display();

    /* Check file and get size */
    switch (Process -> Filetype) {
    case FILE_JPEG:
        if (!is_jpeg(Process -> FileName)) {
            erreur(msgs_lookup("NJPEGE"));
            return (FALSE);
        }
        Process -> Display -> SourceType = Process -> Filetype;
        get_jpeg_size(Process -> FileName,
                      &(Process -> Display -> SourceWidth), &(Process -> Display -> SourceHeight));
        get_jpeg_bpp_and_colorspace(Process -> FileName,&Process -> Display -> SourceBpp, &Process -> Display -> SourceColorspace);
        break;
    case FILE_GIF:
        if (!is_gif(Process -> FileName)) {
            erreur(msgs_lookup("NGIFE"));
            return (FALSE);
        }
        Process -> Display -> SourceType = Process -> Filetype;
        get_gif_size(Process -> FileName,
                     &(Process -> Display -> SourceWidth), &(Process -> Display -> SourceHeight));
        get_gif_bpp_and_colorspace(Process -> FileName,&Process -> Display -> SourceBpp , &Process -> Display -> SourceColorspace);
        break;

    // this next bit added by Rick, 2004/02/25
    case FILE_BMP:
        if (!is_bmp(Process -> FileName)) {
            erreur(msgs_lookup("NJPEGE"));
            return (FALSE);
        }
        Process -> Display -> SourceType = Process -> Filetype;
        get_bmp_size(Process -> FileName,
                      &(Process -> Display -> SourceWidth), &(Process -> Display -> SourceHeight));
        get_bmp_bpp_and_colorspace(Process -> FileName,&Process -> Display -> SourceBpp, &Process -> Display -> SourceColorspace);
        break;
    // (end of addition)


    case FILE_PPM:
        if (!is_ppm(Process -> FileName)) {
            erreur(msgs_lookup("NPPME"));
            return (FALSE);
        }
        Process -> Display -> SourceType = Process -> Filetype;
        get_ppm_size(Process -> FileName,
                     &(Process -> Display -> SourceWidth), &(Process -> Display -> SourceHeight));
        get_ppm_bpp_and_colorspace(Process -> FileName,&Process -> Display -> SourceBpp , &Process -> Display -> SourceColorspace);
        break;
    case FILE_TARGA:
        if (!is_targa(Process -> FileName)) {
            erreur(msgs_lookup("NTARGAE"));
            return (FALSE);
        }
        Process -> Display -> SourceType = Process -> Filetype;
        get_targa_size(Process -> FileName,
                     &(Process -> Display -> SourceWidth), &(Process -> Display -> SourceHeight));
        get_targa_bpp_and_colorspace(Process -> FileName,&Process -> Display -> SourceBpp , &Process -> Display -> SourceColorspace);
        break;
    default:
        return (FALSE);
        break;
    }

    /* Source dpi , user defined */
    Process -> Display -> SourceXDpi = Settings.Input.XDpi;
    Process -> Display -> SourceYDpi = Settings.Input.YDpi;

    /* Sprite dpi , user defined */
    Process -> Display -> XDpi = Settings.Output.XDpi;
    Process -> Display -> YDpi = Settings.Output.YDpi;

    /* Scaling */
    /* Check the dpi */
    Process -> Display -> XMul = Process -> Display -> XDpi;
    Process -> Display -> YMul = Process -> Display -> YDpi;
    Process -> Display -> XDiv = Process -> Display -> SourceXDpi;
    Process -> Display -> YDiv = Process -> Display -> SourceYDpi;

    /* Fraction reduction */
    red_frac( &(Process->Display->XMul), &(Process->Display->XDiv) );
    red_frac( &(Process->Display->YMul), &(Process->Display->YDiv) );

    switch(Settings.Output.Scaling) {
    case FYEO_SCAL_1:
    /*
       Process -> Display -> XMul *= 1;
       Process -> Display -> XDiv *= 1;
       Process -> Display -> YMul *= 1;
       Process -> Display -> YDiv *= 1;
       */
    break;
    case FYEO_SCAL_4:
       Process -> Display -> XMul *= 1;
       Process -> Display -> XDiv *= 4;
       Process -> Display -> YMul *= 1;
       Process -> Display -> YDiv *= 4;
    break;
    case FYEO_SCAL_8:
       Process -> Display -> XMul *= 1;
       Process -> Display -> XDiv *= 8;
       Process -> Display -> YMul *= 1;
       Process -> Display -> YDiv *= 8;
    break;
    case FYEO_SCAL_CUSTOM:
       Process -> Display -> XMul *= Settings.Output.CustomXMul;
       Process -> Display -> XDiv *= Settings.Output.CustomXDiv;
       Process -> Display -> YMul *= Settings.Output.CustomYMul;
       Process -> Display -> YDiv *= Settings.Output.CustomYDiv;
    break;
    case FYEO_SCAL_FIT:
       PixH = Process -> Display -> SourceHeight * Process -> Display -> YMul;
       PixH /= Process -> Display -> YDiv;
       PixW = Process -> Display -> SourceWidth * Process -> Display -> XMul;
       PixW /= Process -> Display -> XDiv;

       if( (PixW>Settings.Output.FillX) || (PixH>Settings.Output.FillY) ) {

          if( PixW*Settings.Output.FillY > PixH*Settings.Output.FillX ) {

             OldMul=Settings.Output.FillX;
             OldDiv=PixW;
             red_frac_arrond( &OldMul, &OldDiv, 16);
             Process->Display->XMul*=OldMul;
             Process->Display->XDiv*=OldDiv;
             /* We can still use OldMul and OldDiv, as they
                contain the good value. */
             Process->Display->YMul*=OldMul;
             Process->Display->YDiv*=OldDiv;
          } else {
             OldMul=Settings.Output.FillY;
             OldDiv=PixH;
             red_frac_arrond( &OldMul, &OldDiv ,16);
             Process->Display->XMul*=OldMul;
             Process->Display->XDiv*=OldDiv;
             /* We can still use OldMul and OldDiv, as they
                contain the good value. */
             Process->Display->YMul*=OldMul;
             Process->Display->YDiv*=OldDiv;
          }
       } else {
         /*
          Process -> Display -> XMul *= 1;
          Process -> Display -> XDiv *= 1;
          Process -> Display -> YMul *= 1;
          Process -> Display -> YDiv *= 1;
          */
       }
    break;
    case FYEO_SCAL_FILL:
        Process -> Display -> XMul = Settings.Output.FillX;
        Process -> Display -> XDiv = Process -> Display -> SourceWidth;
        Process -> Display -> YMul = Settings.Output.FillY;
        Process -> Display -> YDiv = Process -> Display -> SourceHeight;
    break;
    default:
    break;
    }

    /* Fraction reduction */
    red_frac( &(Process -> Display -> XMul), &(Process -> Display -> XDiv) );
    red_frac( &(Process -> Display -> YMul), &(Process -> Display -> YDiv) );

    /* PixSize must be divisible by XMuls */
    Process -> Display -> PixWidth = Process -> Display -> SourceWidth / Process -> Display -> XDiv;
    Process -> Display -> PixWidth *= Process -> Display -> XMul;
    Process -> Display -> PixHeight = Process -> Display -> SourceHeight / Process -> Display -> YDiv;
    Process -> Display -> PixHeight *= Process -> Display -> YMul;

    /* Check if image is big enough */
    if (Process -> Display -> PixWidth < PIC_MIN_WIDTH) {
        Process -> Display -> SpriteWidth = PIC_MIN_WIDTH;
        Process -> Display -> XPos = (Process -> Display -> SpriteWidth - Process -> Display -> PixWidth) / 2;
    }
    else {
        Process -> Display -> SpriteWidth = Process -> Display -> PixWidth;
        Process -> Display -> XPos = 0;
    }

    if (Process -> Display -> PixHeight < PIC_MIN_HEIGHT) {
        Process -> Display -> SpriteHeight = PIC_MIN_HEIGHT;
        Process -> Display -> YPos = (Process -> Display -> SpriteHeight - Process -> Display -> PixHeight) / 2;
    }
    else {
        Process -> Display -> SpriteHeight = Process -> Display -> PixHeight;
        Process -> Display -> YPos = 0;
    }

    /* 0 lines processed */
    Process -> Display -> LinesProcessed = Process -> Display -> YPos;
    FYEO_lines_processed = Process -> Display -> YPos;

    /*** We keep the whole file pathname, so that adjust closing a window
     now shows the contents of the corresponding directory. JM ***/
    strcpy(Process -> Display -> PicName, Process -> FileName);

    /* Compute screen mode and bpp from settings */
    Process -> Display -> Colorspace = Settings.Output.ColorSpace;
    Process -> Display -> Bpp = Settings.Output.Bpp;
    Process -> Display -> Mode = get_mode_from_specs(Settings.Output.Bpp,Settings.Output.XDpi,Settings.Output.YDpi);

    /* Try to create the sprite and the window , if unable free display and return FALSE */
    if (!FYEO_create_sprite(Process -> Display)) {
        FYEO_free_display(Process -> Display);
        return (FALSE);
    }

    /* Try to create window , if unable free sprite and display and return FALSE */
    if (!FYEO_create_window(Process -> Display)) {
        FYEO_destroy_sprite(Process -> Display);
        FYEO_free_display(Process -> Display);
        return (FALSE);
    }

    /* Display the window */
    FYEO_title_process(Process -> Display);
    FYEO_resize_window(Process -> Display);
    FYEO_display_window(Process -> Display);

    return (TRUE);
}

/* Display destruction*/
static void FYEO_destroy_display(FYEO_display * Display) {
    FYEO_destroy_sprite(Display);
    FYEO_destroy_window(Display);
    FYEO_free_display(Display);
}

/** Destruction of all displays , use the linked list */
static void FYEO_destroy_all_display(void) {
    while (TheDisplay -> Next != NULL)
        FYEO_destroy_display(TheDisplay -> Next -> Display);
}

/*****************/
/* File handling */
/*****************/

/* Set up a process */
static void FYEO_process_file(char *Filename, int Filetype) {
    Process.Filetype = Filetype;
    strcpy(Process.FileName, Filename);

    if (FYEO_init_display(&Process)) {
        if (FYEO_init_process(&Process))
            Status = ACTIVE;
        else
            FYEO_destroy_display(Process.Display);
    }
}

// Respond to a file drag/double click
//
// Display can be a valid FYEO_display or NULL, if a double-click or a
// load on the iconbar occurs.
// Context is a parameter specifying in which conditions the dataload was
// made (drag to iconbar, to window, or broadcast dataopen).
// All checks are made against the corresponding picture headers, NOT
// filetypes. If a file filetyped Gif, Jpeg, Targa or PPM is broadcast
// DATAOPEN, and the header does not check succesfully, the file IS
// considered treated by the function (i.e. returns TRUE), and an acknowledge
// is issued to the Wimp. This is absolutely right because another FYEO would
// load onto the iconbar otherwise. Returns TRUE if the DATALOAD or DATAOPEN
// file was handled.

static BOOL FYEO_load_file(FYEO_display * Display,Load_Context Context) {
 char *PPFile[256];
 int Filetype;
 BOOL Gif,Jpeg,PPM,Targa,BMP;  // BMP added by Rick, 2004/02/25
 _kernel_osfile_block Block;

     /* Validate settings */
     Settings = NewSettings;
     Filetype = xferrecv_checkinsert(PPFile);

     /*** Test if *PPFile is a directory ***/
     /*** or see if there was no file inserted ***/
     if ((Filetype == -1) || (_kernel_osfile(17,*PPFile,&Block) != 1)) return (TRUE);

     /*** Should now handle file drags during processing. JM ***/
     if (Status == ACTIVE) {
      if ((FYEO_display *) Display == Process.Display) {
       /*** Destroy any trace of activity then accept file drag ***/
       FYEO_abort_process(&Process);
       win_claim_idle_events(-1);
       Status = WAITING;
      }
      // Still processing another picture. We buffer the filename
      // to process the picture a little bit later on.
      else {
        int Next=(ToDoHead+1)%ToDoSize;
        if (Next!=ToDoTail) {
          strcpy(ToDoList[ToDoHead],*PPFile);
          ToDoHead=Next;
          xferrecv_insertfileok();
        } else erreur(msgs_lookup("TooMuchE"));
        // else, we could display an error (Too much work for me, job forgotten). JM
        return (TRUE);
      }
     }
     // In the case where the file is double-clicked, we must ensure that the
     // filetypes are the ones FYEO handle. If we don't do that, FYEO will
     // examine every file that it is asked by the wimp to DATAOPEN. This
     // could lead to automatic filetyping of files that aren't pictures !
     // BMP added by Rick, 2004/02/25
     if ((Filetype!=FILE_JPEG) && (Filetype!=FILE_GIF) && (Filetype!=FILE_PPM) && (Filetype!=FILE_TARGA) && (Filetype!=FILE_BMP)) {
       if (Context==DATAOPEN) return(FALSE);
     }

     // BMP added by Rick, 2004/02/25
     Gif=Jpeg=PPM=Targa=BMP=FALSE;
     if ((Jpeg = is_jpeg(*PPFile)) || (Gif = is_gif(*PPFile)) || (PPM = is_ppm(*PPFile)) || (Targa = is_targa(*PPFile)) || (BMP = is_bmp(*PPFile)) ) {
        Filetype = FILE_JPEG*Jpeg+FILE_GIF*Gif+FILE_PPM*PPM+FILE_TARGA*Targa+FILE_BMP*BMP;
        /* If needed filetype it */
        if (Settings.Misc.AutoFiletype) set_filetype(*PPFile, Filetype);

        /* Acknowledge */
        xferrecv_insertfileok();
        /* If dragged on a opened window , keep the extent */
        if (Display != NULL) {
           FYEO_current_is_display_box(Display);
           FYEO_destroy_display((FYEO_display *) Display);
        }
        else FYEO_current_is_standard_box();

        FYEO_process_file(*PPFile, Filetype);
        return(TRUE);
     } else {
      if ( ((Filetype==FILE_JPEG) || (Filetype==FILE_GIF) ||
           (Filetype==FILE_PPM) || (Filetype==FILE_TARGA) || (Filetype==FILE_BMP) ) ) {
        /* Acknowledge */
        xferrecv_insertfileok();
        erreur(msgs_lookup("DRTypeE"));
        return(TRUE);
      } else return(FALSE);
     }
}

/* Used when application is launched with args */
static void FYEO_force_file(char *Filename) {
 int Filetype;

   /* Validate settings */
   Settings = NewSettings;
   /* The filetype must be ok */
   Filetype = get_filetype(Filename);
   if(Filetype == FILE_UNDEF) erreur_interne(msgs_lookup("ARBadTE"));
   /* A new window */
   FYEO_current_is_standard_box();
   Status=ACTIVE;
   FYEO_process_file(Filename, Filetype);
}


/***********************************/
/* HANDLER des boites de dialogues */
/***********************************/
/* Displays 'About this program' */
static void FYEO_info_aboutprog(void) {
 char Version[32], Author[32], Purpose[32]; dbox D;

    D = dbox_new("ProgInfo");
    sprintf(Purpose,FYEO_PURPOSE);
    dbox_setfield(D, FYEO_INFO_PURPOSE,Purpose);
    sprintf(Version,FYEO_VERSIOND);
    dbox_setfield(D, FYEO_INFO_VERSION,Version);
    sprintf(Author,FYEO_AUTHOR1);
    dbox_setfield(D, FYEO_INFO_AUTHOR1,Author);
    sprintf(Author,FYEO_AUTHOR2);
    dbox_setfield(D, FYEO_INFO_AUTHOR2,Author);
    sprintf(Author,FYEO_AUTHOR3);
    dbox_setfield(D, FYEO_INFO_AUTHOR3,Author);
    sprintf(Author,FYEO_AUTHOR4);
    dbox_setfield(D, FYEO_INFO_AUTHOR4,Author);
    dbox_show(D);
    dbox_fillin(D);
    dbox_dispose(&D);
}

/* Displays 'About this program' */
static void FYEO_welcome(void) {
 char Version[32], Copy[32];
 dbox D; wimp_wstate State;
 wimp_box Box; int WWidth,WHeight,MWidth,MHeight;

    D = dbox_new("Welcome");

    sprintf(Version,FYEO_VERSION);
    dbox_setfield(D, WEL_VERSION,Version);
    sprintf(Copy, FYEO_COPYRIGHT);
    dbox_setfield(D, WEL_COPYRIGHT,Copy);

    dbox_show(D);

    /* center to screen */
    wimp_get_wind_state(dbox_syshandle(D),&State);
    Box = State.o.box;
    WWidth = Box.x1 - Box.x0;
    WHeight = Box.y1 - Box.y0;
    /* -1 Means cur mode */
    /* Get mode dimensions in OS units */
    MWidth = get_mode_width(-1) * ( 180 / get_mode_xdpi(-1));
    MHeight = get_mode_height(-1) * ( 180 / get_mode_ydpi(-1));
    Box.x0 = (MWidth - WWidth)/2;
    Box.y0 = (MHeight - WHeight)/2;
    Box.x1 = Box.x0 + WWidth;
    Box.y1 = Box.y0 + WHeight;

    State.o.behind = -1;
    State.o.box = Box;
    wimp_open_wind(&State.o);

    dbox_fillin(D);
    dbox_dispose(&D);
}

/* Displays 'About this picture' */
static void FYEO_info_aboutpic(FYEO_display * Display) {
dbox D;
char StrSpName[64], StrSpX[16], StrSpY[16], StrSpB[16];
char StrSpQ[16], StrSpProc[64], StrSpCol[64];

char StrSoX[16], StrSoY[16], StrSoB[16];
char StrSoQ[16], StrSoType[16], StrSoCol[64];

    sprintf(StrSpName, "%s", strrchr(Display -> PicName, '.')+1);
    sprintf(StrSpX, "%d pix.", Display -> PixWidth);
    sprintf(StrSpY, "%d pix.", Display -> PixHeight);
    sprintf(StrSpB, "%d bpp", Display -> Bpp);
    sprintf(StrSpQ, "%dx%d dpi", Display -> XDpi, Display -> YDpi);
    sprintf(StrSpProc, "%.2f secs", Display -> ProcessTime);

    switch (Display -> Colorspace) {
    case FYEO_RGB:
        sprintf(StrSpCol, "RGB");
        break;
    case FYEO_GRAY:
        sprintf(StrSpCol, "Grayscale");
        break;
    case FYEO_YUV:
        sprintf(StrSpCol, "YUV");
        break;
    case FYEO_CYMK:
        sprintf(StrSpCol, "CYMK");
        break;
    case FYEO_COLORMAPED:
        sprintf(StrSpCol, "Color map");
        break;
    default:
        sprintf(StrSpCol, "Unknown");
        break;
    }

    sprintf(StrSoX, "%d pix.", Display -> SourceWidth);
    sprintf(StrSoY, "%d pix.", Display -> SourceHeight);
    sprintf(StrSoB, "%d bpp", Display -> SourceBpp);
    sprintf(StrSoQ, "%dx%d dpi", Display -> SourceXDpi, Display -> SourceYDpi);

    switch (Display -> SourceType) {
    case FILE_JPEG:
        sprintf(StrSoType, "JPEG");
        break;
    case FILE_GIF:
        sprintf(StrSoType, "GIF");
        break;
    case FILE_BMP:
        sprintf(StrSoType, "BMP"); // BMP added by Rick, 2004/02/25
        break;
    case FILE_PPM:
        sprintf(StrSoType, "PPM");
        break;
    case FILE_TARGA:
        sprintf(StrSoType, "TARGA");
        break;
    default:
        sprintf(StrSoType, "Unknown");
        break;
    }

    switch (Display -> SourceColorspace) {
    case FYEO_RGB:
        sprintf(StrSoCol, "RGB");
        break;
    case FYEO_GRAY:
        sprintf(StrSoCol, "Grayscale");
        break;
    case FYEO_YUV:
        sprintf(StrSoCol, "YUV");
        break;
    case FYEO_CYMK:
        sprintf(StrSoCol, "CYMK");
        break;
    case FYEO_COLORMAPED:
        sprintf(StrSoCol, "Color map");
        break;
    default:
        sprintf(StrSoCol, "Unknown");
        break;
    };

    D = dbox_new("PicInfo");

    dbox_setfield(D,PICINFO_SPRITE_NAME,StrSpName);
    dbox_setfield(D,PICINFO_SPRITE_WIDTH,StrSpX);
    dbox_setfield(D,PICINFO_SPRITE_HEIGHT,StrSpY);
    dbox_setfield(D,PICINFO_SPRITE_BPP,StrSpB);
    dbox_setfield(D,PICINFO_SPRITE_QUALITY,StrSpQ);
    dbox_setfield(D,PICINFO_SPRITE_PROCESS,StrSpProc);
    dbox_setfield(D,PICINFO_SPRITE_COLORSPACE,StrSpCol);
    dbox_setfield(D,PICINFO_SOURCE_WIDTH,StrSoX);
    dbox_setfield(D,PICINFO_SOURCE_HEIGHT,StrSoY);
    dbox_setfield(D,PICINFO_SOURCE_BPP,StrSoB);
    dbox_setfield(D,PICINFO_SOURCE_QUALITY,StrSoQ);
    dbox_setfield(D,PICINFO_SOURCE_TYPE,StrSoType);
    dbox_setfield(D,PICINFO_SOURCE_COLORSPACE,StrSoCol);

    dbox_show(D);
    dbox_fillin(D);
    dbox_dispose(&D);
}

/****************************************************************************************************/
/* HANDLER des boites de settings                                                                   */
/****************************************************************************************************/
/* Output choices */
#define OUTPUT_DPI_DEPENDENCIES(D,SettingsPtr) \
dbox_setnumeric((D), OUTPUT_SETTINGS_4545DPI, ((SettingsPtr)->XDpi == 45) && ((SettingsPtr)->YDpi == 45));\
dbox_setnumeric((D), OUTPUT_SETTINGS_9045DPI, ((SettingsPtr)->XDpi == 90) && ((SettingsPtr)->YDpi == 45));\
dbox_setnumeric((D), OUTPUT_SETTINGS_9090DPI, ((SettingsPtr)->XDpi == 90) && ((SettingsPtr)->YDpi == 90));

#define OUTPUT_BPP_DEPENDENCIES(D,SettingsPtr) \
    dbox_setnumeric((D), OUTPUT_SETTINGS_8BPP,(SettingsPtr)->Bpp == 8); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_16BPP,(SettingsPtr)->Bpp == 16); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_32BPP,(SettingsPtr)->Bpp == 32); \
    if((SettingsPtr)->Bpp == 8) { \
       dbox_unfadefield((D),OUTPUT_SETTINGS_DITHER); \
       dbox_unfadefield((D),OUTPUT_SETTINGS_DITHER_MENU); \
    } \
    else { \
       dbox_fadefield((D),OUTPUT_SETTINGS_DITHER); \
       dbox_fadefield((D),OUTPUT_SETTINGS_DITHER_MENU); \
    };


#define OUTPUT_COLORSPACE_DEPENDENCIES(D,SettingsPtr) \
    dbox_setnumeric((D), OUTPUT_SETTINGS_GREY, (SettingsPtr)->ColorSpace == FYEO_GRAY); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_COL, (SettingsPtr)->ColorSpace == FYEO_RGB);

#define OUTPUT_SCALING_DEPENDENCIES(D,SettingsPtr) \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_1, (SettingsPtr)->Scaling == FYEO_SCAL_1); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_4, (SettingsPtr)->Scaling == FYEO_SCAL_4); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_8, (SettingsPtr)->Scaling == FYEO_SCAL_8); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_CUSTOM, (SettingsPtr)->Scaling == FYEO_SCAL_CUSTOM); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_FIT, (SettingsPtr)->Scaling == FYEO_SCAL_FIT); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_FILL, (SettingsPtr)->Scaling == FYEO_SCAL_FILL); \

#define OUTPUT_CURRENT_DEPENDENCIES(D,SettingsPtr) \
    dbox_setnumeric((D), OUTPUT_SETTINGS_USE_CURRENT, (SettingsPtr)->UseCurrent); \
    if((SettingsPtr)->UseCurrent) { \
       dbox_fadefield((D),OUTPUT_SETTINGS_4545DPI ); \
       dbox_fadefield((D),OUTPUT_SETTINGS_9045DPI ); \
       dbox_fadefield((D),OUTPUT_SETTINGS_9090DPI ); \
       dbox_fadefield((D),OUTPUT_SETTINGS_8BPP ); \
       dbox_fadefield((D),OUTPUT_SETTINGS_16BPP ); \
       dbox_fadefield((D),OUTPUT_SETTINGS_32BPP ); \
    } \
    else { \
       dbox_unfadefield((D),OUTPUT_SETTINGS_4545DPI ); \
       dbox_unfadefield((D),OUTPUT_SETTINGS_9045DPI ); \
       dbox_unfadefield((D),OUTPUT_SETTINGS_9090DPI ); \
       dbox_unfadefield((D),OUTPUT_SETTINGS_8BPP ); \
       dbox_unfadefield((D),OUTPUT_SETTINGS_16BPP ); \
       dbox_unfadefield((D),OUTPUT_SETTINGS_32BPP ); \
    };

#define OUTPUT_GET_WRITEABLE(D,SettingsPtr,Buffer,BufferSize) \
    dbox_getfield((D), OUTPUT_SETTINGS_SCAL_XMUL, (Buffer) , (BufferSize)); \
    (SettingsPtr)->CustomXMul = atoi((Buffer)); \
    dbox_getfield((D), OUTPUT_SETTINGS_SCAL_XDIV, (Buffer) , (BufferSize)); \
    (SettingsPtr)->CustomXDiv = atoi((Buffer)); \
    dbox_getfield((D), OUTPUT_SETTINGS_SCAL_YMUL, (Buffer) , (BufferSize)); \
    (SettingsPtr)->CustomYMul = atoi((Buffer)); \
    dbox_getfield((D), OUTPUT_SETTINGS_SCAL_YDIV, (Buffer) , (BufferSize));\
    (SettingsPtr)->CustomYDiv = atoi((Buffer)); \
    dbox_getfield((D), OUTPUT_SETTINGS_SCAL_FILL_X, (Buffer) , (BufferSize)); \
    (SettingsPtr)->FillX = atoi((Buffer)); \
    dbox_getfield((D), OUTPUT_SETTINGS_SCAL_FILL_Y, (Buffer) , (BufferSize)); \
    (SettingsPtr)->FillY = atoi((Buffer));

#define OUTPUT_SET_WRITEABLE(D,SettingsPtr) \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_XMUL, (SettingsPtr)->CustomXMul); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_XDIV, (SettingsPtr)->CustomXDiv); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_YMUL, (SettingsPtr)->CustomYMul); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_YDIV, (SettingsPtr)->CustomYDiv); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_FILL_X, (SettingsPtr)->FillX); \
    dbox_setnumeric((D), OUTPUT_SETTINGS_SCAL_FILL_Y, (SettingsPtr)->FillY);

#define OUTPUT_DITHER_DEPENDENCIES(D,SettingsPtr,Buffer) \
    switch((SettingsPtr)->Dither) { \
    case DITHER_NONE: \
       sprintf((Buffer),"None"); \
       dbox_setfield((D), OUTPUT_SETTINGS_DITHER, (Buffer)); \
       break; \
    case DITHER_SIMPLE: \
       sprintf((Buffer),"Simple"); \
       dbox_setfield((D), OUTPUT_SETTINGS_DITHER, (Buffer)); \
       break; \
    case DITHER_FLOYD: \
       sprintf((Buffer),"Floyd-Steinberg"); \
       dbox_setfield((D), OUTPUT_SETTINGS_DITHER, (Buffer)); \
       break; \
    default: \
       break; \
    };

static void FYEO_output_settings_init(output_settings * OutputSettingsPtr) {
 wimp_w OHandle,
        GHandle = (wimp_w)dbox_syshandle(GlobalSettingsBox -> Box);
 wimp_wstate OState,GState;

    if (OutputSettingsBox == NULL) {
      /* Alloc struct and init fields , create dialogue box */
      OutputSettingsBox = (output_settings_box *)malloc(sizeof(output_settings_box));
      OutputSettingsBox -> NewSettings = * OutputSettingsPtr;
      OutputSettingsBox -> OldSettingsPtr = OutputSettingsPtr;
      OutputSettingsBox -> Box = dbox_new("Output_ch");

      /* Register event handler , handle = 0 , not used */
      dbox_eventhandler(OutputSettingsBox -> Box , FYEO_output_settings_handler,0);

      /* Register unknown events handler , for MODE change dependencies */
      win_add_unknown_event_processor(FYEO_output_settings_raw_unknown_handler,0);

      /* Raw handler for help */
      dbox_raw_eventhandler(OutputSettingsBox -> Box , FYEO_output_settings_raw_handler,0);

      /* Init dialog box */
      OUTPUT_CURRENT_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)
      OUTPUT_BPP_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)
      OUTPUT_DPI_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)
      OUTPUT_COLORSPACE_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)
      OUTPUT_SCALING_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)
      OUTPUT_DITHER_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings,OutputSettingsBox-> OutputBuffer);
      OUTPUT_SET_WRITEABLE(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)

      /* Show static box */
      dbox_showstatic(OutputSettingsBox -> Box);
    }

    wimp_get_wind_state(GHandle,&GState);
    OHandle = (wimp_w)dbox_syshandle(OutputSettingsBox -> Box);
    wimp_get_wind_state(OHandle,&OState);
    OState.o.box = GState.o.box;
    OState.o.box.x0 += 8; OState.o.box.x1 += 8;
    OState.o.box.y0 -= 68; OState.o.box.y1 -= 68;
    OState.o.behind =- 1;
    wimp_open_wind(&OState.o);
}

static void FYEO_output_settings_term(void) {
   if(OutputSettingsBox != NULL) {
      /* Unregister unknown events handler , for MODE change dependencies */
      win_remove_unknown_event_processor(FYEO_output_settings_raw_unknown_handler,0);
      dbox_dispose(&OutputSettingsBox->Box);
      free(OutputSettingsBox);
      OutputSettingsBox = NULL;
   };
}

static void FYEO_output_settings_handler(dbox D,void * Handle) {
 output_settings * SettingsPtr = &OutputSettingsBox->NewSettings;

   switch (dbox_read(D)) {
      case OUTPUT_SETTINGS_8BPP:
         SettingsPtr->Bpp = 8;
         OUTPUT_BPP_DEPENDENCIES(D,SettingsPtr)
         break;

      case OUTPUT_SETTINGS_16BPP:
         SettingsPtr->Bpp = 16;
         OUTPUT_BPP_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_32BPP:
         SettingsPtr->Bpp = 32;
         OUTPUT_BPP_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_4545DPI:
         SettingsPtr->XDpi = 45;
         SettingsPtr->YDpi = 45;
         OUTPUT_DPI_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_9045DPI:
         SettingsPtr->XDpi = 90;
         SettingsPtr->YDpi = 45;
         OUTPUT_DPI_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_9090DPI:
         SettingsPtr->XDpi = 90;
         SettingsPtr->YDpi = 90;
         OUTPUT_DPI_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_USE_CURRENT:
         SettingsPtr->UseCurrent = !SettingsPtr->UseCurrent;
         OUTPUT_CURRENT_DEPENDENCIES(D,SettingsPtr)
         if (SettingsPtr->UseCurrent) {
            SettingsPtr->Bpp = get_mode_bpp(get_cur_mode());
            if (SettingsPtr->Bpp < 8) SettingsPtr->Bpp = 8;
            SettingsPtr->XDpi = get_mode_xdpi(get_cur_mode());
            SettingsPtr->YDpi = get_mode_ydpi(get_cur_mode());
            OUTPUT_BPP_DEPENDENCIES(D,SettingsPtr)
            OUTPUT_DPI_DEPENDENCIES(D,SettingsPtr)
         }
         break;

     case OUTPUT_SETTINGS_GREY:
         SettingsPtr->ColorSpace = FYEO_GRAY;
         OUTPUT_COLORSPACE_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_COL:
         SettingsPtr->ColorSpace = FYEO_RGB;
         OUTPUT_COLORSPACE_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_SCAL_1:
         SettingsPtr->Scaling = FYEO_SCAL_1;
         OUTPUT_SCALING_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_SCAL_4:
         SettingsPtr->Scaling = FYEO_SCAL_4;
         OUTPUT_SCALING_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_SCAL_8:
         SettingsPtr->Scaling = FYEO_SCAL_8;
         OUTPUT_SCALING_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_SCAL_CUSTOM:
         SettingsPtr->Scaling = FYEO_SCAL_CUSTOM;
         OUTPUT_SCALING_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_SCAL_FIT:
         SettingsPtr->Scaling = FYEO_SCAL_FIT;
         OUTPUT_SCALING_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_SCAL_FILL:
         SettingsPtr->Scaling = FYEO_SCAL_FILL;
         OUTPUT_SCALING_DEPENDENCIES(D,SettingsPtr)
         break;

     case OUTPUT_SETTINGS_DITHER_MENU:
        {
           coords_cvtstr Conv;
           wimp_wstate Wind;
           wimp_icon Icon;

           menu_setflags(DitherMenu,DITHER_MENU_NONE,SettingsPtr->Dither == DITHER_NONE,0);
           menu_setflags(DitherMenu,DITHER_MENU_SIMPLE,SettingsPtr->Dither == DITHER_SIMPLE,0);
           menu_setflags(DitherMenu,DITHER_MENU_FLOYD,SettingsPtr->Dither == DITHER_FLOYD,0);

           wimp_get_wind_state(dbox_syshandle(D),&Wind);
           Conv.box = Wind.o.box;
           Conv.scx = 0;
           Conv.scy = 0;
           wimp_get_icon_info(dbox_syshandle(D),OUTPUT_SETTINGS_DITHER_MENU,&Icon);
           coords_box_toscreen(&Icon.box,&Conv);
           wimp_create_menu(DMPtr,Icon.box.x1,Icon.box.y1);
           // RICK 2004/01/29 as 32bit RISC_OSLib doesn't export these values
           // event__current_menu = (int)DitherMenu;
           // event__current_menu_window = (int)dbox_syshandle(D);
        }
        break;

     default:
         break;
     }
}

static BOOL FYEO_output_settings_raw_handler(dbox D , void * Event, void * Handle) {
 wimp_eventstr * E = (wimp_eventstr *)Event;

    switch (E -> e) {

    case wimp_ESEND:
    case wimp_ESENDWANTACK:
        switch (E -> data.msg.hdr.action) {
        case wimp_MHELPREQUEST:
           E->data.msg.hdr.your_ref = E->data.msg.hdr.my_ref;
           E->data.msg.hdr.action = wimp_MHELPREPLY;
           E->data.msg.hdr.size = 256;
           switch(E->data.msg.data.helprequest.m.i) {
           case OUTPUT_SETTINGS_8BPP:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("8BppH"));
             break;
           case OUTPUT_SETTINGS_16BPP:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("16BppH"));
             break;
           case OUTPUT_SETTINGS_32BPP:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("32BppH"));
             break;
           case OUTPUT_SETTINGS_4545DPI:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("O4545DpiH"));
             break;
           case OUTPUT_SETTINGS_9045DPI:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("O9045DpiH"));
             break;
           case OUTPUT_SETTINGS_9090DPI:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("O9090DpiH"));
             break;
           case OUTPUT_SETTINGS_USE_CURRENT:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("UseCurH"));
             break;
           case OUTPUT_SETTINGS_GREY:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("GreyH"));
             break;
           case OUTPUT_SETTINGS_COL:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ColH"));
             break;
           case OUTPUT_SETTINGS_SCAL_1:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("Scal1H"));
             break;
           case OUTPUT_SETTINGS_SCAL_4:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("Scal4H"));
             break;
           case OUTPUT_SETTINGS_SCAL_8:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("Scal8H"));
             break;
           case OUTPUT_SETTINGS_SCAL_CUSTOM:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalCusH"));
             break;
           case OUTPUT_SETTINGS_SCAL_XMUL:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalXMulH"));
             break;
           case OUTPUT_SETTINGS_SCAL_XDIV:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalXDivH"));
             break;
           case OUTPUT_SETTINGS_SCAL_YMUL:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalYMulH"));
             break;
           case OUTPUT_SETTINGS_SCAL_YDIV:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalYDivH"));
             break;
           case OUTPUT_SETTINGS_SCAL_FIT:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalFitH"));
             break;
           case OUTPUT_SETTINGS_SCAL_FILL:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalFillH"));
             break;
           case OUTPUT_SETTINGS_SCAL_FILL_X:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalFillXH"));
             break;
           case OUTPUT_SETTINGS_SCAL_FILL_Y:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("ScalFillYH"));
             break;
           case OUTPUT_SETTINGS_DITHER:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("DitherH"));
             break;
           case OUTPUT_SETTINGS_DITHER_MENU:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("DitherMH"));
             break;
           default:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("OutputH"));
             break;
           };
           wimp_sendmessage(wimp_ESEND, &E->data.msg,E->data.msg.hdr.task);
           return (TRUE);
           break;
        default:
           return(FALSE);
           break;
        }
    default:
       return (FALSE);
    }
}

static BOOL FYEO_output_settings_raw_unknown_handler(wimp_eventstr * E, void * Handle) {
    switch (E -> e) {
    case wimp_EMENU:
        // RICK 2004/01/29 as 32bit RISC_OSLib doesn't export these values
        // if(event__current_menu != (int)DitherMenu)
        //    return(FALSE);
        switch(E->data.menu[0]+1) {
           case DITHER_MENU_NONE:
              OutputSettingsBox -> NewSettings.Dither = DITHER_NONE;
              break;
           case DITHER_MENU_SIMPLE:
              OutputSettingsBox -> NewSettings.Dither = DITHER_SIMPLE;
              break;
           case DITHER_MENU_FLOYD:
              OutputSettingsBox -> NewSettings.Dither = DITHER_FLOYD;
              break;
           default:
              break;
        }
        OUTPUT_DITHER_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings,
                  OutputSettingsBox -> OutputBuffer);
        if (dbox_persist()) {
           menu_setflags(DitherMenu,DITHER_MENU_NONE,OutputSettingsBox -> NewSettings.Dither == DITHER_NONE,0);
           menu_setflags(DitherMenu,DITHER_MENU_SIMPLE,OutputSettingsBox -> NewSettings.Dither == DITHER_SIMPLE,0);
           menu_setflags(DitherMenu,DITHER_MENU_FLOYD,OutputSettingsBox -> NewSettings.Dither == DITHER_FLOYD,0);

           wimp_create_menu(DMPtr,-1,-1);
        }
        return(TRUE);
        break;
    case wimp_ESEND:
    case wimp_ESENDWANTACK:
        switch (E -> data.msg.hdr.action) {
        case wimp_MMODECHANGE:
           output_settings_mode_change_dependencies(&OutputSettingsBox -> NewSettings);
           OUTPUT_BPP_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)
           OUTPUT_DPI_DEPENDENCIES(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)
           OUTPUT_SET_WRITEABLE(OutputSettingsBox -> Box,&OutputSettingsBox -> NewSettings)
           return(FALSE);
           break;
        default:
           return (FALSE);
           break;
        }
    default:
       return (FALSE);
    }
}

/** Input choices */
#define INPUT_DPI_DEPENDENCIES(D,SettingsPtr) \
    dbox_setnumeric((D), INPUT_SETTINGS_4545DPI, ((SettingsPtr)->XDpi == 45) && ((SettingsPtr)->YDpi == 45)); \
    dbox_setnumeric((D), INPUT_SETTINGS_9045DPI, ((SettingsPtr)->XDpi == 90) && ((SettingsPtr)->YDpi == 45)); \
    dbox_setnumeric((D), INPUT_SETTINGS_4590DPI, ((SettingsPtr)->XDpi == 45) && ((SettingsPtr)->YDpi == 90)); \
    dbox_setnumeric((D), INPUT_SETTINGS_9090DPI, ((SettingsPtr)->XDpi == 90) && ((SettingsPtr)->YDpi == 90));

static void FYEO_input_settings_init(input_settings * InputSettingsPtr) {
 wimp_w IHandle,
        GHandle = (wimp_w)dbox_syshandle(GlobalSettingsBox -> Box);
 wimp_wstate IState,GState;

    if (InputSettingsBox == NULL) {
      /* Alloc struct and init fields , create dialogue box */
      InputSettingsBox = (input_settings_box *)malloc(sizeof(input_settings_box));
      InputSettingsBox -> NewSettings = * InputSettingsPtr;
      InputSettingsBox -> OldSettingsPtr = InputSettingsPtr;
      InputSettingsBox -> Box = dbox_new("Input_ch");

      /* Register event handler , not use the handle */
      dbox_eventhandler(InputSettingsBox -> Box , FYEO_input_settings_handler,0);

      /* Raw handler for help */
      dbox_raw_eventhandler(InputSettingsBox -> Box , FYEO_input_settings_raw_handler,0);

      /* Init dialog box */
      INPUT_DPI_DEPENDENCIES(InputSettingsBox -> Box,&InputSettingsBox -> NewSettings)

      /* Show static box */
      dbox_showstatic(InputSettingsBox -> Box);
    }

    wimp_get_wind_state(GHandle,&GState);
    IHandle = (wimp_w)dbox_syshandle(InputSettingsBox -> Box);
    wimp_get_wind_state(IHandle,&IState);
    IState.o.box = GState.o.box;
    IState.o.box.x0 += 8; IState.o.box.x1 += 8;
    IState.o.box.y0 -= 68; IState.o.box.y1 -= 68;
    IState.o.behind =- 1;
    wimp_open_wind(&IState.o);
}

static void FYEO_input_settings_term(void) {
   if(InputSettingsBox != NULL) {
      dbox_dispose(&InputSettingsBox->Box);
      free(InputSettingsBox);
      InputSettingsBox = NULL;
   }
}

static void FYEO_input_settings_handler(dbox D,void * Handle) {
 input_settings * SettingsPtr = &InputSettingsBox->NewSettings;

   switch (dbox_read(D)) {
     case INPUT_SETTINGS_4545DPI:
         SettingsPtr->XDpi = 45;
         SettingsPtr->YDpi = 45;
         INPUT_DPI_DEPENDENCIES(D,SettingsPtr)
         break;

     case INPUT_SETTINGS_9045DPI:
         SettingsPtr->XDpi = 90;
         SettingsPtr->YDpi = 45;
         INPUT_DPI_DEPENDENCIES(D,SettingsPtr)
         break;

     case INPUT_SETTINGS_4590DPI:
         SettingsPtr->XDpi = 45;
         SettingsPtr->YDpi = 90;
         INPUT_DPI_DEPENDENCIES(D,SettingsPtr)
         break;

     case INPUT_SETTINGS_9090DPI:
         SettingsPtr->XDpi = 90;
         SettingsPtr->YDpi = 90;
         INPUT_DPI_DEPENDENCIES(D,SettingsPtr)
         break;

     default:
         break;
     }
}

static BOOL FYEO_input_settings_raw_handler(dbox D , void * Event, void * Handle) {
 wimp_eventstr * E = (wimp_eventstr *)Event;

    switch (E -> e) {
    case wimp_ESEND:
    case wimp_ESENDWANTACK:
        switch (E -> data.msg.hdr.action) {
        case wimp_MHELPREQUEST:
           E->data.msg.hdr.your_ref = E->data.msg.hdr.my_ref;
           E->data.msg.hdr.action = wimp_MHELPREPLY;
           E->data.msg.hdr.size = 256;
           switch(E->data.msg.data.helprequest.m.i) {
           case INPUT_SETTINGS_4545DPI:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("I4545DpiH"));
             break;
           case INPUT_SETTINGS_9045DPI:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("I9045DpiH"));
             break;
           case INPUT_SETTINGS_4590DPI:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("I4590DpiH"));
             break;
           case INPUT_SETTINGS_9090DPI:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("I9090DpiH"));
             break;
           default:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("InputH"));
             break;
           };
           wimp_sendmessage(wimp_ESEND, &E->data.msg,E->data.msg.hdr.task);
           return (TRUE);
           break;
        default:
           return(FALSE);
           break;
        }
    default:
       return (FALSE);
    }
}

/** Misc choices */

#define MISC_AUTO_DEPENDENCIES(D,SettingsPtr) \
    dbox_setnumeric((D), MISC_SETTINGS_AUTOFILETYPE, (SettingsPtr)->AutoFiletype == TRUE );

static void FYEO_misc_settings_init(misc_settings * MiscSettingsPtr) {
 wimp_w MHandle,
        GHandle = (wimp_w)dbox_syshandle(GlobalSettingsBox -> Box);
 wimp_wstate MState,GState;

    if (MiscSettingsBox == NULL) {
      /* Alloc struct and init fields , create dialogue box */
      MiscSettingsBox = (misc_settings_box *)malloc(sizeof(misc_settings_box));
      MiscSettingsBox -> NewSettings = * MiscSettingsPtr;
      MiscSettingsBox -> OldSettingsPtr = MiscSettingsPtr;
      MiscSettingsBox -> Box = dbox_new("Misc_ch");

      /* Register event handler */
      dbox_eventhandler(MiscSettingsBox -> Box , FYEO_misc_settings_handler,0);

      /* Raw handler for help */
      dbox_raw_eventhandler(MiscSettingsBox -> Box , FYEO_misc_settings_raw_handler,0);

      /* Init dialog box */
      MISC_AUTO_DEPENDENCIES(MiscSettingsBox -> Box,&MiscSettingsBox -> NewSettings)

      /* Show static box */
      dbox_showstatic(MiscSettingsBox -> Box);
    }

    wimp_get_wind_state(GHandle,&GState);
    MHandle = (wimp_w)dbox_syshandle(MiscSettingsBox -> Box);
    wimp_get_wind_state(MHandle,&MState);
    MState.o.box = GState.o.box;
    MState.o.box.x0 += 8; MState.o.box.x1 += 8;
    MState.o.box.y0 -= 68; MState.o.box.y1 -= 68;
    MState.o.behind =- 1;
    wimp_open_wind(&MState.o);
}

static void FYEO_misc_settings_term(void) {
   if(MiscSettingsBox != NULL) {
      dbox_dispose(&MiscSettingsBox->Box);
      free(MiscSettingsBox);
      MiscSettingsBox = NULL;
   }
}

static void FYEO_misc_settings_handler(dbox D,void * Handle) {
 misc_settings * SettingsPtr = &MiscSettingsBox->NewSettings;

   switch (dbox_read(D)) {
     case MISC_SETTINGS_AUTOFILETYPE:
        SettingsPtr -> AutoFiletype = !SettingsPtr -> AutoFiletype;
        MISC_AUTO_DEPENDENCIES(D,SettingsPtr)
        break;
     default:
         break;
     }
}

static BOOL FYEO_misc_settings_raw_handler(dbox D , void * Event, void * Handle) {
 wimp_eventstr * E = (wimp_eventstr *)Event;

    switch (E -> e) {
    case wimp_ESEND:
    case wimp_ESENDWANTACK:
        switch (E -> data.msg.hdr.action) {
        case wimp_MHELPREQUEST:
           E->data.msg.hdr.your_ref = E->data.msg.hdr.my_ref;
           E->data.msg.hdr.action = wimp_MHELPREPLY;
           E->data.msg.hdr.size = 256;
           switch(E->data.msg.data.helprequest.m.i) {
           case MISC_SETTINGS_AUTOFILETYPE:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("AutoH"));
             break;
           default:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("MiscH"));
             break;
           }
           wimp_sendmessage(wimp_ESEND, &E->data.msg,E->data.msg.hdr.task);
           return (TRUE);
           break;
        default:
           return(FALSE);
           break;
        }
    default:
       return (FALSE);
    }
}


static void FYEO_global_settings_init(FYEO_settings * SettingsPtr) {

    /* One window ! */
    if(GlobalSettingsBox != NULL) {
       wimp_wstate State;
       wimp_w BoxHandle;

       BoxHandle = (wimp_w)dbox_syshandle(GlobalSettingsBox -> Box);

       /* Get window state */
       if(wimpt_complain(wimp_get_wind_state(BoxHandle,&State))==0) {
          State.o.behind = -1; // Make sure window is opened front
          redraw_pref_windows(&State.o,TRUE);
       }
       return;
    }

    /* Alloc struct and init fields , create dialogue box */
    GlobalSettingsBox = (global_settings_box *)malloc(sizeof(global_settings_box));
    GlobalSettingsBox -> SettingsPtr = SettingsPtr;
    GlobalSettingsBox -> Box = dbox_new("Choices");

    /* Register event handler */
    dbox_eventhandler(GlobalSettingsBox -> Box , FYEO_global_settings_handler,0);

    /* Register raw event handler*/
    dbox_raw_eventhandler(GlobalSettingsBox -> Box,FYEO_global_settings_raw_handler,0);

    /* Show static box */
    dbox_showstatic(GlobalSettingsBox -> Box);

    /* Now, we create all 3 dbox */
    FYEO_misc_settings_init(&SettingsPtr -> Misc);
    FYEO_input_settings_init(&SettingsPtr -> Input);
    FYEO_output_settings_init(&SettingsPtr -> Output);
    select_icon(&GlobalSettingsBox -> Box,OUTPUT_SETTINGS_BUT);
}

static void FYEO_global_settings_term(void) {
   if(GlobalSettingsBox != NULL) {
      dbox_dispose(&GlobalSettingsBox->Box);
      free(GlobalSettingsBox);
      GlobalSettingsBox = NULL;
   }
}

static void FYEO_global_settings_handler(dbox D,void * Handle) {
 FYEO_settings * SettingsPtr = GlobalSettingsBox->SettingsPtr;
 int window;
 wimp_wstate GlobWin;

   switch (dbox_read(D)) {
     case GLOBAL_CANCEL:
         FYEO_output_settings_term();
         FYEO_input_settings_term();
         FYEO_misc_settings_term();
         FYEO_global_settings_term();
         break;

     case MISC_SETTINGS_BUT:

         wimp_get_wind_state(dbox_syshandle(GlobalSettingsBox -> Box),&GlobWin);
         FYEO_misc_settings_init(&SettingsPtr -> Misc);
         select_icon(&D,MISC_SETTINGS_BUT);
         FrontWindow = dbox_syshandle(MiscSettingsBox -> Box);
         redraw_pref_windows(&GlobWin.o, FALSE);

         break;

     case INPUT_SETTINGS_BUT:

         wimp_get_wind_state(dbox_syshandle(GlobalSettingsBox -> Box),&GlobWin);
         FYEO_input_settings_init(&SettingsPtr -> Input);
         select_icon(&D,INPUT_SETTINGS_BUT);
         FrontWindow = dbox_syshandle(InputSettingsBox -> Box);
         redraw_pref_windows(&GlobWin.o,FALSE);
         break;

     case OUTPUT_SETTINGS_BUT:

         wimp_get_wind_state(dbox_syshandle(GlobalSettingsBox -> Box),&GlobWin);
         FYEO_output_settings_init(&SettingsPtr -> Output);
         select_icon(&D,OUTPUT_SETTINGS_BUT);
         FrontWindow = dbox_syshandle(OutputSettingsBox -> Box);
         redraw_pref_windows(&GlobWin.o,FALSE);
         break;

     case GLOBAL_HELP:
         FYEO_launch_help();
         break;

     case GLOBAL_DEFAULT:
         output_settings_default(&SettingsPtr -> Output);

         OUTPUT_CURRENT_DEPENDENCIES(OutputSettingsBox->Box,
				&SettingsPtr -> Output)
         OUTPUT_BPP_DEPENDENCIES(OutputSettingsBox->Box,
				&SettingsPtr -> Output)
         OUTPUT_DPI_DEPENDENCIES(OutputSettingsBox->Box,
				&SettingsPtr -> Output)
         OUTPUT_COLORSPACE_DEPENDENCIES(OutputSettingsBox->Box,
				&SettingsPtr -> Output)
         OUTPUT_SCALING_DEPENDENCIES(OutputSettingsBox->Box,
         			&SettingsPtr -> Output)
         OUTPUT_SET_WRITEABLE(OutputSettingsBox->Box,
         			&SettingsPtr -> Output)
         OUTPUT_DITHER_DEPENDENCIES(OutputSettingsBox->Box,
         			&SettingsPtr -> Output,
         			OutputSettingsBox -> OutputBuffer);

         input_settings_default(&SettingsPtr->Input);
         INPUT_DPI_DEPENDENCIES(InputSettingsBox->Box,&SettingsPtr -> Input)

         misc_settings_default(&SettingsPtr->Misc);
         MISC_AUTO_DEPENDENCIES(MiscSettingsBox->Box,&SettingsPtr -> Misc)
         break;

     case GLOBAL_OK:
         OUTPUT_GET_WRITEABLE(OutputSettingsBox->Box,
                              &OutputSettingsBox->NewSettings,
                              OutputSettingsBox-> OutputBuffer,OUTPUT_BUFFER)
         * (OutputSettingsBox -> OldSettingsPtr) = OutputSettingsBox -> NewSettings;
         * (InputSettingsBox -> OldSettingsPtr) = InputSettingsBox -> NewSettings;
         * (MiscSettingsBox -> OldSettingsPtr) = MiscSettingsBox -> NewSettings;
         if (!dbox_persist()) {
          FYEO_output_settings_term();
          FYEO_input_settings_term();
          FYEO_misc_settings_term();
          FYEO_global_settings_term();
         }
         break;

     case PREFERENCES_SAVE_BUT:
        // the following four statements copied across, by Rick 2004/02/25
                OUTPUT_GET_WRITEABLE(OutputSettingsBox->Box,
                              &OutputSettingsBox->NewSettings,
                              OutputSettingsBox-> OutputBuffer,OUTPUT_BUFFER)
         * (OutputSettingsBox -> OldSettingsPtr) = OutputSettingsBox -> NewSettings;
         * (InputSettingsBox -> OldSettingsPtr) = InputSettingsBox -> NewSettings;
         * (MiscSettingsBox -> OldSettingsPtr) = MiscSettingsBox -> NewSettings;

        settings_save(SettingsPtr);
        // copied this next block to the "Save" does an auto-OK [Rick 2004/02/25]
         if (!dbox_persist()) {
          FYEO_output_settings_term();
          FYEO_input_settings_term();
          FYEO_misc_settings_term();
          FYEO_global_settings_term();
         }

        break;

     default:
         break;
     }
}

static void redraw_pref_windows(wimp_openstr * WinS, BOOL ToFront) {
 wimp_w In,Out,Misc;
 wimp_wstate InS,OutS,MiscS;

       In=dbox_syshandle(InputSettingsBox->Box);
       Out=dbox_syshandle(OutputSettingsBox->Box);
       Misc=dbox_syshandle(MiscSettingsBox->Box);

       wimp_get_wind_state(In,&InS);
       wimp_get_wind_state(Out,&OutS);
       wimp_get_wind_state(Misc,&MiscS);

       InS.o.box.x0 = OutS.o.box.x0 = MiscS.o.box.x0 = (*WinS).box.x0+8;
       InS.o.box.x1 = OutS.o.box.x1 = MiscS.o.box.x1 = (*WinS).box.x1+8;
       InS.o.box.y0 = OutS.o.box.y0 = MiscS.o.box.y0 = (*WinS).box.y0-68;
       InS.o.box.y1 = OutS.o.box.y1 = MiscS.o.box.y1 = (*WinS).box.y1-68;

       if (FrontWindow == In) {
         if (ToFront) InS.o.behind = -1;
         wimp_open_wind(&InS.o);
         (*WinS).behind = In;
         wimp_open_wind(WinS);
         OutS.o.behind = (*WinS).w;
         wimp_open_wind(&OutS.o);
         MiscS.o.behind = Out;
         wimp_open_wind(&MiscS.o);
       } else {
         if (FrontWindow == Misc) {
          if (ToFront) MiscS.o.behind = -1;
          wimp_open_wind(&MiscS.o);
          (*WinS).behind = Misc;
          wimp_open_wind(WinS);
          OutS.o.behind = (*WinS).w;
          wimp_open_wind(&OutS.o);
          InS.o.behind = Out;
          wimp_open_wind(&InS.o);
         } else {
          if (ToFront) OutS.o.behind = -1;
          wimp_open_wind(&OutS.o);
          (*WinS).behind = Out;
          wimp_open_wind(WinS);
          MiscS.o.behind = (*WinS).w;
          wimp_open_wind(&MiscS.o);
          InS.o.behind = Misc;
          wimp_open_wind(&InS.o);
         }
       }
}

static BOOL FYEO_global_settings_raw_handler(dbox D , void * Event, void * Handle) {
 wimp_eventstr * E = (wimp_eventstr *)Event;
 wimp_mousestr But;

    switch (E -> e) {
    case wimp_EOPEN:
     wimp_get_point_info(&But);
     redraw_pref_windows(&E->data.o,
                  (But.bbits == wimp_BLEFT) || (But.bbits == wimp_BDRAGLEFT));
       break;
    case wimp_ESEND:
    case wimp_ESENDWANTACK:
        switch (E -> data.msg.hdr.action) {
        case wimp_MHELPREQUEST:
           E->data.msg.hdr.your_ref = E->data.msg.hdr.my_ref;
           E->data.msg.hdr.action = wimp_MHELPREPLY;
           E->data.msg.hdr.size = 256;
           switch(E->data.msg.data.helprequest.m.i) {
           case OUTPUT_SETTINGS_BUT:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("OutputH"));
             break;
           case INPUT_SETTINGS_BUT:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("InputH"));
             break;
           case MISC_SETTINGS_BUT:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("MiscH"));
             break;
           case PREFERENCES_SAVE_BUT:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("SaveH"));
             break;
           case GLOBAL_HELP:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("HelpH"));
             break;
           case GLOBAL_DEFAULT:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("DefH"));
             break;
           case GLOBAL_CANCEL:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("CanH"));
             break;
           case GLOBAL_OK:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("OkH"));
             break;
           default:
             sprintf(E->data.msg.data.helpreply.text,msgs_lookup("GlobalH"));
             break;
           };
           wimp_sendmessage(wimp_ESEND, &E->data.msg,E->data.msg.hdr.task);
           return (TRUE);
           break;
        default:
           return(FALSE);
           break;
        }
    default:
       return (FALSE);
    }
}

/*** Extra function ***/
void select_icon(dbox * D, int Icon) {
  int iconblock[32], i;

  for (i=1;i<4;i++) {
   wimp_set_icon_state(dbox_syshandle(*D),i,0,1<<21);
  }

  wimp_set_icon_state(dbox_syshandle(*D),Icon,1<<21,1<<21);

}

/********/
/* Help */
/********/
static void FYEO_launch_help(void) {
 char Command[256];

   sprintf(Command,msgs_lookup("HelpRun"));
   os_cli(Command);
}

/********/
/* Misc */
/********/
/** Used by magnifier */
static void FYEO_mag_handler(void *Display) {
 wimp_redrawstr R;
 wimp_wstate State;
    wimp_get_wind_state(((FYEO_display *) Display) -> WinHandle, &State);

    ((FYEO_display *) Display) -> MagHeight = ((FYEO_display *) Display) -> MagWidth;
    ((FYEO_display *) Display) -> DivHeight = ((FYEO_display *) Display) -> DivWidth;
    FYEO_resize_window((FYEO_display *) Display);

    R.w = ((FYEO_display *) Display) -> WinHandle;
    R.box.x0 = 0;
    R.box.x1 = ((FYEO_display *) Display) -> ScaledOSWidth;
    R.box.y0 = -((FYEO_display *) Display) -> ScaledOSHeight;
    R.box.y1 = 0;

    wimp_open_wind(&State.o);
    wimp_force_redraw(&R);
}

/** Used by saveas */
static BOOL FYEO_save_sprite(char *Filename, void *Display) {
   return(mysprite_save(((FYEO_display *) Display) -> Sprite, Filename));
}

/*********/
/* Menus */
/*********/
/** Create icon menu */
static void FYEO_create_icon_menu(void) {
    Icone_Menu = menu_new("FYEO", msgs_lookup("IconMenu"));
}

/** Destroy icon menu */
static void FYEO_destroy_icon_menu(void) {
    menu_dispose(&Icone_Menu, TRUE);
}

/** Create window menu */
static void FYEO_create_window_menu(FYEO_display * Display) {
    Display -> Menu = menu_new( strrchr(Display -> PicName, '.') + 1, msgs_lookup("PicMenu"));
}

/** Destroy window menu */
static void FYEO_destroy_window_menu(FYEO_display * Display) {
    menu_dispose(&Display -> Menu, TRUE);
}

/** Icon bar menu handler */
static void FYEO_icon_menu_proc(void *Handle, char *Hit) {
    switch (Hit[0]) {
    case ICON_MENU_INFO:
        FYEO_info_aboutprog();
        break;
    case ICON_MENU_CHOICES:
        FYEO_global_settings_init(&NewSettings);
        break;
    case ICON_MENU_QUIT:
        /** Abort process if active , destroy all display , settings box and terminate */
        if (Status == ACTIVE)
            FYEO_abort_process(&Process);
        FYEO_destroy_all_display();
        FYEO_output_settings_term();
        FYEO_input_settings_term();
        FYEO_misc_settings_term();
        FYEO_global_settings_term();
        FYEO_terminate();
        break;
    default:
        break;
    }
}
/** Pic menu handler */
static void FYEO_window_menu_proc(void *Display, char *Hit) {
    switch (Hit[0]) {
        case PIC_MENU_INFO:
        FYEO_info_aboutpic((FYEO_display *) Display);
        break;
    case PIC_MENU_ZOOM:
        magnify_select(&((FYEO_display *) Display) -> MagWidth,
                       &((FYEO_display *) Display) -> DivWidth, 99, 99, FYEO_mag_handler,
                       Display);
        break;
    case PIC_MENU_SAVE:
        saveas(FILE_SPRITE, ((FYEO_display *) Display) -> PicName,
          mysprite_get_size(((FYEO_display *) Display) -> Sprite), FYEO_save_sprite, NULL, NULL, Display);
        break;
    }
}
/****************************/
/* PicWindow Event handlers */
/****************************/

/** Known events handler */
static void FYEO_window_event_handler(wimp_eventstr * E, void *Display) {
 int Shift;
 wimp_mousestr But;
 char OSCall[256]="filer_opendir ";

    switch (E -> e) {
        case wimp_ENULL:
        if ((Status == ACTIVE) && ((FYEO_display *) Display == Process.Display)) {
            /* Do some processing */
            switch (FYEO_do_process(&Process)) {
                case OK:
                /* Ok , update the window */
                FYEO_update_window((FYEO_display *) Display);
                break;
            case END:
                /* End , terminate process and unclaim idle events */
                FYEO_terminate_process(&Process);
                FYEO_title_waiting((FYEO_display *) Display);
                win_claim_idle_events(-1);
                Status = WAITING;
                break;
            case ERREUR:
                /* * Erreur, abort process and destroy display */
                FYEO_abort_process(&Process);
                FYEO_destroy_display((FYEO_display *) Display);
                win_claim_idle_events(-1);
                Status = WAITING;
                break;
            default:
                break;
            }
        }
        break;
    case wimp_EREDRAW:
       {
        BOOL More;
        wimp_redrawstr R;

        R.w = ((FYEO_display *) Display) -> WinHandle;
        wimp_redraw_wind(&R, &More);
        while (More) {
            FYEO_redo_window((FYEO_display *) Display, R);
            wimp_get_rectangle(&R, &More);
        }
       }

        break;

    case wimp_EOPEN:
        wimp_open_wind(&E -> data.o);
        break;
/*
    case wimp_EKEY:
        switch(E->data.key.chcode) {
         case 32: {
           os_swi(OS_GBPB,9,);
         };
         default:
        }
        break;
*/
    case wimp_EBUT:
        os_swi6(Wimp_SetCaretPosition,E->data.c.w,-1,0,0,-1,-1);
        break;

 /* We intercept both the mouse buttons and the shift key state so
    that we can react like the filer windows. JM */
    case wimp_ECLOSE:
        wimp_get_point_info(&But);
        Shift=akbd_pollsh();

        if (But.bbits == wimp_BRIGHT) {
         strcat(OSCall,((FYEO_display *) Display) -> PicName);
         path(OSCall);   /* replaces last '.' with '\0' */
         os_cli(OSCall); /* filer_opendir on picture path */
        }

        if ((Shift && (But.bbits == wimp_BLEFT)) || (!Shift)) {
          /*** If processing and not active window : close forbidden ***/
          if (Status == WAITING)
              FYEO_destroy_display((FYEO_display *) Display);
          /*** If active window , abort process and destroy display ***/
          if ((Status == ACTIVE) && ((FYEO_display *) Display == Process.Display)) {
              FYEO_abort_process(&Process);
              FYEO_destroy_display((FYEO_display *) Display);
              Status = WAITING;
          }
        }
        break;

    case wimp_ESEND:
    case wimp_ESENDWANTACK:
        switch (E -> data.msg.hdr.action) {
        case wimp_MHELPREQUEST:
            E -> data.msg.hdr.your_ref = E -> data.msg.hdr.my_ref;
            E -> data.msg.hdr.action = wimp_MHELPREPLY;
            E -> data.msg.hdr.size = 256;
            sprintf(E -> data.msg.data.helpreply.text,msgs_lookup("PicWinH"));
            wimp_sendmessage(wimp_ESEND, &E -> data.msg, E -> data.msg.hdr.task);
            break;
        case wimp_MDATASAVE:
            E -> data.msg.hdr.your_ref = E -> data.msg.hdr.my_ref;
            E -> data.msg.hdr.action = wimp_MDATASAVEOK;
            E -> data.msg.hdr.size = 256;
            E -> data.msg.data.datasaveok.estsize = -1;
            sprintf(E -> data.msg.data.datasaveok.name,"<Wimp$Scrap>");
            wimp_sendmessage(wimp_ESEND, &E -> data.msg, E -> data.msg.hdr.task);
            break;
        case wimp_MDATALOAD:
            /* Pic in same window */
            if (!(FYEO_load_file(Display,WINDOW))) erreur(msgs_lookup("DRTypeE"));
            break;
        default:
            break;
        }
        break;
    default:
        break;
    }
}

/** Pic unknow event handler */
static BOOL FYEO_window_unknown_handler(wimp_eventstr * E, void *Display) {
    switch (E -> data.msg.hdr.action) {
    case wimp_PALETTECHANGE:
        mysprite_notify_display_change(((FYEO_display *) (Display)) -> Sprite);
        /* Pass the events to other handlers */
        return(FALSE);
        break;
    case wimp_MMODECHANGE:
        mysprite_notify_display_change(((FYEO_display *) (Display)) -> Sprite);
        FYEO_resize_window((FYEO_display *) Display);
        /* Pass the events to other handlers */
        return(FALSE);
        break;
    default:
        break;
    }
    return (FALSE);
}

/************************/
/* Icone Event handlers */
/************************/
/** Called when click on icon bar */
static void FYEO_left_icon_click(wimp_i I) {
    FYEO_global_settings_init(&NewSettings);
}

/** Icon bar unknown event handler */
static BOOL FYEO_icon_unknown_handler(wimp_eventstr * E, void *Handle) {
    switch (E -> e) {
     case wimp_ENULL:
      if (ToDoTail!=ToDoHead) {
       FYEO_force_file(ToDoList[ToDoTail]);
       ToDoTail=((ToDoTail+1)%ToDoSize);
      }
      return(TRUE);
      break;
    case wimp_ESEND:
    case wimp_ESENDWANTACK:
       switch (E -> data.msg.hdr.action) {
       case wimp_MDATAOPEN:
          return (FYEO_load_file(NULL,DATAOPEN));
          break;
       case wimp_MMODECHANGE:
          output_settings_mode_change_dependencies(&NewSettings.Output);
          /* Allows mode change to be passed on */
          return(FALSE);
          break;
       default:
          return(FALSE);
          break;
       };
       break;
    default:
       return (FALSE);
       break;
    }
}

/** Called when dragging a file on icon bar */
static void FYEO_icon_load_handler(wimp_eventstr * E, void *Handle) {
    switch (E -> data.msg.hdr.action) {
    case wimp_MDATASAVE:
            E -> data.msg.hdr.your_ref = E -> data.msg.hdr.my_ref;
            E -> data.msg.hdr.action = wimp_MDATASAVEOK;
            E -> data.msg.hdr.size = 256;
            E -> data.msg.data.datasaveok.estsize = -1;
            sprintf(E -> data.msg.data.datasaveok.name,"<Wimp$Scrap>");
            wimp_sendmessage(wimp_ESEND, &E -> data.msg, E -> data.msg.hdr.task);
        break;
    case wimp_MDATALOAD:
        /* Need a new window */
        if (!(FYEO_load_file(NULL,ICONBAR))) erreur(msgs_lookup("DRTypeE"));
        break;
    default:
        break;
    }
}

/************/
/* Settings */
/************/

/** Load settings */
static void FYEO_load_settings(FYEO_settings * Settings)
{
   if ( !settings_load(Settings) )
   {
      erreur("Unable to find preferences file, using defaults. Please set FYEO2's preferences...");
      output_settings_default(&Settings -> Output);
      input_settings_default(&Settings -> Input);
      misc_settings_default(&Settings -> Misc);
   }
   /* Mode dependencies */
   output_settings_mode_change_dependencies(&Settings -> Output);

   return;
}

/*************************************************/
/* Initialisation - terminaison de l'application */
/*************************************************/
/** Loading of dither table */
/** Iconbar initialisation */
static void FYEO_icone_init(void) {
    /* Put icon on iconbar */
    baricon("fyeo_bar",(int)resspr_area(), FYEO_left_icon_click);

    /* Menu creation */
    FYEO_create_icon_menu();

    /* Attach menu and menu proc to Icon bar */
    event_attachmenu(win_ICONBAR, Icone_Menu, FYEO_icon_menu_proc, 0);

    /* Registers an event handler */
    win_register_event_handler(win_ICONBARLOAD, FYEO_icon_load_handler, 0);

    /* Adds an unknown event processor */
    win_add_unknown_event_processor(FYEO_icon_unknown_handler, 0);
}

/** Iconbar terminaison */
static void FYEO_icone_terminate(void) {
    /* Remove the unknow event processor */
    win_remove_unknown_event_processor(FYEO_icon_unknown_handler, 0);

    /* Unregisters an event handler */
    win_register_event_handler(win_ICONBARLOAD, 0, 0);

    /* Remove menu and menu proc attachement */
    event_attachmenu(win_ICONBAR,0,FYEO_icon_menu_proc,0);

    FYEO_destroy_icon_menu();
}

/** Application initialisation */
static void FYEO_initialise(void) {

    /* External inits */
    res_init("FYEO");

    // Watch out here ! The RiscOS library seems to search for a "Sprites"
    // sprite file in the Application directory . If it's present,
    // resspr_area() is the sprite area containing this file. If it's not
    // present, the Wimp sprite area is used. The problem is that if you want
    // to use sprites from both areas, you're stuck, because references to
    // "forbidden" sprite area will lead to errors.
    if(wimpt_init("FYEO")>=300) {
       resspr_init();
       template_init();
    } else {
       template_init();
       resspr_init();
    }

    dbox_init();

    flex_init();
    visdelay_init();
    msgs_init();

    mysprite_init(); /* Warning : to be called after flex_init, msgs_init and visdelay_init */
    init_range_table();

    /* Internal inits */
    FYEO_init_dlist();
    FYEO_init_boxes();
    FYEO_icone_init();

    /* Init status et settings */
    Status = WAITING;
    FYEO_load_settings(&NewSettings);
    Settings = NewSettings;

    /* Creation du menu dither */
    DitherMenu = menu_new("Dithering",msgs_lookup("DitMenu"));
    DMPtr = menu_syshandle(DitherMenu);

    FYEO_welcome();
}

/** Application terminaison */
static void FYEO_terminate(void) {
    FYEO_icone_terminate();

    /* Destruction du menu dither */
    menu_dispose(&DitherMenu,FALSE);

    exit(0);
}


#ifdef SIGNALTRAPPING
static void sigtrap(int sig)
{
   _kernel_oserror oe;

   oe.errnum = 1;
   sprintf(oe.errmess, "Encountered unexpected internal error (type %d), aborting...", sig);

   exit(EXIT_FAILURE);
}
#endif


/***********************/
/* Fonction principale */
/***********************/

int main(int Argc, char *Argv[]) {

#ifdef SIGNALTRAPPING
   // Rick 2004/03/06, an attempt to trap errors with damaged files...
   signal(SIGABRT, sigtrap);
   signal(SIGFPE,  sigtrap);
   signal(SIGILL,  sigtrap);
   signal(SIGINT,  sigtrap);
   signal(SIGSEGV, sigtrap);
   signal(SIGTERM, sigtrap);
   signal(SIGSTAK, sigtrap);
   signal(SIGUSR1, sigtrap);
   signal(SIGUSR2, sigtrap);
   signal(SIGOSERROR, sigtrap);
#endif

    /** Whole initialisation */
    FYEO_initialise();
    /** Set event mask , IDLE events are grabbed */
    event_setmask((wimp_emask)0);
    /** Get arguments if any */
    if (Argc >= 2)
        FYEO_force_file(Argv[1]);
    /** Handle events */
    while (TRUE)
        event_process();
}


// RICK 2004/01/29 so other modules can push images into the to-do list

void push_into_todo(char *filename)
{
   int Next = (ToDoHead + 1) % ToDoSize;
   if (Next != ToDoTail)
   {
      strcpy(ToDoList[ToDoHead], filename);
      ToDoHead = Next;
       // xferrecv_insertfileok(); there won't be any to reply to...
   }
   else
   {
      erreur(msgs_lookup("TooMuchE"));
   }

   return;
}



void doing_jpegtrans_display(void)
{
   // dbox is already defined, module-globally, as "jpegtransify"
   wimp_wstate State;
   wimp_box Box;
   int WWidth, WHeight, MWidth, MHeight;
   dlist *Current;

   jpegtransify = dbox_new("jpegtranify");

   dbox_showstatic(jpegtransify);

   /* center to screen */
   wimp_get_wind_state(dbox_syshandle(jpegtransify),&State);
   Box = State.o.box;
   WWidth = Box.x1 - Box.x0;
   WHeight = Box.y1 - Box.y0;
   /* -1 Means cur mode */
   /* Get mode dimensions in OS units */
   MWidth = get_mode_width(-1) * ( 180 / get_mode_xdpi(-1));
   MHeight = get_mode_height(-1) * ( 180 / get_mode_ydpi(-1));
   Box.x0 = (MWidth - WWidth)/2;
   Box.y0 = (MHeight - WHeight)/2;
   Box.x1 = Box.x0 + WWidth;
   Box.y1 = Box.y0 + WHeight;

   State.o.behind = -1;
   State.o.box = Box;
   wimp_open_wind(&State.o);

   // kill off the display attached to the current process,
   // we SO do not want a blank window to appear!
   Current = TheDisplay;
   while (Current -> Next != NULL)
      Current = Current -> Next;
   wimp_close_wind(Current -> Display -> WinHandle);
   // above, we're ASSUMING that the window we want to close is the last one to
   // have been created - which it pretty-much should be, okay? :-)
   // We *only* CLOSE the window definition, something else will do the necessary
   // unlinking &crap for us.

   // ensure the "what's going on now" gets displayed on the screen
   for (int idleloop = 0; idleloop < 5; idleloop++)
      event_process();

   // (we'll keep it on-screen during the jpegtransing)

   return;
}



void doing_jpegtrans_undisplay(void)
{
   // waste the "what's going on now" message;
   // the world sucks, we don't want to know what's going on
   dbox_dispose(&jpegtransify);

   return;
}
