/*
 * AcornGUI.cc
 *
 * Frodo's Graphical User Interface for Acorn RISC OS machines (WIMP)
 * (C) 1997 Andreas Dehmel
 *
 */



#include "sysdeps.h"

#include "C64.h"
#include "VIC.h"
#include "Display.h"
#include "Prefs.h"
#include "main.h"
#include "ROlib.h"
#include "AcornGUI.h"
#include "Version.h"



#define CUSTOM_SPRITES	"FrodoRsrc:Sprites"
#define TEMPLATES_FILE	"FrodoRsrc:Templates"
#define WIMP_SCRAP_FILE	"<Wimp$Scrap>"


#define FileType_Data		0xffd
#define FileType_Text		0xfff
#define FileType_C64File	0x064
#define FileType_D64File	0x164

#define IconSpriteSize		68	// OS units of an icon sprite (for dragging)
#define EstimatedPrefsSize	1024	// can't say how big it'll be before saving...
#define EstimatedConfSize	256
#define EstimatedRAMSize	0x10400
#define EstimatedSnapSize	0x11000




// For the scanning of the joystick keys in the SysConfig window
#define IntKey_ScanFrom		3
// This key gets translated to 0xff, i.e. none (joystick inactive)
#define IntKey_Void		44


#define Key_Return		13




// LED number to Icon number translation:
char LEDtoIcon[4] = {Icon_Pane_LED0, Icon_Pane_LED1, Icon_Pane_LED2, Icon_Pane_LED3};
// Drive number to Icon number (Pane) translation:
char PDrvToIcon[4] = {Icon_Pane_Drive0, Icon_Pane_Drive1, Icon_Pane_Drive2, Icon_Pane_Drive3};
// Drive number to Icon number (Prefs) translation:
#define IconsPerDrive	4
char DriveToIcon[16] = {
  Icon_Prefs_Dr8DIR,  Icon_Prefs_Dr8D64,  Icon_Prefs_Dr8T64,  Icon_Prefs_Dr8Path,
  Icon_Prefs_Dr9DIR,  Icon_Prefs_Dr9D64,  Icon_Prefs_Dr9T64,  Icon_Prefs_Dr9Path,
  Icon_Prefs_Dr10DIR, Icon_Prefs_Dr10D64, Icon_Prefs_Dr10T64, Icon_Prefs_Dr10Path,
  Icon_Prefs_Dr11DIR, Icon_Prefs_Dr11D64, Icon_Prefs_Dr11T64, Icon_Prefs_Dr11Path
};
// SID type to Icon number translation:
char SIDtoIcon[3] = {
  Icon_Prefs_SIDNone, Icon_Prefs_SIDDigi, Icon_Prefs_SIDCard
};
// REU type to Icon number translation:
char REUtoIcon[4] = {
  Icon_Prefs_REUNone, Icon_Prefs_REU128, Icon_Prefs_REU256, Icon_Prefs_REU512
};
char KJoyToIcon[2][5] = {
  {Icon_Conf_Joy1Up, Icon_Conf_Joy1Down, Icon_Conf_Joy1Left, Icon_Conf_Joy1Right, Icon_Conf_Joy1Fire},
  {Icon_Conf_Joy2Up, Icon_Conf_Joy2Down, Icon_Conf_Joy2Left, Icon_Conf_Joy2Right, Icon_Conf_Joy2Fire}
};





// Internal keynumbers lookup table
// Single characters are represented by single chars, others by pointers to strings
int IntKeyTable[128] = {
  (int)"Shft", (int)"Ctrl", (int)"Alt", (int)"ShftL",
  (int)"CtrlL", (int)"AltL", (int)"ShftR", (int)"CtrlR",
  (int)"AltR", (int)"Slct", (int)"Menu", (int)"Adjst",
  0, 0, 0, 0,
  'q', '3', '4', '5',
  (int)"F4", '8', (int)"F7", '-',
  '6', (int)"Left", (int)"num6", (int)"num7",
  (int)"F11", (int)"F12", (int)"F10", (int)"ScLck",
  (int)"Prnt", 'w', 'e', 't',
  '7', 'i', '9', '0',
  '-', (int)"Down", (int)"num8", (int)"num9",
  (int)"Brk", '`', '£', (int)"Del",
  '1', '2', 'd', 'r',
  '6', 'u', 'o', 'p',
  '[', (int)"Up", (int)"num+", (int)"num-",
  (int)"nmEnt", (int)"Isrt", (int)"Home", (int)"PgUp",
  (int)"CpLck", 'a', 'x', 'f',
  'y', 'j', 'k', '2',
  ';', (int)"Ret", (int)"num/", 0,
  (int)"num.", (int)"nmLck", (int)"PgDwn", '\'',
  0, 's', 'c', 'g',
  'h', 'n', 'l', ';',
  ']', (int)"Del", (int)"num#", (int)"num*",
  0, '=', (int)"Extra", 0,
  (int)"Tab", 'z', (int)"Space", 'v',
  'b', 'm', ',', '.',
  '/', (int)"Copy", (int)"num0", (int)"num1",
  (int)"num3", 0, 0, 0,
  (int)"Esc", (int)"F1", (int)"F2", (int)"F3",
  (int)"F5", (int)"F6", (int)"F8", (int)"F9",
  '\\', (int)"Right", (int)"num4", (int)"num5",
  (int)"num2", 0, 0, 0
};





// The icon bar icon
RO_IconDesc IBarIcon = {
  -1, 0, 0, 68, 68, 0x301a,
#ifdef FRODO_SC
  "!frodosc"
#else
# ifdef FRODO_PC
  "!frodopc"
# else
  "!frodo"
# endif
#endif
};





// The menus

char *MIBTextPrefs = "Preferences...";
char *MIBTextConf = "Configuration...";


struct MenuIconBar {
  RO_MenuHead head;
  RO_MenuItem item[Menu_IBar_Items];
} MenuIconBar = {
  {
#ifdef FRODO_SC
  "FrodoSC",
#else
# ifdef FRODO_PC
  "FrodoPC",
# else
  "Frodo",
# endif
#endif
  7, 2, 7, 0, Menu_IBar_Width, Menu_Height, 0},
  {
    {0, (RO_MenuHead*)-1, Menu_Flags, "Info"},
    {0, (RO_MenuHead*)-1, Menu_Flags + IFlg_Indir, ""},
    {0, (RO_MenuHead*)-1, Menu_Flags + IFlg_Indir, ""},
    {0, (RO_MenuHead*)-1, Menu_Flags, "Sound"},
    {MFlg_LastItem, (RO_MenuHead*)-1, Menu_Flags, "Quit"}
  }
};

struct MenuEmuWindow {
  RO_MenuHead head;
  RO_MenuItem item[Menu_EWind_Items];
} MenuEmuWindow = {
  {
#ifdef FRODO_SC
    "FrodoSC Job",
#else
# ifdef FRODO_PC
    "FrodoPC Job",
# else
    "Frodo Job",
# endif
#endif
    7, 2, 7, 0, Menu_EWind_Width, Menu_Height, 0},
  {
    {0, (RO_MenuHead*)-1, Menu_Flags, "Info"},
    {0, (RO_MenuHead*)-1, Menu_Flags, "Sound"},
    {MFlg_Warning, (RO_MenuHead*)-1, Menu_Flags, "Save RAM"},
    {MFlg_LastItem | MFlg_Warning, (RO_MenuHead*)-1, Menu_Flags, "Snapshot"}
  }
};




// For less writing in LoadATemplate...
#define wic(base,no,off) base[RO_WINDOW_WORDS + no*RO_ICON_WORDS + off]

// For less writing in WindowToThePrefs...
#define pread_opt(var,icn)	PrefsWindow->GetIconState(Icon_Prefs_##icn,AuxBlock);\
				prefs->##var = ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0) ? false : true;



//
// WIMP member-functions
//

