2006-12-02 03:35:21 +00:00
|
|
|
//
|
2007-04-27 06:41:34 +00:00
|
|
|
// File: VLSheetViewLyrics.mm - Lyrics editing functionality
|
2006-12-02 03:35:21 +00:00
|
|
|
//
|
2007-04-27 06:41:34 +00:00
|
|
|
// Author(s):
|
|
|
|
//
|
|
|
|
// (MN) Matthias Neeracher
|
|
|
|
//
|
|
|
|
// Copyright © 2006-2007 Matthias Neeracher
|
2006-12-02 03:35:21 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
#import "VLSheetView.h"
|
|
|
|
#import "VLSheetViewLyrics.h"
|
|
|
|
#import "VLSheetViewInternal.h"
|
2006-12-30 09:57:40 +00:00
|
|
|
#import "VLDocument.h"
|
2006-12-02 03:35:21 +00:00
|
|
|
|
|
|
|
#import "VLModel.h"
|
|
|
|
#import "VLSoundOut.h"
|
|
|
|
|
|
|
|
@implementation VLLyricsEditable
|
|
|
|
|
2011-09-10 21:33:40 +00:00
|
|
|
- (void)highlightWord
|
|
|
|
{
|
2011-09-11 02:03:22 +00:00
|
|
|
VLLocation end = fSelection;
|
|
|
|
if (!fSong->NextWord(fStanza, end))
|
|
|
|
end.fMeasure = 1000;
|
|
|
|
[fView highlightTextInStanza:fStanza start:fSelection end:end];
|
|
|
|
std::string word = fSong->GetWord(fStanza, fSelection);
|
2011-09-10 23:15:21 +00:00
|
|
|
fText = [[NSString alloc] initWithUTF8String:word.c_str()];
|
2011-09-10 21:33:40 +00:00
|
|
|
}
|
|
|
|
|
2006-12-02 03:35:21 +00:00
|
|
|
- (VLLyricsEditable *)initWithView:(VLSheetView *)view
|
|
|
|
song:(VLSong *)song
|
2006-12-02 09:02:44 +00:00
|
|
|
stanza:(int)stanza
|
2011-09-11 02:03:22 +00:00
|
|
|
at:(VLLocation)at
|
2006-12-02 03:35:21 +00:00
|
|
|
{
|
2007-04-13 04:26:35 +00:00
|
|
|
self = [super init];
|
|
|
|
fView = view;
|
|
|
|
fSong = song;
|
|
|
|
fStanza = stanza;
|
2011-09-11 02:03:22 +00:00
|
|
|
fSelection = at;
|
|
|
|
fAnchor = at;
|
|
|
|
fNext = at;
|
2006-12-02 03:35:21 +00:00
|
|
|
|
2011-09-11 02:03:22 +00:00
|
|
|
fSong->FindWord(fStanza, fSelection);
|
2011-09-10 21:33:40 +00:00
|
|
|
[self highlightWord];
|
2006-12-02 03:35:21 +00:00
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2011-09-10 23:15:21 +00:00
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
[fText release];
|
|
|
|
}
|
|
|
|
|
2006-12-02 03:35:21 +00:00
|
|
|
- (NSString *) stringValue
|
|
|
|
{
|
2011-09-10 23:15:21 +00:00
|
|
|
return fText;
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setStringValue:(NSString *)val
|
|
|
|
{
|
2006-12-04 07:04:24 +00:00
|
|
|
[[fView document] willChangeSong];
|
2011-09-11 02:03:22 +00:00
|
|
|
fSong->SetWord(fStanza, fSelection, val ? [val UTF8String] : "", &fNext);
|
2006-12-04 07:04:24 +00:00
|
|
|
[[fView document] didChangeSong];
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) validValue:(NSString *)val
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) moveToNext
|
2006-12-02 09:02:44 +00:00
|
|
|
{
|
2011-09-11 02:03:22 +00:00
|
|
|
if (fNext != fSelection) {
|
|
|
|
fSelection = fNext;
|
|
|
|
fSong->FindWord(fStanza, fSelection);
|
2007-04-13 04:26:35 +00:00
|
|
|
} else {
|
2011-09-11 02:03:22 +00:00
|
|
|
if (!fSong->NextWord(fStanza, fSelection)) {
|
|
|
|
fSelection.fMeasure = 0;
|
|
|
|
fSelection.fAt = VLFraction(0);
|
|
|
|
fSong->FindWord(fStanza, fSelection);
|
2007-04-13 04:26:35 +00:00
|
|
|
}
|
2006-12-02 09:02:44 +00:00
|
|
|
}
|
2011-09-11 02:03:22 +00:00
|
|
|
fNext = fSelection;
|
2011-09-10 21:33:40 +00:00
|
|
|
[self highlightWord];
|
2011-09-11 02:03:22 +00:00
|
|
|
[fView scrollMeasureToVisible:fSelection.fMeasure];
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) moveToPrev
|
|
|
|
{
|
2011-09-11 02:03:22 +00:00
|
|
|
if (!fSong->PrevWord(fStanza, fSelection)) {
|
|
|
|
fSelection.fMeasure = fSong->CountMeasures()-1;
|
|
|
|
fSelection.fAt = fSong->Properties(fSelection.fMeasure).fTime;
|
|
|
|
fSong->PrevWord(fStanza, fSelection);
|
2006-12-02 09:02:44 +00:00
|
|
|
}
|
2011-09-11 02:03:22 +00:00
|
|
|
fNext = fSelection;
|
2011-09-10 21:33:40 +00:00
|
|
|
[self highlightWord];
|
2011-09-11 02:03:22 +00:00
|
|
|
[fView scrollMeasureToVisible:fSelection.fMeasure];
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) highlightCursor
|
|
|
|
{
|
2011-09-11 02:03:22 +00:00
|
|
|
std::string word = fSong->GetWord(fStanza, fSelection);
|
2008-05-29 18:54:30 +00:00
|
|
|
if (!word.size())
|
2011-09-11 02:03:22 +00:00
|
|
|
[fView highlightLyricsInStanza:fStanza at:fSelection];
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
|
|
|
|
2011-09-10 23:15:21 +00:00
|
|
|
- (BOOL)canExtendSelection:(VLRegion)region
|
|
|
|
{
|
|
|
|
return region == kRegionLyrics;
|
|
|
|
}
|
|
|
|
|
2011-09-11 02:03:22 +00:00
|
|
|
- (void)extendSelection:(VLLocation)at
|
2011-09-10 23:15:21 +00:00
|
|
|
{
|
2011-09-11 02:03:22 +00:00
|
|
|
if (!fSong->FindWord(fStanza, at))
|
2011-09-10 23:15:21 +00:00
|
|
|
return;
|
2011-09-11 02:03:22 +00:00
|
|
|
if (at < fAnchor) {
|
2011-09-10 23:15:21 +00:00
|
|
|
//
|
|
|
|
// Backward from anchor
|
|
|
|
//
|
2011-09-11 02:03:22 +00:00
|
|
|
fSelection = at;
|
|
|
|
at = fAnchor;
|
2011-09-10 23:15:21 +00:00
|
|
|
} else {
|
|
|
|
//
|
|
|
|
// Forward from anchor
|
|
|
|
//
|
2011-09-11 02:03:22 +00:00
|
|
|
fSelection = fAnchor;
|
|
|
|
fSong->FindWord(fStanza, fSelection);
|
2011-09-10 23:15:21 +00:00
|
|
|
}
|
2011-09-11 02:03:22 +00:00
|
|
|
if (!fSong->NextWord(fStanza, at))
|
|
|
|
at.fMeasure = 1000;
|
|
|
|
[fView highlightTextInStanza:fStanza start:fSelection end:at];
|
2011-09-10 23:15:21 +00:00
|
|
|
std::string text;
|
2011-09-11 02:03:22 +00:00
|
|
|
VLLocation textAt = fSelection;
|
|
|
|
while (textAt < at) {
|
2011-09-10 23:15:21 +00:00
|
|
|
if (text.size())
|
|
|
|
text += ' ';
|
2011-09-11 02:03:22 +00:00
|
|
|
text += fSong->GetWord(fStanza, textAt);
|
2011-09-26 02:45:37 +00:00
|
|
|
if (!fSong->NextWord(fStanza, textAt))
|
|
|
|
break;
|
2011-09-10 23:15:21 +00:00
|
|
|
}
|
|
|
|
[fText release];
|
|
|
|
fText = [[NSString alloc] initWithUTF8String:text.c_str()];
|
|
|
|
[fView updateEditTarget];
|
|
|
|
}
|
|
|
|
|
2006-12-02 03:35:21 +00:00
|
|
|
@end
|
|
|
|
|
2007-12-25 16:49:45 +00:00
|
|
|
class VLCocoaFontHandler : public VLFontHandler {
|
|
|
|
public:
|
|
|
|
VLCocoaFontHandler(NSString * name, float size);
|
|
|
|
|
2008-05-29 18:54:30 +00:00
|
|
|
virtual void Draw(float x, float y, const char * utf8Text, bool highlight);
|
2007-12-25 16:49:45 +00:00
|
|
|
virtual float Width(const char * utf8Text);
|
|
|
|
private:
|
|
|
|
NSDictionary * fTextAttr;
|
|
|
|
};
|
|
|
|
|
|
|
|
VLCocoaFontHandler::VLCocoaFontHandler(NSString * name, float size)
|
|
|
|
{
|
|
|
|
NSFont * font = [NSFont fontWithName:name size:size];
|
|
|
|
|
|
|
|
fTextAttr =
|
|
|
|
[[NSDictionary alloc] initWithObjectsAndKeys:
|
|
|
|
font, NSFontAttributeName, nil];
|
|
|
|
}
|
|
|
|
|
2008-05-29 18:54:30 +00:00
|
|
|
static NSColor * sHighlightColor;
|
|
|
|
|
|
|
|
void VLCocoaFontHandler::Draw(float x, float y,
|
|
|
|
const char * utf8Text, bool highlight)
|
2007-12-25 16:49:45 +00:00
|
|
|
{
|
2008-05-29 18:54:30 +00:00
|
|
|
NSDictionary * attr = fTextAttr;
|
|
|
|
if (highlight) {
|
|
|
|
NSMutableDictionary * aa =
|
|
|
|
[NSMutableDictionary dictionaryWithDictionary:attr];
|
|
|
|
[aa setValue:sHighlightColor forKey:NSBackgroundColorAttributeName];
|
|
|
|
attr = aa;
|
|
|
|
}
|
|
|
|
NSString * t = [NSString stringWithUTF8String:utf8Text];
|
|
|
|
[t drawAtPoint:NSMakePoint(x,y) withAttributes:attr];
|
2007-12-25 16:49:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
float VLCocoaFontHandler::Width(const char * utf8Text)
|
|
|
|
{
|
|
|
|
NSString * t = [NSString stringWithUTF8String:utf8Text];
|
|
|
|
NSSize sz= [t sizeWithAttributes:fTextAttr];
|
|
|
|
|
|
|
|
return sz.width;
|
|
|
|
}
|
|
|
|
|
2006-12-02 03:35:21 +00:00
|
|
|
@implementation VLSheetView (Lyrics)
|
|
|
|
|
|
|
|
- (void) drawLyricsForSystem:(int)system stanza:(size_t)stanza
|
|
|
|
{
|
2007-12-25 16:49:45 +00:00
|
|
|
static VLFontHandler * sRegularFont = nil;
|
|
|
|
static VLFontHandler * sNarrowFont = nil;
|
|
|
|
if (!sRegularFont) {
|
|
|
|
sRegularFont = new VLCocoaFontHandler(@"Arial", 12.0f);
|
|
|
|
sNarrowFont = new VLCocoaFontHandler(@"ArialNarrow", 12.0f);
|
|
|
|
}
|
|
|
|
VLTextLayout text(sRegularFont, sNarrowFont);
|
2006-12-02 03:35:21 +00:00
|
|
|
|
2007-12-23 12:45:17 +00:00
|
|
|
const VLSong * song = [self song];
|
|
|
|
const float kSystemY = [self systemY:system];
|
|
|
|
const VLSystemLayout & kLayout = (*fLayout)[system];
|
|
|
|
const int kFirstMeas = fLayout->FirstMeasure(system);
|
2006-12-02 03:35:21 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Build new list
|
|
|
|
//
|
2007-12-23 12:45:17 +00:00
|
|
|
for (int m = 0; m<kLayout.NumMeasures(); ++m) {
|
|
|
|
int measIdx = m+kFirstMeas;
|
2006-12-02 03:35:21 +00:00
|
|
|
if (measIdx >= song->CountMeasures())
|
|
|
|
break;
|
|
|
|
const VLMeasure measure = song->fMeasures[measIdx];
|
|
|
|
const VLNoteList & notes = measure.fMelody;
|
2011-09-11 02:03:22 +00:00
|
|
|
VLLocation at = {measIdx, VLFraction(0)};
|
2006-12-02 03:35:21 +00:00
|
|
|
for (VLNoteList::const_iterator note = notes.begin();
|
|
|
|
note != notes.end();
|
|
|
|
++note
|
|
|
|
) {
|
|
|
|
if (note->fLyrics.size() < stanza
|
|
|
|
|| !note->fLyrics[stanza-1].fText.size()
|
|
|
|
) {
|
|
|
|
;
|
|
|
|
} else {
|
2011-09-10 23:15:21 +00:00
|
|
|
bool highlight = stanza == fHighlightStanza
|
2011-09-11 02:03:22 +00:00
|
|
|
&& at >= fHighlightStart && at < fHighlightEnd;
|
2011-09-10 23:15:21 +00:00
|
|
|
if (highlight && !sHighlightColor)
|
|
|
|
sHighlightColor = [[self textBackgroundColorForSystem:system] shadowWithLevel:0.2];
|
2008-05-29 18:54:30 +00:00
|
|
|
|
2007-12-25 16:49:45 +00:00
|
|
|
text.AddSyllable(note->fLyrics[stanza-1],
|
2011-09-11 02:03:22 +00:00
|
|
|
[self noteXAt:at],
|
2011-09-10 23:15:21 +00:00
|
|
|
highlight);
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
2011-09-11 02:03:22 +00:00
|
|
|
at.fAt = at.fAt+note->fDuration;
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
|
|
|
}
|
2007-12-25 16:49:45 +00:00
|
|
|
|
|
|
|
text.DrawLine(kSystemY+kLyricsY-stanza*kLyricsH);
|
2008-05-29 18:54:30 +00:00
|
|
|
sHighlightColor = nil;
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) editLyrics
|
|
|
|
{
|
|
|
|
VLEditable * e =
|
|
|
|
[[VLLyricsEditable alloc]
|
|
|
|
initWithView:self
|
|
|
|
song:[self song]
|
2006-12-02 09:02:44 +00:00
|
|
|
stanza:fCursorStanza
|
2011-09-11 02:03:22 +00:00
|
|
|
at:fCursorLocation];
|
2006-12-02 03:35:21 +00:00
|
|
|
[self setEditTarget:e];
|
|
|
|
[fFieldEditor selectText:self];
|
|
|
|
}
|
|
|
|
|
2011-09-11 02:03:22 +00:00
|
|
|
- (void) highlightLyricsInStanza:(size_t)stanza at:(VLLocation)at
|
2006-12-02 03:35:21 +00:00
|
|
|
{
|
2011-09-11 02:03:22 +00:00
|
|
|
const float kSystemY = [self systemY:fLayout->SystemForMeasure(at.fMeasure)];
|
2008-05-29 18:54:30 +00:00
|
|
|
NSRect r =
|
2011-09-11 02:03:22 +00:00
|
|
|
NSMakeRect([self noteXAt:at]-kNoteW*0.5f,
|
2008-05-29 18:54:30 +00:00
|
|
|
kSystemY+kLyricsY-stanza*kLyricsH, kNoteW, kLyricsH);
|
|
|
|
[[NSColor colorWithCalibratedWhite:0.8f alpha:1.0f] setFill];
|
|
|
|
NSRectFillUsingOperation(r, NSCompositePlusDarker);
|
|
|
|
}
|
|
|
|
|
2011-09-11 02:03:22 +00:00
|
|
|
- (void) highlightTextInStanza:(size_t)stanza start:(VLLocation)start end:(VLLocation)end
|
2008-05-29 18:54:30 +00:00
|
|
|
{
|
2011-09-11 02:03:22 +00:00
|
|
|
fHighlightStanza = stanza;
|
|
|
|
fHighlightStart = start;
|
|
|
|
fHighlightEnd = end;
|
2011-09-10 23:15:21 +00:00
|
|
|
[self setNeedsDisplay:YES];
|
2006-12-02 03:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|