/* TrackWindow.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    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.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#include "TrackWindow.h"
#include "Scroll.h"
#include "MainWindowStuff.h"
#include "TrackObject.h"
#include "TrackList.h"
#include "WindowDispatcher.h"
#include "Memory.h"
#include "IconButton.h"
#include "NoteButtonImages.h"
#include "GrowIcon.h"
#include "Main.h"
#include "TrackView.h"
#include "NoteObject.h"
#include "Array.h"
#include "TrackAttributeDialog.h"
#include "CommandChooser.h"
#include "DataMunging.h"
#include "GlobalWindowMenuList.h"
#include "SampleDeviceOutput.h"
#include "Alert.h"
#include "NumberDialog.h"
#include "DiskFileOutput.h"


#define WINDOWXSIZE (500)
#define WINDOWYSIZE (290)

#define BUTTONSXSTART (-1)
#define BUTTONSY (-1)
#define BUTTONWIDTH (24)
#define BUTTONHEIGHT (24)
#define BUTTONINCREMENT (BUTTONWIDTH - 1)
#define BUTTONEXOINCREMENT (3)

#define HSCROLLX (-1)
#define HSCROLLY(WinHeight) ((WinHeight) - 16 + 1)
#define HSCROLLWIDTH(WinWidth) ((WinWidth) + 2 - 15)

#define VSCROLLX(WinWidth) ((WinWidth) - 16 + 1)
#define VSCROLLY (BUTTONSY + BUTTONHEIGHT - 1)
#define VSCROLLHEIGHT(WinHeight) ((WinHeight) - VSCROLLY - 15 + 1)

#define TRACKVIEWX (0)
#define TRACKVIEWY (BUTTONSY + BUTTONHEIGHT)
#define TRACKVIEWWIDTH(WindowWidth) ((WindowWidth) - 15)
#define TRACKVIEWHEIGHT(WindowHeight) ((WindowHeight) - 15 - TRACKVIEWY)


struct TrackWindowRec
	{
		MainWindowRec*					MainWindow;
		TrackObjectRec*					TrackObject;
		TrackListRec*						TrackList;

		/* NoteState contains the flags for a note.  If eCommandFlag bit is set, then */
		/* all of the other bits are disregarded and clicking will ask for a command. */
		/* If it is clear, then the other bits are valid and clicking will create an */
		/* appropriate note. */
		unsigned long						NoteState;
		/* NoteReady is true if NoteState is valid, or False if not */
		MyBoolean								NoteReady;

		WinType*								ScreenID;
		ScrollRec*							HScroll;
		ScrollRec*							VScroll;
		IconButtonRec*					ArrowButton;
		IconButtonRec*					CommandButton;
		IconButtonRec*					SixtyFourthButton;
		IconButtonRec*					ThirtySecondButton;
		IconButtonRec*					SixteenthButton;
		IconButtonRec*					EighthButton;
		IconButtonRec*					QuarterButton;
		IconButtonRec*					HalfButton;
		IconButtonRec*					WholeButton;
		IconButtonRec*					DoubleButton;
		IconButtonRec*					QuadButton;
		IconButtonRec*					SharpButton;
		IconButtonRec*					FlatButton;
		IconButtonRec*					NaturalButton;
		IconButtonRec*					NoteVsRestButton;
		IconButtonRec*					RestVsNoteButton;
		IconButtonRec*					NoDotButton;
		IconButtonRec*					YesDotButton;
		IconButtonRec*					Div1Button;
		IconButtonRec*					Div3Button;
		IconButtonRec*					Div5Button;
		IconButtonRec*					Div7Button;
		TrackViewRec*						TrackView;
		GenericWindowRec*				MyGenericWindow; /* how the window event dispatcher knows us */
		MenuItemType*						MyMenuItem;

		/* for doing fun stuff with the play... menu options */
		MyBoolean								LastMenuOptionKeyDown;
	};


static void							TrackWindowVScrollHook(long Parameter, ScrollType How,
													TrackWindowRec* Window);

static void							TrackWindowHScrollHook(long Parameter, ScrollType How,
													TrackWindowRec* Window);


