/*
**  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1995-1996 Andreas Voss (andreas@avix.rhein-neckar.de)
**
**  Copyright (C) 1995-1996 Per Sigmond (Per.Sigmond@hia.no)
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "wx.h"
#pragma hdrstop

#include "harmony.h"
#include "harmonyp.h"
#include "player.h"
#include "trackwin.h"
#include "pianowin.h"
#include "guitar.h"
#include "song.h"
#include "filter.h"
#include "util.h"
#include "jazz.h"
#include "toolbar.h"
#include "hbanalyz.h"
#include <iostream.h>
#include <fstream.h>

#ifdef wx_xt
#define wxbMessageBox wxMessageBox
#endif

tHBInterface *the_harmony_browser = 0;

#define MEN_CLOSE	1
#define MEN_MIDI	2
#define MEN_TRANSPOSE	4
#define MEN_CLEARSEQ	6
#define MEN_EDIT	7
#define MEN_MOUSE	8
#define MEN_HELP	9

#define MEN_MAJSCALE	10
#define MEN_HARSCALE	11
#define MEN_MELSCALE	12

#define MEN_EQ4		13
#define MEN_EQ3		14
#define MEN_EQ2		15
#define MEN_EQ1		16
#define MEN_EQH		17
#define MEN_EQ0		18
#define MEN_251		19
#define MEN_TRITONE	20
#define MEN_PIANO	21
#define MEN_EQB		22
#define MEN_HAUNSCH	23
#define MEN_ANALYZE	24
#define MEN_IONSCALE	25
#define MEN_SETTINGS	26
#define MEN_LOAD	27
#define MEN_SAVE	28


#ifdef wx_x
#include "../bitmaps/majscale.xpm"
#include "../bitmaps/harscale.xpm"
#include "../bitmaps/melscale.xpm"
#include "../bitmaps/ionscale.xpm"
#include "../bitmaps/same4.xpm"
#include "../bitmaps/same3.xpm"
#include "../bitmaps/same2.xpm"
#include "../bitmaps/same1.xpm"
#include "../bitmaps/sameh.xpm"
#include "../bitmaps/sameb.xpm"
#include "../bitmaps/same0.xpm"
#include "../bitmaps/std251.xpm"
#include "../bitmaps/tritone.xpm"
#include "../bitmaps/haunsch.xpm"
#include "../bitmaps/piano.xpm"
#include "../bitmaps/transpose.xpm"
#include "../bitmaps/analyze.xpm"
#include "../bitmaps/delchord.xpm"
static tToolDef tdefs[] = {
  { MEN_MAJSCALE,  	TRUE,  6, tb_majscale },
  { MEN_HARSCALE,  	TRUE,  6, tb_harscale },
  { MEN_MELSCALE,  	TRUE,  6, tb_melscale },
  { MEN_IONSCALE,  	TRUE, 12, tb_ionscale },

//  { MEN_EQ4,  		TRUE,  6, tb_same4 },
//  { MEN_EQ3,  		TRUE,  6, tb_same3 },
//  { MEN_EQ2,  		TRUE,  6, tb_same2 },
//  { MEN_EQ1,  		TRUE,  6, tb_same1 },
//  { MEN_EQ0,  		TRUE,  6, tb_same0 },
  { MEN_EQH,  		TRUE,  6, tb_sameh },
  { MEN_251,  		TRUE,  6, tb_std251 },
  { MEN_EQB,  		TRUE,  6, tb_sameb },
  { MEN_TRITONE,  	TRUE,  6, tb_tritone },
  { MEN_PIANO, 		TRUE, 12, tb_piano },
  { MEN_HAUNSCH,  	TRUE, 12, tb_haunsch },

  { MEN_TRANSPOSE,	FALSE,  6, tb_transpose },
  { MEN_ANALYZE,	FALSE,  6, tb_analyze },
  { MEN_CLEARSEQ,	FALSE,  6, tb_delchord }
};

#else

static tToolDef tdefs[] = {
  { MEN_MAJSCALE,  	TRUE,  2, "tb_majscale" },
  { MEN_HARSCALE,  	TRUE,  2, "tb_harscale" },
  { MEN_MELSCALE,  	TRUE,  2, "tb_melscale" },
  { MEN_IONSCALE,  	TRUE,  8, "tb_ionscale" },

//  { MEN_EQ4,  		TRUE,  2, "tb_same4" },
//  { MEN_EQ3,  		TRUE,  2, "tb_same3" },
//  { MEN_EQ2,  		TRUE,  2, "tb_same2" },
//  { MEN_EQ1,  		TRUE,  2, "tb_same1" },
//  { MEN_EQ0,  		TRUE,  2, "tb_same0" },
  { MEN_EQH,  		TRUE,  2, "tb_sameh" },
  { MEN_251,  		TRUE,  2, "tb_std251" },
  { MEN_EQB,  		TRUE,  2, "tb_sameb" },
  { MEN_TRITONE,  	TRUE,  2, "tb_tritone" },
  { MEN_PIANO, 		TRUE,  8, "tb_piano" },
  { MEN_HAUNSCH,  	TRUE,  8, "tb_haunsch" },

  { MEN_TRANSPOSE,	FALSE,  2, "tb_transpose" },
  { MEN_ANALYZE,	FALSE,  2, "tb_analyze" },
  { MEN_CLEARSEQ,	FALSE,  2, "tb_delchord" }
};

#endif



// ------------------------ HBPlayer ------------------------

class HBPlayer : public wxTimer
{
    friend class HBCanvas;
  public:
    HBPlayer();
    void StartPlay(const HBContext &);
    void Paste(tEventArray &);
    void StopPlay();
    void SettingsDialog(wxFrame *parent);
    int  IsPlaying() const { return playing; }
    const HBContext &Context()	{ return context; }

    virtual void Notify();

    int  GetChordKeys(int *out, const HBContext &);
    int  GetMeldyKeys(int *out, const HBContext &);
    int  GetBassKey(const HBContext &);

  private:
    HBContext context;
    static int bass_channel, bass_veloc;
    static int chord_channel, chord_veloc;
    static int meldy_channel, meldy_veloc;
    static Bool bass_enabled, chord_enabled, meldy_enabled;
    static int bass_pitch, chord_pitch, meldy_pitch;
    static int meldy_speed;

    int bass_key, chord_keys[12], n_chord_keys;
    int meldy_keys[12], n_meldy_keys, meldy_index;
    int note_length;
    int playing;
};

int HBPlayer::bass_enabled  = 1;
int HBPlayer::bass_channel  = 1;
int HBPlayer::bass_veloc    = 90;
int HBPlayer::bass_pitch    = 40;

int HBPlayer::chord_enabled = 1;
int HBPlayer::chord_channel = 2;
int HBPlayer::chord_veloc   = 90;
int HBPlayer::chord_pitch   = 60;

int HBPlayer::meldy_enabled = 0;
int HBPlayer::meldy_channel = 3;
int HBPlayer::meldy_veloc   = 90;
int HBPlayer::meldy_pitch   = 70;
int HBPlayer::meldy_speed   = 100;


HBPlayer::HBPlayer()
{
  playing = 0;
  bass_key = n_chord_keys = n_meldy_keys = 0;
  note_length = 60;
  meldy_index = 0;
}


int HBPlayer::GetChordKeys(int *out, const HBContext &context)
{
  // build chord keys
  HBChord chord = context.Chord();
  int key = chord.Iter(chord_pitch - 1);
  int n   = chord.Count();
  for (int i = 0; i < n; i++)
  {
    out[i] = key;
    key = chord.Iter(key);
  }
  return n;
}


int HBPlayer::GetBassKey(const HBContext &context)
{
  // build bass note
  int key = context.ChordKey() + bass_pitch - bass_pitch % 12;
  if (key < bass_pitch)
    key += 12;
  return key;
}


int HBPlayer::GetMeldyKeys(int *out, const HBContext &context)
{
  // build melody keys
  HBChord scale = context.Scale();
  int n = scale.Count();

  int key = scale.Iter(meldy_pitch);
  int j = 0;
  for (int i = 0; i < n; i++)
  {
    out[j++] = key;
    key = scale.Iter(key);
  }
  return n;
}

void HBPlayer::Paste(tEventArray &arr)
{
  if (bass_enabled)
  {
    tKeyOn e(0, bass_channel - 1, bass_key, bass_veloc, note_length);
    arr.Put(e.Copy());
  }

  if (chord_enabled)
  {
    for (int i = 0; i < n_chord_keys; i++)
    {
      tKeyOn e(0, chord_channel - 1, chord_keys[i], chord_veloc, note_length);
      arr.Put(e.Copy());
    }
  }
}


void HBPlayer::StartPlay(const HBContext &ct)
{
  int i;

  if (playing)
    StopPlay();
  playing = 1;
  context = ct;

  bass_key = GetBassKey(context);
  n_chord_keys = GetChordKeys(chord_keys, context);
  n_meldy_keys = GetMeldyKeys(meldy_keys, context);

  // Generate KeyOn's
  if (bass_enabled)
  {
    tKeyOn e(0, bass_channel - 1, bass_key, bass_veloc);
    Midi->OutNow(&e);
  }

  if (chord_enabled)
  {
    for (i = 0; i < n_chord_keys; i++)
    {
      tKeyOn e(0, chord_channel - 1, chord_keys[i], chord_veloc);
      Midi->OutNow(&e);
    }
  }

  Notify();
  Start(60000L / 4 / meldy_speed);
}


void HBPlayer::Notify()
{
  if (meldy_enabled)
  {
    tKeyOff of(0, meldy_channel - 1, meldy_keys[meldy_index]);
    Midi->OutNow(&of);
    meldy_index = (meldy_index + 1) % n_meldy_keys;
    tKeyOn on(0, meldy_channel - 1, meldy_keys[meldy_index], meldy_veloc);
    Midi->OutNow(&on);
  }
}


void HBPlayer::StopPlay()
{
  if (!playing)
    return;
  Stop();
  playing = 0;

  int i;
  // Generate KeyOff's
  if (bass_enabled)
  {
    tKeyOff e(0, bass_channel - 1, bass_key);
    Midi->OutNow(&e);
  }

  if (chord_enabled)
  {
    for (i = 0; i < n_chord_keys; i++)
    {
      tKeyOff e(0, chord_channel - 1, chord_keys[i]);
      Midi->OutNow(&e);
    }
  }

  if (meldy_enabled)
  {
    for (i = 0; i < n_meldy_keys; i++)
    {
      tKeyOff of(0, meldy_channel - 1, meldy_keys[i]);
      Midi->OutNow(&of);
    }
  }
}

class tHBPlayerForm : public wxForm
{
  public:
	tHBPlayerForm() : wxForm( USED_WXFORM_BUTTONS ) {}
	void OnHelp()
	{
#ifndef VMS
	  HelpInstance->LoadFile( HelpPathList.FindValidPath("jazz") );
       	  HelpInstance->KeywordSearch("Harmony browser");
#endif
	}
};


void HBPlayer::SettingsDialog(wxFrame *parent)
{
  wxDialogBox *panel = new wxDialogBox(parent, "MIDI settings", FALSE );
  tHBPlayerForm      *form  = new tHBPlayerForm;

  form->Add(wxMakeFormMessage("Note Length for paste into piano window"));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Length", &note_length, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(10.0, 120.0), 0)));
  form->Add(wxMakeFormNewLine());

  panel->SetLabelPosition(wxHORIZONTAL);

  form->Add(wxMakeFormBool("Bass enable", &bass_enabled));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Channel", &bass_channel, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(1.0, 16.0), 0)));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Velocity", &bass_veloc, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(1.0, 127.0), 0)));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Pitch", &bass_pitch, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(30.0, 99.0), 0)));
  form->Add(wxMakeFormNewLine());

  form->Add(wxMakeFormBool("Chord enable", &chord_enabled));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Channel", &chord_channel, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(1.0, 16.0), 0)));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Velocity", &chord_veloc, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(1.0, 127.0), 0)));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Pitch", &chord_pitch, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(30.0, 99.0), 0)));
  form->Add(wxMakeFormNewLine());

  form->Add(wxMakeFormBool("Scale enable", &meldy_enabled));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Channel", &meldy_channel, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(1.0, 16.0), 0)));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Velocity", &meldy_veloc, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(1.0, 127.0), 0)));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Pitch", &meldy_pitch, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(30.0, 99.0), 0)));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Speed", &meldy_speed, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(60.0, 180.0), 0)));
  form->Add(wxMakeFormNewLine());

  form->AssociatePanel(panel);
  panel->Fit();

  panel->Show(TRUE);
}

// =============================================================
// HBCanvas
// =============================================================

class HBCanvas : public wxCanvas
{
    friend class HBSettingsDlg;
    friend class HBFrame;
    friend class HBMatchMarkers;
    friend ostream & operator << (ostream &os, HBCanvas const &a);
    friend istream & operator >> (istream &is, HBCanvas &a);
  public:
    HBCanvas(wxFrame *parent, int x, int y, int w, int h);
    virtual ~HBCanvas();
    virtual void OnPaint(void);
    void DrawMarkers(const HBContext &c);
    void ClearSeq();
    virtual void OnEvent(wxMouseEvent &e);

    int SeqDefined()	{ return n_seq > 0; }
    int GetChordKeys(int *out, int step, int n_steps);
    int GetSelectedChord(int *out);
    int GetSelectedScale(int *out);
    int GetBassKeys(int *out, int step, int n_steps);
    void SettingsDialog(wxFrame *parent);

    void OnMenuCommand(int id, wxToolBar *tool_bar);

    HBPlayer player;
    enum {SEQMAX = 32};

    HBAnalyzer * getAnalyzer();

  protected:
    static const int ScFa;
    void ChordRect(tRect &r, const HBContext &ct);
    void DrawChord(const HBContext &ct);
    void UnDrawChord(const HBContext &ct);
    Bool Find(float x, float y, HBContext &out);

  private:

    float xchord, ychord, wchord, hchord;
    float ofs;
    wxFrame *parent;

    HBContext *seq[SEQMAX];
    int n_seq;

    HBContext mouse_context;

    Bool haunschild_layout;
    Bool mark_4_common;
    Bool mark_3_common;
    Bool mark_2_common;
    Bool mark_1_common;
    Bool mark_b_common;
    Bool mark_0_common;
    Bool mark_1_semi;
    Bool mark_251;
    Bool mark_tritone;
    Bool mark_piano;
    void SetMarker(int id, wxToolBar *tool_bar);
    int  active_marker;

    static int  transpose_res;
    static int  analyze_res;

    static tScaleType scale_type;
    void SetScaleType(int menu_id, tScaleType st, wxToolBar *tb);
};

tScaleType HBCanvas::scale_type = Major;
const int HBCanvas::ScFa = 50;
int HBCanvas::transpose_res = 4;
int HBCanvas::analyze_res = 4;


HBCanvas::HBCanvas(wxFrame *p, int x, int y, int w, int h)
  : wxCanvas(p, x, y, w, h, 0)
{
  parent = p;
  n_seq  = 0;

  active_marker = 0;
  haunschild_layout = 0;
  mark_4_common = 0;
  mark_3_common = 0;
  mark_2_common = 0;
  mark_1_common = 0;
  mark_b_common = 0;
  mark_0_common = 0;
  mark_1_semi = 0;
  mark_251 = 0;
  mark_tritone = 0;
  mark_piano = 0;

  for (int i = 0; i < SEQMAX; i++)
    seq[i] = new HBContext();

  wxDC *dc = GetDC();
  dc->SetFont(wxSMALL_FONT);
  float tw, th;
  dc->GetTextExtent("xD#j75+9-x", &tw, &th);

  wchord = 1.2 * tw;
  hchord = 2.5 * th;
  xchord = 50;
  ychord = 4 * hchord;
  ofs    = th/4;
}


HBCanvas::~HBCanvas()
{
  if (player.IsPlaying())
    player.StopPlay();
  for (int i = 0; i < SEQMAX; i++)
    delete seq[i];
}


#ifndef VMS
inline
#endif
 ostream & operator << (ostream &os, HBCanvas const &a)
{
  int i;
  os << 1 << endl;
  os << a.n_seq << endl;
  for (i = 0; i < a.n_seq; i++)
    os << *a.seq[i];
  return os;
}

#ifndef VMS
inline 
#endif
istream & operator >> (istream &is, HBCanvas &a)
{
  int i, version;
  is >> version;
  if (version != 1)
    wxMessageBox("Wrong file format!", "Error");
  is >> a.n_seq;
  for (i = 0; i < a.n_seq; i++)
    is >> *a.seq[i];
  a.OnPaint();
  return is;
}


void HBCanvas::SetMarker(int id, wxToolBar *tool_bar)
{
  if (id != active_marker)
  {
    if (active_marker && tool_bar->GetToolState(active_marker))
      tool_bar->ToggleTool(active_marker, FALSE);
    tool_bar->ToggleTool(id, TRUE);
    active_marker = id;
  }

  mark_4_common = 0;
  mark_3_common = 0;
  mark_2_common = 0;
  mark_1_common = 0;
  mark_b_common = 0;
  mark_0_common = 0;
  mark_1_semi = 0;
  mark_251 = 0;
  mark_tritone = 0;
  mark_piano = 0;

  switch (id)
  {
    case MEN_EQ4:	mark_4_common = 1; break;
    case MEN_EQ3:	mark_3_common = 1; break;
    case MEN_EQ2:	mark_2_common = 1; break;
    case MEN_EQ1:	mark_1_common = 1; break;
    case MEN_EQB:	mark_b_common = 1; break;
    case MEN_EQH:	mark_1_semi = 1; break;
    case MEN_EQ0:	mark_0_common = 1; break;
    case MEN_251: 	mark_251 = 1; break;
    case MEN_TRITONE:	mark_tritone = 1; break;
    case MEN_PIANO:	mark_piano = 1; break;
  }

#ifndef wx_xt
  if (id > 0 && !tool_bar->GetToolState(id))
    tool_bar->ToggleTool(id, TRUE);
#endif

  OnPaint();

}


int HBCanvas::GetChordKeys(int *out, int step, int n_steps)
{
  if (n_seq == 0)
    return 0;
  int i = step * n_seq / n_steps;
  return player.GetChordKeys(out, *seq[i]);
}


int HBCanvas::GetSelectedChord(int *out)
{
  return player.GetChordKeys(out, mouse_context);
}

int HBCanvas::GetSelectedScale(int *out)
{
  return player.GetMeldyKeys(out, mouse_context);
}


int HBCanvas::GetBassKeys(int *out, int step, int n_steps)
{
  if (n_seq == 0)
    return 0;
  int i = step * n_seq / n_steps;
  out[0] = player.GetBassKey(*seq[i]);
  return 1;
}


void HBCanvas::ChordRect(tRect &r, const HBContext &ct)
{
  if (ct.SeqNr())
  {
    r.x = xchord + (ct.SeqNr() - 1) % 8 * wchord;
    r.y = hchord * ((ct.SeqNr() -1) / 8 + 0.5);
  }
  else if (!haunschild_layout)
  {
    r.x = xchord + ct.ChordNr() * wchord;
    // r.y = ychord + (int)ct.ScaleType() * hchord * 13;
    // r.y += ct.ScaleNr() * hchord;
    r.y = ychord + ct.ScaleNr() * hchord;
  }
  else
  {
    r.x = xchord + (5 * ct.ChordNr() % 7 + 5 * ct.ScaleNr() % 12) % 7 * wchord;
    // r.y = ychord + ((int)ct.ScaleType()) * hchord * 13;
    // r.y += (5 * ct.ScaleNr() % 12) * hchord;
    r.y = ychord + (5 * ct.ScaleNr() % 12) * hchord;
  }

  r.x += ofs;
  r.y -= ofs;
  r.w =  wchord - 2 * ofs;
  r.h =  hchord - 2 * ofs;
}


void HBCanvas::DrawChord(const HBContext &ct)
{
  // draw surrounding box
  tRect r;
  ChordRect(r, ct);
  wxDC *dc = GetDC();
  dc->DrawRectangle(r.x, r.y, r.w, r.h);

  float w, h;
  const char *name = ct.ChordName();
  dc->GetTextExtent(name, &w, &h);
  dc->DrawText((char *)name, r.x + (r.w - w)/2, r.y + (r.h - h)/2);
}


void HBCanvas::UnDrawChord(const HBContext &ct)
{
  // draw surrounding box
  tRect r;
  ChordRect(r, ct);
  wxDC *dc = GetDC();
  dc->SetPen(wxWHITE_PEN);
  dc->DrawRectangle(r.x, r.y, r.w, r.h);
  dc->SetPen(wxBLACK_PEN);
}


void HBCanvas::OnPaint()
{
  wxDC *dc = GetDC();
  dc->Clear();
  dc->DrawText("Seq", 5, 5);

  HBContextIterator iter;
  iter.SetSequence(seq, n_seq);
  iter.SetScaleType(scale_type);
  while (iter())
  {
    const HBContext &ct = iter.Context();
    DrawChord(ct);
    if (ct.ChordNr() == 0 && ct.SeqNr() == 0)
    {
      tRect r;
      ChordRect(r, ct);
      dc->DrawText((char *)ct.ScaleName(), 5, r.y);
    }
  }
  DrawMarkers(mouse_context);

  if (!haunschild_layout)
  {
    for (int j = 0; j < 7; j++)
    {
      HBContext ct(0, j, scale_type);
      tRect r;
      ChordRect(r, ct);
      r.y -= hchord;
      float w, h;

      const char *name = ct.ChordNrName();
      dc->GetTextExtent(name, &w, &h);
      dc->DrawText((char *)name, r.x + (r.w - w)/2, r.y + (r.h - h)/2);

      const char *type = ct.ContextName();
      dc->GetTextExtent(type, &w, &h);
      dc->DrawText((char *)type, r.x + (r.w - w)/2, r.y + (r.h - h)/2 - h);
    }
  }
}

// -----------------------------------------------------------------------------
// HBSettingsForm
// -----------------------------------------------------------------------------

class HBSettingsForm : public wxForm
{
  public:
    HBSettingsForm(HBCanvas *c)
	: wxForm( USED_WXFORM_BUTTONS )
    { cnvs = c; }
    virtual void OnOk() { cnvs->OnPaint(); wxForm::OnOk(); }
    virtual void OnHelp();
  private:
    HBCanvas *cnvs;
};

void HBSettingsForm::OnHelp()
{
#ifndef VMS
  HelpInstance->LoadFile( HelpPathList.FindValidPath("jazz") );
  HelpInstance->KeywordSearch("Harmony browser");
#endif
}


void HBCanvas::SettingsDialog(wxFrame *parent)
{
  wxDialogBox *panel = new wxDialogBox(parent, "settings", FALSE );
  wxForm      *form  = new HBSettingsForm(this);

  panel->SetLabelPosition(wxHORIZONTAL);

  form->Add(wxMakeFormMessage("Transpose quarters per chord (0 = map sequence to selection)"));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Quarters", &transpose_res, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(0.0, 16.0), 0)));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormMessage("Analyze quarters per chord"));
  form->Add(wxMakeFormNewLine());
  form->Add(wxMakeFormShort("Quarters", &analyze_res, wxFORM_DEFAULT, new wxList(wxMakeConstraintRange(1.0, 16.0), 0)));
  form->Add(wxMakeFormNewLine());

  form->AssociatePanel(panel);
  panel->Fit();
  panel->Show(TRUE);
}

// -----------------------------------------------------------------------------
// HBMatchMarkers
// -----------------------------------------------------------------------------


class HBMatchMarkers : public HBMatch
{
  public:
    HBMatchMarkers(const HBContext &ct, HBCanvas *cv);
    virtual bool operator()(const HBContext &);
    const char * GetText() { return msg + 2; }	// + 2 for ", "
  private:
    HBCanvas  *cnvs;
    HBContext context;
    HBChord   chord;
    HBChord   scale;
    int       n_chord;
    int       chord_key;

    int       tritone;
    HBChord   piano;
    int       key251;

    char      msg[100];
};


HBMatchMarkers::HBMatchMarkers(const HBContext &ct, HBCanvas *cv)
  : context(ct)
{
  cnvs      = cv;
  chord     = context.Chord();
  n_chord   = chord.Count();
  chord_key = context.ChordKey();
  scale     = context.Scale();

  msg[0] = 0;
  msg[2] = 0;

  {
    // 251-move
    HBContext tmp(ct.ScaleNr(), ct.ChordNr() + 3, ct.ScaleType());
    key251 = tmp.ChordKey();
  }

  tritone = (chord_key + 6) % 12;

  if (cnvs->mark_piano)
  {
    tEventArray &buf = TrackWin->GetPianoWin()->PasteBuffer;
    for (int i = 0; i < buf.nEvents; i++)
    {
      tKeyOn *on = buf.Events[i]->IsKeyOn();
      if (on)
        piano += on->Key;
    }
  }
}


bool HBMatchMarkers::operator()(const HBContext &o_context)
{
  HBChord o_chord = o_context.Chord();
  int     o_chord_key = o_context.ChordKey();

  HBChord common = (chord & o_chord);
  int n_common = common.Count();

  msg[0] = 0;
  msg[2] = 0;

  if (cnvs->mark_piano && o_chord.Contains(piano))
    strcat(msg, ", P");

  if (cnvs->mark_4_common && o_chord == chord)
    strcat(msg, ", =4");

  if (cnvs->mark_3_common && n_common == 3)
    strcat(msg, ", =3");

  if (cnvs->mark_2_common && n_common == 2)
    strcat(msg, ", =2");

  if (cnvs->mark_1_common && n_common == 1)
    strcat(msg, ", =1");

  if (cnvs->mark_0_common && n_common == 0)
    strcat(msg, ", =0");

  if (cnvs->mark_1_semi && n_common == n_chord - 1)
  {
    HBChord delta = chord ^ o_chord;
    int key = delta.Iter(0);
    if (delta.Contains(key + 1) || delta.Contains(key - 1))
      strcat(msg, ", 1/2");
  }

  if (cnvs->mark_251 && key251 == o_chord_key)
    strcat(msg, ", 251");

  if (cnvs->mark_b_common && chord_key == o_chord_key)
    strcat(msg, ", =B");

  if (cnvs->mark_tritone && o_chord_key == tritone)
    strcat(msg, ", =T");


  return msg[2] ? 1 : 0;
}


void HBCanvas::DrawMarkers(const HBContext &ct)
{
  tRect r;
  wxDC *dc = GetDC();
  dc->SetLogicalFunction(wxXOR);
  dc->SetBrush(wxTRANSPARENT_BRUSH);
  HBMatchMarkers match(ct, this);
  HBContextIterator iter(match);
  iter.SetSequence(seq, n_seq);
  iter.SetScaleType(scale_type);
  while (iter())
  {
    ChordRect(r, iter.Context());
    r.x += 3;
    r.y += 3;
    r.w -= 6;
    r.h -= 6;
    dc->DrawRectangle(r.x, r.y, r.w, r.h);
#if 0
    // text is not removed when changing the selection
    dc->SetLogicalFunction(wxCOPY);
    dc->SetBrush(wxWHITE_BRUSH);
    dc->DrawText((char *)match.GetText(), r.x - 3, r.y - 3);
    dc->SetLogicalFunction(wxXOR);
    dc->SetBrush(wxTRANSPARENT_BRUSH);
#endif
  }

  // invert actual chord
  if (ct.ScaleType() == scale_type)
  {
    dc->SetBrush(wxBLACK_BRUSH);
    ChordRect(r, ct);
    dc->DrawRectangle(r.x, r.y, r.w, r.h);
    if (ct.SeqNr() > 0)
    {
      HBContext c(ct);
      c.SetSeqNr(0);
      ChordRect(r, c);
      dc->DrawRectangle(r.x, r.y, r.w, r.h);
    }
  }
  dc->SetLogicalFunction(wxCOPY);
  dc->SetBrush(wxWHITE_BRUSH);
}

Bool HBCanvas::Find(float x, float y, HBContext &out)
{
  HBContextIterator iter;
  iter.SetSequence(seq, n_seq);
  iter.SetScaleType(scale_type);
  while (iter())
  {
    tRect r;
    ChordRect(r, iter.Context());
    if (r.IsInside(x, y))
    {
      out = iter.Context();
      return TRUE;
    }
  }
  return FALSE;
}


void HBCanvas::ClearSeq()
{
  n_seq = 0;
  mouse_context.SetSeqNr(0);
  OnPaint();
}


void HBCanvas::OnEvent(wxMouseEvent &e)
{
  HBContext context;
  float x, y;
  e.Position(&x, &y);
  if (Find(x, y, context))
  {
    if (e.ButtonDown())
    {
      player.StartPlay(context);
      if (e.LeftDown() && e.ShiftDown() || e.MiddleDown())
      {
	if (context.SeqNr())
	{
	  // remove a chord
	  if (context.SeqNr() == n_seq)
	  {
	    // remove markers first
	    if (mouse_context.SeqNr() == n_seq)
	    {
	      DrawMarkers(mouse_context);
	      mouse_context.SetSeqNr(0);
	      DrawMarkers(mouse_context);
	    }
	    -- n_seq;
	    UnDrawChord(context);
	    context.SetSeqNr(0);
	  }
	}
	else if (n_seq < SEQMAX)
	{
	  // add a chord
	  context.SetSeqNr(n_seq + 1);
	  *seq[n_seq ++] = context;
	  DrawMarkers(mouse_context);
	  DrawChord(context);
	  DrawMarkers(mouse_context);
	}
      }
    }
    else if (e.Dragging() && player.IsPlaying() && context != player.Context())
    {
      player.StopPlay();
      player.StartPlay(context);
    }

    if ((e.LeftDown() || e.MiddleDown()) ) // && context != mouse_context)
    {
      DrawMarkers(mouse_context);
      mouse_context = context;
      //mouse_context.SetSeqNr(0);
      DrawMarkers(mouse_context);

      // paste to PianoWin buffer
      if (!mark_piano)
      {
	tEventArray &buf = TrackWin->GetPianoWin()->PasteBuffer;
	buf.Clear();
	player.Paste(buf);
	TrackWin->GetPianoWin()->Redraw();
      }

      // Show in GuitarWin
      tGuitarWin *guitar = TrackWin->GetPianoWin()->GetGuitarWin();
      if (guitar)
      {
        guitar->ShowPitch(0);	// remove actual pianowin/mouse position
        guitar->Redraw();
      }
    }
  }
  if (e.ButtonUp() && player.IsPlaying())
    player.StopPlay();
}


void HBCanvas::SetScaleType(int menu_id, tScaleType st, wxToolBar *tb)
{
  scale_type = st;
  tb->ToggleTool(MEN_MAJSCALE, FALSE);
  tb->ToggleTool(MEN_HARSCALE, FALSE);
  tb->ToggleTool(MEN_MELSCALE, FALSE);
  tb->ToggleTool(MEN_IONSCALE, FALSE);
  tb->ToggleTool(menu_id, TRUE);
  OnPaint();
}


void HBCanvas::OnMenuCommand(int id, wxToolBar *tool_bar)
{
  switch (id)
  {
    case MEN_LOAD: 
      {
	char * fname = wxFileSelector("Load Harmonies", NULL, NULL, NULL, "*.har");
	if (fname) {
	  ifstream is(fname);
	  is >> *this;
	}
      }
      break;

    case MEN_SAVE: 
      {
	char * fname = wxFileSelector("Save Harmonies", NULL, NULL, NULL, "*.har");
	if (fname) {
	  ofstream os(fname);
	  os << *this;
	}
      }
      break;

    case MEN_MAJSCALE:
      SetScaleType(id, Major, tool_bar);
      break;

    case MEN_HARSCALE:
      SetScaleType(id, Harmon, tool_bar);
      break;

    case MEN_MELSCALE:
      SetScaleType(id, Melod, tool_bar);
      break;

    case MEN_IONSCALE:
      SetScaleType(id, Ionb13, tool_bar);
      break;

    case MEN_HAUNSCH:
      haunschild_layout = !haunschild_layout;
      OnPaint();
      break;

    case MEN_ANALYZE:
      if (TrackWin->EventsSelected())
      {
	wxBeginBusyCursor();
        HBAnalyzer analyzer(seq, (int)SEQMAX);
        n_seq = analyzer.Analyze(TrackWin->Filter, analyze_res);
        OnPaint();
	wxEndBusyCursor();
      }
      break;

    case MEN_TRANSPOSE:
      if (!SeqDefined())
      {
	wxMessageBox("define a chord sequence first", "error");
	return;
      }
      if (TrackWin->EventsSelected())
      {
	wxBeginBusyCursor();
        HBAnalyzer analyzer(seq, n_seq);
        analyzer.Transpose(TrackWin->Filter, transpose_res);
	wxEndBusyCursor();
      }
      break;

    case MEN_SETTINGS:
      SettingsDialog(parent);
      break;

    default:
      SetMarker(id, tool_bar);
      break;

  }
}


HBAnalyzer * HBCanvas::getAnalyzer()
{
  if (n_seq > 0 && TrackWin->SnapSel->Selected)
  {
    HBAnalyzer *analyzer = new HBAnalyzer(seq, n_seq);
    analyzer->Init(TrackWin->Filter, transpose_res);
    return analyzer;
  }
  return 0;
}

// ---------------------------------------------------------
// HBContextDlg
// ---------------------------------------------------------

struct tNamedChord
{
  const char *name;
  long bits;
};

const int n_chord_names = 12;
const int n_scale_names = 46;


tNamedChord chord_names[n_chord_names] = {
  { " j7",	0x891},
  { " m7",	0x489},
  { " 7",	0x491},
  { " m75-",	0x449},
  { " mj7",	0x889},
  { " j75+",	0x911},
  { " 0",	0x249},
  { " sus4",	0xa1},
  { " 7sus4",	0x4a1},
  { " j7sus4",	0x8a1},
  { " alt (79+13-)",	0x519},
  { " 75-",	0x451},
};

tNamedChord scale_names[n_scale_names] = {
  { "***** major scales *****",	0x0},
  { "maj penta",	0x295},
  { "maj I   (ionic)",	0xab5},
  { "maj IV  (lydic)",	0xad5},
  { "har III (ion #5)",	0xb35},
  { "har VI  (lyd #9)",	0xad9},
  { "mel III (lyd #5)",	0xb55},
  { "augmented",	0x333},
  { "hj I    (ionic b13)",	0x9b5},
  { "***** minor scales *****",	0x0},
  { "minor penta",	0x4a9},
  { "maj VI   (aeolic)",	0x5ad},
  { "maj II   (doric)",	0x6ad},
  { "mel II   (doric b9)",	0x6ab},
  { "maj III  (phrygic)",	0x5ab},
  { "japan penta",	0x4a3},
  { "har IV   (dor #11)",	0x6cd},
  { "har I    (harmonic minor)",	0x9ad},
  { "mel I    (melodic minor)",	0xaad},
  { "gipsy",	0x9cd},
  { "hj IV    (melodic #11)",	0xacd},
  { "***** dominant scales *****",	0x0},
  { "major penta",	0x295},
  { "ind. penta",	0x4b1},
  { "maj V (mixolyd)",	0x6b5},
  { "har V (har dominant)",	0x5b3},
  { "mel IV (mixo #11)",	0x6d5},
  { "mixo #11b9",	0x6d3},
  { "mel V (mixo b13)",	0x5b5},
  { "hj V  (mixo b9)",	0x6b3},
  { "full",	0x555},
  { "hj III (har alt)",	0x59b},
  { "mel VII (alt)",	0x55b},
  { "half/full",	0x6db},
  { "***** semi dimin *****",	0x0},
  { "maj VII (locr)",	0x56b},
  { "mel VI  (locr 9)",	0x56d},
  { "har II  (locr 13)",	0x66b},
  { "hj II   (doric b5)",	0x66d},
  { "***** dimin *****",	0x0},
  { "har VII (har dim)",	0x35b},
  { "full/half",	0xa6d},
  { "hj VII  (locr dim)",	0x36b},
  { "***** blues scales *****",	0x0},
  { "minor penta b5",	0x4e9},
  { "blues scale",	0x4f9},
};


class HBContextDlg : public wxDialogBox
{
  public:
    HBContextDlg(HBCanvas *c, wxFrame *parent, HBContext *pcontext);
    ~HBContextDlg();
    static void OkButton(wxButton &but, wxCommandEvent& event);
    static void CancelButton(wxButton &but, wxCommandEvent& event);
    static void PlayButton(wxButton &but, wxCommandEvent& event);
    static void HelpButton(wxButton &but, wxCommandEvent& event);
    static void ChordCheck(wxItem &item, wxCommandEvent& event);
    static void ScaleCheck(wxItem &item, wxCommandEvent& event);
    static void ChordList(wxItem &item, wxCommandEvent& event);
    static void ScaleList(wxItem &item, wxCommandEvent& event);
    void OnOkButton();
    void OnCancelButton();
    void OnPlayButton();
    void OnChordCheck();
    void OnScaleCheck();
    void OnChordList();
    void OnScaleList();

    void OnHelp();

    void ShowValues();

  private:

    HBCanvas   *cnvs;
    wxCheckBox *chord_chk[12];
    wxCheckBox *scale_chk[12];
    wxListBox  *chord_lst;
    wxListBox  *scale_lst;
    wxMessage  *chord_msg;

    wxButton   *ok_but;
    wxButton   *cancel_but;
    wxButton   *play_but;
    wxButton   *help_but;

    HBChord    chord;
    HBChord    scale;
    int        chord_key;
    int        scale_key;
    int	       ChordKey(int i = 0) const { return (chord_key + i) % 12; }
    int	       ScaleKey(int i = 0) const { return (scale_key + i) % 12; }
    HBContext  *pcontext;

    HBPlayer   player;
    void       RestartPlayer();
};


HBContextDlg::HBContextDlg(HBCanvas *c, wxFrame *parent, HBContext *pct)
  : wxDialogBox(parent, "Edit chord/scale", FALSE )
{
  int i;

  cnvs = c;
  pcontext = pct;
  chord = pcontext->Chord();
  scale = pcontext->Scale();
  chord_key = pcontext->ChordKey();
  scale_key = pcontext->ScaleKey();

  // buttons
  ok_but = new wxButton(this, (wxFunction)OkButton, "Ok") ;
  cancel_but = new wxButton(this, (wxFunction)CancelButton, "Cancel") ;
  play_but = new wxButton(this, (wxFunction)PlayButton, "Play") ;
  help_but = new wxButton(this, (wxFunction)HelpButton, "Help" );
  NewLine();

  // top messages
  (void)new wxMessage(this, "Chord: ");
  chord_msg = new wxMessage(this, "Am75-13-sus4");
  NewLine();

  // chord/scale keys
#ifdef wx_xview
  int w = 45;
  int h = 30;
  int y = 70;
#endif

#ifdef wx_motif
  int w = 45;
  int h = 40;
  int y = 80;
#endif

#ifdef wx_xt
  int w = 45;
  int h = 40;
  int y = 80;
#endif

#ifdef wx_msw
  int w = 45;
  int h = 40;
  int y = 80;
#endif

  const char *notename[12] = { "1", 0, "9", 0, "3", "11", 0, "5", 0, "13", 0, "7" };
  for (i = 0; i < 12; i++)
  {
    int x = w * i + 10;
    chord_chk[i] = new wxCheckBox(this, (wxFunction)ChordCheck, " ", x, y+1*h);
    scale_chk[i] = new wxCheckBox(this, (wxFunction)ScaleCheck, " ", x, y+2*h);
    if (notename[i])
      (void) new wxMessage(this, (char *)notename[i], x, y+3*h);
    (void) new wxMessage(this, (char *)HBChord::ScaleName(i + chord_key), x, y+0*h);
  }
  y += 4*h;

  // list boxes                                                               x  y    w    h
  SetLabelPosition(wxVERTICAL);
  char **cnames = new char * [n_chord_names];
  for (i = 0; i < n_chord_names; i++)
    cnames[i] = (char *)chord_names[i].name;
  chord_lst = new wxListBox(this, (wxFunction)ChordList, "Chords", wxSINGLE, 10, y, 100, 200, n_chord_names, cnames, wxNEEDED_SB);
  delete [] cnames;

  char **snames = new char * [n_scale_names];
  for (i = 0; i < n_scale_names; i++)
    snames[i] = (char *)scale_names[i].name;
  scale_lst = new wxListBox(this, (wxFunction)ScaleList, "Scales", wxSINGLE, 200, y, 300, 200, n_scale_names, snames, wxNEEDED_SB);
  delete [] snames;

  // thats it
  Fit();
  Show(TRUE);
  ShowValues();
}

HBContextDlg::~HBContextDlg()
{
  if (player.IsPlaying())
    player.StopPlay();
}


void HBContextDlg::ShowValues()
{
  // show single notes
  int i;
  char buf[30];
  chord.Name(buf, ChordKey(0));
  chord_msg->SetLabel(buf);
  for (i = 0; i < 12; i++)
  {
    chord_chk[i]->SetValue(0 != chord.Contains(ChordKey(i)));
    scale_chk[i]->SetValue(0 != scale.Contains(ScaleKey(i)));
  }

  // update chord list if necessary
  HBChord c = chord;
  c.Rotate(-chord_key);
  i = chord_lst->GetSelection();
  if (i < 0 || c.Keys() != chord_names[i].bits)
  {
    for (i = 0; i < n_chord_names; i++)
    {
      if (chord_names[i].bits == c.Keys())
      {
	chord_lst->SetSelection(i);
	break;
      }
    }
  }

  // update scale list
  HBChord s = scale;
  s.Rotate(-chord_key);
  i = chord_lst->GetSelection();
  if (i < 0 || s.Keys() != scale_names[i].bits)
  {
    for (i = 0; i < n_scale_names; i++)
    {
      if (scale_names[i].bits == s.Keys())
      {
	scale_lst->SetSelection(i);
	break;
      }
    }
  }

}

void HBContextDlg::OnOkButton()
{
  chord.Clear();
  scale.Clear();
  for (int i = 0; i < 12; i++)
  {
    if (chord_chk[i]->GetValue())
      chord += ChordKey(i);
    if (scale_chk[i]->GetValue())
      scale += ScaleKey(i);
  }
  *pcontext->PChord() = chord;
  *pcontext->PScale() = scale;
  cnvs->OnPaint();
  delete this;
}

void HBContextDlg::OnCancelButton()
{
  delete this;
}

void HBContextDlg::OnPlayButton()
{
  if (player.IsPlaying())
  {
    play_but->SetLabel("play");
    player.StopPlay();
  }
  else
  {
    HBContext ct(*pcontext);
    *ct.PChord() = chord;
    *ct.PScale() = scale;
    player.StartPlay(ct);
    play_but->SetLabel("stop");
  }
}

void HBContextDlg::OnHelp()
{
#ifndef VMS
        HelpInstance->LoadFile( HelpPathList.FindValidPath("jazz") );
        HelpInstance->KeywordSearch("Edit chord");
#endif
}


void HBContextDlg::RestartPlayer()
{
  if (player.IsPlaying())
  {
    player.StopPlay();
    HBContext ct(*pcontext);
    *ct.PChord() = chord;
    *ct.PScale() = scale;
    player.StartPlay(ct);
  }
}

void HBContextDlg::OnChordCheck()
{
  char buf[30];
  chord.Clear();
  for (int i = 0; i < 12; i++)
  {
    if (chord_chk[i]->GetValue())
      chord += ChordKey(i);
  }
  chord.Name(buf, ChordKey());
  chord_msg->SetLabel(buf);
  RestartPlayer();
}


void HBContextDlg::OnScaleCheck()
{
  scale.Clear();
  for (int i = 0; i < 12; i++)
  {
    if (scale_chk[i]->GetValue())
      scale += ScaleKey(i);
  }
  RestartPlayer();
}

void HBContextDlg::OnChordList()
{
  int i = chord_lst->GetSelection();
  if (i >= 0)
  {
    HBChord c(chord_names[i].bits);
    c.Rotate(ChordKey());
    chord = c;
    ShowValues();
    RestartPlayer();
  }
}


void HBContextDlg::OnScaleList()
{
  int i = scale_lst->GetSelection();
  if (i >= 0)
  {
    HBChord s(scale_names[i].bits);
    s.Rotate(ScaleKey());
    scale = s;
    ShowValues();
    RestartPlayer();
  }
}


void HBContextDlg::OkButton(wxButton &but, wxCommandEvent& event)
{
  ((HBContextDlg *)but.GetParent())->OnOkButton();
}

void HBContextDlg::HelpButton(wxButton &but, wxCommandEvent& event)
{
  ((HBContextDlg *)but.GetParent())->OnHelp();
}

void HBContextDlg::CancelButton(wxButton &but, wxCommandEvent& event)
{
  ((HBContextDlg *)but.GetParent())->OnCancelButton();
}

void HBContextDlg::PlayButton(wxButton &but, wxCommandEvent& event)
{
  ((HBContextDlg *)but.GetParent())->OnPlayButton();
}

void HBContextDlg::ChordCheck(wxItem &itm, wxCommandEvent& event)
{
  ((HBContextDlg *)itm.GetParent())->OnChordCheck();
}

void HBContextDlg::ScaleCheck(wxItem &itm, wxCommandEvent& event)
{
  ((HBContextDlg *)itm.GetParent())->OnScaleCheck();
}

void HBContextDlg::ChordList(wxItem &itm, wxCommandEvent& event)
{
  ((HBContextDlg *)itm.GetParent())->OnChordList();
}

void HBContextDlg::ScaleList(wxItem &itm, wxCommandEvent& event)
{
  ((HBContextDlg *)itm.GetParent())->OnScaleList();
}


// ------------------- HBFrame ---------------------------

HBFrame::~HBFrame()
{
  delete tool_bar;
  delete cnvs;
}

int HBFrame::SeqDefined()
{
  return cnvs->SeqDefined();
}


int HBFrame::SeqSelected()
{
  if (cnvs->n_seq == 0 || cnvs->mouse_context.SeqNr() == 0)
  {
    wxMessageBox("select a chord from sequence first", "error");
    return 0;
  }
  return 1;
}


int HBFrame::GetChordKeys(int *out, int step, int n_steps)
{
  return cnvs->GetChordKeys(out, step, n_steps);
}

int HBFrame::GetSelectedChord(int *out)
{
  return cnvs->GetSelectedChord(out);
}

int HBFrame::GetSelectedScale(int *out)
{
  return cnvs->GetSelectedScale(out);
}

int HBFrame::GetBassKeys(int *out, int step, int n_steps)
{
  return cnvs->GetBassKeys(out, step, n_steps);
}


void HBFrame::OnMenuCommand(int id)
{
  switch(id)
  {
    case MEN_HELP:
#ifndef VMS
	HelpInstance->LoadFile( HelpPathList.FindValidPath("jazz") );
        HelpInstance->KeywordSearch("Harmony browser");
#endif
	break;

    case MEN_MOUSE:
	wxbMessageBox("left: select chord\n"
	              "  +shift: put chord into sequence\n"
	              "middle: same as left+shift\n"
	              "right: play chord\n", "Mousebuttons", wxOK);
	break;

    case MEN_MIDI:
      cnvs->player.SettingsDialog(this);
      break;

    case MEN_CLEARSEQ:
      cnvs->ClearSeq();
      break;

    case MEN_EDIT:
      {
	if (!SeqSelected())
	  return;
      }
      (void) new HBContextDlg(cnvs, this, cnvs->seq[cnvs->mouse_context.SeqNr() - 1]);
      break;

    case MEN_CLOSE:
      ((HBFrame *)the_harmony_browser)->GetPosition( &HarmonyGeo[0], &HarmonyGeo[1] );
      the_harmony_browser = 0;
      #if defined(wx_motif) && defined(wxVERSION_NUMBER) && (wxVERSION_NUMBER > 1603)
      wxPostDelete(this);	// Motif doesn't like delete this in menu's???
      #else
      delete this;
      #endif
      break;

    default:
      cnvs->OnMenuCommand(id, tool_bar);
      break;
  }
}


HBFrame::HBFrame(wxFrame *parent)
  : wxFrame(0, "Harmony Browser", HarmonyGeo[0], HarmonyGeo[1], 660, 530)
{
  int w, h;
  cnvs = 0;

  tool_bar = new tToolBar(this, tdefs, 13);

  wxMenuBar *menu_bar = new wxMenuBar;
  wxMenu    *menu = new wxMenu;
  menu->Append(MEN_EDIT,	"&Edit chord");
  menu->Append(MEN_SETTINGS,	"Se&ttings");
  menu->Append(MEN_MIDI,	"&Midi");
  menu->Append(MEN_HELP,	"&Help");
  menu->Append(MEN_MOUSE,	"Help &mouse");
  menu->Append(MEN_LOAD,	"&Load");
  menu->Append(MEN_SAVE,	"&Save");
  menu->Append(MEN_CLOSE,	"Cl&ose");
  menu_bar->Append(menu,	"&Menu");
  SetMenuBar(menu_bar);

  GetClientSize(&w, &h);
  cnvs =  new HBCanvas(this, 0, 0, w, h);
  tool_bar->ToggleTool(MEN_MAJSCALE, TRUE);
}

HBAnalyzer * HBFrame::getAnalyzer()
{
  return cnvs->getAnalyzer();
}

void HBFrame::OnSize(int w, int h)
{
  int cw, ch;
  GetClientSize(&cw, &ch);
  float tw = 0.0;
  float th = 0.0;
  if (tool_bar)
    tool_bar->GetMaxSize(&tw, &th);
  if (cnvs)
    cnvs->SetSize(0, (int)th, cw, ch - (int)th);
  if (tool_bar)
    tool_bar->SetSize(0, 0, (int)cw, (int)th);
}

void harmony_browser(tEventWin *parent)
{
  if (!the_harmony_browser)
    the_harmony_browser = new HBFrame((wxFrame *)parent);
  ((HBFrame *)the_harmony_browser)->Show(TRUE);
}

