/*
editor.c
--------

Synth instrument editor
13/6/22
*/

#include "synth.h"
#include "wimp.h"
#include "lib.h"
#include "main.h"
#include "common.h"

void display_user(void); // display.c
void slider_change(int n, int slider, int window, int icon); // display.c

extern menu_t editor_menu;
void key_press(int *blk);


/*
 * update_syn_vars
 * ---------------
 */
void update_syn_vars(int *blk)
{
  ro.user = (ins_t *)blk[0];
  ro.cur_ins = blk[1];
  ro.harm = (harm_t *)blk[2];
  ro.num_wavs = blk[3];
  ro.num_patches = blk[4];
  ro.patch = blk[5]; // bits: 23-16 Prg, 15-8 Lo, 7-0 Hi
  ro.bank_name = (char *)blk[6];
  ro.soundset_name = (char *)blk[7];
  ro.soundset_date = *(unsigned int *)(blk[7] + NAME_LEN); // date follows name
  ro.inst_num = blk[8];
  ro.num_instruments = blk[9];
}


/*
 * synth_command
 * -------------
 * Sends an edit command to the synth and updates our picture of the synth data
 * structures.
 * flags in regs.r[3] are used for the following reason codes:
 *  SYN_SAVE_SRC:
 *   bit 0: save as C source             (reason + 0x100)
 *   bit 1: save as text patch list      (reason + 0x200)
 *   bit 2: save as CSV bank map         (reason + 0x400)
 *   bit 3: save as text instrument list (reason + 0x800)
 *   If all 4 are set, 4 files are saved.
 *  SYN_SAVE_INSTR:
 *   0: create new instrument            (reason + 0x000)
 *   1: create additional reference      (reason + 0x100)
 *   2: move current ref to new ref      (reason + 0x200)
 */
_kernel_oserror *synth_command(int reason, int data)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;
  int blk[12];

  regs.r[0] = reason & 0xff;
  regs.r[1] = (int)blk;
  regs.r[2] = data;
  regs.r[3] = (reason >> 8) & 0xff; // flags
  err = _kernel_swi(MIDISynth_Edit, &regs, &regs);

  // first, deal with INVALID_INSTRUMENT_REF error, mark patch location as invalid
  ro.patch_valid = TRUE;
  if(err)
    if(err->errnum == INVALID_INSTRUMENT_REF)
    {
      ro.patch_valid = FALSE;
      err = NULL; // return no error as we've dealt with it here
    }

  if(!err)
    update_syn_vars(blk);
  else if(err->errnum != INSTRUMENT_DELETED) // INSTRUMENT_DELETED is not really an error, the spare slot will eventually be used.
    report_error(err->errmess, 1);

  return err;
}


/*
 * load_instrument (by patch)
 * ---------------
 * loads the syn.user instrument from a preset instrument.
 * num = instrument patch number
 */
static _kernel_oserror *load_instrument(int num)
{
  _kernel_oserror *err = NULL;

  LIMIT(0, num, ro.num_patches - 1);

  if((err = synth_command(SYN_LOAD_USER, num)) == NULL) // load user from store
    display_user();

  return err;
}


/*
 * load_instr_by_number
 * --------------------
 * loads the syn.user instrument from a preset instrument.
 * num = instrument number
 */
static _kernel_oserror *load_instr_by_number(int num)
{
  _kernel_oserror *err = NULL;

  LIMIT(0, num, ro.num_instruments - 1);

  if((err = synth_command(SYN_LOAD_INSTR, num)) == NULL) // load user from store
    display_user();

  return err;
}


/*
 * open_editor
 * -----------
 */
void open_editor(void)
{
  _kernel_swi_regs regs;
  int blk[10];

  blk[0] = ro.handle[WIN_EDT];
  regs.r[1] = (int) blk;
  _kernel_swi(Wimp_GetWindowState, &regs, &regs);
  _kernel_swi(Wimp_OpenWindow, &regs, &regs);
  _kernel_swi(Wimp_GetWindowState, &regs, &regs);
  blk[0] = ro.cur_pane;
  blk[7] = -1; // bring to front
  _kernel_swi(Wimp_OpenWindow, &regs, &regs);
  blk[0] = ro.handle[WIN_EDT];
  blk[7] = ro.cur_pane;
  _kernel_swi(Wimp_OpenWindow, &regs, &regs);
}


/*
 * close_editor
 * ------------
 */
void close_editor(void)
{
  _kernel_swi_regs regs;
  int blk[10];
  regs.r[1] = (int)blk;

  if (ro.cur_pane != -1)
  {
    blk[0] = ro.cur_pane;
    _kernel_swi(Wimp_CloseWindow, &regs, &regs);
  }

  blk[0] = ro.handle[WIN_EDT];
  _kernel_swi(Wimp_CloseWindow, &regs, &regs);
}


/*
 * set_editor_pane
 * ---------------
 */
