
/*
**  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1994-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 "pianowin.h"
#include "song.h"
#include "mstdfile.h"
#include "filter.h"
#include "dialogs.h"
#include "command.h"
#include "player.h"
#include "wx_timer.h"
#include "jazz.h"
#include "ctrledit.h"
#include "guitar.h"
#include "harmony.h"
#include "harmonyp.h"
#include "hbanalyz.h"
#include "toolbar.h"

#ifdef wx_xt
#define wxbMessageBox wxMessageBox
#endif

// ************************************************************************
// Menubar
// ************************************************************************

#define MEN_SETTINGS	5
#define MEN_FILTER	6
#define MEN_SNAP	7
#define MEN_METERCH	8
#define MEN_HELP_MOUSE	9

#define MEN_COPY	10
#define MEN_SHIFT	11
#define MEN_QUANTIZE	12
#define MEN_UNDO	13
#define MEN_SETCHAN	14
#define MEN_TRANSP	15
#define MEN_VELOC	16
#define MEN_CUT		17
#define MEN_LERI	18
#define MEN_UPDN	19
#define MEN_LENGTH	20
#define MEN_ERASE	21
#define MEN_VISIBLE     22

#define MEN_CTRL_EDIT   23
#define MEN_CTRL_PITCH  24
#define MEN_CTRL_CONTR  25
#define MEN_CTRL_VELOC  26
#define MEN_CTRL_NONE   27
#define MEN_CTRL_MODUL  28

#define MEN_GUITAR	29
#define MEN_HELP_PWIN   30
#define MEN_CLEANUP     31

#define MEN_SNAP_8      32
#define MEN_SNAP_8D     33
#define MEN_SNAP_16     34
#define MEN_SNAP_16D    35
#define MEN_RESET       36
#define MEN_VIS_ALL_TRK 37
#define MEN_SEARCHREP   38
#define MEN_SHIFTL      39
#define MEN_SHIFTR      40

#define MEN_CLOSE	41

// Toolbar Icons

#ifdef wx_x
#include "../bitmaps/note8.xpm"
#include "../bitmaps/note83.xpm"
#include "../bitmaps/note16.xpm"
#include "../bitmaps/note163.xpm"
#include "../bitmaps/cut.xpm"
#include "../bitmaps/delete.xpm"
#include "../bitmaps/quantize.xpm"
#include "../bitmaps/evnts.xpm"
#include "../bitmaps/undo.xpm"
#include "../bitmaps/panic.xpm"
#include "../bitmaps/help.xpm"
#include "../bitmaps/shiftl.xpm"
#include "../bitmaps/shiftr.xpm"
static tToolDef tdefs[] = {
  { MEN_SNAP_8,  	TRUE,  6, tb_note8 },
  { MEN_SNAP_8D,  	TRUE,  6, tb_note83 },
  { MEN_SNAP_16,  	TRUE,  6, tb_note16 },
  { MEN_SNAP_16D,  	TRUE,  12, tb_note163 },

  { MEN_CUT,   		FALSE, 6, tb_cut  },
  { MEN_ERASE, 		FALSE, 6, tb_delete  },
  { MEN_QUANTIZE, 	FALSE, 6, tb_quantize  },
  { MEN_SHIFTL, 	FALSE, 6, tb_shiftl  },
  { MEN_SHIFTR, 	FALSE, 6, tb_shiftr  },
  { MEN_VIS_ALL_TRK, 	TRUE,  12, tb_evnts  },

  { MEN_UNDO,		FALSE, 6, tb_undo  },
  { MEN_RESET, 		FALSE, 6, tb_panic },
  { MEN_HELP_PWIN, 	FALSE, 6, tb_help }
};

#else

static tToolDef tdefs[] = {
  { MEN_SNAP_8,  	TRUE,  2, "tb_note8" },
  { MEN_SNAP_8D,  	TRUE,  2, "tb_note83" },
  { MEN_SNAP_16,  	TRUE,  2, "tb_note16" },
  { MEN_SNAP_16D,  	TRUE,  8, "tb_note163" },

  { MEN_CUT,   		FALSE, 2, "tb_cut"  },
  { MEN_ERASE, 		FALSE, 2, "tb_delete"  },
  { MEN_QUANTIZE, 	FALSE, 2, "tb_quantize"  },
  { MEN_SHIFTL, 	FALSE, 2, "tb_shiftl"  },
  { MEN_SHIFTR, 	FALSE, 2, "tb_shiftr"  },
  { MEN_VIS_ALL_TRK, 	TRUE,  8, "tb_evnts"  },

  { MEN_UNDO,		FALSE, 2, "tb_undo"  },
  { MEN_RESET, 		FALSE, 2, "tb_panic" },
  { MEN_HELP_PWIN, 	FALSE, 2, "tb_help" }
};

#endif


// positions for controller editor
#define CtrlY(h)	(3*(h)/4)
#define CtrlH(h)	((h)/4)

// mouse actions mapping

#define MA_PLAY		1
#define MA_CYCLE	2

#define MA_SELECT	3
#define MA_CONTSEL	4

#define MA_CUTPASTE	5
#define MA_LENGTH	6
#define MA_DIALOG	7
#define MA_LISTEN	8
#define MA_COPY		9
#define MA_VELOCITY    10


const int play_actions[12] = {
  // left	middle		right
  MA_PLAY,	MA_CYCLE,	0,		// plain
  MA_CYCLE,	0,		0,		// shift
  0,		0,		0,		// ctrl
  0,		0,		0		// shift+ctrl
};

const int evnt_2_actions[12] = {
  // left	middle		right
  MA_SELECT,	0,		MA_LENGTH,	// plain
  MA_CONTSEL,	0,		MA_DIALOG,	// shift
  MA_CUTPASTE,	0,		MA_LISTEN,	// ctrl
  0,		0,		0		// shift+ctrl
};

const int evnt_3_actions[12] = {
  // left	middle		right
  MA_SELECT,	MA_CUTPASTE,	MA_LENGTH,	// plain
  MA_CONTSEL,	MA_COPY,	MA_DIALOG,	// shift
  MA_VELOCITY,	MA_LISTEN,	MA_VELOCITY,	// ctrl
  0,		0,		0		// shift+ctrl
};

const char mouse_2_help[] =
	"topline:\n"
	"  left: start/stop play\n"
	"    +shift: start/stop cycle play\n"
	"\n"
	"events:\n"
        "  left: select events\n"
        "    +shift: continue selection\n"
        "    +ctrl: cut/paste event\n"
        "  right: note length / change track\n"
        "    +shift: note dialog\n"
        "    +ctrl: play pitch\n";

const char mouse_3_help[] =
	"topline:\n"
	"  left: start/stop play\n"
	"    +shift: start/stop cycle play\n"
	"  middle: same as left+shift\n"
	"\n"
	"events:\n"
        "  left: select events\n"
        "    +shift: continue selection\n"
        "    +ctrl: increase velocity\n"
        "  middle: cut/paste event\n"
        "    +shift: copy event\n"
        "    +ctrl: play pitch\n"
        "  right: note length / change track\n"
        "    +shift: note dialog\n"
        "    +ctrl: decrease velocity\n";

// -------------------------------------------------------------------------
// MousePiano
// -------------------------------------------------------------------------

class tListen : public wxTimer
{
  public:
    int Active;
    int Pitch, Channel;
    tListen::tListen() { Active = 0; }
    void KeyOn(int Pitch, int Channel, int Veloc = 64, int Millisec = 100);
    void Notify();
};

void tListen::KeyOn(int pitch, int channel, int veloc, int milli)
{
  if (!Active)
  {
    Pitch = pitch;
    Channel = channel;
    tKeyOn k(0, Channel, pitch, veloc);
    Midi->OutNow(&k);
    Active = 1;
    Start(milli);
  }
}

void tListen::Notify()
{
  Stop();
  tKeyOff k(0, Channel, Pitch);
  Midi->OutNow(&k);
  Active = 0;
}

static tListen Listen;

// **************************************************************************
// Pianowin
// **************************************************************************

tPianoWin::tPianoWin(wxFrame *frame, char *title, tSong *song, int x, int y, int width, int height)
  : tEventWin(frame, title, song, x, y, width, height ),
    MousePlay(play_actions),
    MouseEvnt(MouseButtons == 2 ? evnt_2_actions : evnt_3_actions)
{
  int i;

  tool_bar = new tToolBar(this, tdefs, 13);
  tool_bar->ToggleTool(MEN_SNAP_16, TRUE);

  ClocksPerPixel = 4;
  FontSize = 7;
  TrackNr = 0;
  Track = Song->GetTrack(TrackNr);
  SnapDenomiator = 16;
  nSnaps = 0;

  for (i = 0; i < MaxTracks; i++)
    FromLines[i] = 64;

  DrumFont = 0;

  VisibleKeyOn = 1;
  VisiblePitch = 0;
  VisibleController = 0;
  VisibleProgram = 0;
  VisibleTempo = 0;
  VisibleDrumNames = 1;
  VisibleAllTracks = 0;
  VisibleHBChord = 0;

  MouseLine = -1;

  CtrlEdit  = 0;
  GuitarWin = 0;

}


tPianoWin::~tPianoWin()
{
  delete CtrlEdit;
  delete GuitarWin;
}


void tPianoWin::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 (Canvas && CtrlEdit)
  {
    Canvas->SetSize(0, (int)th, cw, CtrlY(ch) - (int)th);
    CtrlEdit->SetSize(wPiano, 0, CtrlY(ch), cw, CtrlH(ch));
  }
  else if (Canvas)
    Canvas->SetSize(0, (int)th, cw, ch - (int)th);
  if (tool_bar)
    tool_bar->SetSize(0, 0, (int)cw, (int)th);
}


void tPianoWin::Setup()
{
  float x, y;

  tEventWin::Setup();

  dc->SetFont(FixedFont);
  dc->GetTextExtent("H", &x, &y);
  hTop = hFixedFont + 2 * LittleBit;

  dc->SetFont(Font);
  dc->GetTextExtent("H", &x, &y);
  LittleBit = (int)(x/2);

  delete DrumFont;
  DrumFont = new wxFont(FontSize+3, wxSWISS, wxNORMAL, wxNORMAL);
  dc->SetFont(DrumFont);
  dc->GetTextExtent("Low Conga mid 2", &x, &y);
  wPiano = (int)x + LittleBit;

  wLeft = wPiano;
}


void tPianoWin::NewPosition(int track, long clock)
{
  FromLines[TrackNr] = FromLine;

  // change track
  if (track >= 0)
  {
    TrackNr = track;
    Track = Song->GetTrack(TrackNr);
    SetTitle(Track->GetName());
  }

  // change position
  if (clock >= 0)
  {
    long x = Clock2x(clock);
    Canvas->SetScrollPosition(x - wLeft, Line2y(FromLines[TrackNr]));
  }

  // OnPaint() redraws only if clock has changed
  if (CtrlEdit && track >= 0)
    CtrlEdit->ReInit(Track, FromClock, ClocksPerPixel);

  Redraw();
}




void tPianoWin::CreateMenu()
{
  wxMenuBar *menu_bar = NULL;

  wxMenu *win_menu = new wxMenu;
  win_menu->Append(MEN_CLOSE, "&Close");

  wxMenu *edit_menu = new wxMenu;
  edit_menu->Append(MEN_ERASE, "&Delete");
  edit_menu->Append(MEN_COPY, "&Copy");
  edit_menu->Append(MEN_CUT, "&Cut");
  edit_menu->Append(MEN_SHIFT, "&Shift ...");
  edit_menu->Append(MEN_QUANTIZE, "&Quantize ...");
  edit_menu->Append(MEN_SETCHAN, "&Set MIDI Channel ...");
  edit_menu->Append(MEN_TRANSP, "&Transpose ...");
  edit_menu->Append(MEN_VELOC, "&Velocity ...");
  edit_menu->Append(MEN_LENGTH, "&Length ...");
  edit_menu->Append(MEN_LERI, "&Left <-> Right");
  edit_menu->Append(MEN_UPDN, "&Up <-> Down");
  edit_menu->Append(MEN_CLEANUP, "&Cleanup ...");
  edit_menu->Append(MEN_SEARCHREP, "&Search Replace ...");

  wxMenu *setting_menu = new wxMenu;
  setting_menu->Append(MEN_FILTER,    "&Filter ...");
  setting_menu->Append(MEN_SETTINGS,  "&Window ...");
  setting_menu->Append(MEN_VISIBLE,   "&Events...");
  setting_menu->Append(MEN_SNAP,      "&Snap ...");
  setting_menu->Append(MEN_METERCH,   "&Meterchange ...");

  wxMenu *misc_menu = new wxMenu;
  misc_menu->Append(MEN_UNDO,   "&Undo");
  misc_menu->Append(MEN_CTRL_PITCH,	"Edit &Pitch");
  misc_menu->Append(MEN_CTRL_VELOC,	"Edit &Velocity");
  misc_menu->Append(MEN_CTRL_MODUL,	"Edit &Modulation");
  misc_menu->Append(MEN_CTRL_CONTR,	"Edit &Controller ...");
  misc_menu->Append(MEN_CTRL_NONE,	"Edit &None");
  misc_menu->Append(MEN_GUITAR,		"&Guitar board");

  wxMenu *help_menu = new wxMenu;
  help_menu->Append(MEN_HELP_PWIN, "&Pianowin");
  help_menu->Append(MEN_HELP_MOUSE, "&Mouse");

  menu_bar = new wxMenuBar;
  menu_bar->Append(win_menu,    "&Window");
  menu_bar->Append(edit_menu,    "&Edit");
  menu_bar->Append(setting_menu, "&Settings");
  menu_bar->Append(misc_menu,    "&Misc");
  menu_bar->Append(help_menu,    "&Help");

  SetMenuBar(menu_bar);
}



void tPianoWin::OnMenuCommand(int id)
{
  int cw, ch;
  GetClientSize(&cw, &ch);

  switch (id)
  {
    case MEN_CLOSE: Show(FALSE); break;
    case MEN_SNAP_8: PasteBuffer.Clear(); SetSnapDenom(8); break;
    case MEN_SNAP_8D: PasteBuffer.Clear(); SetSnapDenom(12); break;
    case MEN_SNAP_16: PasteBuffer.Clear(); SetSnapDenom(16); break;
    case MEN_SNAP_16D: PasteBuffer.Clear(); SetSnapDenom(24); break;

    case MEN_VIS_ALL_TRK:
      VisibleAllTracks = !VisibleAllTracks;
      //VisibleAllTracks = tool_bar->GetToolState(MEN_VIS_ALL_TRK);
      Redraw();
      break;
    case MEN_RESET: Midi->AllNotesOff(1); break;

    case MEN_HELP_MOUSE:
      if (MouseButtons == 2)
	wxbMessageBox((char *)mouse_2_help, "Help", wxOK);
      else
	wxbMessageBox((char *)mouse_3_help, "Help", wxOK);
      break;

    case MEN_HELP_PWIN:
#ifndef VMS
      HelpInstance->LoadFile( HelpPathList.FindValidPath("jazz") );
      HelpInstance->KeywordSearch("Piano Window");
#endif
      break;

    case MEN_ERASE:
      if (EventsSelected())
      {
        tCmdErase cmd(Filter);
	cmd.Execute(1);	// with UNDO
	Redraw();
      }
      break;

    case MEN_CUT:
    case MEN_COPY:
      if (EventsSelected())
      {
	PasteBuffer.Clear();
	tCmdCopyToBuffer cmd(Filter, &PasteBuffer);
	cmd.Execute(0);	// no UNDO
	if (id == MEN_CUT)
	{
	  tCmdErase cmd(Filter);
	  cmd.Execute(1);	// with UNDO
	  Redraw();
	}
	if (GuitarWin)
	  GuitarWin->Redraw();
      }
      break;

    case MEN_SHIFT:
      MenShift(SnapClocks());
      break;

    case MEN_SHIFTL:
    case MEN_SHIFTR:
      if (EventsSelected())
      {
        long steps = ((id == MEN_SHIFTL) ? -SnapClocks() : SnapClocks());
        tCmdShift cmd(Filter, steps);
	cmd.Execute();
	Redraw();
      }
      break;

    case MEN_LERI:
      if (EventsSelected())
      {
	tCmdExchLeftRight cmd(Filter);
	cmd.Execute(1);
	Redraw();
      }
      break;

    case MEN_UPDN:
      if (EventsSelected())
      {
	tCmdExchUpDown cmd(Filter);
	cmd.Execute(1);
	Redraw();
      }
      break;

    case MEN_QUANTIZE:
      if (EventsSelected())
      {
	tCmdQuantize cmd(Filter, SnapClocks());
	cmd.Execute(1);
	Redraw();
      }
      break;

    case MEN_CLEANUP: 	MenCleanup(); break;
    case MEN_SEARCHREP: MenSearchReplace(); break;
    case MEN_SETCHAN: 	MenSetChannel(); break;
    case MEN_TRANSP: 	MenTranspose(); break;
    case MEN_VELOC: 	MenVelocity(); break;
    case MEN_LENGTH: 	MenLength(); break;
    case MEN_UNDO:      Song->Undo(); Redraw(); break;
    case MEN_VISIBLE:   VisibleDialog(); break;
    case MEN_FILTER:    Filter->Dialog(0); break;
    case MEN_SETTINGS:  SettingsDialog(1); break;
    case MEN_SNAP:      SnapDlg(); break;
    case MEN_METERCH:	MenMeterChange(); break;

    case MEN_CTRL_PITCH:
      delete CtrlEdit;
      CtrlEdit = new tPitchEdit(this, "Pitch", wPiano, xx, CtrlY(ch), ww, CtrlH(ch));
      CtrlEdit->ReInit(Track, FromClock, ClocksPerPixel);
      OnSize(0,0);
      break;

    case MEN_CTRL_MODUL:
      delete CtrlEdit;
      CtrlEdit = new tCtrlEdit(1, this, "Modulation", wPiano, xx, CtrlY(ch), ww, CtrlH(ch));
      CtrlEdit->ReInit(Track, FromClock, ClocksPerPixel);
      OnSize(0,0);
      break;

    case MEN_CTRL_CONTR:
      {
	int i = SelectControllerDlg();
	if (i > 0)
	{
	  delete CtrlEdit;
	  CtrlEdit = new tCtrlEdit(i-1, this, ControlNames[i].Name, wPiano, xx, CtrlY(ch), ww, CtrlH(ch));
	  CtrlEdit->ReInit(Track, FromClock, ClocksPerPixel);
	  OnSize(0,0);
	}
      }
      break;

    case MEN_CTRL_VELOC:
      delete CtrlEdit;
      CtrlEdit = new tVelocEdit(this, "Velocity", wPiano, xx, CtrlY(ch), ww, CtrlH(ch));
      CtrlEdit->ReInit(Track, FromClock, ClocksPerPixel);
      OnSize(0,0);
      break;

    case MEN_CTRL_NONE:
      delete CtrlEdit;
      CtrlEdit = 0;
      OnSize(0,0);
      break;

    case MEN_GUITAR:
      if (!GuitarWin)
        GuitarWin = new tGuitarWin(this);
      GuitarWin->Show(TRUE);
      break;
  }
}


// ********************************************************************
// Visible
// ********************************************************************

int tPianoWin::IsVisible(tEvent *e)
{
  switch (e->Stat)
  {
    case StatKeyOn: return VisibleKeyOn;
    case StatPitch: return VisiblePitch;
    case StatControl: return VisibleController;
    case StatProgram: return VisibleProgram;
    case StatSetTempo: return VisibleTempo;
  }
  return 0;
}


int tPianoWin::IsVisible(tTrack *t)
{
  if (!VisibleAllTracks)
    return t == Track;

  return (Track->Channel == DrumChannel) == (t->Channel == DrumChannel);
}


class tVisibleDlg : public wxForm
{
  tPianoWin *win;
  public:

    tVisibleDlg(tPianoWin *p) : wxForm( USED_WXFORM_BUTTONS ), win(p) {}
    void EditForm(wxPanel *panel);
    virtual void OnOk();
    virtual void OnHelp();
};

void tVisibleDlg::OnOk()
{
  win->SetVisibleAllTracks(win->VisibleAllTracks);
  // win->Redraw();
  wxForm::OnOk();
}

void tVisibleDlg::OnHelp()
{
#ifndef VMS
        HelpInstance->LoadFile( HelpPathList.FindValidPath("jazz") );
        HelpInstance->KeywordSearch("Piano window");
#endif
}


void tVisibleDlg::EditForm(wxPanel *panel)
{
  Add(wxMakeFormMessage("Select Events to be shown"));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormBool("NoteOn", &win->VisibleKeyOn));
  //Add(wxMakeFormBool("Pitch", &win->VisiblePitch));
  Add(wxMakeFormBool("Controller", &win->VisibleController));
  Add(wxMakeFormBool("Program", &win->VisibleProgram));
  Add(wxMakeFormBool("Tempo", &win->VisibleTempo));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormBool("Show drumnames on drumtracks", &win->VisibleDrumNames));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormBool("Show events from all Tracks", &win->VisibleAllTracks));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormBool("Show harmonies from Harmony Browser", &win->VisibleHBChord));
  AssociatePanel(panel);
}


void tPianoWin::VisibleDialog()
{
  wxDialogBox *panel = new wxDialogBox(this, "Select Events", FALSE );
  tVisibleDlg * dlg = new tVisibleDlg(this);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}

// ********************************************************************
// Painting
// ********************************************************************



void tPianoWin::OnPaint(long x, long y)
{
  long OldFromClock = FromClock;
  tEventWin::OnPaint(x, y);
  if (CtrlEdit && OldFromClock != FromClock)
    CtrlEdit->ReInit(Track, FromClock, ClocksPerPixel);

  xPiano  = xx;

  long StopClk;
  tBarInfo BarInfo(Song);
  char buf[20];

  dc->BeginDrawing();
  dc->DestroyClippingRegion();
  dc->SetBackground(wxWHITE_BRUSH);
  DrawPlayPosition();
  SnapSel->Draw(xEvents, yEvents, wEvents, hEvents);
  dc->Clear();

  MouseLine = -1;

  #define VLine(x) DrawLine(x, yy, x, yEvents+hEvents)
  #define HLine(y) DrawLine(xx, y, xx + ww, y)

  dc->SetPen(wxBLACK_PEN);

  // vertikale Linien

  dc->VLine(xPiano);
  dc->VLine(xEvents);
  dc->VLine(xEvents-1);
  dc->HLine(yEvents);
  dc->HLine(yEvents-1);
  dc->HLine(yEvents + hEvents);

  // Taktstriche und -nummern

  //dc->SetClippingRegion(xEvents, yy, xEvents + wEvents, yy + hh);
  dc->SetFont(FixedFont);
  BarInfo.SetClock(FromClock);
  StopClk = x2Clock(xx + ww);
  long clk = BarInfo.Clock;
  while (clk < StopClk)
  {
    clk = BarInfo.Clock;
    x = Clock2x(clk);
    // Taktstrich und -nummer
    int i;
    dc->SetPen(wxBLACK_PEN);
    sprintf(buf, "%d", BarInfo.BarNr + 1);
    if (x > xEvents)
    {
      dc->DrawText(buf, x + LittleBit, yEvents - hFixedFont);
      dc->DrawLine(x, yEvents - hFixedFont, x, yEvents+hEvents);
    }

    dc->SetPen(wxGREY_PEN);
    for (i = 0; i < BarInfo.CountsPerBar; i++)
    {
      clk += BarInfo.TicksPerBar / BarInfo.CountsPerBar;
      x = Clock2x(clk);
      if (x > xEvents)
	dc->DrawLine(x, yEvents, x, yEvents+hEvents);
    }
    BarInfo.Next();
  }
  //dc->DestroyClippingRegion();
  dc->SetPen(wxBLACK_PEN);

  // horizontal lines

  for (y = Line2y(FromLine); y < yEvents + hEvents; y += hLine)
    if (y > yEvents)	// cheaper than clipping
      dc->DrawLine(xEvents, y, xEvents + wEvents, y);

  DrawPianoRoll();


  // draw chords from harmony-browser
  if (VisibleHBChord && the_harmony_browser && !Track->IsDrumTrack()) {
    HBAnalyzer *an = the_harmony_browser->getAnalyzer();
    if (an != 0)
    {
      wxBrush cbrush = *wxBLUE_BRUSH;
      cbrush.SetColour(220,220,255);
      wxBrush sbrush = *wxBLUE_BRUSH;
      sbrush.SetColour(230,255,230);

      //dc->SetClippingRegion(xEvents, yEvents, xEvents + wEvents, yEvents + hEvents);
      dc->SetLogicalFunction(wxXOR);
      dc->SetPen(wxTRANSPARENT_PEN);

      int steps = an->Steps();
      for (int step = 0; step < steps; step ++) {
        long start = an->Step2Clock(step);
        long stop  = an->Step2Clock(step + 1);
        if (stop > FromClock && start < ToClock) {
          // this chord is visible
          HBContext *context = an->GetContext(step);
          HBChord chord = context->Chord();
          HBChord scale = context->Scale();

          long x = Clock2x(start);
          if (x < xEvents)	// clip to left border
            x = xEvents;
          long w = Clock2x(stop) - x;
          if (w <= 0)
            continue;

          long h = hLine;
	  for (int i = 0; i < 12; i++) {
	    int pitch = i;
	    wxBrush *brush = 0;
	    if (chord.Contains(i))
	      brush = &cbrush;
	    else if (scale.Contains(i))
	      brush = &sbrush;
	    if (brush) {
	      dc->SetBrush(brush);
	      while (pitch < 127) {
		long y = Pitch2y(pitch);
		if (y >= yEvents && y <= yEvents + hEvents - h) // y-clipping
		  dc->DrawRectangle(x, y, w, h);
		pitch += 12;
	      }
	    }
	  }
	}
      }

      //dc->DestroyClippingRegion();
      dc->SetLogicalFunction(wxCOPY);
      dc->SetPen(wxBLACK_PEN);
      dc->SetBrush(wxBLACK_BRUSH);
      delete an;
    }
  }


  if (VisibleAllTracks)
  {
    int i;
    for (i = 0; i < Song->nTracks; i++)
    {
      tTrack *t = Song->GetTrack(i);
      if (t != Track && IsVisible(t))
        DrawEvents(t, StatKeyOn, wxGREY_BRUSH);
    }
  }

  if (VisibleKeyOn)
    DrawEvents(Track, StatKeyOn, wxBLACK_BRUSH);
  if (VisiblePitch)
    DrawEvents(Track, StatPitch, wxRED_BRUSH);
  if (VisibleController)
    DrawEvents(Track, StatControl, wxCYAN_BRUSH);
  if (VisibleProgram)
    DrawEvents(Track, StatProgram, wxGREEN_BRUSH);
  if (VisibleTempo)
    DrawEvents(Track, StatSetTempo, wxGREEN_BRUSH);

  dc->SetPen(wxBLACK_PEN);
  dc->SetBrush(wxBLACK_BRUSH);
  dc->SetBackground(wxWHITE_BRUSH);	// xor-bug

  SnapSel->Draw(xEvents, yEvents, wEvents, hEvents);

  DrawPlayPosition();
  dc->EndDrawing();
}



const int isBlack[12] = {0,1,0,1,0,0,1,0,1,0,1,0};
#define IsBlack(Key)  isBlack[(Key) % 12]

void tPianoWin::DrawPianoRoll()
{
  char buf[20];

  //dc->SetClippingRegion(xPiano, yEvents, xx+ww, yEvents + hEvents);
  dc->SetBrush(wxBLACK_BRUSH);

  long wBlack = wPiano * 2 / 3;
  int Pitch = 127 - FromLine;
  long y = Line2y(FromLine);

  if (VisibleKeyOn && (Track->Channel != DrumChannel || !VisibleDrumNames))
  {
    dc->SetFont(FixedFont);
    while (Pitch >= 0 && y < yEvents + hEvents)
    {
      if (IsBlack(Pitch))
      {
	dc->DrawRectangle(xx, y, wBlack, hLine);
	dc->DrawLine(xx + wBlack, y + hLine/2, xx + wPiano, y + hLine/2);
      }

      else if ((Pitch % 12) == 0)
      {
	dc->DrawLine(xx, y + hLine, xx + wBlack, y + hLine);
	sprintf(buf, "%d", Pitch / 12);
	dc->DrawText(buf, xx + wBlack + LittleBit, y + hLine / 2);
      }
      else if (!IsBlack(Pitch - 1))
	dc->DrawLine(xx, y + hLine, xx + wPiano, y + hLine);

      y += hLine;
      --Pitch;
    }
  }
  else
  {
    // Draw text?
    tNamedValue *names = 0;
    if (VisibleKeyOn && VisibleDrumNames)
      names = DrumNames;
    else if (VisibleController)
      names = ControlNames;
    else if (VisibleProgram)
      names = VoiceNames;
    if (names)
    {
      dc->SetFont(DrumFont);
      while (Pitch >= 0 && y < yEvents + hEvents)
      {
	dc->DrawText(names[Pitch+1].Name, xx + LittleBit, y);
	y += hLine;
	--Pitch;
      }
    }
    else if (VisiblePitch)
      ;
  }

  //dc->DestroyClippingRegion();
  dc->SetFont(Font);
}



void tPianoWin::DrawEvent(tEvent *e, wxBrush *Brush, int xor)
{
  int length = e->GetLength() / ClocksPerPixel;
  // Always draw at least two pixels to avoid invisible (behind a
  // vertical line) or zero-length events:
  if (length <= 1)
    length = 2;
  dc->BeginDrawing();
  if (xor)
    dc->SetLogicalFunction(wxXOR);
  long x = Clock2x(e->Clock);
  long y = Pitch2y(e->GetPitch());
  if (!xor)	// wxXOR is buggy?!
  {
    dc->SetBrush(wxWHITE_BRUSH);
    dc->DrawRectangle(x, y + LittleBit, length, hLine - 2 * LittleBit);
  }
  dc->SetBrush(Brush);
  dc->DrawRectangle(x, y + LittleBit, length, hLine - 2 * LittleBit);
  if (xor)
    dc->SetLogicalFunction(wxCOPY);
  dc->SetBrush(wxBLACK_BRUSH);
  dc->EndDrawing();
}




void tPianoWin::DrawEvents(tTrack *t, int Stat, wxBrush *Brush)
{
  //dc->SetClippingRegion(xEvents, yEvents, xEvents + wEvents, yEvents + hEvents);
  dc->SetBrush(Brush);

  tEventIterator Iterator(t);
  tEvent *e = Iterator.First();
  int FromPitch = 127 - ToLine;
  int ToPitch   = 127 - FromLine;

  // Koordinate fuer Linien

  long x0 = Clock2x(0);
  long y0 = Line2y(64);

  while (e)
  {
    if (e->Stat == Stat)
    {
      int Pitch   = e->GetPitch();
      long Length = e->GetLength();
      long Clock  = e->Clock;

      long x1 = Clock2x(Clock);
      long y1 = Line2y(127 - Pitch);

      // event partially visible?
      if (Clock + Length >= FromClock && FromPitch < Pitch && Pitch <= ToPitch)
      {
	long DrawLength = Length/ClocksPerPixel;
	// do clipping ourselves
	if (x1 < xEvents)
	{
	  DrawLength -= xEvents - x1;
	  x1 = xEvents;
	}
	// Always draw at least two pixels to avoid invisible (behind a
	// vertical line) or zero-length events:
	if (DrawLength <= 1)
	  DrawLength = 2;
        dc->DrawRectangle(x1, y1 + LittleBit, DrawLength, hLine - 2 * LittleBit);
      }
      x0 = x1;
      y0 = y1;

      if (Clock > ToClock)
        break;
    }
    e = Iterator.Next();
  }
  dc->SetBrush(wxBLACK_BRUSH);
  //dc->DestroyClippingRegion();
}



// ********************************************************************
// Utilities
// ********************************************************************

long tPianoWin::SnapClocks()
{
  return Song->TicksPerQuarter * 4L / SnapDenomiator;
}

int tPianoWin::y2Pitch(long y)
{
  return 127 - y2Line(y);
}


long tPianoWin::Pitch2y(int Pitch)
{
  return Line2y(127 - Pitch);
}


tEvent *tPianoWin::FindEvent(tTrack *Track, long Clock, int Pitch)
// Pitch == -1: search for any pitches
{
  tEventIterator Iterator(Track);
  tEvent *e = Iterator.First();
  while (e)
  {
    if (e->Clock <= Clock)
    {
      if ((e->Clock + e->GetLength() >= Clock)
           && (e->GetPitch() == Pitch || Pitch == -1)
	   && IsVisible(e))
      {
        return e;
      }
    }
    else
      return 0;
    e = Iterator.Next();
  }
  return 0;
}



void tPianoWin::Copy(tTrack *t, tEvent *e, int Kill)
{
  if (!e)
    return;

  Song->NewUndoBuffer();
  PasteBuffer.Clear();
  PasteBuffer.Put(e->Copy());
  if (Kill)
  {
    tKeyOn *k = e->IsKeyOn();
    if (k)
      Listen.KeyOn(k->Key, k->Channel, k->Veloc);
    DrawEvent(e, wxWHITE_BRUSH, 0);
    t->Kill(e);
    t->Cleanup();
  }
  if (GuitarWin)
    GuitarWin->Redraw();
}


void tPianoWin::Paste(tTrack *t, long Clock, int Pitch)
{
  if (PasteBuffer.nEvents == 0)
  {
    tKeyOn *e = new tKeyOn(0, 0, 64, 64, SnapClocks() - 4);
    PasteBuffer.Put(e);
  }
  if (PasteBuffer.nEvents > 1)
    Pitch = -1;	// don't change Pitch

  Song->NewUndoBuffer();
  tEventIterator Iterator(&PasteBuffer);
  tEvent *e = Iterator.First();
  if (e)
  {
    long DeltaClock = Clock - e->Clock;
    int  DeltaPitch = 0;
    if (Pitch >= 0)
      DeltaPitch = Pitch - e->GetPitch();
    while (e)
    {
      tEvent *c = e->Copy();
      c->SetPitch(c->GetPitch() + DeltaPitch);
      c->Clock += DeltaClock;
      if (t->ForceChannel && c->IsChannelEvent())
        c->IsChannelEvent()->Channel = t->Channel - 1;
      tKeyOn *k = c->IsKeyOn();
      if (k)
	Listen.KeyOn(k->Key, k->Channel, k->Veloc);
      DrawEvent(c, c->GetBrush(), 0);
      t->Put(c);
      e = Iterator.Next();
    }
    t->Cleanup();
  }
}

// ********************************************************************
// Mouse
// ********************************************************************

/*
 * left drag: Events markieren fuer Menu
 * left click:
 *   mit shift: copy
 *   ohne shift: cut
 *   auf Leer: paste
 * left click + ctrl:
 *   Note-Dialog
 * right drag:
 *   linke haelfte  : verschieben
 *   rechte halefte : Laenge einstellen
 * right click:
 *   Focus TrackWin
 *
 * PianoRoll:
 *   Left: midiout
 */