/* create a new track editing window */
TrackWindowRec*					NewTrackWindow(struct TrackObjectRec* TrackObject,
													struct MainWindowRec* MainWindow,
													struct TrackListRec* TrackList, short WinX, short WinY,
													short WinWidth, short WinHeight)
	{
		TrackWindowRec*				Window;
		OrdType								ButtonX;

		/* deal with window placement */
		if ((WinWidth < 100) || (WinHeight < 100) || ((eOptionKey & CheckModifiers()) != 0))
			{
				WinX = 1 + WindowOtherEdgeWidths(eDocumentWindow);
				WinY = 1 + WindowTitleBarHeight(eDocumentWindow);
				WinWidth = WINDOWXSIZE;
				WinHeight = WINDOWYSIZE;
			}
		MakeWindowFitOnScreen(&WinX,&WinY,&WinWidth,&WinHeight);

		Window = (TrackWindowRec*)AllocPtrCanFail(sizeof(TrackWindowRec),"TrackWindowRec");
		if (Window == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Window->MainWindow = MainWindow;
		Window->TrackObject = TrackObject;
		Window->TrackList = TrackList;

		Window->ScreenID = MakeNewWindow(eDocumentWindow,eWindowClosable,eWindowZoomable,
			eWindowResizable,WinX,WinY,WinWidth,WinHeight,(void (*)(void*))&TrackWindowUpdator,
			Window);
		if (Window->ScreenID == 0)
			{
			 FailurePoint2:
				ReleasePtr((char*)Window);
				goto FailurePoint1;
			}

		Window->HScroll = NewScrollBar(Window->ScreenID,eHScrollBar,HSCROLLX,
			HSCROLLY(WinHeight),HSCROLLWIDTH(WinWidth));
		if (Window->HScroll == NIL)
			{
			 FailurePoint3:
				KillWindow(Window->ScreenID);
				goto FailurePoint2;
			}

		Window->VScroll = NewScrollBar(Window->ScreenID,eVScrollBar,VSCROLLX(WinWidth),
			VSCROLLY,VSCROLLHEIGHT(WinHeight));
		if (Window->VScroll == NIL)
			{
			 FailurePoint4:
				DisposeScrollBar(Window->HScroll);
				goto FailurePoint3;
			}

		ButtonX = BUTTONSXSTART;

		Window->ArrowButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,BUTTONSY,
			BUTTONWIDTH,BUTTONHEIGHT,ArrowButtonBits,ArrowButtonMouseDownBits,
			ArrowButtonSelectedBits,ArrowButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->ArrowButton == NIL)
			{
			 FailurePoint5:
				DisposeScrollBar(Window->VScroll);
				goto FailurePoint4;
			}
		ButtonX += BUTTONINCREMENT + BUTTONEXOINCREMENT;

		Window->CommandButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,CommandButtonBits,CommandButtonMouseDownBits,
			CommandButtonSelectedBits,CommandButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->CommandButton == NIL)
			{
			 FailurePoint5point1:
				DisposeIconButton(Window->ArrowButton);
				goto FailurePoint5;
			}
		ButtonX += BUTTONINCREMENT + BUTTONEXOINCREMENT;

		Window->SixtyFourthButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,SixtyFourthButtonBits,
			SixtyFourthButtonMouseDownBits,SixtyFourthButtonSelectedBits,
			SixtyFourthButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->SixtyFourthButton == NIL)
			{
			 FailurePoint6:
				DisposeIconButton(Window->CommandButton);
				goto FailurePoint5point1;
			}
		ButtonX += BUTTONINCREMENT;

		Window->ThirtySecondButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,ThirtySecondButtonBits,
			ThirtySecondButtonMouseDownBits,ThirtySecondButtonSelectedBits,
			ThirtySecondButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->ThirtySecondButton == NIL)
			{
			 FailurePoint7:
				DisposeIconButton(Window->SixtyFourthButton);
				goto FailurePoint6;
			}
		ButtonX += BUTTONINCREMENT;

		Window->SixteenthButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,SixteenthButtonBits,
			SixteenthButtonMouseDownBits,SixteenthButtonSelectedBits,
			SixteenthButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->SixteenthButton == NIL)
			{
			 FailurePoint8:
				DisposeIconButton(Window->ThirtySecondButton);
				goto FailurePoint7;
			}
		ButtonX += BUTTONINCREMENT;

		Window->EighthButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,EighthButtonBits,
			EighthButtonMouseDownBits,EighthButtonSelectedBits,
			EighthButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->EighthButton == NIL)
			{
			 FailurePoint9:
				DisposeIconButton(Window->SixteenthButton);
				goto FailurePoint8;
			}
		ButtonX += BUTTONINCREMENT;

		Window->QuarterButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,QuarterButtonBits,
			QuarterButtonMouseDownBits,QuarterButtonSelectedBits,
			QuarterButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->QuarterButton == NIL)
			{
			 FailurePoint10:
				DisposeIconButton(Window->EighthButton);
				goto FailurePoint9;
			}
		ButtonX += BUTTONINCREMENT;

		Window->HalfButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,HalfButtonBits,HalfButtonMouseDownBits,
			HalfButtonSelectedBits,HalfButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->HalfButton == NIL)
			{
			 FailurePoint11:
				DisposeIconButton(Window->QuarterButton);
				goto FailurePoint10;
			}
		ButtonX += BUTTONINCREMENT;

		Window->WholeButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,WholeButtonBits,WholeButtonMouseDownBits,
			WholeButtonSelectedBits,WholeButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->WholeButton == NIL)
			{
			 FailurePoint12:
				DisposeIconButton(Window->HalfButton);
				goto FailurePoint11;
			}
		ButtonX += BUTTONINCREMENT;

		Window->DoubleButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,DoubleButtonBits,DoubleButtonMouseDownBits,
			DoubleButtonSelectedBits,DoubleButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->DoubleButton == NIL)
			{
			 FailurePoint13:
				DisposeIconButton(Window->WholeButton);
				goto FailurePoint12;
			}
		ButtonX += BUTTONINCREMENT;

		Window->QuadButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,QuadButtonBits,QuadButtonMouseDownBits,
			QuadButtonSelectedBits,QuadButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->QuadButton == NIL)
			{
			 FailurePoint14:
				DisposeIconButton(Window->DoubleButton);
				goto FailurePoint13;
			}
		ButtonX += BUTTONINCREMENT + BUTTONEXOINCREMENT;

		Window->SharpButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,SharpButtonBits,SharpButtonMouseDownBits,
			SharpButtonSelectedBits,SharpButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->SharpButton == NIL)
			{
			 FailurePoint15:
				DisposeIconButton(Window->QuadButton);
				goto FailurePoint14;
			}
		ButtonX += BUTTONINCREMENT;

		Window->FlatButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,FlatButtonBits,FlatButtonMouseDownBits,
			FlatButtonSelectedBits,FlatButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->FlatButton == NIL)
			{
			 FailurePoint16:
				DisposeIconButton(Window->SharpButton);
				goto FailurePoint15;
			}
		ButtonX += BUTTONINCREMENT;

		Window->NaturalButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,NaturalButtonBits,NaturalButtonMouseDownBits,
			NaturalButtonSelectedBits,NaturalButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->NaturalButton == NIL)
			{
			 FailurePoint17:
				DisposeIconButton(Window->FlatButton);
				goto FailurePoint16;
			}
		ButtonX += BUTTONINCREMENT + BUTTONEXOINCREMENT;

		Window->NoteVsRestButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,NoteVsRestButtonBits,
			NoteVsRestButtonMouseDownBits,NoteVsRestButtonSelectedBits,
			NoteVsRestButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->NoteVsRestButton == NIL)
			{
			 FailurePoint18:
				DisposeIconButton(Window->NaturalButton);
				goto FailurePoint17;
			}
		ButtonX += BUTTONINCREMENT;

		Window->RestVsNoteButton = NewIconButtonPreparedBitmaps(Window->ScreenID,
			ButtonX,BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,RestVsNoteButtonBits,
			RestVsNoteButtonMouseDownBits,RestVsNoteButtonSelectedBits,
			RestVsNoteButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->RestVsNoteButton == NIL)
			{
			 FailurePoint19:
				DisposeIconButton(Window->NoteVsRestButton);
				goto FailurePoint18;
			}
		ButtonX += BUTTONINCREMENT + BUTTONEXOINCREMENT;

		Window->NoDotButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,NoDotButtonBits,NoDotButtonMouseDownBits,
			NoDotButtonSelectedBits,NoDotButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->NoDotButton == NIL)
			{
			 FailurePoint20:
				DisposeIconButton(Window->RestVsNoteButton);
				goto FailurePoint19;
			}
		ButtonX += BUTTONINCREMENT;

		Window->YesDotButton = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,YesDotButtonBits,YesDotButtonMouseDownBits,
			YesDotButtonSelectedBits,YesDotButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->YesDotButton == NIL)
			{
			 FailurePoint21:
				DisposeIconButton(Window->NoDotButton);
				goto FailurePoint20;
			}
		ButtonX += BUTTONINCREMENT + BUTTONEXOINCREMENT;

		Window->Div1Button = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,Div1ButtonBits,Div1ButtonMouseDownBits,
			Div1ButtonSelectedBits,Div1ButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->Div1Button == NIL)
			{
			 FailurePoint22:
				DisposeIconButton(Window->YesDotButton);
				goto FailurePoint21;
			}
		ButtonX += BUTTONINCREMENT;

		Window->Div3Button = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,Div3ButtonBits,Div3ButtonMouseDownBits,
			Div3ButtonSelectedBits,Div3ButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->Div3Button == NIL)
			{
			 FailurePoint23:
				DisposeIconButton(Window->Div1Button);
				goto FailurePoint22;
			}
		ButtonX += BUTTONINCREMENT;

		Window->Div5Button = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,Div5ButtonBits,Div5ButtonMouseDownBits,
			Div5ButtonSelectedBits,Div5ButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->Div5Button == NIL)
			{
			 FailurePoint24:
				DisposeIconButton(Window->Div3Button);
				goto FailurePoint23;
			}
		ButtonX += BUTTONINCREMENT;

		Window->Div7Button = NewIconButtonPreparedBitmaps(Window->ScreenID,ButtonX,
			BUTTONSY,BUTTONWIDTH,BUTTONHEIGHT,Div7ButtonBits, Div7ButtonMouseDownBits,
			Div7ButtonSelectedBits,Div7ButtonSelectedMouseDownBits,eIconRadioMode);
		if (Window->Div7Button == NIL)
			{
			 FailurePoint25:
				DisposeIconButton(Window->Div5Button);
				goto FailurePoint24;
			}

		Window->TrackView = NewTrackView(TrackObject,Window->ScreenID,TRACKVIEWX,
			TRACKVIEWY,TRACKVIEWWIDTH(WinWidth),TRACKVIEWHEIGHT(WinHeight));
		if (Window->TrackView == NIL)
			{
			 FailurePoint26:
				DisposeIconButton(Window->Div7Button);
				goto FailurePoint25;
			}

		Window->MyGenericWindow = CheckInNewWindow(Window->ScreenID,Window,
			(void (*)(void*,MyBoolean,OrdType,OrdType,ModifierFlags))&TrackWindowDoIdle,
			(void (*)(void*))&TrackWindowBecomeActive,
			(void (*)(void*))&TrackWindowBecomeInactive,
			(void (*)(void*))&TrackWindowJustResized,
			(void (*)(OrdType,OrdType,ModifierFlags,void*))&TrackWindowDoMouseDown,
			(void (*)(unsigned char,ModifierFlags,void*))&TrackWindowDoKeyDown,
			(void (*)(void*))&TrackWindowClose,
			(void (*)(void*))&TrackWindowMenuSetup,
			(void (*)(void*,MenuItemType*))&TrackWindowDoMenuCommand);
		if (Window->MyGenericWindow == NIL)
			{
			 FailurePoint27:
				DisposeTrackView(Window->TrackView);
				goto FailurePoint26;
			}

		Window->MyMenuItem = MakeNewMenuItem(mmWindowMenu,"x",0);
		if (Window->MyMenuItem == NIL)
			{
			 FailurePoint28:
				CheckOutDyingWindow(Window->MyGenericWindow);
				goto FailurePoint27;
			}
		if (!RegisterWindowMenuItem(Window->MyMenuItem,(void (*)(void*))&ActivateThisWindow,
			Window->ScreenID))
			{
			 FailurePoint29:
				KillMenuItem(Window->MyMenuItem);
				goto FailurePoint28;
			}

		Window->NoteState = e4thNote | eDiv1Modifier;
		Window->NoteReady = False;
		TrackWindowUpdateScrollBars(Window);
		TrackWindowResetButtons(Window);
		TrackWindowResetTitlebar(Window);

		return Window;
	}