void set_editor_pane(int icon)
{
  int pane;

  switch(icon)
  {
    case ICON_ED_WAVE1:   pane = ro.handle[WIN_ED_WAVE1];   break;
    case ICON_ED_WAVE2:   pane = ro.handle[WIN_ED_WAVE2];   break;
    case ICON_ED_FILTER:  pane = ro.handle[WIN_ED_FILTER];  break;
    case ICON_ED_NOISE:   pane = ro.handle[WIN_ED_NOISE];   break;
    case ICON_ED_GENERAL: pane = ro.handle[WIN_ED_GENERAL]; break;
   default: return;
  }

  if (ro.cur_pane == pane)
    return;

  _kernel_swi_regs regs;
  int blk[10];
  regs.r[1] = (int)blk;

  if (ro.cur_pane != -1)
  {
    blk[0] = ro.cur_pane;
    _kernel_swi(Wimp_CloseWindow, &regs, &regs);

    ro.cur_pane = pane;
    open_editor();
  }

  ro.cur_pane = pane;
}


/*
 * kit_check
 * ---------
 * If the currently selected instrument for editing is a drum kit,
 * all editing buttons in the editor window are greyed out,
 * otherwise they are enabled.
 */
void kit_check(void)
{
  int state = (ro.patch & 0xff) == PERCUSSION_BANK; // true if current instrument is a drum kit (not editable)
  int win = ro.handle[WIN_EDT];
  icon_disabled_change(state, win, ICON_ED_RELOAD);
  icon_disabled_change(state, win, ICON_ED_CLEAR);
  icon_disabled_change(state, win, ICON_ED_SAVE);
  icon_disabled_change(state, win, ICON_ED_SAVE_NEW);
  icon_disabled_change(state, win, ICON_ED_INST_NAME);
  editor_menu.item[0].icon_flags = (editor_menu.item[0].icon_flags & ~IC_SHADED) | (state << 22);
}


/*
 * update_slider_param
 * -------------------
 * This is used for a slider drag or click.
 * Displays the new slider position, and the associated scaled numeric value.
 * Returns the associated scaled parameter.
 */
static int update_slider_param(int *blk, int slider, int window, int icon)
{
  int n = slider_get_posn(blk, slider);
  slider_display_value(n, slider, window, icon);
  n = trb_to_param(slider, n);
  icon_text_change(itoa(n), ro.handle[window], icon + 1);

  return n;
}


/*
 * env_slider_control
 * ------------------
 * Handles slider controls for an envelope.
 * Returns True if dealt with here, else False.
 */
static int env_slider_control(int *blk, int win, int icon, env_t *env)
{
  switch(icon)
  {
    case ICON_ENV_DY_SLDR: env->delay         = update_slider_param(blk, SLDR_ENV_DY, win, icon); break; // envelope delay
    case ICON_ENV_AR_SLDR: env->attack_step   = update_slider_param(blk, SLDR_ENV_AR, win, icon); break; // envelope attack step
    case ICON_ENV_AT_SLDR: env->attack_target = update_slider_param(blk, SLDR_ENV_AT, win, icon); break; // envelope attack target
    case ICON_ENV_AH_SLDR: env->hold          = update_slider_param(blk, SLDR_ENV_AH, win, icon); break; // envelope attack hold
    case ICON_ENV_DR_SLDR: env->decay_step    = update_slider_param(blk, SLDR_ENV_DR, win, icon); break; // envelope decay rate
    case ICON_ENV_DT_SLDR: env->decay_target  = update_slider_param(blk, SLDR_ENV_DT, win, icon); break; // envelope decay target
    case ICON_ENV_SR_SLDR: env->sustain_step  = update_slider_param(blk, SLDR_ENV_SR, win, icon); break; // envelope sustain rate
    case ICON_ENV_RR_SLDR: env->release_step  = update_slider_param(blk, SLDR_ENV_RR, win, icon); break; // envelope release rate

    default: return FALSE;
  }
  return TRUE;
}

/*
 * edt_slider_control
 * ------------------
 * blk = block from Wimp_GetPointerInfo
 * Returns True if dealt with here, else False
 */