// ------------------------------------------------------------
// tMousePlay - Click in pianoroll
// -----------------------------------------------------------

class tMousePlay : public tMouseAction
{
    int Pitch, Veloc, Channel;
    tPianoWin *Win;
  public:
    tMousePlay(tPianoWin *win, wxMouseEvent &e);
    int Event(wxMouseEvent &e);
};

tMousePlay::tMousePlay(tPianoWin *win, wxMouseEvent &e)
{
  Win = win;

  Pitch = 0;
  Channel = Win->Track->Channel ? Win->Track->Channel - 1 : 0;
  Event(e);
}


int tMousePlay::Event(wxMouseEvent &e)
{
  float x, y;

  int OldPitch = Pitch;
  e.Position(&x, &y);

  if (e.LeftDown())
  {
    Pitch = Win->y2Pitch((long)y);
    Veloc = 64;
  }
  else if (e.MiddleDown())
  {
    Pitch = Win->y2Pitch((long)y);
    Veloc = 80;
  }
  else if (e.RightDown())
  {
    Pitch = Win->y2Pitch((long)y);
    Veloc = 110;
  }
  else if (e.ButtonUp())
  {
    Pitch = 0;
  }
  else if (e.Dragging())
    Pitch = Win->y2Pitch((long)y);
  else
    return 0;

  if (OldPitch && OldPitch != Pitch)
  {
    tKeyOff of(0, Channel, OldPitch);
    Midi->OutNow(&of);
    OldPitch = 0;
  }

  if (Pitch && Pitch != OldPitch)
  {
    tKeyOn on(0, Channel, Pitch, Veloc);
    Midi->OutNow(&on);
    OldPitch = 0;
  }

  if (!Pitch)
  {
    Win->MouseAction = 0;
    delete this;
    return 1;	// done
  }
  return 0;
}