WIMP::WIMP(C64 *my_c64)
{
  int x,y,i;
  WIdata *wid;

  CMOS_DragType = ReadDragType();

  // Determine the dimensions of the icon bar sprite first.
  if (Wimp_SpriteInfo((char*)(&IBarIcon.dat),&x,&y,&i) == NULL)
  {
    IBarIcon.maxx = x << OS_ReadModeVariable(i,4);
    IBarIcon.maxy = y << OS_ReadModeVariable(i,5);
  }

  IBicon = new Icon(0,&IBarIcon); Mask = 0x00000830;
  LastMenu = LastClick = LastDrag = MenuType = DragType = 0;
  UseScrap = false; EmuPaused = false; UseNULL = 0;	// one NULL client - the emulator
  the_c64 = my_c64;
  EmuZoom = 1;

  // Read the customized sprite area (LEDs etc)
  if ((ReadCatalogueInfo(CUSTOM_SPRITES,AuxBlock) & 1) != 0)	// (image) file
  {
    int i;
    FILE *fp;

    i = AuxBlock[2]; SpriteArea = (int*)(new char[i+16]);	// allocate space for sprite
    fp = fopen(CUSTOM_SPRITES,"rb");
    i = fread(SpriteArea+1,1,i,fp); SpriteArea[0] = i+4;
    fclose(fp);
  }
  else
  {
    WimpError.errnum = 0; sprintf(WimpError.errmess,"Can't read sprites!!!");
    Wimp_ReportError(&WimpError,1,TASKNAME);
  }

  // Read Template and create windows
  if (Wimp_OpenTemplate(TEMPLATES_FILE) == NULL)
  {
    // Load Pane first
    LoadATemplate("EmuPane",&EmuPane);
    LoadATemplate("EmuWindow",&EmuWindow);
    LoadATemplate("ThePrefs",&PrefsWindow);
    LoadATemplate("ConfigWind",&ConfigWindow);
    LoadATemplate("InfoWindow",&InfoWindow);
    LoadATemplate("SoundWindow",&SoundWindow);
    LoadATemplate("SaveBox",&SaveBox);
  }
  else
  {
    Wimp_ReportError(&WimpError,1,TASKNAME);
    WimpError.errnum = 0; sprintf(WimpError.errmess,"Error in Templates!!!");
  }
  Wimp_CloseTemplate();

  // Add info window to icon bar menu
  MenuIconBar.item[Menu_IBar_Info].submenu  =
  MenuEmuWindow.item[Menu_EWind_Info].submenu = (RO_MenuHead*)InfoWindow->MyHandle();
  MenuIconBar.item[Menu_IBar_Sound].submenu =
  MenuEmuWindow.item[Menu_EWind_Sound].submenu = (RO_MenuHead*)SoundWindow->MyHandle();
  MenuEmuWindow.item[Menu_EWind_SaveRAM].submenu =
  MenuEmuWindow.item[Menu_EWind_Snapshot].submenu = (RO_MenuHead*)SaveBox->MyHandle();
  // I couldn't find ONE FUCKING WAY to do this in the initialisation directly and I'm
  // REALLY PISSED OFF!
  wid = &(MenuIconBar.item[Menu_IBar_Prefs].dat);
  wid->ind.tit = (int*)MIBTextPrefs; wid->ind.val = NULL; wid->ind.len = sizeof(MIBTextPrefs);
  wid = &(MenuIconBar.item[Menu_IBar_Config].dat);
  wid->ind.tit = (int*)MIBTextConf; wid->ind.val = NULL; wid->ind.len = sizeof(MIBTextConf);

  // Write default path into config window
  ConfigWindow->WriteIconText(Icon_Conf_ConfPath,DEFAULT_SYSCONF);

  // Set up the contents of the prefs window and init with the default prefs path
  ThePrefsToWindow();
  PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,DEFAULT_PREFS);
  // Grey out SID card icon -- not supported!
  PrefsWindow->SetIconState(Icon_Prefs_SIDCard,IFlg_Grey,IFlg_Grey);

  // Open Emulator window + pane in the center of the screen and give it the input focus
  Wimp_GetCaretPosition(&LastCaret);
  OpenEmuWindow();
  Wimp_SetCaretPosition(EmuWindow->MyHandle(),-1,-100,100,-1,-1); // emu window gets input focus

  // Init export files
  sprintf(RAMFile+44,"C64_RAM"); sprintf(SnapFile+44,"FrodoSnap");
}


WIMP::~WIMP(void)
{
  delete IBicon;
  delete EmuWindow; delete EmuPane; delete PrefsWindow; delete ConfigWindow;
  delete InfoWindow; delete SoundWindow; delete SaveBox;
}


bool WIMP::LoadATemplate(char *Name, Window **Which)
{
  char *Buffer, *Indirect, *Desc;
  int IndSize, Pos;
  char TempName[12];

  strncpy((char*)TempName,Name,12);
  Buffer = NULL; Pos = 0;
  if (Wimp_LoadTemplate(&Buffer,&Indirect,0,(char*)-1,TempName,&Pos) != NULL) {return(false);}
  Buffer = new char[(int)Buffer + 4];
  IndSize = (int)Indirect; Indirect = new char[IndSize];
  Pos = 0; Desc = Buffer+4;
  Wimp_LoadTemplate(&Desc,&Indirect,Indirect+IndSize,(char*)-1,TempName,&Pos);
  if (Which == &EmuWindow)
  {
    int eigen, dx, dy;
    RO_Window *row = (RO_Window*)Buffer;
    RORes res;

    // Center emulator window on screen
    eigen = (res.eigx < res.eigy) ? res.eigx : res.eigy;
    dx = (DISPLAY_X << eigen); dy = (DISPLAY_Y << eigen);
    row->vminx = res.resx/2 - dx/2; row->vmaxx = row->vminx + dx;
    row->vminy = res.resy/2 - dy/2; row->vmaxy = row->vminy + dy;
    row->wminy = -dy; row->wmaxx = dx;
    Indirect = (char*)VERSION_STRING;
  }
  else
  {
    if (Which == &EmuPane)	// EmuPane needs customized sprites
    {
      register RO_Window *row = (RO_Window*)Buffer;
      register int *b = (int*)Buffer;

      // Insert sprite pointer in window and icon definitions
      row->SpriteAreaPtr = (int)SpriteArea;
      wic(b,Icon_Pane_LED0,RawIB_Data1) = wic(b,Icon_Pane_LED1,RawIB_Data1) =
      wic(b,Icon_Pane_LED2,RawIB_Data1) = wic(b,Icon_Pane_LED3,RawIB_Data1) = (int)SpriteArea;
      sprintf((char*)wic(b,Icon_Pane_Pause,RawIB_Data0),PANE_TEXT_PAUSE);
      sprintf((char*)wic(b,Icon_Pane_Toggle,RawIB_Data0),PANE_TEXT_ZOOM2);
    }
    else if (Which == &SoundWindow)	// ditto
    {
      register RO_Window *row = (RO_Window*)Buffer;
      register int *b = (int*)Buffer;
      register int orr;

      row->SpriteAreaPtr = (int)SpriteArea;
      // if sound emulation off grey out notes
      if (ThePrefs.SIDType == SIDTYPE_NONE) {orr = IFlg_Grey;} else {orr = 0;}
      wic(b,Icon_Sound_Notes,RawIB_Flags)=(wic(b,Icon_Sound_Notes,RawIB_Flags)&~IFlg_Grey)|orr;
    }
    else if (Which == &InfoWindow)	// ditto
    {
      register RO_Window *row = (RO_Window*)Buffer;

      row->SpriteAreaPtr = (int)SpriteArea;
    }
    Indirect = NULL;
  }
  *Which = new Window((int*)Buffer,Indirect);
  return(true);
}


// All changes in position of the Emulator window have to be treated seperately
// to keep the pane attached to it at all times!
void WIMP::OpenEmuWindow(void)
{
  RO_Window *wind;
  int work[WindowB_WFlags], i;

  wind = EmuWindow->Descriptor();
  EmuPane->GetWorkArea(&work[WindowB_VMinX]); i = work[WindowB_VMaxX] - work[WindowB_VMinX];
  if ((work[WindowB_VMinX] = wind->vminx - EmuPaneSpace - i) < 0)
  {
    // if main window still on screen then keep pane on screen as well
    if (wind->vminx >= 0) {work[WindowB_VMinX] = 0;}
    // otherwise align pane and main window on the left
    else {work[WindowB_VMinX] = wind->vminx;}
  }
  work[WindowB_VMaxX] = work[WindowB_VMinX] + i;
  work[WindowB_VMinY] = wind->vmaxy - (work[WindowB_VMaxY]-work[WindowB_VMinY]);
  work[WindowB_VMaxY] = wind->vmaxy;
  work[WindowB_Handle] = EmuPane->MyHandle();
  work[WindowB_ScrollX] = work[WindowB_ScrollY] = 0;
  work[WindowB_Stackpos] = -1;	// open on top of stack
  // open the pane first
  EmuPane->open(work);
  wind->stackpos = EmuPane->MyHandle();	// open window behind pane.
  EmuWindow->open();
}


