1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2016 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "utils.h"
14 #include "score.h"
15 #include "chord.h"
16 #include "measure.h"
17 #include "tie.h"
18 #include "tuplet.h"
19 #include "staff.h"
20 #include "part.h"
21 #include "drumset.h"
22 #include "slur.h"
23 #include "navigate.h"
24 #include "stringdata.h"
25 #include "undo.h"
26 #include "range.h"
27 #include "excerpt.h"
28 #include "accidental.h"
29
30 namespace Ms {
31
32 //---------------------------------------------------------
33 // noteValForPosition
34 //---------------------------------------------------------
35
noteValForPosition(Position pos,AccidentalType at,bool & error)36 NoteVal Score::noteValForPosition(Position pos, AccidentalType at, bool &error)
37 {
38 error = false;
39 Segment* s = pos.segment;
40 int line = pos.line;
41 Fraction tick = s->tick();
42 int staffIdx = pos.staffIdx;
43 Staff* st = staff(staffIdx);
44 ClefType clef = st->clef(tick);
45 const Instrument* instr = st->part()->instrument(s->tick());
46 NoteVal nval;
47 const StringData* stringData = 0;
48
49 // pitched/unpitched note entry depends on instrument (override StaffGroup)
50 StaffGroup staffGroup = st->staffType(tick)->group();
51 if (staffGroup != StaffGroup::TAB)
52 staffGroup = instr->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
53
54 switch (staffGroup) {
55 case StaffGroup::PERCUSSION: {
56 if (_is.rest())
57 break;
58 const Drumset* ds = instr->drumset();
59 nval.pitch = _is.drumNote();
60 if (nval.pitch < 0) {
61 error = true;
62 return nval;
63 }
64 nval.headGroup = ds->noteHead(nval.pitch);
65 if (nval.headGroup == NoteHead::Group::HEAD_INVALID) {
66 error = true;
67 return nval;
68 }
69 break;
70 }
71 case StaffGroup::TAB: {
72 if (_is.rest()) {
73 error = true;
74 return nval;
75 }
76 stringData = instr->stringData();
77 line = st->staffType(tick)->visualStringToPhys(line);
78 if (line < 0 || line >= stringData->strings()) {
79 error = true;
80 return nval;
81 }
82 // build a default NoteVal for that string
83 nval.string = line;
84 if (pos.fret != FRET_NONE) // if a fret is given, use it
85 nval.fret = pos.fret;
86 else { // if no fret, use 0 as default
87 _is.setString(line);
88 nval.fret = 0;
89 }
90 // reduce within fret limit
91 if (nval.fret > stringData->frets())
92 nval.fret = stringData->frets();
93 // for open strings, only accepts fret 0 (strings in StringData are from bottom to top)
94 int strgDataIdx = stringData->strings() - line - 1;
95 if (nval.fret > 0 && stringData->stringList().at(strgDataIdx).open == true)
96 nval.fret = 0;
97 nval.pitch = stringData->getPitch(line, nval.fret, st, tick);
98 break;
99 }
100
101 case StaffGroup::STANDARD: {
102 AccidentalVal acci = (at == AccidentalType::NONE ? s->measure()->findAccidental(s, staffIdx, line, error) : Accidental::subtype2value(at));
103 if (error)
104 return nval;
105 int step = absStep(line, clef);
106 int octave = step/7;
107 nval.pitch = step2pitch(step) + octave * 12 + int(acci);
108 if (styleB(Sid::concertPitch))
109 nval.tpc1 = step2tpc(step % 7, acci);
110 else {
111 nval.pitch += instr->transpose().chromatic;
112 nval.tpc2 = step2tpc(step % 7, acci);
113 Interval v = st->part()->instrument(tick)->transpose();
114 if (v.isZero())
115 nval.tpc1 = nval.tpc2;
116 else
117 nval.tpc1 = Ms::transposeTpc(nval.tpc2, v, true);
118 }
119 }
120 break;
121 }
122 return nval;
123 }
124
125 //---------------------------------------------------------
126 // addPitch
127 //---------------------------------------------------------
128
addPitch(NoteVal & nval,bool addFlag,InputState * externalInputState)129 Note* Score::addPitch(NoteVal& nval, bool addFlag, InputState* externalInputState)
130 {
131 InputState& is = externalInputState ? (*externalInputState) : _is;
132
133 if (addFlag) {
134 ChordRest* c = toChordRest(is.lastSegment()->element(is.track()));
135
136 if (c == 0 || !c->isChord()) {
137 qDebug("Score::addPitch: cr %s", c ? c->name() : "zero");
138 return 0;
139 }
140 Note* note = addNote(toChord(c), nval, /* forceAccidental */ false, externalInputState);
141 if (is.lastSegment() == is.segment()) {
142 NoteEntryMethod entryMethod = is.noteEntryMethod();
143 if (entryMethod != NoteEntryMethod::REALTIME_AUTO && entryMethod != NoteEntryMethod::REALTIME_MANUAL)
144 is.moveToNextInputPos();
145 }
146 return note;
147 }
148 expandVoice(is.segment(), is.track());
149
150 // insert note
151 Direction stemDirection = Direction::AUTO;
152 int track = is.track();
153 if (is.drumNote() != -1) {
154 nval.pitch = is.drumNote();
155 const Drumset* ds = is.drumset();
156 nval.headGroup = ds->noteHead(nval.pitch);
157 stemDirection = ds->stemDirection(nval.pitch);
158 track = ds->voice(nval.pitch) + (is.track() / VOICES) * VOICES;
159 is.setTrack(track);
160 expandVoice(is.segment(), is.track());
161 }
162 if (!is.cr())
163 return 0;
164 Fraction duration;
165 if (is.usingNoteEntryMethod(NoteEntryMethod::REPITCH)) {
166 duration = is.cr()->ticks();
167 }
168 else if (is.usingNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO) || is.usingNoteEntryMethod(NoteEntryMethod::REALTIME_MANUAL)) {
169 // FIXME: truncate duration at barline in real-time modes.
170 // The user might try to enter a duration that is too long to fit in the remaining space in the measure.
171 // We could split the duration at the barline and continue into the next bar, but this would create extra
172 // notes, extra ties, and extra pain. Instead, we simply truncate the duration at the barline.
173 Fraction ticks2measureEnd = is.segment()->measure()->ticks() - is.segment()->rtick();
174 duration = is.duration() > ticks2measureEnd ? ticks2measureEnd : is.duration().fraction();
175 }
176 else {
177 duration = is.duration().fraction();
178 }
179 Note* note = 0;
180 Note* firstTiedNote = 0;
181 Note* lastTiedNote = 0;
182 if (is.usingNoteEntryMethod(NoteEntryMethod::REPITCH) && is.cr()->isChord()) {
183 // repitch mode for MIDI input (where we are given a pitch) is handled here
184 // for keyboard input (where we are given a staff position), there is a separate function Score::repitchNote()
185 // the code is similar enough that it could possibly be refactored
186 Chord* chord = toChord(is.cr());
187 note = new Note(this);
188 note->setParent(chord);
189 note->setTrack(chord->track());
190 note->setNval(nval);
191 lastTiedNote = note;
192 if (!addFlag) {
193 std::vector<Note*> notes = chord->notes();
194 // break all ties into current chord
195 // these will exist only if user explicitly moved cursor to a tied-into note
196 // in ordinary use, cursor will autoamtically skip past these during note entry
197 for (Note* n : notes) {
198 if (n->tieBack())
199 undoRemoveElement(n->tieBack());
200 }
201 // for single note chords only, preserve ties by changing pitch of all forward notes
202 // the tie forward itself will be added later
203 // multi-note chords get reduced to single note chords anyhow since we remove the old notes below
204 // so there will be no way to preserve those ties
205 if (notes.size() == 1 && notes.front()->tieFor()) {
206 Note* tn = notes.front()->tieFor()->endNote();
207 while (tn) {
208 Chord* tc = tn->chord();
209 if (tc->notes().size() != 1) {
210 undoRemoveElement(tn->tieBack());
211 break;
212 }
213 if (!firstTiedNote)
214 firstTiedNote = tn;
215 lastTiedNote = tn;
216 undoChangePitch(tn, note->pitch(), note->tpc1(), note->tpc2());
217 if (tn->tieFor())
218 tn = tn->tieFor()->endNote();
219 else
220 break;
221 }
222 }
223 // remove all notes from chord
224 // the new note will be added below
225 while (!chord->notes().empty())
226 undoRemoveElement(chord->notes().front());
227 }
228 // add new note to chord
229 undoAddElement(note);
230 setPlayNote(true);
231 // recreate tie forward if there is a note to tie to
232 // one-sided ties will not be recreated
233 if (firstTiedNote) {
234 Tie* tie = new Tie(this);
235 tie->setStartNote(note);
236 tie->setEndNote(firstTiedNote);
237 tie->setTick(tie->startNote()->tick());
238 tie->setTick2(tie->endNote()->tick());
239 tie->setTrack(note->track());
240 undoAddElement(tie);
241 }
242 select(lastTiedNote);
243 }
244 else if (!is.usingNoteEntryMethod(NoteEntryMethod::REPITCH)) {
245 Segment* seg = setNoteRest(is.segment(), track, nval, duration, stemDirection, /* forceAccidental */ false, /* rhythmic */ false, externalInputState);
246 if (seg) {
247 note = toChord(seg->element(track))->upNote();
248 }
249 }
250
251 if (is.slur()) {
252 //
253 // extend slur
254 //
255 ChordRest* e = searchNote(is.tick(), is.track());
256 if (e) {
257 Fraction stick = Fraction(0, 1);
258 Element* ee = is.slur()->startElement();
259 if (ee->isChordRest())
260 stick = toChordRest(ee)->tick();
261 else if (ee->isNote())
262 stick = toNote(ee)->chord()->tick();
263 if (stick == e->tick()) {
264 is.slur()->setTick(stick);
265 is.slur()->setStartElement(e);
266 }
267 else {
268 is.slur()->setTick2(e->tick());
269 is.slur()->setEndElement(e);
270 }
271 }
272 else
273 qDebug("addPitch: cannot find slur note");
274 }
275 if (is.usingNoteEntryMethod(NoteEntryMethod::REPITCH)) {
276 // move cursor to next note, but skip tied notes (they were already repitched above)
277 ChordRest* next = lastTiedNote ? nextChordRest(lastTiedNote->chord()) : nextChordRest(is.cr());
278 while (next && !next->isChord())
279 next = nextChordRest(next);
280 if (next)
281 is.moveInputPos(next->segment());
282 }
283 else {
284 NoteEntryMethod entryMethod = is.noteEntryMethod();
285 if (entryMethod != NoteEntryMethod::REALTIME_AUTO && entryMethod != NoteEntryMethod::REALTIME_MANUAL)
286 is.moveToNextInputPos();
287 }
288 return note;
289 }
290
291 //---------------------------------------------------------
292 // putNote
293 // mouse click in state NoteType::ENTRY
294 //---------------------------------------------------------
295
putNote(const QPointF & pos,bool replace,bool insert)296 void Score::putNote(const QPointF& pos, bool replace, bool insert)
297 {
298 Position p;
299 if (!getPosition(&p, pos, _is.voice())) {
300 qDebug("cannot put note here, get position failed");
301 return;
302 }
303 Score* score = p.segment->score();
304 // it is not safe to call Score::repitchNote() if p is on a TAB staff
305 bool isTablature = staff(p.staffIdx)->isTabStaff(p.segment->tick());
306
307 // calculate actual clicked line from staffType offset and stepOffset
308 Staff* ss = score->staff(p.staffIdx);
309 int stepOffset = ss->staffType(p.segment->tick())->stepOffset();
310 qreal stYOffset = ss->staffType(p.segment->tick())->yoffset().val();
311 qreal lineDist = ss->staffType(p.segment->tick())->lineDistance().val();
312 p.line -= stepOffset + 2 * stYOffset / lineDist;
313
314 if (score->inputState().usingNoteEntryMethod(NoteEntryMethod::REPITCH) && !isTablature)
315 score->repitchNote(p, replace);
316 else {
317 if (insert
318 || score->inputState().usingNoteEntryMethod(NoteEntryMethod::TIMEWISE))
319 score->insertChord(p);
320 else
321 score->putNote(p, replace);
322 }
323 }
324
putNote(const Position & p,bool replace)325 void Score::putNote(const Position& p, bool replace)
326 {
327 Staff* st = staff(p.staffIdx);
328 Segment* s = p.segment;
329
330 _is.setTrack(p.staffIdx * VOICES + _is.voice());
331 _is.setSegment(s);
332
333 if (score()->excerpt() && !score()->excerpt()->tracks().isEmpty() && score()->excerpt()->tracks().key(_is.track(), -1) == -1)
334 return;
335
336 Direction stemDirection = Direction::AUTO;
337 bool error;
338 NoteVal nval = noteValForPosition(p, _is.accidentalType(), error);
339 if (error)
340 return;
341
342 const StringData* stringData = 0;
343
344 // pitched/unpitched note entry depends on instrument (override StaffGroup)
345 StaffGroup staffGroup = st->staffType(s->tick())->group();
346 if (staffGroup != StaffGroup::TAB)
347 staffGroup = st->part()->instrument(s->tick())->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
348
349 switch (staffGroup) {
350 case StaffGroup::PERCUSSION: {
351 const Drumset* ds = st->part()->instrument(s->tick())->drumset();
352 stemDirection = ds->stemDirection(nval.pitch);
353 break;
354 }
355 case StaffGroup::TAB:
356 stringData = st->part()->instrument(s->tick())->stringData();
357 _is.setDrumNote(-1);
358 break;
359 case StaffGroup::STANDARD:
360 _is.setDrumNote(-1);
361 break;
362 }
363
364 expandVoice();
365 ChordRest* cr = _is.cr();
366 bool addToChord = false;
367
368 if (cr) {
369 // retrieve total duration of current chord
370 TDuration d = cr->durationType();
371 // if not in replace mode AND chord duration == input duration AND not rest input
372 // we need to add to current chord (otherwise, we will need to replace it or create a new one)
373 if (!replace
374 && (d == _is.duration())
375 && cr->isChord()
376 && !_is.rest())
377 {
378 if (st->isTabStaff(cr->tick())) { // TAB
379 // if a note on same string already exists, update to new pitch/fret
380 foreach (Note* note, toChord(cr)->notes())
381 if (note->string() == nval.string) { // if string is the same
382 // if adding a new digit will keep fret number within fret limit,
383 // add a digit to existing fret number
384 if (stringData) {
385 int fret = note->fret() * 10 + nval.fret;
386 if (fret <= stringData->frets() ) {
387 nval.fret = fret;
388 nval.pitch = stringData->getPitch(nval.string, nval.fret, st, s->tick());
389 }
390 else
391 qDebug("can't increase fret to %d", fret);
392 }
393 // set fret number (original or combined) in all linked notes
394 int tpc1 = note->tpc1default(nval.pitch);
395 int tpc2 = note->tpc2default(nval.pitch);
396 undoChangeFretting(note, nval.pitch, nval.string, nval.fret, tpc1, tpc2);
397 setPlayNote(true);
398 return;
399 }
400 }
401 else { // not TAB
402 // if a note with the same pitch already exists in the chord, remove it
403 Chord* chord = toChord(cr);
404 Note* note = chord->findNote(nval.pitch);
405 if (note) {
406 if (chord->notes().size() > 1)
407 undoRemoveElement(note);
408 return;
409 }
410 }
411 addToChord = true; // if no special case, add note to chord
412 }
413 }
414 bool forceAccidental = false;
415 if (_is.accidentalType() != AccidentalType::NONE) {
416 NoteVal nval2 = noteValForPosition(p, AccidentalType::NONE, error);
417 forceAccidental = (nval.pitch == nval2.pitch);
418 }
419 if (addToChord && cr->isChord()) {
420 // if adding, add!
421 addNote(toChord(cr), nval, forceAccidental);
422 _is.setAccidentalType(AccidentalType::NONE);
423 return;
424 }
425 else {
426 // if not adding, replace current chord (or create a new one)
427
428 if (_is.rest())
429 nval.pitch = -1;
430 setNoteRest(_is.segment(), _is.track(), nval, _is.duration().fraction(), stemDirection, forceAccidental);
431 _is.setAccidentalType(AccidentalType::NONE);
432 }
433 if (!st->isTabStaff(cr->tick()))
434 _is.moveToNextInputPos();
435 }
436
437 //---------------------------------------------------------
438 // repitchNote
439 //---------------------------------------------------------
440
repitchNote(const Position & p,bool replace)441 void Score::repitchNote(const Position& p, bool replace)
442 {
443 Segment* s = p.segment;
444 Fraction tick = s->tick();
445 Staff* st = staff(p.staffIdx);
446 ClefType clef = st->clef(tick);
447
448 NoteVal nval;
449 bool error = false;
450 AccidentalType at = _is.accidentalType();
451 if (_is.drumset() && _is.drumNote() != -1) {
452 nval.pitch = _is.drumNote();
453 }
454 else {
455 AccidentalVal acci = (at == AccidentalType::NONE ? s->measure()->findAccidental(s, p.staffIdx, p.line, error) : Accidental::subtype2value(at));
456 if (error)
457 return;
458 int step = absStep(p.line, clef);
459 int octave = step / 7;
460 nval.pitch = step2pitch(step) + octave * 12 + int(acci);
461
462 if (styleB(Sid::concertPitch))
463 nval.tpc1 = step2tpc(step % 7, acci);
464 else {
465 nval.pitch += st->part()->instrument(s->tick())->transpose().chromatic;
466 nval.tpc2 = step2tpc(step % 7, acci);
467 }
468 }
469
470 if (!_is.segment())
471 return;
472
473 Chord* chord;
474 ChordRest* cr = _is.cr();
475 if (!cr) {
476 cr = _is.segment()->nextChordRest(_is.track());
477 if (!cr)
478 return;
479 }
480 if (cr->isRest()) { //skip rests
481 ChordRest* next = nextChordRest(cr);
482 while(next && !next->isChord())
483 next = nextChordRest(next);
484 if (next)
485 _is.moveInputPos(next->segment());
486 return;
487 }
488 else {
489 chord = toChord(cr);
490 }
491 Note* note = new Note(this);
492 note->setParent(chord);
493 note->setTrack(chord->track());
494 note->setNval(nval);
495
496 Note* firstTiedNote = 0;
497 Note* lastTiedNote = note;
498 if (replace) {
499 std::vector<Note*> notes = chord->notes();
500 // break all ties into current chord
501 // these will exist only if user explicitly moved cursor to a tied-into note
502 // in ordinary use, cursor will autoamtically skip past these during note entry
503 for (Note* n : notes) {
504 if (n->tieBack())
505 undoRemoveElement(n->tieBack());
506 }
507 // for single note chords only, preserve ties by changing pitch of all forward notes
508 // the tie forward itself will be added later
509 // multi-note chords get reduced to single note chords anyhow since we remove the old notes below
510 // so there will be no way to preserve those ties
511 if (notes.size() == 1 && notes.front()->tieFor()) {
512 Note* tn = notes.front()->tieFor()->endNote();
513 while (tn) {
514 Chord* tc = tn->chord();
515 if (tc->notes().size() != 1) {
516 undoRemoveElement(tn->tieBack());
517 break;
518 }
519 if (!firstTiedNote)
520 firstTiedNote = tn;
521 lastTiedNote = tn;
522 undoChangePitch(tn, note->pitch(), note->tpc1(), note->tpc2());
523 if (tn->tieFor())
524 tn = tn->tieFor()->endNote();
525 else
526 break;
527 }
528 }
529 // remove all notes from chord
530 // the new note will be added below
531 while (!chord->notes().empty())
532 undoRemoveElement(chord->notes().front());
533 }
534 // add new note to chord
535 undoAddElement(note);
536 bool forceAccidental = false;
537 if (_is.accidentalType() != AccidentalType::NONE) {
538 NoteVal nval2 = noteValForPosition(p, AccidentalType::NONE, error);
539 forceAccidental = (nval.pitch == nval2.pitch);
540 }
541 if (forceAccidental) {
542 int tpc = styleB(Sid::concertPitch) ? nval.tpc1 : nval.tpc2;
543 AccidentalVal alter = tpc2alter(tpc);
544 at = Accidental::value2subtype(alter);
545 Accidental* a = new Accidental(this);
546 a->setAccidentalType(at);
547 a->setRole(AccidentalRole::USER);
548 a->setParent(note);
549 undoAddElement(a);
550 }
551 setPlayNote(true);
552 setPlayChord(true);
553 // recreate tie forward if there is a note to tie to
554 // one-sided ties will not be recreated
555 if (firstTiedNote) {
556 Tie* tie = new Tie(this);
557 tie->setStartNote(note);
558 tie->setEndNote(firstTiedNote);
559 tie->setTick(tie->startNote()->tick());
560 tie->setTick2(tie->endNote()->tick());
561 tie->setTrack(note->track());
562 undoAddElement(tie);
563 }
564 select(lastTiedNote);
565 // move to next Chord
566 ChordRest* next = nextChordRest(lastTiedNote->chord());
567 while (next && !next->isChord())
568 next = nextChordRest(next);
569 if (next)
570 _is.moveInputPos(next->segment());
571 }
572
573 //---------------------------------------------------------
574 // insertChord
575 //---------------------------------------------------------
576
insertChord(const Position & pos)577 void Score::insertChord(const Position& pos)
578 {
579 // insert
580 // TODO:
581 // - check voices
582 // - split chord/rest
583
584 Element* el = selection().element();
585 if (!el || !(el->isNote() || el->isRest()))
586 return;
587 Segment* seg = pos.segment;
588 if (seg->splitsTuplet()) {
589 MScore::setError(CANNOT_INSERT_TUPLET);
590 return;
591 }
592 if (_is.insertMode())
593 globalInsertChord(pos);
594 else
595 localInsertChord(pos);
596 }
597
598 //---------------------------------------------------------
599 // localInsertChord
600 //---------------------------------------------------------
601
localInsertChord(const Position & pos)602 void Score::localInsertChord(const Position& pos)
603 {
604 const TDuration duration = _is.duration();
605 const Fraction fraction = duration.fraction();
606 const Fraction len = fraction;
607 Segment* seg = pos.segment;
608 Fraction tick = seg->tick();
609 Measure* measure = seg->measure()->isMMRest() ? seg->measure()->mmRestFirst() : seg->measure();
610 const Fraction targetMeasureLen = measure->ticks() + fraction;
611
612 // Shift spanners, enlarge the measure.
613 // The approach is similar to that in Measure::adjustToLen() but does
614 // insert time to the middle of the measure rather than to the end.
615 undoInsertTime(tick, len);
616 undo(new InsertTime(this, tick, len));
617
618 for (Score* score : scoreList()) {
619 Measure* m = score->tick2measure(tick);
620 undo(new ChangeMeasureLen(m, targetMeasureLen));
621 Segment* scoreSeg = m->tick2segment(tick);
622 for (Segment* s = scoreSeg; s; s = s->next())
623 s->undoChangeProperty(Pid::TICK, s->rtick() + len);
624 }
625
626 // Fill the inserted time with rests.
627 // This is better to be done in master score to cover all staves.
628 MasterScore* ms = masterScore();
629 Measure* msMeasure = ms->tick2measure(tick);
630 const int msTracks = ms->ntracks();
631
632 Segment* firstSeg = msMeasure->first(SegmentType::ChordRest);
633 for (int track = 0; track < msTracks; ++track) {
634 Element* maybeRest = firstSeg->element(track);
635 bool measureIsFull = false;
636
637 // I. Convert any measure rests into normal (non-measure) rest(s) of equivalent duration
638 if (maybeRest && maybeRest->isRest() && toRest(maybeRest)->durationType().isMeasure()) {
639 ms->undoRemoveElement(maybeRest);
640 Rest* measureRest = toRest(maybeRest);
641 // If measure rest is situated at measure start we will fill
642 // the whole measure with rests.
643 measureIsFull = measureRest->rtick().isZero();
644 const Fraction fillLen = measureIsFull ? targetMeasureLen : measureRest->ticks();
645 ms->setRest(measureRest->tick(), track, fillLen, /* useDots */ false, /* tuplet */ nullptr, /* useFullMeasureRest */ false);
646 }
647
648 // II. Make chord or rest in other track longer if it crosses the insert area
649 if (!measureIsFull) {
650 ChordRest* cr = ms->findCR(tick, track);
651 if (cr && cr->tick() < tick && (cr->tick() + cr->actualTicks()) > tick) {
652 if (cr->isRest()) {
653 const Fraction fillLen = cr->ticks() + fraction;
654 ms->undoRemoveElement(cr);
655 ms->setRest(cr->tick(), track, fillLen, /* useDots */ false, /* tuplet */ nullptr, /* useFullMeasureRest */ false);
656 }
657 else if (cr->isChord()) {
658 Chord* chord = toChord(cr);
659 std::vector<TDuration> durations = toDurationList(chord->ticks() + fraction, /* useDots */ true);
660 Fraction p = chord->tick();
661 ms->undoRemoveElement(chord);
662 Chord* prevChord = nullptr;
663 for (const TDuration& dur : durations) {
664 Chord* prototype = prevChord ? prevChord : chord;
665 const bool genTie = bool(prevChord);
666 prevChord = ms->addChord(p, dur, prototype, genTie, /* tuplet */ nullptr);
667 p += dur.fraction();
668 }
669 // TODO: reconnect ties if this chord was tied to other
670 }
671 measureIsFull = true;
672 }
673 }
674
675 // III. insert rest(s) to fill the inserted space
676 if (!measureIsFull && msMeasure->hasVoice(track))
677 ms->setRest(tick, track, fraction, /* useDots */ false, /* tuplet */ nullptr);
678 }
679
680 // Put the note itself.
681 Segment* s = measure->undoGetSegment(SegmentType::ChordRest, tick);
682 Position p(pos);
683 p.segment = s;
684 putNote(p, true);
685 }
686
687 //---------------------------------------------------------
688 // globalInsertChord
689 //---------------------------------------------------------
690
globalInsertChord(const Position & pos)691 void Score::globalInsertChord(const Position& pos)
692 {
693 ChordRest* cr = selection().cr();
694 int track = cr ? cr->track() : -1;
695 deselectAll();
696 Segment* s1 = pos.segment;
697 Segment* s2 = lastSegment();
698 TDuration duration = _is.duration();
699 Fraction fraction = duration.fraction();
700 ScoreRange r;
701
702 r.read(s1, s2, false);
703
704 int strack = 0; // for now for all tracks
705 int etrack = nstaves() * VOICES;
706 Fraction stick = s1->tick();
707 Fraction etick = s2->tick();
708 Fraction ticks = fraction;
709 Fraction len = r.ticks();
710
711 if (!r.truncate(fraction))
712 appendMeasures(1);
713
714 putNote(pos, true);
715 Fraction dtick = s1->tick() + ticks;
716 int voiceOffsets[VOICES] { 0, 0, 0, 0 };
717 len = r.ticks();
718 for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx)
719 makeGap1(dtick, staffIdx, r.ticks(), voiceOffsets);
720 r.write(this, dtick);
721
722 for (auto i : spanner()) {
723 Spanner* s = i.second;
724 if (s->track() >= strack && s->track() < etrack) {
725 if (s->tick() >= stick && s->tick() < etick)
726 s->undoChangeProperty(Pid::SPANNER_TICK, s->tick() + ticks);
727 else if (s->tick2() >= stick && s->tick2() < etick)
728 s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + ticks);
729 }
730 }
731
732 if (track != -1) {
733 Measure* m = tick2measure(dtick);
734 Segment* s = m->findSegment(SegmentType::ChordRest, dtick);
735 Element* e = s->element(track);
736 if (e)
737 select(e->isChord() ? toChord(e)->notes().front() : e);
738 }
739 }
740
741
742 } // namespace Ms
743
744