/* dispose of the track editing window. */
void										DisposeTrackWindow(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		HideMenu(TrackListGetTrackMenu(Window->TrackList));
		TrackObjectClosingWindowNotify(Window->TrackObject,
			GetWindowXStart(Window->ScreenID),GetWindowYStart(Window->ScreenID),
			GetWindowWidth(Window->ScreenID),GetWindowHeight(Window->ScreenID));
		DeregisterWindowMenuItem(Window->MyMenuItem);
		KillMenuItem(Window->MyMenuItem);
		CheckOutDyingWindow(Window->MyGenericWindow);
		DisposeTrackView(Window->TrackView);
		DisposeScrollBar(Window->HScroll);
		DisposeScrollBar(Window->VScroll);
		DisposeIconButton(Window->ArrowButton);
		DisposeIconButton(Window->CommandButton);
		DisposeIconButton(Window->SixtyFourthButton);
		DisposeIconButton(Window->ThirtySecondButton);
		DisposeIconButton(Window->SixteenthButton);
		DisposeIconButton(Window->EighthButton);
		DisposeIconButton(Window->QuarterButton);
		DisposeIconButton(Window->HalfButton);
		DisposeIconButton(Window->WholeButton);
		DisposeIconButton(Window->DoubleButton);
		DisposeIconButton(Window->QuadButton);
		DisposeIconButton(Window->SharpButton);
		DisposeIconButton(Window->FlatButton);
		DisposeIconButton(Window->NaturalButton);
		DisposeIconButton(Window->NoteVsRestButton);
		DisposeIconButton(Window->RestVsNoteButton);
		DisposeIconButton(Window->NoDotButton);
		DisposeIconButton(Window->YesDotButton);
		DisposeIconButton(Window->Div1Button);
		DisposeIconButton(Window->Div3Button);
		DisposeIconButton(Window->Div5Button);
		DisposeIconButton(Window->Div7Button);
		KillWindow(Window->ScreenID);
		ReleasePtr((char*)Window);
	}


/* bring the track window to the top and give it the focus */
void										TrackWindowBringToTop(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		ActivateThisWindow(Window->ScreenID);
	}


void										TrackWindowDoIdle(TrackWindowRec* Window,
													MyBoolean CheckCursorFlag, OrdType XLoc, OrdType YLoc,
													ModifierFlags Modifiers)
	{
		CheckPtrExistence(Window);
		/* check mouse EVERY time, not just on mouse update cycles */
		if (TrackViewHitTest(Window->TrackView,XLoc,YLoc))
			{
				/* note plotting mode cursor */
				TrackViewUpdateMouseCursor(Window->TrackView,XLoc,YLoc,Window->NoteReady);
			}
		 else
			{
				TrackViewUndrawCursorBar(Window->TrackView);
				SetArrowCursor();
			}
	}


void										TrackWindowBecomeActive(TrackWindowRec* Window)
	{
		OrdType								XSize;
		OrdType								YSize;

		CheckPtrExistence(Window);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		EnableScrollBar(Window->HScroll);
		EnableScrollBar(Window->VScroll);
		/* force redraw of track view object to incorporate any changes to tracks */
		/* displayed in this window (since we don't redraw it in the background in */
		/* order to save time) */
		MarkForDeferredUpdate(Window->ScreenID);
		SetClipRect(Window->ScreenID,XSize - 15,YSize - 15,XSize,YSize);
		DrawBitmap(Window->ScreenID,XSize-15,YSize-15,GetGrowIcon(True/*enablegrowicon*/));
		/* decrease lag time so that we update cursor more often */
		SetEventSleepTime(0.1);
		ShowMenu(TrackListGetTrackMenu(Window->TrackList));
	}


void										TrackWindowBecomeInactive(TrackWindowRec* Window)
	{
		OrdType								XSize;
		OrdType								YSize;

		CheckPtrExistence(Window);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		DisableScrollBar(Window->HScroll);
		DisableScrollBar(Window->VScroll);
		SetClipRect(Window->ScreenID,XSize - 15,YSize - 15,XSize,YSize);
		DrawBitmap(Window->ScreenID,XSize-15,YSize-15,GetGrowIcon(False/*disablegrowicon*/));
		/* reset event delay time to default */
		SetEventSleepTime(0.25);
		HideMenu(TrackListGetTrackMenu(Window->TrackList));
	}


