/*
---------------------------------------------------------------------------------------------
			MIDI Sequencer - Final Year Project, A.J. Green
---------------------------------------------------------------------------------------------

File Name:	PianoRoll.c

Description:	Code to manage piano roll style subwindows for display tracks in the MIDI 
		file.

Author:		AJG

History:

Update	Date		Programmer	Comments
======	====		==========	========
001	15/03/94	AJG		File Created.

--------------------------------------------------------------------------------------------
*/


#include <MidiXInclude.h>
#include <MidiFile.h>
#include <MidiErrorHandler.h>

#include "Globals.h"
#include "Consts.h"
#include "Main.h"
#include "PianoRoll.h"
#include "PianoRollMenu.h"
#include "EventListMenu.h"
#include "Parse.h"
#include "Menu.h"
#include "Clipboard.h"
#include "Undo.h"

#include <Debug.h>

/********************************/
/* Private function prototypes. */
/********************************/

void Midi_PianoRollDrawBackground(PRWindowList ParentWindow);
void Midi_PianoRollDrawNotes(PRWindowList ParentWindow);

PRWindowList	MIDIPianoRollWindows = NULL;

extern char **TrackListEntries;

short Midi_NoteSpacingTable[] = { 0,1,0,1,1,0,1,0,1,0,1,1 };
short Midi_NoteSharpsTable[]  = { 0,1,0,1,0,0,1,0,1,0,1,0 };

int Midi_PianoRollCalculateNoteYPos(byte Note)
{
int 	Octave;
int 	PitchMod;
int 	Counter;
int 	YReturn;
int	YCalc;

BEGIN("Midi_PianoRollCalculateNoteYPos");

	Octave   = Note / 12;
	PitchMod = Note % 12;

	Counter = 0;
	YCalc   = 0;

	while(PitchMod)
	{
		YCalc -= PIANO_ROLL_STAVE_SPACING * Midi_NoteSpacingTable[Counter++];
		--PitchMod;
	}

	YCalc = YCalc / 2 + PIANO_ROLL_MIDDLE_C_YPOS - (Octave - 5) * PIANO_ROLL_OCTAVE_YSIZE;

	YReturn = YCalc + 2;

RETURN_INT(YReturn);
}

void Midi_PianoRollSetupGCs(PRWindowList NewWindow)
{
XGCValues GCSetupBuffer;
unsigned long   GCMask = GCFunction | GCPlaneMask | 
                         GCFillStyle | GCStipple |
                         GCLineWidth;

BEGIN("Midi_PianoRollSetupGCs");

  /* cc, 6/96: only create one set of GCs and share, instead of
     creating five per PianoRoll window without ever freeing them */

  if (MIDIPianoRollWindows)
    {
      MIDIPianoRollWindows = (PRWindowList)First(MIDIPianoRollWindows);
      NewWindow->Clear      = MIDIPianoRollWindows->Clear;
      NewWindow->DrawForce  = MIDIPianoRollWindows->DrawForce;
      NewWindow->DrawXor    = MIDIPianoRollWindows->DrawXor;
      NewWindow->LightGrey  = MIDIPianoRollWindows->LightGrey;
      NewWindow->Grey       = MIDIPianoRollWindows->Grey;
    }
  else
    {

      XGetGCValues(XtDisplay(topLevel), 
		   DefaultGCOfScreen(XtScreen(topLevel)), 
		   GCMask,
		   &GCSetupBuffer);

      GCSetupBuffer.line_width = 0;
      GCSetupBuffer.stipple    = LightGrey;

      NewWindow->Clear = YCreateGC(GCMask, &GCSetupBuffer, NoShade, True);

      GCSetupBuffer.function = GXcopy;
    
      NewWindow->DrawForce = YCreateGC(GCMask, &GCSetupBuffer, NoShade, False);
    
      GCSetupBuffer.function = GXxor;
    
      NewWindow->DrawXor = YCreateGC(GCMask, &GCSetupBuffer, NoShade, False);
    
      GCSetupBuffer.function   = GXcopy;
      GCSetupBuffer.fill_style = FillOpaqueStippled;
      
      NewWindow->LightGrey = YCreateGC(GCMask, &GCSetupBuffer, NoShade, False);
      
      GCSetupBuffer.stipple = Grey;
      
      NewWindow->Grey = YCreateGC(GCMask, &GCSetupBuffer, NoShade, False);
    }

END;
}

static XExposeEvent event = { Expose, 0L, True, NULL, NULL, 0, 0, 300, 200, 0 };

void Midi_PianoRollScrollbarSetThumbPos(PRWindowList);

void Midi_PianoRollForwardCB(Widget w, XtPointer a, XtPointer b)
{
PRWindowList ParentWindow;

BEGIN("Midi_PianoRollForwardCB");

	ParentWindow = Midi_PRGetWindowFromWidget(w);

	if (ParentWindow->BarNumber * 4 * MIDIHeaderBuffer.Timing.Division >= 
	    ParentWindow->LastEvtTime) END;

	++ParentWindow->BarNumber;

	Midi_PianoRollDrawBackground(ParentWindow);
	Midi_PianoRollDrawNotes(ParentWindow);

	event.display = XtDisplay(ParentWindow->PianoRollLabel);
	event.window  =  XtWindow(ParentWindow->PianoRollLabel);

	XSendEvent(XtDisplay(ParentWindow->PianoRollLabel),
		   XtWindow(ParentWindow->PianoRollLabel),  False,
		   ExposureMask, (XEvent *)&event);

	Midi_PianoRollScrollbarSetThumbPos(ParentWindow);
END;
}


