
/*
**  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 "eventwin.h"
#include "util.h"
#include "song.h"
#include "dialogs.h"
#include "jazz.h"
#include "toolbar.h"

// ************************************************************************
// tCanvas
// ************************************************************************

#ifdef wx_xt
#define ScLine 1L
#define ScPage 200L
#else
#define ScLine 50L
#define ScPage 8L
#endif



tCanvas::tCanvas(tEventWin *frame, int x, int y, int w, int h, int style)
  : wxCanvas(frame, x, y, w, h, style)
{
  EventWin = frame;
}


void tCanvas::OnPaint(void)
{
  int x, y, w, h;
  GetClientSize(&w, &h);
  ViewStart(&x, &y);
  EventWin->OnPaint((long)x * ScLine, (long)y * ScLine);
}


void tCanvas::OnEvent(wxMouseEvent &e)
{
  EventWin->OnMouseEvent(e);
}

void tCanvas::OnChar(wxKeyEvent &e)
{
  if (!EventWin->OnKeyEvent(e))
    wxCanvas::OnChar(e);
}


void tCanvas::SetScrollRanges()
{
  long w, h;
  EventWin->GetVirtSize(&w, &h);
  SetScrollbars(ScLine, ScLine, w/ScLine, h/ScLine, ScPage, ScPage);
#ifdef wx_xt
  EnableScrolling(TRUE, TRUE);
#else
  EnableScrolling(FALSE, FALSE);
#endif
}

void tCanvas::SetScrollPosition(long x, long y)
{
  x /= ScLine;
  y /= ScLine;
  Scroll(x, y);
}


// ************************************************************************
// tEventWin
// ************************************************************************

tEventWin::tEventWin(wxFrame *parent, char *title, tSong *song, int x, int y, int width, int height )
  : wxFrame(parent, title, x, y, width, height) // default is 640x442
{
  tool_bar  = 0;

  Song      = song;
  ParentWin = parent;
  NextWin   = 0;
  Filter    = new tFilter(Song);

  Canvas = 0;
  MouseAction = 0;
  SnapSel = 0;
  DialogBox = 0;
  MixerForm = 0;
  dc = 0;

  xEvents = yEvents = wEvents = hEvents = 0;
  hLine = 0;
  LittleBit = 1;

  FontSize = 12;
  ClocksPerPixel = 36;
  UseColors = 0;
  hTop   = 40;
  wLeft  = 100;

  PlayClock = -1;

  Font = 0;
  FixedFont = 0;

  hFixedFont = 0;
  xx = yy = ww = hh = 0;      // canvas coords
  FromClock = ToClock = 0;
  FromLine = ToLine = 0;
}


tEventWin::~tEventWin()
{
  delete Canvas;
  delete SnapSel;
  delete Filter;
  delete tool_bar;

  if (MixerForm)
    delete MixerForm;
}



void tEventWin::CreateMenu()
{
#if 0
  wxMenu *menu = new wxMenu;
  menu->Append(999, "&MenuItem");
  wxMenuBar *menu_bar = new wxMenuBar;
  menu_bar->Append(menu, "&Debug");
  SetMenuBar(menu_bar);
#endif
}



void tEventWin::CreateCanvas()
{
  int w, h;
  GetClientSize(&w, &h);
  Canvas = new tCanvas(this, 0, 0, w, h);
  dc = Canvas->GetDC();
}


void tEventWin::Create()
{
  CreateMenu();
  CreateCanvas();
  SnapSel = new tSnapSelection(Canvas);

  Setup();
  Canvas->SetScrollRanges();
}


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

  dc->SetFont(NULL);
  delete FixedFont;
  FixedFont = new wxFont(12, wxSWISS, wxNORMAL, wxNORMAL);
  dc->SetFont(FixedFont);
  dc->GetTextExtent("H", &x, &y);
  hFixedFont = (int)y;

  delete Font;
  Font = new wxFont(FontSize, wxSWISS, wxNORMAL, wxNORMAL);
  dc->SetFont(Font);

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

  dc->GetTextExtent("HXWjgi", &x, &y);
  hLine = (int)y + LittleBit;
}


void tEventWin::OnSize(int w, int h)
{
  wxFrame::OnSize(w, h);

  float maxToolBarWidth  = 0.0;
  float maxToolBarHeight = 0.0;
  if (tool_bar)
    tool_bar->GetMaxSize(&maxToolBarWidth, &maxToolBarHeight);

  int frameWidth, frameHeight;
  GetClientSize(&frameWidth, &frameHeight);
  if (Canvas)
    Canvas->SetSize(0, (int)maxToolBarHeight, (int)frameWidth, (int)(frameHeight - maxToolBarHeight));
  if (tool_bar)
    tool_bar->SetSize(0, 0, (int)frameWidth, (int)maxToolBarHeight);

#if 0
  if (Canvas)
  {
    int w, h;
    GetClientSize(&w, &h);
    Canvas->SetSize(0, 0, w, h);
  }
#endif

}


// ******************************************************************
// Window-Settings Dialog
// ******************************************************************


static tNamedValue TrackFontSizes[] =
{
  {"Tiny",    8 },
  {"Small",  10 },
  {"Medium", 12 },
  {"Large",  14 },
  {"Huge",   17 },
  {0,        12 }
};

static tNamedValue PianoFontSizes[] =
{
  {"Tiny",    6 },
  {"Small",   7 },
  {"Medium",  8 },
  {"Large",  10 },
  {"Huge",   12 },

  {0,         8 }
};


static tNamedValue TrackEventSizes[] =
{
  {"Tiny",   60 },
  {"Small",  48 },
  {"Medium", 36 },
  {"Large",  24 },
  {"Huge",   12 },
  {0,        36 }
};

static tNamedValue PianoEventSizes[] =
{
  {"Tiny",   16 },
  {"Small",  8 },
  {"Medium", 4 },
  {"Large",  2 },
  {"Huge",   1 },
  {0,        6 }
};


class tEventWinDlg : public wxForm
{
  tEventWin *EventWin;
  tNamedChoice xSize, ySize;
 public:
  tEventWinDlg(tEventWin *w, tNamedValue *xSizes, tNamedValue *ySizes);
  void EditForm(wxPanel *panel);
  virtual void OnOk();
  virtual void OnCancel();
  virtual void OnHelp();
};



tEventWinDlg::tEventWinDlg(tEventWin *w, tNamedValue *xSizes, tNamedValue *ySizes)
  : wxForm( USED_WXFORM_BUTTONS ),
    xSize("Event Size", xSizes, &w->ClocksPerPixel),
    ySize("Font Size",  ySizes, &w->FontSize)
  {
    EventWin = w;
  }



void tEventWinDlg::OnCancel()
{
  EventWin->DialogBox = 0;
  wxForm::OnCancel();
}


void tEventWinDlg::OnOk()
{
  xSize.GetValue();
  ySize.GetValue();

  EventWin->DialogBox = 0;
  EventWin->Setup();
  EventWin->Canvas->SetScrollRanges();
  // EventWin->Redraw();
  wxForm::OnOk();
}


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

void tEventWinDlg::EditForm(wxPanel *panel)
{
  Add(xSize.mkFormItem(150));
  Add(ySize.mkFormItem(150));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormBool("Use Colors", &EventWin->UseColors));
  AssociatePanel(panel);
}


void tEventWin::SettingsDialog(int piano)
{
  tEventWinDlg *dlg;
  if (DialogBox)
  {
    DialogBox->Show(TRUE);
    return;
  }
  DialogBox = new wxDialogBox(this, "Window Settings", FALSE );
  if (piano)
    dlg = new tEventWinDlg(this, PianoEventSizes, PianoFontSizes);
  else
    dlg = new tEventWinDlg(this, TrackEventSizes, TrackFontSizes);
  dlg->EditForm(DialogBox);
  DialogBox->Fit();
  DialogBox->Show(TRUE);
}

// *******************************************************************
// Coord-Functions
// *******************************************************************


long tEventWin::x2Clock(long x)
{
  return (x - xEvents) * ClocksPerPixel + FromClock;
}


long tEventWin::Clock2x(long clk)
{
  return xEvents + (clk - FromClock) / ClocksPerPixel;
}


long tEventWin::x2BarClock(long x, int next)
{
  long clk = x2Clock(x);
  tBarInfo b(Song);
  b.SetClock(clk);
  while (next--)
    b.Next();
  return b.Clock;
}


long tEventWin::y2yLine(long y, int up)
{
  if (up)
    y += hLine;
  y -= hTop;
  y -= y % hLine;
  y += hTop;
  return y;
}

long tEventWin::y2Line(long y, int up)
{
  if (up)
    y += hLine;
  y -= hTop;
  return y / hLine;
}


long tEventWin::Line2y(long Line)
{
  return Line * hLine + hTop;
}

void tEventWin::LineText(long x, long y, long w, char *str, int h)
{
  if (h < 0)
  {
    h = hLine;
    y = y2yLine(y);
  }
  dc->SetBrush(wxWHITE_BRUSH);
  if (w && h)
    dc->DrawRectangle(x, y, w, h);
  dc->DrawText(str, x + LittleBit, y + LittleBit);
}

// *******************************************************************
// Painting behavior
// *******************************************************************

void tEventWin::Redraw()
{
  Setup();
  Canvas->OnPaint();
}


void tEventWin::OnPaint(long x, long y)
{
//printf("EventWin::OnPaint: x %ld, y %ld, w %ld, h %ld\n", x, y, w, h);
  xx = x;
  yy = y;

// wxCanvas::GetClientSize returns huge values, at least in wx_xt
  int xc, yc;
  GetClientSize(&xc, &yc);
  ww = xc;
  hh = yc;

  xEvents = xx + wLeft;
  yEvents = yy + hTop;
  wEvents = ww - wLeft;
  hEvents = hh - hTop;
// printf("EventWin::OnPaint: xe %ld, ye %ld, we %ld, he %ld\n", xEvents, yEvents, wEvents, hEvents);

  FromLine = yy / hLine;
  ToLine   = (yy + hh - hTop) / hLine;
  FromClock = xx * ClocksPerPixel;
  ToClock = x2Clock(xx + ww);
}

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

int tEventWin::OnKeyEvent(wxKeyEvent &e)
{
  return 0;
}

int tEventWin::OnMouseEvent(wxMouseEvent &e)
{
  if (!MouseAction)
  {
    // create SnapSel?

    float fx, fy;
    e.Position(&fx, &fy);
    long x = (long)fx;
    long y = (long)fy;
    if (xEvents < x && x < xEvents + wEvents && yEvents < y && y < yEvents + hEvents)
    {
      if (e.LeftDown())
      {
        if (!e.ShiftDown() && SnapSel->Selected)
        {
          SnapSel->Draw(xEvents, yEvents, wEvents, hEvents);
          SnapSel->Selected = 0;
        }
        else
	{
	  SnapSelStart(e);
	  dc->SetClippingRegion(xEvents, yEvents, wEvents, hEvents);
	  if (SnapSel->Selected)
	    SnapSel->Draw();
	  SnapSel->Event(e);
	  MouseAction = SnapSel;
	}
      }
    }
  }

  else
  {
    // MouseAction active

    if (MouseAction->Event(e))
    {
      // MouseAction finished

      if (MouseAction == SnapSel)
      {
	SnapSelStop(e);
	SnapSel->Draw();
	dc->DestroyClippingRegion();
	MouseAction = 0;
	return 1;
      }

      MouseAction = 0;
    }
  }
  return 0;
}

// ******************************************************************
// dummies
// ******************************************************************

Bool tEventWin::OnClose()
{
  return FALSE;
}

void tEventWin::OnMenuCommand(int) {}
void tEventWin::SnapSelStart(wxMouseEvent &e){}
void tEventWin::SnapSelStop(wxMouseEvent &e) {}
void tEventWin::GetVirtSize(long *w, long *h)
{
  *w = 5000L; *h = 1000;
}

// ------------------------------------------------------------------------------------
// PlayPosition
// -----------------------------------------------------------------------------------

void tEventWin::NewPlayPosition(long Clock)
{
  if (!SnapSel->Active && ((Clock > (FromClock + 5 * ToClock) / 6L) || (Clock < FromClock)) && (Clock >= 0L) )
  {
    long x = Clock2x(Clock);
    Canvas->SetScrollPosition(x - wLeft, yy);
  }

  if (!SnapSel->Active)	// sets clipping
  {
    if (PlayClock != Clock) {
      DrawPlayPosition();
      PlayClock = Clock;
      DrawPlayPosition();
    }
  }
  if (NextWin)
    NextWin->NewPlayPosition(Clock);
}


void tEventWin::DrawPlayPosition()
{
  if (!SnapSel->Active && PlayClock >= FromClock && PlayClock < ToClock)
  {
    dc->SetLogicalFunction(wxXOR);
    dc->SetBrush(wxBLACK_BRUSH);
    dc->SetPen(wxBLACK_PEN);
    long x = Clock2x(PlayClock);
    dc->DrawRectangle(x, yy, 2*LittleBit, hTop);
    dc->SetLogicalFunction(wxCOPY);
  }
  if (NextWin)
    NextWin->DrawPlayPosition();
}

// **************************************************************************
// EventsSelected
// **************************************************************************

int tEventWin::EventsSelected()
{
  if (!SnapSel->Selected)
  {
    wxMessageBox("no selection active", "Error");
    return 0;
  }
  return 1;
}

// **************************************************************************
// Quantize
// **************************************************************************

void tEventWin::MenQuantize()
{
  if (!EventsSelected())
    return;
  wxDialogBox *panel = new wxDialogBox(this, "Quantize", FALSE );
  tQuantizeDlg * dlg = new tQuantizeDlg(this, Filter);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}

// **************************************************************************
// Cleanup
// **************************************************************************

void tEventWin::MenCleanup()
{
  if (!EventsSelected())
    return;
  wxDialogBox *panel = new wxDialogBox(this, "Cleanup", FALSE );
  tCleanupDlg * dlg = new tCleanupDlg(this, Filter);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}


// **************************************************************************
// SearchReplace
// **************************************************************************

void tEventWin::MenSearchReplace()
{
  if (!EventsSelected())
    return;
  wxDialogBox *panel = new wxDialogBox(this, "Search Replace", FALSE );
  tSearchReplaceDlg * dlg = new tSearchReplaceDlg(this, Filter);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}


// **************************************************************************
// SetChannel
// **************************************************************************

void tEventWin::MenSetChannel()
{
  if (!EventsSelected())
    return;
  wxDialogBox *panel = new wxDialogBox(this, "Set MIDI Channel", FALSE );
  tSetChannelDlg * dlg = new tSetChannelDlg(Filter);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}


// **************************************************************************
// Transpose
// **************************************************************************

void tEventWin::MenTranspose()
{
  if (!EventsSelected())
    return;
  wxDialogBox *panel = new wxDialogBox(this, "Transpose", FALSE );
  tTransposeDlg * dlg = new tTransposeDlg(this, Filter);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}


void tEventWin::MenShift(long Unit)
{
  if (EventsSelected())
  {
    wxDialogBox *panel = new wxDialogBox(this, "Shift", FALSE );
    tShiftDlg * dlg = new tShiftDlg(this, Filter, Unit);
    dlg->EditForm(panel);
    panel->Fit();
    panel->Show(TRUE);
  }
}


// ********************************************************************************
// Delete
// ********************************************************************************


void tEventWin::MenDelete()
{
  if (!EventsSelected())
    return;
  wxDialogBox *panel = new wxDialogBox(this, "Delete", FALSE );
  tDeleteDlg * dlg = new tDeleteDlg(this, Filter);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}

// ********************************************************************************
// Velocity
// ********************************************************************************


void tEventWin::MenVelocity()
{
  if (!EventsSelected())
    return;
  wxDialogBox *panel = new wxDialogBox(this, "Velocity", FALSE );
  tVelocityDlg * dlg = new tVelocityDlg(Filter);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}

// ********************************************************************************
// Length
// ********************************************************************************


void tEventWin::MenLength()
{
  if (!EventsSelected())
    return;
  wxDialogBox *panel = new wxDialogBox(this, "Length", FALSE );
  tLengthDlg * dlg = new tLengthDlg(this, Filter);
  dlg->EditForm(panel);
  panel->Fit();
  panel->Show(TRUE);
}

// ******************************************************************
// MeterChange Dialog
// ******************************************************************


class tMeterChangeDlg : public wxForm
{
public:
  tEventWin *EventWin;
  static int Numerator;
  static int Denomiator;
  static int BarNr;
  tMeterChangeDlg(tEventWin *w);
  void EditForm(wxPanel *panel);
  virtual void OnOk();
  virtual void OnCancel();
  virtual void OnHelp();
};


int tMeterChangeDlg::Numerator = 4;
int tMeterChangeDlg::Denomiator = 4;
int tMeterChangeDlg::BarNr = 1;

tMeterChangeDlg::tMeterChangeDlg(tEventWin *w)
: wxForm( USED_WXFORM_BUTTONS )
{
  EventWin = w;
}


void tMeterChangeDlg::OnCancel()
{
  EventWin->DialogBox = 0;
  wxForm::OnCancel();
}


void tMeterChangeDlg::OnOk()
{
  EventWin->Song->SetMeterChange(BarNr, Numerator, Denomiator);
  EventWin->Redraw();
  EventWin->DialogBox = 0;
  wxForm::OnOk();
}

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


void tMeterChangeDlg::EditForm(wxPanel *panel)
{
  Add(wxMakeFormShort("BarNr:",     &BarNr,      wxFORM_DEFAULT, 0,0));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormShort("Numerator",  &Numerator,  wxFORM_DEFAULT, 0,0));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormShort("Denomiator", &Denomiator, wxFORM_DEFAULT, 0,0));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormMessage("Supported Denomiators: 2,4,8,16,32"));
  AssociatePanel(panel);
}


void tEventWin::MenMeterChange()
{
  tMeterChangeDlg *dlg;
  if (DialogBox)
  {
    DialogBox->Show(TRUE);
    return;
  }
  DialogBox = new wxDialogBox(this, "MeterChange", FALSE );
  dlg = new tMeterChangeDlg(this);
  dlg->EditForm(DialogBox);
  DialogBox->Fit();
  DialogBox->Show(TRUE);
}