void										TrackWindowJustResized(TrackWindowRec* Window)
	{
		OrdType								XSize;
		OrdType								YSize;

		CheckPtrExistence(Window);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,0,0,XSize,YSize);
		DrawBoxErase(Window->ScreenID,0,0,XSize,YSize);
		SetTrackViewPosition(Window->TrackView,TRACKVIEWX,TRACKVIEWY,
			TRACKVIEWWIDTH(XSize),TRACKVIEWHEIGHT(YSize));
		TrackWindowUpdateScrollBars(Window);
		SetScrollLocation(Window->HScroll,HSCROLLX,HSCROLLY(YSize),HSCROLLWIDTH(XSize));
		SetScrollLocation(Window->VScroll,VSCROLLX(XSize),VSCROLLY,VSCROLLHEIGHT(YSize));
	}


void										TrackWindowDoMouseDown(OrdType XLoc, OrdType YLoc,
													ModifierFlags Modifiers, TrackWindowRec* Window)
	{
		if ((XLoc >= GetWindowWidth(Window->ScreenID) - 15)
			&& (XLoc <= GetWindowWidth(Window->ScreenID))
			&& (YLoc >= GetWindowHeight(Window->ScreenID) - 15)
			&& (YLoc <= GetWindowHeight(Window->ScreenID)))
			{
				UserGrowWindow(Window->ScreenID,XLoc,YLoc);
				TrackWindowJustResized(Window);
			}
		else if (ScrollHitTest(Window->VScroll,XLoc,YLoc))
			{
				ScrollHitProc(Window->VScroll,Modifiers,XLoc,YLoc,Window,
					(void (*)(long, ScrollType, void*))&TrackWindowVScrollHook);
			}
		else if (ScrollHitTest(Window->HScroll,XLoc,YLoc))
			{
				ScrollHitProc(Window->HScroll,Modifiers,XLoc,YLoc,Window,
					(void (*)(long, ScrollType, void*))&TrackWindowHScrollHook);
			}
		else if (IconButtonHitTest(Window->ArrowButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->ArrowButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = False;
						TrackWindowResetButtons(Window);
						TrackViewUndrawCursorBar(Window->TrackView);
					}
			}
		else if (IconButtonHitTest(Window->CommandButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->CommandButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState |= eCommandFlag;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->SixtyFourthButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->SixtyFourthButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | e64thNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->ThirtySecondButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->ThirtySecondButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | e32ndNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->SixteenthButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->SixteenthButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | e16thNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->EighthButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->EighthButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | e8thNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->QuarterButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->QuarterButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | e4thNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->HalfButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->HalfButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | e2ndNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->WholeButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->WholeButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | eWholeNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->DoubleButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->DoubleButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | eDoubleNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->QuadButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->QuadButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDurationMask) | eQuadNote;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->SharpButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->SharpButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eFlatModifier) | eSharpModifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->FlatButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->FlatButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eSharpModifier) | eFlatModifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->NaturalButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->NaturalButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = Window->NoteState & ~(eSharpModifier | eFlatModifier);
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->NoteVsRestButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->NoteVsRestButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = Window->NoteState & ~eRestModifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->RestVsNoteButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->RestVsNoteButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = Window->NoteState | eRestModifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->NoDotButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->NoDotButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = Window->NoteState & ~eDotModifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->YesDotButton,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->YesDotButton,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = Window->NoteState | eDotModifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->Div1Button,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->Div1Button,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDivisionMask) | eDiv1Modifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->Div3Button,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->Div3Button,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDivisionMask) | eDiv3Modifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->Div5Button,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->Div5Button,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDivisionMask) | eDiv5Modifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (IconButtonHitTest(Window->Div7Button,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->Div7Button,XLoc,YLoc,NIL,NIL))
					{
						Window->NoteReady = True;
						Window->NoteState &= ~eCommandFlag;
						Window->NoteState = (Window->NoteState & ~eDivisionMask) | eDiv7Modifier;
						TrackWindowResetButtons(Window);
					}
			}
		else if (TrackViewHitTest(Window->TrackView,XLoc,YLoc))
			{
				if ((Modifiers & eCommandKey) != 0)
					{
						/* single note selection */
						TrackViewTrySingleNoteSelection(Window->TrackView,XLoc,YLoc);
					}
				else if ((Modifiers & eControlKey) != 0)
					{
						/* set ties */
						if (TrackViewIsASingleNoteSelected(Window->TrackView))
							{
								TrackViewSetTieOnNote(Window->TrackView,XLoc,YLoc);
							}
					}
				else
					{
						if (Window->NoteReady && ((Modifiers & eShiftKey) == 0))
							{
								if ((Window->NoteState & eCommandFlag) == 0)
									{
										/* adding a note to the track */
										TrackViewAddNote(Window->TrackView,XLoc,YLoc,Window->NoteState);
									}
								 else
									{
										NoteCommands				TheCommandTheyWant;

										/* adding a command to the track */
										if (ChooseCommandFromList(&TheCommandTheyWant))
											{
												TrackViewAddCommand(Window->TrackView,XLoc,YLoc,
													TheCommandTheyWant);
											}
									}
							}
						 else
							{
								/* block selection */
								TrackViewDoMouseDown(Window->TrackView,XLoc,YLoc,
									((Modifiers & eShiftKey) != 0),
									(void (*)(void *))&TrackWindowUpdateScrollBars,Window);
							}
						TrackWindowUpdateHScrollBar(Window);
					}
			}
	}