void Midi_PianoRollBackwardCB(Widget w, XtPointer a, XtPointer b)
{
PRWindowList ParentWindow;

BEGIN("Midi_PianoRollBackwardCB");

	ParentWindow = Midi_PRGetWindowFromWidget(w);

	if (ParentWindow->BarNumber == 0) END;

	--ParentWindow->BarNumber;

	Midi_PianoRollDrawBackground(ParentWindow);
	Midi_PianoRollDrawNotes(ParentWindow);

	event.display = XtDisplay(ParentWindow->PianoRollLabel);
	event.window  =  XtWindow(ParentWindow->PianoRollLabel);

	XSendEvent(XtDisplay(ParentWindow->PianoRollLabel),
		   XtWindow(ParentWindow->PianoRollLabel),  False,
		   ExposureMask, (XEvent *)&event);

	Midi_PianoRollScrollbarSetThumbPos(ParentWindow);
END;
}


void Midi_PianoRollFfwdCB(Widget w, XtPointer a, XtPointer b)
{
PRWindowList ParentWindow;
int	     FfwdDist;

BEGIN("Midi_PianoRollFfwdCB");

	ParentWindow = Midi_PRGetWindowFromWidget(w);

	FfwdDist = (ParentWindow->PianoRollWidth / PIANO_ROLL_DEFAULT_BAR_WIDTH);

	ParentWindow->BarNumber  += FfwdDist;

	if (ParentWindow->BarNumber * 4 * MIDIHeaderBuffer.Timing.Division >= 
	    ParentWindow->LastEvtTime)
	{
		ParentWindow->BarNumber -= FfwdDist;
		END;
	}

	Midi_PianoRollDrawBackground(ParentWindow);
	Midi_PianoRollDrawNotes(ParentWindow);

	event.display = XtDisplay(ParentWindow->PianoRollLabel);
	event.window  =  XtWindow(ParentWindow->PianoRollLabel);

	XSendEvent(XtDisplay(ParentWindow->PianoRollLabel),
		   XtWindow(ParentWindow->PianoRollLabel),  False,
		   ExposureMask, (XEvent *)&event);

	Midi_PianoRollScrollbarSetThumbPos(ParentWindow);
END;
}


void Midi_PianoRollRewindCB(Widget w, XtPointer a, XtPointer b)
{
PRWindowList ParentWindow;

BEGIN("Midi_PianoRollRewindCB");

	ParentWindow = Midi_PRGetWindowFromWidget(w);

	ParentWindow->BarNumber -= (ParentWindow->PianoRollWidth / PIANO_ROLL_DEFAULT_BAR_WIDTH);

	if (ParentWindow->BarNumber < 0) ParentWindow->BarNumber = 0;

	Midi_PianoRollDrawBackground(ParentWindow);
	Midi_PianoRollDrawNotes(ParentWindow);

	event.display = XtDisplay(ParentWindow->PianoRollLabel);
	event.window  =  XtWindow(ParentWindow->PianoRollLabel);

	XSendEvent(XtDisplay(ParentWindow->PianoRollLabel),
		   XtWindow(ParentWindow->PianoRollLabel),  False,
		   ExposureMask, (XEvent *)&event);

	Midi_PianoRollScrollbarSetThumbPos(ParentWindow);
END;
}


void Midi_PianoRollScrollbarInit(PRWindowList ParentWindow)
{
float	  ThumbSize = 1.0;
Arg	  arg;
XtArgVal *ld;

BEGIN("Midi_PianoRollScrollbarInit");

        if (ParentWindow->LastEvtTime > 0) {

	  ThumbSize = ((float)(ParentWindow->PianoRollWidth / PIANO_ROLL_DEFAULT_BAR_WIDTH) *
		       4 * MIDIHeaderBuffer.Timing.Division) / ParentWindow->LastEvtTime;
	} else ThumbSize = 1.0;

	if (ThumbSize < 0) ThumbSize = 0.0;
	if (ThumbSize > 1) ThumbSize = 1.0;

	if (sizeof(float) > sizeof(XtArgVal)) 
	{
		XtSetArg(arg, XtNshown, &ThumbSize);
	}
	else 
	{
    		ld = (XtArgVal *)&ThumbSize;
    		XtSetArg(arg, XtNshown, *ld);
	}

	XtSetValues(ParentWindow->Scrollbar, &arg, 1);


END;
}

void Midi_PianoRollScrollbarSetThumbPos(PRWindowList ParentWindow)
{
Arg 	  arg;
XtArgVal *ld;
float	  Top;

BEGIN("Midi_PianoRollScrollbarSetThumbPos");

        if (ParentWindow->LastEvtTime > 0) {

	  Top = ((float)ParentWindow->BarNumber * (4 * MIDIHeaderBuffer.Timing.Division) / 
		 ParentWindow->LastEvtTime);

	} else Top = 0;

	if (sizeof(float) > sizeof(XtArgVal)) XtSetArg(arg, XtNtopOfThumb, &Top);
	else
	{
		ld = (XtArgVal *)&Top;
		XtSetArg(arg, XtNtopOfThumb, *ld);
	}

	XtSetValues(ParentWindow->Scrollbar, &arg, 1);


	ParentWindow->Dragging 	     = False;
	ParentWindow->DragStartCoord = 0;
	ParentWindow->DragEndCoord   = 0;

END;
}

void Midi_PianoRollScrollbarJumpCB(Widget w, XtPointer a, XtPointer CBdata)
{
float 		position;
PRWindowList	ParentWindow;
int		BarNumber;

BEGIN("Midi_PianoRollScrollbarJumpCB");

	ParentWindow = Midi_PRGetWindowFromWidget(w);

	position = *((float *)CBdata);

	
	BarNumber = position * ParentWindow->LastEvtTime / (4 * MIDIHeaderBuffer.Timing.Division);

	if (ParentWindow->BarNumber == BarNumber) END;

	ParentWindow->BarNumber = BarNumber;

	Midi_PianoRollDrawBackground(ParentWindow);
	Midi_PianoRollDrawNotes(ParentWindow);

	event.display = XtDisplay(ParentWindow->PianoRollLabel);
	event.window  =  XtWindow(ParentWindow->PianoRollLabel);

	XSendEvent(XtDisplay(ParentWindow->PianoRollLabel),
		   XtWindow(ParentWindow->PianoRollLabel),  False,
		   ExposureMask, (XEvent *)&event);
END;
}