void tPianoWin::MousePiano(wxMouseEvent &e)
{
  if (e.ButtonDown())
    MouseAction = new tMousePlay(this, e);
}


int tPianoWin::Channel()
{
  return Track->Channel ? Track->Channel - 1 : 0;
}

// -------------------------------------------------------------------------
// tKeyLengthDragger
// -------------------------------------------------------------------------

class tKeyLengthDragger : public tMouseAction
{
    tKeyOn    *Event;
    tKeyOn    *Copy;
    tPianoWin *Win;
    tTrack    *Track;
    wxDC      *dc;

  public:
    tKeyLengthDragger(tKeyOn *k, tPianoWin *w);
    int Dragging(wxMouseEvent &e);
    int RightUp(wxMouseEvent &e);
};


tKeyLengthDragger::tKeyLengthDragger(tKeyOn *k, tPianoWin *w)
{
  Event = k;
  Copy  = k->Copy() -> IsKeyOn();
  Win   = w;

  Win->DrawEvent(Copy, wxWHITE_BRUSH, 0);
  Win->DrawEvent(Copy, Copy->GetBrush(), 1);
}

int tKeyLengthDragger::Dragging(wxMouseEvent &e)
{
  float fx, fy;

  Win->DrawEvent(Copy, Copy->GetBrush(), 1);
  e.Position(&fx, &fy);
  long Clock = Win->x2Clock((long)fx);
  int  Length = Clock - Copy->Clock;
  if (Length <= 0)
    Length = 1;
  Copy->Length = Length;
  Win->DrawEvent(Copy, Copy->GetBrush(), 1);
  return 0;
}