void										TrackWindowDoKeyDown(unsigned char KeyCode,
													ModifierFlags Modifiers, TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		if ((Modifiers & eCommandKey) != 0)
			{
				/* don't pay attention to errant menu shortcuts */
				return;
			}
		switch (KeyCode)
			{
				case eLeftArrow:
					TrackWindowHScrollHook(0,eScrollLineMinus,Window);
					break;
				case eRightArrow:
					TrackWindowHScrollHook(0,eScrollLinePlus,Window);
					break;
				case eUpArrow:
					TrackWindowVScrollHook(0,eScrollLineMinus,Window);
					break;
				case eDownArrow:
					TrackWindowVScrollHook(0,eScrollLinePlus,Window);
					break;
				case '/':
					Window->NoteReady = True;
					Window->NoteState = e4thNote | eDiv1Modifier;
					TrackWindowResetButtons(Window);
					TrackViewUndrawCursorBar(Window->TrackView);
					break;
				case 'a':
				case eCancelKey:
					Window->NoteReady = False;
					TrackWindowResetButtons(Window);
					TrackViewUndrawCursorBar(Window->TrackView);
					break;
				case 'z':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					TrackWindowResetButtons(Window);
					TrackViewUndrawCursorBar(Window->TrackView);
					break;
				case 'x':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | e64thNote;
					TrackWindowResetButtons(Window);
					break;
				case 't':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | e32ndNote;
					TrackWindowResetButtons(Window);
					break;
				case 's':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | e16thNote;
					TrackWindowResetButtons(Window);
					break;
				case 'e':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | e8thNote;
					TrackWindowResetButtons(Window);
					break;
				case 'q':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | e4thNote;
					TrackWindowResetButtons(Window);
					break;
				case 'h':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | e2ndNote;
					TrackWindowResetButtons(Window);
					break;
				case 'w':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | eWholeNote;
					TrackWindowResetButtons(Window);
					break;
				case 'd':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | eDoubleNote;
					TrackWindowResetButtons(Window);
					break;
				case 'f':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDurationMask) | eQuadNote;
					TrackWindowResetButtons(Window);
					break;
				case '=':
				case '+':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eFlatModifier) | eSharpModifier;
					TrackWindowResetButtons(Window);
					break;
				case '-':
				case '_':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eSharpModifier) | eFlatModifier;
					TrackWindowResetButtons(Window);
					break;
				case '0':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = Window->NoteState & ~(eSharpModifier | eFlatModifier);
					TrackWindowResetButtons(Window);
					break;
				case 'n':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = Window->NoteState & ~eRestModifier;
					TrackWindowResetButtons(Window);
					break;
				case 'r':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = Window->NoteState | eRestModifier;
					TrackWindowResetButtons(Window);
					break;
				case ',':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = Window->NoteState & ~eDotModifier;
					TrackWindowResetButtons(Window);
					break;
				case '.':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = Window->NoteState | eDotModifier;
					TrackWindowResetButtons(Window);
					break;
				case '1':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDivisionMask) | eDiv1Modifier;
					TrackWindowResetButtons(Window);
					break;
				case '3':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDivisionMask) | eDiv3Modifier;
					TrackWindowResetButtons(Window);
					break;
				case '5':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDivisionMask) | eDiv5Modifier;
					TrackWindowResetButtons(Window);
					break;
				case '7':
					Window->NoteReady = True;
					Window->NoteState &= ~eCommandFlag;
					Window->NoteState = (Window->NoteState & ~eDivisionMask) | eDiv7Modifier;
					TrackWindowResetButtons(Window);
					break;
				case 'c':
					Window->NoteReady = True;
					Window->NoteState |= eCommandFlag;
					TrackWindowResetButtons(Window);
					break;
				case 8:
					/* the delete key deletes selected stuff */
					if (TrackViewIsASingleNoteSelected(Window->TrackView)
						|| TrackViewIsASingleCommandSelected(Window->TrackView))
						{
							TrackViewDeleteSingleNoteOrCommand(Window->TrackView);
						}
					else if (TrackViewIsARangeSelected(Window->TrackView))
						{
							TrackViewDeleteRangeSelection(Window->TrackView);
						}
					TrackWindowUpdateScrollBars(Window);
					break;
				case 13: /* carriage return brings up attribute dialog for the selected thing */
					if (TrackViewIsASingleNoteSelected(Window->TrackView)
						|| TrackViewIsASingleCommandSelected(Window->TrackView))
						{
							TrackViewEditSingleSelectionAttributes(Window->TrackView);
						}
					break;
				case 3: /* show selection */
					TrackWindowShowSelection(Window);
					break;
				case '<': /* transpose note down 1 step */
					if ((TrackViewIsARangeSelected(Window->TrackView))
						|| TrackViewIsASingleNoteSelected(Window->TrackView))
						{
							TrackViewTransposeSelection(Window->TrackView,-1);
						}
					break;
				case '>': /* transpose note up 1 step */
					if ((TrackViewIsARangeSelected(Window->TrackView))
						|| TrackViewIsASingleNoteSelected(Window->TrackView))
						{
							TrackViewTransposeSelection(Window->TrackView,1);
						}
					break;
			}
	}


void										TrackWindowClose(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		DisposeTrackWindow(Window);
	}


void										TrackWindowUpdator(TrackWindowRec* Window)
	{
		OrdType								XSize;
		OrdType								YSize;

		CheckPtrExistence(Window);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		RedrawScrollBar(Window->HScroll);
		RedrawScrollBar(Window->VScroll);
		RedrawIconButton(Window->ArrowButton);
		RedrawIconButton(Window->CommandButton);
		RedrawIconButton(Window->SixtyFourthButton);
		RedrawIconButton(Window->ThirtySecondButton);
		RedrawIconButton(Window->SixteenthButton);
		RedrawIconButton(Window->EighthButton);
		RedrawIconButton(Window->QuarterButton);
		RedrawIconButton(Window->HalfButton);
		RedrawIconButton(Window->WholeButton);
		RedrawIconButton(Window->DoubleButton);
		RedrawIconButton(Window->QuadButton);
		RedrawIconButton(Window->SharpButton);
		RedrawIconButton(Window->FlatButton);
		RedrawIconButton(Window->NaturalButton);
		RedrawIconButton(Window->NoteVsRestButton);
		RedrawIconButton(Window->RestVsNoteButton);
		RedrawIconButton(Window->NoDotButton);
		RedrawIconButton(Window->YesDotButton);
		RedrawIconButton(Window->Div1Button);
		RedrawIconButton(Window->Div3Button);
		RedrawIconButton(Window->Div5Button);
		RedrawIconButton(Window->Div7Button);
		TrackViewRedrawAll(Window->TrackView);
		SetClipRect(Window->ScreenID,0,0,XSize,YSize);
		DrawLine(Window->ScreenID,eBlack,0,BUTTONSY + BUTTONHEIGHT - 1,XSize,0);
		DrawBitmap(Window->ScreenID,XSize - 15,YSize - 15,
			GetGrowIcon(Window->MyGenericWindow == GetCurrentWindowID()));
	}


void										TrackWindowMenuSetup(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		MainWindowEnableGlobalMenus(Window->MainWindow);
		ChangeItemName(mCloseFile,"Close Track Editor");
		EnableMenuItem(mCloseFile);
		ChangeItemName(mDeleteObject,"Delete Track");
		EnableMenuItem(mDeleteObject);
		TrackListEnableMenuItems(Window->TrackList);
		TrackListMenuItemCheckmarks(Window->TrackList,
			TrackObjectGetBackgroundList(Window->TrackObject));
		SetItemCheckmark(TrackObjectGetMenuItem(Window->TrackObject));
		EnableMenuItem(mEditTrackAttributes);
		SetItemCheckmark(Window->MyMenuItem);

		EnableMenuItem(mShowSelection);

		EnableMenuItem(mSelectAll);
		if (TrackViewIsThereInsertionPoint(Window->TrackView)
			|| TrackViewIsARangeSelected(Window->TrackView))
			{
				EnableMenuItem(mPaste);
				ChangeItemName(mPaste,"Paste");
			}
		if (TrackViewIsASingleNoteSelected(Window->TrackView)
			|| TrackViewIsASingleCommandSelected(Window->TrackView))
			{
				EnableMenuItem(mClear);
				ChangeItemName(mClear,"Delete Note");
			}
		if (TrackViewIsARangeSelected(Window->TrackView))
			{
				EnableMenuItem(mClear);
				ChangeItemName(mClear,"Delete Selection");
				EnableMenuItem(mCut);
				ChangeItemName(mCut,"Cut Selection");
				EnableMenuItem(mCopy);
				ChangeItemName(mCopy,"Copy Selection");
			}
		if (TrackViewCanWeUndo(Window->TrackView))
			{
				EnableMenuItem(mUndo);
				ChangeItemName(mUndo,"Undo Track Edit");
			}
		if ((TrackViewIsARangeSelected(Window->TrackView))
			|| TrackViewIsASingleNoteSelected(Window->TrackView))
			{
				EnableMenuItem(mTransposeSelection);
			}

		EnableMenuItem(mPlayThisTrackFromHere);
		EnableMenuItem(mPlayAllTracksFromHere);
		Window->LastMenuOptionKeyDown = ((CheckModifiers() & eOptionKey) != 0);
		if (Window->LastMenuOptionKeyDown)
			{
				ChangeItemName(mPlayThisTrackFromHere,"Play This Track To Disk");
				ChangeItemName(mPlayAllTracksFromHere,"Play All Tracks To Disk");
			}
		 else
			{
				ChangeItemName(mPlayThisTrackFromHere,"Play This Track");
				ChangeItemName(mPlayAllTracksFromHere,"Play All Tracks");
			}

		EnableMenuItem(mGotoMeasureBar);
	}