void Midi_PianoRollScrollbarScrollCB(Widget w, XtPointer a, XtPointer CBdata)
{
PRWindowList	ParentWindow;
Dimension 	Length;
int	  	Delta;
int		Pos;

BEGIN("Midi_PianoRollScrollbarScrollCB");

	ParentWindow = Midi_PRGetWindowFromWidget(w);

	if (!ParentWindow) END;

	Pos = (int)CBdata;

	YGetValue(ParentWindow->Scrollbar, XtNlength, &Length);

	Delta = (ParentWindow->PianoRollWidth / PIANO_ROLL_DEFAULT_BAR_WIDTH) * -((float)Pos / Length);

	ParentWindow->BarNumber += Delta;

	if (ParentWindow->BarNumber < 0) ParentWindow->BarNumber = 0;

	if (ParentWindow->BarNumber * 4 * MIDIHeaderBuffer.Timing.Division >= 
	    ParentWindow->LastEvtTime)
	{
		ParentWindow->BarNumber = ParentWindow->LastEvtTime / (4 * MIDIHeaderBuffer.Timing.Division);
	}

	Midi_PianoRollScrollbarSetThumbPos(ParentWindow);
	Midi_PianoRollDrawBackground(ParentWindow);
	Midi_PianoRollDrawNotes(ParentWindow);

	event.display = XtDisplay(ParentWindow->PianoRollLabel);
	event.window  =  XtWindow(ParentWindow->PianoRollLabel);

	XSendEvent(XtDisplay(ParentWindow->PianoRollLabel),
		   XtWindow(ParentWindow->PianoRollLabel),  False,
		   ExposureMask, (XEvent *)&event);

	ParentWindow->Dragging 	     = False;
	ParentWindow->DragStartCoord = 0;
	ParentWindow->DragEndCoord   = 0;


END;
}


	




void Midi_PianoRollDrawSelectionBars(PRWindowList ParentWindow, GC drawGC)
{
Dimension LabelHeight;
Dimension Offset;

BEGIN("Midi_PianoRollDrawSelectionBars");

	if (!ParentWindow->DragStartCoord && !ParentWindow->DragEndCoord) END;

	Offset = 0;
	YGetValue(ParentWindow->PianoRollLabel, XtNheight, &LabelHeight);

	if (LabelHeight > PIANO_ROLL_PIXMAP_HEIGHT)
	{
		Offset = (LabelHeight - PIANO_ROLL_PIXMAP_HEIGHT) / 2;
	}

	if (IsMono && (ParentWindow->DragStartCoord != ParentWindow->DragEndCoord))
	{
		XDrawLine(display, ParentWindow->LabelPixmap, ParentWindow->DrawXor,
			  ParentWindow->DragStartCoord, 4, ParentWindow->DragEndCoord, 4);

		XDrawLine(display, ParentWindow->LabelPixmap, ParentWindow->DrawXor,
			  ParentWindow->DragEndCoord, 4, 
			  ParentWindow->DragEndCoord, PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, ParentWindow->LabelPixmap, ParentWindow->DrawXor,
			  ParentWindow->DragEndCoord, PIANO_ROLL_PIXMAP_HEIGHT - 4,
			  ParentWindow->DragStartCoord, PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, ParentWindow->LabelPixmap, ParentWindow->DrawXor,
			  ParentWindow->DragStartCoord, PIANO_ROLL_PIXMAP_HEIGHT - 4,
			  ParentWindow->DragStartCoord, 4);


		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), ParentWindow->DrawXor,
			  ParentWindow->DragStartCoord, Offset + 4, 
			  ParentWindow->DragEndCoord,   Offset + 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), ParentWindow->DrawXor,
			  ParentWindow->DragEndCoord, Offset + 4, 
			  ParentWindow->DragEndCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), ParentWindow->DrawXor,
			  ParentWindow->DragEndCoord,   Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4,
			  ParentWindow->DragStartCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), ParentWindow->DrawXor,
			  ParentWindow->DragStartCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4,
			  ParentWindow->DragStartCoord, Offset + 4);

		END;
	}


	else if (ParentWindow->DragStartCoord != ParentWindow->DragEndCoord)
	{

		XDrawLine(display, ParentWindow->LabelPixmap, drawGC, 
			  ParentWindow->DragStartCoord, 8, ParentWindow->DragStartCoord, 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC, 
			  ParentWindow->DragStartCoord, 8, ParentWindow->DragStartCoord, 4);

		XDrawLine(display, ParentWindow->LabelPixmap, drawGC, 
			  ParentWindow->DragStartCoord, 4, ParentWindow->DragEndCoord, 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC, 
			  ParentWindow->DragStartCoord, 4, ParentWindow->DragEndCoord, 4);

		XDrawLine(display, ParentWindow->LabelPixmap, drawGC, 
			  ParentWindow->DragEndCoord, 8, ParentWindow->DragEndCoord, 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC, 
			  ParentWindow->DragEndCoord, 8, ParentWindow->DragEndCoord, 4);


		XDrawLine(display, ParentWindow->LabelPixmap, drawGC, 
			  ParentWindow->DragStartCoord, PIANO_ROLL_PIXMAP_HEIGHT - 8, 
	       		  ParentWindow->DragStartCoord, PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC, 
			  ParentWindow->DragStartCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 8, 
			  ParentWindow->DragStartCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, ParentWindow->LabelPixmap, drawGC, 
			  ParentWindow->DragStartCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4,
			  ParentWindow->DragEndCoord,   Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC, 
			  ParentWindow->DragStartCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4,
			  ParentWindow->DragEndCoord,   Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, ParentWindow->LabelPixmap, drawGC, 
			  ParentWindow->DragEndCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 8,
			  ParentWindow->DragEndCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC, 
			  ParentWindow->DragEndCoord, PIANO_ROLL_PIXMAP_HEIGHT - 8, 
			  ParentWindow->DragEndCoord, PIANO_ROLL_PIXMAP_HEIGHT - 4);
	}
	else
	{
		XDrawLine(display, ParentWindow->LabelPixmap, drawGC,
			  ParentWindow->DragStartCoord, 8, ParentWindow->DragStartCoord - 4, 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC,
			  ParentWindow->DragStartCoord,     Offset + 8, 
			  ParentWindow->DragStartCoord - 4, Offset + 4);

		XDrawLine(display, ParentWindow->LabelPixmap, drawGC,
			  ParentWindow->DragStartCoord, 8, ParentWindow->DragStartCoord + 4, 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC,
			  ParentWindow->DragStartCoord,     Offset + 8, 
			  ParentWindow->DragStartCoord + 4, Offset + 4);



		XDrawLine(display, ParentWindow->LabelPixmap, drawGC,
			  ParentWindow->DragStartCoord, PIANO_ROLL_PIXMAP_HEIGHT - 8,
			  ParentWindow->DragStartCoord - 4, PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC,
			  ParentWindow->DragStartCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 8,
			  ParentWindow->DragStartCoord - 4, Offset + PIANO_ROLL_PIXMAP_HEIGHT -  4);

		XDrawLine(display, ParentWindow->LabelPixmap, drawGC,
			  ParentWindow->DragStartCoord, PIANO_ROLL_PIXMAP_HEIGHT - 8,
			  ParentWindow->DragStartCoord + 4, PIANO_ROLL_PIXMAP_HEIGHT - 4);

		XDrawLine(display, XtWindow(ParentWindow->PianoRollLabel), drawGC,
			  ParentWindow->DragStartCoord, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 8,
 			  ParentWindow->DragStartCoord + 4, Offset + PIANO_ROLL_PIXMAP_HEIGHT - 4);
	}