void WIMP::OpenEmuWindow(int *Where)
{
  int work[WindowB_WFlags], i;

  EmuPane->GetWorkArea(&work[WindowB_VMinX]); i = work[WindowB_VMaxX] - work[WindowB_VMinX];
  if ((work[WindowB_VMinX] = Where[WindowB_VMinX] - EmuPaneSpace - i) < 0)
  {
    if (Where[WindowB_VMinX] >= 0) {work[WindowB_VMinX] = 0;}
    else {work[WindowB_VMinX] = Where[WindowB_VMinX];}
  }
  work[WindowB_VMaxX] = work[WindowB_VMinX] + i;
  work[WindowB_VMinY] = Where[WindowB_VMaxY] - (work[WindowB_VMaxY]-work[WindowB_VMinY]);
  work[WindowB_VMaxY] = Where[WindowB_VMaxY];
  work[WindowB_Handle] = EmuPane->MyHandle();
  work[WindowB_ScrollX] = work[WindowB_ScrollY] = 0;
  work[WindowB_Stackpos] = Where[WindowB_Stackpos];
  // open the pane first
  EmuPane->open(work);
  Where[WindowB_Stackpos] = EmuPane->MyHandle();	// open window behind pane
  EmuWindow->open(Where);
}


void WIMP::CloseEmuWindow(void)
{
  EmuWindow->close(); EmuPane->close();
}


void WIMP::UpdateEmuWindow(void)
{
  C64Display *disp = the_c64->TheDisplay;

  EmuWindow->update(disp->BitmapBase(),disp);
}


// Write the values given in ThePrefs into the Prefs Window
void WIMP::ThePrefsToWindow(void)
{
  int i, j, k;

  // Update the data for each of the drives
  for (i=0; i<4; i++)
  {
    switch(ThePrefs.DriveType[i])
    {
      case DRVTYPE_D64: j=1; break;
      case DRVTYPE_T64: j=2; break;
      default: j=0; break;	// otherwise type 0 = DIR
    }
    // unselect all but other icons
    for (k=0; k<3; k++)
    {
      if (k != j) {PrefsWindow->SetIconState(DriveToIcon[i*IconsPerDrive + k],0,IFlg_Slct);}
    }
    // and select the correct one.
    PrefsWindow->SetIconState(DriveToIcon[i*IconsPerDrive + j],IFlg_Slct,IFlg_Slct);
    // Now update the path name (buffer won't change, i.e. use original data)
    PrefsWindow->WriteIconText(DriveToIcon[i*IconsPerDrive + 3],ThePrefs.DrivePath[i]);
  }
  if (ThePrefs.Emul1541Proc) {i = IFlg_Slct;} else {i = 0;}
  PrefsWindow->SetIconState(Icon_Prefs_Emul1541,i,IFlg_Slct);
  SetLEDIcons(ThePrefs.Emul1541Proc);
  if (ThePrefs.MapSlash) {i = IFlg_Slct;} else {i = 0;}
  PrefsWindow->SetIconState(Icon_Prefs_MapSlash,i,IFlg_Slct);

  // SID prefs
  switch (ThePrefs.SIDType)
  {
    case SIDTYPE_DIGITAL: i = 1; break;
    case SIDTYPE_SIDCARD: i = 2; break;
    default: i = 0; break;	// includes NONE
  }
  for (j=0; j<3; j++)
  {
    if (j != i) {PrefsWindow->SetIconState(SIDtoIcon[j],0,IFlg_Slct);}
  }
  PrefsWindow->SetIconState(SIDtoIcon[i],IFlg_Slct,IFlg_Slct);
  PrefsWindow->SetIconState(Icon_Prefs_SIDFilter,(ThePrefs.SIDFilters)?IFlg_Slct:0,IFlg_Slct);

  // REU prefs
  switch (ThePrefs.REUSize)
  {
    case REU_128K: i=1; break;
    case REU_256K: i=2; break;
    case REU_512K: i=3; break;
    default: i=0; break;
  }
  for (j=0; j<4; j++)
  {
    if (j != i) {PrefsWindow->SetIconState(REUtoIcon[j],0,IFlg_Slct);}
  }
  PrefsWindow->SetIconState(REUtoIcon[i],IFlg_Slct,IFlg_Slct);

  // Skip Frames
  PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,ThePrefs.SkipFrames);

  // Sprites
  PrefsWindow->SetIconState(Icon_Prefs_SprOn,(ThePrefs.SpritesOn)?IFlg_Slct:0,IFlg_Slct);
  PrefsWindow->SetIconState(Icon_Prefs_SprColl,(ThePrefs.SpriteCollisions)?IFlg_Slct:0,IFlg_Slct);
  // Joystick
  PrefsWindow->SetIconState(Icon_Prefs_Joy1On,(ThePrefs.Joystick1On)?IFlg_Slct:0,IFlg_Slct);
  PrefsWindow->SetIconState(Icon_Prefs_Joy2On,(ThePrefs.Joystick2On)?IFlg_Slct:0,IFlg_Slct);
  PrefsWindow->SetIconState(Icon_Prefs_JoySwap,(ThePrefs.JoystickSwap)?IFlg_Slct:0,IFlg_Slct);

  // Misc
  SetSpeedLimiter(ThePrefs.LimitSpeed);
  PrefsWindow->SetIconState(Icon_Prefs_FastReset,(ThePrefs.FastReset)?IFlg_Slct:0,IFlg_Slct);
  PrefsWindow->SetIconState(Icon_Prefs_CIAHack,(ThePrefs.CIAIRQHack)?IFlg_Slct:0,IFlg_Slct);

  // Cycles
  PrefsWindow->WriteIconNumber(Icon_Prefs_CycleNorm,ThePrefs.NormalCycles);
  PrefsWindow->WriteIconNumber(Icon_Prefs_CycleBad,ThePrefs.BadLineCycles);
  PrefsWindow->WriteIconNumber(Icon_Prefs_CycleCIA,ThePrefs.CIACycles);
  PrefsWindow->WriteIconNumber(Icon_Prefs_CycleFloppy,ThePrefs.FloppyCycles);
}


// Update ThePrefs according to the values given in the Prefs Window
void WIMP::WindowToThePrefs(void)
{
  int i, j, k;
  Prefs *prefs = new Prefs(ThePrefs);

  for (i=0; i<4; i++)
  {
    // find out which of the drive type icons is selected
    j = -1;
    do
    {
      ++j; PrefsWindow->GetIconState(DriveToIcon[i*IconsPerDrive + j],AuxBlock);
    }
    while ((j < 3) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0));
    switch (j)
    {
      case 1:  prefs->DriveType[i] = DRVTYPE_D64; break;
      case 2:  prefs->DriveType[i] = DRVTYPE_T64; break;
      default: prefs->DriveType[i] = DRVTYPE_DIR; break;
    }
    strcpy(prefs->DrivePath[i],PrefsWindow->ReadIconText(DriveToIcon[i*IconsPerDrive + 3]));
  }
  // Emulation of the 1541 processor is a special case as it also affects LED1-3
  pread_opt(Emul1541Proc,Emul1541);
  SetLEDIcons(prefs->Emul1541Proc);
  pread_opt(MapSlash,MapSlash);

  // SID
  j = -1;
  do
  {
    ++j; PrefsWindow->GetIconState(SIDtoIcon[j],AuxBlock);
  }
  while ((j < 3) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0));
  switch (j)
  {
    case 1:  prefs->SIDType = SIDTYPE_DIGITAL; break;
    case 2:  prefs->SIDType = SIDTYPE_SIDCARD; break;
    default: prefs->SIDType = SIDTYPE_NONE; break;
  }
  pread_opt(SIDFilters,SIDFilter);
  SoundWindow->SetIconState(Icon_Sound_Notes,(prefs->SIDType==SIDTYPE_NONE)?IFlg_Grey:0,IFlg_Grey);

  // REU
  j = -1;
  do
  {
    ++j; PrefsWindow->GetIconState(REUtoIcon[j],AuxBlock);
  }
  while ((j < 4) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0));
  switch (j)
  {
    case 1:  prefs->REUSize = REU_128K; break;
    case 2:  prefs->REUSize = REU_256K; break;
    case 3:  prefs->REUSize = REU_512K; break;
    default: prefs->REUSize = REU_NONE; break;
  }

  // Skip Frames
  prefs->SkipFrames = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText);

  // Sprites
  pread_opt(SpritesOn,SprOn); pread_opt(SpriteCollisions,SprColl);

  // Joystick
  pread_opt(Joystick1On,Joy1On); pread_opt(Joystick2On,Joy2On); pread_opt(JoystickSwap,JoySwap);

  // Misc
  pread_opt(LimitSpeed,LimSpeed); pread_opt(FastReset,FastReset); pread_opt(CIAIRQHack,CIAHack);

  // Cycles
  prefs->NormalCycles	= PrefsWindow->ReadIconNumber(Icon_Prefs_CycleNorm);
  prefs->BadLineCycles	= PrefsWindow->ReadIconNumber(Icon_Prefs_CycleBad);
  prefs->CIACycles	= PrefsWindow->ReadIconNumber(Icon_Prefs_CycleCIA);
  prefs->FloppyCycles	= PrefsWindow->ReadIconNumber(Icon_Prefs_CycleFloppy);

  // Finally make the changes known to the system:
  the_c64->NewPrefs(prefs);
  ThePrefs = *prefs;
  delete prefs;
}


