
/*
**  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 "maction.h"

// -----------------------------------------------------------------
// tMouseMapper - map mouse button to Command-ID
// -----------------------------------------------------------------

tMouseMapper::tMouseMapper(const int a[12])
{
  for (int i = 0; i < 12; i++)
    actions[i] = a[i];
}

tMouseMapper::tMouseMapper()
{
  for (int i = 0; i < 12; i++)
    actions[i] = 0;
}

void tMouseMapper::SetAction(int code, Button but, Bool shift, Bool ctrl)
{
  int i = 0;
  switch (but)
  {
    case Left:
      i = 0;
      break;
    case Middle:
      i = 1;
      break;
    case Right:
      i = 2;
      break;
  }
  if (shift)
    i += 3;
  if (ctrl)
    i += 6;
  actions[i] = code;
}

int tMouseMapper::Action(wxMouseEvent &e)
{
  if (!e.ButtonDown())
    return 0;

  int i = 0;	// left down
  if (e.MiddleDown())
    i = 1;
  else
  if (e.RightDown())
    i = 2;

  if (e.ShiftDown())
    i += 3;
  if (e.ControlDown())
    i += 6;
  return actions[i];
}

// -----------------------------------------------------------------
// tSelection - Rechteck mit Maus aufziehen
// -----------------------------------------------------------------

tSelection::tSelection(wxCanvas *canvas)
{
  Canvas = canvas;
  dc = canvas->GetDC();
  Active = 0;
  Selected = 0;
}


int tSelection::LeftDown(wxMouseEvent &e)
{
  if (!Active)
  {
    float x, y;
    Active = 1;
    dc = Canvas->GetDC();
    dc->SetBrush(wxBLACK_BRUSH);
    dc->SetLogicalFunction(wxXOR);
    if (Selected && e.ShiftDown())
    {
      // Continue selection
      tRect rr = r;
      rr.SetNormal();
      if (rr.w && rr.h)
	dc->DrawRectangle(rr.x, rr.y, rr.w, rr.h);
      Dragging(e);
    }
    else
    {
      Selected = 0;
      e.Position(&x, &y);
      Snap(x, y, 0);
      r.x = x;
      r.y = y;
      r.w = 1;
      r.h = 1;
      dc->DrawRectangle(r.x, r.y, r.w, r.h);
    }
    dc->SetLogicalFunction(wxCOPY);
  }
  return 0;
}


int tSelection::Dragging(wxMouseEvent &e)
{
  if (!Active)
    LeftDown(e);

  if (Active)
  {
    float x, y;
    tRect r1, r2;

    e.Position(&x, &y);
    Snap(x, y, 1);

    r1 = r;
    r1.SetNormal();

    r.w = x - r.x;
    r.h = y - r.y;

    r2 = r;
    r2.SetNormal();
    if (r1.x != r2.x || r1.y != r2.y || r1.w != r2.w || r1.h != r2.h)
    {
      dc->SetLogicalFunction(wxXOR);
      if (r1.w && r1.h)
	dc->DrawRectangle(r1.x, r1.y, r1.w, r1.h);
      if (r2.w && r2.h)
	dc->DrawRectangle(r2.x, r2.y, r2.w, r2.h);
      dc->SetLogicalFunction(wxCOPY);
    }
  }
  return 0;
}


int tSelection::LeftUp(wxMouseEvent &e)
{
  if (Active)
  {
    r.SetNormal();
    dc->SetLogicalFunction(wxXOR);
    if (r.w && r.h)
      dc->DrawRectangle(r.x, r.y, r.w, r.h);
    dc->SetLogicalFunction(wxCOPY);
    Active = 0;
    Selected =  (r.w > 3 && r.h > 3);
    return 1;
  }
  return 0;
}


void tSelection::Draw()
{
  if (Selected)
  {
    tRect rr = r;
    dc->SetLogicalFunction(wxXOR);
    dc->SetBrush(wxBLACK_BRUSH);
    rr.SetNormal();
    if (rr.w && rr.h)
      dc->DrawRectangle(rr.x, rr.y, rr.w, rr.h);
    dc->SetLogicalFunction(wxCOPY);
  }
}


void tSelection::Draw(long x, long y, long w, long h)
{
  if (Selected)
  {
    dc->SetClippingRegion(x, y, x+w, y+h);
    Draw();
    dc->DestroyClippingRegion();
  }
}

void tSelection::Select(tRect &rr, long x, long y, long w, long h)
{
  // clear old rectangle
  Draw(x, y, w, h);
  // make new one
  r = rr;
  Selected = 1;
  Draw(x, y, w, h);
}

// ***********************************************************************
// tSnapSelection
// ***********************************************************************




static void SnapVec(long &x, long *Coords, int nCoords, int up)
{
  int i;
  for (i = 0; i < nCoords; i++)
  {
    if (Coords[i] > x)
    {
      if (up || i == 0)
	x = Coords[i];
      else
        x = Coords[i-1];
      return;
    }
  }
  x = Coords[nCoords - 1];
}


static void SnapMod(long &x, long Min, long Max, long Step, int up)
{
  if (x <= Min)
  {
    x = Min;
    return;
  }
  if (x >= Max)
  {
    x = Max;
    return;
  }
  x -= (x - Min) % Step;
  if (up)
    x += Step;
}



void tSnapSelection::Snap(float &fx, float &fy, int drag)
{
  long x = (long)fx;
  long y = (long)fy;
  if (xCoords)
    SnapVec(x, xCoords, nxCoords, drag);
  else if (xStep)
    SnapMod(x, xMin, xMax, xStep, drag);

  if (yCoords)
    SnapVec(y, yCoords, nyCoords, drag);
  else if (yStep)
    SnapMod(y, yMin, yMax, yStep, drag);
  fx = x;
  fy = y;
}


tSnapSelection::tSnapSelection(wxCanvas *c)
  : tSelection(c)
{
  xCoords = 0;
  yCoords = 0;
  xStep = yStep = 0;
}

void tSnapSelection::SetXSnap(long nx, long *cx)
{
  xCoords = cx;
  nxCoords = nx;
  xStep = 0;
}

void tSnapSelection::SetYSnap(long ny, long *cy)
{
  yCoords = cy;
  nyCoords = ny;
  yStep = 0;
}

void tSnapSelection::SetXSnap(long xmin, long xmax, long xstep)
{
  xMin = xmin;
  xMax = xmax;
  xStep = xstep;
  xCoords = 0;
}

void tSnapSelection::SetYSnap(long ymin, long ymax, long ystep)
{
  yMin = ymin;
  yMax = ymax;
  yStep = ystep;
  yCoords = 0;
}


// *************************************************************************
// tMouseCounter
// *************************************************************************

tMouseCounter::tMouseCounter(wxDC *ddc, tRect *rr, int val, int min, int max, int wait)
{
  dc = ddc;
  r  = *rr;
  Value = val;
  Min = min;
  Max = max;
  Timeout = 500;
  Wait = wait;
}


int tMouseCounter::LeftDown(wxMouseEvent &e)
{
  Delta = e.ShiftDown() ? 10 : 1;
  Start(Timeout);
  if (Wait)
    ShowValue();
  else
    Notify();
  return 0;
}

int tMouseCounter::LeftUp(wxMouseEvent &e)
{
  Stop();
  return 1;
}

int tMouseCounter::RightDown(wxMouseEvent &e)
{
  Delta = e.ShiftDown() ? -10 :  -1;
  Start(Timeout);
  if (Wait)
    ShowValue();
  else
    Notify();
  return 0;
}


int tMouseCounter::RightUp(wxMouseEvent &e)
{
  Stop();
  return 1;
}


void tMouseCounter::Notify()
{
  Value += Delta;
  if (Value > Max)
    Value = Max;
  if (Value < Min)
    Value = Min;
  ShowValue();
  if (Timeout > 50)
  {
    Stop();
    Timeout >>= 1;
    Start(Timeout);
  }
}


void tMouseCounter::ShowValue()
{
  char buf[20];
  dc->SetBrush(wxWHITE_BRUSH);
  dc->SetPen(wxTRANSPARENT_PEN);
  if (r.w && r.h)
    dc->DrawRectangle((int)r.x, (int)r.y, (int)r.w, (int)r.h);
  dc->SetPen(wxBLACK_PEN);
  sprintf(buf, "%3d", Value);
  dc->DrawText(buf, (int)r.x, (int)r.y);
}

// -------------------------------------------------------------------------
// tMarkDestin
// -------------------------------------------------------------------------


tMarkDestin::tMarkDestin(wxCanvas *canvas, wxFrame *frame, int left)
{
  wxCursor *c;
  Canvas = canvas;
  Frame  = frame;
  if (left)
    c = new wxCursor(wxCURSOR_POINT_LEFT);
  else
    c = new wxCursor(wxCURSOR_POINT_RIGHT);
  Canvas->SetCursor(c);
  Aborted = 1;
  //Frame->SetStatusText("Click Destination point");
}

int tMarkDestin::ButtonDown(wxMouseEvent &e)
{
  wxCursor *c = new wxCursor(wxCURSOR_ARROW);
  Canvas->SetCursor(c);
  e.Position(&x, &y);
  return 1;
}

int tMarkDestin::RightDown(wxMouseEvent &e)
{
  ButtonDown(e);
  Aborted = 1;
  //Frame->SetStatusText("Operation aborted");
  return 1;
}

int tMarkDestin::LeftDown(wxMouseEvent &e)
{
  ButtonDown(e);
  Aborted = 0;
  //Frame->SetStatusText("");
  return 1;
}