int edt_slider_control(int *blk)
{
  ins_t *i = ro.user;
  int icon = ro.slider.icon;

  if(ro.slider.window == ro.handle[WIN_ED_WAVE1]) // editor wave1 pane
  {
    if(!env_slider_control(blk, WIN_ED_WAVE1, icon, &i->wave[0].env)) // envelope
      if(icon == ICON_WAV_GT_SLDR)
        i->wave[0].glide = update_slider_param(blk, SLDR_GLIDE, WIN_ED_WAVE1, icon); // glide time
  }
  else if(ro.slider.window == ro.handle[WIN_ED_WAVE2]) // editor wave2 pane
  {
    if(!env_slider_control(blk, WIN_ED_WAVE2, icon, &i->wave[1].env)) // envelope
      if(icon == ICON_WAV_GT_SLDR)
        i->wave[1].glide = update_slider_param(blk, SLDR_GLIDE, WIN_ED_WAVE2, icon); // glide time
  }
  else if(ro.slider.window == ro.handle[WIN_ED_FILTER]) // editor filter pane
  {
    if(!env_slider_control(blk, WIN_ED_FILTER, icon, &i->filter_env)) // envelope
      if(icon == ICON_FLT_RES_SLDR)
        i->filter_q = update_slider_param(blk, SLDR_RESONANCE, WIN_ED_FILTER, icon); // resonance
  }
  else if(ro.slider.window == ro.handle[WIN_ED_NOISE]) // editor noise pane
    env_slider_control(blk, WIN_ED_NOISE, icon, &i->noise_env); // envelope

  else if(ro.slider.window == ro.handle[WIN_ED_GENERAL]) // editor general pane
    switch(ro.slider.icon)
    {
      case ICON_COM_FM_SLDR:   i->fm_depth  = update_slider_param(blk, SLDR_FM,         WIN_ED_GENERAL, ICON_COM_FM_SLDR);   break; // FM Mod Depth
      case ICON_COM_DT_SLDR:   i->detune    = update_slider_param(blk, SLDR_DETUNE,     WIN_ED_GENERAL, ICON_COM_DT_SLDR);   break; // Wave 1/2 Detune
      case ICON_MOD_RATE_SLDR: i->mod_rate  = update_slider_param(blk, SLDR_MOD_RATE,   WIN_ED_GENERAL, ICON_MOD_RATE_SLDR); break; // Modulation rate
      case ICON_MOD_DPTH_SLDR: i->mod_depth = update_slider_param(blk, SLDR_MOD_DEPTH,  WIN_ED_GENERAL, ICON_MOD_DPTH_SLDR); break; // Modulation Depth
      case ICON_GEN_ERT_SLDR:  i->retrig    = update_slider_param(blk, SLDR_GEN_RETRIG, WIN_ED_GENERAL, ICON_GEN_ERT_SLDR);  break; // Envelope Retrigger Time
      case ICON_GEN_GAIN_SLDR: i->gain      = update_slider_param(blk, SLDR_GEN_GAIN,   WIN_ED_GENERAL, ICON_GEN_GAIN_SLDR); break; // Instrument Overall Gain
    }

  else
    return FALSE;

  return TRUE;
}


/*
 * edt_mouse_click
 * ---------------
 * blk = block from Wimp_Poll
 * state = icon state, selected or not
 * Returns True if dealt with here, else False
 */