END;
}


void Midi_PRCheckResizeCB(Widget w, XtPointer ClientData, XEvent *e, Boolean *cont)
{
PRWindowList ParentWindow;
Dimension    CurrentWidth;
Dimension    CurrentHeight;

BEGIN("Midi_PRCheckResizeCB");

	ParentWindow = (PRWindowList)ClientData;

	YGetValue(ParentWindow->PianoRollLabel, XtNwidth,  &CurrentWidth);
	YGetValue(ParentWindow->PianoRollLabel, XtNheight, &CurrentHeight);

	if (CurrentWidth  == ParentWindow->PianoRollWidth &&
	    CurrentHeight == ParentWindow->PianoRollHeight) 
	{
		END;
	}

	if (CurrentWidth != ParentWindow->PianoRollWidth)
	{

		XFreePixmap(XtDisplay(ParentWindow->PianoRollLabel), ParentWindow->LabelPixmap);

		ParentWindow->PianoRollWidth  = CurrentWidth;

		ParentWindow->LabelPixmap = XCreatePixmap(XtDisplay(ParentWindow->PianoRollLabel), 
		      			 	          RootWindowOfScreen(XtScreen(ParentWindow->PianoRollLabel)),
		      				          ParentWindow->PianoRollWidth,
		      				          PIANO_ROLL_PIXMAP_HEIGHT,
		      				          DefaultDepthOfScreen(XtScreen(ParentWindow->PianoRollLabel)));

	}

	ParentWindow->PianoRollHeight = CurrentHeight;

	Midi_PianoRollDrawBackground(ParentWindow);
	Midi_PianoRollDrawNotes(ParentWindow);
	YSetValue(ParentWindow->PianoRollLabel, XtNbitmap, ParentWindow->LabelPixmap);

	ParentWindow->Dragging 	     = False;
	ParentWindow->DragStartCoord = 0;
	ParentWindow->DragEndCoord   = 0;

END;
}


void Midi_PianoRollButtonPressCB(Widget w, XtPointer clientData, XEvent *e, Boolean *cont)
{
PRWindowList  ParentWindow;
XButtonEvent *ButtonEvent;

BEGIN("Midi_PianoRollButtonPressCB");
		
	ParentWindow = (PRWindowList)clientData;
	ButtonEvent = (XButtonEvent *)e;

	if (ParentWindow->Dragging || ButtonEvent->type != ButtonPress || ButtonEvent->button != Button1) END;

	Midi_PianoRollDrawSelectionBars(ParentWindow, ParentWindow->Clear);

	ParentWindow->Dragging = True;	

	ParentWindow->DragStartCoord = ButtonEvent->x;
	ParentWindow->DragEndCoord   = ButtonEvent->x;

	Midi_PianoRollDrawSelectionBars(ParentWindow, ParentWindow->DrawForce);

END;
}


void Midi_PianoRollButtonReleaseCB(Widget w, XtPointer clientData, XEvent *e, Boolean *cont)
{
PRWindowList  ParentWindow;
XButtonEvent *ButtonEvent;

BEGIN("Midi_PianoRollButtonReleaseCB");

	ButtonEvent  = (XButtonEvent *)e;
	ParentWindow = (PRWindowList)clientData;

	if (!ParentWindow->Dragging || ButtonEvent->type != ButtonRelease || ButtonEvent->button != Button1) END;

	ParentWindow->Dragging = False;
	ParentWindow->DragEndCoord = ButtonEvent->x;

	if (ParentWindow->DragStartCoord != ParentWindow->DragEndCoord)
	{
		Midi_ELLeaveMenuMode((ELWindowList)ParentWindow,
				     NothingSelectedMode);
	}
	else Midi_ELEnterMenuMode((ELWindowList)ParentWindow,
				  NothingSelectedMode);
END;
}



void Midi_PianoRollPointerMotionCB(Widget w, XtPointer clientData, XEvent *e, Boolean *cont)
{
PRWindowList  ParentWindow;
XMotionEvent *MotionEvent;

BEGIN("Midi_PianoRollPointerMotionCB");

	MotionEvent  = (XMotionEvent *)e;
	ParentWindow = (PRWindowList)clientData;

	if (!ParentWindow->Dragging) END;

	Midi_PianoRollDrawSelectionBars(ParentWindow, ParentWindow->Clear);

	ParentWindow->DragEndCoord = MotionEvent->x;

	Midi_PianoRollDrawSelectionBars(ParentWindow, ParentWindow->DrawForce);

END;
}