void										TrackWindowDoMenuCommand(TrackWindowRec* Window,
													MenuItemType* MenuItem)
	{
		TrackObjectRec*				Track;

		CheckPtrExistence(Window);
		if (MainWindowDoGlobalMenuItem(Window->MainWindow,MenuItem))
			{
			}
		else if (MenuItem == mCloseFile)
			{
				TrackWindowClose(Window);
			}
		else if (MenuItem == mDeleteObject)
			{
				TrackListDeleteTrack(Window->TrackList,Window->TrackObject);
			}
		else if (NIL != (Track = TrackListLookupMenuItem(Window->TrackList,MenuItem)))
			{
				ArrayRec*							Backgrounded;

				Backgrounded = TrackObjectGetBackgroundList(Window->TrackObject);
				if (ArrayFindElement(Backgrounded,Track) >= 0)
					{
						/* item is in array -- remove it */
						if (Window->TrackObject != Track)
							{
								TrackObjectRemoveBackgroundObj(Window->TrackObject,Track);
								TrackObjectRemoveDependentView(Track,Window->TrackView);
								TrackViewRemoveBackgroundTrack(Window->TrackView,Track);
							}
					}
				 else
					{
						/* item is not in array -- add it */
						if (Window->TrackObject != Track)
							{
								/* our track needs to know that it has another track in background */
								if (!TrackObjectAddBackgroundObj(Window->TrackObject,Track))
									{
									 AddTrackFailurePoint1:
										return;
									}
								/* the other track needs to know about our view so that */
								/* it can notify it about updates */
								if (!TrackObjectAddDependentView(Track,Window->TrackView))
									{
									 AddTrackFailurePoint2:
										TrackObjectRemoveBackgroundObj(Window->TrackObject,Track);
										goto AddTrackFailurePoint1;
									}
								/* the view needs to know so it can add it to the scheduler */
								if (!TrackViewAddBackgroundTrack(Window->TrackView,Track))
									{
									 AddTrackFailurePoint3:
										TrackObjectRemoveDependentView(Track,Window->TrackView);
										goto AddTrackFailurePoint2;
									}
							}
					}
				TrackWindowUpdateScrollBars(Window);
			}
		else if (MenuItem == mEditTrackAttributes)
			{
				TrackAttributeDialog(Window->TrackObject);
			}
		else if (MenuItem == mSelectAll)
			{
				TrackViewSelectAll(Window->TrackView);
			}
		else if (MenuItem == mPaste)
			{
				TrackViewAttemptPaste(Window->TrackView);
				TrackWindowUpdateScrollBars(Window);
			}
		else if (MenuItem == mClear)
			{
				if (TrackViewIsASingleNoteSelected(Window->TrackView)
					|| TrackViewIsASingleCommandSelected(Window->TrackView))
					{
						TrackViewDeleteSingleNoteOrCommand(Window->TrackView);
					}
				else if (TrackViewIsARangeSelected(Window->TrackView))
					{
						TrackViewDeleteRangeSelection(Window->TrackView);
					}
				TrackWindowShowSelection(Window);
			}
		else if (MenuItem == mCut)
			{
				TrackViewCutRangeSelection(Window->TrackView);
				TrackWindowShowSelection(Window);
			}
		else if (MenuItem == mCopy)
			{
				TrackViewCopyRangeSelection(Window->TrackView);
			}
		else if (MenuItem == mUndo)
			{
				TrackViewUndo(Window->TrackView);
				TrackWindowShowSelection(Window);
			}
		else if (MenuItem == mPlayThisTrackFromHere)
			{
				long							StartIndex;
				ArrayRec*					TrackList;

				if (TrackViewIsARangeSelected(Window->TrackView))
					{
						StartIndex = TrackViewGetRangeStart(Window->TrackView);
					}
				else if (TrackViewIsThereInsertionPoint(Window->TrackView))
					{
						StartIndex = TrackViewGetInsertionPointIndex(Window->TrackView);
					}
				else if (TrackViewIsASingleNoteSelected(Window->TrackView)
					|| TrackViewIsASingleCommandSelected(Window->TrackView))
					{
						StartIndex = TrackViewGetSingleNoteSelectionFrameNumber(Window->TrackView);
					}
				else
					{
						StartIndex = 0; /* default to the beginning for other selections */
					}
				TrackList = NewArray();
				if (TrackList == NIL)
					{
					 PlaySingleFailurePoint1:
						AlertHalt("There is not enough memory available to play the track.",NIL);
						return;
					}
				if (!ArrayAppendElement(TrackList,Window->TrackObject))
					{
					 PlaySingleFailurePoint2:
						DisposeArray(TrackList);
						goto PlaySingleFailurePoint1;
					}
				if (!Window->LastMenuOptionKeyDown)
					{
						SynthToSoundDevice(Window->MainWindow,TrackList,Window->TrackObject,
							StartIndex,MainWindowGetSamplingRate(Window->MainWindow),
							MainWindowGetEnvelopeRate(Window->MainWindow),
							MainWindowGetStereo(Window->MainWindow),
							Double2LargeBCD(MainWindowGetBeatsPerMinute(Window->MainWindow)),
							Double2LargeBCD(MainWindowGetVolumeScaling(Window->MainWindow)),
							MainWindowGetInterpolationOverTime(Window->MainWindow),
							MainWindowGetInterpolationAcrossWaves(Window->MainWindow),
							Double2LargeBCD(MainWindowGetScanningGap(Window->MainWindow)),
							MainWindowGetOutputNumBits(Window->MainWindow),
							Double2LargeBCD(MainWindowGetBufferDuration(Window->MainWindow)),False);
					}
				 else
					{
						SynthToAIFFFile(Window->MainWindow,TrackList,Window->TrackObject,
							StartIndex,MainWindowGetSamplingRate(Window->MainWindow),
							MainWindowGetEnvelopeRate(Window->MainWindow),
							MainWindowGetStereo(Window->MainWindow),
							Double2LargeBCD(MainWindowGetBeatsPerMinute(Window->MainWindow)),
							Double2LargeBCD(MainWindowGetVolumeScaling(Window->MainWindow)),
							MainWindowGetInterpolationOverTime(Window->MainWindow),
							MainWindowGetInterpolationAcrossWaves(Window->MainWindow),
							Double2LargeBCD(MainWindowGetScanningGap(Window->MainWindow)),
							MainWindowGetOutputNumBits(Window->MainWindow),False);
					}
				DisposeArray(TrackList);
			}
		else if (MenuItem == mPlayAllTracksFromHere)
			{
				long							StartIndex;
				ArrayRec*					TrackList;
				long							TrackListScan;

				if (TrackViewIsARangeSelected(Window->TrackView))
					{
						StartIndex = TrackViewGetRangeStart(Window->TrackView);
					}
				else if (TrackViewIsThereInsertionPoint(Window->TrackView))
					{
						StartIndex = TrackViewGetInsertionPointIndex(Window->TrackView);
					}
				else if (TrackViewIsASingleNoteSelected(Window->TrackView)
					|| TrackViewIsASingleCommandSelected(Window->TrackView))
					{
						StartIndex = TrackViewGetSingleNoteSelectionFrameNumber(Window->TrackView);
					}
				else
					{
						StartIndex = 0; /* default to the beginning for other selections */
					}
				TrackList = TrackListGetListOfAllTracks(Window->TrackList);
				if (TrackList == NIL)
					{
						AlertHalt("There is not enough memory available to play the track.",NIL);
						return;
					}
				TrackListScan = 0;
				/* keep only the tracks that are marked to be played */
				while (TrackListScan < ArrayGetLength(TrackList))
					{
						TrackObjectRec*			MaybeTrack;

						MaybeTrack = (TrackObjectRec*)ArrayGetElement(TrackList,TrackListScan);
						CheckPtrExistence(MaybeTrack);
						if ((MaybeTrack != Window->TrackObject) /* we MUST keep the current track! */
							&& !TrackObjectShouldItBePlayed(MaybeTrack))
							{
								ArrayDeleteElement(TrackList,TrackListScan);
							}
						 else
							{
								TrackListScan += 1;
							}
					}
				if (!Window->LastMenuOptionKeyDown)
					{
						SynthToSoundDevice(Window->MainWindow,TrackList,Window->TrackObject,
							StartIndex,MainWindowGetSamplingRate(Window->MainWindow),
							MainWindowGetEnvelopeRate(Window->MainWindow),
							MainWindowGetStereo(Window->MainWindow),
							Double2LargeBCD(MainWindowGetBeatsPerMinute(Window->MainWindow)),
							Double2LargeBCD(MainWindowGetVolumeScaling(Window->MainWindow)),
							MainWindowGetInterpolationOverTime(Window->MainWindow),
							MainWindowGetInterpolationAcrossWaves(Window->MainWindow),
							Double2LargeBCD(MainWindowGetScanningGap(Window->MainWindow)),
							MainWindowGetOutputNumBits(Window->MainWindow),
							Double2LargeBCD(MainWindowGetBufferDuration(Window->MainWindow)),False);
					}
				 else
					{
						SynthToAIFFFile(Window->MainWindow,TrackList,Window->TrackObject,
							StartIndex,MainWindowGetSamplingRate(Window->MainWindow),
							MainWindowGetEnvelopeRate(Window->MainWindow),
							MainWindowGetStereo(Window->MainWindow),
							Double2LargeBCD(MainWindowGetBeatsPerMinute(Window->MainWindow)),
							Double2LargeBCD(MainWindowGetVolumeScaling(Window->MainWindow)),
							MainWindowGetInterpolationOverTime(Window->MainWindow),
							MainWindowGetInterpolationAcrossWaves(Window->MainWindow),
							Double2LargeBCD(MainWindowGetScanningGap(Window->MainWindow)),
							MainWindowGetOutputNumBits(Window->MainWindow),False);
					}
				DisposeArray(TrackList);
			}
		else if (MenuItem == mShowSelection)
			{
				TrackWindowShowSelection(Window);
			}
		else if (MenuItem == mTransposeSelection)
			{
				long						Transpose;

				Transpose = DoNumberDialog("Transpose half-steps:",0,mCut,mPaste,mCopy,
					mUndo,mSelectAll,mClear);
				if (Transpose != 0)
					{
						TrackViewTransposeSelection(Window->TrackView,Transpose);
					}
			}
		else if (MenuItem == mGotoMeasureBar)
			{
				long						NewMeasure;

				NewMeasure = DoNumberDialog("Go To Measure:",0,mCut,mPaste,mCopy,
					mUndo,mSelectAll,mClear);
				if (NewMeasure != 0)
					{
						TrackViewShowMeasure(Window->TrackView,NewMeasure);
						TrackWindowUpdateScrollBars(Window);
					}
			}
		else
			{
				EXECUTE(PRERR(ForceAbort,"TrackWindowDoMenuCommand:  unknown menu command"));
			}
	}


