Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mainwin: Add support for shift+dblClick to insert syllable or chord #204

Merged
merged 3 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 12 additions & 15 deletions src/core/muselementfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,18 @@ bool CAMusElementFactory::configureNote(int pitch,
if (voice->lastNote() == mpoMusElement) {
// note was appended, reposition elements in dependent contexts accordingly
for (CALyricsContext* lc : voice->lyricsContextList()) {
lc->repositSyllables();
lc->repositionElements();
}
for (CAContext* context : voice->staff()->sheet()->contextList()) {
switch (context->contextType()) {
case CAContext::FunctionMarkContext:
static_cast<CAFunctionMarkContext*>(context)->repositFunctions();
static_cast<CAFunctionMarkContext*>(context)->repositionElements();
break;
case CAContext::FiguredBassContext:
static_cast<CAFiguredBassContext*>(context)->repositFiguredBassMarks();
static_cast<CAFiguredBassContext*>(context)->repositionElements();
break;
case CAContext::ChordNameContext:
static_cast<CAChordNameContext*>(context)->repositChordNames();
static_cast<CAChordNameContext*>(context)->repositionElements();
break;
default:
break;
Expand All @@ -283,21 +283,16 @@ bool CAMusElementFactory::configureNote(int pitch,
} else {
// note was inserted somewhere inbetween, insert empty element in dependent contexts accordingly
for (CALyricsContext* lc : voice->lyricsContextList()) {
lc->addEmptySyllable(mpoMusElement->timeStart(), mpoMusElement->timeLength());
lc->insertEmptyElement(mpoMusElement->timeStart());
lc->repositionElements();
}
for (CAContext* context : voice->staff()->sheet()->contextList()) {
switch (context->contextType()) {
case CAContext::FunctionMarkContext:
static_cast<CAFunctionMarkContext*>(context)->addEmptyFunction(
mpoMusElement->timeStart(), mpoMusElement->timeLength());
break;
case CAContext::FiguredBassContext:
static_cast<CAFiguredBassContext*>(context)->addEmptyFiguredBassMark(
mpoMusElement->timeStart(), mpoMusElement->timeLength());
break;
case CAContext::ChordNameContext:
static_cast<CAChordNameContext*>(context)->addEmptyChordName(
mpoMusElement->timeStart(), mpoMusElement->timeLength());
context->insertEmptyElement(mpoMusElement->timeStart());
context->repositionElements();
break;
default:
break;
Expand Down Expand Up @@ -483,12 +478,14 @@ bool CAMusElementFactory::configureRest(CAVoice* voice, CAMusElement* right)
removeMusElem(true);
else {
for (CALyricsContext* lc : voice->lyricsContextList()) {
lc->repositSyllables();
lc->repositionElements();
}
for (CAContext* context : voice->staff()->sheet()->contextList()) {
switch (context->contextType()) {
case CAContext::FunctionMarkContext:
case CAContext::FiguredBassContext:
case CAContext::ChordNameContext:
static_cast<CAChordNameContext*>(context)->repositChordNames();
static_cast<CAChordNameContext*>(context)->repositionElements();
break;
default:
break;
Expand Down
2 changes: 1 addition & 1 deletion src/import/lilypondimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ CALyricsContext* CALilyPondImport::importLyricsContextImpl()
lc->addSyllable(lastSyllable = new CASyllable(text, false, false, lc, timeSDummy, 0));
}
}
lc->repositSyllables(); // sets syllables timeStarts and timeLengths
lc->repositionElements(); // sets syllables timeStarts and timeLengths

return lc;
}
Expand Down
25 changes: 9 additions & 16 deletions src/score/chordnamecontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ CAChordNameContext::CAChordNameContext(QString name, CASheet* sheet)
: CAContext(name, sheet)
{
setContextType(ChordNameContext);
repositChordNames();
repositionElements();
}

CAChordNameContext::~CAChordNameContext()
Expand All @@ -52,27 +52,20 @@ void CAChordNameContext::addChordName(CAChordName* m, bool replace)
}
}

/*!
Inserts an empty chord name and shifts the chord names after.
This function is usually called when initializing the context.
*/
void CAChordNameContext::addEmptyChordName(int timeStart, int timeLength)
CAMusElement* CAChordNameContext::insertEmptyElement(int timeStart)
{
int i;
for (i = 0; i < _chordNameList.size() && _chordNameList[i]->timeStart() < timeStart; i++)
;
_chordNameList.insert(i, (new CAChordName(CADiatonicPitch::Undefined, "", this, timeStart, timeLength)));
CAChordName *newChord = new CAChordName(CADiatonicPitch::Undefined, "", this, timeStart, 1);
_chordNameList.insert(i, newChord);
for (i++; i < _chordNameList.size(); i++)
_chordNameList[i]->setTimeStart(_chordNameList[i]->timeStart() + timeLength);
}
_chordNameList[i]->setTimeStart(_chordNameList[i]->timeStart() + 1);

/*!
It repositions the existing chord names (sets timeStart and timeLength) one by one according to the playable music
elements above the context.
return newChord;
}

\sa CALyricsContext::repositSyllables(), CAFiguredBassContext::repositFiguredBassMarks(), CAFunctionMarkContext::repositFunctions()
*/
void CAChordNameContext::repositChordNames()
void CAChordNameContext::repositionElements()
{
int ts, tl;
int curIdx; // contains current position in _chordNameList
Expand All @@ -86,7 +79,7 @@ void CAChordNameContext::repositChordNames()

// add new empty chord names, if playables still exist above
if (curIdx == _chordNameList.size()) {
addEmptyChordName(ts, tl);
insertEmptyElement(ts);
}

// apply timeStart and timeLength to existing chord names
Expand Down
4 changes: 2 additions & 2 deletions src/score/chordnamecontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ class CAChordNameContext : public CAContext {
CAMusElement* next(CAMusElement* elt);
CAMusElement* previous(CAMusElement* elt);
bool remove(CAMusElement* elt);
CAMusElement *insertEmptyElement(int timeStart);
void repositionElements();

QList<CAChordName*>& chordNameList() { return _chordNameList; }
CAChordName* chordNameAtTimeStart(int timeStart);

void repositChordNames();
void addChordName(CAChordName*, bool replace = true);
void addEmptyChordName(int timeStart, int timeLength);

private:
QList<CAChordName*> _chordNameList;
Expand Down
17 changes: 17 additions & 0 deletions src/score/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,20 @@ CAContext::~CAContext()

\sa CAMusElement::clone(), CADocument::clone()
*/

/*!
\fn CAContext::insertEmptyElement(int timeStart)
Inserts an empty dependent element (syllable, chord name, figured bass mark, function mark) to the context.
After the call the elements need to be repositioned manually (subsequent timeStarts and timeLengths will be out of place).
This function is usually called when initializing the dependent context or inserting a new note.

\sa CAContext::repositionElements()
*/

/*!
\fn CAContext::repositionElements()
Repositions the existing dependent elements (syllables, chord names, figured bass marks, function marks) by setting timeStart and timeLength
one by one according to the playable music it depends on. The order is preserved.

\sa CAContext::insertEmptyElement(int)
*/
3 changes: 3 additions & 0 deletions src/score/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ class CAContext {
CASheet* sheet() { return _sheet; }
void setSheet(CASheet* sheet) { _sheet = sheet; }


virtual void clear() = 0;
virtual CAMusElement* next(CAMusElement* elt) = 0;
virtual CAMusElement* previous(CAMusElement* elt) = 0;
virtual bool remove(CAMusElement* elt) = 0;
virtual CAMusElement *insertEmptyElement(int timeStart) = 0;
virtual void repositionElements() = 0;

protected:
void setContextType(CAContextType t) { _contextType = t; }
Expand Down
131 changes: 62 additions & 69 deletions src/score/figuredbasscontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ CAFiguredBassContext::CAFiguredBassContext(QString name, CASheet* sheet)
: CAContext(name, sheet)
{
setContextType(FiguredBassContext);
repositFiguredBassMarks();
repositionElements();
}

CAFiguredBassContext::~CAFiguredBassContext()
Expand All @@ -51,74 +51,6 @@ void CAFiguredBassContext::addFiguredBassMark(CAFiguredBassMark* m, bool replace
_figuredBassMarkList[i]->setTimeStart(_figuredBassMarkList[i]->timeStart() + m->timeLength());
}

/*!
Inserts an empty figured bass mark and shifts the marks after.
This function is usually called when initializing the context.
*/
void CAFiguredBassContext::addEmptyFiguredBassMark(int timeStart, int timeLength)
{
int i;
for (i = 0; i < _figuredBassMarkList.size() && _figuredBassMarkList[i]->timeStart() < timeStart; i++)
;
_figuredBassMarkList.insert(i, (new CAFiguredBassMark(this, timeStart, timeLength)));
for (i++; i < _figuredBassMarkList.size(); i++)
_figuredBassMarkList[i]->setTimeStart(_figuredBassMarkList[i]->timeStart() + timeLength);
}

/*!
Updates timeStarts and timeLength of all figured bass marks according to the chords they belong.
Adds new empty figured bass marks at the end, if needed.

\sa CALyricsContext::repositSyllables(), CAFunctionMarkContext::repositFunctions(), CAChordNameContext::repositChordNames()
*/
void CAFiguredBassContext::repositFiguredBassMarks()
{
if (!sheet()) {
return;
}

QList<CAPlayable*> chord = sheet()->getChord(0);
int fbmIdx = 0;
while (chord.size()) {
int maxTimeStart = chord[0]->timeStart();
int minTimeEnd = chord[0]->timeEnd();
bool notes = false; // are notes present in the chord or only rests?
for (int i = 1; i < chord.size(); i++) {
if (chord[i]->musElementType() == CAMusElement::Note) {
notes = true;
}

if (chord[i]->timeStart() > maxTimeStart) {
maxTimeStart = chord[i]->timeStart();
}
if (chord[i]->timeEnd() < minTimeEnd) {
minTimeEnd = chord[i]->timeEnd();
}
}

// only assign figured bass marks under the notes
if (notes) {
// add new empty figured bass, if none exist
if (fbmIdx == _figuredBassMarkList.size()) {
addEmptyFiguredBassMark(maxTimeStart, minTimeEnd - maxTimeStart);
}

CAFiguredBassMark* mark = _figuredBassMarkList[fbmIdx];
mark->setTimeStart(maxTimeStart);
mark->setTimeLength(minTimeEnd - maxTimeStart);
fbmIdx++;
}

chord = sheet()->getChord(minTimeEnd);
}

// updated times for the figured bass marks at the end (after the score)
for (; fbmIdx < _figuredBassMarkList.size(); fbmIdx++) {
_figuredBassMarkList[fbmIdx]->setTimeStart(((fbmIdx > 0) ? _figuredBassMarkList[fbmIdx - 1] : _figuredBassMarkList[0])->timeEnd());
_figuredBassMarkList[fbmIdx]->setTimeLength(CAPlayableLength::Quarter);
}
}

/*!
Returns figured bass mark at the given \a time.
*/
Expand Down Expand Up @@ -188,3 +120,64 @@ bool CAFiguredBassContext::remove(CAMusElement* elt)

return success;
}

CAMusElement *CAFiguredBassContext::insertEmptyElement(int timeStart)
{
CAFiguredBassMark *newElt = new CAFiguredBassMark(this, timeStart, 1);
int i;
for (i = 0; i < _figuredBassMarkList.size() && _figuredBassMarkList[i]->timeStart() < timeStart; i++)
;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked and this is old code, but was / is there a plan to add more code?
Else for loop could be removed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the for loop here is to compute the value of i (it's declared outside of the for block), because we have no more intelligent way of computing the position than linearly scanning the list. We could do binary search in the future though.

_figuredBassMarkList.insert(i, newElt);
for (i++; i < _figuredBassMarkList.size(); i++)
_figuredBassMarkList[i]->setTimeStart(_figuredBassMarkList[i]->timeStart() + 1);

return newElt;
}

void CAFiguredBassContext::repositionElements()
{
if (!sheet()) {
return;
}

QList<CAPlayable*> chord = sheet()->getChord(0);
int fbmIdx = 0;
while (chord.size()) {
int maxTimeStart = chord[0]->timeStart();
int minTimeEnd = chord[0]->timeEnd();
bool notes = false; // are notes present in the chord or only rests?
for (int i = 1; i < chord.size(); i++) {
if (chord[i]->musElementType() == CAMusElement::Note) {
notes = true;
}

if (chord[i]->timeStart() > maxTimeStart) {
maxTimeStart = chord[i]->timeStart();
}
if (chord[i]->timeEnd() < minTimeEnd) {
minTimeEnd = chord[i]->timeEnd();
}
}

// only assign figured bass marks under the notes
if (notes) {
// add new empty figured bass, if none exist
if (fbmIdx == _figuredBassMarkList.size()) {
insertEmptyElement(maxTimeStart);
}

CAFiguredBassMark* mark = _figuredBassMarkList[fbmIdx];
mark->setTimeStart(maxTimeStart);
mark->setTimeLength(minTimeEnd - maxTimeStart);
fbmIdx++;
}

chord = sheet()->getChord(minTimeEnd);
}

// updated times for the figured bass marks at the end (after the score)
for (; fbmIdx < _figuredBassMarkList.size(); fbmIdx++) {
_figuredBassMarkList[fbmIdx]->setTimeStart(((fbmIdx > 0) ? _figuredBassMarkList[fbmIdx - 1] : _figuredBassMarkList[0])->timeEnd());
_figuredBassMarkList[fbmIdx]->setTimeLength(CAPlayableLength::Quarter);
}
}
4 changes: 2 additions & 2 deletions src/score/figuredbasscontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ class CAFiguredBassContext : public CAContext {
CAMusElement* next(CAMusElement* elt);
CAMusElement* previous(CAMusElement* elt);
bool remove(CAMusElement* elt);
CAMusElement *insertEmptyElement(int timeStart);
void repositionElements();

QList<CAFiguredBassMark*>& figuredBassMarkList() { return _figuredBassMarkList; }
CAFiguredBassMark* figuredBassMarkAtTimeStart(int timeStart);

void repositFiguredBassMarks();
void addFiguredBassMark(CAFiguredBassMark*, bool replace = true);
void addEmptyFiguredBassMark(int timeStart, int timeLength);

private:
QList<CAFiguredBassMark*> _figuredBassMarkList;
Expand Down
Loading