int edt_mouse_click(int *blk, int state)
{
  static int swapped;     // true when temp is loaded from syn.user
  static ins_t temp;
  ins_t *i = ro.user;
  int n;
  tone_t w;

  int inc = 0;
  if(blk[2] == MOUSE_SELECT)
    inc = 1;
  else if (blk[2] == MOUSE_ADJUST)
    inc = -1;

  // Editor window
  if(blk[3] == ro.handle[WIN_EDT])
  {
    if((blk[2] == MOUSE_SELECT) || (blk[2] == MOUSE_ADJUST))
    {
      int cur = read_numeric_value(blk[3], ICON_ED_PATCH); // get current patch number
      int cur_ins = read_numeric_value(blk[3], ICON_ED_INST_NUM); // get current instrument number

      switch(blk[4])
      {
        case ICON_ED_WAVE1:
        case ICON_ED_WAVE2:
        case ICON_ED_FILTER:
        case ICON_ED_NOISE:
        case ICON_ED_GENERAL:
          set_editor_pane(blk[4]);
          break;

        case ICON_ED_PATCH_DEC: // previous patch
          inc = -inc;
        case ICON_ED_PATCH_INC: // next patch
          cur += inc;
          WRAP(0, cur, ro.num_patches - 1);
          load_instrument(cur);
          kit_check();
          break;

        case ICON_ED_INST_DEC: // previous instrument
          inc = -inc;
        case ICON_ED_INST_INC: // next instrument
          cur_ins += inc;
          {
            _kernel_oserror *oserr = NULL;
            do
            {
              WRAP(0, cur_ins, ro.num_instruments - 1);
              oserr = load_instr_by_number(cur_ins);
              if(oserr)
                if(oserr->errnum == INSTRUMENT_DELETED)  // empty slot in instrument store
                  cur_ins += inc;
            }
            while(oserr);
          }
          break;

        case ICON_ED_RELOAD: // reload / restore
          read_text_string(blk[3], ICON_ED_INST_NAME, ro.user->name); // read instrument name
          read_text_string(blk[3], ICON_ED_BANK_NAME, ro.bank_name);  // read bank name
          if(swapped)
          {
            *ro.user = temp;      // user = temp
            display_user();
          }
          else
          {
            temp = *ro.user;      // temp = user
            load_instrument(cur); // user = current
          }
          swapped ^= 1;
          icon_text_change((swapped) ? "Restore" : "Reload", blk[3], blk[4]);
          icon_colour_change((swapped) ? 0x1b : 0x17, blk[3], blk[4]); // red, black
          break;

        case ICON_ED_CLEAR: // clear user
          memset(ro.user, 0, sizeof(ins_t));
          display_user();
          break;

        case ICON_ED_SAVE: // current = user
          read_text_string(blk[3], ICON_ED_INST_NAME, ro.user->name); // read instrument name
          read_text_string(blk[3], ICON_ED_BANK_NAME, ro.bank_name);  // read bank name
          synth_command(SYN_SAVE_USER, ro.cur_ins);
          break;

        case ICON_ED_SAVE_NEW: // new variation = user;
          read_text_string(blk[3], ICON_ED_INST_NAME, ro.user->name); // read instrument name
          read_text_string(blk[3], ICON_ED_BANK_NAME, ro.bank_name);  // read bank name
          synth_command(SYN_SAVE_NEW, 0);
          break;
      }
    }
  }

  // Editor wave1 pane
  else if(blk[3] == ro.handle[WIN_ED_WAVE1])
  {
    if((blk[2] == MOUSE_SELECT) || (blk[2] == MOUSE_ADJUST))
      switch(blk[4])
      {
        case ICON_ENV_DY_BACK: // Delay
        case ICON_ENV_AR_BACK: // Attack Rate
        case ICON_ENV_AT_BACK: // Attack Target
        case ICON_ENV_AH_BACK: // Attack Hold
        case ICON_ENV_DR_BACK: // Decay Rate
        case ICON_ENV_DT_BACK: // Decay Target
        case ICON_ENV_SR_BACK: // Sustain Rate
        case ICON_ENV_RR_BACK: // Release Rate
        case ICON_WAV_GT_BACK: // Glide Time
          blk[4]++;
        case ICON_ENV_DY_SLDR:
        case ICON_ENV_AR_SLDR:
        case ICON_ENV_AT_SLDR:
        case ICON_ENV_AH_SLDR:
        case ICON_ENV_DR_SLDR:
        case ICON_ENV_DT_SLDR:
        case ICON_ENV_SR_SLDR:
        case ICON_ENV_RR_SLDR:
        case ICON_WAV_GT_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_WAV_NUM_DEC: // Wave number dec
          inc = -inc;
        case ICON_WAV_NUM_INC: // Wave number inc
          if((test_menu.item[0].item_flags & IT_TICKED) == 0)
          {
            n = read_numeric_value(blk[3], ICON_WAV_NUM_VAL) + inc;
            WRAP(0, n, ro.num_wavs - 1);
            while(ro.harm[n].name[0] == 0) // skip deleted slots
            {
              n += inc;
              WRAP(0, n, ro.num_wavs - 1);
            }
            icon_text_change(ro.harm[n].name, blk[3], ICON_WAV_NUM_NAME);
            icon_text_change(itoa(n), blk[3], ICON_WAV_NUM_VAL);
            i->wave[0].number = n;
          }
          break;

        case ICON_WAV_IPO_DEC: // Initial Pitch Offset dec
          inc = -inc;
        case ICON_WAV_IPO_INC: // Initial Pitch Offset inc
          n = read_numeric_value(blk[3], ICON_WAV_IPO_VAL) + inc;
          LIMIT(-127, n, 127);
          icon_text_change(itoa(n), blk[3], ICON_WAV_IPO_VAL);
          i->wave[0].initial_pitch = n;
          break;

        case ICON_WAV_FPO_DEC: // Final Pitch Offset dec
          inc = -inc;
        case ICON_WAV_FPO_INC: // Final Pitch Offset inc
          n = read_numeric_value(blk[3], ICON_WAV_FPO_VAL) + inc;
          LIMIT(-127, n, 127);
          icon_text_change(itoa(n), blk[3], ICON_WAV_FPO_VAL);
          i->wave[0].pitch = n;
          break;

        case ICON_WAV_PM:  i->switches = (i->switches & ~(1<<PITCH1_MOD)) | (state << PITCH1_MOD); break; // Pitch Mod
        case ICON_WAV_AUM: i->switches = (i->switches & ~(1<<AMP1_MOD))   | (state << AMP1_MOD);   break; // Amp Unsigned Mod
        case ICON_WAV_ASM: i->switches = (i->switches & ~(1<<AMP1_RING))  | (state << AMP1_RING);  break; // Amp Signed Mod

        case ICON_WAV_COPY:
          i->wave[1] = i->wave[0];
          display_user();
          break;

        case ICON_SWAP_WAVES:
          w = i->wave[0];
          i->wave[0] = i->wave[1];
          i->wave[1] = w;
          display_user();
          break;

        case ICON_WAV_MASTER:
          i->master_env = state;
          display_user();
          break;
      }
  }

  // Editor wave2 pane
  else if(blk[3] == ro.handle[WIN_ED_WAVE2])
  {
    if((blk[2] == MOUSE_SELECT) || (blk[2] == MOUSE_ADJUST))
      switch(blk[4])
      {
        case ICON_ENV_DY_BACK: // Delay
        case ICON_ENV_AR_BACK: // Attack Rate
        case ICON_ENV_AT_BACK: // Attack Target
        case ICON_ENV_AH_BACK: // Attack Hold
        case ICON_ENV_DR_BACK: // Decay Rate
        case ICON_ENV_DT_BACK: // Decay Target
        case ICON_ENV_SR_BACK: // Sustain Rate
        case ICON_ENV_RR_BACK: // Release Rate
        case ICON_WAV_GT_BACK: // Glide Time
          blk[4]++;
        case ICON_ENV_DY_SLDR:
        case ICON_ENV_AR_SLDR:
        case ICON_ENV_AT_SLDR:
        case ICON_ENV_AH_SLDR:
        case ICON_ENV_DR_SLDR:
        case ICON_ENV_DT_SLDR:
        case ICON_ENV_SR_SLDR:
        case ICON_ENV_RR_SLDR:
        case ICON_WAV_GT_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_WAV_NUM_DEC: // Wave number dec
          inc = -inc;
        case ICON_WAV_NUM_INC: // Wave number inc
          if((test_menu.item[1].item_flags & IT_TICKED) == 0)
          {
            n = read_numeric_value(blk[3], ICON_WAV_NUM_VAL) + inc;
            WRAP(0, n, ro.num_wavs - 1);
            while(ro.harm[n].name[0] == 0) // skip deleted slots
            {
              n += inc;
              WRAP(0, n, ro.num_wavs - 1);
            }
            icon_text_change(ro.harm[n].name, blk[3], ICON_WAV_NUM_NAME);
            icon_text_change(itoa(n), blk[3], ICON_WAV_NUM_VAL);
            i->wave[1].number = n;
          }
          break;

        case ICON_WAV_IPO_DEC: // Initial Pitch Offset dec
          inc = -inc;
        case ICON_WAV_IPO_INC: // Initial Pitch Offset inc
          n = read_numeric_value(blk[3], ICON_WAV_IPO_VAL) + inc;
          LIMIT(-127, n, 127);
          icon_text_change(itoa(n), blk[3], ICON_WAV_IPO_VAL);
          i->wave[1].initial_pitch = n;
          break;

        case ICON_WAV_FPO_DEC: // Final Pitch Offset dec
          inc = -inc;
        case ICON_WAV_FPO_INC: // Final Pitch Offset inc
          n = read_numeric_value(blk[3], ICON_WAV_FPO_VAL) + inc;
          LIMIT(-127, n, 127);
          icon_text_change(itoa(n), blk[3], ICON_WAV_FPO_VAL);
          i->wave[1].pitch = n;
          break;

        case ICON_WAV_PM:  i->switches = (i->switches & ~(1<<PITCH2_MOD)) | (state << PITCH2_MOD); break; // Pitch Mod
        case ICON_WAV_AUM: i->switches = (i->switches & ~(1<<AMP2_MOD))   | (state << AMP2_MOD);   break; // Amp Unsigned Mod
        case ICON_WAV_ASM: i->switches = (i->switches & ~(1<<AMP2_RING))  | (state << AMP2_RING);  break; // Amp Signed Mod

        case ICON_WAV_COPY:
          i->wave[0] = i->wave[1];
          display_user();
          break;

        case ICON_SWAP_WAVES:
          w = i->wave[0];
          i->wave[0] = i->wave[1];
          i->wave[1] = w;
          display_user();
          break;

        case ICON_WAV_MASTER:
          i->master_env = state * 2;
          display_user();
          break;
      }
  }

  // Editor filter pane
  else if(blk[3] == ro.handle[WIN_ED_FILTER])
  {
    if((blk[2] == MOUSE_SELECT) || (blk[2] == MOUSE_ADJUST))
      switch(blk[4])
      {
        case ICON_ENV_DY_BACK: // Delay
        case ICON_ENV_AR_BACK: // Attack Rate
        case ICON_ENV_AT_BACK: // Attack Target
        case ICON_ENV_AH_BACK: // Attack Hold
        case ICON_ENV_DR_BACK: // Decay Rate
        case ICON_ENV_DT_BACK: // Decay Target
        case ICON_ENV_SR_BACK: // Sustain Rate
        case ICON_ENV_RR_BACK: // Release Rate
        case ICON_FLT_RES_BACK: // Resonance
          blk[4]++;
        case ICON_ENV_DY_SLDR:
        case ICON_ENV_AR_SLDR:
        case ICON_ENV_AT_SLDR:
        case ICON_ENV_AH_SLDR:
        case ICON_ENV_DR_SLDR:
        case ICON_ENV_DT_SLDR:
        case ICON_ENV_SR_SLDR:
        case ICON_ENV_RR_SLDR:
        case ICON_FLT_RES_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_FLT_CF_DEC: // Filter Cuttoff Frequency dec
          inc = -inc;
        case ICON_FLT_CF_INC: // Filter Cuttoff Frequency inc
          n = read_numeric_value(blk[3], ICON_FLT_CF_VAL) + inc;
          LIMIT(0 ,n, 127);
          icon_text_change(itoa(n), blk[3], ICON_FLT_CF_VAL);
          i->filter_fc = n;
          break;

        case ICON_FLT_LOW_PASS: i->switches = (i->switches & ~(1<<LOWPASS)) | (state << LOWPASS); break; // Low Pass
        case ICON_FLT_BAND_PASS: i->switches = (i->switches & ~(1<<BANDPASS)) | (state << BANDPASS); break; // Band Pass
        case ICON_FLT_HIGH_PASS: i->switches = (i->switches & ~(1<<HIGHPASS)) | (state << HIGHPASS); break; // High Pass
        case ICON_FLT_RETRIG: i->switches = (i->switches & ~(1<<FILTER_RETRIGGER)) | (state << FILTER_RETRIGGER); break; // Retrigger
        case ICON_FLT_INVERT: i->switches = (i->switches & ~(1<<INV_FILTER_ENV)) | (state << INV_FILTER_ENV); break; // Invert Envelope
        case ICON_FLT_PM: i->switches = (i->switches & ~(1<<FILTER_MOD)) | (state << FILTER_MOD); break; // Pitch Mod
        case ICON_FLT_WF: i->switches = (i->switches & ~(1<<FILTERED_TONE)) | (state << FILTERED_TONE); break; // Wave Filter
        case ICON_FLT_PT: i->switches = (i->switches & ~(1<<FILTER_TRACK)) | (state << FILTER_TRACK); break; // Pitch Track
        case ICON_FLT_ET: i->switches = (i->switches & ~(1<<FILTER_ENV_TRACK)) | (state << FILTER_ENV_TRACK); break; // Envelope Track

        case ICON_FLT_MASTER:
          i->master_env = state * 3;
          display_user();
          break;
      }
  }

  // Editor noise pane
  else if(blk[3] == ro.handle[WIN_ED_NOISE])
  {
    if((blk[2] == MOUSE_SELECT) || (blk[2] == MOUSE_ADJUST))
      switch(blk[4])
      {
        case ICON_ENV_DY_BACK: // Delay
        case ICON_ENV_AR_BACK: // Attack Rate
        case ICON_ENV_AT_BACK: // Attack Target
        case ICON_ENV_AH_BACK: // Attack Hold
        case ICON_ENV_DR_BACK: // Decay Rate
        case ICON_ENV_DT_BACK: // Decay Target
        case ICON_ENV_SR_BACK: // Sustain Rate
        case ICON_ENV_RR_BACK: // Release Rate
          blk[4]++;
        case ICON_ENV_DY_SLDR:
        case ICON_ENV_AR_SLDR:
        case ICON_ENV_AT_SLDR:
        case ICON_ENV_AH_SLDR:
        case ICON_ENV_DR_SLDR:
        case ICON_ENV_DT_SLDR:
        case ICON_ENV_SR_SLDR:
        case ICON_ENV_RR_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_NOISE_AM: i->switches = (i->switches & ~(1<<NOISE_MOD)) | (state << NOISE_MOD); break; // Amplitude Modulation
        case ICON_NOISE_RETRIG: i->switches = (i->switches & ~(1<<NOISE_RETRIGGER)) | (state << NOISE_RETRIGGER); break; // Retrigger
        case ICON_NOISE_ET: i->switches = (i->switches & ~(1<<NOISE_ENV_TRACK)) | (state << NOISE_ENV_TRACK); break; // Envelope Track

        case ICON_NOISE_MASTER:
          i->master_env = state * 4;
          display_user();
          break;
      }
  }

  // Editor general pane
  else if(blk[3] == ro.handle[WIN_ED_GENERAL])
  {
    if((blk[2] == MOUSE_SELECT) || (blk[2] == MOUSE_ADJUST))
      switch(blk[4])
      {
        case ICON_COM_FM_BACK:   // FM Mod Depth
        case ICON_COM_DT_BACK:   // Wave 1/2 Detune
        case ICON_MOD_RATE_BACK: // Modulation rate
        case ICON_MOD_DPTH_BACK: // Modulation Depth
        case ICON_GEN_ERT_BACK:  // Envelope Retrigger Time
        case ICON_GEN_GAIN_BACK: // Instrument Overall Gain
          blk[4]++;
        case ICON_COM_FM_SLDR:
        case ICON_COM_DT_SLDR:
        case ICON_MOD_RATE_SLDR:
        case ICON_MOD_DPTH_SLDR:
        case ICON_GEN_ERT_SLDR:
        case ICON_GEN_GAIN_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_MOD_NUM_DEC: // Modulator Wave number dec
          inc = -inc;
        case ICON_MOD_NUM_INC: // Modulator Wave number inc
          if((test_menu.item[2].item_flags & IT_TICKED) == 0)
          {
            n = read_numeric_value(blk[3], ICON_MOD_NUM_VAL) + inc;
            WRAP(0, n, ro.num_wavs - 1);
            while(ro.harm[n].name[0] == 0) // skip deleted slots
            {
              n += inc;
              WRAP(0, n, ro.num_wavs - 1);
            }
            icon_text_change(ro.harm[n].name, blk[3], ICON_MOD_NUM_NAME);
            icon_text_change(itoa(n), blk[3], ICON_MOD_NUM_VAL);
            i->mod_wave = n;
          }
          break;

        case ICON_COM_PT: i->switches = (i->switches & ~(1<<PITCH_TRACK)) | (state << PITCH_TRACK); break; // Common Wave, Pitch Track
        case ICON_COM_ET: i->switches = (i->switches & ~(1<<WAVE_ENV_TRACK)) | (state << WAVE_ENV_TRACK); break; // Common Wave, Envelope Track
        case ICON_COM_RETRIG: i->switches = (i->switches & ~(1<<WAVE_RETRIGGER)) | (state << WAVE_RETRIGGER); break; // Common Wave, Retrigger
        case ICON_COM_W1_OUT: i->switches = (i->switches & ~(1<<TONE1_OUT)) | (state << TONE1_OUT); break; // Wave 1 Out
        case ICON_COM_W2_OUT: i->switches = (i->switches & ~(1<<TONE2_OUT)) | (state << TONE2_OUT); break; // Wave 2 Out
        case ICON_COM_WRM: i->switches = (i->switches & ~(1<<TONE_RING_MOD)) | (state << TONE_RING_MOD); break; // Wave Ring Mod
        case ICON_GEN_DLE: i->switches = (i->switches & ~(1<<DEFINED_LENGTH)) | (state << DEFINED_LENGTH); break; // Defined Length Envelopes
        case ICON_GEN_MONO: i->switches = (i->switches & ~(1<<MONO_SCAN)) | (state << MONO_SCAN); break; // Mono Instrument
      }
  }

  else
    return FALSE;

  if(blk[2] == MOUSE_MENU)
    open_menu(blk[0] - 64, blk[1], MENU_EDITOR, (int)&editor_menu);

  return TRUE;
}