int tKeyLengthDragger::RightUp(wxMouseEvent &e)
{
  Win->DrawEvent(Copy, Copy->GetBrush(), 1);
  Win->DrawEvent(Copy, Copy->GetBrush(), 0);
  Win->Track->Kill(Event);
  Win->Track->Put(Copy);
  Win->Track->Cleanup();
  Win->MouseAction = 0;
  delete this;
  return 0;
}


// --------------------------------------------------------------------
// VelocCounter
// --------------------------------------------------------------------

class tVelocCounter : public tMouseCounter
{
    tPianoWin *Win;
    tKeyOn    *KeyOn;
  public:
    int Event(wxMouseEvent &e);
    tVelocCounter(tPianoWin *w, tRect *r, tKeyOn *e)
      : tMouseCounter(w->dc, r, e->Veloc, 1, 127)
    {
      Win = w;
      KeyOn = e;
      Win->dc->SetFont(Win->FixedFont);
    }
};



int tVelocCounter::Event(wxMouseEvent &e)
{
  if (tMouseCounter::Event(e))
  {
    tKeyOn *Copy = (tKeyOn *)KeyOn->Copy();
    Copy->Veloc = Value;
    Win->Track->Kill(KeyOn);
    Win->Track->Put(Copy);
    Win->Track->Cleanup();

    Win->MouseAction = 0;
    Win->dc->SetFont(Win->Font);
    delete this;
  }
  return 0;
}




