Implements automatic bracketing and bar line spanning.

This commit is contained in:
Niek van den Berg 2021-06-01 13:58:56 +02:00
parent 980d36edc4
commit 3d7011315a
5 changed files with 234 additions and 51 deletions

View file

@ -3765,6 +3765,15 @@ void Score::setScoreOrder(ScoreOrder order)
_scoreOrder = order;
}
//---------------------------------------------------------
// setBracketsAndBarlines
//---------------------------------------------------------
void Score::setBracketsAndBarlines()
{
scoreOrder().setBracketsAndBarlines(this);
}
//---------------------------------------------------------
// lassoSelect
//---------------------------------------------------------

View file

@ -1030,6 +1030,7 @@ public:
ScoreOrder scoreOrder() const;
void setScoreOrder(ScoreOrder order);
void setBracketsAndBarlines();
void lassoSelect(const mu::RectF&);
void lassoSelectEnd(bool);

View file

@ -94,60 +94,44 @@ void ScoreOrder::readSoloists(Ms::XmlReader& reader, const QString section)
groups << sg;
}
//---------------------------------------------------------
// readUnsorted
//---------------------------------------------------------
void ScoreOrder::readUnsorted(Ms::XmlReader& reader, const QString section, bool inSection)
{
QString group { reader.attribute("group", QString("")) };
reader.skipCurrentElement();
if (hasGroup(UNSORTED_ID, group)) {
return;
}
ScoreGroup sg;
sg.family = QString(UNSORTED_ID);
sg.section = section;
sg.unsorted = group;
sg.bracket = !inSection ? true : false;
sg.showSystemMarkings = !inSection ? false : readBoolAttribute(reader, "showSystemMarkings", false);
sg.barLineSpan = !inSection ? false : readBoolAttribute(reader, "barLineSpan", true);
sg.thinBracket = !inSection ? false : readBoolAttribute(reader, "thinBrackets", true);
groups << sg;
}
//---------------------------------------------------------
// readFamily
//---------------------------------------------------------
void ScoreOrder::readFamily(Ms::XmlReader& reader, const QString section, bool inSection)
{
const QString id { reader.readElementText().toUtf8().data() };
ScoreGroup sg;
sg.family = id;
sg.section = section;
sg.bracket = !inSection ? false : true;
sg.showSystemMarkings = !inSection ? false : readBoolAttribute(reader, "showSystemMarkings", false);
sg.barLineSpan = !inSection ? false : readBoolAttribute(reader, "barLineSpan", true);
sg.thinBracket = !inSection ? false : readBoolAttribute(reader, "thinBrackets", true);
groups << sg;
}
//---------------------------------------------------------
// readSection
//---------------------------------------------------------
void ScoreOrder::readSection(Ms::XmlReader& reader)
{
QString id { reader.attribute("id") };
QString sectionId { reader.attribute("id") };
bool showSystemMarkings = readBoolAttribute(reader, "showSystemMarkings", false);
bool barLineSpan = readBoolAttribute(reader, "barLineSpan", true);
bool thinBrackets = readBoolAttribute(reader, "thinBrackets", true);
while (reader.readNextStartElement()) {
if (reader.name() == "family") {
readFamily(reader, id, true);
ScoreGroup sg;
sg.family = reader.readElementText().toUtf8().data();
sg.section = sectionId;
sg.bracket = true;
sg.showSystemMarkings = showSystemMarkings;
sg.barLineSpan = barLineSpan;
sg.thinBracket = thinBrackets;
groups << sg;
} else if (reader.name() == "unsorted") {
readUnsorted(reader, id, true);
QString group { reader.attribute("group", QString("")) };
if (hasGroup(UNSORTED_ID, group)) {
reader.skipCurrentElement();
return;
}
ScoreGroup sg;
sg.family = QString(UNSORTED_ID);
sg.section = sectionId;
sg.unsorted = group;
sg.bracket = false;
sg.showSystemMarkings = readBoolAttribute(reader, "showSystemMarkings", false);
sg.barLineSpan = readBoolAttribute(reader, "barLineSpan", true);
sg.thinBracket = readBoolAttribute(reader, "thinBrackets", true);
groups << sg;
reader.skipCurrentElement();
} else {
reader.unknown();
}
@ -177,6 +161,155 @@ bool ScoreOrder::isValid() const
return !id.isEmpty();
}
//---------------------------------------------------------
// getFamilyName
//---------------------------------------------------------
QString ScoreOrder::getFamilyName(const InstrumentTemplate* instrTemplate, bool soloist) const
{
if (!instrTemplate) {
return QString("<unsorted>");
}
if (soloist) {
return QString("<soloists>");
} else if (instrumentMap.contains(instrTemplate->id)) {
return instrumentMap[instrTemplate->id].id;
} else if (instrTemplate->family) {
return instrTemplate->family->id;
}
return QString("<unsorted>");
}
//---------------------------------------------------------
// getGroup
//---------------------------------------------------------
ScoreGroup ScoreOrder::getGroup(const QString family, const QString instrumentGroup) const
{
static const QString UNSORTED = QString("<unsorted>");
ScoreGroup unsortedScoreGroup;
for (const ScoreGroup& sg : groups) {
if ((sg.family == UNSORTED) && sg.unsorted.isEmpty()) {
unsortedScoreGroup = sg;
break;
}
}
if (family.isEmpty()) {
return unsortedScoreGroup;
}
for (const ScoreGroup& sg : groups) {
if (sg.family == family) {
return sg;
}
if ((sg.family == UNSORTED) && (sg.unsorted == instrumentGroup)) {
unsortedScoreGroup = sg;
}
}
return unsortedScoreGroup;
}
//---------------------------------------------------------
// setBracketsAndBarlines
//---------------------------------------------------------
void ScoreOrder::setBracketsAndBarlines(Score* score)
{
if (groups.size() <= 0) {
return;
}
bool prvThnBracket { false };
bool prvBarLineSpan { false };
QString prvSection { "" };
int prvInstrument { 0 };
Staff* prvStaff { nullptr };
Staff* thkBracketStaff { nullptr };
Staff* thnBracketStaff { nullptr };
int thkBracketSpan { 0 };
int thnBracketSpan { 0 };
for (Part* part : score->parts()) {
InstrumentIndex ii = searchTemplateIndexForId(part->instrument()->getId());
if (!ii.instrTemplate) {
continue;
}
QString family { getFamilyName(ii.instrTemplate, part->soloist()) };
const ScoreGroup sg = getGroup(family, instrumentGroups[ii.groupIndex]->id);
int staffIdx { 0 };
bool blockThinBracket { false };
for (Staff* staff : *part->staves()) {
for (BracketItem* bi : staff->brackets()) {
score->undo(new RemoveBracket(staff, bi->column(), bi->bracketType(), bi->bracketSpan()));
}
staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, 0);
if (prvSection.isEmpty() || (sg.section != prvSection)) {
if (thkBracketStaff && (thkBracketSpan > 1)) {
score->undoAddBracket(thkBracketStaff, 0, BracketType::NORMAL, thkBracketSpan);
}
if (sg.bracket && !staffIdx) {
thkBracketStaff = sg.bracket ? staff : nullptr;
thkBracketSpan = 0;
}
}
if (sg.bracket && !staffIdx) {
thkBracketSpan += part->nstaves();
}
if (!staffIdx || (ii.instrIndex != prvInstrument)) {
if (thnBracketStaff && (thnBracketSpan > 1)) {
score->undoAddBracket(thnBracketStaff, 1, BracketType::SQUARE, thnBracketSpan);
}
if (ii.instrIndex != prvInstrument) {
thnBracketStaff = (sg.thinBracket && !blockThinBracket) ? staff : nullptr;
thnBracketSpan = 0;
}
}
if (ii.instrTemplate->nstaves() > 1) {
blockThinBracket = true;
if (ii.instrTemplate->bracket[staffIdx] != BracketType::NO_BRACKET) {
score->undoAddBracket(staff, 2, ii.instrTemplate->bracket[staffIdx], ii.instrTemplate->bracketSpan[staffIdx]);
}
staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, ii.instrTemplate->barlineSpan[staffIdx]);
if (staffIdx < ii.instrTemplate->nstaves()) {
++staffIdx;
}
prvStaff = nullptr;
} else {
if (sg.thinBracket && !staffIdx) {
thnBracketSpan += part->nstaves();
}
if (prvStaff) {
prvStaff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN,
(prvBarLineSpan && (!prvSection.isEmpty() && (sg.section == prvSection))));
}
prvStaff = staff;
++staffIdx;
}
prvSection = sg.section;
prvBarLineSpan = sg.barLineSpan;
prvThnBracket = sg.thinBracket;
}
prvInstrument = ii.instrIndex;
}
if (thkBracketStaff && (thkBracketSpan > 1)) {
score->undoAddBracket(thkBracketStaff, 0, BracketType::NORMAL, thkBracketSpan);
}
if (thnBracketStaff && (thnBracketSpan > 1) && prvThnBracket) {
score->undoAddBracket(thnBracketStaff, 1, BracketType::SQUARE, thnBracketSpan);
}
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
@ -184,7 +317,7 @@ bool ScoreOrder::isValid() const
void ScoreOrder::read(Ms::XmlReader& reader)
{
id = reader.attribute("id");
const QString id { "" };
const QString sectionId { "" };
while (reader.readNextStartElement()) {
if (reader.name() == "name") {
name = qApp->translate("OrderXML", reader.readElementText().toUtf8().data());
@ -193,11 +326,34 @@ void ScoreOrder::read(Ms::XmlReader& reader)
} else if (reader.name() == "instrument") {
readInstrument(reader);
} else if (reader.name() == "family") {
readFamily(reader, id, false);
ScoreGroup sg;
sg.family = reader.readElementText().toUtf8().data();
sg.section = sectionId;
sg.bracket = false;
sg.showSystemMarkings = false;
sg.barLineSpan = false;
sg.thinBracket = false;
groups << sg;
} else if (reader.name() == "soloists") {
readSoloists(reader, id);
readSoloists(reader, sectionId);
} else if (reader.name() == "unsorted") {
readUnsorted(reader, id, false);
QString group { reader.attribute("group", QString("")) };
if (hasGroup(UNSORTED_ID, group)) {
reader.skipCurrentElement();
return;
}
ScoreGroup sg;
sg.family = QString(UNSORTED_ID);
sg.section = sectionId;
sg.unsorted = group;
sg.bracket = true;
sg.showSystemMarkings = false;
sg.barLineSpan = false;
sg.thinBracket = false;
groups << sg;
reader.skipCurrentElement();
} else {
reader.unknown();
}

View file

@ -23,6 +23,7 @@
#define __SCOREORDER_H__
#include "libmscore/mscore.h"
#include "instrtemplate.h"
namespace Ms {
//---------------------------------------------------------
@ -68,12 +69,14 @@ struct ScoreOrder
bool readBoolAttribute(Ms::XmlReader& reader, const char* name, bool defValue);
void readInstrument(Ms::XmlReader& reader);
void readSoloists(Ms::XmlReader& reader, const QString section);
void readUnsorted(Ms::XmlReader& reader, const QString section, bool inSection);
void readFamily(Ms::XmlReader& reader, const QString section, bool inSection);
void readSection(Ms::XmlReader& reader);
bool hasGroup(const QString& id, const QString& group=QString()) const;
bool isValid() const;
QString getFamilyName(const InstrumentTemplate* instrTemplate, bool soloist) const;
ScoreGroup getGroup(const QString family, const QString instrumentGroup) const;
void setBracketsAndBarlines(Score* score);
void read(Ms::XmlReader& reader);
void write(Ms::XmlWriter& xml) const;

View file

@ -144,6 +144,8 @@ void NotationParts::setParts(const mu::instruments::PartInstrumentList& parts)
sortParts(parts, masterScore(), originalStaves);
masterScore()->setBracketsAndBarlines();
updateScore();
m_partsNotifier->changed();
@ -153,6 +155,7 @@ void NotationParts::setScoreOrder(const instruments::ScoreOrder& order)
{
Ms::ScoreOrder so = ScoreOrderConverter::convertScoreOrder(order);
masterScore()->undo(new Ms::ChangeScoreOrder(masterScore(), so));
masterScore()->setBracketsAndBarlines();
}
void NotationParts::setPartVisible(const ID& partId, bool visible)
@ -660,6 +663,9 @@ void NotationParts::appendStaff(Staff* staff, const ID& destinationPartId)
staff->setPart(destinationPart);
insertStaff(staff, staffIndex);
masterScore()->setBracketsAndBarlines();
updateScore();
Ms::Instrument* instrument = instrumentInfo.instrument;
@ -737,6 +743,8 @@ void NotationParts::removeParts(const IDList& partsIds)
sortParts(parts, masterScore(), originalStaves);
masterScore()->setBracketsAndBarlines();
updateScore();
}
@ -799,6 +807,7 @@ void NotationParts::removeStaves(const IDList& stavesIds)
score()->cmdRemoveStaff(staff->idx());
}
masterScore()->setBracketsAndBarlines();
updateScore();
}
@ -846,6 +855,8 @@ void NotationParts::moveParts(const IDList& sourcePartsIds, const ID& destinatio
sortParts(parts, masterScore(), masterScore()->staves());
masterScore()->setBracketsAndBarlines();
updateScore();
m_partsNotifier->changed();
@ -1046,6 +1057,9 @@ void NotationParts::moveStaves(const IDList& sourceStavesIds, const ID& destinat
destinationStaffIndex -= score()->staffIdx(destinationPart); // NOTE: convert to local part's staff index
doMoveStaves(staves, destinationStaffIndex, destinationPart);
masterScore()->setBracketsAndBarlines();
updateScore();
}