void										TrackWindowUpdateVScrollBar(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		SetMaxScrollIndex(Window->VScroll,
			TrackViewGetVerticalDegreesOfFreedom(Window->TrackView));
		SetScrollIndex(Window->VScroll,TrackViewGetVerticalOffset(Window->TrackView));
		RedrawScrollBar(Window->VScroll);
	}


/* update the positioning of the horizontal scroll bar and redraw */
void										TrackWindowUpdateHScrollBar(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		SetMaxScrollIndex(Window->HScroll,
			TrackViewGetHorizontalDegreesOfFreedom(Window->TrackView));
		SetScrollIndex(Window->HScroll,TrackViewGetHorizontalOffset(Window->TrackView));
		RedrawScrollBar(Window->HScroll);
	}


static void							TrackWindowVScrollHook(long Parameter, ScrollType How,
													TrackWindowRec* Window)
	{
		long									NewPosition;

		CheckPtrExistence(Window);
		switch (How)
			{
				case eScrollToPosition:
					NewPosition = Parameter;
					break;
				case eScrollPageMinus:
					NewPosition = TrackViewGetVerticalOffset(Window->TrackView)
						- (7 * GetTrackViewHeight(Window->TrackView)) / 8;
					break;
				case eScrollPagePlus:
					NewPosition = TrackViewGetVerticalOffset(Window->TrackView)
						+ (7 * GetTrackViewHeight(Window->TrackView)) / 8;
					break;
				case eScrollLineMinus:
					NewPosition = TrackViewGetVerticalOffset(Window->TrackView)
						- (1 * GetTrackViewHeight(Window->TrackView)) / 8;
					break;
				case eScrollLinePlus:
					NewPosition = TrackViewGetVerticalOffset(Window->TrackView)
						+ (1 * GetTrackViewHeight(Window->TrackView)) / 8;
					break;
				default:
					EXECUTE(PRERR(AllowResume,"TrackWindowVScrollHook:  Unknown scroll opcode"));
					break;
			}
		TrackViewSetNewVerticalOffset(Window->TrackView,NewPosition);
		TrackWindowUpdateVScrollBar(Window);
	}


static void							TrackWindowHScrollHook(long Parameter, ScrollType How,
													TrackWindowRec* Window)
	{
		long									NewPosition;

		CheckPtrExistence(Window);
		switch (How)
			{
				case eScrollToPosition:
					NewPosition = Parameter;
					break;
				case eScrollPageMinus:
					NewPosition = TrackViewGetHorizontalOffset(Window->TrackView)
						- (7 * GetTrackViewWidth(Window->TrackView)) / 8;
					break;
				case eScrollPagePlus:
					NewPosition = TrackViewGetHorizontalOffset(Window->TrackView)
						+ (7 * GetTrackViewWidth(Window->TrackView)) / 8;
					break;
				case eScrollLineMinus:
					NewPosition = TrackViewGetHorizontalOffset(Window->TrackView)
						- (1 * GetTrackViewWidth(Window->TrackView)) / 8;
					break;
				case eScrollLinePlus:
					NewPosition = TrackViewGetHorizontalOffset(Window->TrackView)
						+ (1 * GetTrackViewWidth(Window->TrackView)) / 8;
					break;
				default:
					EXECUTE(PRERR(AllowResume,"TrackWindowHScrollHook:  Unknown scroll opcode"));
					break;
			}
		TrackViewSetNewHorizontalOffset(Window->TrackView,NewPosition);
		TrackWindowUpdateHScrollBar(Window);
	}


