diff --git a/src/libs/core/score/tmeasureobject.cpp b/src/libs/core/score/tmeasureobject.cpp index 91f38a359d8cc12a6f7a6e3f428d5a36145883e4..fe54db3460a8ddd36de087635d1f745aa8cd04d1 100644 --- a/src/libs/core/score/tmeasureobject.cpp +++ b/src/libs/core/score/tmeasureobject.cpp @@ -56,7 +56,7 @@ void TmeasureObject::setStaff(TstaffObject* st) { void TmeasureObject::appendNewNotes(int segmentId, int count) { // so far we are sure there is enough space for whole note list in this measure -// qDebug() << debug() << "append" << count << "note(s)"; +// qDebug() << debug() << "append" << count << "note(s) from" << segmentId; for (int n = segmentId; n < segmentId + count; ++n) m_notes.append(m_score->noteSegment(n)); updateRhythmicGroups(); diff --git a/src/libs/core/score/tnoteobject.cpp b/src/libs/core/score/tnoteobject.cpp index 982956d4e3bee33093a6379766b67b7ea59d0a99..e18691a42d6de85945903198fb6be3930d50be52 100644 --- a/src/libs/core/score/tnoteobject.cpp +++ b/src/libs/core/score/tnoteobject.cpp @@ -113,6 +113,14 @@ TnoteObject::~TnoteObject() { } +void TnoteObject::setStaff(TstaffObject* staffObj) { + if (staffObj != m_staff) { + m_staff = staffObj; + setParentItem(m_staff->staffItem()); + } +} + + void TnoteObject::setMeasure(TmeasureObject* m) { m_measure = m; } diff --git a/src/libs/core/score/tnoteobject.h b/src/libs/core/score/tnoteobject.h index 0fbf281b31601e08707f6219934b2409026c3739..9871abd807394dee4062e87c7af2ee487dd1349c 100644 --- a/src/libs/core/score/tnoteobject.h +++ b/src/libs/core/score/tnoteobject.h @@ -44,6 +44,7 @@ public: ~TnoteObject(); TstaffObject* staff() const { return m_staff; } + void setStaff(TstaffObject* staffObj); TmeasureObject* measure() { return m_measure; } void setMeasure(TmeasureObject* m); diff --git a/src/libs/core/score/tscoreobject.cpp b/src/libs/core/score/tscoreobject.cpp index ce37e57d128c2ecb906a965e65632c28d95feb89..4e573b72dc4181cd41df1a78a30fbf00d35d1278 100644 --- a/src/libs/core/score/tscoreobject.cpp +++ b/src/libs/core/score/tscoreobject.cpp @@ -27,7 +27,7 @@ #include <QtCore/qdebug.h> #include "checktime.h" -#define MIN_STAFF_FACTOR (1.2) // if TstaffObject::gapFactor is less than this value - new staff is necessary +// #define MIN_STAFF_FACTOR (1.2) // if TstaffObject::gapFactor is less than this value - new staff is necessary TscoreObject::TscoreObject(QObject* parent) : @@ -78,21 +78,15 @@ CHECKTIME ( auto lastMeasure = m_measures.last(); if (lastMeasure->free() == 0) { // new measure is needed lastMeasure = new TmeasureObject(m_measures.count(), this); - auto lastSt = lastStaff(); - if (lastSt->gapFactor() < MIN_STAFF_FACTOR) { // and new staff is needed as well - emit staffCreate(); - lastSt = lastStaff(); - } - lastStaff()->appendMeasure(lastMeasure); m_measures << lastMeasure; + lastStaff()->appendMeasure(lastMeasure); } int noteDur = n.duration(); if (noteDur > lastMeasure->free()) { // split note that is adding - int leftDuration = noteDur - lastMeasure->free(); - QList<Tnote> notesToCurrent; - QList<Tnote> notesToNext; + int leftDuration = noteDur - lastMeasure->free(); int lastNoteId = m_segments.count(); + QList<Tnote> notesToCurrent; solveList(n, lastMeasure->free(), notesToCurrent); // solve free duration in current measure if (notesToCurrent.isEmpty()) qDebug() << "[TscoreObject] can't resolve duration of" << lastMeasure->free(); @@ -101,6 +95,7 @@ CHECKTIME ( lastMeasure->appendNewNotes(lastNoteId, notesToCurrent.count()); } + QList<Tnote> notesToNext; solveList(n, leftDuration, notesToNext); // solve remaining duration for the next measure lastNoteId = m_segments.count(); // update id of the last note segment if (notesToNext.isEmpty()) @@ -108,20 +103,14 @@ CHECKTIME ( else { appendNoteList(notesToNext); auto newLastMeasure = new TmeasureObject(m_measures.count(), this); // add a new measure - auto lastSt = lastStaff(); - if (lastSt->gapFactor() < 1.2) { - emit staffCreate(); - lastSt = lastStaff(); - } - lastSt->appendMeasure(newLastMeasure); m_measures << newLastMeasure; + lastStaff()->appendMeasure(newLastMeasure); newLastMeasure->appendNewNotes(lastNoteId, notesToNext.count()); } - } else { // just add new note to the measure + } else { // just add new note to the last measure m_notes << n; int lastNoteId = m_segments.count(); - auto newSeg = new TnotePair(lastNoteId, &m_notes.last()); - m_segments << newSeg; + m_segments << new TnotePair(lastNoteId, &m_notes.last()); lastMeasure->appendNewNotes(lastNoteId, 1); } @@ -182,7 +171,10 @@ int TscoreObject::meterToInt() { return static_cast<int>(m_meter->meter()); } qreal TscoreObject::stavesHeight() { - return m_staves.isEmpty() ? 0.0 : lastStaff()->staffItem()->y() + lastStaff()->staffItem()->height() * lastStaff()->scale(); + if (m_staves.isEmpty()) + return 0.0; + auto last = lastStaff(); + return last->staffItem()->y() + last->staffItem()->height() * last->scale(); } @@ -194,6 +186,7 @@ void TscoreObject::addStaff(TstaffObject* st) { m_staves.append(st); if (m_staves.count() == 1) // initialize first measure of first staff st->appendMeasure(m_measures.first()); + // next staves position ca be set only when staffItem is set, see TstaffObject::setStaffItem() then connect(st, &TstaffObject::hiNotePosChanged, [=](int staffNr, qreal offset){ for (int i = staffNr; i < m_staves.size(); ++i) // move every staff about offset m_staves[i]->staffItem()->setY(m_staves[i]->staffItem()->y() + offset); @@ -209,6 +202,27 @@ void TscoreObject::addStaff(TstaffObject* st) { } +void TscoreObject::shiftMeasures(int firstId, int count) { + qDebug() << "[TscoreObject] shifting" << count << "measure(s) from" << firstId; + auto sourceStaff = m_measures[firstId]->staff(); + TstaffObject* targetStaff = nullptr; + if (sourceStaff == lastStaff()) { // create new staff to shift measure(s) there + emit staffCreate(); + targetStaff = lastStaff(); + } else + targetStaff = m_staves[sourceStaff->number()]; + + for (int m = firstId; m < firstId + count; ++m) { + auto meas = m_measures[m]; + for (int n = 0; n < meas->noteCount(); ++n) + meas->note(n)->object()->setStaff(targetStaff); + + sourceStaff->takeMeasure(meas->number() - sourceStaff->firstMeasureNr()); + targetStaff->appendMeasure(meas); + } + targetStaff->refresh(); +} + //################################################################################################# //################### PRIVATE ############################################ //################################################################################################# diff --git a/src/libs/core/score/tscoreobject.h b/src/libs/core/score/tscoreobject.h index bfa482eea976c8756bf06a46eac562be024c9c53..9ef6a109640c624c590f1e3bc424c0b7f9a44532 100644 --- a/src/libs/core/score/tscoreobject.h +++ b/src/libs/core/score/tscoreobject.h @@ -116,7 +116,15 @@ public: signals: void meterChanged(); + + /** + * Asks Score.qml about create new staff + */ void staffCreate(); + + /** + * Informs Score.qml that content widget height has to be adjusted to all staves height + */ void stavesHeightChanged(); protected: @@ -124,6 +132,13 @@ protected: TclefOffset clefOffset() const { return m_clefOffset; } + /** + * Shifts @p count measures starting from @p firstId among all score measures. + * It detects the staff the measure belongs to and finds the next one or creates a new staff if not exists. + * Also it manages ownership of initial (source) staff shifted notes and sets parent of every such note to target staff. + */ + void shiftMeasures(int firstId, int count = 1); + private: /** * Appends notes to @p m_notes list, creates corresponding @p TnotePair diff --git a/src/libs/core/score/tstaffobject.cpp b/src/libs/core/score/tstaffobject.cpp index c6c4b0cc2ed3bf4fa9c7ee90c02cb4a45dea3a9f..404aabc12bf79d09545bc1e28eb2c8c12254b4df 100644 --- a/src/libs/core/score/tstaffobject.cpp +++ b/src/libs/core/score/tstaffobject.cpp @@ -31,20 +31,18 @@ TstaffObject::TstaffObject(QObject* parent) : m_score(nullptr), m_upperLine(16.0), m_staffItem(nullptr), - m_loNotePos(28.0), m_hiNotePos(12.0) + m_loNotePos(28.0), m_hiNotePos(8.0) { } -TstaffObject::~TstaffObject() { - qDebug() << debug() << "is going delete"; +TstaffObject::~TstaffObject() { + qDebug() << "[TstaffObject] is going delete"; } void TstaffObject::setScore(TscoreObject* s) { m_score = s; - setParent(s); - qDebug() << debug() << this << "got a score parent" << s; m_score->addStaff(this); } @@ -63,16 +61,15 @@ void TstaffObject::setUpperLine(qreal upLine) { void TstaffObject::setStaffItem(QQuickItem* si) { m_staffItem = si; - if (m_score->stavesCount() > 1) { + if (m_score->stavesCount() > 1) { // initial staff position, depends on lowest note in the previous staff auto prevStaff = m_score->staff(m_score->stavesCount() - 2); - m_staffItem->setY(prevStaff->staffItem()->y() + prevStaff->minHeight() * prevStaff->scale()); // scale of this staff is not set yet + m_staffItem->setY(prevStaff->staffItem()->y() + (prevStaff->loNotePos() - hiNotePos()) * prevStaff->scale()); // scale of this staff is not set yet } - qDebug() << debug() << "set staff item to" << m_staffItem; } int TstaffObject::firstMeasureNr() { - return m_measures.first()->number(); + return m_measures.empty() ? 0 : m_measures.first()->number(); } @@ -111,28 +108,38 @@ void TstaffObject::fit() { } qreal factor = 2.5; - qreal m_gapsSum = 0.0; - qreal m_allNotesWidth = 0.0; + qreal gapsSum = 0.0; + m_allNotesWidth = 0.0; for (int m = 0; m < m_measures.size(); ++m) { auto measure = m_measures[m]; for (int n = 0; n < measure->noteCount(); ++n) { auto note = measure->note(n)->object(); - m_gapsSum += note->rhythmFactor(); + gapsSum += note->rhythmFactor(); m_allNotesWidth += note->width(); - if (n > 1) { - factor = (m_staffItem->width() - m_notesIndent - m_allNotesWidth - 1.0) / m_gapsSum; - // if (factor < 1.0) { // shift current measure and the next ones - // needShift = true; - // break; // rest of the notes goes to the next staff - // } + factor = (m_staffItem->width() - m_notesIndent - m_allNotesWidth - 1.0) / gapsSum; + if (factor < 0.8) { // shift current measure and the next ones + if (m == 0) + qDebug() << debug() << "!!!!!! Split this measure among staves !!!!!"; + else { + for (int nn = n; nn >= 0; --nn) { // revert gapsSum and m_allNotesWidth to state at the end of the previous measure + auto revertNote = measure->note(nn)->object(); + gapsSum -= revertNote->rhythmFactor(); + m_allNotesWidth -= revertNote->width(); + } + m_gapFactor = (m_staffItem->width() - m_notesIndent - m_allNotesWidth - 1.0) / gapsSum; // allow factor bigger than 2.5 + m_score->shiftMeasures(measure->number(), m_measures.count() - m); + updateNotesPos(); + return; + } + break; // rest of the notes goes to the next staff } } } m_gapFactor = qBound(0.5, factor, 2.5); // notes in this staff are ready to positioning updateNotesPos(); - qDebug() << debug() << "gap factor" << m_gapFactor << m_allNotesWidth << m_gapsSum; +// qDebug() << debug() << "gap factor" << m_gapFactor << "notes count" << lastMeasure()->last()->index() + 1; } @@ -141,7 +148,8 @@ void TstaffObject::updateNotesPos(int startMeasure) { if (firstMeas->isEmpty()) return; - qDebug() << debug() << "updating notes positions from" << startMeasure << "measure"; + qDebug() << debug() << "updating notes positions from" << startMeasure << "measure" + << "gap factor" << m_gapFactor << "notes count" << lastMeasure()->last()->index() + 1; TnoteObject* prevNote = nullptr; if (startMeasure == 0) firstMeas->first()->object()->setX(m_notesIndent); @@ -178,9 +186,19 @@ void TstaffObject::checkNotesRange(bool doEmit) { void TstaffObject::appendMeasure(TmeasureObject* m) { m->setStaff(this); m_measures << m; + if (m_measures.count() == 1) + emit firstMeasureNrChanged(); } +void TstaffObject::takeMeasure(int id) { + m_measures.removeAt(id); + if (id == 0) + emit firstMeasureNrChanged(); +} + + + //################################################################################################# //################### PRIVATE ############################################ //################################################################################################# diff --git a/src/libs/core/score/tstaffobject.h b/src/libs/core/score/tstaffobject.h index 473b9737074d7576d42820ab84cf72017b6b81ae..b8ac45e22d7971625ea882ac2289f3c2fdfa5809 100644 --- a/src/libs/core/score/tstaffobject.h +++ b/src/libs/core/score/tstaffobject.h @@ -33,7 +33,7 @@ class Tnote; /** - * + * @class TstaffObject is C++ logic of Staff.qml */ class NOOTKACORE_EXPORT TstaffObject : public QObject { @@ -106,6 +106,11 @@ public: */ qreal minHeight() const { return m_loNotePos - (number() == 0 ? 0.0 : m_hiNotePos); } + /** + * Width of all notes on the staff + */ + qreal allNotesWidth() { return m_allNotesWidth; } + /** * Scaling factor of the staff */ @@ -133,6 +138,7 @@ protected: void checkNotesRange(bool doEmit = true); void appendMeasure(TmeasureObject* m); + void takeMeasure(int id); private: void findLowestNote(); /**< Checks all Y positions of staff notes to find lowest one */ @@ -146,6 +152,7 @@ private: qreal m_notesIndent; qreal m_gapFactor; qreal m_loNotePos, m_hiNotePos; + qreal m_allNotesWidth = 0.0; }; #endif // TSTAFFOBJECT_H diff --git a/src/qml/score/Score.qml b/src/qml/score/Score.qml index c1678564fd487e7677ab3985dae1fc43027d56cb..dcf57b8c1c64cda5c79ac67c253af89edf50050a 100644 --- a/src/qml/score/Score.qml +++ b/src/qml/score/Score.qml @@ -37,6 +37,7 @@ Flickable { score.contentY = score.contentHeight - score.height lastStaff.keySignature.onKeySignatureChanged.connect(setKeySignature) } + onStavesHeightChanged: score.contentHeight = Math.max(stavesHeight, score.height) } property alias scale: staff0.scale @@ -61,7 +62,7 @@ Flickable { width: parent.width contentWidth: score.width - contentHeight: scoreObj.stavesHeight +// contentHeight: Math.max(scoreObj.stavesHeight, score.height) Rectangle { id: bgRect