コード例 #1
0
sint32 c3_PopupWindow::AddTitle( MBCHAR *titleBlock )
{
	if (m_title)
	{
		// No action: already created
	}
	else
	{
		AUI_ERRCODE errcode = AUI_ERRCODE_OK;
		m_title = new c3_Static(&errcode, aui_UniqueId(), "c3_PopupTitle");
		TestControl(m_title);
		char *ldlBlock = "c3_PopupTitle.c3_PopupTitleText";
		m_titleText = 
			new c3_Static
		        (&errcode, 
		         aui_UniqueId(), 
		         (titleBlock) ? titleBlock : ldlBlock
		        );
		TestControl(m_titleText);

		m_title->AddSubControl(m_titleText);
		m_title->Move((m_width - m_title->Width()) / 2, 0);

		InsertChild(m_title, 0);
		m_title->SetParentWindow(this);
		m_title->SetBlindness(true);
	}

	return 1;
}
コード例 #2
0
static void
MacScaleEventProc(
    ClientData clientData,	/* Information about window. */
    XEvent *eventPtr)		/* Information about event. */
{
    MacScale *macScalePtr = (MacScale *) clientData;
    Point where;
    Rect bounds;
    int part;
    CGrafPtr destPort, savePort;
    Boolean portChanged;

#ifdef TK_MAC_DEBUG_SCALE
    fprintf(stderr,"MacScaleEventProc\n" );
#endif

    /*
     * To call Macintosh control routines we must have the port
     * set to the window containing the control. We will then test
     * which part of the control was hit and act accordingly.
     */

    destPort = TkMacOSXGetDrawablePort(Tk_WindowId(macScalePtr->info.tkwin));
    portChanged = QDSwapPort(destPort, &savePort);
    TkMacOSXSetUpClippingRgn(Tk_WindowId(macScalePtr->info.tkwin));

    TkMacOSXWinBounds((TkWindow *) macScalePtr->info.tkwin, &bounds);
    where.h = eventPtr->xbutton.x + bounds.left;
    where.v = eventPtr->xbutton.y + bounds.top;
#ifdef TK_MAC_DEBUG_SCALE
    TkMacOSXDbgMsg("calling TestControl");
#endif
    part = TestControl(macScalePtr->scaleHandle, where);
    if (part == 0) {
	return;
    }

    TkMacOSXTrackingLoop(1);
    part = HandleControlClick(macScalePtr->scaleHandle, where,
	    TkMacOSXModifierState(), scaleActionProc);
    TkMacOSXTrackingLoop(0);

    /*
     * Update the value for the widget.
     */

    macScalePtr->info.value = GetControlValue(macScalePtr->scaleHandle);
    /* TkScaleSetValue(&macScalePtr->info, macScalePtr->info.value, 1, 0); */

    /*
     * The HandleControlClick call will "eat" the ButtonUp event. We now
     * generate a ButtonUp event so Tk will unset implicit grabs etc.
     */

    TkGenerateButtonEventForXPointer(Tk_WindowId(macScalePtr->info.tkwin));

    if (portChanged) {
	QDSwapPort(savePort, NULL);
    }
}
コード例 #3
0
int
TkpScaleElement(
    TkScale *scalePtr,		/* Widget record for scale. */
    int x, int y)		/* Coordinates within scalePtr's window. */
{
    MacScale *macScalePtr = (MacScale *) scalePtr;
    ControlPartCode part;
    Point where;
    Rect bounds;
    CGrafPtr destPort, savePort;
    Boolean portChanged;

#ifdef TK_MAC_DEBUG_SCALE
    TkMacOSXDbgMsg("TkpScaleElement");
#endif
    destPort = TkMacOSXGetDrawablePort(Tk_WindowId(scalePtr->tkwin));
    portChanged = QDSwapPort(destPort, &savePort);

    /*
     * All of the calculations in this procedure mirror those in
     * DisplayScrollbar. Be sure to keep the two consistent.
     */

    TkMacOSXWinBounds((TkWindow *) scalePtr->tkwin, &bounds);
    where.h = x + bounds.left;
    where.v = y + bounds.top;
    part = TestControl(macScalePtr->scaleHandle, where);

    if (portChanged) {
	QDSwapPort(savePort, NULL);
    }

#ifdef TK_MAC_DEBUG_SCALE
    fprintf (stderr,"ScalePart %d, pos ( %d %d )\n", part, where.h, where.v );
#endif

    switch (part) {
	case inSlider:
	    return SLIDER;
	case inInc:
	    if (scalePtr->orient == ORIENT_VERTICAL) {
		return TROUGH1;
	    } else {
		return TROUGH2;
	    }
	case inDecr:
	    if (scalePtr->orient == ORIENT_VERTICAL) {
		return TROUGH2;
	    } else {
		return TROUGH1;
	    }
	default:
	    return OTHER;
    }
}
コード例 #4
0
Boolean TActiveScroller::HandleMouseClick(Point inPoint)
{
	#if TARGET_API_MAC_CARBON==1
		UPortSaver		safe(GetControlOwner(scrollBar));
	#else
		UPortSaver		safe((**scrollBar).contrlOwner);
	#endif

	short			partCode;

	if (partCode=TestControl(scrollBar,inPoint))
	{
		switch (partCode)
		{
			case kControlUpButtonPart:
			case kControlDownButtonPart:
			case kControlPageDownPart:
			case kControlPageUpPart:
			case kControlIndicatorPart:
				SetupScroll();
				if (partCode==kControlIndicatorPart)
				{
					// Dim the thumb
					HiliteControl(scrollBar,kControlIndicatorPart);
					IndicatorProc(inPoint);
					// Undim the thumb
					HiliteControl(scrollBar,kControlNoPart);
				}
				else
				{
					// Must call this to pass the current object to the tracking proc
					SetTrackingObject(this);
					TrackControl(scrollBar,inPoint,scrollBarProcUPP);
				}
				ShutdownScroll();
				return true;
				break;
		}
	}
	
	
	return false;	// missed the scroll bar
}
コード例 #5
0
sint32 c3_PopupWindow::AddOk
(
	void (*actionFunc)( aui_Control *, uint32, uint32, void *),
	void *cookie,
	MBCHAR *buttonBlock
)
{
	if (m_ok)
	{
		// No action: already created
	}
	else
	{
		AUI_ERRCODE errcode = AUI_ERRCODE_OK;
		m_ok = new ctp2_Button( &errcode, aui_UniqueId(), buttonBlock, actionFunc, cookie );
		TestControl( m_ok );
		m_ok->Move( m_width - m_ok->Width() - 17, m_height - m_ok->Height() - 17);
		InsertChild(m_ok, 0);
		m_ok->SetParentWindow(this);
	}

	return 1;
}
コード例 #6
0
ファイル: ZActor.cpp プロジェクト: Asunaya/RefinedGunz
void ZActor::OnUpdate(float fDelta)
{
	if(m_pVMesh) {
		m_pVMesh->SetVisibility(1.f);
	}

	if (CheckFlag(AF_MY_CONTROL))
	{
		m_TaskManager.Run(fDelta);
		CheckDead(fDelta);

		ProcessNetwork(fDelta);

		// for test - bird
		if (m_bTestControl)
		{
			TestControl(fDelta);
		}
		else
		{
			__BP(60,"ZActor::OnUpdate::ProcessAI");
			if(isThinkAble())
				ProcessAI(fDelta);
			__EP(60);
		}

		ProcessMovement(fDelta);
	}
	
	ProcessMotion(fDelta);

	if (CheckFlag(AF_MY_CONTROL))
	{
		UpdateHeight(fDelta);
	}
}
コード例 #7
0
void UpdateEditor()
{
	static GHEvent *pHold[8] = { (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1 };

	bool ctrlState = MFInput_Read(Key_LControl, IDD_Keyboard) || MFInput_Read(Key_RControl, IDD_Keyboard);
	bool shiftState = MFInput_Read(Key_LShift, IDD_Keyboard) || MFInput_Read(Key_LShift, IDD_Keyboard);
	bool altState = MFInput_Read(Key_LAlt, IDD_Keyboard) || MFInput_Read(Key_RAlt, IDD_Keyboard);

	int res = gEditor.pSong->GetRes();

	if(TestControl(dBCtrl_Edit_Save, GHCT_Once))
	{
		gEditor.pSong->SaveChart();
		MFVoice *pVoice = MFSound_Play(gEditor.pSaveSound, MFPF_BeginPaused);
		MFSound_SetVolume(pVoice, gConfig.sound.fxLevel);
		MFSound_Pause(pVoice, false);

		if(gConfig.editor.saveAction[0])
			system(gConfig.editor.saveAction);
	}

	if(TestControl(dBCtrl_Edit_Event, GHCT_Once))
		PlaceEvent();
	else if(TestControl(dBCtrl_Edit_TrackEvent, GHCT_Once))
		PlaceTrackEvent();
	else if(TestControl(dBCtrl_Edit_Section, GHCT_Once))
		PlaceSection();
	else if(TestControl(dBCtrl_Edit_Quantise, GHCT_Once))
		gpStringBox->Show(MFTranslation_GetString(pStrings, MENU_SETQUANTISE), "", SetCustomQuantisation);
	else if(TestControl(dBCtrl_Edit_Mixer, GHCT_Once))
		((EditorScreen*)dBScreen::GetCurrent())->gMixer.Push();

	// check selection
	if(bSelecting && !TestControl(dBCtrl_Edit_RangeSelect, GHCT_Hold))
	{
		gEditor.selectEnd = gEditor.offset;
		bSelecting = false;
	}
	else if(!bSelecting && TestControl(dBCtrl_Edit_RangeSelect, GHCT_Hold))
	{
		gEditor.selectStart = gEditor.selectEnd = gEditor.offset;
		bSelecting = true;
	}

	if(TestControl(dBCtrl_Edit_Cut, GHCT_Once) || TestControl(dBCtrl_Edit_Copy, GHCT_Once))
	{
		if(gEditor.selectStart != gEditor.selectEnd)
		{
			gEditor.copyLen = gEditor.selectEnd - gEditor.selectStart;

			gEditor.selectEvents.Clear();

			GHEventManager &noteStream = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]];
			GHEvent *pEv = noteStream.GetNextEvent(gEditor.selectStart);

			while(pEv && pEv->tick < gEditor.selectEnd)
			{
				// copy the next pointer incase we cut the note.
				GHEvent *pNext = pEv->Next();

				// just cut/copy notes
				if(pEv->event == GHE_Note)
				{
					// copy to clipboard
					gEditor.selectEvents.AddEvent(pEv->event, pEv->tick - gEditor.selectStart, pEv->key, pEv->parameter);

					// if we cut
					if(TestControl(dBCtrl_Edit_Cut, GHCT_Once))
						pNext = noteStream.RemoveEvent(pEv);
				}

				pEv = pNext;
			}
		}
	}
	else if(TestControl(dBCtrl_Edit_Paste, GHCT_Once))
	{
		GHEvent *pEv = gEditor.selectEvents.First();

		if(pEv)
		{
			int curStream = gEditor.currentStream[gEditor.selectedStream];
			GHEventManager &noteStream = gEditor.pSong->notes[curStream];

			// delete notes in paste range
			GHEvent *pDel = noteStream.GetNextEvent(gEditor.offset);
			while(pDel && pDel->tick < gEditor.offset + gEditor.copyLen)
			{
				if(pDel->event == GHE_Note)
					pDel = noteStream.RemoveEvent(pDel);
				else
					pDel = pDel->Next();
			}

			// paste notes
			while(pEv)
			{
				noteStream.AddEvent(pEv->event, pEv->tick + gEditor.offset, pEv->key, pEv->parameter);
				pEv = pEv->Next();
			}

			gEditor.pSong->CalculateNoteTimes(curStream, gEditor.offset);
		}
	}

	if(TestControl(dBCtrl_Edit_Delete, GHCT_Once))
	{
		GHEventManager &notes = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]];
		if(gEditor.selectStart != gEditor.selectEnd)
		{
			// delete notes in selected range
			GHEvent *pDel = notes.GetNextEvent(gEditor.selectStart);
			while(pDel && pDel->tick < gEditor.selectEnd)
			{
				if(pDel->event == GHE_Note)
					pDel = notes.RemoveEvent(pDel);
				else
					pDel = pDel->Next();
			}
		}
		else
		{
			int numEvents = 0;
			uint32 eventTypes = 0;

			// find note events
			GHEvent *pEvent = notes.GetNextEvent(gEditor.offset);
			while(pEvent && pEvent->tick == gEditor.offset)
			{
				uint32 bit = 1 << pEvent->event;
				if(!(eventTypes & bit))
				{
					++numEvents;
					eventTypes |= bit;
				}
				pEvent = pEvent->Next();
			}

			// find sync events
			if(gEditor.offset > 0)
			{
				pEvent = gEditor.pSong->sync.GetNextEvent(gEditor.offset);
				while(pEvent && pEvent->tick == gEditor.offset)
				{
					uint32 bit = 1 << pEvent->event;
					if(!(eventTypes & bit))
					{
						++numEvents;
						eventTypes |= bit;
					}
					pEvent = pEvent->Next();
				}
			}

			// find global events
			pEvent = gEditor.pSong->events.GetNextEvent(gEditor.offset);
			while(pEvent && pEvent->tick == gEditor.offset)
			{
				uint32 bit = 1 << pEvent->event;
				if(!(eventTypes & bit))
				{
					++numEvents;
					eventTypes |= bit;
				}
				pEvent = pEvent->Next();
			}

			// if there are multiple event types to remove, show a list box
			if(numEvents > 1)
				gpListBox->Show(MFTranslation_GetString(pStrings, SELECT_EVENT_REMOVE), RemoveEventCallback, 200.0f, 100.0f);

			for(int a=0; a<GHE_Max; ++a)
			{
				if(eventTypes & (1 << a))
				{
					if(numEvents > 1)
						gpListBox->AddItem(MFTranslation_GetString(pStrings, EVENT_TYPE_UNKNOWN+a), (void*)(size_t)a);
					else
						RemoveEventCallback(false, 0, NULL, (void*)(size_t)a);
				}
			}
		}
	}

	// shift notes left or right
	int selStart, selEnd;

	if(gEditor.selectStart != gEditor.selectEnd)
	{
		selStart = gEditor.selectStart;
		selEnd = gEditor.selectEnd;
	}
	else
	{
		selStart = gEditor.offset;
		selEnd = gEditor.pSong->GetLastNoteTick();
	}

	if(TestControl(dBCtrl_Edit_ShiftForwards, GHCT_Delay))
	{
		int offset = 4*res / gEditor.quantisation;

		// TODO: gotta remove notes after the selection that will be overwritten as the selection shifts..

		if(altState)
		{
			// shift events, sync and other tracks too
			GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				if(pEv->tick != 0)
					pEv->tick += offset;
				pEv = pEv->Next();
			}

			pEv = gEditor.pSong->events.GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				pEv->tick += offset;
				pEv = pEv->Next();
			}

			for(int i=0; i<GHS_Max; ++i)
			{
				pEv = gEditor.pSong->notes[i].GetNextEvent(selStart);
				while(pEv && pEv->tick < selEnd)
				{
					pEv->tick += offset;
					pEv = pEv->Next();
				}
			}
		}
		else
		{
			GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				pEv->tick += offset;
				pEv = pEv->Next();
			}
		}

		gEditor.selectStart += offset;
		gEditor.selectEnd += offset;
		gEditor.offset += offset;

		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[gEditor.selectedStream], gEditor.offset);
	}
	if(TestControl(dBCtrl_Edit_ShiftBackwards, GHCT_Delay))
	{
		int offset = MFMin(4*res / gEditor.quantisation, gEditor.selectStart);

		// TODO: gotta remove notes before the selection that will be overwritten as the selection shifts..

		if(altState)
		{
			// shift events, sync and other tracks too
			GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				if(pEv->tick != 0)
					pEv->tick -= offset;
				pEv = pEv->Next();
			}

			pEv = gEditor.pSong->events.GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				pEv->tick -= offset;
				pEv = pEv->Next();
			}

			for(int i=0; i<GHS_Max; ++i)
			{
				pEv = gEditor.pSong->notes[i].GetNextEvent(selStart);
				while(pEv && pEv->tick < selEnd)
				{
					pEv->tick -= offset;
					pEv = pEv->Next();
				}
			}
		}
		else
		{
			GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				pEv->tick -= offset;
				pEv = pEv->Next();
			}
		}

		gEditor.selectStart -= offset;
		gEditor.selectEnd -= offset;
		gEditor.offset -= offset;

		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[gEditor.selectedStream], gEditor.offset);
	}
	if(TestControl(dBCtrl_Edit_ShiftLeft, GHCT_Once))
	{
		GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart);

		while(pEv && pEv->tick < selEnd)
		{
			if(pEv->event == GHE_Note)
				pEv->key = MFMax(0, pEv->key - 1);

			pEv = pEv->Next();
		}
	}
	if(TestControl(dBCtrl_Edit_ShiftRight, GHCT_Once))
	{
		GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart);

		while(pEv && pEv->tick < selEnd)
		{
			if(pEv->event == GHE_Note)
				pEv->key = MFMin(4, pEv->key + 1);

			pEv = pEv->Next();
		}
	}

	// change quantisation
	if(TestControl(dBCtrl_Edit_QuantiseDown, GHCT_Delay))
	{
		gEditor.quantiseStep = MFMax(0, gEditor.quantiseStep-1);
		gEditor.quantisation = gQuantiseSteps[gEditor.quantiseStep];
		OffsetToMeasureAndBeat(gEditor.offset, &gEditor.measure, &gEditor.beat);

		MFVoice *pVoice = MFSound_Play(gEditor.pChangeSound, MFPF_BeginPaused);
		MFSound_SetVolume(pVoice, gConfig.sound.fxLevel);
		MFSound_Pause(pVoice, false);
	}
	if(TestControl(dBCtrl_Edit_QuantiseUp, GHCT_Delay))
	{
		gEditor.quantiseStep = MFMin((int)(sizeof(gQuantiseSteps)/sizeof(gQuantiseSteps[0]))-1, gEditor.quantiseStep+1);
		gEditor.quantisation = gQuantiseSteps[gEditor.quantiseStep];
		OffsetToMeasureAndBeat(gEditor.offset, &gEditor.measure, &gEditor.beat);

		MFVoice *pVoice = MFSound_Play(gEditor.pChangeSound, MFPF_BeginPaused);
		MFSound_SetVolume(pVoice, gConfig.sound.fxLevel);
		MFSound_Pause(pVoice, false);
	}

	// move the track
	if(TestControl(dBCtrl_Edit_Forward, GHCT_Delay))
	{
		// forward one step
		++gEditor.beat;
		if(gEditor.beat == gEditor.quantisation)
		{
			++gEditor.measure;
			gEditor.beat = 0;
		}
	}
	if(TestControl(dBCtrl_Edit_Back, GHCT_Delay))
	{
		// back one step
		if(gEditor.measure || gEditor.beat)
		{
			--gEditor.beat;
			if(gEditor.beat < 0)
			{
				--gEditor.measure;
				gEditor.beat += gEditor.quantisation;
			}
		}
	}
	if(TestControl(dBCtrl_Edit_Start, GHCT_Once))
	{
		// go to start
		gEditor.measure = gEditor.beat = 0;
	}
	if(TestControl(dBCtrl_Edit_End, GHCT_Once))
	{
		// go to the last note...
		OffsetToMeasureAndBeat(gEditor.pSong->GetLastNoteTick(), &gEditor.measure, &gEditor.beat);
	}
	if(TestControl(dBCtrl_Edit_UpMeasure, GHCT_Delay))
	{
		// forward one measure
		// TODO: consider bar lengths while moving
		++gEditor.measure;
	}
	if(TestControl(dBCtrl_Edit_DownMeasure, GHCT_Delay))
	{
		// back one measure
		// TODO: consider bar lengths while moving
		if(gEditor.measure < 1)
			gEditor.measure = gEditor.beat = 0;
		else
			--gEditor.measure;
	}
	if(TestControl(dBCtrl_Edit_NextSection, GHCT_Delay))
	{
		GHEvent *pEv = gEditor.pSong->events.GetNextEvent(gEditor.offset);

		while(pEv)
		{
			if(pEv->tick >= gEditor.offset + gEditor.pSong->resolution*4 / gEditor.quantisation && !MFString_CompareN(pEv->GetString(), "section ", 8))
			{
				OffsetToMeasureAndBeat(pEv->tick, &gEditor.measure, &gEditor.beat);
				break;
			}

			pEv = pEv->Next();
		}

		if(!pEv)
			OffsetToMeasureAndBeat(gEditor.pSong->GetLastNoteTick(), &gEditor.measure, &gEditor.beat);
	}
	if(TestControl(dBCtrl_Edit_PrevSection, GHCT_Delay))
	{
		GHEvent *pMostRecent = NULL;
		GHEvent *pEv = gEditor.pSong->events.First();

		while(pEv && pEv->tick < gEditor.offset)
		{
			if(!MFString_CompareN(pEv->GetString(), "section ", 8))
				pMostRecent = pEv;

			pEv = pEv->Next();
		}

		if(pMostRecent)
			OffsetToMeasureAndBeat(pMostRecent->tick, &gEditor.measure, &gEditor.beat);
		else
			gEditor.measure = gEditor.beat = 0;
	}

	int newOffset = MeasureAndBeatToOffset(gEditor.measure, gEditor.beat);
	int shift = newOffset - gEditor.offset;
	shift = MFMax(gEditor.offset + shift, 0) - gEditor.offset;

	if(shift)
	{
		// update BPM if applicable
		int shiftStart, shiftEnd;

		if(shift > 0)
		{
			shiftStart = gEditor.offset;
			shiftEnd = gEditor.offset+shift + 1;
		}
		else
		{
			shiftStart = 0;
			shiftEnd = gEditor.offset+shift + 1;
		}

		gEditor.offset += shift;

		if(bSelecting)
			gEditor.selectEnd = gEditor.offset;

		GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(shiftStart);

		while(pEv && pEv->tick < shiftEnd)
		{
			if(pEv->event == GHE_BPM || pEv->event == GHE_Anchor)
				gEditor.currentBPM = pEv->parameter;
			else if(pEv->event == GHE_TimeSignature)
			{
				gEditor.currentTimeSignature = pEv->parameter;
				gEditor.lastTimeSignature = pEv->tick;
			}

			pEv = pEv->Next();
		}

		gEditor.playingTime = gEditor.pSong->CalculateTimeOfTick(gEditor.offset);

		MFVoice *pVoice = MFSound_Play(gEditor.pStepSound, MFPF_BeginPaused);
		if(pVoice)
		{
			MFSound_SetVolume(pVoice, gConfig.sound.fxLevel);
			MFSound_Pause(pVoice, false);
		}
	}

	// increase/decrease BPM
	if(gEditor.currentBPM > 1000 && TestControl(dBCtrl_Edit_DecreaseBPM, GHCT_Delay))
	{
		// reduce BPM
		int inc = 1000;

		if(shiftState)
			inc /= 10;
		if(ctrlState)
			inc /= 100;
		if(altState)
			inc *= 10;

		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0);
		if(!pEv) pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0);

		if(!pEv)
			pEv = gEditor.pSong->sync.AddEvent(GHE_BPM, gEditor.offset, 0, MFMax(gEditor.currentBPM - inc, 1000));
		else
			pEv->parameter = MFMax(pEv->parameter - inc, 1000);

		gEditor.currentBPM = pEv->parameter;

		// remove this BPM marker if its the same as the previous one..
		if(pEv->event != GHE_Anchor)
		{
			GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_BPM, gEditor.offset);
			if(pPrev && pPrev->parameter == pEv->parameter)
				gEditor.pSong->sync.RemoveEvent(pEv);
		}

		// recalculate the note times from this point on
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], gEditor.offset);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], gEditor.offset);
	}

	if(gEditor.currentBPM < 9999000 && TestControl(dBCtrl_Edit_IncreaseBPM, GHCT_Delay))
	{
		// increase BPM
		int inc = 1000;

		if(shiftState)
			inc /= 10;
		if(ctrlState)
			inc /= 100;
		if(altState)
			inc *= 10;

		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0);
		if(!pEv) pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0);

		if(!pEv)
			pEv = gEditor.pSong->sync.AddEvent(GHE_BPM, gEditor.offset, 0, MFMin(gEditor.currentBPM + inc, 9999000));
		else
			pEv->parameter = MFMin(pEv->parameter + inc, 9999000);

		gEditor.currentBPM = pEv->parameter;

		// remove this BPM marker if its the same as the previous one..
		if(pEv->event != GHE_Anchor)
		{
			GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_BPM, gEditor.offset);
			if(pEv->event != GHE_Anchor && pPrev && pPrev->parameter == pEv->parameter)
				gEditor.pSong->sync.RemoveEvent(pEv);
		}

		// recalculate the note times from this point on
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], gEditor.offset);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], gEditor.offset);
	}

	// place anchor
	if(TestControl(dBCtrl_Edit_Anchor, GHCT_Once) && gEditor.offset > 0)
	{
		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0);

		if(pEv)
		{
			pEv->event = GHE_Anchor;
			pEv->time = gEditor.pSong->CalculateTimeOfTick(pEv->tick);
		}
		else
		{
			pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0);

			if(!pEv)
			{
				pEv = gEditor.pSong->sync.AddEvent(GHE_Anchor, gEditor.offset, 0, gEditor.currentBPM);
				pEv->time = gEditor.pSong->CalculateTimeOfTick(pEv->tick);
			}
			else
			{
				GHEvent *pLast = gEditor.pSong->sync.GetMostRecentSyncEvent(pEv->tick);

				if(pLast && pLast->parameter == pEv->parameter)
					gEditor.pSong->sync.RemoveEvent(pEv);
				else
					pEv->event = GHE_BPM;
			}
		}

		// recalculate the note times
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], 0);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], 0);
	}

	// change time signature
	if(TestControl(dBCtrl_Edit_DecreaseTS, GHCT_Delay) && gEditor.currentTimeSignature > 1)
	{
		int tsTime = gEditor.offset - ((gEditor.offset - gEditor.lastTimeSignature) % (res*gEditor.currentTimeSignature));

		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_TimeSignature, tsTime, 0);

		if(!pEv)
			pEv = gEditor.pSong->sync.AddEvent(GHE_TimeSignature, tsTime, 0, gEditor.currentTimeSignature - 1);
		else
			--pEv->parameter;

		gEditor.currentTimeSignature = pEv->parameter;
		gEditor.lastTimeSignature = tsTime;

		// remove this BPM marker if its the same as the previous one..
		GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_TimeSignature, tsTime);
		if(pPrev && pPrev->parameter == pEv->parameter)
		{
			gEditor.lastTimeSignature = pPrev->tick;
			gEditor.pSong->sync.RemoveEvent(pEv);
		}

		// recalculate the note times from this point on
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], tsTime);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], tsTime);
	}
	else if(TestControl(dBCtrl_Edit_IncreaseTS, GHCT_Delay) && gEditor.currentTimeSignature < 99)
	{
		int tsTime = gEditor.offset - ((gEditor.offset - gEditor.lastTimeSignature) % (res*gEditor.currentTimeSignature));

		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_TimeSignature, tsTime, 0);

		if(!pEv)
			pEv = gEditor.pSong->sync.AddEvent(GHE_TimeSignature, tsTime, 0, gEditor.currentTimeSignature + 1);
		else
			++pEv->parameter;

		gEditor.currentTimeSignature = pEv->parameter;
		gEditor.lastTimeSignature = tsTime;

		// remove this BPM marker if its the same as the previous one..
		GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_TimeSignature, tsTime);
		if(pPrev && pPrev->parameter == pEv->parameter)
		{
			gEditor.lastTimeSignature = pPrev->tick;
			gEditor.pSong->sync.RemoveEvent(pEv);
		}

		// recalculate the note times from this point on
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], tsTime);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], tsTime);
	}

	// add/remove notes
	GHEventManager &noteStream = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]];
	dBControlType keys_righty[] = { dBCtrl_Edit_Note0, dBCtrl_Edit_Note1, dBCtrl_Edit_Note2, dBCtrl_Edit_Note3, dBCtrl_Edit_Note4, dBCtrl_Edit_PS1, dBCtrl_Edit_PS2, dBCtrl_Edit_Note5 };
	dBControlType keys_lefty[] = { dBCtrl_Edit_Note4, dBCtrl_Edit_Note3, dBCtrl_Edit_Note2, dBCtrl_Edit_Note1, dBCtrl_Edit_Note0, dBCtrl_Edit_PS1, dBCtrl_Edit_PS2, dBCtrl_Edit_Note5 };
	dBControlType *keys = gConfig.controls.leftyFlip[0] ? keys_lefty : keys_righty;

	for(int a=0; a<8; a++)
	{
		GHEventType ev = a < 5 ? GHE_Note : GHE_Special;
		int key = a < 5 ? GHEK_Green + a : GHS_Player1 + (a - 5);

		if(TestControl(keys[a], GHCT_Hold))
		{
			if(pHold[a])
			{
				if(pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF)
				{
					pHold[a]->parameter = MFMax(gEditor.offset - pHold[a]->tick, 0);
				}
			}
			else
			{
				GHEvent *pEv = noteStream.FindEvent(ev, gEditor.offset, key);

				if(pEv)
				{
					noteStream.RemoveEvent(pEv);
					pHold[a] = (GHEvent*)(size_t)0xFFFFFFFF;
					for(int i=0; i<8; ++i)
					{
						if(pHold[i] && pHold[i] != (GHEvent*)(size_t)0xFFFFFFFF)
						{
							if(pHold[i] > pEv)
								--pHold[i];
						}
					}
				}
				else
				{
					// check if we are intersecting a hold note
					pEv = noteStream.GetMostRecentEvent(GHE_Note, gEditor.offset);

					if(pEv && pEv->parameter > gEditor.offset - pEv->tick)
					{
						// the last note was a hold note, we'll cut it short...
						do
						{
							pEv->parameter = gEditor.offset - pEv->tick;
							pEv = pEv->Prev();
						}
						while(pEv && pEv->tick == pEv->Next()->tick);
					}

					pEv = noteStream.AddEvent(ev, gEditor.offset, key);
					pEv->time = gEditor.pSong->CalculateTimeOfTick(gEditor.offset);
					pHold[a] = pEv;
				}
			}
		}
		else
		{
			if(a<5)
			{
				// check if we have just released a hold note
				if(pHold[a] && pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF && pHold[a]->parameter != 0)
				{
					// remove any other notes within the hold range
					GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(pHold[a]->tick);

					while(pEv && pEv->tick < pHold[a]->tick+pHold[a]->parameter)
					{
						GHEvent *pNext = pEv->Next();

						if(pEv->event == GHE_Note)
						{
							// and make sure we dont remove chords
							if(pHold[a]->tick != pEv->tick || pHold[a]->parameter != pEv->parameter)
							{
								pNext = noteStream.RemoveEvent(pEv);
								for(int i=0; i<8; ++i)
								{
									if(pHold[i] && pHold[i] != (GHEvent*)(size_t)0xFFFFFFFF)
									{
										if(pHold[i] > pEv)
											--pHold[i];
									}
								}
							}
						}

						pEv = pNext;
					}
				}
			}
			else
			{
				// remove zero length special events...
				if(pHold[a] && pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF && pHold[a]->parameter == 0)
				{
					noteStream.RemoveEvent(pHold[a]);
				}
			}

			pHold[a] = NULL;
		}
	}
}
コード例 #8
0
void Editor::UpdateInput()
{
	GHScreen::UpdateInput();

	// check for main menu
	if(TestControl(dBCtrl_Edit_Menu, GHCT_Once))
		gMenu.Push();

	if(TestControl(dBCtrl_Edit_PopupMenu, GHCT_Once))
		ShowPopupMenu();

	// check for section jump
	if(TestControl(dBCtrl_Edit_Goto, GHCT_Once))
		gpStringBox->Show(MFTranslation_GetString(pStrings, MENU_GOTO), "", GotoSection);

	// update the editor.
	UpdateEditor();

	// enable/disable half time
	if(TestControl(dBCtrl_Edit_PlaybackRate, GHCT_Once))
	{
		bHalfSpeed.data = (bHalfSpeed.data + 1) % (int)(sizeof(gSpeeds) / sizeof(gSpeeds[0]));

		if(gEditor.pSong->pStream)
			MFSound_SetPlaybackRate(MFSound_GetStreamVoice(gEditor.pSong->pStream), gSpeeds[bHalfSpeed]);
		if(gEditor.pSong->pGuitar)
			MFSound_SetPlaybackRate(MFSound_GetStreamVoice(gEditor.pSong->pGuitar), gSpeeds[bHalfSpeed]);
		if(gEditor.pSong->pBass)
			MFSound_SetPlaybackRate(MFSound_GetStreamVoice(gEditor.pSong->pBass), gSpeeds[bHalfSpeed]);
	}

	// swap modes
	if(TestControl(dBCtrl_Edit_StartPlayback, GHCT_Once) || TestControl(dBCtrl_Edit_RestartPlayback, GHCT_Once))
	{
		gEditor.state = GHPS_Playing;

		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], 0);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], 0);

		if(TestControl(dBCtrl_Edit_RestartPlayback, GHCT_Once))
		{
			gEditor.playbackStartOffset = 0;
			gEditor.playingTime = 0;
		}
		else
		{
			gEditor.playbackStartOffset = gEditor.offset;
			gEditor.playingTime = gEditor.pSong->CalculateTimeOfTick(gEditor.offset);
		}

		gEditor.pSong->Play(gEditor.playingTime);

		gPlay.scoreKeeper[0].Begin(gEditor.pSong, (GHStreams)gEditor.currentStream[0], 0, gEditor.playingTime);
		if(gEditor.currentStream[1] != -1)
			gPlay.scoreKeeper[1].Begin(gEditor.pSong, (GHStreams)gEditor.currentStream[1], 1, gEditor.playingTime);

		if(gEditor.pSong->pStream || gEditor.pSong->pGuitar || gEditor.pSong->pBass)
		{
			gEditor.playStart = 0;
			gEditor.lastTimestamp = 0;
		}
		else
		{
			gEditor.playStart = MFSystem_ReadRTC();
		}
		gEditor.startTime = gEditor.playingTime;

		bSelecting = false;

		Pop();
		gPlay.Push();
	}
}
コード例 #9
0
ファイル: Track.cpp プロジェクト: KingDwag/feedback-editor
void Fretboard::Draw(float time, dBChart *pSong, int track)
{
	MFCALLSTACKc;

	MFView_Push();

	MFRect rect;
	MFView_GetViewport(&rect);

	float aspect = rect.width / rect.height;
	aspect = MFClamp(0.82f, aspect, 2.0f);
	MFView_SetAspectRatio(aspect);

	if(viewPoint == 0)
	{
		// incoming
		MFView_ConfigureProjection(MFDEGREES(65.0f)/aspect, 1.0f, 100.0f);

		// Setup the Camera in 3D space.
		MFMatrix cameraMatrix;
		MFVector start = MakeVector( 0, 8, 3 );
		MFVector dir = MakeVector( 0, 5, -8 );
		dir = (dir-start) * (1.0f/1.777777777f);
		cameraMatrix.LookAt(start + dir*aspect, MakeVector(0.0f, 0.0f, 5.0f));
		MFView_SetCameraMatrix(cameraMatrix);
	}
	else if(viewPoint == 1)
	{
		// overhead
		MFView_ConfigureProjection(MFDEGREES(45.0f), 1.0f, 100.0f);
/*
		float aspect = MFDisplay_GetNativeAspectRatio();
		MFView_SetAspectRatio(aspect);

		MFRect projRect;
		projRect.y = 15;
		projRect.height = -30;
		projRect.x = -projRect.y * aspect;
		projRect.width = -projRect.height * aspect;
		MFView_SetOrtho(&projRect);
*/

		// Setup the Camera in 3D space.
		MFMatrix cameraMatrix;
		cameraMatrix.LookAt(MakeVector(0, 30, 10), MakeVector(0, 0, 10), MakeVector(0, 0, 1));
		MFView_SetCameraMatrix(cameraMatrix);
	}
	else if(viewPoint == 2)
	{
		// overhead
		MFView_ConfigureProjection(MFDEGREES(45.0f), 1.0f, 100.0f);
/*
		float aspect = MFDisplay_GetNativeAspectRatio();
		MFView_SetAspectRatio(aspect);

		MFRect projRect;
		projRect.y = 15;
		projRect.height = -30;
		projRect.x = -projRect.y * aspect;
		projRect.width = -projRect.height * aspect;
		MFView_SetOrtho(&projRect);
*/

		// Setup the Camera in 3D space.
		MFMatrix cameraMatrix;
		cameraMatrix.LookAt(MakeVector(0, 20, 13), MakeVector(0, 0, 13), MakeVector(-1, 0, 0));
		MFView_SetCameraMatrix(cameraMatrix);
	}

	MFView_SetProjection();

	MFMaterial *pFB = pSong->pFretboard ? pSong->pFretboard : pFretboard;
	MFMaterial_SetMaterial(pFB);
	MFPrimitive(PT_TriStrip, 0);

	int start = -4;
	int end = 60;
	int fadeStart = end - 10;

	float fretboardRepeat = 15.0f;
	float fretboardWidth = 7.0f;

	float columnWidth = fretboardWidth / 5.0f;
	float ringBorder = 0.1f;

	// draw the fretboard...
	MFBegin(((end-start) / 4) * 2 + 2);
	MFSetColourV(MFVector::white);

	float halfFB = fretboardWidth*0.5f;

	float offset = time*scrollSpeed;
	float topTime = time + end/scrollSpeed;
	float bottomTime = time + start/scrollSpeed;

	int a;
	float textureOffset = fmodf(offset, fretboardRepeat);
	for(a=start; a<end; a+=4)
	{
		float z = (float)a;
		MFSetTexCoord1(1.0f, 1.0f - (z+textureOffset) / fretboardRepeat);
		MFSetPosition(halfFB, 0.0f, z);
		MFSetTexCoord1(0.0f, 1.0f - (z+textureOffset) / fretboardRepeat);
		MFSetPosition(-halfFB, 0.0f, z);
	}

	float z = (float)a;
	MFSetTexCoord1(1.0f, 1.0f - (z+textureOffset) / fretboardRepeat);
	MFSetPosition(halfFB, 0.0f, z);
	MFSetTexCoord1(0.0f, 1.0f - (z+textureOffset) / fretboardRepeat);
	MFSetPosition(-halfFB, 0.0f, z);

	MFEnd();

	// draw the selection region
	MFMaterial_SetMaterial(pFrets);
	MFPrimitive(PT_TriStrip, 0);

	if(gEditor.selectStart != gEditor.selectEnd)
	{
		float selectStartTime = GETSECONDS(pSong->CalculateTimeOfTick(gEditor.selectStart));
		float selectEndTime = GETSECONDS(pSong->CalculateTimeOfTick(gEditor.selectEnd));

		if(selectStartTime < topTime && selectEndTime > bottomTime)
		{
			selectStartTime = (MFMax(bottomTime, selectStartTime) - time) * scrollSpeed;
			selectEndTime = (MFMin(topTime, selectEndTime) - time) * scrollSpeed;

			MFBegin(4);
			MFSetColour(1.0f, 0.0f, 0.0f, 0.5f);
			MFSetPosition(-halfFB, 0.0f, selectEndTime);
			MFSetPosition(halfFB, 0.0f, selectEndTime);
			MFSetPosition(-halfFB, 0.0f, selectStartTime);
			MFSetPosition(halfFB, 0.0f, selectStartTime);
			MFEnd();
		}
	}

	// draw the fretboard edges and bar lines
	const float barWidth = 0.2f;

	MFMaterial_SetMaterial(pBar);
	MFPrimitive(PT_TriStrip, 0);

	MFBegin(4);
	MFSetColour(0.0f, 0.0f, 0.0f, 0.8f);
	MFSetTexCoord1(0,0);
	MFSetPosition(-halfFB, 0.0f, barWidth);
	MFSetTexCoord1(1,0);
	MFSetPosition(halfFB, 0.0f, barWidth);
	MFSetTexCoord1(0,1);
	MFSetPosition(-halfFB, 0.0f, -barWidth);
	MFSetTexCoord1(1,1);
	MFSetPosition(halfFB, 0.0f, -barWidth);
	MFEnd();

	MFMaterial_SetMaterial(pEdge);
	MFPrimitive(PT_TriStrip, 0);
	MFBegin(34);

	MFSetColour(0.0f, 0.0f, 0.0f, 0.3f);
	for(int col=1; col<5; col++)
	{
		if(col > 1)
			MFSetPosition(-halfFB + columnWidth*(float)col - 0.02f, 0.0f, (float)end);

		MFSetTexCoord1(0,0);
		MFSetPosition(-halfFB + columnWidth*(float)col - 0.02f, 0.0f, (float)end);
		MFSetTexCoord1(1,0);
		MFSetPosition(-halfFB + columnWidth*(float)col + 0.02f, 0.0f, (float)end);
		MFSetTexCoord1(0,1);
		MFSetPosition(-halfFB + columnWidth*(float)col - 0.02f, 0.0f, (float)start);
		MFSetTexCoord1(1,1);
		MFSetPosition(-halfFB + columnWidth*(float)col + 0.02f, 0.0f, (float)start);

		MFSetPosition(-halfFB + columnWidth*(float)col + 0.02f, 0.0f, (float)start);
	}
	MFSetColourV(MFVector::white);
	MFSetPosition(-halfFB - 0.1f, 0.0f, (float)end);

	MFSetTexCoord1(0,0);
	MFSetPosition(-halfFB - 0.1f, 0.0f, (float)end);
	MFSetTexCoord1(1,0);
	MFSetPosition(-halfFB + 0.1f, 0.0f, (float)end);
	MFSetTexCoord1(0,1);
	MFSetPosition(-halfFB - 0.1f, 0.0f, (float)start);
	MFSetTexCoord1(1,1);
	MFSetPosition(-halfFB + 0.1f, 0.0f, (float)start);

	MFSetPosition(-halfFB + 0.1f, 0.0f, (float)start);
	MFSetPosition(halfFB - 0.1f, 0.0f, (float)end);

	MFSetTexCoord1(0,0);
	MFSetPosition(halfFB - 0.1f, 0.0f, (float)end);
	MFSetTexCoord1(1,0);
	MFSetPosition(halfFB + 0.1f, 0.0f, (float)end);
	MFSetTexCoord1(0,1);
	MFSetPosition(halfFB - 0.1f, 0.0f, (float)start);
	MFSetTexCoord1(1,1);
	MFSetPosition(halfFB + 0.1f, 0.0f, (float)start);

	MFEnd();

	// draw the frets....
	MFMaterial_SetMaterial(pBar);
	MFPrimitive(PT_TriStrip, 0);

	int bottomTick = pSong->CalculateTickAtTime((int64)(bottomTime*1000000.0f));
	int res = pSong->GetRes();
	int ticks = bHalfFrets ? res/2 : res;
	int fretBeat = bottomTick + ticks - 1;
	fretBeat -= fretBeat % ticks;
	float fretTime = GETSECONDS(pSong->CalculateTimeOfTick(fretBeat));

	while(fretTime < topTime)
	{
		bool halfBeat = (fretBeat % res) != 0;
		bool bar = false;

		if(!halfBeat)
		{
			GHEvent *pLastTS = pSong->sync.GetMostRecentEvent(GHE_TimeSignature, fretBeat);

			if(pLastTS)
				bar = ((fretBeat - pLastTS->tick) % (pLastTS->parameter*res)) == 0;
			else if(fretBeat == 0)
				bar = true;
		}

		float bw = bar ? barWidth : barWidth*0.5f;
		MFBegin(4);

		float position = (fretTime - time) * scrollSpeed;

		if(!halfBeat)
			MFSetColourV(MFVector::white);
		else
			MFSetColourV(MakeVector(1,1,1,0.3f));
		MFSetTexCoord1(0,0);
		MFSetPosition(-halfFB, 0.0f, position + bw);
		MFSetTexCoord1(1,0);
		MFSetPosition(halfFB, 0.0f, position + bw);
		MFSetTexCoord1(0,1);
		MFSetPosition(-halfFB, 0.0f, position + -bw);
		MFSetTexCoord1(1,1);
		MFSetPosition(halfFB, 0.0f, position + -bw);

		MFEnd();

		fretBeat += ticks;
		fretTime = GETSECONDS(pSong->CalculateTimeOfTick(fretBeat));
	}

	// draw the notes...
	GHEventManager &noteStream = pSong->notes[track];
	GHEvent *pEv = noteStream.First();

	int64 topTimeus = (int64)(topTime*1000000.0f);
	while(pEv && pEv->time < topTimeus)
	{
		if((pEv->event == GHE_Note || pEv->event == GHE_Special) && pEv->tick + pEv->parameter >= bottomTick)
		{
			float evTime = GETSECONDS(pEv->time);

			// TODO: we need to calculate the end of the hold...
			float noteEnd = evTime;
			if(pEv->parameter)
				noteEnd = GETSECONDS(pSong->CalculateTimeOfTick(pEv->tick + pEv->parameter));

			if(pEv->event == GHE_Note && pEv->played != 1)
			{
				// draw a note
				int key = pEv->key;
				bool tap = false;

				// check if there is a previous note, and it is in range
				if(pEv->Prev() && pEv->Prev()->tick < pEv->tick && pEv->Prev()->tick > pEv->tick - (res/2) && pEv->Prev()->key != pEv->key
					&& (!pEv->Next() || !(pEv->Next()->tick == pEv->tick))
					&& !pEv->Prev()->parameter && !(pEv->Prev()->Prev() && pEv->Prev()->Prev()->tick == pEv->Prev()->tick))
				{
					tap = true;
				}

				int noteX = gConfig.controls.leftyFlip[0] ? 4-key : key;

				float position = (GETSECONDS(pEv->time) - time)*scrollSpeed;
				float xoffset = -halfFB + columnWidth*0.5f + (float)noteX*columnWidth;

				if(pEv->parameter)
				{
					MFMaterial_SetMaterial(pFrets);

					float whammyTop = (noteEnd - time)*scrollSpeed;

					MFPrimitive(PT_TriStrip, 0);

					// TODO: we could consider not drawing this part of the hold line.. seems reasonable that it terminates at the line...
					if(gEditor.state == GHPS_Stopped)
					{
						if(position < 0.0f)
						{
							MFBegin(4);
							MFSetColourV(gColours[key]);
							MFSetPosition(xoffset - 0.2f, 0.0f, MFMin(whammyTop, 0.0f));
							MFSetPosition(xoffset + 0.2f, 0.0f, MFMin(whammyTop, 0.0f));
							MFSetPosition(xoffset - 0.2f, 0.0f, position);
							MFSetPosition(xoffset + 0.2f, 0.0f, position);
							MFEnd();
						}
					}

					if(whammyTop > 0.0f)
					{
						// this half could have waves cruising down it if we wanted to support the whammy...
						MFBegin(4);
						MFSetColourV(gColours[key]);
						MFSetPosition(xoffset - 0.2f, 0.0f, MFMin(whammyTop, (float)end));
						MFSetPosition(xoffset + 0.2f, 0.0f, MFMin(whammyTop, (float)end));
						MFSetPosition(xoffset - 0.2f, 0.0f, MFMax(position, 0.0f));
						MFSetPosition(xoffset + 0.2f, 0.0f, MFMax(position, 0.0f));
						MFEnd();
					}
				}

				if(evTime >= bottomTime)
				{
					MFMatrix mat;
					mat.SetScale(MakeVector(0.5f/20, 0.5f/20, 0.5f/20));
					mat.Translate(MakeVector(xoffset, 0.03f, position));
					MFModel_SetWorldMatrix(pButton, mat);

					MFStateBlock *pSB = MFStateBlock_CreateTemporary(64);
					MFStateBlock_SetVector(pSB, MFSCV_DiffuseColour, pEv->played == -1 ? MakeVector(0.3f, 0.3f, 0.3f, 1.0f) : MFVector::white);
//					MFStateBlock_SetVector(pSB, MFSCV_DiffuseColour, position < 0.0f ? MakeVector(0.3f, 0.3f, 0.3f, 1.0f) : MFVector::white);

//					MFRenderer_SetRenderStateOverride(MFRS_MaterialOverride, (uint32&)(tap ? pButtonMat[key] : pButtonRing[key]));
					MFRenderer_AddModel(pButton, pSB, MFView_GetViewState());

					// render the note time
					if(bRenderNoteTimes)
					{
						MFView_Push();
						MFView_SetOrtho(&rect);

						MFVector pos;
						MFView_TransformPoint3DTo2D(MakeVector(xoffset, 0.0f, position), &pos);
						pos.x += 16.0f;
						pos.y -= 26.0f;

						int minutes = (int)(pEv->time / 60000000);
						int seconds = (int)((pEv->time % 60000000) / 1000000);
						int milliseconds = (int)((pEv->time % 1000000) / 1000);
						MFFont_DrawTextf(pText, pos, 20.0f, MFVector::yellow, "%s: %d:%02d.%d\nTick: %g", MFTranslation_GetString(pStrings, TRACK_TIME), minutes, seconds, milliseconds, (float)pEv->tick/res);

						MFView_Pop();
					}
				}
			}

			if(pEv->event == GHE_Special)
			{
//				static MFVector specialColours[3] = { MakeVector(1,0,0,1), MakeVector(1,1,0,1), MakeVector(0,0,1,1) };
//				static float specialX[3] = { halfFB + 0.2f, halfFB + 1, -halfFB - 1 };
//				static float specialWidth[3] = { 0.8f, 0.8f, 0.8f };
				static MFVector specialColours[3] = { MakeVector(1,0,0,0.5f), MakeVector(1,1,0,0.5f), MakeVector(0,0,1,0.5f) };
				static float specialX[3] = { -halfFB, halfFB - 0.8f, -halfFB };
				static float specialWidth[3] = { 0.8f, 0.8f, fretboardWidth };

				float bottom = (evTime - time)*scrollSpeed;
				float top = (noteEnd - time)*scrollSpeed;

				int key = pEv->key;

				MFMaterial_SetMaterial(pFrets);

				MFPrimitive(PT_TriStrip, 0);
				MFBegin(4);
				MFSetColourV(specialColours[key]);
				MFSetPosition(specialX[key], 0.0f, MFMin(top, (float)end));
				MFSetPosition(specialX[key]+specialWidth[key], 0.0f, MFMin(top, (float)end));
				MFSetPosition(specialX[key], 0.0f, MFMax(bottom, (float)start));
				MFSetPosition(specialX[key]+specialWidth[key], 0.0f, MFMax(bottom, (float)start));
				MFEnd();
			}
		}

		pEv = pEv->Next();
	}

//	MFRenderer_SetRenderStateOverride(MFRS_MaterialOverride, NULL);

	// draw circles at the bottom..
	MFMaterial_SetMaterial(pRing);
	for(int a=0; a<5; a++)
	{
		DrawRing(-halfFB + (float)a*columnWidth + columnWidth*ringBorder, 0.0f, columnWidth*(1.0f-ringBorder*2));
	}

	for(int a=0; a<5; a++)
	{
		dBControlType keys_righty[] = { dBCtrl_Edit_Note0, dBCtrl_Edit_Note1, dBCtrl_Edit_Note2, dBCtrl_Edit_Note3, dBCtrl_Edit_Note4 };
		dBControlType keys_lefty[] = { dBCtrl_Edit_Note4, dBCtrl_Edit_Note3, dBCtrl_Edit_Note2, dBCtrl_Edit_Note1, dBCtrl_Edit_Note0 };
		dBControlType *keys = gConfig.controls.leftyFlip[0] ? keys_lefty : keys_righty;

		int ringPos = gConfig.controls.leftyFlip[0] ? 4-a : a;

		MFMaterial_SetMaterial(pColourRing[a]);
		DrawRing(-halfFB + (float)ringPos*columnWidth, 0.0f, columnWidth, !TestControl(keys[a], GHCT_Hold));
	}

	// render trigger particles
	MFParticleSystem_Draw(pParticles);

	// render text and stuff
	MFView_SetOrtho(&rect);

	pEv = pSong->sync.GetNextEvent(bottomTick);

	while(pEv && pEv->time < topTimeus)
	{
		float evTime = GETSECONDS(pEv->time);

		if(evTime > bottomTime)
		{
			if(pEv->event == GHE_BPM)
			{
				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(halfFB + 0.2f, 0.0f, position), &pos);
				pos.y -= 12.0f;
				MFFont_DrawTextf(pText, pos, 24.0f, MakeVector(0,0.5f,0,1), "%s: %g", MFTranslation_GetString(pStrings, TRACK_BPM), (float)pEv->parameter * 0.001f);
			}
			if(pEv->event == GHE_Anchor)
			{
				int minutes = (int)(pEv->time / 60000000);
				int seconds = (int)((pEv->time%60000000)/1000000);
				int milliseconds = (int)((pEv->time%1000000)/1000);

				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(halfFB + 0.2f, 0.0f, position), &pos);
				pos.y -= 12.0f;
				MFFont_DrawTextf(pText, pos, 24.0f, MakeVector(0,0.5f,0,1), "A: %02d:%02d.%03d\n   %s: %g", minutes, seconds, milliseconds, MFTranslation_GetString(pStrings, TRACK_BPM), (float)pEv->parameter * 0.001f);
			}
			else if(pEv->event == GHE_TimeSignature)
			{
				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(-halfFB - 0.2f, 0.0f, position), &pos);
				const char *pString = MFStr("TS: %d/4", pEv->parameter);
				pos.x -= MFFont_GetStringWidth(pText, pString, 24.0f);
				pos.y -= 12.0f;
				MFFont_DrawTextf(pText, pos, 24.0f, MFVector::yellow, pString);
			}
		}

		pEv = pEv->Next();
	}

	// render events
	pEv = pSong->events.GetNextEvent(bottomTick);

	int lastChecked = -1;
	float yEventOffset = -12.0f;
	while(pEv && pEv->time < topTimeus)
	{
		float evTime = GETSECONDS(pEv->time);

		if(evTime > bottomTime)
		{
			if(pEv->event == GHE_Event)
			{
				if(lastChecked != pEv->tick)
				{
					yEventOffset = -12.0f;
					lastChecked = pEv->tick;

					if(pSong->sync.FindEvent(GHE_TimeSignature, pEv->tick, 0))
					{
						yEventOffset -= 24.0f;
					}
				}

				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(-halfFB - 0.2f, 0.0f, position), &pos);

				if(!MFString_CompareN(pEv->GetString(), "section ", 8))
				{
					// draw a line across?

					pos.x -= MFFont_GetStringWidth(pText, &pEv->GetString()[8], 24.0f);
					pos.y += yEventOffset;
					MFFont_DrawTextf(pText, pos, 24.0f, MFVector::blue, &pEv->GetString()[8]);
				}
				else
				{
					pos.x -= MFFont_GetStringWidth(pText, pEv->GetString(), 24.0f);
					pos.y += yEventOffset;
					MFFont_DrawTextf(pText, pos, 24.0f, MFVector::white, pEv->GetString());
				}

				yEventOffset -= 24.0f;
			}
		}

		pEv = pEv->Next();
	}

	// render track events
	pEv = pSong->notes[track].GetNextEvent(bottomTick);

	lastChecked = -1;
	yEventOffset = -12.0f;
	while(pEv && pEv->time < topTimeus)
	{
		float evTime = GETSECONDS(pEv->time);

		if(evTime > bottomTime)
		{
			if(pEv->event == GHE_Event)
			{
				if(lastChecked != pEv->tick)
				{
					yEventOffset = -12.0f;
					lastChecked = pEv->tick;

					if(pSong->sync.FindEvent(GHE_TimeSignature, pEv->tick, 0))
					{
						yEventOffset -= 24.0f;
					}

					GHEvent *pOther = pSong->events.FindEvent(GHE_Event, pEv->tick, 0);
					while(pOther && pOther->tick == pEv->tick)
					{
						yEventOffset -= 24.0f;
						pOther = pOther->Next();
					}
				}

				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(-halfFB - 0.2f, 0.0f, position), &pos);

				pos.x -= MFFont_GetStringWidth(pText, pEv->GetString(), 24.0f);
				pos.y += yEventOffset;
				MFFont_DrawTextf(pText, pos, 24.0f, MakeVector(0.6f, 0.8f, 1.0f, 1.0f), pEv->GetString());

				yEventOffset -= 24.0f;
			}
		}

		pEv = pEv->Next();
	}

	MFView_Pop();
}