Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include <climits>
24 :
25 : #include "anchors.h"
26 : #include "factory.h"
27 : #include "figuredbass.h"
28 : #include "fret.h"
29 : #include "harmony.h"
30 : #include "measure.h"
31 : #include "page.h"
32 : #include "score.h"
33 : #include "spanner.h"
34 : #include "staff.h"
35 : #include "system.h"
36 : #include "textlinebase.h"
37 : #include "timesig.h"
38 : #include "utils.h"
39 :
40 : #include "rendering/score/measurelayout.h"
41 :
42 : using namespace mu::engraving::rendering::score;
43 :
44 : namespace mu::engraving {
45 : /***************************************
46 : * EditTimeTickAnchors
47 : * ************************************/
48 :
49 0 : void EditTimeTickAnchors::updateAnchors(const EngravingItem* item)
50 : {
51 0 : if (!item->allowTimeAnchor()) {
52 0 : item->score()->hideAnchors();
53 0 : return;
54 : }
55 :
56 0 : Fraction startTickMainRegion = item->isSpannerSegment() ? toSpannerSegment(item)->spanner()->tick() : item->tick();
57 0 : Fraction endTickMainRegion = item->isSpannerSegment() ? toSpannerSegment(item)->spanner()->tick2() : item->tick();
58 :
59 0 : Score* score = item->score();
60 0 : Measure* startMeasure = score->tick2measure(startTickMainRegion);
61 0 : Measure* endMeasure = score->tick2measure(endTickMainRegion);
62 0 : if (!startMeasure || !endMeasure) {
63 0 : return;
64 : }
65 :
66 0 : staff_idx_t staff = item->staffIdx();
67 0 : Measure* startOneBefore = startMeasure->prevMeasure();
68 0 : for (MeasureBase* mb = startOneBefore ? startOneBefore : startMeasure; mb && mb->tick() <= endMeasure->tick(); mb = mb->next()) {
69 0 : if (!mb->isMeasure()) {
70 0 : continue;
71 : }
72 0 : updateAnchors(toMeasure(mb), staff);
73 : }
74 :
75 0 : Fraction startTickExtendedRegion = startMeasure->tick();
76 0 : Fraction endTickExtendedRegion = endMeasure->endTick();
77 0 : voice_idx_t voiceIdx = item->hasVoiceAssignmentProperties() && item->getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>()
78 0 : != VoiceAssignment::CURRENT_VOICE_ONLY ? VOICES : item->voice();
79 :
80 0 : score->setShowAnchors(ShowAnchors(voiceIdx, staff, startTickMainRegion, endTickMainRegion, startTickExtendedRegion,
81 0 : endTickExtendedRegion));
82 :
83 0 : item->triggerLayout();
84 : }
85 :
86 0 : void EditTimeTickAnchors::updateAnchors(Measure* measure, staff_idx_t staffIdx, const std::set<Fraction>& additionalAnchorRelTicks)
87 : {
88 0 : Fraction startTick = Fraction(0, 1);
89 0 : Fraction endTick = measure->ticks();
90 :
91 0 : TimeSig* timeSig = measure->score()->staff(staffIdx)->timeSig(measure->tick());
92 0 : Fraction timeSigFrac = timeSig ? timeSig->sig() : measure->timesig();
93 0 : Fraction halfDivision = Fraction(1, 2 * timeSigFrac.denominator());
94 0 : if (timeSig) {
95 0 : halfDivision /= timeSig->stretch();
96 : }
97 :
98 0 : std::set<Fraction> anchorTicks { additionalAnchorRelTicks };
99 0 : for (Fraction tick = startTick; tick <= endTick; tick += halfDivision) {
100 0 : anchorTicks.insert(tick);
101 : }
102 0 : for (Segment* seg = measure->first(SegmentType::Duration); seg;
103 0 : seg = seg->next(SegmentType::Duration)) {
104 0 : anchorTicks.insert(seg->rtick());
105 : }
106 :
107 0 : for (const Fraction& anchorTick : anchorTicks) {
108 0 : createTimeTickAnchor(measure, anchorTick, staffIdx);
109 : }
110 :
111 0 : updateLayout(measure);
112 0 : }
113 :
114 204 : TimeTickAnchor* EditTimeTickAnchors::createTimeTickAnchor(Measure* measure, Fraction relTick, staff_idx_t staffIdx)
115 : {
116 204 : TimeTickAnchor* returnAnchor = nullptr;
117 :
118 204 : Staff* staff = measure->score()->staff(staffIdx);
119 204 : IF_ASSERT_FAILED(staff) {
120 0 : return nullptr;
121 : }
122 :
123 419 : for (EngravingObject* linkedObj : staff->linkList()) {
124 215 : IF_ASSERT_FAILED(linkedObj) {
125 0 : continue;
126 : }
127 :
128 215 : Staff* linkedStaff = toStaff(linkedObj);
129 215 : Measure* linkedMeasure = linkedObj == staff ? measure : linkedStaff->score()->tick2measureMM(measure->tick());
130 215 : if (!linkedMeasure) {
131 0 : continue;
132 : }
133 :
134 215 : Segment* segment = linkedMeasure->getSegmentR(SegmentType::TimeTick, relTick);
135 215 : track_idx_t track = staff2track(linkedStaff->idx());
136 215 : EngravingItem* element = segment->element(track);
137 215 : TimeTickAnchor* anchor = element ? toTimeTickAnchor(element) : nullptr;
138 215 : if (!anchor) {
139 213 : anchor = Factory::createTimeTickAnchor(segment);
140 213 : anchor->setTrack(track);
141 213 : segment->add(anchor);
142 : }
143 215 : if (linkedMeasure == measure) {
144 212 : returnAnchor = anchor;
145 : }
146 204 : }
147 :
148 204 : return returnAnchor;
149 : }
150 :
151 164 : void EditTimeTickAnchors::updateLayout(Measure* measure)
152 : {
153 164 : measure->computeTicks();
154 :
155 164 : Score* score = measure->score();
156 164 : LayoutContext ctx(score);
157 164 : MeasureLayout::layoutTimeTickAnchors(measure, ctx);
158 164 : }
159 :
160 0 : void MoveElementAnchors::moveElementAnchors(EngravingItem* element, KeyboardKey key, KeyboardModifier mod)
161 : {
162 0 : EngravingObject* owner = element->ownershipParent();
163 0 : Segment* segment = owner && owner->isSegment() ? toSegment(owner) : nullptr;
164 0 : if (!segment) {
165 0 : return;
166 : }
167 :
168 0 : bool altMod = mod & AltModifier;
169 :
170 0 : bool anchorToEndOfPrevious = element->getProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS).toBool();
171 0 : bool changeAnchorType = altMod && canAnchorToEndOfPrevious(element);
172 0 : bool resetAnchorType = !altMod && anchorToEndOfPrevious;
173 0 : if (changeAnchorType || resetAnchorType) {
174 0 : element->undoChangeProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS, !anchorToEndOfPrevious,
175 0 : element->propertyFlags(Pid::ANCHOR_TO_END_OF_PREVIOUS));
176 :
177 0 : bool needMoveSeg = (key == Key_Left && anchorToEndOfPrevious) || (key == Key_Right && !anchorToEndOfPrevious);
178 0 : if (!needMoveSeg) {
179 0 : checkMeasureBoundariesAndMoveIfNeed(element);
180 0 : return;
181 : }
182 : }
183 :
184 0 : moveSegment(element, /*forward*/ key == Key_Right);
185 0 : EditTimeTickAnchors::updateAnchors(element);
186 0 : checkMeasureBoundariesAndMoveIfNeed(element);
187 : }
188 :
189 0 : bool MoveElementAnchors::canAnchorToEndOfPrevious(const EngravingItem* element)
190 : {
191 0 : switch (element->type()) {
192 0 : case ElementType::HARMONY:
193 : case ElementType::FRET_DIAGRAM:
194 : case ElementType::REHEARSAL_MARK:
195 0 : return false;
196 0 : default:
197 0 : return true;
198 : }
199 : }
200 :
201 296 : void MoveElementAnchors::checkMeasureBoundariesAndMoveIfNeed(EngravingItem* element)
202 : {
203 296 : Segment* curSeg = toSegment(element->parent());
204 296 : Fraction curTick = curSeg->tick();
205 296 : Measure* curMeasure = curSeg->measure();
206 296 : Measure* prevMeasure = curMeasure->prevMeasure();
207 296 : bool anchorToEndOfPrevious = element->getProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS).toBool();
208 296 : bool needMoveToNext = curTick == curMeasure->endTick() && !anchorToEndOfPrevious;
209 296 : bool needMoveToPrevious = curSeg->rtick().isZero() && anchorToEndOfPrevious && prevMeasure;
210 :
211 296 : if (!needMoveToPrevious && !needMoveToNext) {
212 294 : return;
213 : }
214 :
215 2 : Segment* newSeg = nullptr;
216 2 : if (needMoveToPrevious) {
217 0 : newSeg = prevMeasure->findSegment(SegmentType::TimeTick, curSeg->tick());
218 0 : if (!newSeg) {
219 0 : TimeTickAnchor* anchor = EditTimeTickAnchors::createTimeTickAnchor(prevMeasure, curTick - prevMeasure->tick(),
220 : element->staffIdx());
221 0 : EditTimeTickAnchors::updateLayout(prevMeasure);
222 0 : newSeg = anchor->segment();
223 : }
224 : } else {
225 2 : newSeg = curSeg->next1(SegmentType::ChordRest);
226 : }
227 :
228 2 : if (newSeg && newSeg->tick() == curTick) {
229 0 : moveSegment(element, newSeg, Fraction(0, 1));
230 : }
231 : }
232 :
233 0 : void MoveElementAnchors::moveElementAnchorsOnDrag(EngravingItem* element, EditData& ed)
234 : {
235 0 : EngravingObject* owner = element->ownershipParent();
236 0 : Segment* segment = owner && owner->isSegment() ? toSegment(owner) : nullptr;
237 0 : if (!segment) {
238 0 : return;
239 : }
240 :
241 0 : KeyboardModifiers km = ed.modifiers;
242 0 : if (km & (ShiftModifier | ControlModifier)) {
243 0 : return;
244 : }
245 :
246 0 : EditTimeTickAnchors::updateAnchors(element);
247 :
248 0 : Segment* newSeg = findNewAnchorableSegmentFromDrag(element, segment);
249 :
250 0 : if (newSeg && (newSeg != segment && !newSeg->measure()->isMMRest())) {
251 0 : PointF curOffset = element->offset();
252 0 : moveSegment(element, newSeg, newSeg->tick() - segment->tick());
253 0 : rebaseOffsetOnMoveSegment(element, curOffset, newSeg, segment);
254 : }
255 : }
256 :
257 0 : Segment* MoveElementAnchors::findNewAnchorableSegmentFromDrag(EngravingItem* element, Segment* curSeg)
258 : {
259 0 : const System* system = curSeg->system();
260 0 : if (!system) {
261 0 : return nullptr;
262 : }
263 :
264 0 : const Measure* newMeasure = nullptr;
265 0 : double xRef = element->canvasX() - system->canvasX();
266 0 : for (MeasureBase* mb : system->measures()) {
267 0 : if (!mb->isMeasure()) {
268 0 : continue;
269 : }
270 0 : if (mb->x() + mb->width() > xRef) {
271 0 : newMeasure = toMeasure(mb);
272 0 : break;
273 : }
274 : }
275 0 : if (!newMeasure) {
276 0 : return nullptr;
277 : }
278 :
279 0 : Segment* newSeg = nullptr;
280 0 : dragPositionToSegment(element->canvasPos(), newMeasure, element->staffIdx(), &newSeg, 0.5, true);
281 :
282 0 : return newSeg;
283 : }
284 :
285 0 : Segment* MoveElementAnchors::findNewAnchorSegmentForLine(LineSegment* lineSegment, const EditData& ed, const Segment* curSeg)
286 : {
287 0 : SLine* line = lineSegment->line();
288 0 : Score* score = lineSegment->score();
289 0 : if (!line->allowTimeAnchor()) {
290 0 : if (ed.key == Key_Left) {
291 0 : return curSeg->prev1WithElemsOnStaff(lineSegment->staffIdx());
292 : }
293 0 : if (ed.key == Key_Right) {
294 0 : Segment* lastCRSegInScore = score->lastSegment();
295 0 : while (lastCRSegInScore && !lastCRSegInScore->isChordRestType()) {
296 0 : lastCRSegInScore = lastCRSegInScore->prev1(SegmentType::ChordRest);
297 : }
298 0 : if (curSeg == lastCRSegInScore && curSeg == line->endSegment()) {
299 : // If we reach this point, it means that the line in question does not accept time anchors
300 : // and that the line's end segment is the last CR segment in the score. Trying to use
301 : // next1WithElemsOnStaff won't do anything from here, but the last segment in the score is
302 : // still a valid new anchor segment for this line (see also LineSegment::edit)...
303 0 : return score->lastSegment();
304 : }
305 0 : return curSeg->next1WithElemsOnStaff(lineSegment->staffIdx());
306 : }
307 : }
308 :
309 0 : switch (ed.key) {
310 0 : case Key_Left: {
311 0 : if (ed.modifiers & ControlModifier) {
312 0 : Measure* m = curSeg->rtick().isZero() ? curSeg->measure()->prevMeasure() : curSeg->measure();
313 0 : return m ? m->findFirstR(SegmentType::ChordRest, Fraction(0, 1)) : nullptr;
314 : }
315 0 : return findNewAnchorableSegment(curSeg, /*forward*/ false);
316 : }
317 0 : case Key_Right: {
318 0 : if (ed.modifiers & ControlModifier) {
319 0 : if (Measure* measure = curSeg->measure()->nextMeasure()) {
320 0 : return measure->findFirstR(SegmentType::ChordRest, Fraction(0, 1));
321 : } else {
322 0 : Measure* m = score->lastMeasure();
323 0 : EditTimeTickAnchors::updateAnchors(m, lineSegment->staffIdx());
324 0 : return m->getChordRestOrTimeTickSegment(m->endTick());
325 : }
326 : }
327 0 : return findNewAnchorableSegment(curSeg, /*forward*/ true);
328 : }
329 0 : case Key_Home: {
330 0 : Measure* m = curSeg->measure()->system()->firstMeasure();
331 0 : if (curSeg->rtick() == Fraction(0, 1) && m == curSeg->measure() && m->prevMeasure()) {
332 0 : m = m->prevMeasure()->system()->firstMeasure();
333 : }
334 0 : return m->findFirstR(SegmentType::ChordRest, Fraction(0, 1));
335 : }
336 0 : case Key_End: {
337 0 : Measure* measure = curSeg->measure()->system()->lastMeasure()->nextMeasure();
338 0 : if (measure) {
339 0 : return measure->findFirstR(SegmentType::ChordRest, Fraction(0, 1));
340 : } else {
341 0 : Measure* m = score->lastMeasure();
342 0 : EditTimeTickAnchors::updateAnchors(m, lineSegment->staffIdx());
343 0 : return m->getChordRestOrTimeTickSegment(m->endTick());
344 : }
345 : }
346 : }
347 :
348 0 : return nullptr;
349 : }
350 :
351 0 : void MoveElementAnchors::moveSegment(EngravingItem* element, bool forward)
352 : {
353 0 : Segment* curSeg = toSegment(element->ownershipParent());
354 0 : Segment* newSeg = getNewSegment(element, curSeg, forward);
355 :
356 0 : if (newSeg) {
357 0 : moveSegment(element, newSeg, newSeg->tick() - curSeg->tick());
358 : }
359 0 : }
360 :
361 0 : Segment* MoveElementAnchors::getNewSegment(EngravingItem* element, Segment* curSeg, bool forward)
362 : {
363 0 : switch (element->type()) {
364 0 : case ElementType::REHEARSAL_MARK:
365 : {
366 0 : Measure* measure = curSeg->measure();
367 0 : Measure* newMeasure = forward ? measure->nextMeasureMM() : measure->prevMeasureMM();
368 0 : return newMeasure ? newMeasure->first(SegmentType::ChordRest) : nullptr;
369 : }
370 0 : default:
371 0 : return findNewAnchorableSegment(curSeg, forward);
372 : }
373 : }
374 :
375 0 : Segment* MoveElementAnchors::findNewAnchorableSegment(const Segment* curSeg, bool forward)
376 : {
377 0 : Segment* newSeg = forward ? curSeg->next1(SegmentType::Duration) : curSeg->prev1(SegmentType::Duration);
378 :
379 : // Continue until we get to a different tick than where we are
380 0 : while (newSeg && newSeg->tick() == curSeg->tick()) {
381 0 : newSeg = forward ? newSeg->next1(SegmentType::Duration) : newSeg->prev1(SegmentType::Duration);
382 : }
383 0 : if (!newSeg) {
384 0 : return nullptr;
385 : }
386 :
387 : // Always prefer ChordRest segment if one exists at the same tick
388 0 : if (newSeg->segmentType() != SegmentType::ChordRest) {
389 0 : Segment* newChordRestSeg = forward ? newSeg->next1(SegmentType::ChordRest) : newSeg->prev1(SegmentType::ChordRest);
390 0 : if (newChordRestSeg && newChordRestSeg->tick() == newSeg->tick()) {
391 0 : newSeg = newChordRestSeg;
392 : }
393 : }
394 :
395 0 : return newSeg;
396 : }
397 :
398 0 : void MoveElementAnchors::moveSegment(EngravingItem* element, Segment* newSeg, Fraction tickDiff)
399 : {
400 0 : switch (element->type()) {
401 0 : case ElementType::FIGURED_BASS:
402 0 : doMoveSegment(toFiguredBass(element), newSeg, tickDiff);
403 0 : break;
404 0 : case ElementType::HARMONY:
405 : case ElementType::FRET_DIAGRAM:
406 0 : doMoveHarmonyOrFretDiagramSegment(element, newSeg, tickDiff);
407 0 : break;
408 0 : default:
409 0 : doMoveSegment(toEngravingItem(element), newSeg, tickDiff);
410 0 : break;
411 : }
412 0 : }
413 :
414 0 : void MoveElementAnchors::doMoveSegment(EngravingItem* element, Segment* newSeg, Fraction tickDiff)
415 : {
416 0 : IF_ASSERT_FAILED(element->parent() && element->parent()->isSegment()) {
417 0 : return;
418 : }
419 :
420 : // NOTE: when creating mmRests, we clone text elements from the underlying measure onto the
421 : // mmRest measure. This creates lots of additional linked copies which are hard to manage
422 : // and can result in duplicates. Here we need to remove copies on mmRests because they are invalidated
423 : // when moving segments (and if needed will be recreated at the next layout). In future we need to
424 : // change approach and *move* elements onto the mmRests, not clone them. [M.S.]
425 :
426 0 : Score* score = element->score();
427 0 : Measure* parentMeasure = toMeasure(element->findAncestor(ElementType::MEASURE));
428 0 : bool parentMeasureIsMMRest = parentMeasure && parentMeasure->isMMRest();
429 :
430 0 : std::list<EngravingObject*> linkedElements = element->linkList();
431 0 : for (EngravingObject* linkedElement : linkedElements) {
432 0 : if (linkedElement == element) {
433 0 : continue;
434 : }
435 0 : Measure* linkedParentMeasure = toMeasure(toEngravingItem(linkedElement)->findAncestor(ElementType::MEASURE));
436 0 : bool linkedParentMeasureIsMMrest = linkedParentMeasure && linkedParentMeasure->isMMRest();
437 0 : if (linkedParentMeasureIsMMrest != parentMeasureIsMMRest) {
438 0 : for (EngravingObject* child : linkedElement->children()) {
439 : // e.g. fret diagram should also remove the harmony
440 0 : child->undoUnlink();
441 0 : score->undoRemoveElement(toEngravingItem(child));
442 : }
443 0 : linkedElement->undoUnlink();
444 0 : score->undoRemoveElement(static_cast<EngravingItem*>(linkedElement));
445 : }
446 : }
447 :
448 0 : score->undoChangeParent(element, newSeg, element->staffIdx());
449 0 : moveSnappedItems(element, newSeg, tickDiff);
450 0 : }
451 :
452 0 : void MoveElementAnchors::doMoveSegment(FiguredBass* element, Segment* newSeg, Fraction tickDiff)
453 : {
454 0 : IF_ASSERT_FAILED(element->parent() && element->parent()->isSegment()) {
455 0 : return;
456 : }
457 :
458 0 : Segment* oldSeg = element->segment();
459 :
460 0 : doMoveSegment(toEngravingItem(element), newSeg, tickDiff);
461 :
462 0 : track_idx_t startTrack = staff2track(element->staffIdx());
463 0 : track_idx_t endTrack = startTrack + VOICES;
464 :
465 : // Shorten this if needed
466 0 : if (newSeg->tick() > oldSeg->tick()) {
467 0 : FiguredBass* nextFB = nullptr;
468 0 : Fraction endTick = newSeg->tick() + element->ticks();
469 0 : for (Segment* seg = newSeg->next1(SegmentType::Duration); seg && seg->tick() <= endTick;
470 0 : seg = seg->next1(SegmentType::Duration)) {
471 0 : nextFB = toFiguredBass(seg->findAnnotation(ElementType::FIGURED_BASS, startTrack, endTrack));
472 0 : if (nextFB) {
473 0 : break;
474 : }
475 : }
476 0 : if (nextFB) {
477 0 : element->setTicks(nextFB->tick() - newSeg->tick());
478 : }
479 : }
480 :
481 : // Shorten previous if needed
482 0 : if (newSeg->tick() < oldSeg->tick()) {
483 0 : FiguredBass* prevFB = nullptr;
484 0 : for (Segment* seg = newSeg->prev1(SegmentType::Duration); seg && seg->measure()->isAfterOrEqual(newSeg->measure());
485 0 : seg = seg->prev1(SegmentType::Duration)) {
486 0 : prevFB = (FiguredBass*)(seg->findAnnotation(ElementType::FIGURED_BASS, startTrack, endTrack));
487 0 : if (prevFB) {
488 0 : break;
489 : }
490 : }
491 0 : if (prevFB) {
492 0 : prevFB->setTicks(std::min(prevFB->ticks(), newSeg->tick() - prevFB->tick()));
493 : }
494 : }
495 : }
496 :
497 0 : void MoveElementAnchors::doMoveHarmonyOrFretDiagramSegment(EngravingItem* element, Segment* newSeg, Fraction tickDiff)
498 : {
499 0 : for (EngravingObject* item : element->linkList()) {
500 0 : Score* score = item->score();
501 0 : Measure* measure = score->tick2measure(newSeg->tick());
502 0 : Segment* chordRestSeg = measure->undoGetSegment(SegmentType::ChordRest, newSeg->tick());
503 0 : if (item == element) {
504 0 : newSeg = chordRestSeg;
505 : }
506 0 : }
507 :
508 0 : Fraction oldTick = element->tick();
509 :
510 0 : doMoveSegment(toEngravingItem(element), newSeg, tickDiff);
511 :
512 0 : for (EngravingObject* item : element->linkList()) {
513 0 : Score* score = item->score();
514 0 : Segment* oldSegment = score->tick2segment(oldTick, true, SegmentType::ChordRest, false);
515 0 : if (oldSegment) {
516 0 : oldSegment->checkEmpty();
517 0 : if (oldSegment->empty()) {
518 0 : score->undoRemoveElement(oldSegment);
519 : }
520 : }
521 0 : }
522 0 : }
523 :
524 0 : void MoveElementAnchors::moveSnappedItems(EngravingItem* element, Segment* newSeg, Fraction tickDiff)
525 : {
526 0 : if (EngravingItem* itemAfter = element->mutldata()->itemSnappedAfter()) {
527 0 : if (itemAfter->isTextBase() && itemAfter->parent() != newSeg) {
528 0 : element->score()->undoChangeParent(itemAfter, newSeg, itemAfter->staffIdx());
529 0 : moveSnappedItems(itemAfter, newSeg, tickDiff);
530 0 : } else if (itemAfter->isTextLineBaseSegment()) {
531 0 : TextLineBase* textLine = ((TextLineBaseSegment*)itemAfter)->textLineBase();
532 0 : if (textLine->tick() != newSeg->tick()) {
533 0 : textLine->undoMoveStart(tickDiff);
534 : }
535 : }
536 : }
537 :
538 0 : if (EngravingItem* itemBefore = element->mutldata()->itemSnappedBefore()) {
539 0 : if (itemBefore->isTextBase() && itemBefore->parent() != newSeg) {
540 0 : element->score()->undoChangeParent(itemBefore, newSeg, itemBefore->staffIdx());
541 0 : moveSnappedItems(itemBefore, newSeg, tickDiff);
542 0 : } else if (itemBefore->isTextLineBaseSegment()) {
543 0 : TextLineBase* textLine = ((TextLineBaseSegment*)itemBefore)->textLineBase();
544 0 : if (textLine->tick2() != newSeg->tick()) {
545 0 : textLine->undoMoveEnd(tickDiff);
546 : }
547 : }
548 : }
549 0 : }
550 :
551 0 : void MoveElementAnchors::rebaseOffsetOnMoveSegment(EngravingItem* element, const PointF& curOffset, Segment* newSeg, Segment* oldSeg)
552 : {
553 0 : PointF offsetShift = newSeg->pagePos() - oldSeg->pagePos();
554 0 : element->setOffset(curOffset - offsetShift);
555 :
556 0 : if (EngravingItem* itemBefore = element->mutldata()->itemSnappedBefore()) {
557 0 : if (itemBefore->isTextBase()) {
558 0 : itemBefore->setOffset(itemBefore->offset() - offsetShift);
559 : }
560 : }
561 :
562 0 : if (EngravingItem* itemAfter = element->mutldata()->itemSnappedAfter()) {
563 0 : if (itemAfter->isTextBase()) {
564 0 : itemAfter->setOffset(itemAfter->offset() - offsetShift);
565 : }
566 : }
567 0 : }
568 :
569 : /********************************************
570 : * TimeTickAnchor
571 : * *****************************************/
572 :
573 213 : TimeTickAnchor::TimeTickAnchor(DummyParentOr<Segment> parent)
574 0 : : EngravingItem(ElementType::TIME_TICK_ANCHOR, parent,
575 : ElementFlag::ON_STAFF
576 0 : | ElementFlag::NOT_SELECTABLE
577 213 : | ElementFlag::GENERATED)
578 : {
579 213 : setZ(-INT_MAX); // Make sure it is behind everything
580 213 : }
581 :
582 0 : TimeTickAnchor::DrawRegion TimeTickAnchor::drawRegion() const
583 : {
584 0 : const ShowAnchors& showAnchors = score()->showAnchors();
585 0 : const staff_idx_t thisStaffIdx = staffIdx();
586 0 : const Fraction thisTick = segment()->tick();
587 :
588 0 : const bool trackOutOfRange = thisStaffIdx < showAnchors.staffIdx || thisStaffIdx >= showAnchors.endStaffIdx;
589 0 : const bool tickOutOfRange = thisTick < showAnchors.startTickExtendedRegion || thisTick >= showAnchors.endTickExtendedRegion;
590 0 : if (trackOutOfRange || tickOutOfRange) {
591 0 : return DrawRegion::OUT_OF_RANGE;
592 : }
593 :
594 0 : if (thisTick < showAnchors.startTickMainRegion || thisTick >= showAnchors.endTickMainRegion) {
595 0 : return DrawRegion::EXTENDED_REGION;
596 : }
597 :
598 0 : return DrawRegion::MAIN_REGION;
599 : }
600 :
601 0 : voice_idx_t TimeTickAnchor::voiceIdx() const
602 : {
603 0 : return score()->showAnchors().voiceIdx;
604 : }
605 : } // namespace mu::engraving
|