void Midi_PianoRollQuitAction(Widget w, XEvent *e, String *s, Cardinal *c)
{
  PRWindowList windows;
  Begin("Midi_PianoRollQuitAction");

  windows = MIDIPianoRollWindows;

  while (windows) {
    if (windows->Shell == w) break;
    windows = (PRWindowList)Next(windows);
  }

  if (!windows) End;

  Midi_RemovePianoRollWindow(windows->TrackNum);
  End;
}


Widget Midi_PianoRollLocateWindowShell(int TrackNum)
{
PRWindowList Windows;
BEGIN("Midi_PianoRollLocateWindowShell");

    Windows = MIDIPianoRollWindows;

    while(Windows)
      {
	if (TrackNum == Windows->TrackNum)
	  {
	    RETURN_PTR(Windows->Shell);
	  }
	
	Windows = (PRWindowList)Next(Windows);
      }

RETURN_PTR(NULL);
} 



void Midi_PianoRollWindowCreate(int TrackNum)
{
PRWindowList NewWindow;
Dimension    h1, h2;
char	     TitleBuffer[256];
Atom	     WmProtocols;

Widget	     BottomBox,
	     HelpButton,
	     RewindButton,
	     BackButton,
	     ForwardButton,
	     FfwdButton;

Widget       WindowShell;

Widget	     ViewScroll;

BEGIN("Midi_PianoRollWindowCreate");

	WindowShell = Midi_PianoRollLocateWindowShell(TrackNum);
	if (WindowShell) {
	  XMapRaised(XtDisplay(WindowShell), XtWindow(WindowShell));
	  END;
	}

	NewWindow = (PRWindowList)NewList(sizeof(PRWindowListElt));

	if (NewWindow == NULL)
	{
		Error(FATAL, "Unable to allocate space for new window.");
	}

	NewWindow->TrackNum   = TrackNum;
	NewWindow->BarNumber  = 0;
	if (MIDITracks[TrackNum]) {
	    NewWindow->LastEvtTime = ((EventList)Last(MIDITracks[TrackNum]))->Event.DeltaTime;
	} else NewWindow->LastEvtTime = 0;

	NewWindow->Shell    = XtAppCreateShell("Rosegarden Sequencer Piano Roll", ProgramName,
					       applicationShellWidgetClass, display, NULL, 0);

	NewWindow->Pane     = YCreateWidget("Piano Roll", 
					    panedWidgetClass, NewWindow->Shell);

	NewWindow->TopBox   = YCreateShadedWidget("Top Box", formWidgetClass,
						  NewWindow->Pane, MediumShade);

	NewWindow->MenuBar  = YCreateShadedWidget("Menu Bar", boxWidgetClass, 
					    	   NewWindow->TopBox, MediumShade);
	NewWindow->HelpBox = YCreateShadedWidget("Help Box", boxWidgetClass,
						 NewWindow->TopBox, MediumShade);
	NewWindow->Toolbar = YCreateToolbar(NewWindow->Pane);

	XtVaSetValues(NewWindow->TopBox, XtNdefaultDistance, 0, NULL);

	XtVaSetValues(NewWindow->MenuBar,
		      XtNleft,   XawChainLeft,   XtNright,  XawChainRight,
		      XtNtop,    XawChainTop,    XtNbottom, XawChainTop,
		      XtNhorizDistance, 0,       XtNvertDistance, 0,
		      XtNborderWidth, 0, NULL);

	XtVaSetValues(NewWindow->HelpBox,
		      XtNfromHoriz, NewWindow->MenuBar, XtNleft, XawChainRight,
		      XtNright,  XawChainRight,  XtNtop, XawChainTop,
		      XtNbottom, XawChainTop,    XtNhorizDistance, 0,
		      XtNvertDistance, 0,        XtNborderWidth, 0, NULL);

	NewWindow->ViewPort = YCreateWidget("View Port", viewportWidgetClass, NewWindow->Pane);

	NewWindow->PianoRollLabel = YCreateWidget("Label", labelWidgetClass, NewWindow->ViewPort);

	NewWindow->Scrollbar = YCreateWidget("Scrollbar", scrollbarWidgetClass, NewWindow->Pane);

	BottomBox = YCreateShadedWidget("Bottom Box", boxWidgetClass, NewWindow->Pane, MediumShade);

	RewindButton  = YCreateSurroundedWidget("Rewind",  repeaterWidgetClass, 
						BottomBox, SurroundShade, NoShade);

	BackButton    = YCreateSurroundedWidget("Back",    repeaterWidgetClass, 
						BottomBox, SurroundShade, NoShade);

	NewWindow->CounterDisplay = YCreateLabel("  0.00", BottomBox);

	ForwardButton = YCreateSurroundedWidget("Forward", repeaterWidgetClass, 
						BottomBox, SurroundShade, NoShade);

	FfwdButton    = YCreateSurroundedWidget("Ffwd",    repeaterWidgetClass, 
						BottomBox, SurroundShade, NoShade);

	XtAddCallback(ForwardButton, XtNcallback, Midi_PianoRollForwardCB,  NULL);
	XtAddCallback(BackButton,    XtNcallback, Midi_PianoRollBackwardCB, NULL);
	XtAddCallback(FfwdButton,    XtNcallback, Midi_PianoRollFfwdCB,     NULL);
	XtAddCallback(RewindButton,  XtNcallback, Midi_PianoRollRewindCB,   NULL);

	XtAddCallback(NewWindow->Scrollbar, XtNscrollProc, Midi_PianoRollScrollbarScrollCB, NULL);
	XtAddCallback(NewWindow->Scrollbar, XtNjumpProc,   Midi_PianoRollScrollbarJumpCB,   NULL);

	XtAddEventHandler(NewWindow->PianoRollLabel, ButtonPressMask, 
			  True, Midi_PianoRollButtonPressCB, NewWindow);

	XtAddEventHandler(NewWindow->PianoRollLabel, ButtonReleaseMask,
			  True, Midi_PianoRollButtonReleaseCB, NewWindow);

	XtAddEventHandler(NewWindow->PianoRollLabel, PointerMotionMask, 
			  True, Midi_PianoRollPointerMotionCB, NewWindow);

	NewWindow->Dragging = False;
	  
	YSetValue(RewindButton,  XtNbitmap, Rewind);
	YSetValue(BackButton,    XtNbitmap, Back);
	YSetValue(ForwardButton, XtNbitmap, Forward);
	YSetValue(FfwdButton,    XtNbitmap, Ffwd);

	sprintf(TitleBuffer, "%s Sequencer Piano Roll - %s", ProgramName, TrackListEntries[TrackNum]);


	YSetValue(NewWindow->Shell, XtNtitle, 	   TitleBuffer);
	YSetValue(NewWindow->Shell, XtNiconName,   TitleBuffer);
	YSetValue(NewWindow->Shell, XtNiconPixmap, RoseMap);
	YSetValue(NewWindow->Shell, XtNiconMask,   RoseMask);

	YSetValue(NewWindow->ViewPort, XtNforceBars,  True);
	YSetValue(NewWindow->ViewPort, XtNallowHoriz, False);
 
	YSetValue(NewWindow->Scrollbar, XtNorientation, XtorientHorizontal);
	YSetValue(NewWindow->Scrollbar, XtNmin, 14);
	YSetValue(NewWindow->Scrollbar, XtNmax, 14);

	NewWindow->TrackMenuButton = YCreateMenuButton("Track", NewWindow->MenuBar);
	NewWindow->EditMenuButton  = YCreateMenuButton("Edit", NewWindow->MenuBar);

	/* removed until we get around to actually implementing it... */
	NewWindow->EventMenuButton = YCreateMenuButton("Event", NewWindow->MenuBar);
	XtUnmanageChild(XtParent(NewWindow->EventMenuButton));

	HelpButton = YCreateCommand("Help", NewWindow->HelpBox);

	XtVaSetValues(NewWindow->MenuBar, XtNleft, XawChainLeft, XtNright,
		      XawChainRight, XtNtop, XawChainTop, XtNbottom,
		      XawChainTop, NULL);

	if (!appData.interlockWindow) YSetValue(HelpButton, XtNsensitive, False);

	XtAddCallback(HelpButton, XtNcallback, Midi_HelpCallback, "Track - Piano Roll");

	Midi_PRInstallMenus(NewWindow);

	XtSetMappedWhenManaged(NewWindow->Shell, False);
  
  	XtRealizeWidget(NewWindow->Shell);
  
  	YGetValue(NewWindow->TrackMenuButton, XtNheight, &h1);
	YGetValue(RewindButton, XtNheight, &h2);
	YGetValue(NewWindow->ViewPort,  XtNwidth,  &NewWindow->PianoRollWidth);
	YGetValue(NewWindow->PianoRollLabel,  XtNheight, &NewWindow->PianoRollHeight);

	XtUnrealizeWidget(NewWindow->Shell);

	YSetValue(NewWindow->PianoRollLabel, XtNwidth, NewWindow->PianoRollWidth);
	YSetValue(NewWindow->ViewPort, XtNallowVert, True);

	XtSetMappedWhenManaged(NewWindow->Shell, True);

	NewWindow->LabelPixmap = XCreatePixmap(XtDisplay(NewWindow->PianoRollLabel), 
		      			       RootWindowOfScreen(XtScreen(NewWindow->PianoRollLabel)),
		      			       NewWindow->PianoRollWidth,
		      			       PIANO_ROLL_PIXMAP_HEIGHT,
		      			       DefaultDepthOfScreen(XtScreen(NewWindow->PianoRollLabel)));

	XtAddEventHandler(NewWindow->PianoRollLabel, ExposureMask, True, Midi_PRCheckResizeCB, NewWindow);

	Midi_PianoRollSetupGCs(NewWindow);
	Midi_PianoRollDrawBackground(NewWindow);
	Midi_PianoRollDrawNotes(NewWindow);

 	YSetValue(NewWindow->TopBox, XtNmin, h1 + 15);
	YSetValue(NewWindow->TopBox, XtNmax, h1 + 15);

 	YSetValue(BottomBox, XtNmin, h2 + 15);
	YSetValue(BottomBox, XtNmax, h2 + 15);

	YSetValue(NewWindow->PianoRollLabel, XtNbitmap, NewWindow->LabelPixmap);

	XtRealizeWidget(NewWindow->Shell);

	WmProtocols = XInternAtom(XtDisplay(NewWindow->Shell),
				  "WM_DELETE_WINDOW", False);
 	XtOverrideTranslations(NewWindow->Shell,
     			       XtParseTranslationTable("<Message>WM_PROTOCOLS: pianoroll-wm-quit()"));
	XSetWMProtocols(XtDisplay(NewWindow->Shell),
			XtWindow(NewWindow->Shell), &WmProtocols, 1);

	if (MIDIPianoRollWindows)
	{
		Nconc(MIDIPianoRollWindows, NewWindow);
	}
	else MIDIPianoRollWindows = NewWindow;

	Midi_PREnterMenuMode(NewWindow, NothingSelectedMode);

	if (Midi_ClipboardIsEmpty())
	{
		Midi_PREnterMenuMode(NewWindow, NothingCutMode);
	}

	if (Midi_UndoIsBufferEmpty())
	{
		Midi_PREnterMenuMode(NewWindow, NothingDoneMode);
	}

	Midi_PianoRollScrollbarInit(NewWindow);

	ViewScroll = XtNameToWidget(NewWindow->ViewPort, "vertical");
	YSetValue(ViewScroll, XtNthumb, LightGrey);
	YSetValue(NewWindow->Scrollbar, XtNthumb, LightGrey);

	NewWindow->DragStartCoord = 0;
	NewWindow->DragEndCoord   = 0;
	NewWindow->SelectStartEvt = NULL;
	NewWindow->SelectEndEvt   = NULL;

	XtInstallAccelerators(NewWindow->ViewPort, NewWindow->Shell);
END;
}






