From ed2a79a74edb20ca1f448dd024abb280882aebb4 Mon Sep 17 00:00:00 2001 From: Matthias Neeracher Date: Sun, 28 Aug 2011 12:42:39 +0200 Subject: [PATCH] Refactor cursor handling from a pitch based approach to a grid based one --- Sources/VLSheetView.h | 15 ++-- Sources/VLSheetView.mm | 138 +++++++------------------------- Sources/VLSheetViewNotes.h | 3 +- Sources/VLSheetViewNotes.mm | 112 ++++++++++---------------- Sources/VLSheetViewSelection.mm | 32 ++------ 5 files changed, 88 insertions(+), 212 deletions(-) diff --git a/Sources/VLSheetView.h b/Sources/VLSheetView.h index 3074320..e0235c7 100644 --- a/Sources/VLSheetView.h +++ b/Sources/VLSheetView.h @@ -63,6 +63,12 @@ enum VLRecalc { kFirstRecalc }; +enum VLCursorVisual { + kCursorExtend = 1<<15, + kCursorFlagsMask= 0x8000, + kCursorNoPitch = -128 +}; + @class VLEditable; @interface VLSheetView : NSView { @@ -76,9 +82,8 @@ enum VLRecalc { VLRegion fCursorRegion; int fCursorMeasure; VLFract fCursorAt; - int fCursorPitch; - int fCursorActualPitch; - VLMusicElement fCursorAccidental; + int fCursorVertPos; + uint16_t fCursorVisual; size_t fCursorStanza; int fSelStart; int fSelEnd; @@ -121,11 +126,11 @@ enum VLRecalc { - (float) systemY:(int)system; - (int) gridInSection:(int)section withPitch:(int)pitch visual:(uint16_t)visual; +- (float) noteYInGrid:(int)vertPos; - (float) noteYInSection:(int)section withPitch:(int)pitch visual:(uint16_t *)visual; - (float) noteYInSection:(int)section withPitch:(int)pitch; - (VLMusicElement)accidentalForVisual:(uint16_t)visual; -- (float) noteYInMeasure:(int)measure withPitch:(int)pitch visual:(uint16_t *)visual; -- (float) noteYInMeasure:(int)measure withPitch:(int)pitch; +- (float) noteYInMeasure:(int)measure withGrid:(int)vertPos; - (float) noteXInMeasure:(int)measure at:(VLFraction)at; - (void) scrollMeasureToVisible:(int)measure; diff --git a/Sources/VLSheetView.mm b/Sources/VLSheetView.mm index d71443b..2ff3def 100644 --- a/Sources/VLSheetView.mm +++ b/Sources/VLSheetView.mm @@ -96,7 +96,8 @@ static float sFlatPos[] = { fNeedsRecalc = kFirstRecalc; fClickMode = ' '; fDisplayScale = 1.0f; - fCursorPitch = VLNote::kNoPitch; + fCursorVertPos = 0; + fCursorVisual = 0; fSelStart = 0; fSelEnd = -1; fNumTopLedgers = 0; @@ -152,12 +153,17 @@ static float sFlatPos[] = { return VLPitchToGrid(pitch, visual, key); } +- (float) noteYInGrid:(int)vertPos +{ + return (vertPos*0.5f - 1.0) * kLineH; +} + - (float) noteYInSection:(int)section withPitch:(int)pitch visual:(uint16_t *)visual { int key = [self song]->fProperties[section].fKey; int grid = VLPitchToGrid(pitch, *visual, key); - return (grid*0.5f - 1.0) * kLineH; + return [self noteYInGrid:grid]; } - (float) noteYInSection:(int)section withPitch:(int)pitch @@ -166,7 +172,7 @@ static float sFlatPos[] = { uint16_t visual = 0; int grid = VLPitchToGrid(pitch, visual, key); - return (grid*0.5f - 1.0) * kLineH; + return [self noteYInGrid:grid]; } - (VLMusicElement)accidentalForVisual:(uint16_t)visual @@ -187,20 +193,10 @@ static float sFlatPos[] = { } } -- (float) noteYInMeasure:(int)measure withPitch:(int)pitch visual:(uint16_t *)visual +- (float) noteYInMeasure:(int)measure withGrid:(int)vertPos { return [self systemY:fLayout->SystemForMeasure(measure)] - + [self noteYInSection:[self song]->fMeasures[measure].fPropIdx - withPitch:pitch visual:visual]; -} - -- (float) noteYInMeasure:(int)measure withPitch:(int)pitch -{ - uint16_t dummyVis = 0; - - return [self systemY:fLayout->SystemForMeasure(measure)] - + [self noteYInSection:[self song]->fMeasures[measure].fPropIdx - withPitch:pitch visual:&dummyVis]; + + [self noteYInGrid:vertPos]; } - (float) noteXInMeasure:(int)measure at:(VLFraction)at @@ -674,109 +670,38 @@ const char * sBreak[3] = {"", "\xE2\xA4\xBE", "\xE2\x8E\x98"}; [fFieldEditor setAction:nil]; } -const float kSemiFloor = -5.0f*kLineH; -static int8_t sSemiToPitch[] = { - 47, // B - 48, 50, // D - 52, 53, // F - 55, 57, // A - 59, 60, // Middle C - 62, 64, // E - 65, 67, // G - 69, 71, // B - 72, 74, // D - 76, 77, // F - 79, 81, // A - 83, 84, // C - 86, 88, // E - 89, 91, // G - 93, 95, // B - 96, 98 // D -}; - -static int8_t sFlatAcc[] = { - 6, // Cb - 11, - 4, // Db - 9, - 2, // Eb - 7, // Fb - 12, - 5, // Gb - 10, - 3, // Ab - 8, - 1, // Bb -}; - -static int8_t sSharpAcc[] = { - 2, // C# is the 2nd sharp - 9, - 4, // D# - 11, - 6, // E# - 1, // F# - 8, - 3, // G# - 10, - 5, // A# - 12, - 7, // B# -}; +const float kSemiFloor = -1.0f*kLineH; - (void) accidentalFromEvent:(NSEvent *)event { - fCursorAccidental = (VLMusicElement)0; - - // - // Extension - // - if (([event modifierFlags] & (NSShiftKeyMask|NSAlternateKeyMask|NSCommandKeyMask))==NSShiftKeyMask) { - fCursorAccidental = kMusicExtendCursor; - return; - } - int cursorSection = - [self song]->fMeasures[fCursorMeasure].fPropIdx; - const VLProperties & prop = - [self song]->fProperties[cursorSection]; - switch ([event modifierFlags] & (NSShiftKeyMask|NSAlternateKeyMask|NSCommandKeyMask)) { + case NSShiftKeyMask: + fCursorVisual = kCursorExtend; + break; case NSShiftKeyMask|NSAlternateKeyMask: - fCursorAccidental = kMusic2FlatCursor; // Gbb - fCursorActualPitch = fCursorPitch-2; + fCursorVisual = VLNote::kWant2Flat; // Gbb break; case NSAlternateKeyMask: - fCursorAccidental = kMusicFlatCursor; // Gb - fCursorActualPitch = fCursorPitch-1; + fCursorVisual = VLNote::kWantFlat; // Gb break; case NSShiftKeyMask|NSCommandKeyMask: - fCursorAccidental = kMusic2SharpCursor; // G## - fCursorActualPitch = fCursorPitch+2; + fCursorVisual = VLNote::kWant2Sharp; // G## break; case NSCommandKeyMask: - fCursorAccidental = kMusicSharpCursor; // G# - fCursorActualPitch = fCursorPitch+1; + fCursorVisual = VLNote::kWantSharp; // G# break; case NSAlternateKeyMask|NSCommandKeyMask: - fCursorAccidental = kMusicNaturalCursor; // G - fCursorActualPitch = fCursorPitch; + fCursorVisual = VLNote::kWantNatural; // G break; default: - switch (VLVisualInKey(fCursorPitch, prop.fKey)) { - case VLNote::kWantFlat: - fCursorActualPitch = fCursorPitch-1; - case VLNote::kWantSharp: - fCursorActualPitch = fCursorPitch+1; - default: - fCursorActualPitch = fCursorPitch; - } + fCursorVisual = 0; break; } } - (VLRegion) findRegionForEvent:(NSEvent *) event { - fCursorPitch = VLNote::kNoPitch; + fCursorVertPos = kCursorNoPitch; NSPoint loc = [event locationInWindow]; loc = [self convertPoint:loc fromView:nil]; @@ -841,8 +766,7 @@ static int8_t sSharpAcc[] = { } loc.y -= kSystemBaseline+kSemiFloor; - int semi = static_cast(roundf(loc.y / (0.5f*kLineH))); - fCursorPitch = sSemiToPitch[semi]; + fCursorVertPos = static_cast(roundf(loc.y / (0.5f*kLineH))); [self accidentalFromEvent:event]; @@ -854,16 +778,16 @@ static int8_t sSharpAcc[] = { if ([event modifierFlags] & NSAlphaShiftKeyMask) return; // Keyboard mode, ignore mouse - bool hadCursor = fCursorPitch != VLNote::kNoPitch; + bool hadCursor = fCursorRegion == kRegionNote; [self findRegionForEvent:event]; - bool hasCursor = fCursorPitch != VLNote::kNoPitch; + bool hasCursor = fCursorRegion == kRegionNote; [self setNeedsDisplay:(hadCursor || hasCursor)]; } - (void)flagsChanged:(NSEvent *)event { - if (fCursorPitch != VLNote::kNoPitch) { + if (fCursorRegion == kRegionNote) { [self accidentalFromEvent:event]; [self setNeedsDisplay:YES]; } @@ -877,7 +801,7 @@ static int8_t sSharpAcc[] = { - (void) mouseExited:(NSEvent *)event { - fCursorPitch = VLNote::kNoPitch; + fCursorRegion = kRegionNowhere; [[self window] setAcceptsMouseMovedEvents:NO]; [self setNeedsDisplay:YES]; } @@ -919,14 +843,6 @@ static int8_t sSharpAcc[] = { NSString * k = [event charactersIgnoringModifiers]; switch ([k characterAtIndex:0]) { - case '\r': - [self startKeyboardCursor]; - [self addNoteAtCursor]; - break; - case ' ': - [self startKeyboardCursor]; - VLSoundOut::Instance()->PlayNote(VLNote(1, fCursorPitch)); - break; case 'r': if (fClickMode == 'r') fClickMode = ' '; diff --git a/Sources/VLSheetViewNotes.h b/Sources/VLSheetViewNotes.h index aa9312d..3b1a4cf 100644 --- a/Sources/VLSheetViewNotes.h +++ b/Sources/VLSheetViewNotes.h @@ -14,8 +14,7 @@ - (void) drawNotesForSystem:(int)system; - (void) addNoteAtCursor; -- (void) startKeyboardCursor; -- (void) drawNoteCursor:(int)pitch inMeasure:(size_t)measure at:(VLFract)at accidental:(VLMusicElement)accidental; +- (void) drawNoteCursor:(int)vertPos inMeasure:(size_t)measure at:(VLFract)at visual:(uint16_t)visual; @end diff --git a/Sources/VLSheetViewNotes.mm b/Sources/VLSheetViewNotes.mm index f2b4c3a..e59d2af 100644 --- a/Sources/VLSheetViewNotes.mm +++ b/Sources/VLSheetViewNotes.mm @@ -21,58 +21,31 @@ - (void) addNoteAtCursor { - if (fCursorMeasure > -1 && fCursorActualPitch) { - VLNote newNote(1, fClickMode==' ' ? fCursorActualPitch : VLNote::kNoPitch); - switch (fCursorAccidental) { - case kMusicFlatCursor: - newNote.fVisual |= VLNote::kWantFlat; - break; - case kMusicSharpCursor: - newNote.fVisual |= VLNote::kWantSharp; - break; - case kMusic2FlatCursor: - newNote.fVisual |= VLNote::kWant2Flat; - break; - case kMusic2SharpCursor: - newNote.fVisual |= VLNote::kWant2Sharp; - break; - case kMusicNaturalCursor: - newNote.fVisual |= VLNote::kWantNatural; - break; - default: - break; - } + if (fCursorMeasure > -1 && fCursorVertPos != kCursorNoPitch) { [[self document] willChangeSong]; - if (fCursorAccidental == kMusicExtendCursor) - newNote = [self song]->ExtendNote(fCursorMeasure, fCursorAt); - else if (fClickMode == 'k') + if (fCursorVisual == kCursorExtend) { + VLNote oldNote = [self song]->ExtendNote(fCursorMeasure, fCursorAt); + VLSoundOut::Instance()->PlayNote(oldNote); + } else if (fClickMode == 'k') { [self song]->DelNote(fCursorMeasure, fCursorAt); - else + } else { + int pitch = VLNote::kNoPitch; + if (fClickMode == ' ') + pitch = VLGridToPitch(fCursorVertPos, fCursorVisual, + [self song]->Properties(fCursorMeasure).fKey); + VLNote newNote(1, pitch, fCursorVisual & ~kCursorFlagsMask); [self song]->AddNote(VLLyricsNote(newNote), fCursorMeasure, fCursorAt); - [[self document] didChangeSong]; - - if (fClickMode == ' ') VLSoundOut::Instance()->PlayNote(newNote); - else - fClickMode = ' '; + } + [[self document] didChangeSong]; } } -- (void) startKeyboardCursor -{ - if (fCursorMeasure < 0) { - fCursorMeasure = 0; - fCursorPitch = VLNote::kMiddleC; - fCursorActualPitch = fCursorPitch; - fCursorAt = VLFraction(0); - } -} - -- (void) drawLedgerLinesInSection:(int)section withPitch:(int)pitch visual:(uint16_t)visual at:(NSPoint)p +- (void) drawLedgerLines:(int)vertPos at:(NSPoint)p { p.x += kLedgerX; - int step = ([self gridInSection:section withPitch:pitch visual:visual]-2)/2; - + + int step = (vertPos-2) / 2; for (int i=0; i-- > step; ) { NSPoint p0 = p; p0.y += i*kLineH; @@ -89,36 +62,37 @@ } } -- (void) drawNoteCursor:(int)pitch inMeasure:(size_t)measure at:(VLFract)at - accidental:(VLMusicElement)accidental - mode:(char)mode +- (void) drawLedgerLinesInSection:(int)section withPitch:(int)pitch visual:(uint16_t)visual at:(NSPoint)p +{ + [self drawLedgerLines:[self gridInSection:section withPitch:pitch visual:visual] at:p]; +} + +- (void) drawNoteCursor:(int)vertPos inMeasure:(size_t)measure at:(VLFract)at + visual:(uint16_t)visual mode:(char)mode { int cursorX; int cursorY; - int cursorSect; VLMusicElement cursorElt; + VLMusicElement accidental = mode ? [self accidentalForVisual:visual] : kMusicNothing; cursorX = [self noteXInMeasure:measure at:at]; - if (accidental == kMusicExtendCursor) { - cursorY = [self noteYInMeasure:measure withPitch:pitch]; - cursorElt = accidental; + if (visual == kCursorExtend) { + cursorY = [self noteYInGrid:vertPos]; + cursorElt = kMusicExtendCursor; } else { - uint16_t visual = 0; switch (mode) { default: - cursorY = [self noteYInMeasure:measure withPitch:pitch visual:&visual] - kNoteY; - cursorSect = [self song]->fMeasures[measure].fPropIdx; - [self drawLedgerLinesInSection:cursorSect withPitch:pitch - visual:visual at:NSMakePoint(cursorX, - [self systemY:fLayout->SystemForMeasure(measure)])]; + cursorY = [self noteYInMeasure:measure withGrid:vertPos] - kNoteY; + [self drawLedgerLines:vertPos at:NSMakePoint(cursorX, + [self systemY:fLayout->SystemForMeasure(measure)])]; cursorElt = kMusicNoteCursor; break; case 'r': - cursorY = [self noteYInMeasure:measure withPitch:65]; + cursorY = [self noteYInMeasure:measure withGrid:3]; cursorElt = kMusicRestCursor; break; case 'k': - cursorY = [self noteYInMeasure:measure withPitch:pitch]; + cursorY = [self noteYInGrid:vertPos]; cursorElt = kMusicKillCursor; break; } @@ -128,9 +102,11 @@ [[self musicElement:cursorElt] compositeToPoint:xy operation: NSCompositeSourceOver]; - if (mode && accidental && accidental != kMusicExtendCursor) { - xy.y += kNoteY; - switch (cursorElt= accidental) { + + if (accidental) { + xy.y += kNoteY; + (int &)accidental += kMusicFlatCursor-kMusicFlat; + switch (accidental) { case kMusicFlatCursor: xy.x += kFlatW; xy.y += kFlatY; @@ -152,22 +128,21 @@ xy.y += kNaturalY; break; } - [[self musicElement:cursorElt] + [[self musicElement:accidental] compositeToPoint:xy operation: NSCompositeSourceOver]; } } -- (void) drawNoteCursor:(int)pitch inMeasure:(size_t)measure at:(VLFract)at - accidental:(VLMusicElement)accidental +- (void) drawNoteCursor:(int)vertPos inMeasure:(size_t)measure at:(VLFract)at visual:(uint16_t)visual { - [self drawNoteCursor:pitch inMeasure:measure at:at accidental:accidental mode:0]; + [self drawNoteCursor:vertPos inMeasure:measure at:at visual:visual mode:0]; } - (void) drawNoteCursor { - [self drawNoteCursor:fCursorPitch inMeasure:fCursorMeasure at:fCursorAt - accidental:fCursorAccidental mode:fClickMode]; + [self drawNoteCursor:fCursorVertPos inMeasure:fCursorMeasure at:fCursorAt + visual:fCursorVisual mode:fClickMode]; } - (void) drawNote:(int)visual at:(NSPoint)p @@ -390,7 +365,6 @@ accidental:[self accidentalForVisual:filterVisuals(step, visual)] tied:tied]; } else { - VLMusicElement accidental; pos = NSMakePoint([self noteXInMeasure:measIdx at:at], kSystemY+[self noteYInSection:measure.fPropIdx withPitch:65]); [self drawRest:note->fVisual & VLNote::kNoteHeadMask at: pos]; @@ -415,7 +389,7 @@ if (hasTriplets) { [self drawTripletBracketFrom:tripletStartX to:tripletEndX atY:tripletY]; } - if (fCursorPitch != VLNote::kNoPitch && fLayout->SystemForMeasure(fCursorMeasure) == system) + if (fCursorRegion == kRegionNote && fLayout->SystemForMeasure(fCursorMeasure) == system) [self drawNoteCursor]; } diff --git a/Sources/VLSheetViewSelection.mm b/Sources/VLSheetViewSelection.mm index 0d3deb8..df8f382 100644 --- a/Sources/VLSheetViewSelection.mm +++ b/Sources/VLSheetViewSelection.mm @@ -16,14 +16,15 @@ #import "VLSheetViewLyrics.h" #import "VLSheetWindow.h" #import "VLDocument.h" +#import "VLPitchGrid.h" @interface VLPlaybackEditable : VLEditable { VLSheetView * fView; size_t fStanza; size_t fNoteMeasure; VLFract fNoteAt; - int fNotePitch; - VLMusicElement fNoteAccidental; + int fNoteVert; + uint16_t fNoteVisual; size_t fChordMeasure; VLFract fChordAt; } @@ -50,29 +51,10 @@ { VLMIDIUserEvent * event = (VLMIDIUserEvent *)[ev pointerValue]; if (event->fPitch) { - fNotePitch = event->fPitch; - fNoteAccidental = kMusicNothing; - switch (event->fVisual & VLNote::kAccidentalsMask) { - case VLNote::kWantFlat: - fNoteAccidental = kMusicFlatCursor; - break; - case VLNote::kWantSharp: - fNoteAccidental = kMusicSharpCursor; - break; - case VLNote::kWant2Flat: - fNoteAccidental = kMusic2FlatCursor; - break; - case VLNote::kWant2Sharp: - fNoteAccidental = kMusic2SharpCursor; - break; - case VLNote::kWantNatural: - fNoteAccidental = kMusicNaturalCursor; - break; - default: - break; - } fNoteMeasure = event->fMeasure; fNoteAt = event->fAt; + fNoteVisual = event->fVisual & VLNote::kAccidentalsMask; + fNoteVert = VLPitchToGrid(event->fPitch, fNoteVisual, [fView song]->Properties(fNoteMeasure).fKey); fStanza = event->fStanza; [fView highlightTextInStanza:fStanza measure:fNoteMeasure at:fNoteAt one:YES]; } else { @@ -85,8 +67,8 @@ - (void) highlightCursor { - if (fNoteMeasure != 0x80000000 && fNotePitch != VLNote::kNoPitch) - [fView drawNoteCursor:fNotePitch inMeasure:fNoteMeasure at:fNoteAt accidental:fNoteAccidental]; + if (fNoteMeasure != 0x80000000 && fNoteVert != kCursorNoPitch) + [fView drawNoteCursor:fNoteVert inMeasure:fNoteMeasure at:fNoteAt visual:fNoteVisual]; if (fChordMeasure != 0x80000000) [fView highlightChordInMeasure:fChordMeasure at:fChordAt]; }