/*
 * slider_value_entry
 * ------------------
 */
static int slider_value_entry(int n, int max, int slider, int window, int icon)
{
  LIMIT(0, n, max - 1);
  slider_change(n, slider, window, icon - 1);
  return n;
}


/*
 * env_key_press
 * ------------------
 * Handles key presses for an envelope.
 * Returns True if dealt with here, else False.
 */
static int env_key_press(int n, int win, int icon, env_t *env)
{
  switch(icon)
  {
    case ICON_ENV_DY_VAL: env->delay         = slider_value_entry(n, 4096,  SLDR_ENV_DY, win, icon); break; // Delay
    case ICON_ENV_AR_VAL: env->attack_step   = slider_value_entry(n, 32768, SLDR_ENV_AR, win, icon); break; // Attack Rate
    case ICON_ENV_AT_VAL: env->attack_target = slider_value_entry(n, 32768, SLDR_ENV_AT, win, icon); break; // Attack Target
    case ICON_ENV_AH_VAL: env->hold          = slider_value_entry(n, 4096,  SLDR_ENV_AH, win, icon); break; // Atack Hold
    case ICON_ENV_DR_VAL: env->decay_step    = slider_value_entry(n, 32768, SLDR_ENV_DR, win, icon); break; // Decay Rate
    case ICON_ENV_DT_VAL: env->decay_target  = slider_value_entry(n, 32768, SLDR_ENV_DT, win, icon); break; // Decay Target
    case ICON_ENV_SR_VAL: env->sustain_step  = slider_value_entry(n, 32768, SLDR_ENV_SR, win, icon); break; // Sustain Rate
    case ICON_ENV_RR_VAL: env->release_step  = slider_value_entry(n, 32768, SLDR_ENV_RR, win, icon); break; // Release Rate

    default: return FALSE;
  }
  return TRUE;
}