void Midi_PianoRollDrawBackground(PRWindowList ParentWindow)
{
int i, y, Offset;

BEGIN("Midi_PianoRollDrawBackground");

	XFillRectangle(XtDisplay(topLevel), ParentWindow->LabelPixmap,
		       ParentWindow->Clear,
		       0, 0, ParentWindow->PianoRollWidth, PIANO_ROLL_PIXMAP_HEIGHT);

	Offset = 0;

	if (ParentWindow->BarNumber == 0)
	{
		XCopyArea(XtDisplay(topLevel), TrebleClef, ParentWindow->LabelPixmap, 
			  ParentWindow->DrawForce, 0, 0, PIANO_ROLL_CLEF_WIDTH, PIANO_ROLL_CLEF_HEIGHT,
			  0, PIANO_ROLL_TOP_STAVE_OFFSET - 8);

		XCopyArea(XtDisplay(topLevel), BassClef, ParentWindow->LabelPixmap, 
			  ParentWindow->DrawForce, 0, 0, PIANO_ROLL_CLEF_WIDTH, PIANO_ROLL_CLEF_HEIGHT,
			  0, PIANO_ROLL_TOP_STAVE_OFFSET + 6 * PIANO_ROLL_STAVE_SPACING - 8);
		
		Offset = PIANO_ROLL_CLEF_WIDTH + PIANO_ROLL_DEFAULT_BAR_WIDTH;

		XDrawLine(XtDisplay(topLevel), ParentWindow->LabelPixmap, ParentWindow->DrawForce,
			  0, PIANO_ROLL_TOP_STAVE_OFFSET, 0, 
			  PIANO_ROLL_TOP_STAVE_OFFSET + PIANO_ROLL_STAVE_HEIGHT - 1);

		XDrawLine(XtDisplay(topLevel), ParentWindow->LabelPixmap, ParentWindow->DrawForce,
			  0, PIANO_ROLL_TOP_STAVE_OFFSET + 6 * PIANO_ROLL_STAVE_SPACING, 0, 
			  PIANO_ROLL_TOP_STAVE_OFFSET + 6 * PIANO_ROLL_STAVE_SPACING + 
			  PIANO_ROLL_STAVE_HEIGHT - 1);

	}

	/**********************/
	/* Draw Treble Stave. */
	/**********************/

	for(i = 0; i < 5; ++i)
	{
		y = PIANO_ROLL_TOP_STAVE_OFFSET + PIANO_ROLL_STAVE_SPACING * i;

		XDrawLine(XtDisplay(topLevel), ParentWindow->LabelPixmap, ParentWindow->DrawForce,
			  0, y, ParentWindow->PianoRollWidth, y);

		y = PIANO_ROLL_TOP_STAVE_OFFSET + PIANO_ROLL_STAVE_SPACING * (i + 6);

		XDrawLine(XtDisplay(topLevel), ParentWindow->LabelPixmap, ParentWindow->DrawForce,
			  0, y, ParentWindow->PianoRollWidth, y);

	}

	i = Offset;

	while(i < ParentWindow->PianoRollWidth)
	{
		XDrawLine(XtDisplay(topLevel), ParentWindow->LabelPixmap, ParentWindow->DrawForce,
			  i, PIANO_ROLL_TOP_STAVE_OFFSET, i, 
			  PIANO_ROLL_TOP_STAVE_OFFSET + PIANO_ROLL_STAVE_HEIGHT - 1);
		
		XDrawLine(XtDisplay(topLevel), ParentWindow->LabelPixmap, ParentWindow->DrawForce,
			  i, PIANO_ROLL_TOP_STAVE_OFFSET + 6 * PIANO_ROLL_STAVE_SPACING, i, 
			  PIANO_ROLL_TOP_STAVE_OFFSET + 6 * PIANO_ROLL_STAVE_SPACING + 
			  PIANO_ROLL_STAVE_HEIGHT - 1);

		i += PIANO_ROLL_DEFAULT_BAR_WIDTH;
	}

END;
}