// --------------------------------------------------------------------


void tPianoWin::MouseCutPaste(wxMouseEvent &e, Bool cut)
{
  float fx, fy;
  e.Position(&fx, &fy);
  long x = (long)fx;
  long y = (long)fy;

  long Clock = x2Clock(x);
  int  Pitch = y2Pitch(y);
  tEvent *m = FindEvent(Track, Clock, Pitch);
  if (m)
    Copy(Track, m, cut);
  else
    Paste(Track, SnapClock(Clock), Pitch);
  if (VisibleHBChord)
    Redraw();
}


void tPianoWin::MouseEvents(wxMouseEvent &e)
{
  int action = MouseEvnt.Action(e);
  if (action)
  {
    float fx, fy;

    e.Position(&fx, &fy);
    long x = (long)fx;
    long y = (long)fy;

    long Clock = x2Clock(x);
    int  Pitch = y2Pitch(y);
    tEvent *m = FindEvent(Track, Clock, Pitch);
    tKeyOn *k = 0;
    if (m)
      k = m->IsKeyOn();

    switch (action)
    {
      case MA_CUTPASTE:
        MouseCutPaste(e, 1);
        break;

      case MA_COPY:
        MouseCutPaste(e, 0);
        break;

      case MA_LENGTH	:
        if (k)
	  MouseAction = new tKeyLengthDragger(k, this);
	else // event not found, maybe change to another Track
	if (VisibleAllTracks)
	{
	  int i;
	  for (i = 0; i < Song->nTracks; i++)
	  {
	    tTrack *t = Song->GetTrack(i);
	    if (IsVisible(t) && FindEvent(t, Clock, Pitch))
	    {
	      NewPosition(i, -1L);
	      break;
	    }
	  }
	}
	break;

      case MA_DIALOG	:
	EventDialog(m, this, Track, Clock, Track->Channel - 1, Pitch);
        break;

      case MA_LISTEN	:
	MousePiano(e);
	break;

      case MA_SELECT	:
      case MA_CONTSEL	:
	tEventWin::OnMouseEvent(e);
	break;

      case MA_VELOCITY:
        if (k)
        {
	  tRect r;
	  r.x = xx + LittleBit;
	  r.y = yy + LittleBit;
	  r.w = wPiano - 2 * LittleBit;
	  r.h = hFixedFont;

	  tVelocCounter *VelocCounter = new tVelocCounter(this, &r, k);
	  VelocCounter->Event(e);
	  MouseAction = VelocCounter;
	}
	break;

    }
  }
}