// Update the SysConfig window according to the values used
void WIMP::SysConfToWindow(void)
{
  int i, j, k;
  Joy_Keys *jk;
  char OneChar[2], *b;

  OneChar[1] = 0;

  // Write timings
  the_c64->ReadTimings(&i,&j,&k);
  ConfigWindow->WriteIconNumber(Icon_Conf_PollAfter, i);
  ConfigWindow->WriteIconNumber(Icon_Conf_SpeedAfter, j);
  ConfigWindow->WriteIconNumber(Icon_Conf_SoundAfter, k);

  // Write joystick keys
  for (i=0; i<2; i++)
  {
    jk = &(the_c64->TheDisplay->JoystickKeys[i]); NewJoyKeys[i] = *jk;
    if ((j = jk->up) >= 128) {j = 127;}
    j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;}
    ConfigWindow->WriteIconText(KJoyToIcon[i][0], b);
    if ((j = jk->down) >= 128) {j = 127;}
    j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;}
    ConfigWindow->WriteIconText(KJoyToIcon[i][1], b);
    if ((j = jk->left) >= 128) {j = 127;}
    j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;}
    ConfigWindow->WriteIconText(KJoyToIcon[i][2], b);
    if ((j = jk->right) >= 128) {j = 127;}
    j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;}
    ConfigWindow->WriteIconText(KJoyToIcon[i][3], b);
    if ((j = jk->fire) >= 128) {j = 127;}
    j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;}
    ConfigWindow->WriteIconText(KJoyToIcon[i][4], b);
  }
}


// Update SysConfig according to the values in the window
void WIMP::WindowToSysConf(void)
{
  int i, j, k;
  Joy_Keys *jk;

  // Read timings
  i = ConfigWindow->ReadIconNumber(Icon_Conf_PollAfter);
  j = ConfigWindow->ReadIconNumber(Icon_Conf_SpeedAfter);
  k = ConfigWindow->ReadIconNumber(Icon_Conf_SoundAfter);
  the_c64->WriteTimings(i,j,k);

  // Read joystick keys
  for (i=0; i<2; i++)
  {
    jk = &(the_c64->TheDisplay->JoystickKeys[i]); *jk = NewJoyKeys[i];
  }
}


// Low-level keyboard scan in SysConfig Window
void WIMP::PollSysConfWindow(void)
{
  Wimp_GetPointerInfo(Block);
  if (Block[MouseB_Window] == ConfigWindow->MyHandle())
  {
    int i, j;

    for (i=0; i<2; i++)
    {
      for (j=0; j<5; j++)
      {
        if (Block[MouseB_Icon] == KJoyToIcon[i][j])
        {
          int key;

          // Gain caret (to window, but none of its icons!)
          Wimp_GetCaretPosition(&LastCaret);
          Wimp_SetCaretPosition(ConfigWindow->MyHandle(),-1,0,0,-1,-1);
          if ((key = ScanKeys(IntKey_ScanFrom)) != 0xff)
          {
            char OneChar[2], *b;

            if (key == IntKey_Void) {key = 0xff;}
            switch (j)
            {
              case 0: NewJoyKeys[i].up = key; break;
              case 1: NewJoyKeys[i].down = key; break;
              case 2: NewJoyKeys[i].left = key; break;
              case 3: NewJoyKeys[i].right = key; break;
              case 4: NewJoyKeys[i].fire = key; break;
            }
            if (key >= 128) {key = 127;}
            key = IntKeyTable[key];
            if (key < 256) {OneChar[0]=key; OneChar[1]=0; b=OneChar;} else {b=(char*)key;}
            ConfigWindow->WriteIconText(KJoyToIcon[i][j], b);
          }
        }
      }
    }
  }
}


// Start a drag operation on the icon <number> in the window <host>
void WIMP::DragIconSprite(Window *host, unsigned int number)
{
  host->GetIconState(number,AuxBlock);
  if ((AuxBlock[IconB_Flags] & IFlg_Sprite) != 0)	// it needs to have a sprite, of course
  {
    char spritename[16] = "\0";

    if ((AuxBlock[IconB_Flags] & IFlg_Indir) == 0)	// not indirected
    {
      strncpy(spritename,((char*)AuxBlock+IconB_Data0),15);
    }
    else
    {
      if ((AuxBlock[IconB_Flags] & IFlg_Text) == 0)
      {
        strncpy(spritename,(char*)AuxBlock[IconB_Data0],15);
      }
      else	// this necessitates parsing the validation string
      {
        register char *b, *d, *s, c;

        s = spritename;
        if ((b = (char*)AuxBlock[IconB_Data1]) != NULL)	// pointer to val str
        {
          do
          {
            c = *b++;
            if ((c == 's') || (c == 'S'))	// sprite command
            {
              c = *b++; while ((c != ';') && (c >= 32)) {*s++ = c; c = *b++;}
              c = 0; *s++ = c;	// we can stop now
            }
            else if (c >= 32)	// some other command ==> skip to next.
            {
              c = *b++;
              while ((c != ';') && (c >= 32)) {if (c == '\\') {b++;} c = *b++;}
            }
          }
          while (c >= 32);
        }
      }
    }
    // we should now have the spritename
    if (spritename[0] != '\0')
    {
      LastDrag = host->MyHandle(); LastIcon = number;
      if (CMOS_DragType == 0)	// Drag outline
      {
        ROScreen *screen = the_c64->TheDisplay->screen;

        AuxBlock[DragB_Handle] = LastDrag; AuxBlock[DragB_Type] = 5;
        Wimp_GetPointerInfo(AuxBlock + DragB_IMinX);
        // Drag fixed sized box of this size:
        AuxBlock[DragB_IMinX] -= IconSpriteSize/2;
        AuxBlock[DragB_IMaxX] = AuxBlock[DragB_IMinX] + IconSpriteSize;
        AuxBlock[DragB_IMinY] -= IconSpriteSize/2;
        AuxBlock[DragB_IMaxY] = AuxBlock[DragB_IMinY] + IconSpriteSize;
        // Parent box is whole screen
        AuxBlock[DragB_BBMinX] = AuxBlock[DragB_BBMinY] = 0;
        AuxBlock[DragB_BBMaxX] = screen->resx; AuxBlock[DragB_BBMaxY] = screen->resy;
        Wimp_DragBox(AuxBlock);
      }
      else	// DragASprite
      {
        Wimp_GetPointerInfo(AuxBlock);
        AuxBlock[DASB_MinX] -= IconSpriteSize/2;
        AuxBlock[DASB_MaxX] = AuxBlock[DASB_MinX] + IconSpriteSize;
        AuxBlock[DASB_MinY] -= IconSpriteSize/2;
        AuxBlock[DASB_MaxY] = AuxBlock[DASB_MinY] + IconSpriteSize;
        DragASprite_Start(0xc5,1,spritename,AuxBlock,NULL);
      }
    }
  }
}


// Blk is a block as in MouseClick or PointerInfo
int WIMP::CalculateVolume(int *Blk)
{
  int orgx, vol;

  SoundWindow->getstate(AuxBlock);
  orgx = AuxBlock[WindowB_VMinX] - AuxBlock[WindowB_ScrollX];
  SoundWindow->GetIconState(Icon_Sound_Volume, AuxBlock);
  vol = (MaximumVolume*((Blk[MouseB_PosX] - orgx) - AuxBlock[IconB_MinX] - WellBorder)) / (AuxBlock[IconB_MaxX] - AuxBlock[IconB_MinX] - 2*WellBorder);
  if (vol < 0) {vol = 0;} if (vol > MaximumVolume) {vol = MaximumVolume;}
  return(vol);
}


// Check whether a filename contains a full pathname and reports an error if not.
int WIMP::CheckFilename(char *name)
{
  bool OK = false;
  register char *b, *d, c;

  b = d = name; c = *b++;
  while (c > 32)
  {
    // valid path must contain '$' or ':'
    if ((c == '$') || (c == ':')) {OK = true; d = b;}
    if (c == '.') {d = b;}
    c = *b++;
  }
  if ((b - d) == 1) {OK = false;}	// filename mustn't end with '$', ':' or '.'
  if (OK) {return(0);}

  WimpError.errnum = 0; sprintf(WimpError.errmess,"Bad filename %s",name);
  Wimp_ReportError(&WimpError,1,TASKNAME);
  return(-1);
}