void Midi_PianoRollDrawNotes(PRWindowList ParentWindow)
{
EventList CurrEvtPtr;
MIDIEvent CurrentEvent;
int 	  Ypos, Xpos;
int	  NoteBoxLength;
long	  StartDispTime;
long	  EndDispTime;
float	  Resolution;
GC	  NoteShade;
char	  FormatBuffer[16];

BEGIN("Midi_PianoRollDrawNotes");

	CurrEvtPtr = MIDITracks[ParentWindow->TrackNum];

	StartDispTime = ParentWindow->BarNumber * 4 * MIDIHeaderBuffer.Timing.Division;
	EndDispTime   = StartDispTime + 
	  		(ParentWindow->PianoRollWidth / PIANO_ROLL_DEFAULT_BAR_WIDTH + 1) *
			4 * MIDIHeaderBuffer.Timing.Division;

	Resolution = ((float)PIANO_ROLL_DEFAULT_BAR_WIDTH) / (4 * MIDIHeaderBuffer.Timing.Division);

	sprintf(FormatBuffer,"%d.00", (ParentWindow->BarNumber * 4));
	YSetValue(ParentWindow->CounterDisplay, XtNlabel, FormatBuffer);

	/*************************************/
	/* Zip along to the next note event. */
	/*************************************/

	while(CurrEvtPtr)
	{
		if (MessageType(CurrEvtPtr->Event.EventCode) == MIDI_NOTE_ON &&
		    CurrEvtPtr->Event.DeltaTime + CurrEvtPtr->Event.EventData.Note.Duration >= StartDispTime)
		{
			break;
		}
		else CurrEvtPtr = (EventList)Next(CurrEvtPtr);
	}

	if (!CurrEvtPtr) END;

	while (CurrEvtPtr && CurrEvtPtr->Event.DeltaTime < EndDispTime)
	{
		CurrentEvent = &CurrEvtPtr->Event;
	
		Ypos = Midi_PianoRollCalculateNoteYPos(CurrentEvent->EventData.Note.Note);

		Xpos = (CurrentEvent->DeltaTime - StartDispTime) * Resolution;

		if (ParentWindow->BarNumber == 0) Xpos += PIANO_ROLL_CLEF_WIDTH;

		NoteBoxLength = CurrentEvent->EventData.Note.Duration * Resolution;

		if (Midi_NoteSharpsTable[CurrentEvent->EventData.Note.Note % 12])
		{
			NoteShade = ParentWindow->LightGrey;
		}
		else NoteShade = ParentWindow->Grey;

		XFillRectangle(XtDisplay(topLevel), ParentWindow->LabelPixmap,
			       ParentWindow->Clear,
			       Xpos-1, Ypos-1, NoteBoxLength+2, 8);

		XFillRectangle(XtDisplay(topLevel), ParentWindow->LabelPixmap,
			       NoteShade,
			       Xpos, Ypos, NoteBoxLength, 5);

		XDrawRectangle(XtDisplay(topLevel), ParentWindow->LabelPixmap,
			       ParentWindow->DrawForce,
			       Xpos, Ypos, NoteBoxLength, 5); 

		do
		{
			CurrEvtPtr = (EventList)Next(CurrEvtPtr);
		}
		while(CurrEvtPtr && MessageType(CurrEvtPtr->Event.EventCode) != MIDI_NOTE_ON);
	}
END;
}