// ------------------------------------------------------------------------
// dispatch Mouseevent
// ------------------------------------------------------------------------

void tPianoWin::ShowPitch(int pitch)
{
  long line = y2Line(Pitch2y(pitch));
  if (line >= FromLine && line != MouseLine)
  {
    dc->SetLogicalFunction(wxXOR);
    dc->SetBrush(wxBLACK_BRUSH);
    if (MouseLine >= 0)
      dc->DrawRectangle(xPiano, Line2y(MouseLine) + LittleBit, wPiano, hLine - 2*LittleBit);
    MouseLine = line;
    dc->DrawRectangle(xPiano, Line2y(MouseLine) + LittleBit, wPiano, hLine - 2*LittleBit);
    dc->SetLogicalFunction(wxCOPY);
  }
}


int tPianoWin::OnMouseEvent(wxMouseEvent &e)
{
  // aktuelle Zeile am linken Rand als Balken zeichnen

  if (e.Moving() && !e.Dragging())
  {
    float fx, fy;
    e.Position(&fx, &fy);
    int pitch = y2Pitch(long(fy));
    ShowPitch(pitch);
    if (GuitarWin)
      GuitarWin->ShowPitch(pitch);
  }

  // dispatch

  if (!MouseAction)
  {
    float fx, fy;
    e.Position(&fx, &fy);
    long x = (long)fx;
    long y = (long)fy;

    if (y > yEvents)	// click in event area?
    {
      if (xPiano < x && x < xPiano + wPiano)
        MousePiano(e);
      else if (xEvents < x && x < xEvents + wEvents)
        MouseEvents(e);
      else
        tEventWin::OnMouseEvent(e);
    }

    else		// click in top line
    {
      int action = MousePlay.Action(e);

      if (action)
      {
        if (!Midi->Playing)
        {
          long Clock, LoopClock;
	  if (action == MA_CYCLE)
	  {
	    if (SnapSel->Selected)
	    {
	      Clock = Filter->FromClock;
	      LoopClock = Filter->ToClock;
	    }
	    else
	     {
	      Clock = x2BarClock((long)x, 0);
	      LoopClock = x2BarClock((long)x, 4);
	    }
	  }
	  else {
	    Clock = SnapClock(x2Clock((long)x));
	    LoopClock = 0;
	  }
	  Midi->StartPlay(Clock, LoopClock);
	}
	else			// Stop Record/Play
	  Midi->StopPlay();
      }
    }

  }
  else
    tEventWin::OnMouseEvent(e);

  return 0;
}



