mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 19:23:59 +00:00
177 lines
4.8 KiB
Plaintext
177 lines
4.8 KiB
Plaintext
|
//
|
||
|
// VLSheetViewChords.mm
|
||
|
// Vocalese
|
||
|
//
|
||
|
// Created by Matthias Neeracher on 1/4/06.
|
||
|
// Copyright 2006 __MyCompanyName__. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#import "VLSheetView.h"
|
||
|
#import "VLSheetViewChords.h"
|
||
|
#import "VLSheetViewInternal.h"
|
||
|
|
||
|
#import "VLModel.h"
|
||
|
#import "VLSoundOut.h"
|
||
|
|
||
|
@implementation VLSheetView (Chords)
|
||
|
|
||
|
- (NSAttributedString *) attributedStringWithCppString:(const std::string &)s
|
||
|
attributes:(NSDictionary *)a
|
||
|
{
|
||
|
return [[[NSAttributedString alloc]
|
||
|
initWithString:[NSString stringWithUTF8String:s.c_str()]
|
||
|
attributes: a]
|
||
|
autorelease];
|
||
|
}
|
||
|
|
||
|
- (NSAttributedString *) stringWithChord:(const VLChord &)chord
|
||
|
{
|
||
|
const VLSong * song = [self song];
|
||
|
const VLProperties & prop = song->fProperties.front();
|
||
|
|
||
|
static NSDictionary * sBigFont = nil;
|
||
|
static NSDictionary * sSuperFont = nil;
|
||
|
if (!sBigFont)
|
||
|
sBigFont =
|
||
|
[[NSDictionary alloc] initWithObjectsAndKeys:
|
||
|
[NSFont fontWithName: @"Helvetica" size: 14],
|
||
|
NSFontAttributeName,
|
||
|
nil];
|
||
|
if (!sSuperFont)
|
||
|
sSuperFont =
|
||
|
[[NSDictionary alloc] initWithObjectsAndKeys:
|
||
|
[NSFont fontWithName: @"Helvetica" size: 12],
|
||
|
NSFontAttributeName,
|
||
|
[NSNumber numberWithInt: 1],
|
||
|
NSSuperscriptAttributeName,
|
||
|
nil];
|
||
|
|
||
|
std::string name, ext, root;
|
||
|
if (chord.fPitch != VLNote::kNoPitch)
|
||
|
chord.Name(name, ext, root, prop.fKey > 0);
|
||
|
|
||
|
NSMutableAttributedString * s =
|
||
|
[[[NSMutableAttributedString alloc] init] autorelease];
|
||
|
[s appendAttributedString:
|
||
|
[self attributedStringWithCppString: name
|
||
|
attributes: sBigFont]];
|
||
|
[s appendAttributedString:
|
||
|
[self attributedStringWithCppString: ext.size() ? ext : " "
|
||
|
attributes: sSuperFont]];
|
||
|
if (root.size())
|
||
|
[s appendAttributedString:
|
||
|
[self attributedStringWithCppString: "/" + root
|
||
|
attributes: sBigFont]];
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
- (void) setupChords
|
||
|
{
|
||
|
const VLSong * song = [self song];
|
||
|
const VLProperties & prop = song->fProperties.front();
|
||
|
NSView * chordView = [chords contentView];
|
||
|
|
||
|
//
|
||
|
// Delete old list of chord boxes
|
||
|
//
|
||
|
for (NSEnumerator * e = [[chordView subviews] objectEnumerator];
|
||
|
NSView * subview = [e nextObject];
|
||
|
)
|
||
|
[subview removeFromSuperview];
|
||
|
|
||
|
//
|
||
|
// Build new list
|
||
|
//
|
||
|
NSView * prevKeyView = nil;
|
||
|
NSFont * chordFont = [NSFont controlContentFontOfSize: 14];
|
||
|
int beatsPerGroup = quarterBeats / groups;
|
||
|
for (int m = 0; m<visibleMeasures; ++m) {
|
||
|
const float x0 = clefKeyW+m*measureW;
|
||
|
int measure = firstMeasure+m;
|
||
|
VLChordList::const_iterator cCur = song->fMeasures[measure].fChords.begin();
|
||
|
VLChordList::const_iterator cEnd = song->fMeasures[measure].fChords.end();
|
||
|
VLFraction at(0);
|
||
|
for (int beat = 0; beat<quarterBeats; ++beat) {
|
||
|
const float x = x0+kNoteW*(beat*prop.fDivisions+(beat / beatsPerGroup)+0.5f);
|
||
|
NSRect f = NSMakeRect(x, 0, kChordW, kChordH);
|
||
|
NSTextField * chord = [[NSTextField alloc] initWithFrame: f];
|
||
|
[chordView addSubview: chord];
|
||
|
[prevKeyView setNextKeyView: chord];
|
||
|
prevKeyView = chord;
|
||
|
[chord setBordered: NO];
|
||
|
[chord setBezeled: NO];
|
||
|
[chord setDrawsBackground: NO];
|
||
|
[chord setTarget: self];
|
||
|
[chord setAction: @selector(editChord:)];
|
||
|
[[chord cell] setSendsActionOnEndEditing: YES];
|
||
|
[chord setTag: (measure << 8) | beat];
|
||
|
[chord setFont: chordFont];
|
||
|
while (cCur != cEnd && at < VLFraction(beat, 4))
|
||
|
at += (cCur++)->fDuration;
|
||
|
if (cCur != cEnd && at == VLFraction(beat, 4) && cCur->fPitch != VLNote::kNoPitch)
|
||
|
[chord setAttributedStringValue: [self stringWithChord:*cCur]];
|
||
|
[chord release];
|
||
|
}
|
||
|
}
|
||
|
[chords setNeedsDisplay: YES];
|
||
|
}
|
||
|
|
||
|
|
||
|
- (IBAction) editChord:(id)sender
|
||
|
{
|
||
|
VLSong * song = [self song];
|
||
|
|
||
|
std::string chordName =
|
||
|
(const char *)[[[sender stringValue] lowercaseString] UTF8String];
|
||
|
//
|
||
|
// Normalize # and b
|
||
|
//
|
||
|
for (;;) {
|
||
|
size_t found;
|
||
|
|
||
|
found = chordName.find("\xE2\x99\xAF", 0, 3);
|
||
|
if (found != std::string::npos) {
|
||
|
chordName.replace(found, 3, 1, '#');
|
||
|
continue;
|
||
|
}
|
||
|
found = chordName.find("\xE2\x99\xAD", 0, 3);
|
||
|
if (found != std::string::npos) {
|
||
|
chordName.replace(found, 3, 1, 'b');
|
||
|
continue;
|
||
|
}
|
||
|
found = chordName.find(" ", 0, 1);
|
||
|
if (found != std::string::npos) {
|
||
|
chordName.erase(found);
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
//
|
||
|
// Check for valid chord
|
||
|
//
|
||
|
VLChord chord(chordName);
|
||
|
if (!chordName.size()) {
|
||
|
[sender setTextColor: [NSColor blackColor]];
|
||
|
} else if (chord.fPitch == VLChord::kNoPitch) {
|
||
|
if (chordName.size()) {
|
||
|
NSBeep();
|
||
|
[sender setTextColor: [NSColor redColor]];
|
||
|
}
|
||
|
int tag = [sender tag];
|
||
|
song->DelChord(tag >> 8, VLFraction(tag & 0xFF, 4));
|
||
|
} else {
|
||
|
NSAttributedString * s = [self stringWithChord:chord];
|
||
|
[sender setAttributedStringValue: s];
|
||
|
[sender setTextColor: [NSColor blackColor]];
|
||
|
NSSize sz = [sender frame].size;
|
||
|
sz.width = [s size].width+10.0;
|
||
|
[sender setFrameSize: sz];
|
||
|
VLSoundOut::Instance()->PlayChord(chord);
|
||
|
int tag = [sender tag];
|
||
|
song->AddChord(chord, tag >> 8, VLFraction(tag & 0xFF, 4));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@end
|