void Midi_UpdatePianoRollWindow(int TrackNum)
{
PRWindowList SubWindow;
char	     TitleBuffer[256];

BEGIN("Midi_UpdatePianoRollWindow");

	SubWindow = MIDIPianoRollWindows;

	while (SubWindow)
	{
		if (SubWindow->TrackNum == TrackNum)
		{
			SubWindow->DragStartCoord = 0;
			SubWindow->DragEndCoord   = 0;
			SubWindow->SelectStartEvt = NULL;
			SubWindow->SelectEndEvt   = NULL;

			Midi_PianoRollDrawSelectionBars(SubWindow, SubWindow->Clear);
			Midi_PianoRollDrawBackground(SubWindow);
			Midi_PianoRollDrawNotes(SubWindow);
			event.display = XtDisplay(SubWindow->PianoRollLabel);
			event.window  =  XtWindow(SubWindow->PianoRollLabel);

			XSendEvent(XtDisplay(SubWindow->PianoRollLabel),
				   XtWindow(SubWindow->PianoRollLabel),  False,
				   ExposureMask, (XEvent *)&event);

			sprintf(TitleBuffer, "%s Sequencer Piano Roll - %s",
				ProgramName, TrackListEntries[TrackNum]);
			YSetValue(SubWindow->Shell, XtNtitle, 	   TitleBuffer);
			YSetValue(SubWindow->Shell, XtNiconName,   TitleBuffer);
			END;
		}
		SubWindow = (PRWindowList)Next(SubWindow);
	}
END;
}


void Midi_RemovePianoRollWindow(int TrackNum)
{
PRWindowList SubWindow;

BEGIN("Midi_RemovePianoRollWindow");

	SubWindow = MIDIPianoRollWindows;

	while(SubWindow)
	{
	  if (SubWindow->TrackNum == TrackNum)
	    {
	      if (!Prev(SubWindow) && !Next(SubWindow)) {
		XFreeGC(XtDisplay(topLevel), SubWindow->Clear);
		XFreeGC(XtDisplay(topLevel), SubWindow->DrawForce);
		XFreeGC(XtDisplay(topLevel), SubWindow->DrawXor);
		XFreeGC(XtDisplay(topLevel), SubWindow->LightGrey);
		XFreeGC(XtDisplay(topLevel), SubWindow->Grey);
	      }

	      XFreePixmap(XtDisplay(topLevel), SubWindow->LabelPixmap);
	      XtFree(SubWindow->TrackMenu);
	      XtFree(SubWindow->EditMenu);
	      XtFree(SubWindow->EventMenu);
	      YDestroyToolbar(SubWindow->Toolbar);
	      XtDestroyWidget(SubWindow->Shell);
	      MIDIPianoRollWindows = (PRWindowList)First(Remove(SubWindow));
	      break;
	    }
	  SubWindow = (PRWindowList)Next(SubWindow);
	}
END;
}
			
void Midi_PianoRollDeleteAllWindows()
{
BEGIN("Midi_PianoRollDeleteAllWindows");

        if (MIDIPianoRollWindows) {
	  XFreeGC(XtDisplay(topLevel), MIDIPianoRollWindows->Clear);
	  XFreeGC(XtDisplay(topLevel), MIDIPianoRollWindows->DrawForce);
	  XFreeGC(XtDisplay(topLevel), MIDIPianoRollWindows->DrawXor);
	  XFreeGC(XtDisplay(topLevel), MIDIPianoRollWindows->LightGrey);
	  XFreeGC(XtDisplay(topLevel), MIDIPianoRollWindows->Grey);
	}

	while(MIDIPianoRollWindows)
	{
		XFreePixmap(XtDisplay(topLevel), MIDIPianoRollWindows->LabelPixmap);
		XtFree(MIDIPianoRollWindows->TrackMenu);
		XtFree(MIDIPianoRollWindows->EditMenu);
		XtFree(MIDIPianoRollWindows->EventMenu);
		YDestroyToolbar(MIDIPianoRollWindows->Toolbar);
		XtDestroyWidget(MIDIPianoRollWindows->Shell);
		MIDIPianoRollWindows = (PRWindowList)First(Remove(MIDIPianoRollWindows));
	}
END;
}