int tPianoWin::OnKeyEvent(wxKeyEvent &e)
{
  if (e.ShiftDown())
  {
    switch (e.KeyCode())
    {
      case WXK_UP:
        if (TrackNr > 0)
        {
          TrackNr --;
          NewPosition(TrackNr, -1L);
	}
	return 1;
      case WXK_DOWN:
        if (TrackNr < Song->nTracks - 1)
        {
          TrackNr ++;
          NewPosition(TrackNr, -1L);
	}
	return 1;
    }
  }
  return 0;
}

// ------------------------------------------------------------------------
// Snapper
// ------------------------------------------------------------------------


void tPianoWin::SnapSelStop(wxMouseEvent &e)
{
  if (SnapSel->Selected)
  {

    long fr = y2Pitch((long)(SnapSel->r.y + SnapSel->r.h - 1));
    long to = y2Pitch((long)SnapSel->r.y + 1);

    Filter->FltEvents[FltKeyOn].Selected = VisibleKeyOn;
    Filter->FltEvents[FltKeyOn].FromValue = fr;
    Filter->FltEvents[FltKeyOn].ToValue   = to;

    Filter->FltEvents[FltPitch].Selected = VisiblePitch;
    Filter->FltEvents[FltPitch].FromValue = (fr << 7) - 8192;
    Filter->FltEvents[FltPitch].ToValue   = ((to + 1) << 7) - 8192;

    Filter->FltEvents[FltControl].Selected = VisibleController;
    Filter->FltEvents[FltControl].FromValue = fr;
    Filter->FltEvents[FltControl].ToValue   = to;

    Filter->FltEvents[FltProgram].Selected = VisibleProgram;
    Filter->FltEvents[FltProgram].FromValue = fr;
    Filter->FltEvents[FltProgram].ToValue   = to;

    Filter->FltEvents[FltTempo].Selected = VisibleTempo;
    Filter->FltEvents[FltTempo].FromValue = fr;
    Filter->FltEvents[FltTempo].ToValue   = to;

    Filter->FromTrack = TrackNr;
    Filter->ToTrack   = TrackNr;
    Filter->FromClock = SnapClock(x2Clock((long)SnapSel->r.x + 1));
    Filter->ToClock   = SnapClock(x2Clock((long)(SnapSel->r.x + SnapSel->r.w + 1)));
  }
}