/* reset the state of the note attribute buttons according to the attribute flags */
void										TrackWindowResetButtons(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		if (!Window->NoteReady)
			{
				SetIconButtonState(Window->ArrowButton,True);
				SetIconButtonState(Window->CommandButton,False);
			 NoteButtonsOffPoint:
				SetIconButtonState(Window->SixtyFourthButton,False);
				SetIconButtonState(Window->ThirtySecondButton,False);
				SetIconButtonState(Window->SixteenthButton,False);
				SetIconButtonState(Window->EighthButton,False);
				SetIconButtonState(Window->QuarterButton,False);
				SetIconButtonState(Window->HalfButton,False);
				SetIconButtonState(Window->WholeButton,False);
				SetIconButtonState(Window->DoubleButton,False);
				SetIconButtonState(Window->QuadButton,False);
				SetIconButtonState(Window->SharpButton,False);
				SetIconButtonState(Window->FlatButton,False);
				SetIconButtonState(Window->NaturalButton,False);
				SetIconButtonState(Window->NoteVsRestButton,False);
				SetIconButtonState(Window->RestVsNoteButton,False);
				SetIconButtonState(Window->NoDotButton,False);
				SetIconButtonState(Window->YesDotButton,False);
				SetIconButtonState(Window->Div1Button,False);
				SetIconButtonState(Window->Div3Button,False);
				SetIconButtonState(Window->Div5Button,False);
				SetIconButtonState(Window->Div7Button,False);
			}
		 else
			{
				SetIconButtonState(Window->ArrowButton,False);
				if ((Window->NoteState & eCommandFlag) != 0)
					{
						SetIconButtonState(Window->CommandButton,True);
						goto NoteButtonsOffPoint;
					}
				 else
					{
						SetIconButtonState(Window->CommandButton,False);

						SetIconButtonState(Window->SixtyFourthButton,
							(Window->NoteState & eDurationMask) == e64thNote);
						SetIconButtonState(Window->ThirtySecondButton,
							(Window->NoteState & eDurationMask) == e32ndNote);
						SetIconButtonState(Window->SixteenthButton,
							(Window->NoteState & eDurationMask) == e16thNote);
						SetIconButtonState(Window->EighthButton,
							(Window->NoteState & eDurationMask) == e8thNote);
						SetIconButtonState(Window->QuarterButton,
							(Window->NoteState & eDurationMask) == e4thNote);
						SetIconButtonState(Window->HalfButton,
							(Window->NoteState & eDurationMask) == e2ndNote);
						SetIconButtonState(Window->WholeButton,
							(Window->NoteState & eDurationMask) == eWholeNote);
						SetIconButtonState(Window->DoubleButton,
							(Window->NoteState & eDurationMask) == eDoubleNote);
						SetIconButtonState(Window->QuadButton,
							(Window->NoteState & eDurationMask) == eQuadNote);

						SetIconButtonState(Window->SharpButton,
							(Window->NoteState & eSharpModifier) != 0);
						SetIconButtonState(Window->FlatButton,
							(Window->NoteState & eFlatModifier) != 0);
						SetIconButtonState(Window->NaturalButton,
							(Window->NoteState & (eFlatModifier | eSharpModifier)) == 0);

						SetIconButtonState(Window->NoteVsRestButton,
							(Window->NoteState & eRestModifier) == 0);
						SetIconButtonState(Window->RestVsNoteButton,
							(Window->NoteState & eRestModifier) != 0);

						SetIconButtonState(Window->NoDotButton,
							(Window->NoteState & eDotModifier) == 0);
						SetIconButtonState(Window->YesDotButton,
							(Window->NoteState & eDotModifier) != 0);

						SetIconButtonState(Window->Div1Button,
							(Window->NoteState & eDivisionMask) == eDiv1Modifier);
						SetIconButtonState(Window->Div3Button,
							(Window->NoteState & eDivisionMask) == eDiv3Modifier);
						SetIconButtonState(Window->Div5Button,
							(Window->NoteState & eDivisionMask) == eDiv5Modifier);
						SetIconButtonState(Window->Div7Button,
							(Window->NoteState & eDivisionMask) == eDiv7Modifier);
					}
			}
	}


/* the name of the file has changed, so update the title bar of the window.  the */
/* NewFilename is a non-null-terminated string.  the caller is responsible for */
/* disposing of it */
void										TrackWindowGlobalNameChange(TrackWindowRec* Window,
													char* NewFilename)
	{
		char*									LocalNameCopy;

		CheckPtrExistence(Window);
		CheckPtrExistence(NewFilename);
		LocalNameCopy = TrackObjectGetNameCopy(Window->TrackObject);
		if (LocalNameCopy != NIL)
			{
				char*							SeparatorString;

				SeparatorString = StringToBlockCopy(":  ");
				if (SeparatorString != NIL)
					{
						char*							LeftHalfOfString;

						LeftHalfOfString = ConcatBlockCopy(NewFilename,SeparatorString);
						if (LeftHalfOfString != NIL)
							{
								char*							TotalString;

								TotalString = ConcatBlockCopy(LeftHalfOfString,LocalNameCopy);
								if (TotalString != NIL)
									{
										char*							NullTerminatedString;

										NullTerminatedString = BlockToStringCopy(TotalString);
										if (NullTerminatedString != NIL)
											{
												SetWindowName(Window->ScreenID,NullTerminatedString);
												ChangeItemName(Window->MyMenuItem,NullTerminatedString);
												ReleasePtr(NullTerminatedString);
											}
										ReleasePtr(TotalString);
									}
								ReleasePtr(LeftHalfOfString);
							}
						ReleasePtr(SeparatorString);
					}
				ReleasePtr(LocalNameCopy);
			}
	}


/* reset the title bar even if the filename hasn't changed */
void										TrackWindowResetTitlebar(TrackWindowRec* Window)
	{
		char*									DocumentName;

		CheckPtrExistence(Window);
		DocumentName = GetCopyOfDocumentName(Window->MainWindow);
		if (DocumentName != NIL)
			{
				TrackWindowGlobalNameChange(Window,DocumentName);
				ReleasePtr(DocumentName);
			}
	}


/* update both scrollbars */
void										TrackWindowUpdateScrollBars(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		TrackWindowUpdateVScrollBar(Window);
		TrackWindowUpdateHScrollBar(Window);
	}


/* show the current selection in the window */
void										TrackWindowShowSelection(TrackWindowRec* Window)
	{
		CheckPtrExistence(Window);
		TrackViewShowSelection(Window->TrackView);
		TrackWindowUpdateScrollBars(Window);
	}