void WIMP::SnapshotSaved(bool OK)
{
  if (OK)
  {
    int *b = (int*)SnapFile;

    if (b[MsgB_Sender] != 0)
    {
      b[MsgB_YourRef] = b[MsgB_MyRef]; b[MsgB_Action] = Message_DataLoad;
      Wimp_SendMessage(18,b,b[MsgB_Sender],b[6]);
    }
    else {Wimp_CreateMenu((int*)-1,0,0);}
    SaveType = 0;
  }
}


void WIMP::IssueSnapshotRequest(void)
{
  if (EmuPaused)
  {
    EmuPane->WriteIconTextU(Icon_Pane_Pause, PANE_TEXT_PAUSE);
    EmuPaused = false;
  }
  the_c64->RequestSnapshot();
}


// Sets the Emu pane's LEDs according to the floppy emulation state
void WIMP::SetLEDIcons(bool FloppyEmulation)
{
  int i, eor;

  if (FloppyEmulation) {eor = IFlg_Grey;} else {eor = 0;}
  for (i=1; i<4; i++)
  {
    EmuPane->SetIconState(LEDtoIcon[i],eor,IFlg_Grey);
  }
}



// Doesn't open window, just resizes...
void WIMP::SetEmuWindowSize(void)
{
  register int i;
  register C64Display *disp = the_c64->TheDisplay;

  i = (disp->screen->eigx < disp->screen->eigy) ? disp->screen->eigx : disp->screen->eigy;
  if (EmuZoom == 2) {i++;}
  EmuWindow->extent(0,-(DISPLAY_Y << i),(DISPLAY_X << i),0);
}



// switch between zoom 1 and zoom 2
void WIMP::ToggleEmuWindowSize(void)
{
  int x,y;

  // Icon in pane shows _alternative_ zoom mode
  if (EmuZoom == 1)
  {
    EmuZoom = 2;
    EmuPane->WriteIconText(Icon_Pane_Toggle,"1 x");
  }
  else
  {
    EmuZoom = 1;
    EmuPane->WriteIconText(Icon_Pane_Toggle,"2 x");
  }
  SetEmuWindowSize();
  EmuWindow->GetWorkArea(AuxBlock);
  x = AuxBlock[2] - AuxBlock[0]; y = AuxBlock[3] - AuxBlock[1];
  EmuWindow->getstate(AuxBlock);
  AuxBlock[WindowB_VMaxX] = AuxBlock[WindowB_VMinX] + x;
  AuxBlock[WindowB_VMinY] = AuxBlock[WindowB_VMaxY] - y;
  // Open emu window alone to get the dimensions set by the WIMP
  Wimp_OpenWindow(AuxBlock);
  // Then open with the pane at the correct position
  EmuWindow->getstate(AuxBlock); OpenEmuWindow(AuxBlock); UpdateEmuWindow();
}



int WIMP::ReadEmuWindowSize(void)
{
  return EmuZoom;
}



// Set a new drive path for drive DrNum. MsgBlock is the DataLoad MessageBlock.
void WIMP::NewDriveImage(int DrNum, int *MsgBlock, bool SetNow)
{
  int type, j = -1, map = -1;

  // determine currently selected type
  do
  {
    ++j; PrefsWindow->GetIconState(DriveToIcon[DrNum*IconsPerDrive + j], AuxBlock);
  }
  while ((j < 3) && ((AuxBlock[6] & IFlg_Slct) == 0));
  if (j >= 3) {j = 0;}
  // Check the type and set the drive type accordingly
  type = ReadCatalogueInfo(((char*)Block)+44,AuxBlock);
  // New path is directory but DRVTYPE != DIR ==> set DIR
  if ((type == 2) && (j != 0)) {map = 0;}
  // New path is not directory/image but DRVTYPE == DIR ==> remap to D64
  if (((type & 2) == 0) && (j == 0)) {map = 1;}
  // Filetype indicated D64 image?
  if (((type = AuxBlock[0]) & 0xfff00000) == 0xfff00000)	// typed file
  {
    type = (type >> 8) & 0xfff;
    // D64 image can also be used in DIR mode (-> D64ImageFS). Only remap from T64!
    if ((type == FileType_D64File) && (j == 2)) {map = 1;}
  }
  // Do we need to remap?
  if (map >= 0)
  {
    PrefsWindow->SetIconState(DriveToIcon[DrNum*IconsPerDrive+j],0,IFlg_Slct);
    PrefsWindow->SetIconState(DriveToIcon[DrNum*IconsPerDrive+map],IFlg_Slct,IFlg_Slct);
    j = map;
  }
  // j is the number of the emulation mode (DIR (0), D64 (1), T64 (2))
  PrefsWindow->WriteIconText(DriveToIcon[DrNum*IconsPerDrive+3],((char*)Block)+44);
  // Send acknowledge message
  Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck;
  Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]);

  // Set this drive path immediately?
  if (SetNow)
  {
    Prefs *prefs = new Prefs(ThePrefs);

    prefs->DriveType[DrNum] = j; strcpy(prefs->DrivePath[DrNum],((char*)Block)+44);
    the_c64->NewPrefs(prefs);
    ThePrefs = *prefs;
    delete prefs;
  }
}



void WIMP::SetSpeedLimiter(bool LimitSpeed)
{
  RO_Icon *roi;
  char *b, c;

  PrefsWindow->SetIconState(Icon_Prefs_LimSpeed,(LimitSpeed) ? IFlg_Slct : 0, IFlg_Slct);
  roi = EmuPane->GetIcon(Icon_Pane_Speed);
  if ((b = (char*)roi->dat.ind.val) != NULL)
  {
    do
    {
      c = *b++;
      if ((c == 'r') || (c == 'R'))	// boRder command?
      {
        if (LimitSpeed) {*b++ = '1';} else {*b++ = '2';}
        c = 0;	// stop now
      }
      else if (c >= 32)	// skip to next command
      {
        c = *b++;
        while ((c >= 32) && (c != ';')) {if (c == '\\') {b++;} c = *b++;}
      }
    }
    while (c >= 32);
    EmuPane->UpdateIcon(Icon_Pane_Speed);
  }
}





void WIMP::Poll(bool Paused)
{
  int event;
  bool LastPause;

  LastPause = EmuPaused; EmuPaused = Paused;

  // If emulator is paused disable null events
  if ((!EmuPaused) || (UseNULL > 0)) {Mask &= 0xfffffffe;} else {Mask |= 1;}

  do
  {
    // Loop until a null event is received, then continue the emulation.
    // (this looks best)
    do
    {
      event = Wimp_Poll(Mask,Block,NULL);
      switch (event)
      {
        case 1: Redraw(); break;
        case 2: OpenWindow(); break;
        case 3: CloseWindow(); break;
        case 6: MouseClick(); break;
        case 7: UserDrag(); break;
        case 8: if ((EmuPaused) && (Block[KeyPB_Window] == EmuWindow->MyHandle()))
                {
                  if (the_c64->TheDisplay->CheckForUnpause(!LastPause)) {EmuPaused = false;}
                }
                KeyPressed(); break;
        case 9: MenuSelection(); break;
        case 17:
        case 18: UserMessage(); break;
        case 19: UserMessageAck(); break;
        default: break;
      }
      // This is important
      if ((!EmuPaused) || (UseNULL > 0)) {Mask &= 0xfffffffe;} else {Mask |= 1;}
      LastPause = EmuPaused;
    }
    while (event != 0);
    if (UseNULL > 0) {PollSysConfWindow();}
    // This should probably become a new member-function one day...
    if (DragType == DRAG_VolumeWell)
    {
      Wimp_GetPointerInfo(Block);
      if (Block[MouseB_Icon] == Icon_Sound_Volume)	// should always be the case (BBox)!
      {
        the_c64->HostVolume = CalculateVolume(Block);
        SoundWindow->ForceIconRedraw(Icon_Sound_Volume);
      }
    }
  }
  while (EmuPaused);
}


