Skip to content

Commit

Permalink
fix #8924: native support for slash notation
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcSabatella committed Nov 18, 2014
1 parent 486ce2c commit 58e5a74
Show file tree
Hide file tree
Showing 28 changed files with 5,371 additions and 258 deletions.
11 changes: 8 additions & 3 deletions libmscore/accidental.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,10 @@ void Accidental::layout()
el.clear();

QRectF r;
if (staff() && staff()->isTabStaff()) { //in TAB, accidentals are not shown
setbbox(QRectF());
// don't show accidentals for tab or slash notation
if ((staff() && staff()->isTabStaff())
|| (note() && note()->fixed())) {
setbbox(r);
return;
}

Expand Down Expand Up @@ -385,8 +387,11 @@ Accidental::Type Accidental::name2subtype(const QString& tag)

void Accidental::draw(QPainter* painter) const
{
if (staff() && staff()->isTabStaff()) //in TAB, accidentals are not shown
// don't show accidentals for tab or slash notation
if ((staff() && staff()->isTabStaff())
|| (note() && note()->fixed())) {
return;
}
painter->setPen(curColor());
foreach(const SymElement& e, el)
score()->scoreFont()->draw(e.sym, painter, magS(), QPointF(e.x, 0.0));
Expand Down
84 changes: 80 additions & 4 deletions libmscore/chord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1585,7 +1585,7 @@ void Chord::updateNotes(AccidentalState* as)
if (drumset) {
for (Note* note : nl) {
int pitch = note->pitch();
if (drumset->isValid(pitch)) {
if (drumset->isValid(pitch) && !note->fixed()) {
note->setHeadGroup(drumset->noteHead(pitch));
note->setLine(drumset->line(pitch));
}
Expand Down Expand Up @@ -1655,7 +1655,7 @@ void Chord::cmdUpdateNotes(AccidentalState* as)
if (!drumset->isValid(pitch)) {
// qDebug("unmapped drum note %d", pitch);
}
else {
else if (!note->fixed()) {
note->setHeadGroup(drumset->noteHead(pitch));
note->setLine(drumset->line(pitch));
continue;
Expand Down Expand Up @@ -1770,7 +1770,7 @@ void Chord::layoutPitched()
lhead = qMax(lhead, -x1);

Accidental* accidental = note->accidental();
if (accidental) {
if (accidental && !note->fixed()) {
// convert x position of accidental to segment coordinate system
qreal x = accidental->pos().x() + note->pos().x() + chordX;
// distance from accidental to note already taken into account
Expand Down Expand Up @@ -2070,7 +2070,10 @@ void Chord::layoutTablature()
headWidth = fretWidth;
// centre fret string on stem
qreal x = stemX - fretWidth*0.5;
note->setPos(x, tab->physStringToVisual(note->string()) * lineDist);
if (note->fixed())
note->setPos(x, note->line() * lineDist / 2);
else
note->setPos(x, tab->physStringToVisual(note->string()) * lineDist);
}
// horiz. spacing: leave half width at each side of the (potential) stem
qreal halfHeadWidth = headWidth * 0.5;
Expand Down Expand Up @@ -2735,6 +2738,79 @@ void Chord::setStemSlash(StemSlash* s)
_stemSlash = s;
}

//---------------------------------------------------------
// slash
//---------------------------------------------------------

bool Chord::slash()
{
Note* n = upNote();
return n->fixed() && n->headGroup() == NoteHead::Group::HEAD_SLASH && !n->play();
}

//---------------------------------------------------------
// setSlash
//---------------------------------------------------------

void Chord::setSlash(bool flag, bool stemless)
{
int line = 0;

if (!flag) {
// restore to normal
undoChangeProperty(P_ID::NO_STEM, false);
undoChangeProperty(P_ID::SMALL, false);
undoChangeProperty(P_ID::USER_OFF, QPointF());
for (Note* n : _notes) {
n->undoChangeProperty(P_ID::HEAD_GROUP, int(NoteHead::Group::HEAD_NORMAL));
n->undoChangeProperty(P_ID::FIXED, false);
n->undoChangeProperty(P_ID::FIXED_LINE, 0);
n->undoChangeProperty(P_ID::PLAY, true);
n->undoChangeProperty(P_ID::VISIBLE, true);
}
return;
}

// set stem to auto (mostly important for rhythmic notation on drum staves)
undoChangeProperty(P_ID::STEM_DIRECTION, int(MScore::Direction::AUTO));

// make stemless if asked
if (stemless)
undoChangeProperty(P_ID::NO_STEM, true);

if (track() % VOICES < 2) {
// use middle line
line = staff()->lines() - 1;
}
else {
// set outside the staff
qreal y = 0.0;
if (track() % 2) {
line = staff()->lines() * 2 - 1;
y = 0.5 * spatium();
}
else {
line = -1;
y = -0.5 * spatium();
}
// set small, and add a little vertical offset to separate from staff
undoChangeProperty(P_ID::SMALL, true);
undoChangeProperty(P_ID::USER_OFF, QPointF(0.0, y));
}

int ns = _notes.size();
for (int i = 0; i < ns; ++i) {
Note* n = _notes[i];
n->undoChangeProperty(P_ID::HEAD_GROUP, static_cast<int>(NoteHead::Group::HEAD_SLASH));
n->undoChangeProperty(P_ID::FIXED, true);
n->undoChangeProperty(P_ID::FIXED_LINE, line);
n->undoChangeProperty(P_ID::PLAY, false);
// hide all but first notehead
if (i)
n->undoChangeProperty(P_ID::VISIBLE, false);
}
}

//---------------------------------------------------------
// mag
//---------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions libmscore/chord.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ class Chord : public ChordRest {
Glissando* glissando() const { return _glissando; }
StemSlash* stemSlash() const { return _stemSlash; }
void setStemSlash(StemSlash* s);
bool slash();
void setSlash(bool flag, bool stemless);

const QList<Chord*>& graceNotes() const { return _graceNotes; }
QList<Chord*>& graceNotes() { return _graceNotes; }
Expand Down
109 changes: 108 additions & 1 deletion libmscore/cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2321,6 +2321,10 @@ void Score::cmd(const QAction* a)
cmdExplode();
else if (cmd == "implode")
cmdImplode();
else if (cmd == "slash-fill")
cmdSlashFill();
else if (cmd == "slash-rhythm")
cmdSlashRhythm();
else
qDebug("unknown cmd <%s>", qPrintable(cmd));
}
Expand Down Expand Up @@ -2536,5 +2540,108 @@ void Score::cmdImplode()
setLayoutAll(true);
}

}
//---------------------------------------------------------
// cmdSlashFill
/// fills selected region with slashes
//---------------------------------------------------------

void Score::cmdSlashFill()
{
int startStaff = selection().staffStart();
int endStaff = selection().staffEnd();
Segment* startSegment = selection().startSegment();
Segment* endSegment = selection().endSegment();
int endTick = endSegment ? endSegment->tick() : lastSegment()->tick() + 1;
Chord* firstSlash = 0;
Chord* lastSlash = 0;

// loop through staves in selection
for (int staffIdx = startStaff; staffIdx < endStaff; ++staffIdx) {
// loop through segments adding slashes on each beat
for (Segment* s = startSegment; s && s->tick() < endTick; s = s->next1()) {
if (s->segmentType() != Segment::Type::ChordRest)
continue;
// determine beat type based on time signature
int d = s->measure()->timesig().denominator();
int n = (d > 4 && s->measure()->timesig().numerator() % 3 == 0) ? 3 : 1;
Fraction f(n, d);
// skip over any leading segments before next (first) beat
if (s->tick() % f.ticks())
continue;
//expandVoice(s, staffIdx * VOICES);
// construct note
int line = 0;
bool error = false;
NoteVal nv;
if (staff(staffIdx)->staffType()->group() == StaffGroup::TAB)
line = staff(staffIdx)->lines() / 2;
else
line = staff(staffIdx)->lines() - 1;
if (staff(staffIdx)->staffType()->group() == StaffGroup::PERCUSSION) {
nv.pitch = 0;
nv.headGroup = NoteHead::Group::HEAD_SLASH;
}
else {
Position p;
p.segment = s;
p.staffIdx = staffIdx;
p.line = line;
p.fret = FRET_NONE;
_is.setRest(false); // needed for tab
nv = noteValForPosition(p, error);
}
if (error)
continue;
// insert & turn into slash
s = setNoteRest(s, staffIdx * VOICES, nv, f);
Chord* c = static_cast<Chord*>(s->element(staffIdx * VOICES));
if (c->links()) {
foreach (Element* e, *c->links()) {
Chord* lc = static_cast<Chord*>(e);
lc->setSlash(true, true);
}
}
else
c->setSlash(true, true);
lastSlash = c;
if (!firstSlash)
firstSlash = c;
}
}

// re-select the slashes
deselectAll();
if (firstSlash && lastSlash) {
select(firstSlash, SelectType::RANGE);
select(lastSlash, SelectType::RANGE);
}
setLayoutAll(true);
}

//---------------------------------------------------------
// cmdSlashRhythm
/// converts rhythms in selected region to slashes
//---------------------------------------------------------

void Score::cmdSlashRhythm()
{
QList<Chord*> chords;
// loop through all notes in selection
foreach (Element* e, selection().elements()) {
if (e->type() != Element::Type::NOTE)
continue;
Note* n = static_cast<Note*>(e);
if (n->noteType() != NoteType::NORMAL)
continue;
Chord* c = n->chord();
// check for duplicates (chords with multiple notes)
if (chords.contains(c))
continue;
chords.append(c);
// toggle slash setting
c->setSlash(!c->slash(), false);
}
setLayoutAll(true);
}

}
4 changes: 2 additions & 2 deletions libmscore/layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ void Score::layoutChords3(QList<Note*>& notes, Staff* staff, Segment* segment)
for (int i = nNotes-1; i >= 0; --i) {
Note* note = notes[i];
Accidental* ac = note->accidental();
if (ac) {
if (ac && !note->fixed()) {
ac->layout();
AcEl acel;
acel.note = note;
Expand Down Expand Up @@ -3600,7 +3600,7 @@ qreal Score::computeMinWidth(Segment* fs, bool firstMeasureInSystem)
grace = true;
else {
for (Note* note : c->notes()) {
if (note->accidental()) {
if (note->accidental() && !note->fixed()) {
accidental = true;
// segment-relative
accidentalX = qMin(accidentalX, note->accidental()->x() + note->x() + c->x());
Expand Down
4 changes: 2 additions & 2 deletions libmscore/measure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ void Measure::layoutCR0(ChordRest* cr, qreal mm)
if (!drumset->isValid(pitch)) {
// qDebug("unmapped drum note %d", pitch);
}
else {
else if (!note->fixed()) {
note->setHeadGroup(drumset->noteHead(pitch));
note->setLine(drumset->line(pitch));
continue;
Expand Down Expand Up @@ -3131,7 +3131,7 @@ void Measure::layoutX(qreal stretch)
grace = true;
else {
for (Note* note : c->notes()) {
if (note->accidental()) {
if (note->accidental() && !note->fixed()) {
accidental = true;
accidentalX = qMin(accidentalX, note->accidental()->x() + note->x() + c->x());
}
Expand Down
Loading

0 comments on commit 58e5a74

Please sign in to comment.