void tPianoWin::SnapSelStart(wxMouseEvent &)
{
  nSnaps = 0;
  long clk = SnapClock(FromClock, 0);
  long qnt = SnapClocks();
  while (clk <= ToClock && nSnaps < MaxSnaps)
  {
    xSnaps[nSnaps++] = Clock2x(clk);
    clk += qnt;
  }
  if (nSnaps < MaxSnaps)
    SnapSel->SetXSnap(nSnaps, xSnaps);
  else
    SnapSel->SetXSnap(0,0,0);
  SnapSel->SetYSnap(FromLine * hLine + hTop, yEvents + hEvents, hLine);
}


long tPianoWin::SnapClock(long clk, int up)
{
  long qnt = SnapClocks();
  clk -= (clk % qnt);
  if (up)
    clk += qnt;
  return clk;
}



void tPianoWin::GetVirtSize(long *w, long *h)
{
  //*w = 500 * Song->TicksPerQuarter / ClocksPerPixel;
  *w = 32000L * 50;	// mehr kann wxwin nicht!!!
  *h = 135 * hLine + hTop;
}


void tPianoWin::SnapDlg()
{
  static char *Names[] =
  { "1/8", "1/12", "1/16", "1/24", "1/32", "1/48", "1/96", (char *)0 };
  static long Values[] =
  {   8, 12, 16, 24, 32, 48, 96 };

  int i = wxGetSingleChoiceIndex("Select Snap", "Snap", 7, Names);
  if (i >= 0)
    //SnapDenomiator = Values[i];
    SetSnapDenom(Values[i]);
}


#if 0
void tPianoWin::SetSnapDenom(long value)
{
  SnapDenomiator = value;
  tool_bar->ToggleTool(MEN_SNAP_8, FALSE);
  tool_bar->ToggleTool(MEN_SNAP_8D, FALSE);
  tool_bar->ToggleTool(MEN_SNAP_16, FALSE);
  tool_bar->ToggleTool(MEN_SNAP_16D, FALSE);

  int id = -1;
  switch ((int)value)
  {
    case 8 : id = MEN_SNAP_8; break;
    case 12: id = MEN_SNAP_8D; break;
    case 16: id = MEN_SNAP_16; break;
    case 24: id = MEN_SNAP_16D; break;
  }
  if (id >= 0)
    tool_bar->ToggleTool(id, TRUE);
}
#else
void tPianoWin::SetSnapDenom(long value)
{
  const int N = 4;
  const struct { int id; long val; } tab[N] = {
    { MEN_SNAP_8,    8 },
    { MEN_SNAP_8D,  12 },
    { MEN_SNAP_16,  16 },
    { MEN_SNAP_16D, 24 },
  };

  int i, idon = -1, idof = -1;
  for (i = 0; i < N; i++)
  {
    if (tab[i].val == value)
      idon = tab[i].id;
    if (tab[i].val == SnapDenomiator)
      idof = tab[i].id;
  }

  SnapDenomiator = value;
  if (idof >= 0 && tool_bar->GetToolState(idof))
    tool_bar->ToggleTool(idof, FALSE);
#ifndef wx_xt
  if (idon >= 0 && !tool_bar->GetToolState(idon))
    tool_bar->ToggleTool(idon, TRUE);
#endif
}

#endif

void tPianoWin::SetVisibleAllTracks(Bool value)
{
  tool_bar->ToggleTool(MEN_VIS_ALL_TRK, value);
  VisibleAllTracks = value;
  Redraw();
}