void WIMP::Redraw(void)
{
  if (Block[RedrawB_Handle] == EmuWindow->MyHandle()) // emulator main window
  {
    C64Display *disp = the_c64->TheDisplay;

    EmuWindow->redraw(Block,disp->BitmapBase(),disp);
  }
  else if (Block[RedrawB_Handle] == SoundWindow->MyHandle())	// sound window
  {
    int more;
    int minx, miny, maxx, maxy, thresh;

    more = Wimp_RedrawWindow(Block);
    // compute screen coordinates of work (0,0)
    minx = Block[RedrawB_VMinX] - Block[RedrawB_ScrollX];
    maxy = Block[RedrawB_VMaxY] - Block[RedrawB_ScrollY];
    // get volume well bounding box
    SoundWindow->GetIconState(Icon_Sound_Volume, AuxBlock);
    maxx = minx + AuxBlock[IconB_MaxX] - WellBorder; minx += AuxBlock[IconB_MinX] + WellBorder;
    miny = maxy + AuxBlock[IconB_MinY] + WellBorder; maxy += AuxBlock[IconB_MaxY] - WellBorder;
    thresh = minx + (the_c64->HostVolume * (maxx - minx))/ MaximumVolume;
    while (more != 0)
    {
      // clip
      if ((minx <= Block[RedrawB_CMaxX]) && (maxx >= Block[RedrawB_CMinX]) &&
          (miny <= Block[RedrawB_CMaxY]) && (maxy >= Block[RedrawB_CMinY]))
      {
        if (Block[RedrawB_CMinX] < thresh)
        {
          ColourTrans_SetGCOL(0x00ff0000,0,0);	// green
          OS_Plot(0x04,minx,miny); OS_Plot(0x65,thresh,maxy);
        }
        if (Block[RedrawB_CMaxX] > thresh)
        {
          ColourTrans_SetGCOL(0xffffff00,0,0);	// white
          OS_Plot(0x04,thresh,miny); OS_Plot(0x65,maxx,maxy);
        }
      }
      more = Wimp_GetRectangle(Block);
    }
  }
  else // Dummy redraw loop
  {
    int more;

    more = Wimp_RedrawWindow(Block);
    while (more != 0)
    {
      more = Wimp_GetRectangle(Block);
    }
  }
}


void WIMP::OpenWindow(void)
{
  if (Block[WindowB_Handle] == EmuWindow->MyHandle()) {OpenEmuWindow(Block);}
  else if (Block[WindowB_Handle] != EmuPane->MyHandle())
  {
    Wimp_OpenWindow(Block);
  }
}


void WIMP::CloseWindow(void)
{
  if (Block[WindowB_Handle] == EmuWindow->MyHandle()) {CloseEmuWindow();}
  else if (Block[WindowB_Handle] != EmuPane->MyHandle())
  {
    if (Block[WindowB_Handle] == ConfigWindow->MyHandle()) {UseNULL--;}
    Wimp_CloseWindow(Block);
  }
}


void WIMP::MouseClick(void)
{
  if ((Block[MouseB_Window] == -2) && (Block[MouseB_Icon] == IBicon->IHandle))	// Icon Bar icon
  {
    if (Block[MouseB_Buttons] == 2)	// Menu
    {
      Wimp_CreateMenu((int*)&MenuIconBar,Block[MouseB_PosX]-MenuIconBar.head.width/2,96+Menu_Height*Menu_IBar_Items);
      LastMenu = Menu_IBar;
    }
    else	// Some other click
    {
      if (!EmuWindow->OpenStatus(Block)) {Block[WindowB_Stackpos] = -1; OpenEmuWindow(Block);}
    }
  }
  else if (Block[MouseB_Window] == EmuWindow->MyHandle())	// Emulator window
  {
    if (Block[MouseB_Buttons] >= 256)	// click
    {
      Wimp_GetCaretPosition(&LastCaret);
      Wimp_SetCaretPosition(Block[MouseB_Window],-1,-100,100,-1,-1);	// gain input focus
    }
    if (Block[MouseB_Buttons] == 2)	// menu
    {
      Wimp_CreateMenu((int*)&MenuEmuWindow,Block[MouseB_PosX]-MenuEmuWindow.head.width/2,Block[MouseB_PosY]);
      LastMenu = Menu_Emulator;
    }
  }
  else if (Block[MouseB_Window] == EmuPane->MyHandle())	// Emulator pane
  {
    if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4))
    {
      switch (Block[MouseB_Icon])
      {
        case Icon_Pane_Pause:	// Pause icon
          if (EmuPaused)
          {
            EmuPane->WriteIconText(Icon_Pane_Pause,PANE_TEXT_PAUSE);
            the_c64->Resume(); EmuPaused = false;
          }
          else
          {
            EmuPane->WriteIconText(Icon_Pane_Pause,PANE_TEXT_RESUME);
            the_c64->Pause(); EmuPaused = true;
            // Update the window now!
            UpdateEmuWindow();
          }
          break;
        case Icon_Pane_Reset: the_c64->Reset(); break;
        case Icon_Pane_Toggle: ToggleEmuWindowSize(); break;
        case Icon_Pane_Speed:
          ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; SetSpeedLimiter(ThePrefs.LimitSpeed);
          break;
        default: break;
      }
    }
  }
  else if (Block[MouseB_Window] == PrefsWindow->MyHandle())		// Prefs window
  {
    if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4))	// normal click
    {
      register int i;

      switch(Block[MouseB_Icon])
      {
        case Icon_Prefs_SkipFLeft:
             i = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText);
             if (i > 0)
             {
               PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,--i);
               ThePrefs.SkipFrames = i; // instant update
             }
             break;
        case Icon_Prefs_SkipFRight:
             i = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText);
             PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,++i);
             ThePrefs.SkipFrames = i;	// instant update
             break;
        case Icon_Prefs_Cancel: ThePrefsToWindow(); break;	// restore current settings
        case Icon_Prefs_OK: WindowToThePrefs();
             if (Block[MouseB_Buttons] == 4) {PrefsWindow->close();}
             break;		// use new prefs
        case Icon_Prefs_Save:
             WindowToThePrefs();
             if (CheckFilename(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)) == 0)
             {
               ThePrefs.Save(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath));
             }
             break;
        default: break;
      }
    }
    else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64))
    // drag (only the sprite)
    {
      if (Block[MouseB_Icon] == Icon_Prefs_PrefSprite)
      {
        DragIconSprite(PrefsWindow, Icon_Prefs_PrefSprite);
        DragType = DRAG_PrefsSprite;
      }
    }
  }
  else if (Block[MouseB_Window] == ConfigWindow->MyHandle())	// sys config window
  {
    if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4))
    {
      if (Block[MouseB_Icon] == Icon_Conf_OK)
      {
        WindowToSysConf(); UseNULL--;
        if (Block[MouseB_Buttons] == 4) {ConfigWindow->close();}
      }
      else if (Block[MouseB_Icon] == Icon_Conf_Save)
      {
        if (CheckFilename(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)) == 0)
        {
          WindowToSysConf(); UseNULL--; ConfigWindow->close();
          the_c64->SaveSystemConfig(ConfigWindow->ReadIconText(Icon_Conf_ConfPath));
        }
      }
    }
    else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64))
    {
      if (Block[MouseB_Icon] == Icon_Conf_ConfSprite)
      {
        DragIconSprite(ConfigWindow, Icon_Conf_ConfSprite);
        DragType = DRAG_ConfSprite;
      }
    }
  }
  else if (Block[MouseB_Window] == SoundWindow->MyHandle())		// sound window
  {
    if (Block[MouseB_Icon] == Icon_Sound_Volume)
    {
      if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4))		// click
      {
        the_c64->HostVolume = CalculateVolume(Block); Sound_Volume(the_c64->HostVolume);
        SoundWindow->ForceIconRedraw(Icon_Sound_Volume);
      }
      else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64))	// drag
      {
        int orgx, orgy;

        SoundWindow->getstate(AuxBlock);
        orgx = AuxBlock[WindowB_VMinX] - AuxBlock[WindowB_ScrollX];
        orgy = AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_ScrollY];
        SoundWindow->GetIconState(Icon_Sound_Volume, &AuxBlock[DragB_BBMinX - IconB_MinX]);
        AuxBlock[DragB_BBMinX] += orgx; AuxBlock[DragB_BBMinY] += orgy;
        AuxBlock[DragB_BBMaxX] += orgx; AuxBlock[DragB_BBMaxY] += orgy;
        AuxBlock[DragB_Type] = 7;
        Wimp_DragBox(AuxBlock);
        DragType = DRAG_VolumeWell; UseNULL++;
      }
    }
  }
  else if (Block[MouseB_Window] == SaveBox->MyHandle())
  {
    if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4))
    {
      if (Block[MouseB_Icon] == Icon_Save_OK)
      {
        if (CheckFilename(SaveBox->ReadIconText(Icon_Save_Path)) == 0)
        {
          if (SaveType == SAVE_RAM)
          {
            strcpy(RAMFile+44,SaveBox->ReadIconText(Icon_Save_Path));
            the_c64->SaveRAM(RAMFile+44);
            Wimp_CreateMenu((int*)-1,0,0);
            SaveType = 0;
          }
          else if (SaveType == SAVE_Snapshot)
          {
            *(((int*)SnapFile) + MsgB_Sender) = 0;
            strcpy(SnapFile+44,SaveBox->ReadIconText(Icon_Save_Path));
            IssueSnapshotRequest();
          }
        }
      }
    }
    else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64))
    {
      if (Block[MouseB_Icon] == Icon_Save_Sprite)
      {
        DragIconSprite(SaveBox, Icon_Save_Sprite);
        DragType = DRAG_SaveSprite;
      }
    }
  }
}