/*
 * edt_key_press
 * -------------
 * blk = block from Wimp_GetIconState
 * key = current character
 * p = pointer to icon text
 * Returns True if dealt with here, else False
 */
int edt_key_press(int *blk, int key, char *p)
{
  ins_t *i = ro.user;
  int n = atoi(p);

  // PgDown or PgUp, previous or next patch
  if((key == 0x19e)||(key == 0x19f))
  {
    n = ro.cur_ins;
    if(key == 0x19e)
    {
      if(--n < 0) n = ro.num_patches - 1;
    }
    else if(++n >= ro.num_patches) n = 0;
    load_instrument(n);
    kit_check();
  }

  // main editor window
  else if(blk[0] == ro.handle[WIN_EDT])
  {
    if(key == '\r')
      switch(blk[1])
      {
        case ICON_ED_PATCH: // Patch Number
          if(ro.patch_valid) // ignore the situation of an unreferenced instrument
          {
            load_instrument(n);
            kit_check();
          }
          break;

        case ICON_ED_INST_NUM: // Instrument Number
          load_instr_by_number(n);
          kit_check();
          break;

        case ICON_ED_INST_NAME: // Instrument Name
          if(p[0])
            strncpy(ro.user->name,    p, NAME_LEN);
          break;

        case ICON_ED_BANK_NAME: // Bank Name
          if(p[0])
            strncpy(ro.bank_name,     p, NAME_LEN);
          break;

        case ICON_ED_SOUND_SET: // Sound Set Name
          if(p[0])
            strncpy(ro.soundset_name, p, NAME_LEN);
         break;
      }
  }

  // editor wave1 pane
  else if(blk[0] == ro.handle[WIN_ED_WAVE1])
  {
    if(key == '\r')
    {
      if(!env_key_press(n, WIN_ED_WAVE1, blk[1], &i->wave[0].env))
        switch(blk[1])
        {
          case ICON_WAV_GT_VAL: i->wave[0].glide = slider_value_entry(n, 32768, SLDR_GLIDE, WIN_ED_WAVE1, blk[1]); break; // Glide Time

          case ICON_WAV_NUM_VAL: // Wave Number
            if(test_menu.item[0].item_flags & IT_TICKED)
            {
              icon_text_change("Test", blk[0], ICON_WAV_NUM_NAME);
              icon_text_change("", blk[0], blk[1]);
            }
            else
            {
              LIMIT(0, n, ro.num_wavs - 1);
              while(ro.harm[n].name[0] == 0) // skip deleted slots
                if(++n >= ro.num_wavs)
                  n = 0;
              i->wave[0].number = n;
              icon_text_change(ro.harm[n].name, blk[0], ICON_WAV_NUM_NAME);
              icon_text_change(itoa(n), blk[0], blk[1]);
            }
            break;

          case ICON_WAV_IPO_VAL: // Initial Pitch Offset
            LIMIT(-127, n, 127);
            i->wave[0].initial_pitch = n;
            icon_text_change(itoa(n), blk[0], ICON_WAV_IPO_VAL);
            break;

          case ICON_WAV_FPO_VAL: // Final Pitch Offset
            LIMIT(-127, n, 127);
            i->wave[0].pitch = n;
            icon_text_change(itoa(n), blk[0], ICON_WAV_FPO_VAL);
            break;
        }
    }
  }

  // editor wave2 pane
  else if(blk[0] == ro.handle[WIN_ED_WAVE2])
  {
    if(key == '\r')
    {
      if(!env_key_press(n, WIN_ED_WAVE2, blk[1], &i->wave[1].env))
        switch(blk[1])
        {
          case ICON_WAV_GT_VAL: i->wave[1].glide = slider_value_entry(n, 32768, SLDR_GLIDE, WIN_ED_WAVE2, blk[1]); break; // Glide Time

          case ICON_WAV_NUM_VAL: // Wave Number
            if(test_menu.item[1].item_flags & IT_TICKED)
            {
              icon_text_change("Test", blk[0], ICON_WAV_NUM_NAME);
              icon_text_change("", blk[0], blk[1]);
            }
            else
            {
              LIMIT(0, n, ro.num_wavs - 1);
              while(ro.harm[n].name[0] == 0) // skip deleted slots
                if(++n >= ro.num_wavs)
                  n = 0;
              i->wave[1].number = n;
              icon_text_change(ro.harm[n].name, blk[0], ICON_WAV_NUM_NAME);
              icon_text_change(itoa(n), blk[0], blk[1]);
            }
            break;

          case ICON_WAV_IPO_VAL: // Initial Pitch Offset
            LIMIT(-127, n, 127);
            i->wave[1].initial_pitch = n;
            icon_text_change(itoa(n), blk[0], ICON_WAV_IPO_VAL);
            break;

          case ICON_WAV_FPO_VAL: // Final Pitch Offset
            LIMIT(-127, n, 127);
            i->wave[1].pitch = n;
            icon_text_change(itoa(n), blk[0], ICON_WAV_FPO_VAL);
            break;
        }
    }
  }

  // editor filter pane
  else if(blk[0] == ro.handle[WIN_ED_FILTER])
  {
    if(key == '\r')
    {
      if(!env_key_press(n, WIN_ED_FILTER, blk[1], &i->filter_env))
        switch(blk[1])
        {
          case ICON_FLT_RES_VAL: i->filter_q = slider_value_entry(n, 1024, SLDR_RESONANCE, WIN_ED_FILTER, blk[1]); break; // Resonance

          case ICON_FLT_CF_VAL: // Filter Cuttoff Frequency
            LIMIT(0, n, 127);
            i->filter_fc = n;
            icon_text_change(itoa(n), blk[0], ICON_FLT_CF_VAL);
            break;
        }
    }
  }

  // editor noise pane
  else if(blk[0] == ro.handle[WIN_ED_NOISE])
  {
    if(key == '\r')
      env_key_press(n, WIN_ED_NOISE, blk[1], &i->noise_env);
  }

  // editor general pane
  else if(blk[0] == ro.handle[WIN_ED_GENERAL])
  {
    if(key == '\r')
      switch(blk[1])
      {
        case ICON_COM_FM_VAL:   i->fm_depth  = slider_value_entry(n, 32768, SLDR_FM,         WIN_ED_GENERAL, blk[1]); break; // FM Mod Depth
        case ICON_COM_DT_VAL:   i->detune    = slider_value_entry(n, 1024,  SLDR_DETUNE,     WIN_ED_GENERAL, blk[1]); break; // Wave 1/2 Detune
        case ICON_MOD_RATE_VAL: i->mod_rate  = slider_value_entry(n, 8192,  SLDR_MOD_RATE,   WIN_ED_GENERAL, blk[1]); break; // Modulation rate
        case ICON_MOD_DPTH_VAL: i->mod_depth = slider_value_entry(n, 32768, SLDR_MOD_DEPTH,  WIN_ED_GENERAL, blk[1]); break; // Modulation Depth
        case ICON_GEN_ERT_VAL:  i->retrig    = slider_value_entry(n, 1024,  SLDR_GEN_RETRIG, WIN_ED_GENERAL, blk[1]); break; // Envelope Retrigger Time
        case ICON_GEN_GAIN_VAL: i->gain      = slider_value_entry(n, 10241, SLDR_GEN_GAIN,   WIN_ED_GENERAL, blk[1]); break; // Instrument Overall Gain (+20dB to -20dB, 1024 = 0dB)

        case ICON_MOD_NUM_VAL: // Modulator Wave number
          if(test_menu.item[2].item_flags & IT_TICKED)
          {
            icon_text_change("Test", blk[0], ICON_MOD_NUM_NAME);
            icon_text_change("", blk[0], blk[1]);
          }
          else
          {
            LIMIT(0, n, ro.num_wavs - 1);
            while(ro.harm[n].name[0] == 0) // skip deleted slots
                if(++n >= ro.num_wavs)
                  n = 0;
            i->mod_wave = n;
            icon_text_change(ro.harm[n].name, blk[0], ICON_MOD_NUM_NAME);
            icon_text_change(itoa(n), blk[0], blk[1]);
          }
          break;
      }
  }

  else
    return FALSE;

  return TRUE;
}