// A drag operation has terminated
void WIMP::UserDrag(void)
{
  char *b = NULL;
  int filetype, size;

  if ((CMOS_DragType == 0) || (DragType == DRAG_VolumeWell))
  {
    Wimp_DragBox(NULL);
  }
  else
  {
    DragASprite_Stop();
  }

  if (DragType == DRAG_VolumeWell)
  {
    UseNULL--; DragType = 0; Sound_Volume(the_c64->HostVolume);	// just set the new volume.
  }

  // Drag of the path sprite
  if (DragType == DRAG_PrefsSprite)
  {
    b = PrefsWindow->ReadIconText(Icon_Prefs_PrefPath); filetype = FileType_Text;
    size = EstimatedPrefsSize;	// can't say how large it's gonna be
  }
  else if (DragType == DRAG_ConfSprite)
  {
    b = ConfigWindow->ReadIconText(Icon_Conf_ConfPath); filetype = FileType_Text;
    size = EstimatedConfSize;
  }
  else if (DragType == DRAG_SaveSprite)
  {
    b = SaveBox->ReadIconText(Icon_Save_Path); filetype = FileType_Data;
    if (SaveType == SAVE_RAM) {size = EstimatedRAMSize;}
    else if (SaveType == SAVE_Snapshot) {size = EstimatedSnapSize;}
    else {size = 0;}
  }

  // now b should point to the path and filetype should contain the type
  if (b != NULL)
  {
    Wimp_GetPointerInfo(Block);
    // Not on background and not on my own icon bar icon
    if ((Block[MouseB_Window] != -1) && ((Block[MouseB_Window] != -2) || (Block[MouseB_Icon] != IBicon->IHandle)))
    {
      int handle = Block[MouseB_Window];

      // None of my windows
      if ((handle != EmuWindow->MyHandle()) && (handle != EmuPane->MyHandle()) &&
          (handle != PrefsWindow->MyHandle()) && (handle != ConfigWindow->MyHandle()) &&
          (handle != InfoWindow->MyHandle()) && (handle != SaveBox->MyHandle()))
      {
        char *d, c;

        d = b; c = *b++;
        // get pointer to leafname in d
        while (c > 32) {if ((c == '.') || (c == ':')) {d = b;} c = *b++;}
        // Build message block
        Block[5] = Block[MouseB_Window]; Block[6] = Block[MouseB_Icon];
        Block[7] = Block[MouseB_PosX]; Block[8] = Block[MouseB_PosY];
        Block[9] = size; Block[10] = filetype;
        Block[MsgB_YourRef] = 0; Block[MsgB_Action] = Message_DataSave;
        b = ((char*)Block) + 44; c = *d++;
        while (c > 32) {*b++ = c; c = *d++;}
        *b++ = 0; Block[MsgB_Size] = (((int)(b - (char*)Block)) + 3) & 0xfffffffc;
        Wimp_SendMessage(18,Block,Block[5],Block[6]);
      }
    }
  }
  else {DragType = 0;}
  LastDrag = 0;
}


void WIMP::KeyPressed(void)
{
  register int key = Block[KeyPB_Key];

  if (Block[KeyPB_Window] == EmuWindow->MyHandle())
  {
    if ((key >= 0x180) && (key <= 0x1fd)) // special keys (incl. FKeys)
    {
      key -= 0x180;
      if ((((key & 0x4f) >= 0x05) && ((key & 0x4f) <= 0x09)) ||	// F5 -F9 [shift|ctrl]
          (((key & 0x4f) >= 0x4a) && ((key & 0x4f) <= 0x4c)))	// F10-F12 [shift|ctrl]
      {
        if ((key != 0x06) && (key != 0x07) && (key != 0x08))
        {
          Wimp_ProcessKey(Block[KeyPB_Key]);
        }
      }
    }
    return;
  }
  else if (Block[KeyPB_Window] == PrefsWindow->MyHandle())
  {
    if (key == Key_Return)
    {
      WindowToThePrefs();
      if (Block[KeyPB_Icon] == Icon_Prefs_PrefPath)	// return pressed in path string icon?
      {
        if (CheckFilename(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)) == 0)
        {
          ThePrefs.Save(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath));
        }
      }
      return;
    }
  }
  else if (Block[KeyPB_Window] == ConfigWindow->MyHandle())
  {
    if ((key == Key_Return) && (Block[KeyPB_Icon] != -1))
    {
      WindowToSysConf(); UseNULL--; ConfigWindow->close();
      if (Block[KeyPB_Icon] == Icon_Conf_ConfPath)	// return pressed in path string icon?
      {
        if (CheckFilename(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)) == 0)
        {
          the_c64->SaveSystemConfig(ConfigWindow->ReadIconText(Icon_Conf_ConfPath));
        }
      }
      return;
    }
  }
  else if (Block[KeyPB_Window] == SaveBox->MyHandle())
  {
    if (key == Key_Return)
    {
      if (Block[KeyPB_Icon] == Icon_Save_Path)
      {
        if (CheckFilename(SaveBox->ReadIconText(Icon_Save_Path)) == 0)
        {
          if (SaveType == SAVE_RAM)
          {
            strcpy(RAMFile+44,SaveBox->ReadIconText(Icon_Save_Path));
            the_c64->SaveRAM(RAMFile+44);
            Wimp_CreateMenu((int*)-1,0,0);
            SaveType = 0;
          }
          else if (SaveType == SAVE_Snapshot)
          {
            *(((int*)SnapFile) + MsgB_Sender) = 0;
            strcpy(SnapFile+44,SaveBox->ReadIconText(Icon_Save_Path));
            IssueSnapshotRequest();
          }
        }
      }
      return;
    }
  }
  Wimp_ProcessKey(Block[KeyPB_Key]);
}


void WIMP::MenuSelection(void)
{
  int Buttons;

  Wimp_GetPointerInfo(AuxBlock); Buttons = AuxBlock[MouseB_Buttons];

  switch (LastMenu)
  {
    case Menu_IBar:
        if (Block[0] == Menu_IBar_Quit) {EmuPaused = false; the_c64->Quit();}
        else if (Block[0] == Menu_IBar_Prefs)
        {
          // Is it already open? Then don't do anything
          if (!PrefsWindow->OpenStatus())
          {
            int y;

            // Open Prefs window with top left corner in top left corner of screen
            PrefsWindow->getstate(AuxBlock);
            y = the_c64->TheDisplay->screen->resy;
            AuxBlock[WindowB_VMaxX] -= AuxBlock[WindowB_VMinX]; AuxBlock[WindowB_VMinX] = 0;
            AuxBlock[WindowB_VMinY] = y - (AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY]);
            AuxBlock[WindowB_VMaxY] = y;
            // Open Prefs window on top
            AuxBlock[WindowB_Stackpos] = -1;
            PrefsWindow->open(AuxBlock);
          }
          else
          {
            PrefsWindow->getstate(AuxBlock);
            AuxBlock[WindowB_Stackpos] = -1; PrefsWindow->open(AuxBlock);
          }
        }
        else if (Block[0] == Menu_IBar_Config)
        {
          if (!ConfigWindow->OpenStatus())
          {
            int x, y;

            // Update window contents
            SysConfToWindow();
            // Open config window in top right corner of screen
            ConfigWindow->getstate(AuxBlock);
            x = the_c64->TheDisplay->screen->resx; y = the_c64->TheDisplay->screen->resy;
            AuxBlock[WindowB_VMinX] = x - (AuxBlock[WindowB_VMaxX] - AuxBlock[WindowB_VMinX]);
            AuxBlock[WindowB_VMaxX] = x;
            AuxBlock[WindowB_VMinY] = y - (AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY]);
            AuxBlock[WindowB_VMaxY] = y;
            AuxBlock[WindowB_Stackpos] = -1;
            ConfigWindow->open(AuxBlock);
            // We need NULL-events for the low-level keyboard scan.
            UseNULL++;
          }
          else
          {
            ConfigWindow->getstate(AuxBlock);
            AuxBlock[WindowB_Stackpos] = -1; ConfigWindow->open(AuxBlock);
          }
        }
        if (Buttons == 1) {Wimp_CreateMenu((int*)&MenuIconBar,0,0);}
        break;
    case Menu_Emulator:
        if (Buttons == 1) {Wimp_CreateMenu((int*)&MenuEmuWindow,0,0);}
        break;
    default: break;
  }
}


// Handle regular messages
void WIMP::UserMessage(void)
{
  C64Display *disp = the_c64->TheDisplay;
  int i;

  switch (Block[MsgB_Action]) // Message Action
  {
    case Message_Quit:
    	 EmuPaused = false; the_c64->Quit(); break;
    case Message_ModeChange:
    	 disp->ModeChange(); the_c64->TheVIC->ReInitColors(); SetEmuWindowSize();
    	 // The window could have changed position ==> reposition pane as well!
    	 // we have to force the window to the screen manually
    	 EmuWindow->getstate(AuxBlock);
    	 if ((AuxBlock[WindowB_WFlags] & (1<<16)) != 0)	// is it open anyway?
    	 {
    	   i = AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY];
    	   if (AuxBlock[WindowB_VMaxY] > disp->screen->resy - TitleBarHeight)
    	   {
    	     AuxBlock[WindowB_VMaxY] = disp->screen->resy - TitleBarHeight;
      	     if ((AuxBlock[WindowB_VMinY] = AuxBlock[WindowB_VMaxY] - i) < TitleBarHeight)
    	     {
    	       AuxBlock[WindowB_VMinY] = TitleBarHeight;
    	     }
    	   }
    	   i = AuxBlock[WindowB_VMaxX] - AuxBlock[WindowB_VMinX];
    	   if (AuxBlock[WindowB_VMaxX] > disp->screen->resx - TitleBarHeight)
    	   {
    	     AuxBlock[WindowB_VMaxX] = disp->screen->resx - TitleBarHeight;
    	     if ((AuxBlock[WindowB_VMinX] = AuxBlock[WindowB_VMaxX] - i) < 0)
    	     {
    	       AuxBlock[WindowB_VMinX] = 0;
    	     }
    	   }
    	   // Don't you just love it -- you can't open the window directly, you need
    	   // a delay... like for instance by sending yourself an OpenWindow message...
    	   Wimp_SendMessage(2,AuxBlock,TaskHandle,0);
    	 }
    	 break;
    case Message_PaletteChange:
         // Updating EmuWindow is pointless since the bitmap still contains data for another mode
         disp->ModeChange(); the_c64->TheVIC->ReInitColors();
         break;
    case Message_DataSave:
         i = -1;	// indicator whether file is accepted
         if ((Block[5] == EmuWindow->MyHandle()) && (Block[10] == FileType_C64File)) {i=0;}
         else if ((Block[5] == EmuWindow->MyHandle()) && (Block[10] == FileType_Data)) {i=0;}
         else if ((Block[5] == PrefsWindow->MyHandle()) && (Block[10] == FileType_Text)) {i=0;}
         else if ((Block[5] == ConfigWindow->MyHandle()) && (Block[10] == FileType_Text)) {i=0;}
         if (i >= 0)
         {
           Block[9] = -1;	// file unsafe
           strcpy(((char*)Block)+44,WIMP_SCRAP_FILE);
           Block[MsgB_Size] = (48 + strlen(WIMP_SCRAP_FILE)) & 0xfffffffc;
           Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataSaveAck;
           Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]);
           UseScrap = true;
         }
         break;
    case Message_DataLoad:
         if (Block[5] == EmuWindow->MyHandle())	// Emulator window: load file?
         {
           if (Block[10] == FileType_C64File)	// Load only files with type &64 this way
           {
             FILE *fp;

             if ((fp = fopen(((char*)Block)+44,"rb")) != NULL)
             {
               uint8 lo, hi, *mem = the_c64->RAM;
               int length;

               lo = fgetc(fp); hi = fgetc(fp); length = lo + (hi<<8);
               length += fread(mem+length,1,0x10000-length,fp);
               // length is now end-address
               fclose(fp);
               mem[0xc3] = lo; mem[0xc4] = hi; // Load-address
               lo = length & 0xff; hi = (length >> 8) & 0xff;
               mem[0xae] = mem[0x2d] = mem[0x2f] = mem[0x31] = mem[0x33] = lo;
               mem[0xaf] = mem[0x2e] = mem[0x30] = mem[0x32] = mem[0x34] = hi;
               Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck;
               Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]);
             }
           }
           else if (Block[10] == FileType_Data)
           {
             if (the_c64->LoadSnapshot(((char*)Block)+44))
             {
               Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck;
               Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]);
             }
           }
         }
         else if (Block[5] == PrefsWindow->MyHandle())	// Prefs window?
         {
           if (Block[10] == FileType_Text)	// load a prefs file?
           {
             Prefs *prefs = new Prefs(ThePrefs);

             prefs->Load(((char*)Block)+44);
             the_c64->NewPrefs(prefs);
             ThePrefs = *prefs;
             delete prefs;
             PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,((char*)Block)+44);
             ThePrefsToWindow();
             Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck;
             Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]);
           }
           else		// interpret as drive path (if dragged on one of the drive path icons)
           {
             switch (Block[6])
             {
               case Icon_Prefs_Dr8Path:  i = 0; break;
               case Icon_Prefs_Dr9Path:  i = 1; break;
               case Icon_Prefs_Dr10Path: i = 2; break;
               case Icon_Prefs_Dr11Path: i = 3; break;
               default: -1; break;
             }
             if (i >= 0) {NewDriveImage(i,Block,false);}
           }
         }
         else if (Block[5] == ConfigWindow->MyHandle())	// load sys config file
         {
           if (Block[10] == FileType_Text)
           {
             the_c64->LoadSystemConfig(((char*)Block)+44);
             SysConfToWindow();
             ConfigWindow->WriteIconText(Icon_Conf_ConfPath,((char*)Block)+44);
             Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck;
             Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]);
           }
         }
         else if (Block[5] == EmuPane->MyHandle())	// emulator pane
         {
           switch (Block[6])
           {
             case Icon_Pane_Drive0:
             case Icon_Pane_LED0: i = 0; break;
             case Icon_Pane_Drive1:
             case Icon_Pane_LED1: i = 1; break;
             case Icon_Pane_Drive2:
             case Icon_Pane_LED2: i = 2; break;
             case Icon_Pane_Drive3:
             case Icon_Pane_LED3: i = 3; break;
             default: i = -1; break;
           }
           if (i >= 0) {NewDriveImage(i,Block,true);}
         }
         // Clean up if necessary
         if (UseScrap) {DeleteFile(WIMP_SCRAP_FILE); UseScrap = false;}
         break;
    case Message_DataSaveAck:
         if (DragType == DRAG_PrefsSprite)
         {
           WindowToThePrefs();	// read window entries
           ThePrefs.Save(((char*)Block)+44);
           if (Block[9] != -1)	// we're talking to the filer ==> set new pathname
           {
             PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,((char*)Block)+44);
           }
           Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad;
           Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]);
         }
         else if (DragType == DRAG_ConfSprite)
         {
           WindowToSysConf();	// read window entries
           the_c64->SaveSystemConfig(((char*)Block)+44);
           if (Block[9] != -1)
           {
             ConfigWindow->WriteIconText(Icon_Conf_ConfPath,((char*)Block)+44);
           }
           Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad;
           Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]);
         }
         else if (DragType == DRAG_SaveSprite)
         {
           if (SaveType == SAVE_RAM)
           {
             memcpy(RAMFile,(char*)Block,256); the_c64->SaveRAM(RAMFile+44);
             Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad;
             Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]);
           }
           else if (SaveType == SAVE_Snapshot)
           {
             memcpy(SnapFile,(char*)Block,256);
             IssueSnapshotRequest();
           }
         }
         break;
    case Message_DataLoadAck:
         if (DragType == DRAG_ConfSprite)
         {
           UseNULL--; ConfigWindow->close();
         }
         if (DragType == DRAG_SaveSprite)
         {
           Wimp_CreateMenu((int*)-1,0,0);
         }
         DragType = SaveType = 0; break;
    case Message_MenuWarning:
         if (LastMenu == Menu_Emulator)
         {
           if (Block[8] == Menu_EWind_SaveRAM)
           {
             SaveType = SAVE_RAM; SaveBox->WriteIconText(Icon_Save_Path,RAMFile+44);
           }
           else if (Block[8] == Menu_EWind_Snapshot)
           {
             SaveType = SAVE_Snapshot; SaveBox->WriteIconText(Icon_Save_Path,SnapFile+44);
           }
           else {SaveType = 0;}
           Wimp_CreateSubMenu((int*)Block[5],Block[6],Block[7]);
         }
         break;
    default: break;
  }
}


// If a recorded message was not answered, i.e. something went wrong.
void WIMP::UserMessageAck(void)
{
  switch(Block[MsgB_Action])
  {
    case Message_DataSave:
         sprintf(WimpError.errmess,"Can't save data."); break;
    case Message_DataLoad:
         sprintf(WimpError.errmess,"Receiver couldn't load data."); break;
    default:
         sprintf(WimpError.errmess,"Some error occurred..."); break;
  }
  WimpError.errnum = 0; Wimp_ReportError(&WimpError,1,TASKNAME);
}
