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 "timeline.h"
24 :
25 : #include <QApplication>
26 : #include <QGraphicsTextItem>
27 : #include <QMenu>
28 : #include <QMouseEvent>
29 : #include <QScrollBar>
30 : #include <QTextDocument>
31 :
32 : #include "translation.h"
33 :
34 : #include "engraving/dom/barline.h"
35 : #include "engraving/dom/jump.h"
36 : #include "engraving/dom/key.h"
37 : #include "engraving/dom/keysig.h"
38 : #include "engraving/dom/marker.h"
39 : #include "engraving/dom/measure.h"
40 : #include "engraving/dom/measurebase.h"
41 : #include "engraving/dom/mscore.h"
42 : #include "engraving/dom/page.h"
43 : #include "engraving/dom/part.h"
44 : #include "engraving/dom/rehearsalmark.h"
45 : #include "engraving/dom/score.h"
46 : #include "engraving/dom/staff.h"
47 : #include "engraving/dom/system.h"
48 : #include "engraving/dom/tempotext.h"
49 : #include "engraving/dom/timesig.h"
50 : #include "engraving/types/typesconv.h"
51 :
52 : #include "notation/inotationelements.h" // IWYU pragma: keep
53 : #include "notation/inotationinteraction.h"
54 : #include "notation/inotationselection.h"
55 : #include "notation/inotationundostack.h" // IWYU pragma: keep
56 :
57 : #include "instrumentsscene/instrumentscommands.h"
58 :
59 : #include "log.h"
60 :
61 : using namespace mu::notation;
62 : using namespace mu::engraving;
63 : using namespace mu::instrumentsscene;
64 :
65 : using namespace Qt::StringLiterals;
66 :
67 : //---------------------------------------------------------
68 : // TRowLabels
69 : //---------------------------------------------------------
70 :
71 0 : TRowLabels::TRowLabels(QSplitter* splitter, Timeline* time)
72 0 : : QGraphicsView(splitter)
73 : {
74 0 : TRACEFUNC;
75 :
76 0 : setFocusPolicy(Qt::NoFocus);
77 0 : setObjectName("TRowLabels");
78 :
79 0 : _splitter = splitter;
80 0 : _timeline = time;
81 0 : setScene(new QGraphicsScene);
82 0 : scene()->setBackgroundBrush(time->activeTheme().backgroundColor);
83 0 : setSceneRect(0, 0, 50, time->height());
84 :
85 0 : setMinimumWidth(0);
86 :
87 0 : QSplitter* split = _splitter;
88 0 : QList<int> sizes;
89 : // TODO: Replace 70 with hard coded value
90 0 : sizes << 70 << 10000;
91 0 : split->setSizes(sizes);
92 :
93 0 : setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
94 0 : setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
95 0 : setContentsMargins(0, 0, 0, 0);
96 0 : setAlignment(Qt::Alignment((Qt::AlignLeft | Qt::AlignTop)));
97 :
98 0 : connect(verticalScrollBar(), &QScrollBar::valueChanged, time->verticalScrollBar(), &QScrollBar::setValue);
99 0 : connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &TRowLabels::restrictScroll);
100 0 : connect(this, &TRowLabels::moved, time, &Timeline::mouseOver);
101 :
102 : static const char* udArrow[] = {
103 : "10 18 2 1",
104 : "# c #000000",
105 : ". c #d3d3d3",
106 : "..........",
107 : "..........",
108 : "....##....",
109 : "...####...",
110 : "..##..##..",
111 : "..#....#..",
112 : "..........",
113 : "..........",
114 : "..........",
115 : "..........",
116 : "..........",
117 : "..........",
118 : "..#....#..",
119 : "..##..##..",
120 : "...####...",
121 : "....##....",
122 : "..........",
123 : ".........."
124 : };
125 :
126 : static const char* uArrow[] = {
127 : "10 18 2 1",
128 : "# c #000000",
129 : ". c #d3d3d3",
130 : "..........",
131 : "..........",
132 : "....##....",
133 : "...####...",
134 : "..##..##..",
135 : "..#....#..",
136 : "..........",
137 : "..........",
138 : "..........",
139 : "..........",
140 : "..........",
141 : "..........",
142 : "..........",
143 : "..........",
144 : "..........",
145 : "..........",
146 : "..........",
147 : ".........."
148 : };
149 :
150 : static const char* dArrow[] = {
151 : "10 18 2 1",
152 : "# c #000000",
153 : ". c #d3d3d3",
154 : "..........",
155 : "..........",
156 : "..........",
157 : "..........",
158 : "..........",
159 : "..........",
160 : "..........",
161 : "..........",
162 : "..........",
163 : "..........",
164 : "..........",
165 : "..........",
166 : "..#....#..",
167 : "..##..##..",
168 : "...####...",
169 : "....##....",
170 : "..........",
171 : ".........."
172 : };
173 :
174 : static const char* cuArrow[] = {
175 : "9 18 2 1",
176 : "# c #000000",
177 : ". c #d3d3d3",
178 : ".........",
179 : ".........",
180 : ".........",
181 : ".........",
182 : "....#....",
183 : "...###...",
184 : "..#.#.#..",
185 : ".#..#..#.",
186 : "....#....",
187 : "....#....",
188 : "....#....",
189 : "....#....",
190 : ".........",
191 : ".........",
192 : ".........",
193 : ".........",
194 : ".........",
195 : "........."
196 : };
197 :
198 : static const char* cdArrow[] = {
199 : "9 18 2 1",
200 : "# c #000000",
201 : ". c #d3d3d3",
202 : ".........",
203 : ".........",
204 : ".........",
205 : ".........",
206 : ".........",
207 : "....#....",
208 : "....#....",
209 : "....#....",
210 : "....#....",
211 : "....#....",
212 : ".#..#..#.",
213 : "..#.#.#..",
214 : "...###...",
215 : "....#....",
216 : ".........",
217 : ".........",
218 : ".........",
219 : "........."
220 : };
221 :
222 : static const char* openEye[] = {
223 : "11 18 2 1",
224 : "# c #000000",
225 : ". c #d3d3d3",
226 : "...........",
227 : "...........",
228 : "...........",
229 : "...........",
230 : "...####....",
231 : ".##....##..",
232 : ".#......#..",
233 : "#........#.",
234 : "#...##...#.",
235 : "#...##...#.",
236 : "#........#.",
237 : ".#......#..",
238 : ".##....##..",
239 : "...####....",
240 : "...........",
241 : "...........",
242 : "...........",
243 : "..........."
244 : };
245 :
246 : static const char* closedEye[] = {
247 : "11 18 2 1",
248 : "# c #000000",
249 : ". c #d3d3d3",
250 : "...........",
251 : "...........",
252 : "...........",
253 : "...........",
254 : "...........",
255 : "...........",
256 : "...........",
257 : "..######...",
258 : "##..##..##.",
259 : "##..##..##.",
260 : "..######...",
261 : "...........",
262 : "...........",
263 : "...........",
264 : "...........",
265 : "...........",
266 : "...........",
267 : "..........."
268 : };
269 :
270 0 : _mouseoverMap[MouseOverValue::COLLAPSE_DOWN_ARROW] = new QPixmap(cdArrow);
271 0 : _mouseoverMap[MouseOverValue::COLLAPSE_UP_ARROW] = new QPixmap(cuArrow);
272 0 : _mouseoverMap[MouseOverValue::MOVE_DOWN_ARROW] = new QPixmap(dArrow);
273 0 : _mouseoverMap[MouseOverValue::MOVE_UP_DOWN_ARROW] = new QPixmap(udArrow);
274 0 : _mouseoverMap[MouseOverValue::MOVE_UP_ARROW] = new QPixmap(uArrow);
275 0 : _mouseoverMap[MouseOverValue::OPEN_EYE] = new QPixmap(openEye);
276 0 : _mouseoverMap[MouseOverValue::CLOSED_EYE] = new QPixmap(closedEye);
277 :
278 0 : std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
279 0 : _oldItemInfo = tmp;
280 :
281 0 : connect(this, &TRowLabels::requestContextMenu, _timeline, &Timeline::contextMenuEvent);
282 0 : }
283 :
284 0 : bool TRowLabels::handleEvent(QEvent* e)
285 : {
286 0 : return QWidget::event(e);
287 : }
288 :
289 : //---------------------------------------------------------
290 : // TRowLabels::restrictScroll
291 : //---------------------------------------------------------
292 :
293 0 : void TRowLabels::restrictScroll(int value)
294 : {
295 0 : TRACEFUNC;
296 :
297 0 : if (value > _timeline->verticalScrollBar()->maximum()) {
298 0 : verticalScrollBar()->setValue(_timeline->verticalScrollBar()->maximum());
299 : }
300 0 : for (std::vector<std::pair<QGraphicsItem*, int> >::iterator it = _metaLabels.begin();
301 0 : it != _metaLabels.end(); ++it) {
302 0 : std::pair<QGraphicsItem*, int> pairGraphicInt = *it;
303 :
304 0 : QGraphicsItem* graphicsItem = pairGraphicInt.first;
305 :
306 0 : QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
307 0 : QGraphicsLineItem* graphicsLineItem = qgraphicsitem_cast<QGraphicsLineItem*>(graphicsItem);
308 0 : QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
309 0 : int y = pairGraphicInt.second * 20;
310 0 : int scrollbarValue = verticalScrollBar()->value();
311 :
312 0 : if (graphicsRectItem) {
313 0 : QRectF rectf = graphicsRectItem->rect();
314 0 : rectf.setY(qreal(scrollbarValue + y));
315 0 : rectf.setHeight(20);
316 0 : graphicsRectItem->setRect(rectf);
317 0 : } else if (graphicsLineItem) {
318 0 : QLineF linef = graphicsLineItem->line();
319 0 : linef.setLine(linef.x1(), y + scrollbarValue + 1, linef.x2(), y + scrollbarValue + 1);
320 0 : graphicsLineItem->setLine(linef);
321 0 : } else if (graphicsPixmapItem) {
322 0 : graphicsPixmapItem->setY(qreal(scrollbarValue + y + 1));
323 : } else {
324 0 : graphicsItem->setY(qreal(scrollbarValue + y));
325 : }
326 : }
327 0 : viewport()->update();
328 0 : }
329 :
330 : //---------------------------------------------------------
331 : // TRowLabels::updateLabels
332 : //---------------------------------------------------------
333 :
334 0 : void TRowLabels::updateLabels(std::vector<std::pair<QString, bool> > labels, int height)
335 : {
336 0 : TRACEFUNC;
337 :
338 0 : scene()->clear();
339 0 : _metaLabels.clear();
340 0 : if (labels.empty()) {
341 0 : return;
342 : }
343 :
344 0 : unsigned numMetas = _timeline->nmetas();
345 0 : int maxWidth = -1;
346 0 : int measureWidth = 0;
347 0 : for (unsigned row = 0; row < labels.size(); row++) {
348 : // Draw instrument name rectangle
349 0 : int ypos = (row < numMetas) ? row * height + verticalScrollBar()->value() : row * height + 3;
350 0 : QGraphicsRectItem* graphicsRectItem = new QGraphicsRectItem(0, ypos, width(), height);
351 0 : QGraphicsTextItem* graphicsTextItem = new QGraphicsTextItem(labels[row].first);
352 :
353 0 : if (row == numMetas - 1) {
354 0 : measureWidth = graphicsTextItem->boundingRect().width();
355 : }
356 0 : maxWidth = std::max(maxWidth, int(graphicsTextItem->boundingRect().width()));
357 :
358 0 : QFontMetrics f(QApplication::font());
359 0 : QString partName = f.elidedText(labels[row].first, Qt::ElideRight, width());
360 0 : graphicsTextItem->setPlainText(partName);
361 0 : graphicsTextItem->setX(0);
362 0 : graphicsTextItem->setY(ypos);
363 0 : if (labels[row].second) {
364 0 : graphicsTextItem->setDefaultTextColor(_timeline->activeTheme().labelsColor1);
365 : } else {
366 0 : graphicsTextItem->setDefaultTextColor(_timeline->activeTheme().labelsColor2);
367 : }
368 0 : graphicsRectItem->setPen(QPen(_timeline->activeTheme().labelsColor2));
369 0 : graphicsRectItem->setBrush(QBrush(_timeline->activeTheme().labelsColor3));
370 0 : graphicsTextItem->setZValue(-1);
371 0 : graphicsRectItem->setZValue(-1);
372 :
373 0 : graphicsRectItem->setData(0, QVariant::fromValue<bool>(false));
374 0 : graphicsTextItem->setData(0, QVariant::fromValue<bool>(false));
375 :
376 0 : MouseOverValue mouseOverArrow = MouseOverValue::NONE;
377 0 : if (numMetas - 1 == row && (numMetas > 2 || _timeline->collapsed())) {
378 : // Measures meta
379 0 : if (_timeline->collapsed()) {
380 0 : mouseOverArrow = MouseOverValue::COLLAPSE_DOWN_ARROW;
381 : } else {
382 0 : mouseOverArrow = MouseOverValue::COLLAPSE_UP_ARROW;
383 : }
384 0 : } else if (row < numMetas - 1) {
385 0 : if (row != 0 && row + 1 <= numMetas - 2) {
386 0 : mouseOverArrow = MouseOverValue::MOVE_UP_DOWN_ARROW;
387 0 : } else if (row == 0 && row + 1 < numMetas - 1) {
388 0 : mouseOverArrow = MouseOverValue::MOVE_DOWN_ARROW;
389 0 : } else if (row == numMetas - 2 && row != 0) {
390 0 : mouseOverArrow = MouseOverValue::MOVE_UP_ARROW;
391 : }
392 0 : } else if (numMetas <= row) {
393 0 : if (_timeline->numToStaff(row - numMetas)
394 0 : && _timeline->numToStaff(row - numMetas)->show()) {
395 0 : mouseOverArrow = MouseOverValue::OPEN_EYE;
396 : } else {
397 0 : mouseOverArrow = MouseOverValue::CLOSED_EYE;
398 : }
399 : }
400 0 : graphicsTextItem->setData(1, QVariant::fromValue<MouseOverValue>(mouseOverArrow));
401 0 : graphicsRectItem->setData(1, QVariant::fromValue<MouseOverValue>(mouseOverArrow));
402 :
403 0 : graphicsTextItem->setData(2, QVariant::fromValue<unsigned>(row));
404 0 : graphicsRectItem->setData(2, QVariant::fromValue<unsigned>(row));
405 :
406 0 : scene()->addItem(graphicsRectItem);
407 0 : scene()->addItem(graphicsTextItem);
408 0 : if (row < numMetas) {
409 0 : std::pair<QGraphicsItem*, int> p1 = std::make_pair(graphicsRectItem, row);
410 0 : std::pair<QGraphicsItem*, int> p2 = std::make_pair(graphicsTextItem, row);
411 0 : _metaLabels.push_back(p1);
412 0 : _metaLabels.push_back(p2);
413 0 : graphicsRectItem->setZValue(1);
414 0 : graphicsTextItem->setZValue(2);
415 : }
416 0 : }
417 : QGraphicsLineItem* graphicsLineItem = new QGraphicsLineItem(0,
418 0 : height * numMetas + verticalScrollBar()->value() + 1,
419 0 : std::max(maxWidth + 20, 70),
420 0 : height * numMetas + verticalScrollBar()->value() + 1);
421 0 : graphicsLineItem->setPen(QPen(QColor(150, 150, 150), 4));
422 0 : graphicsLineItem->setZValue(0);
423 0 : graphicsLineItem->setData(0, QVariant::fromValue<bool>(false));
424 0 : scene()->addItem(graphicsLineItem);
425 :
426 0 : std::pair<QGraphicsItem*, int> graphicsLineItemPair = std::make_pair(graphicsLineItem, numMetas);
427 0 : _metaLabels.push_back(graphicsLineItemPair);
428 :
429 0 : setSceneRect(0, 0, maxWidth, _timeline->getHeight() + _timeline->horizontalScrollBar()->height());
430 :
431 0 : std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
432 0 : _oldItemInfo = tmp;
433 :
434 0 : setMinimumWidth(measureWidth + 9);
435 0 : setMaximumWidth(std::max(maxWidth + 20, 70));
436 0 : mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));
437 0 : }
438 :
439 : //---------------------------------------------------------
440 : // TRowLabels::resizeEvent
441 : //---------------------------------------------------------
442 :
443 0 : void TRowLabels::resizeEvent(QResizeEvent*)
444 : {
445 0 : std::vector<std::pair<QString, bool> > labels = _timeline->getLabels();
446 0 : updateLabels(labels, 20);
447 0 : }
448 :
449 : //---------------------------------------------------------
450 : // TRowLabels::mousePressEvent
451 : //---------------------------------------------------------
452 :
453 0 : void TRowLabels::mousePressEvent(QMouseEvent* event)
454 : {
455 0 : if (event->button() == Qt::RightButton) {
456 0 : return;
457 : }
458 :
459 0 : TRACEFUNC;
460 :
461 0 : QPointF scenePt = mapToScene(event->pos());
462 0 : unsigned numMetas = _timeline->nmetas();
463 :
464 : // Check if mouse position in scene is on the last meta
465 0 : QPointF measureMetaTl = QPointF(0, (numMetas - 1) * 20 + verticalScrollBar()->value());
466 0 : QPointF measureMetaBr = QPointF(width(), numMetas * 20 + verticalScrollBar()->value());
467 0 : if (QRectF(measureMetaTl, measureMetaBr).contains(scenePt) && (numMetas > 2 || _timeline->collapsed())) {
468 0 : if (std::get<0>(_oldItemInfo)) {
469 0 : std::pair<QGraphicsItem*, int> p = std::make_pair(std::get<0>(_oldItemInfo), std::get<2>(_oldItemInfo));
470 0 : std::vector<std::pair<QGraphicsItem*, int> >::iterator it = std::find(_metaLabels.begin(), _metaLabels.end(), p);
471 0 : if (it != _metaLabels.end()) {
472 0 : _metaLabels.erase(it);
473 : }
474 0 : scene()->removeItem(std::get<0>(_oldItemInfo));
475 : }
476 0 : std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
477 0 : _oldItemInfo = tmp;
478 0 : mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));
479 :
480 0 : _timeline->setCollapsed(!_timeline->collapsed());
481 0 : _timeline->updateGridView();
482 : } else {
483 : // Check if pixmap was selected
484 0 : if (QGraphicsItem* graphicsItem = scene()->itemAt(scenePt, transform())) {
485 0 : QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
486 0 : if (graphicsPixmapItem) {
487 0 : unsigned row = graphicsPixmapItem->data(2).value<unsigned>();
488 0 : if (row == numMetas - 1) {
489 0 : return;
490 0 : } else if (row < numMetas - 1) {
491 : // Find mid point between up and down arrow
492 0 : qreal midPoint = graphicsPixmapItem->boundingRect().height() / 2 + graphicsPixmapItem->scenePos().y();
493 0 : if (scenePt.y() > midPoint) {
494 0 : emit swapMeta(row, false);
495 : } else {
496 0 : emit swapMeta(row, true);
497 : }
498 0 : } else if (row >= numMetas) {
499 0 : _timeline->toggleShow(row - numMetas);
500 : }
501 : } else {
502 0 : _dragging = true;
503 0 : setCursor(Qt::SizeAllCursor);
504 0 : _oldLoc = QPoint(int(scenePt.x()), int(scenePt.y()));
505 : }
506 : } else {
507 0 : _dragging = true;
508 0 : setCursor(Qt::SizeAllCursor);
509 0 : _oldLoc = QPoint(int(scenePt.x()), int(scenePt.y()));
510 : }
511 : }
512 0 : }
513 :
514 : //---------------------------------------------------------
515 : // TRowLabels::mouseMoveEvent
516 : //---------------------------------------------------------
517 :
518 0 : void TRowLabels::mouseMoveEvent(QMouseEvent* event)
519 : {
520 0 : QPointF scenePt = mapToScene(event->pos());
521 0 : if (_dragging) {
522 0 : setCursor(Qt::SizeAllCursor);
523 0 : int yOffset = int(_oldLoc.y()) - int(scenePt.y());
524 0 : verticalScrollBar()->setValue(verticalScrollBar()->value() + yOffset);
525 : } else {
526 0 : mouseOver(scenePt);
527 : }
528 0 : emit moved(QPointF(-1, -1));
529 0 : }
530 :
531 : //---------------------------------------------------------
532 : // TRowLabels::mouseReleaseEvent
533 : //---------------------------------------------------------
534 :
535 0 : void TRowLabels::mouseReleaseEvent(QMouseEvent* event)
536 : {
537 0 : if (QGraphicsItem* graphicsItem = scene()->itemAt(mapToScene(event->pos()), transform())) {
538 0 : QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
539 0 : if (graphicsPixmapItem) {
540 0 : setCursor(Qt::PointingHandCursor);
541 : } else {
542 0 : setCursor(Qt::ArrowCursor);
543 : }
544 : } else {
545 0 : setCursor(Qt::ArrowCursor);
546 : }
547 0 : _dragging = false;
548 0 : }
549 :
550 : //---------------------------------------------------------
551 : // TRowLabels::leaveEvent
552 : //---------------------------------------------------------
553 :
554 0 : void TRowLabels::leaveEvent(QEvent*)
555 : {
556 0 : if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
557 0 : mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));
558 : }
559 0 : }
560 :
561 : //---------------------------------------------------------
562 : // TRowLabels::contextMenuEvent
563 : //---------------------------------------------------------
564 :
565 0 : void TRowLabels::contextMenuEvent(QContextMenuEvent* event)
566 : {
567 0 : emit requestContextMenu(event);
568 0 : }
569 :
570 : //---------------------------------------------------------
571 : // TRowLabels::mouseOver
572 : //---------------------------------------------------------
573 :
574 0 : void TRowLabels::mouseOver(QPointF scenePt)
575 : {
576 0 : TRACEFUNC;
577 :
578 : // Handle drawing of arrows
579 0 : if (QGraphicsItem* graphicsItem = scene()->itemAt(scenePt, transform())) {
580 0 : QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
581 0 : if (graphicsPixmapItem) {
582 0 : setCursor(Qt::PointingHandCursor);
583 0 : return;
584 : }
585 :
586 0 : MouseOverValue mouseOverArrow = graphicsItem->data(1).value<MouseOverValue>();
587 0 : if (mouseOverArrow != MouseOverValue::NONE) {
588 0 : QPixmap* pixmapArrow = _mouseoverMap[mouseOverArrow];
589 0 : QGraphicsPixmapItem* graphicsPixmapItemArrow = new QGraphicsPixmapItem(*pixmapArrow);
590 0 : unsigned row = graphicsItem->data(2).value<unsigned>();
591 :
592 0 : QString tooltip;
593 0 : switch (mouseOverArrow) {
594 0 : case MouseOverValue::COLLAPSE_DOWN_ARROW:
595 0 : tooltip = muse::qtrc("notation/timeline", "Expand meta rows");
596 0 : break;
597 0 : case MouseOverValue::COLLAPSE_UP_ARROW:
598 0 : tooltip = muse::qtrc("notation/timeline", "Collapse meta rows");
599 0 : break;
600 0 : case MouseOverValue::MOVE_DOWN_ARROW:
601 0 : tooltip = muse::qtrc("notation/timeline", "Move meta row down one");
602 0 : break;
603 0 : case MouseOverValue::MOVE_UP_ARROW:
604 0 : tooltip = muse::qtrc("notation/timeline", "Move meta row up one");
605 0 : break;
606 0 : case MouseOverValue::MOVE_UP_DOWN_ARROW:
607 0 : tooltip = muse::qtrc("notation/timeline", "Move meta row up/down one");
608 0 : break;
609 0 : case MouseOverValue::OPEN_EYE:
610 0 : tooltip = muse::qtrc("notation/timeline", "Hide instrument in score");
611 0 : break;
612 0 : case MouseOverValue::CLOSED_EYE:
613 0 : tooltip = muse::qtrc("notation/timeline", "Show instrument in score");
614 0 : break;
615 0 : default:
616 0 : tooltip = "";
617 0 : break;
618 : }
619 :
620 0 : graphicsPixmapItemArrow->setToolTip(tooltip);
621 0 : if (mouseOverArrow == MouseOverValue::OPEN_EYE || mouseOverArrow == MouseOverValue::CLOSED_EYE) {
622 0 : graphicsPixmapItemArrow->setData(0, QVariant::fromValue<bool>(false));
623 : } else {
624 0 : graphicsPixmapItemArrow->setData(0, QVariant::fromValue<bool>(true));
625 : }
626 0 : graphicsPixmapItemArrow->setData(1, QVariant::fromValue<MouseOverValue>(mouseOverArrow));
627 0 : graphicsPixmapItemArrow->setData(2, QVariant::fromValue<unsigned>(row));
628 :
629 : // Draw arrow at correct location
630 0 : if (row < _timeline->nmetas()) {
631 0 : graphicsPixmapItemArrow->setPos(width() - 12, verticalScrollBar()->value() + 1 + row * 20);
632 0 : graphicsPixmapItemArrow->setZValue(3);
633 : } else {
634 0 : graphicsPixmapItemArrow->setPos(width() - 13, row * 20 + 5);
635 0 : graphicsPixmapItemArrow->setZValue(-1);
636 : }
637 :
638 0 : if (std::get<2>(_oldItemInfo) == row && std::get<1>(_oldItemInfo) == mouseOverArrow) {
639 : // DO NOTHING
640 : } else {
641 0 : if (std::get<0>(_oldItemInfo)) {
642 0 : std::pair<QGraphicsItem*, int> p = std::make_pair(std::get<0>(_oldItemInfo), std::get<2>(_oldItemInfo));
643 0 : std::vector<std::pair<QGraphicsItem*, int> >::iterator it = std::find(_metaLabels.begin(), _metaLabels.end(), p);
644 0 : if (it != _metaLabels.end()) {
645 0 : _metaLabels.erase(it);
646 : }
647 0 : scene()->removeItem(std::get<0>(_oldItemInfo));
648 : }
649 0 : std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(graphicsPixmapItemArrow, mouseOverArrow, row);
650 0 : _oldItemInfo = tmp;
651 0 : if (mouseOverArrow != MouseOverValue::OPEN_EYE && mouseOverArrow != MouseOverValue::CLOSED_EYE) {
652 0 : std::pair<QGraphicsItem*, int> p = std::make_pair(graphicsPixmapItemArrow, row);
653 0 : _metaLabels.push_back(p);
654 : }
655 0 : scene()->addItem(graphicsPixmapItemArrow);
656 : }
657 0 : } else {
658 0 : if (std::get<0>(_oldItemInfo)) {
659 0 : scene()->removeItem(std::get<0>(_oldItemInfo));
660 : }
661 0 : std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
662 0 : _oldItemInfo = tmp;
663 : }
664 : } else {
665 0 : if (std::get<0>(_oldItemInfo)) {
666 0 : std::pair<QGraphicsItem*, int> p = std::make_pair(std::get<0>(_oldItemInfo), std::get<2>(_oldItemInfo));
667 0 : std::vector<std::pair<QGraphicsItem*, int> >::iterator it = std::find(_metaLabels.begin(), _metaLabels.end(), p);
668 0 : if (it != _metaLabels.end()) {
669 0 : _metaLabels.erase(it);
670 : }
671 0 : scene()->removeItem(std::get<0>(_oldItemInfo));
672 : }
673 0 : std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
674 0 : _oldItemInfo = tmp;
675 : }
676 0 : if (QGraphicsItem* graphicsItem = scene()->itemAt(scenePt, transform())) {
677 0 : QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
678 0 : if (graphicsPixmapItem) {
679 0 : setCursor(Qt::PointingHandCursor);
680 : } else {
681 0 : setCursor(Qt::ArrowCursor);
682 : }
683 : } else {
684 0 : setCursor(Qt::ArrowCursor);
685 : }
686 0 : }
687 :
688 : //---------------------------------------------------------
689 : // TRiwLabels::cursorIsOn
690 : //---------------------------------------------------------
691 :
692 0 : QString TRowLabels::cursorIsOn()
693 : {
694 0 : QPointF scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
695 0 : QGraphicsItem* graphicsItem = scene()->itemAt(scenePos, transform());
696 0 : if (graphicsItem) {
697 0 : auto it = _metaLabels.begin();
698 0 : for (; it != _metaLabels.end(); ++it) {
699 0 : if ((*it).first == graphicsItem) {
700 0 : break;
701 : }
702 : }
703 0 : if (it != _metaLabels.end()) {
704 0 : return "meta";
705 : } else {
706 0 : return "instrument";
707 : }
708 : } else {
709 0 : return "";
710 : }
711 : }
712 :
713 : //---------------------------------------------------------
714 : // Timeline
715 : //---------------------------------------------------------
716 :
717 0 : Timeline::Timeline(QSplitter* splitter, const muse::modularity::ContextPtr& iocCtx)
718 0 : : QGraphicsView(splitter), muse::Contextable(iocCtx)
719 : {
720 0 : TRACEFUNC;
721 :
722 0 : setFocusPolicy(Qt::NoFocus);
723 0 : setAlignment(Qt::Alignment((Qt::AlignLeft | Qt::AlignTop)));
724 0 : setAttribute(Qt::WA_OpaquePaintEvent);
725 0 : setObjectName("Timeline");
726 :
727 : // theming
728 0 : _lightTheme.backgroundColor = QColor(Qt::lightGray);
729 0 : _lightTheme.labelsColor1 = QColor(Qt::black);
730 0 : _lightTheme.labelsColor2 = QColor(150, 150, 150);
731 0 : _lightTheme.labelsColor3 = QColor(211, 211, 211);
732 0 : _lightTheme.gridColor1 = QColor(150, 150, 150);
733 0 : _lightTheme.gridColor2 = QColor(211, 211, 211);
734 0 : _lightTheme.measureMetaColor = QColor(0, 0, 0);
735 0 : _lightTheme.selectionColor = QColor(173, 216, 230);
736 0 : _lightTheme.nonVisiblePenColor = QColor(100, 150, 250);
737 0 : _lightTheme.nonVisibleBrushColor = QColor(192, 192, 192, 180);
738 0 : _lightTheme.colorBoxColor = QColor(Qt::gray);
739 0 : _lightTheme.metaValuePenColor = QColor(Qt::black);
740 0 : _lightTheme.metaValueBrushColor = QColor(Qt::gray);
741 :
742 0 : _darkTheme.backgroundColor = QColor(35, 35, 35);
743 0 : _darkTheme.labelsColor1 = QColor(225, 225, 225);
744 0 : _darkTheme.labelsColor2 = QColor(55, 55, 55);
745 0 : _darkTheme.labelsColor3 = QColor(70, 70, 70);
746 0 : _darkTheme.gridColor1 = QColor(50, 50, 50);
747 0 : _darkTheme.gridColor2 = QColor(75, 75, 75);
748 0 : _darkTheme.measureMetaColor = QColor(200, 200, 200);
749 0 : _darkTheme.selectionColor = QColor(55, 70, 75);
750 0 : _darkTheme.nonVisiblePenColor = QColor(40, 60, 80);
751 0 : _darkTheme.nonVisibleBrushColor = QColor(55, 55, 55, 180);
752 0 : _darkTheme.colorBoxColor = QColor(Qt::gray);
753 0 : _darkTheme.metaValuePenColor = QColor(Qt::lightGray);
754 0 : _darkTheme.metaValueBrushColor = QColor(Qt::darkGray);
755 :
756 0 : _splitter = splitter;
757 0 : _rowNames = new TRowLabels(splitter, this);
758 0 : _splitter->addWidget(_rowNames);
759 0 : _splitter->addWidget(this);
760 0 : _splitter->setChildrenCollapsible(false);
761 0 : _splitter->setStretchFactor(0, 0);
762 0 : _splitter->setStretchFactor(1, 0);
763 :
764 0 : setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
765 :
766 0 : setScene(new QGraphicsScene);
767 0 : setSceneRect(0, 0, 100, 100);
768 0 : scene()->setBackgroundBrush(QBrush(activeTheme().backgroundColor));
769 :
770 0 : connect(verticalScrollBar(), &QScrollBar::valueChanged, _rowNames->verticalScrollBar(), &QScrollBar::setValue);
771 0 : connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &Timeline::handleScroll);
772 0 : connect(_rowNames, &TRowLabels::swapMeta, this, &Timeline::swapMeta);
773 0 : connect(this, &Timeline::moved, _rowNames, &TRowLabels::mouseOver);
774 :
775 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Tempo"), &Timeline::tempoMeta, true });
776 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Time signature"), &Timeline::timeMeta, true });
777 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Rehearsal mark"), &Timeline::rehearsalMeta, true });
778 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Key signature"), &Timeline::keyMeta, true });
779 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Barlines"), &Timeline::barlineMeta, true });
780 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Jumps and markers"), &Timeline::jumpMarkerMeta, true });
781 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Measures"), &Timeline::measureMeta, true });
782 :
783 0 : std::tuple<QGraphicsItem*, int, QColor> ohi(nullptr, -1, QColor());
784 0 : _oldHoverInfo = ohi;
785 0 : std::tuple<int, qreal, EngravingItem*, EngravingItem*, bool> ri(0, 0, nullptr, nullptr, false);
786 0 : _repeatInfo = ri;
787 :
788 : static const char* startRepeat[] = {
789 : "7 14 2 1",
790 : "# c #000000",
791 : ". c None",
792 : "##.#...",
793 : "##.#...",
794 : "##.#...",
795 : "##.#...",
796 : "##.#.##",
797 : "##.#.##",
798 : "##.#...",
799 : "##.#...",
800 : "##.#.##",
801 : "##.#.##",
802 : "##.#...",
803 : "##.#...",
804 : "##.#...",
805 : "##.#..."
806 : };
807 :
808 : static const char* endRepeat[] = {
809 : "7 14 2 1",
810 : "# c #000000",
811 : ". c None",
812 : "...#.##",
813 : "...#.##",
814 : "...#.##",
815 : "...#.##",
816 : "##.#.##",
817 : "##.#.##",
818 : "...#.##",
819 : "...#.##",
820 : "##.#.##",
821 : "##.#.##",
822 : "...#.##",
823 : "...#.##",
824 : "...#.##",
825 : "...#.##"
826 : };
827 :
828 : static const char* endBarline[] = {
829 : "7 14 2 1",
830 : "# c #000000",
831 : ". c None",
832 : "...#.##",
833 : "...#.##",
834 : "...#.##",
835 : "...#.##",
836 : "...#.##",
837 : "...#.##",
838 : "...#.##",
839 : "...#.##",
840 : "...#.##",
841 : "...#.##",
842 : "...#.##",
843 : "...#.##",
844 : "...#.##",
845 : "...#.##"
846 : };
847 :
848 : static const char* doubleBarline[] = {
849 : "7 14 2 1",
850 : "# c #000000",
851 : ". c None",
852 : "..#.#..",
853 : "..#.#..",
854 : "..#.#..",
855 : "..#.#..",
856 : "..#.#..",
857 : "..#.#..",
858 : "..#.#..",
859 : "..#.#..",
860 : "..#.#..",
861 : "..#.#..",
862 : "..#.#..",
863 : "..#.#..",
864 : "..#.#..",
865 : "..#.#.."
866 : };
867 :
868 : static const char* reverseEndBarline[] = {
869 : "7 14 2 1",
870 : "# c #000000",
871 : ". c None",
872 : "##.#...",
873 : "##.#...",
874 : "##.#...",
875 : "##.#...",
876 : "##.#...",
877 : "##.#...",
878 : "##.#...",
879 : "##.#...",
880 : "##.#...",
881 : "##.#...",
882 : "##.#...",
883 : "##.#...",
884 : "##.#...",
885 : "##.#..."
886 : };
887 :
888 : static const char* heavyBarline[] = {
889 : "6 14 2 1",
890 : "# c #000000",
891 : ". c None",
892 : "..##..",
893 : "..##..",
894 : "..##..",
895 : "..##..",
896 : "..##..",
897 : "..##..",
898 : "..##..",
899 : "..##..",
900 : "..##..",
901 : "..##..",
902 : "..##..",
903 : "..##..",
904 : "..##..",
905 : "..##.."
906 : };
907 :
908 : static const char* doubleHeavyBarline[] = {
909 : "7 14 2 1",
910 : "# c #000000",
911 : ". c None",
912 : ".##.##.",
913 : ".##.##.",
914 : ".##.##.",
915 : ".##.##.",
916 : ".##.##.",
917 : ".##.##.",
918 : ".##.##.",
919 : ".##.##.",
920 : ".##.##.",
921 : ".##.##.",
922 : ".##.##.",
923 : ".##.##.",
924 : ".##.##.",
925 : ".##.##."
926 : };
927 :
928 0 : QPixmap* startRepeatPixmap = new QPixmap(startRepeat);
929 0 : QPixmap* endRepeatPixmap = new QPixmap(endRepeat);
930 0 : QPixmap* endBarlinePixmap = new QPixmap(endBarline);
931 0 : QPixmap* doubleBarlinePixmap = new QPixmap(doubleBarline);
932 0 : QPixmap* reverseEndBarlinePixmap = new QPixmap(reverseEndBarline);
933 0 : QPixmap* heavyBarlinePixmap = new QPixmap(heavyBarline);
934 0 : QPixmap* doubleHeavyBarlinePixmap = new QPixmap(doubleHeavyBarline);
935 :
936 0 : _barlines[BarLineType::START_REPEAT] = startRepeatPixmap;
937 0 : _barlines[BarLineType::END_REPEAT] = endRepeatPixmap;
938 0 : _barlines[BarLineType::END] = endBarlinePixmap;
939 0 : _barlines[BarLineType::DOUBLE] = doubleBarlinePixmap;
940 0 : _barlines[BarLineType::REVERSE_END] = reverseEndBarlinePixmap;
941 0 : _barlines[BarLineType::HEAVY] = heavyBarlinePixmap;
942 0 : _barlines[BarLineType::DOUBLE_HEAVY] = doubleHeavyBarlinePixmap;
943 :
944 0 : uiConfiguration()->currentThemeChanged().onNotify(this, [this]() {
945 0 : updateTimelineTheme();
946 0 : });
947 0 : }
948 :
949 0 : bool Timeline::handleEvent(QEvent* e)
950 : {
951 0 : return QWidget::event(e);
952 : }
953 :
954 : //---------------------------------------------------------
955 : // Timeline::drawGrid
956 : //---------------------------------------------------------
957 :
958 0 : void Timeline::drawGrid(int globalRows, int globalCols, int startMeasure, int endMeasure)
959 : {
960 0 : TRACEFUNC;
961 :
962 0 : if (endMeasure < 0) {
963 0 : endMeasure = globalCols;
964 : }
965 0 : if (startMeasure < 0) {
966 0 : endMeasure = startMeasure;
967 : }
968 :
969 0 : const bool rebuildAll = (
970 0 : gridRows != globalRows || gridCols != globalCols
971 0 : || (startMeasure == 0 && 2 * (endMeasure - startMeasure) > globalCols) // rebuild all if more than half of score has changed
972 : );
973 0 : const bool rebuildPartial = !rebuildAll && (startMeasure >= 0);
974 :
975 0 : const unsigned numMetas = nmetas();
976 :
977 0 : if (rebuildAll) {
978 0 : clearScene();
979 0 : startMeasure = 0;
980 0 : endMeasure = globalCols;
981 : } else {
982 0 : if (rebuildPartial) {
983 : const QRectF replacedRect
984 0 : = getMeasureRect(startMeasure, 0, numMetas) | getMeasureRect(endMeasure - 1, globalRows - 1, numMetas);
985 0 : const QList<QGraphicsItem*> replacedItems = scene()->items(replacedRect, Qt::ContainsItemShape);
986 0 : for (QGraphicsItem* item : replacedItems) {
987 0 : if (item->data(keyItemType).value<ItemType>() != ItemType::TYPE_MEASURE) {
988 0 : continue;
989 : }
990 0 : scene()->removeItem(item);
991 0 : delete item;
992 : }
993 0 : }
994 :
995 : // Meta rows are still rebuilt from scratch, remove old meta rows manually
996 0 : const QList<QGraphicsItem*> items = scene()->items();
997 0 : for (QGraphicsItem* item : items) {
998 0 : if (item->data(keyItemType).value<ItemType>() != ItemType::TYPE_META) {
999 0 : continue;
1000 : }
1001 0 : scene()->removeItem(item);
1002 0 : delete item;
1003 : }
1004 0 : }
1005 :
1006 0 : _metaRows.clear();
1007 :
1008 0 : if (globalRows == 0 || globalCols == 0) {
1009 0 : return;
1010 : }
1011 :
1012 0 : int stagger = 0;
1013 0 : setMinimumHeight(_gridHeight * (numMetas + 1) + 5 + horizontalScrollBar()->height());
1014 0 : setMinimumWidth(_gridWidth * 3);
1015 0 : _globalZValue = 1;
1016 :
1017 : // Draw grid
1018 0 : Measure* currMeasure = score()->firstMeasure();
1019 0 : for (int i = 0; i < startMeasure; ++i) {
1020 0 : currMeasure = currMeasure->nextMeasure();
1021 : }
1022 :
1023 0 : QList<Part*> partList = getParts();
1024 :
1025 0 : for (int col = startMeasure; col < endMeasure; col++) {
1026 0 : for (int row = 0; row < globalRows; row++) {
1027 0 : QGraphicsRectItem* graphicsRectItem = new QGraphicsRectItem(getMeasureRect(col, row, numMetas));
1028 0 : graphicsRectItem->setData(keyItemType, QVariant::fromValue(ItemType::TYPE_MEASURE));
1029 :
1030 0 : setMetaData(graphicsRectItem, row, ElementType::INVALID, currMeasure, false, 0);
1031 :
1032 0 : QString translateMeasure = muse::qtrc("notation/timeline", "Measure");
1033 0 : QChar initialLetter = translateMeasure[0];
1034 0 : QTextDocument doc;
1035 0 : QString partName = "";
1036 0 : if (partList.size() > row) {
1037 0 : doc.setHtml(partList.at(row)->longName());
1038 0 : partName = doc.toPlainText();
1039 0 : if (partName.isEmpty()) { // No Long instrument name? Fall back to Part name
1040 0 : doc.setHtml(partList.at(row)->partName());
1041 0 : partName = doc.toPlainText();
1042 : }
1043 0 : if (partName.isEmpty()) { // No Part name? Fall back to Instrument name
1044 0 : partName = partList.at(row)->instrumentName();
1045 : }
1046 : }
1047 :
1048 0 : graphicsRectItem->setToolTip(initialLetter + u" "_s + QString::number(currMeasure->measureNumber() + 1) + u", "_s + partName);
1049 0 : graphicsRectItem->setPen(QPen(activeTheme().backgroundColor));
1050 0 : graphicsRectItem->setBrush(QBrush(colorBox(graphicsRectItem)));
1051 0 : graphicsRectItem->setZValue(-3);
1052 0 : scene()->addItem(graphicsRectItem);
1053 0 : }
1054 :
1055 0 : currMeasure = currMeasure->nextMeasure();
1056 : }
1057 0 : setSceneRect(0, 0, getWidth(), getHeight());
1058 :
1059 : // Draw meta rows and separator
1060 : QGraphicsLineItem* graphicsLineItemSeparator = new QGraphicsLineItem(0,
1061 0 : _gridHeight * numMetas + verticalScrollBar()->value() + 1,
1062 0 : getWidth() - 1,
1063 0 : _gridHeight * numMetas + verticalScrollBar()->value() + 1);
1064 0 : graphicsLineItemSeparator->setData(keyItemType, QVariant::fromValue(ItemType::TYPE_META));
1065 0 : graphicsLineItemSeparator->setPen(QPen(activeTheme().gridColor1, 4));
1066 0 : graphicsLineItemSeparator->setZValue(-2);
1067 0 : scene()->addItem(graphicsLineItemSeparator);
1068 0 : std::pair<QGraphicsItem*, int> pairGraphicsIntSeparator(graphicsLineItemSeparator, numMetas);
1069 0 : _metaRows.push_back(pairGraphicsIntSeparator);
1070 :
1071 0 : for (unsigned row = 0; row < numMetas; row++) {
1072 : QGraphicsRectItem* metaRow = new QGraphicsRectItem(0,
1073 0 : _gridHeight * row + verticalScrollBar()->value(),
1074 0 : getWidth(),
1075 0 : _gridHeight);
1076 0 : metaRow->setData(keyItemType, QVariant::fromValue(ItemType::TYPE_META));
1077 0 : metaRow->setBrush(QBrush(activeTheme().gridColor2));
1078 0 : metaRow->setPen(QPen(activeTheme().gridColor1));
1079 0 : metaRow->setData(0, QVariant::fromValue<int>(-1));
1080 :
1081 0 : scene()->addItem(metaRow);
1082 :
1083 0 : std::pair<QGraphicsItem*, int> pairGraphicsIntMeta(metaRow, row);
1084 0 : _metaRows.push_back(pairGraphicsIntMeta);
1085 : }
1086 :
1087 0 : int xPos = 0;
1088 :
1089 : // Create stagger array if _collapsedMeta is false
1090 0 : std::vector<int> staggerArr(numMetas, 0); // Default initialized, loop not required
1091 :
1092 0 : bool noKey = true;
1093 0 : std::get<4>(_repeatInfo) = false;
1094 :
1095 0 : for (Measure* cm = score()->firstMeasure(); cm; cm = cm->nextMeasure()) {
1096 0 : for (Segment* currSeg = cm->first(); currSeg; currSeg = currSeg->next()) {
1097 : // Toggle noKey if initial key signature is found
1098 0 : if (currSeg->isKeySigType() && cm == score()->firstMeasure()) {
1099 0 : if (noKey && currSeg->tick().isZero()) {
1100 0 : noKey = false;
1101 : }
1102 : }
1103 :
1104 : // If no initial key signature is found, add key signature
1105 0 : if (cm == score()->firstMeasure() && noKey
1106 0 : && (currSeg->isTimeSigType() || currSeg->isChordRestType())) {
1107 0 : if (getMetaRow(muse::qtrc("notation/timeline", "Key signature")) != numMetas) {
1108 0 : if (_collapsedMeta) {
1109 0 : keyMeta(0, &stagger, xPos);
1110 : } else {
1111 0 : keyMeta(0, &staggerArr[getMetaRow(muse::qtrc("notation/timeline", "Key signature"))], xPos);
1112 : }
1113 : }
1114 0 : noKey = false;
1115 : }
1116 0 : int row = 0;
1117 0 : for (auto it = _metas.begin(); it != _metas.end(); ++it) {
1118 0 : std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
1119 0 : if (!std::get<2>(meta)) {
1120 0 : continue;
1121 : }
1122 0 : void (Timeline::* func)(Segment*, int*, int) = std::get<1>(meta);
1123 0 : if (_collapsedMeta) {
1124 0 : (this->*func)(currSeg, &stagger, xPos);
1125 : } else {
1126 0 : (this->*func)(currSeg, &staggerArr[row], xPos);
1127 : }
1128 0 : row++;
1129 0 : }
1130 : }
1131 : // Handle all jumps here
1132 0 : if (getMetaRow(muse::qtrc("notation/timeline", "Jumps and markers")) != numMetas) {
1133 0 : ElementList measureElementsList = cm->el();
1134 0 : for (EngravingItem* element : measureElementsList) {
1135 0 : std::get<3>(_repeatInfo) = element;
1136 0 : if (element->isMarker()) {
1137 0 : jumpMarkerMeta(0, &stagger, xPos);
1138 : }
1139 : }
1140 0 : for (EngravingItem* element : measureElementsList) {
1141 0 : if (element->isJump()) {
1142 0 : std::get<2>(_repeatInfo) = element;
1143 0 : if (_collapsedMeta) {
1144 0 : jumpMarkerMeta(0, &stagger, xPos);
1145 : } else {
1146 0 : jumpMarkerMeta(0, &std::get<0>(_repeatInfo), xPos);
1147 : }
1148 : }
1149 : }
1150 0 : }
1151 0 : stagger = 0;
1152 0 : std::get<0>(_repeatInfo) = 0;
1153 :
1154 0 : for (unsigned row = 0; row < numMetas; row++) {
1155 0 : staggerArr[row] = 0;
1156 : }
1157 0 : xPos += _gridWidth;
1158 0 : std::get<4>(_repeatInfo) = false;
1159 : }
1160 :
1161 0 : gridRows = globalRows;
1162 0 : gridCols = globalCols;
1163 0 : }
1164 :
1165 : //---------------------------------------------------------
1166 : // Timeline::tempoMeta
1167 : //---------------------------------------------------------
1168 :
1169 0 : void Timeline::tempoMeta(Segment* seg, int* stagger, int pos)
1170 : {
1171 : // Find position of tempoMeta in metas
1172 0 : int row = getMetaRow(muse::qtrc("notation/timeline", "Tempo"));
1173 :
1174 : // Add all tempo texts in this segment
1175 0 : const std::vector<EngravingItem*> annotations = seg->annotations();
1176 0 : for (EngravingItem* element : annotations) {
1177 0 : if (element->isTempoText()) {
1178 0 : TempoText* text = toTempoText(element);
1179 0 : qreal x = pos + (*stagger) * _spacing;
1180 0 : if (addMetaValue(x, pos, text->plainText(), row, ElementType::TEMPO_TEXT, element, 0, seg->measure())) {
1181 0 : (*stagger)++;
1182 0 : _globalZValue++;
1183 : }
1184 : }
1185 : }
1186 0 : }
1187 :
1188 : //---------------------------------------------------------
1189 : // Timeline::timeMeta
1190 : //---------------------------------------------------------
1191 :
1192 0 : void Timeline::timeMeta(Segment* seg, int* stagger, int pos)
1193 : {
1194 0 : if (!seg->isTimeSigType()) {
1195 0 : return;
1196 : }
1197 :
1198 0 : TRACEFUNC;
1199 :
1200 0 : int x = pos + (*stagger) * _spacing;
1201 :
1202 : // Find position of timeMeta in metas
1203 0 : int row = getMetaRow(muse::qtrc("notation/timeline", "Time signature"));
1204 :
1205 0 : TimeSig* originalTimeSig = toTimeSig(seg->element(0));
1206 0 : if (!originalTimeSig) {
1207 0 : return;
1208 : }
1209 :
1210 : // Check if same across all staves
1211 0 : const size_t nrows = score()->staves().size();
1212 0 : bool same = true;
1213 0 : for (size_t track = 0; track < nrows; track++) {
1214 0 : const TimeSig* currTimeSig = toTimeSig(seg->element(track * VOICES));
1215 0 : if (!currTimeSig) {
1216 0 : same = false;
1217 0 : break;
1218 : }
1219 0 : if (*currTimeSig == *originalTimeSig) {
1220 0 : continue;
1221 : }
1222 0 : same = false;
1223 0 : break;
1224 : }
1225 0 : if (!same) {
1226 0 : return;
1227 : }
1228 0 : QString text = QString::number(originalTimeSig->numerator()) + QString("/") + QString::number(originalTimeSig->denominator());
1229 :
1230 0 : if (addMetaValue(x, pos, text, row, ElementType::TIMESIG, 0, seg, seg->measure())) {
1231 0 : (*stagger)++;
1232 0 : _globalZValue++;
1233 : }
1234 0 : }
1235 :
1236 : //---------------------------------------------------------
1237 : // Timeline::rehearsalMeta
1238 : //---------------------------------------------------------
1239 :
1240 0 : void Timeline::rehearsalMeta(Segment* seg, int* stagger, int pos)
1241 : {
1242 0 : int row = getMetaRow(muse::qtrc("notation/timeline", "Rehearsal mark"));
1243 :
1244 0 : for (EngravingItem* element : seg->annotations()) {
1245 0 : int x = pos + (*stagger) * _spacing;
1246 0 : if (element->isRehearsalMark()) {
1247 0 : RehearsalMark* rehearsal_mark = toRehearsalMark(element);
1248 0 : if (!rehearsal_mark) {
1249 0 : continue;
1250 : }
1251 :
1252 0 : if (addMetaValue(x, pos, rehearsal_mark->plainText(), row, ElementType::REHEARSAL_MARK, element, 0, seg->measure())) {
1253 0 : (*stagger)++;
1254 0 : _globalZValue++;
1255 : }
1256 : }
1257 : }
1258 0 : }
1259 :
1260 : //---------------------------------------------------------
1261 : // Timeline::keyMeta
1262 : //---------------------------------------------------------
1263 :
1264 0 : void Timeline::keyMeta(Segment* seg, int* stagger, int pos)
1265 : {
1266 : // If seg is nullptr, handle initial key signature
1267 0 : if (seg && !seg->isKeySigType()) {
1268 0 : return;
1269 : }
1270 :
1271 0 : TRACEFUNC;
1272 :
1273 0 : int row = getMetaRow(muse::qtrc("notation/timeline", "Key signature"));
1274 0 : std::map<Key, int> keyFrequencies;
1275 0 : const std::vector<Staff*>& staves = score()->staves();
1276 :
1277 0 : int track = 0;
1278 0 : for (Staff* stave : staves) {
1279 0 : if (!stave->show()) {
1280 0 : track += VOICES;
1281 0 : continue;
1282 : }
1283 :
1284 : // Ignore unpitched staves
1285 0 : if ((seg && !stave->isPitchedStaff(seg->tick())) || (!seg && !stave->isPitchedStaff(Fraction(0, 1)))) {
1286 0 : track += VOICES;
1287 0 : continue;
1288 : }
1289 :
1290 : // Add corrected key signature to map
1291 : // Atonal -> Key::INVALID
1292 : // Custom -> Key::NUM_OF
1293 0 : const KeySig* currKeySig = nullptr;
1294 0 : if (seg) {
1295 0 : currKeySig = toKeySig(seg->element(track));
1296 : }
1297 :
1298 : Key globalKey;
1299 0 : if (seg) {
1300 0 : globalKey = stave->concertKey(seg->tick());
1301 : } else {
1302 0 : globalKey = stave->concertKey(Fraction(0, 1));
1303 : }
1304 0 : if (currKeySig) {
1305 0 : if (currKeySig->generated()) {
1306 0 : return;
1307 : }
1308 0 : globalKey = currKeySig->concertKey();
1309 : }
1310 :
1311 0 : if (currKeySig && currKeySig->isAtonal()) {
1312 0 : globalKey = Key::INVALID;
1313 0 : } else if (currKeySig && currKeySig->isCustom()) {
1314 0 : globalKey = Key::NUM_OF;
1315 : }
1316 :
1317 0 : std::map<Key, int>::iterator it = keyFrequencies.find(globalKey);
1318 0 : if (it != keyFrequencies.end()) {
1319 0 : keyFrequencies[globalKey]++;
1320 : } else {
1321 0 : keyFrequencies[globalKey] = 1;
1322 : }
1323 :
1324 0 : track += VOICES;
1325 : }
1326 :
1327 : // Change key into QString
1328 0 : Key newKey = Key::C;
1329 0 : int maxKeyFreq = 0;
1330 0 : for (std::map<Key, int>::iterator iter = keyFrequencies.begin(); iter != keyFrequencies.end(); ++iter) {
1331 0 : if (iter->second > maxKeyFreq) {
1332 0 : newKey = iter->first;
1333 0 : maxKeyFreq = iter->second;
1334 : }
1335 : }
1336 0 : QString keyText;
1337 0 : QString tooltip;
1338 0 : if (newKey == Key::INVALID) {
1339 0 : keyText = "X";
1340 0 : tooltip = TConv::translatedUserName(Key::INVALID, true);
1341 0 : } else if (newKey == Key::NUM_OF) {
1342 0 : keyText = "?";
1343 0 : tooltip = muse::qtrc("notation/timeline", "Custom key signature");
1344 0 : } else if (int(newKey) == 0) {
1345 0 : keyText = "\u266E";
1346 0 : tooltip = TConv::translatedUserName(Key::C);
1347 0 : } else if (int(newKey) < 0) {
1348 0 : keyText = QString::number(std::abs(int(newKey))) + "\u266D";
1349 0 : tooltip = TConv::translatedUserName(newKey);
1350 : } else {
1351 0 : keyText = QString::number(std::abs(int(newKey))) + "\u266F";
1352 0 : tooltip = TConv::translatedUserName(newKey);
1353 : }
1354 :
1355 0 : int x = pos + (*stagger) * _spacing;
1356 0 : Measure* measure = seg ? seg->measure() : 0;
1357 0 : if (addMetaValue(x, pos, keyText, row, ElementType::KEYSIG, 0, seg, measure, tooltip)) {
1358 0 : (*stagger)++;
1359 0 : _globalZValue++;
1360 : }
1361 0 : }
1362 :
1363 : //---------------------------------------------------------
1364 : // Timeline::barLineMeta
1365 : //---------------------------------------------------------
1366 :
1367 0 : void Timeline::barlineMeta(Segment* seg, int* stagger, int pos)
1368 : {
1369 0 : if (!seg->isBeginBarLineType() && !seg->isEndBarLineType() && !seg->isBarLine() && !seg->isStartRepeatBarLineType()) {
1370 0 : return;
1371 : }
1372 :
1373 0 : TRACEFUNC;
1374 :
1375 : // Find position of repeat_meta in metas
1376 0 : int row = getMetaRow(muse::qtrc("notation/timeline", "Barlines"));
1377 :
1378 0 : QString repeatText = "";
1379 0 : BarLine* barline = toBarLine(seg->element(0));
1380 :
1381 0 : if (barline) {
1382 0 : switch (barline->barLineType()) {
1383 0 : case BarLineType::START_REPEAT:
1384 : case BarLineType::END_REPEAT:
1385 : case BarLineType::END:
1386 : case BarLineType::DOUBLE:
1387 : case BarLineType::REVERSE_END:
1388 : case BarLineType::HEAVY:
1389 : case BarLineType::DOUBLE_HEAVY:
1390 0 : repeatText = BarLine::translatedUserTypeName(barline->barLineType());
1391 0 : break;
1392 0 : case BarLineType::END_START_REPEAT:
1393 : // actually an end repeat followed by a start repeat, so nothing needs to be done here
1394 : default:
1395 0 : break;
1396 : }
1397 0 : _isBarline = true;
1398 : } else {
1399 0 : return;
1400 : }
1401 :
1402 0 : Measure* measure = seg->measure();
1403 0 : ElementType elementType = ElementType::BAR_LINE;
1404 0 : EngravingItem* element = nullptr;
1405 :
1406 0 : if (repeatText == "") {
1407 0 : _isBarline = false;
1408 0 : return;
1409 : }
1410 :
1411 0 : int x = pos + (*stagger) * _spacing;
1412 0 : if (addMetaValue(x, pos, repeatText, row, elementType, element, seg, measure)) {
1413 0 : (*stagger)++;
1414 0 : _globalZValue++;
1415 : }
1416 0 : _isBarline = false;
1417 0 : }
1418 :
1419 : //---------------------------------------------------------
1420 : // Timeline::jumpMarkerMeta
1421 : //---------------------------------------------------------
1422 :
1423 0 : void Timeline::jumpMarkerMeta(Segment* seg, int* stagger, int pos)
1424 : {
1425 0 : if (seg) {
1426 0 : return;
1427 : }
1428 :
1429 0 : TRACEFUNC;
1430 :
1431 : // Find position of repeat_meta in metas
1432 0 : int row = getMetaRow(muse::qtrc("notation/timeline", "Jumps and markers"));
1433 :
1434 0 : QString text = "";
1435 0 : EngravingItem* element = nullptr;
1436 0 : if (std::get<2>(_repeatInfo)) {
1437 0 : element = std::get<2>(_repeatInfo);
1438 0 : } else if (std::get<3>(_repeatInfo)) {
1439 0 : element = std::get<3>(_repeatInfo);
1440 : }
1441 :
1442 : Measure* measure;
1443 : ElementType elementType;
1444 0 : if (std::get<2>(_repeatInfo)) {
1445 0 : Jump* jump = toJump(std::get<2>(_repeatInfo));
1446 0 : text = jump->plainText();
1447 0 : measure = jump->measure();
1448 0 : elementType = ElementType::JUMP;
1449 : } else {
1450 0 : Marker* marker = toMarker(std::get<3>(_repeatInfo));
1451 0 : std::list<TextFragment> tf_list = marker->fragmentList();
1452 0 : for (TextFragment tf : tf_list) {
1453 0 : text.push_back(tf.text);
1454 0 : }
1455 0 : measure = marker->measure();
1456 0 : if (marker->markerType() == MarkerType::FINE
1457 0 : || marker->markerType() == MarkerType::TOCODA
1458 0 : || marker->markerType() == MarkerType::TOCODASYM
1459 0 : || marker->markerType() == MarkerType::DA_CODA
1460 0 : || marker->markerType() == MarkerType::DA_DBLCODA
1461 : ) {
1462 0 : elementType = ElementType::MARKER;
1463 0 : std::get<2>(_repeatInfo) = std::get<3>(_repeatInfo);
1464 0 : std::get<3>(_repeatInfo) = nullptr;
1465 : } else {
1466 0 : elementType = ElementType::MARKER;
1467 : }
1468 0 : }
1469 :
1470 0 : if (text == "") {
1471 0 : std::get<2>(_repeatInfo) = nullptr;
1472 0 : std::get<3>(_repeatInfo) = nullptr;
1473 0 : return;
1474 : }
1475 :
1476 0 : int x = pos + (*stagger) * _spacing;
1477 0 : if (addMetaValue(x, pos, text, row, elementType, element, seg, measure)) {
1478 0 : (*stagger)++;
1479 0 : _globalZValue++;
1480 : }
1481 0 : std::get<2>(_repeatInfo) = nullptr;
1482 0 : std::get<3>(_repeatInfo) = nullptr;
1483 0 : }
1484 :
1485 : //---------------------------------------------------------
1486 : // Timeline::measureMeta
1487 : //---------------------------------------------------------
1488 :
1489 0 : void Timeline::measureMeta(Segment*, int*, int pos)
1490 : {
1491 0 : TRACEFUNC;
1492 :
1493 : // Increment decided by zoom level
1494 0 : int incrementValue = 1;
1495 0 : int halfway = (_maxZoom + _minZoom) / 2;
1496 0 : if (_gridWidth <= _maxZoom && _gridWidth > halfway) {
1497 0 : incrementValue = 1;
1498 0 : } else if (_gridWidth <= halfway && _gridWidth > _minZoom) {
1499 0 : incrementValue = 5;
1500 : } else {
1501 0 : incrementValue = 10;
1502 : }
1503 :
1504 0 : int currMeasureNumber = pos / _gridWidth;
1505 0 : if (currMeasureNumber == _globalMeasureNumber) {
1506 0 : return;
1507 : }
1508 :
1509 0 : _globalMeasureNumber = currMeasureNumber;
1510 : // Check if 1 or 5*n
1511 0 : if (currMeasureNumber + 1 != 1 && (currMeasureNumber + 1) % incrementValue != 0) {
1512 0 : return;
1513 : }
1514 :
1515 : // Find position of measureMeta in metas
1516 0 : int row = getMetaRow(muse::qtrc("notation/timeline", "Measures"));
1517 :
1518 : // Adjust number
1519 : Measure* currMeasure;
1520 0 : for (currMeasure = score()->firstMeasure(); currMeasureNumber != 0; currMeasureNumber--, currMeasure = currMeasure->nextMeasure()) {
1521 : }
1522 :
1523 : // Add measure number
1524 0 : QString measureNumber = (currMeasure->excludeFromNumbering()) ? u"( )"_s : QString::number(currMeasure->measureNumber() + 1);
1525 0 : QGraphicsTextItem* graphicsTextItem = new QGraphicsTextItem(measureNumber);
1526 0 : graphicsTextItem->setData(keyItemType, QVariant::fromValue(ItemType::TYPE_META));
1527 0 : graphicsTextItem->setDefaultTextColor(activeTheme().measureMetaColor);
1528 0 : graphicsTextItem->setX(pos);
1529 0 : graphicsTextItem->setY(_gridHeight * row + verticalScrollBar()->value());
1530 :
1531 0 : QFont f = graphicsTextItem->font();
1532 0 : f.setPointSizeF(7.0);
1533 0 : graphicsTextItem->setFont(f);
1534 :
1535 : // Center text
1536 0 : qreal remainingWidth = _gridWidth - graphicsTextItem->boundingRect().width();
1537 0 : qreal remainingHeight = _gridHeight - graphicsTextItem->boundingRect().height();
1538 0 : graphicsTextItem->setX(graphicsTextItem->x() + remainingWidth / 2);
1539 0 : graphicsTextItem->setY(graphicsTextItem->y() + remainingHeight / 2);
1540 :
1541 0 : int endOfText = graphicsTextItem->x() + graphicsTextItem->boundingRect().width();
1542 0 : int endOfGrid = getWidth();
1543 0 : if (endOfText <= endOfGrid) {
1544 0 : scene()->addItem(graphicsTextItem);
1545 :
1546 0 : std::pair<QGraphicsItem*, int> pairMeasureText = std::make_pair(graphicsTextItem, row);
1547 0 : _metaRows.push_back(pairMeasureText);
1548 : }
1549 0 : }
1550 :
1551 : //---------------------------------------------------------
1552 : // Timeline::getMetaRow
1553 : //---------------------------------------------------------
1554 :
1555 0 : unsigned Timeline::getMetaRow(QString targetText)
1556 : {
1557 0 : if (_collapsedMeta) {
1558 0 : if (targetText == muse::qtrc("notation/timeline", "Measures")) {
1559 0 : return 1;
1560 : } else {
1561 0 : return 0;
1562 : }
1563 : }
1564 0 : int row = 0;
1565 0 : for (auto it = _metas.begin(); it != _metas.end(); ++it) {
1566 0 : std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
1567 0 : QString metaText = std::get<0>(meta);
1568 0 : bool visible = std::get<2>(meta);
1569 0 : if (metaText == targetText && visible) {
1570 0 : break;
1571 0 : } else if (!visible) {
1572 0 : continue;
1573 : }
1574 0 : row++;
1575 0 : }
1576 0 : return row;
1577 : }
1578 :
1579 : //---------------------------------------------------------
1580 : // Timeline::addMetaValue
1581 : //---------------------------------------------------------
1582 :
1583 0 : bool Timeline::addMetaValue(int x, int pos, QString metaText, int row, ElementType elementType, EngravingItem* element, Segment* seg,
1584 : Measure* measure, QString tooltip)
1585 : {
1586 0 : TRACEFUNC;
1587 :
1588 0 : QGraphicsTextItem* graphicsTextItem = new QGraphicsTextItem(metaText);
1589 0 : qreal textWidth = graphicsTextItem->boundingRect().width();
1590 :
1591 0 : QGraphicsPixmapItem* graphicsPixmapItem = nullptr;
1592 :
1593 : std::map<QString, BarLineType> barLineTypes = {
1594 0 : { BarLine::translatedUserTypeName(BarLineType::START_REPEAT), BarLineType::START_REPEAT },
1595 0 : { BarLine::translatedUserTypeName(BarLineType::END_REPEAT), BarLineType::END_REPEAT },
1596 0 : { BarLine::translatedUserTypeName(BarLineType::END), BarLineType::END },
1597 0 : { BarLine::translatedUserTypeName(BarLineType::DOUBLE), BarLineType::DOUBLE },
1598 0 : { BarLine::translatedUserTypeName(BarLineType::REVERSE_END), BarLineType::REVERSE_END },
1599 0 : { BarLine::translatedUserTypeName(BarLineType::HEAVY), BarLineType::HEAVY },
1600 0 : { BarLine::translatedUserTypeName(BarLineType::DOUBLE_HEAVY), BarLineType::DOUBLE_HEAVY },
1601 0 : };
1602 :
1603 0 : BarLineType barLineType = barLineTypes[metaText];
1604 :
1605 0 : if (_isBarline) {
1606 0 : graphicsPixmapItem = new QGraphicsPixmapItem(*_barlines[barLineType]);
1607 : }
1608 :
1609 0 : if (graphicsPixmapItem) {
1610 0 : textWidth = 10;
1611 0 : if (textWidth > _gridWidth) {
1612 0 : textWidth = _gridWidth;
1613 0 : if (barLineType == BarLineType::END_REPEAT && std::get<4>(_repeatInfo)) {
1614 0 : textWidth /= 2;
1615 : }
1616 : }
1617 : }
1618 :
1619 0 : if (textWidth + x > getWidth()) {
1620 0 : textWidth = getWidth() - x;
1621 : }
1622 :
1623 : // Adjust x for end repeats
1624 0 : if ((barLineType == BarLineType::END_REPEAT
1625 0 : || barLineType == BarLineType::END
1626 0 : || barLineType == BarLineType::DOUBLE
1627 0 : || barLineType == BarLineType::REVERSE_END
1628 0 : || barLineType == BarLineType::HEAVY
1629 0 : || barLineType == BarLineType::DOUBLE_HEAVY
1630 0 : || std::get<2>(_repeatInfo))
1631 0 : && !_collapsedMeta) {
1632 0 : if (std::get<0>(_repeatInfo) > 0) {
1633 0 : x = pos + _gridWidth - std::get<1>(_repeatInfo) + std::get<0>(_repeatInfo) * _spacing;
1634 : } else {
1635 0 : x = pos + _gridWidth - textWidth;
1636 0 : std::get<1>(_repeatInfo) = textWidth;
1637 : }
1638 : // Check if extending past left side
1639 0 : if (x < 0) {
1640 0 : textWidth = textWidth + x;
1641 0 : x = 0;
1642 : }
1643 : }
1644 :
1645 : // Return if past width
1646 0 : if (x >= getWidth()) {
1647 0 : return false;
1648 : }
1649 :
1650 : QGraphicsItem* itemToAdd;
1651 0 : if (graphicsPixmapItem) {
1652 : // Exact values required for repeat pixmap to work visually
1653 0 : if (textWidth != 10) {
1654 0 : graphicsPixmapItem = new QGraphicsPixmapItem();
1655 : }
1656 0 : if (barLineType == BarLineType::START_REPEAT) {
1657 0 : std::get<4>(_repeatInfo) = true;
1658 : }
1659 0 : graphicsPixmapItem->setX(x + 2);
1660 0 : graphicsPixmapItem->setY(_gridHeight * row + verticalScrollBar()->value() + 3);
1661 0 : itemToAdd = graphicsPixmapItem;
1662 0 : } else if (metaText == "\uE047" || metaText == "\uE048") {
1663 0 : graphicsTextItem->setX(x);
1664 0 : graphicsTextItem->setY(_gridHeight * row + verticalScrollBar()->value() - 2);
1665 0 : itemToAdd = graphicsTextItem;
1666 0 : } else if (row == 0) {
1667 0 : graphicsTextItem->setX(x);
1668 0 : graphicsTextItem->setY(_gridHeight * row + verticalScrollBar()->value() - 6);
1669 0 : itemToAdd = graphicsTextItem;
1670 : } else {
1671 0 : graphicsTextItem->setX(x);
1672 0 : graphicsTextItem->setY(_gridHeight * row + verticalScrollBar()->value() - 1);
1673 0 : itemToAdd = graphicsTextItem;
1674 : }
1675 :
1676 0 : QFontMetrics f(QApplication::font());
1677 0 : QString partName = f.elidedText(graphicsTextItem->toPlainText(),
1678 : Qt::ElideRight,
1679 0 : textWidth);
1680 :
1681 : // Set tool tip if elided
1682 0 : if (tooltip != "") {
1683 0 : graphicsTextItem->setToolTip(tooltip);
1684 0 : } else if (partName != metaText) {
1685 0 : graphicsTextItem->setToolTip(graphicsTextItem->toPlainText());
1686 : }
1687 0 : graphicsTextItem->setPlainText(partName);
1688 :
1689 : // Make text fit within rectangle
1690 0 : while (graphicsTextItem->boundingRect().width() > textWidth
1691 0 : && graphicsTextItem->toPlainText() != "") {
1692 0 : QString text = graphicsTextItem->toPlainText();
1693 0 : text.chop(1);
1694 0 : graphicsTextItem->setPlainText(text);
1695 0 : }
1696 :
1697 : QGraphicsRectItem* graphicsRectItem = new QGraphicsRectItem(x,
1698 0 : _gridHeight * row + verticalScrollBar()->value(),
1699 : textWidth,
1700 0 : _gridHeight);
1701 0 : if (tooltip != "") {
1702 0 : graphicsRectItem->setToolTip(tooltip);
1703 0 : } else if (partName != metaText || graphicsPixmapItem) {
1704 0 : graphicsRectItem->setToolTip(metaText);
1705 : }
1706 :
1707 0 : setMetaData(graphicsRectItem, -1, elementType, measure, true, element, itemToAdd, seg);
1708 0 : setMetaData(itemToAdd, -1, elementType, measure, true, element, graphicsRectItem, seg);
1709 :
1710 0 : graphicsRectItem->setData(keyItemType, QVariant::fromValue(ItemType::TYPE_META));
1711 0 : itemToAdd->setData(keyItemType, QVariant::fromValue(ItemType::TYPE_META));
1712 :
1713 0 : graphicsRectItem->setZValue(_globalZValue);
1714 0 : itemToAdd->setZValue(_globalZValue);
1715 :
1716 0 : graphicsRectItem->setPen(QPen(activeTheme().metaValuePenColor));
1717 0 : graphicsRectItem->setBrush(QBrush(activeTheme().metaValueBrushColor));
1718 :
1719 0 : scene()->addItem(graphicsRectItem);
1720 0 : scene()->addItem(itemToAdd);
1721 :
1722 0 : std::pair<QGraphicsItem*, int> pairTimeRect = std::make_pair(graphicsRectItem, row);
1723 0 : std::pair<QGraphicsItem*, int> pairTimeText = std::make_pair(itemToAdd, row);
1724 0 : _metaRows.push_back(pairTimeRect);
1725 0 : _metaRows.push_back(pairTimeText);
1726 :
1727 0 : if (barLineType == BarLineType::END_REPEAT) {
1728 0 : std::get<0>(_repeatInfo)++;
1729 : }
1730 :
1731 0 : return true;
1732 0 : }
1733 :
1734 : //---------------------------------------------------------
1735 : // Timeline::setMetaData
1736 : //---------------------------------------------------------
1737 :
1738 0 : void Timeline::setMetaData(QGraphicsItem* gi, int staff, ElementType et, Measure* m, bool full_measure, EngravingItem* e,
1739 : QGraphicsItem* pairItem,
1740 : Segment* seg)
1741 : {
1742 : // full_measure true for meta values
1743 : // pr is null for grid items, set for meta values
1744 : // seg is set if key meta
1745 0 : gi->setData(0, QVariant::fromValue<int>(staff));
1746 0 : gi->setData(1, QVariant::fromValue<ElementType>(et));
1747 0 : gi->setData(2, QVariant::fromValue<void*>(m));
1748 0 : gi->setData(3, QVariant::fromValue<bool>(full_measure));
1749 0 : gi->setData(4, QVariant::fromValue<void*>(e));
1750 0 : gi->setData(5, QVariant::fromValue<void*>(pairItem));
1751 0 : gi->setData(6, QVariant::fromValue<void*>(seg));
1752 0 : }
1753 :
1754 : //---------------------------------------------------------
1755 : // Timeline::getWidth
1756 : //---------------------------------------------------------
1757 :
1758 0 : int Timeline::getWidth() const
1759 : {
1760 0 : if (score()) {
1761 0 : return static_cast<int>(score()->nmeasures()) * _gridWidth;
1762 : } else {
1763 0 : return 0;
1764 : }
1765 : }
1766 :
1767 : //---------------------------------------------------------
1768 : // Timeline::getHeight
1769 : //---------------------------------------------------------
1770 :
1771 0 : int Timeline::getHeight() const
1772 : {
1773 0 : if (score()) {
1774 0 : return (nstaves() + static_cast<int>(nmetas())) * _gridHeight + 3;
1775 : } else {
1776 0 : return 0;
1777 : }
1778 : }
1779 :
1780 : //---------------------------------------------------------
1781 : // Timeline::correctStave
1782 : //---------------------------------------------------------
1783 :
1784 0 : staff_idx_t Timeline::correctStave(staff_idx_t stave)
1785 : {
1786 : // Find correct stave (skipping hidden staves)
1787 0 : const std::vector<Staff*>& list = score()->staves();
1788 0 : size_t count = 0;
1789 0 : while (stave >= count) {
1790 0 : if (count >= list.size()) {
1791 0 : count = list.size() - 1;
1792 0 : return count;
1793 : }
1794 0 : if (!list.at(count)->show()) {
1795 0 : stave++;
1796 : }
1797 0 : count++;
1798 : }
1799 0 : return stave;
1800 : }
1801 :
1802 : //---------------------------------------------------------
1803 : // Timeline::correctPart
1804 : //---------------------------------------------------------
1805 :
1806 0 : int Timeline::correctPart(staff_idx_t stave)
1807 : {
1808 : // Find correct stave (skipping hidden staves)
1809 0 : const std::vector<Staff*>& list = score()->staves();
1810 0 : staff_idx_t count = correctStave(stave);
1811 0 : return getParts().indexOf(list.at(count)->part());
1812 : }
1813 :
1814 : //---------------------------------------------------------
1815 : // Timeline::getParts
1816 : //---------------------------------------------------------
1817 :
1818 0 : QList<Part*> Timeline::getParts()
1819 : {
1820 0 : const std::vector<Part*>& realPartList = score()->parts();
1821 0 : QList<Part*> partList;
1822 0 : for (Part* p : realPartList) {
1823 0 : for (size_t i = 0; i < p->nstaves(); i++) {
1824 0 : partList.append(p);
1825 : }
1826 : }
1827 :
1828 0 : return partList;
1829 0 : }
1830 :
1831 : //---------------------------------------------------------
1832 : // clearScene
1833 : //---------------------------------------------------------
1834 :
1835 0 : void Timeline::clearScene()
1836 : {
1837 0 : scene()->clear();
1838 :
1839 : // clear pointers to scene items, they have been deleted by clear()
1840 0 : nonVisiblePathItem = nullptr;
1841 0 : visiblePathItem = nullptr;
1842 0 : selectionItem = nullptr;
1843 0 : }
1844 :
1845 : //---------------------------------------------------------
1846 : // Timeline::changeSelection
1847 : //---------------------------------------------------------
1848 :
1849 0 : void Timeline::changeSelection(SelState)
1850 : {
1851 0 : TRACEFUNC;
1852 :
1853 0 : scene()->blockSignals(true);
1854 0 : scene()->clearSelection();
1855 :
1856 0 : QRectF selectionRect = _selectionPath.boundingRect();
1857 0 : if (selectionRect == QRectF()) {
1858 0 : return;
1859 : }
1860 :
1861 0 : int nmeta = nmetas();
1862 :
1863 : // Get borders of the current viewport
1864 0 : int leftBorder = horizontalScrollBar()->value();
1865 0 : int rightBorder = horizontalScrollBar()->value() + viewport()->width();
1866 0 : int topBorder = verticalScrollBar()->value() + nmeta * _gridHeight;
1867 0 : int bottomBorder = verticalScrollBar()->value() + viewport()->height();
1868 :
1869 0 : bool selectionExtendsUp = false, selectionExtendsLeft = false;
1870 0 : bool selectionExtendsRight = false, selectionExtendsDown = false;
1871 :
1872 : // Figure out which directions the selection extends
1873 0 : if (selectionRect.top() < topBorder) {
1874 0 : selectionExtendsUp = true;
1875 : }
1876 0 : if (selectionRect.left() < leftBorder - 1) {
1877 0 : selectionExtendsLeft = true;
1878 : }
1879 0 : if (selectionRect.right() > rightBorder) {
1880 0 : selectionExtendsRight = true;
1881 : }
1882 0 : if (selectionRect.bottom() > bottomBorder) {
1883 0 : selectionExtendsDown = true;
1884 : }
1885 :
1886 0 : if (selectionExtendsDown
1887 0 : && _oldSelectionRect.bottom() != selectionRect.bottom()
1888 0 : && !_metaValue) {
1889 0 : int newScrollbarValue = int(verticalScrollBar()->value() + selectionRect.bottom() - bottomBorder);
1890 0 : verticalScrollBar()->setValue(newScrollbarValue);
1891 0 : } else if (selectionExtendsUp
1892 0 : && !selectionExtendsDown
1893 0 : && _oldSelectionRect.bottom() != selectionRect.bottom()
1894 0 : && !_metaValue
1895 0 : && _oldSelectionRect.contains(selectionRect)) {
1896 0 : int newScrollbarValue = int(verticalScrollBar()->value() + selectionRect.bottom() - bottomBorder);
1897 0 : verticalScrollBar()->setValue(newScrollbarValue);
1898 : }
1899 :
1900 0 : if (selectionExtendsRight
1901 0 : && _oldSelectionRect.right() != selectionRect.right()) {
1902 0 : int newScrollbarValue = int(horizontalScrollBar()->value() + selectionRect.right() - rightBorder);
1903 0 : horizontalScrollBar()->setValue(newScrollbarValue);
1904 : }
1905 0 : if (selectionExtendsUp
1906 0 : && _oldSelectionRect.top() != selectionRect.top()
1907 0 : && !_metaValue) {
1908 0 : int newScrollbarValue = int(selectionRect.top()) - nmeta * _gridHeight;
1909 0 : verticalScrollBar()->setValue(newScrollbarValue);
1910 : }
1911 0 : if (selectionExtendsLeft
1912 0 : && _oldSelectionRect.left() != selectionRect.left()) {
1913 0 : int newScrollbarValue = int(selectionRect.left());
1914 0 : horizontalScrollBar()->setValue(newScrollbarValue);
1915 : }
1916 :
1917 0 : if (selectionExtendsLeft
1918 0 : && !selectionExtendsRight
1919 0 : && _oldSelectionRect.right() != selectionRect.right()
1920 0 : && _oldSelectionRect.contains(selectionRect)) {
1921 0 : int newScrollbarValue = int(horizontalScrollBar()->value() + selectionRect.right() - rightBorder);
1922 0 : horizontalScrollBar()->setValue(newScrollbarValue);
1923 : }
1924 0 : if (selectionExtendsRight
1925 0 : && !selectionExtendsLeft
1926 0 : && _oldSelectionRect.left() != selectionRect.left()
1927 0 : && _oldSelectionRect.contains(selectionRect)) {
1928 0 : int newScrollbarValue = int(selectionRect.left());
1929 0 : horizontalScrollBar()->setValue(newScrollbarValue);
1930 : }
1931 :
1932 0 : if (selectionExtendsDown
1933 0 : && !selectionExtendsUp
1934 0 : && _oldSelectionRect.top() != selectionRect.top()
1935 0 : && !_metaValue
1936 0 : && _oldSelectionRect.contains(selectionRect)) {
1937 0 : int newScrollbarValue = int(selectionRect.top()) - nmeta * _gridHeight;
1938 0 : verticalScrollBar()->setValue(newScrollbarValue);
1939 : }
1940 :
1941 0 : _oldSelectionRect = selectionRect;
1942 :
1943 0 : _metaValue = false;
1944 0 : scene()->blockSignals(false);
1945 0 : }
1946 :
1947 : //---------------------------------------------------------
1948 : // Timeline::drawSelection
1949 : //---------------------------------------------------------
1950 :
1951 0 : void Timeline::drawSelection()
1952 : {
1953 0 : if (!score()) {
1954 0 : return;
1955 : }
1956 :
1957 0 : TRACEFUNC;
1958 :
1959 0 : _selectionPath = QPainterPath();
1960 0 : _selectionPath.setFillRule(Qt::WindingFill);
1961 :
1962 0 : std::set<std::tuple<Measure*, int, ElementType> > metaLabelsSet;
1963 :
1964 0 : INotationSelectionPtr selection = interaction()->selection();
1965 :
1966 0 : for (EngravingItem* element : selection->elements()) {
1967 0 : if (element->tick() == Fraction(-1, 1)) {
1968 0 : continue;
1969 : } else {
1970 0 : switch (element->type()) {
1971 0 : case ElementType::INSTRUMENT_NAME:
1972 : case ElementType::VBOX:
1973 : case ElementType::HBOX:
1974 : case ElementType::TEXT:
1975 : case ElementType::TIE_SEGMENT:
1976 : case ElementType::SLUR_SEGMENT:
1977 : case ElementType::TIE:
1978 : case ElementType::SLUR:
1979 : case ElementType::HAMMER_ON_PULL_OFF:
1980 0 : continue;
1981 : break;
1982 0 : default: break;
1983 : }
1984 : }
1985 :
1986 : int staffIdx;
1987 0 : Fraction tick = element->tick();
1988 0 : Measure* measure = score()->tick2measure(tick);
1989 0 : staffIdx = static_cast<int>(element->staffIdx());
1990 0 : if (numToStaff(staffIdx) && !numToStaff(staffIdx)->show()) {
1991 0 : continue;
1992 : }
1993 :
1994 0 : if ((element->isTempoText()
1995 0 : || element->isKeySig()
1996 0 : || element->isTimeSig()
1997 0 : || element->isRehearsalMark()
1998 0 : || element->isJump()
1999 0 : || element->isMarker())
2000 0 : && !element->generated()) {
2001 0 : staffIdx = -1;
2002 : }
2003 :
2004 0 : if (element->isBarLine()) {
2005 0 : staffIdx = -1;
2006 0 : BarLine* barline = toBarLine(element);
2007 0 : if (barline
2008 0 : && (barline->barLineType() == BarLineType::END_REPEAT
2009 0 : || barline->barLineType() == BarLineType::END
2010 0 : || barline->barLineType() == BarLineType::DOUBLE
2011 0 : || barline->barLineType() == BarLineType::REVERSE_END
2012 0 : || barline->barLineType() == BarLineType::HEAVY
2013 0 : || barline->barLineType() == BarLineType::DOUBLE_HEAVY)
2014 0 : && measure != score()->lastMeasure()) {
2015 0 : if (measure->prevMeasure()) {
2016 0 : measure = measure->prevMeasure();
2017 : }
2018 : }
2019 : }
2020 :
2021 : // element->type() for meta rows, invalid for everything else
2022 0 : ElementType elementType = (staffIdx == -1) ? element->type() : ElementType::INVALID;
2023 :
2024 : // If has a multi measure rest, find the count and add each measure to it
2025 : // ws: If style flag Sid::createMultiMeasureRests is not set, then
2026 : // measure->mmRest() is not valid
2027 :
2028 0 : if (measure->mmRest() && measure->score()->style().styleB(Sid::createMultiMeasureRests)) {
2029 0 : int mmrestCount = measure->mmRest()->mmRestCount();
2030 0 : Measure* tmpMeasure = measure;
2031 0 : for (int mmrestMeasure = 0; mmrestMeasure < mmrestCount; mmrestMeasure++) {
2032 0 : std::tuple<Measure*, int, ElementType> tmp(tmpMeasure, staffIdx, elementType);
2033 0 : metaLabelsSet.insert(tmp);
2034 0 : tmpMeasure = tmpMeasure->nextMeasure();
2035 : }
2036 : } else {
2037 0 : std::tuple<Measure*, int, ElementType> tmp(measure, staffIdx, elementType);
2038 0 : metaLabelsSet.insert(tmp);
2039 : }
2040 : }
2041 :
2042 0 : const QList<QGraphicsItem*> graphicsItemList = scene()->items();
2043 0 : for (QGraphicsItem* graphicsItem : graphicsItemList) {
2044 0 : int stave = graphicsItem->data(0).value<int>();
2045 0 : ElementType elementType = graphicsItem->data(1).value<ElementType>();
2046 0 : Measure* measure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());
2047 :
2048 0 : std::tuple<Measure*, int, ElementType> targetTuple(measure, stave, elementType);
2049 0 : std::set<std::tuple<Measure*, int, ElementType> >::iterator it;
2050 0 : it = metaLabelsSet.find(targetTuple);
2051 :
2052 0 : if (stave == -1 && it != metaLabelsSet.end()) {
2053 : //Make sure the element is correct
2054 0 : const std::vector<EngravingItem*>& elementList = interaction()->selection()->elements();
2055 0 : EngravingItem* targetElement = static_cast<EngravingItem*>(graphicsItem->data(4).value<void*>());
2056 0 : Segment* seg = static_cast<Segment*>(graphicsItem->data(6).value<void*>());
2057 :
2058 0 : if (targetElement) {
2059 0 : for (EngravingItem* element : elementList) {
2060 0 : if (element == targetElement) {
2061 0 : QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
2062 0 : if (graphicsRectItem) {
2063 0 : graphicsRectItem->setBrush(QBrush(activeTheme().selectionColor));
2064 : }
2065 : }
2066 : }
2067 0 : } else if (seg) {
2068 0 : for (EngravingItem* element : elementList) {
2069 0 : QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
2070 0 : if (graphicsRectItem) {
2071 0 : for (size_t track = 0; track < score()->nstaves() * VOICES; track++) {
2072 0 : if (element == seg->element(track)) {
2073 0 : graphicsRectItem->setBrush(QBrush(activeTheme().selectionColor));
2074 : }
2075 : }
2076 : }
2077 : }
2078 : } else {
2079 0 : QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
2080 0 : if (graphicsRectItem) {
2081 0 : graphicsRectItem->setBrush(QBrush(activeTheme().selectionColor));
2082 : }
2083 : }
2084 : }
2085 : // Change color from gray to only blue
2086 0 : else if (it != metaLabelsSet.end()) {
2087 0 : QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
2088 0 : graphicsRectItem->setBrush(QBrush(QColor(graphicsRectItem->brush().color().red(),
2089 0 : graphicsRectItem->brush().color().green(),
2090 0 : 255)));
2091 0 : _selectionPath.addRect(graphicsRectItem->rect());
2092 : } else {
2093 : // Ensure unselected measures are not marked selected
2094 0 : QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
2095 0 : if (graphicsRectItem && graphicsRectItem->data(keyItemType).value<ItemType>() == ItemType::TYPE_MEASURE) {
2096 0 : graphicsRectItem->setBrush(QBrush(colorBox(graphicsRectItem)));
2097 : }
2098 : }
2099 : }
2100 :
2101 0 : if (selectionItem) {
2102 0 : scene()->removeItem(selectionItem);
2103 0 : delete selectionItem;
2104 0 : selectionItem = nullptr;
2105 : }
2106 :
2107 0 : selectionItem = new QGraphicsPathItem(_selectionPath.simplified());
2108 0 : if (selection->isRange()) {
2109 0 : selectionItem->setPen(QPen(QColor(0, 0, 255), 3));
2110 : } else {
2111 0 : selectionItem->setPen(QPen(QColor(0, 0, 0), 1));
2112 : }
2113 :
2114 0 : selectionItem->setBrush(Qt::NoBrush);
2115 0 : selectionItem->setZValue(-1);
2116 0 : scene()->addItem(selectionItem);
2117 :
2118 0 : if (std::get<0>(_oldHoverInfo)) {
2119 0 : std::get<0>(_oldHoverInfo) = nullptr;
2120 0 : std::get<1>(_oldHoverInfo) = -1;
2121 : }
2122 0 : }
2123 :
2124 : //---------------------------------------------------------
2125 : // Timeline::mousePressEvent
2126 : //---------------------------------------------------------
2127 :
2128 0 : void Timeline::mousePressEvent(QMouseEvent* event)
2129 : {
2130 0 : if (!score()) {
2131 0 : return;
2132 : }
2133 :
2134 0 : if (event->button() == Qt::RightButton) {
2135 0 : return;
2136 : }
2137 :
2138 0 : TRACEFUNC;
2139 :
2140 : // Set as clicked
2141 0 : _mousePressed = true;
2142 0 : scene()->clearSelection();
2143 :
2144 0 : QPointF scenePt = mapToScene(event->pos());
2145 : // Set as old location
2146 0 : _oldLoc = QPoint(int(scenePt.x()), int(scenePt.y()));
2147 0 : QList<QGraphicsItem*> graphicsItemList = scene()->items(scenePt);
2148 : // Find highest z value for rect
2149 0 : int maxZValue = -4;
2150 0 : QGraphicsItem* currGraphicsItem = nullptr;
2151 0 : for (QGraphicsItem* graphicsItem : graphicsItemList) {
2152 0 : QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
2153 0 : if (graphicsRectItem && graphicsItem->zValue() > maxZValue) {
2154 0 : currGraphicsItem = graphicsItem;
2155 0 : maxZValue = graphicsItem->zValue();
2156 : }
2157 : }
2158 0 : if (currGraphicsItem) {
2159 0 : int stave = currGraphicsItem->data(0).value<int>();
2160 0 : Measure* currMeasure = static_cast<Measure*>(currGraphicsItem->data(2).value<void*>());
2161 0 : if (numToStaff(stave) && !numToStaff(stave)->show()) {
2162 0 : return;
2163 : }
2164 :
2165 0 : if (!currMeasure) {
2166 0 : int nmeta = nmetas();
2167 0 : int bottomOfMeta = nmeta * _gridHeight + verticalScrollBar()->value();
2168 :
2169 : // Handle measure box clicks
2170 0 : if (scenePt.y() > (nmeta - 1) * _gridHeight + verticalScrollBar()->value()
2171 0 : && scenePt.y() < bottomOfMeta) {
2172 0 : QRectF tmp(scenePt.x(), 0, 3, nmeta * _gridHeight + nstaves() * _gridHeight);
2173 0 : QList<QGraphicsItem*> gl = scene()->items(tmp);
2174 0 : Measure* measure = nullptr;
2175 :
2176 0 : for (QGraphicsItem* graphicsItem : gl) {
2177 0 : measure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());
2178 : //-3 z value is the grid square values
2179 0 : if (graphicsItem->zValue() == -3 && measure) {
2180 0 : break;
2181 : }
2182 : }
2183 :
2184 0 : if (measure) {
2185 0 : interaction()->showItem(measure);
2186 : }
2187 0 : }
2188 0 : if (scenePt.y() < bottomOfMeta) {
2189 0 : return;
2190 : }
2191 :
2192 0 : QList<QGraphicsItem*> gl = items(event->pos());
2193 0 : for (QGraphicsItem* graphicsItem : gl) {
2194 0 : currMeasure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());
2195 0 : stave = graphicsItem->data(0).value<int>();
2196 0 : if (currMeasure) {
2197 0 : break;
2198 : }
2199 : }
2200 0 : if (!currMeasure) {
2201 0 : interaction()->clearSelection();
2202 0 : return;
2203 : }
2204 0 : }
2205 :
2206 0 : bool metaValueClicked = currGraphicsItem->data(3).value<bool>();
2207 :
2208 0 : scene()->clearSelection();
2209 0 : if (metaValueClicked) {
2210 0 : _metaValue = true;
2211 0 : _oldSelectionRect = QRect();
2212 :
2213 0 : interaction()->showItem(currMeasure, 0);
2214 0 : verticalScrollBar()->setValue(0);
2215 :
2216 0 : Segment* seg = static_cast<Segment*>(currGraphicsItem->data(6).value<void*>());
2217 :
2218 0 : if (seg) {
2219 0 : std::vector<EngravingItem*> elements;
2220 :
2221 0 : for (size_t track = 0; track < score()->nstaves() * VOICES; track++) {
2222 0 : EngravingItem* element = seg->element(track);
2223 0 : if (element) {
2224 0 : elements.push_back(element);
2225 : }
2226 : }
2227 :
2228 0 : if (elements.empty()) {
2229 0 : interaction()->clearSelection();
2230 : } else {
2231 0 : interaction()->select(elements);
2232 : }
2233 0 : } else {
2234 : // Also select the elements that they correspond to
2235 0 : ElementType elementType = currGraphicsItem->data(1).value<ElementType>();
2236 0 : SegmentType segmentType = SegmentType::Invalid;
2237 0 : if (elementType == ElementType::KEYSIG) {
2238 0 : segmentType = SegmentType::KeySig;
2239 0 : } else if (elementType == ElementType::TIMESIG) {
2240 0 : segmentType = SegmentType::TimeSig;
2241 : }
2242 :
2243 0 : if (segmentType != SegmentType::Invalid) {
2244 0 : Segment* currSeg = currMeasure->first();
2245 0 : for (; currSeg && currSeg->segmentType() != segmentType; currSeg = currSeg->next()) {
2246 : }
2247 0 : if (currSeg) {
2248 0 : std::vector<EngravingItem*> elements;
2249 :
2250 0 : for (size_t j = 0; j < score()->nstaves(); j++) {
2251 0 : EngravingItem* element = currSeg->firstElementForNavigation(j);
2252 0 : if (element) {
2253 0 : elements.push_back(element);
2254 : }
2255 : }
2256 :
2257 0 : if (elements.empty()) {
2258 0 : interaction()->clearSelection();
2259 : } else {
2260 0 : interaction()->select(elements);
2261 : }
2262 0 : }
2263 : } else {
2264 : // Select just the element for tempo_text
2265 0 : EngravingItem* element = static_cast<EngravingItem*>(currGraphicsItem->data(4).value<void*>());
2266 0 : if (element) {
2267 0 : interaction()->select({ element });
2268 0 : } else if (currMeasure) {
2269 0 : interaction()->select({ currMeasure });
2270 : } else {
2271 0 : interaction()->clearSelection();
2272 : }
2273 : }
2274 : }
2275 : } else {
2276 : // Handle cell clicks
2277 0 : if (event->modifiers() == Qt::ShiftModifier) {
2278 0 : if (currMeasure->mmRest()) {
2279 0 : currMeasure = currMeasure->mmRest();
2280 0 : } else if (!currMeasure->isMMRest()) {
2281 0 : currMeasure = currMeasure->prevMeasureMM();
2282 : }
2283 :
2284 0 : if (currMeasure) {
2285 0 : interaction()->select({ currMeasure }, SelectType::RANGE, stave);
2286 : }
2287 0 : } else if (event->modifiers() == Qt::ControlModifier) {
2288 0 : if (interaction()->selection()->isNone()) {
2289 0 : if (currMeasure->mmRest()) {
2290 0 : currMeasure = currMeasure->mmRest();
2291 0 : } else if (!currMeasure->isMMRest()) {
2292 0 : currMeasure = currMeasure->prevMeasureMM();
2293 : }
2294 :
2295 0 : if (currMeasure) {
2296 0 : interaction()->select({ currMeasure }, SelectType::RANGE, 0);
2297 0 : interaction()->select({ currMeasure }, SelectType::RANGE, score()->nstaves() - 1);
2298 : }
2299 : } else {
2300 0 : interaction()->clearSelection();
2301 : }
2302 : } else {
2303 0 : if (currMeasure->mmRest()) {
2304 0 : currMeasure = currMeasure->mmRest();
2305 0 : } else if (!currMeasure->isMMRest()) {
2306 0 : currMeasure = currMeasure->prevMeasureMM();
2307 : }
2308 :
2309 0 : if (currMeasure) {
2310 0 : interaction()->select({ currMeasure }, SelectType::SINGLE, stave);
2311 : }
2312 : }
2313 :
2314 0 : if (currMeasure) {
2315 0 : interaction()->showItem(currMeasure, stave);
2316 : }
2317 : }
2318 : } else {
2319 0 : interaction()->clearSelection();
2320 : }
2321 :
2322 0 : this->seekSelection();
2323 0 : }
2324 :
2325 0 : void Timeline::seekSelection()
2326 : {
2327 0 : const INotationSelectionPtr selection = interaction()->selection();
2328 0 : const std::vector<EngravingItem*>& elements = selection->elements();
2329 0 : if (elements.empty()) {
2330 0 : return;
2331 : }
2332 :
2333 0 : EngravingItem* elementToSeek = elements.front();
2334 0 : for (EngravingItem* element : elements) {
2335 0 : if (element->tick() > elementToSeek->tick()) {
2336 0 : continue;
2337 : }
2338 0 : elementToSeek = element;
2339 : }
2340 :
2341 0 : playbackController()->seekElement(elementToSeek);
2342 0 : }
2343 :
2344 : //---------------------------------------------------------
2345 : // Timeline::mouseMoveEvent
2346 : //---------------------------------------------------------
2347 :
2348 0 : void Timeline::mouseMoveEvent(QMouseEvent* event)
2349 : {
2350 0 : QPointF newLoc = mapToScene(event->pos());
2351 0 : if (!_mousePressed) {
2352 0 : if (cursorIsOn(event->pos()) == "meta") {
2353 0 : setCursor(Qt::ArrowCursor);
2354 0 : mouseOver(newLoc);
2355 0 : } else if (cursorIsOn(event->pos()) == "invalid") {
2356 0 : setCursor(Qt::ForbiddenCursor);
2357 : } else {
2358 0 : setCursor(Qt::ArrowCursor);
2359 : }
2360 :
2361 0 : emit moved(QPointF(-1, -1));
2362 0 : return;
2363 : }
2364 :
2365 0 : if (state == ViewState::NORMAL) {
2366 0 : if (event->modifiers() == Qt::ShiftModifier) {
2367 : // Slight wiggle room for selection (Same as score)
2368 0 : if (std::abs(newLoc.x() - _oldLoc.x()) > 2
2369 0 : || std::abs(newLoc.y() - _oldLoc.y()) > 2) {
2370 0 : interaction()->clearSelection();
2371 0 : updateGrid();
2372 0 : state = ViewState::LASSO;
2373 0 : _selectionBox = new QGraphicsRectItem();
2374 0 : _selectionBox->setRect(_oldLoc.x(), _oldLoc.y(), 0, 0);
2375 0 : _selectionBox->setPen(QPen(QColor(0, 0, 255), 2));
2376 0 : _selectionBox->setBrush(QBrush(QColor(0, 0, 255, 50)));
2377 0 : scene()->addItem(_selectionBox);
2378 : }
2379 : } else {
2380 0 : state = ViewState::DRAG;
2381 0 : setCursor(Qt::SizeAllCursor);
2382 : }
2383 : }
2384 :
2385 0 : if (state == ViewState::LASSO) {
2386 0 : QRect tmp = QRect((_oldLoc.x() < newLoc.x()) ? _oldLoc.x() : newLoc.x(),
2387 0 : (_oldLoc.y() < newLoc.y()) ? _oldLoc.y() : newLoc.y(),
2388 0 : std::abs(newLoc.x() - _oldLoc.x()),
2389 0 : std::abs(newLoc.y() - _oldLoc.y()));
2390 0 : _selectionBox->setRect(tmp);
2391 0 : } else if (state == ViewState::DRAG) {
2392 0 : int x_offset = int(_oldLoc.x()) - int(newLoc.x());
2393 0 : int yOffset = int(_oldLoc.y()) - int(newLoc.y());
2394 0 : horizontalScrollBar()->setValue(horizontalScrollBar()->value() + x_offset);
2395 0 : verticalScrollBar()->setValue(verticalScrollBar()->value() + yOffset);
2396 : }
2397 :
2398 0 : emit moved(QPointF(-1, -1));
2399 : }
2400 :
2401 : //---------------------------------------------------------
2402 : // Timeline::mouseReleaseEvent
2403 : //---------------------------------------------------------
2404 :
2405 0 : void Timeline::mouseReleaseEvent(QMouseEvent*)
2406 : {
2407 0 : _mousePressed = false;
2408 :
2409 0 : if (state == ViewState::LASSO) {
2410 0 : scene()->removeItem(_selectionBox);
2411 0 : interaction()->clearSelection();
2412 :
2413 : int width, height;
2414 0 : QPoint loc = mapFromScene(_selectionBox->rect().topLeft());
2415 0 : width = int(_selectionBox->rect().width());
2416 0 : height = int(_selectionBox->rect().height());
2417 :
2418 0 : QList<QGraphicsItem*> graphicsItemList = items(QRect(loc.x(), loc.y(), width, height));
2419 : // Find top left and bottom right to create selection
2420 0 : QGraphicsItem* tlGraphicsItem = nullptr;
2421 0 : QGraphicsItem* brGraphicsItem = nullptr;
2422 0 : for (QGraphicsItem* graphicsItem : graphicsItemList) {
2423 0 : Measure* currMeasure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());
2424 0 : if (!currMeasure) {
2425 0 : continue;
2426 : }
2427 0 : int stave = graphicsItem->data(0).value<int>();
2428 0 : if (stave == -1) {
2429 0 : continue;
2430 : }
2431 :
2432 0 : if (!tlGraphicsItem && !brGraphicsItem) {
2433 0 : tlGraphicsItem = graphicsItem;
2434 0 : brGraphicsItem = graphicsItem;
2435 0 : continue;
2436 : }
2437 :
2438 0 : if (graphicsItem->boundingRect().top() < tlGraphicsItem->boundingRect().top()) {
2439 0 : tlGraphicsItem = graphicsItem;
2440 : }
2441 0 : if (graphicsItem->boundingRect().left() < tlGraphicsItem->boundingRect().left()) {
2442 0 : tlGraphicsItem = graphicsItem;
2443 : }
2444 :
2445 0 : if (graphicsItem->boundingRect().bottom() > brGraphicsItem->boundingRect().bottom()) {
2446 0 : brGraphicsItem = graphicsItem;
2447 : }
2448 0 : if (graphicsItem->boundingRect().right() > brGraphicsItem->boundingRect().right()) {
2449 0 : brGraphicsItem = graphicsItem;
2450 : }
2451 : }
2452 :
2453 : // Select single tlGraphicsItem and then range brGraphicsItem
2454 0 : if (tlGraphicsItem && brGraphicsItem) {
2455 0 : Measure* tlMeasure = static_cast<Measure*>(tlGraphicsItem->data(2).value<void*>());
2456 0 : int tlStave = tlGraphicsItem->data(0).value<int>();
2457 0 : Measure* brMeasure = static_cast<Measure*>(brGraphicsItem->data(2).value<void*>());
2458 0 : int brStave = brGraphicsItem->data(0).value<int>();
2459 0 : if (tlMeasure && brMeasure) {
2460 : // Focus selection of mmRests here
2461 0 : if (tlMeasure->mmRest()) {
2462 0 : tlMeasure = tlMeasure->mmRest();
2463 0 : } else if (!tlMeasure->isMMRest()) {
2464 0 : tlMeasure = tlMeasure->prevMeasureMM();
2465 : }
2466 0 : if (brMeasure->mmRest()) {
2467 0 : brMeasure = brMeasure->mmRest();
2468 0 : } else if (!brMeasure->isMMRest()) {
2469 0 : brMeasure = brMeasure->prevMeasureMM();
2470 : }
2471 :
2472 0 : if (tlMeasure) {
2473 0 : interaction()->select({ tlMeasure }, SelectType::SINGLE, tlStave);
2474 : }
2475 :
2476 0 : if (brMeasure) {
2477 0 : interaction()->select({ brMeasure }, SelectType::RANGE, brStave);
2478 : }
2479 : }
2480 :
2481 0 : if (tlMeasure) {
2482 0 : interaction()->showItem(tlMeasure, tlStave);
2483 : }
2484 : }
2485 0 : } else if (state == ViewState::DRAG) {
2486 0 : setCursor(Qt::ArrowCursor);
2487 : }
2488 0 : state = ViewState::NORMAL;
2489 0 : }
2490 :
2491 : //---------------------------------------------------------
2492 : // Timeline::leaveEvent
2493 : //---------------------------------------------------------
2494 :
2495 0 : void Timeline::leaveEvent(QEvent*)
2496 : {
2497 0 : if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
2498 0 : QPointF p = mapToScene(mapFromGlobal(QCursor::pos()));
2499 0 : mouseOver(p);
2500 : }
2501 0 : }
2502 :
2503 : //---------------------------------------------------------
2504 : // Timeline::wheelEvent
2505 : //---------------------------------------------------------
2506 :
2507 0 : void Timeline::wheelEvent(QWheelEvent* event)
2508 : {
2509 0 : if (event->modifiers().testFlag(Qt::ControlModifier)) {
2510 0 : qreal originalCursorPos = mapToScene(mapFromGlobal(QCursor::pos())).x();
2511 0 : int originalScrollValue = horizontalScrollBar()->value();
2512 0 : qreal ratio = originalCursorPos / qreal(getWidth());
2513 :
2514 0 : if (event->angleDelta().y() > 0 && _gridWidth < _maxZoom) {
2515 0 : _gridWidth++;
2516 0 : updateGridFull();
2517 0 : } else if (event->angleDelta().y() < 0 && _gridWidth > _minZoom) {
2518 0 : _gridWidth--;
2519 0 : updateGridFull();
2520 : }
2521 :
2522 : // Attempt to keep mouse in original spot
2523 0 : qreal newPos = qreal(getWidth()) * ratio;
2524 0 : int offset = newPos - originalCursorPos;
2525 0 : horizontalScrollBar()->setValue(originalScrollValue + offset);
2526 0 : } else if (event->modifiers().testFlag(Qt::ShiftModifier)) {
2527 0 : qreal numOfSteps = qreal(event->angleDelta().y()) / 2;
2528 0 : horizontalScrollBar()->setValue(horizontalScrollBar()->value() - int(numOfSteps));
2529 : } else {
2530 0 : QGraphicsView::wheelEvent(event);
2531 : }
2532 0 : }
2533 :
2534 : //---------------------------------------------------------
2535 : // showEvent
2536 : //---------------------------------------------------------
2537 :
2538 0 : void Timeline::showEvent(QShowEvent* evt)
2539 : {
2540 0 : QGraphicsView::showEvent(evt);
2541 0 : if (!evt->spontaneous()) {
2542 0 : setNotation(m_notation);
2543 : }
2544 0 : }
2545 :
2546 : //---------------------------------------------------------
2547 : // changeEvent
2548 : //---------------------------------------------------------
2549 :
2550 0 : void Timeline::changeEvent(QEvent* event)
2551 : {
2552 0 : QGraphicsView::changeEvent(event);
2553 0 : if (event->type() == QEvent::LanguageChange) {
2554 0 : _metas.clear();
2555 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Tempo"), &Timeline::tempoMeta, true });
2556 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Time signature"), &Timeline::timeMeta, true });
2557 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Rehearsal mark"), &Timeline::rehearsalMeta, true });
2558 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Key signature"), &Timeline::keyMeta, true });
2559 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Barlines"), &Timeline::barlineMeta, true });
2560 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Jumps and markers"), &Timeline::jumpMarkerMeta, true });
2561 0 : _metas.push_back({ muse::qtrc("notation/timeline", "Measures"), &Timeline::measureMeta, true });
2562 :
2563 0 : updateGridFull();
2564 : }
2565 0 : }
2566 :
2567 : //---------------------------------------------------------
2568 : // Timeline::updateGrid
2569 : //---------------------------------------------------------
2570 :
2571 0 : void Timeline::updateGrid(int startMeasure, int endMeasure)
2572 : {
2573 0 : TRACEFUNC;
2574 :
2575 0 : if (score() && score()->firstMeasure()) {
2576 0 : drawGrid(static_cast<int>(nstaves()), static_cast<int>(score()->nmeasures()), startMeasure, endMeasure);
2577 0 : updateView();
2578 0 : drawSelection();
2579 0 : mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));
2580 0 : _rowNames->updateLabels(getLabels(), _gridHeight);
2581 : }
2582 0 : viewport()->update();
2583 0 : }
2584 :
2585 : //---------------------------------------------------------
2586 : // updateGridFromCmdState
2587 : //---------------------------------------------------------
2588 :
2589 0 : void Timeline::updateGridFromCmdState()
2590 : {
2591 0 : if (!score()) {
2592 0 : updateGridFull();
2593 0 : return;
2594 : }
2595 :
2596 0 : const CmdState& cState = score()->cmdState();
2597 :
2598 0 : const bool layoutChanged = cState.layoutRange();
2599 :
2600 0 : if (!layoutChanged) {
2601 0 : updateGridView();
2602 0 : return;
2603 : }
2604 :
2605 0 : const bool layoutAll = layoutChanged && (cState.startTick() < Fraction(0, 1) || cState.endTick() < Fraction(0, 1));
2606 :
2607 0 : const Measure* startMeasure = layoutAll ? nullptr : score()->tick2measure(cState.startTick());
2608 0 : const int startMeasureIndex = startMeasure ? startMeasure->measureIndex() : 0;
2609 :
2610 0 : const Measure* endMeasure = layoutAll ? nullptr : score()->tick2measure(cState.endTick());
2611 0 : const int endMeasureIndex = endMeasure ? (endMeasure->measureIndex() + 1) : static_cast<int>(score()->nmeasures());
2612 :
2613 0 : updateGrid(startMeasureIndex, endMeasureIndex);
2614 : }
2615 :
2616 : //---------------------------------------------------------
2617 : // Timeline::setNotation
2618 : //---------------------------------------------------------
2619 :
2620 0 : void Timeline::setNotation(INotationPtr notation)
2621 : {
2622 0 : m_notation = notation;
2623 :
2624 0 : clearScene();
2625 :
2626 0 : if (m_notation) {
2627 0 : drawGrid(nstaves(), static_cast<int>(score()->nmeasures()));
2628 0 : drawSelection();
2629 0 : changeSelection(SelState::NONE);
2630 0 : _rowNames->updateLabels(getLabels(), _gridHeight);
2631 : } else {
2632 : // Clear timeline if no score is present
2633 0 : if (_splitter && _splitter->count() > 0) {
2634 0 : TRowLabels* tRowLabels = static_cast<TRowLabels*>(_splitter->widget(0));
2635 0 : std::vector<std::pair<QString, bool> > noLabels;
2636 0 : tRowLabels->updateLabels(noLabels, 0);
2637 0 : }
2638 0 : _metaRows.clear();
2639 0 : setSceneRect(0, 0, 0, 0);
2640 : }
2641 0 : }
2642 :
2643 : //---------------------------------------------------------
2644 : // Timeline::updateView
2645 : //---------------------------------------------------------
2646 :
2647 0 : void Timeline::updateView()
2648 : {
2649 0 : if (!score()) {
2650 0 : return;
2651 : }
2652 :
2653 0 : TRACEFUNC;
2654 :
2655 : //! FIXME
2656 0 : RectF canvas; // = QRectF(_cv->matrix().inverted().mapRect(_cv->geometry()));
2657 :
2658 : // Find visible elements in timeline
2659 0 : QPainterPath visiblePainterPath = QPainterPath();
2660 0 : visiblePainterPath.setFillRule(Qt::WindingFill);
2661 :
2662 : // Find visible measures of score
2663 0 : int measureIndex = 0;
2664 0 : const int numMetas = nmetas();
2665 :
2666 0 : for (Measure* currMeasure = score()->firstMeasure(); currMeasure; currMeasure = currMeasure->nextMeasure(), ++measureIndex) {
2667 0 : System* system = currMeasure->system();
2668 :
2669 0 : if (currMeasure->mmRest() && score()->style().styleB(Sid::createMultiMeasureRests)) {
2670 : // Handle mmRests
2671 0 : Measure* mmrestMeasure = currMeasure->mmRest();
2672 0 : system = mmrestMeasure->system();
2673 0 : if (!system) {
2674 0 : measureIndex += currMeasure->mmRestCount();
2675 0 : continue;
2676 : }
2677 :
2678 : // Add all measures within mmRest to visibleItemsSet if mmRest_visible
2679 0 : for (; currMeasure != mmrestMeasure->mmRestLast(); currMeasure = currMeasure->nextMeasure(), ++measureIndex) {
2680 0 : for (size_t staff = 0; staff < score()->staves().size(); staff++) {
2681 0 : if (!score()->staff(staff)->show()) {
2682 0 : continue;
2683 : }
2684 0 : RectF staveRect = RectF(system->canvasBoundingRect().left(),
2685 : system->staffCanvasYpage(staff),
2686 : system->width(),
2687 0 : system->staff(staff)->bbox().height());
2688 0 : RectF showRect = mmrestMeasure->canvasBoundingRect().intersected(staveRect);
2689 :
2690 0 : if (canvas.intersects(showRect)) {
2691 0 : visiblePainterPath.addRect(getMeasureRect(measureIndex, static_cast<int>(staff), numMetas));
2692 : }
2693 : }
2694 : }
2695 :
2696 : // Handle last measure in mmRest
2697 0 : for (size_t staff = 0; staff < score()->staves().size(); staff++) {
2698 0 : if (!score()->staff(staff)->show()) {
2699 0 : continue;
2700 : }
2701 0 : RectF staveRect = RectF(system->canvasBoundingRect().left(),
2702 : system->staffCanvasYpage(staff),
2703 : system->width(),
2704 0 : system->staff(staff)->bbox().height());
2705 0 : RectF showRect = mmrestMeasure->canvasBoundingRect().intersected(staveRect);
2706 :
2707 0 : if (canvas.intersects(showRect)) {
2708 0 : visiblePainterPath.addRect(getMeasureRect(measureIndex, static_cast<int>(staff), numMetas));
2709 : }
2710 : }
2711 0 : continue;
2712 0 : }
2713 :
2714 0 : if (!system) {
2715 0 : continue;
2716 : }
2717 :
2718 0 : for (size_t staff = 0; staff < score()->staves().size(); staff++) {
2719 0 : if (!score()->staff(staff)->show()) {
2720 0 : continue;
2721 : }
2722 0 : RectF staveRect = RectF(system->canvasBoundingRect().left(),
2723 : system->staffCanvasYpage(staff),
2724 : system->width(),
2725 0 : system->staff(staff)->bbox().height());
2726 0 : RectF showRect = currMeasure->canvasBoundingRect().intersected(staveRect);
2727 :
2728 0 : if (canvas.intersects(showRect)) {
2729 0 : visiblePainterPath.addRect(getMeasureRect(measureIndex, static_cast<int>(staff), numMetas));
2730 : }
2731 : }
2732 : }
2733 :
2734 0 : if (nonVisiblePathItem) {
2735 0 : scene()->removeItem(nonVisiblePathItem);
2736 0 : delete nonVisiblePathItem;
2737 0 : nonVisiblePathItem = nullptr;
2738 : }
2739 0 : if (visiblePathItem) {
2740 0 : scene()->removeItem(visiblePathItem);
2741 0 : delete visiblePathItem;
2742 0 : visiblePathItem = nullptr;
2743 : }
2744 :
2745 0 : QPainterPath nonVisiblePainterPath = QPainterPath();
2746 0 : nonVisiblePainterPath.setFillRule(Qt::WindingFill);
2747 :
2748 0 : QRectF timelineRect = QRectF(0, 0, getWidth(), getHeight());
2749 0 : nonVisiblePainterPath.addRect(timelineRect);
2750 :
2751 0 : nonVisiblePainterPath = nonVisiblePainterPath.subtracted(visiblePainterPath);
2752 :
2753 0 : nonVisiblePathItem = new QGraphicsPathItem(nonVisiblePainterPath.simplified());
2754 :
2755 0 : QPen nonVisiblePen = QPen(activeTheme().nonVisiblePenColor);
2756 0 : QBrush nonVisibleBrush = QBrush(activeTheme().nonVisibleBrushColor);
2757 0 : nonVisiblePathItem->setPen(QPen(nonVisibleBrush.color()));
2758 0 : nonVisiblePathItem->setBrush(nonVisibleBrush);
2759 0 : nonVisiblePathItem->setZValue(-3);
2760 :
2761 0 : visiblePathItem = new QGraphicsPathItem(visiblePainterPath.simplified());
2762 0 : visiblePathItem->setPen(nonVisiblePen);
2763 0 : visiblePathItem->setBrush(Qt::NoBrush);
2764 0 : visiblePathItem->setZValue(-2);
2765 :
2766 0 : scene()->addItem(nonVisiblePathItem);
2767 0 : scene()->addItem(visiblePathItem);
2768 0 : }
2769 :
2770 : //---------------------------------------------------------
2771 : // Timeline::nstaves
2772 : //---------------------------------------------------------
2773 :
2774 0 : int Timeline::nstaves() const
2775 : {
2776 0 : return static_cast<int>(score()->staves().size());
2777 : }
2778 :
2779 : //---------------------------------------------------------
2780 : // Timeline::colorBox
2781 : //---------------------------------------------------------
2782 :
2783 0 : QColor Timeline::colorBox(QGraphicsRectItem* item)
2784 : {
2785 0 : Measure* measure = static_cast<Measure*>(item->data(2).value<void*>());
2786 0 : staff_idx_t stave = static_cast<staff_idx_t>(item->data(0).value<int>());
2787 0 : for (Segment* seg = measure->first(); seg; seg = seg->next()) {
2788 0 : if (!seg->isChordRestType()) {
2789 0 : continue;
2790 : }
2791 0 : for (track_idx_t track = stave * VOICES; track < stave * VOICES + VOICES; track++) {
2792 0 : ChordRest* chordRest = seg->cr(track);
2793 0 : if (chordRest) {
2794 0 : ElementType crt = chordRest->type();
2795 0 : if (crt == ElementType::CHORD || crt == ElementType::MEASURE_REPEAT) {
2796 0 : return activeTheme().colorBoxColor;
2797 : }
2798 : }
2799 : }
2800 : }
2801 0 : return QColor(224, 224, 224);
2802 : }
2803 :
2804 : //---------------------------------------------------------
2805 : // Timeline::getLabels
2806 : //---------------------------------------------------------
2807 :
2808 0 : std::vector<std::pair<QString, bool> > Timeline::getLabels()
2809 : {
2810 0 : if (!score()) {
2811 0 : std::vector<std::pair<QString, bool> > noLabels;
2812 0 : return noLabels;
2813 0 : }
2814 :
2815 0 : QList<Part*> partList = getParts();
2816 : // Transfer them into a vector of qstrings and then add the meta row names
2817 0 : std::vector<std::pair<QString, bool> > rowLabels;
2818 0 : if (_collapsedMeta) {
2819 0 : std::pair<QString, bool> first = std::make_pair("", true);
2820 0 : std::pair<QString, bool> second = std::make_pair(muse::qtrc("notation/timeline", "Measures"), true);
2821 0 : rowLabels.push_back(first);
2822 0 : rowLabels.push_back(second);
2823 0 : } else {
2824 0 : for (auto it = _metas.begin(); it != _metas.end(); ++it) {
2825 0 : std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
2826 0 : if (!std::get<2>(meta)) {
2827 0 : continue;
2828 : }
2829 0 : std::pair<QString, bool> metaLabel = std::make_pair(std::get<0>(meta), true);
2830 0 : rowLabels.push_back(metaLabel);
2831 0 : }
2832 : }
2833 :
2834 0 : for (int stave = 0; stave < partList.size(); stave++) {
2835 0 : QTextDocument doc;
2836 0 : QString partName = "";
2837 0 : doc.setHtml(partList.at(stave)->longName());
2838 0 : partName = doc.toPlainText();
2839 0 : if (partName.isEmpty()) { // No Long instrument name? Fall back to Part name
2840 0 : doc.setHtml(partList.at(stave)->partName());
2841 0 : partName = doc.toPlainText();
2842 : }
2843 0 : if (partName.isEmpty()) { // No Part name? Fall back to Instrument name
2844 0 : partName = partList.at(stave)->instrumentName();
2845 : }
2846 :
2847 0 : std::pair<QString, bool> instrumentLabel = std::make_pair(partName, partList.at(stave)->show());
2848 0 : rowLabels.push_back(instrumentLabel);
2849 0 : }
2850 0 : return rowLabels;
2851 0 : }
2852 :
2853 : //---------------------------------------------------------
2854 : // Timeline::handleScroll
2855 : //---------------------------------------------------------
2856 :
2857 0 : void Timeline::handleScroll(int value)
2858 : {
2859 0 : if (!score()) {
2860 0 : return;
2861 : }
2862 :
2863 0 : for (auto it = _metaRows.begin(); it != _metaRows.end(); ++it) {
2864 0 : std::pair<QGraphicsItem*, int> pairGraphicsInt = *it;
2865 :
2866 0 : QGraphicsItem* graphicsItem = pairGraphicsInt.first;
2867 0 : QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
2868 0 : QGraphicsLineItem* graphicsLineItem = qgraphicsitem_cast<QGraphicsLineItem*>(graphicsItem);
2869 0 : QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
2870 :
2871 0 : int rowY = pairGraphicsInt.second * _gridHeight;
2872 0 : int scrollbarValue = value;
2873 :
2874 0 : if (graphicsRectItem) {
2875 0 : QRectF rectf = graphicsRectItem->rect();
2876 0 : rectf.setY(qreal(scrollbarValue + rowY));
2877 0 : rectf.setHeight(_gridHeight);
2878 0 : graphicsRectItem->setRect(rectf);
2879 0 : } else if (graphicsLineItem) {
2880 0 : QLineF linef = graphicsLineItem->line();
2881 0 : linef.setLine(linef.x1(), rowY + scrollbarValue + 1, linef.x2(), rowY + scrollbarValue + 1);
2882 0 : graphicsLineItem->setLine(linef);
2883 0 : } else if (graphicsPixmapItem) {
2884 0 : graphicsPixmapItem->setY(qreal(scrollbarValue + rowY + 3));
2885 : } else {
2886 0 : graphicsItem->setY(qreal(scrollbarValue + rowY));
2887 : }
2888 : }
2889 0 : viewport()->update();
2890 : }
2891 :
2892 : //---------------------------------------------------------
2893 : // Timeline::mouseOver
2894 : //---------------------------------------------------------
2895 :
2896 0 : void Timeline::mouseOver(QPointF pos)
2897 : {
2898 0 : TRACEFUNC;
2899 :
2900 : // Choose item with the largest original Z value...
2901 0 : QList<QGraphicsItem*> graphicsList = scene()->items(pos);
2902 0 : QGraphicsItem* hoveredGraphicsItem = 0;
2903 0 : int maxZValue = -1;
2904 0 : for (QGraphicsItem* currGraphicsItem : graphicsList) {
2905 0 : if (qgraphicsitem_cast<QGraphicsTextItem*>(currGraphicsItem)) {
2906 0 : continue;
2907 : }
2908 0 : if (currGraphicsItem->zValue() >= maxZValue && currGraphicsItem->zValue() < _globalZValue) {
2909 0 : hoveredGraphicsItem = currGraphicsItem;
2910 0 : maxZValue = hoveredGraphicsItem->zValue();
2911 0 : } else if (currGraphicsItem->zValue() > _globalZValue && std::get<1>(_oldHoverInfo) >= maxZValue) {
2912 0 : hoveredGraphicsItem = currGraphicsItem;
2913 0 : maxZValue = std::get<1>(_oldHoverInfo);
2914 : }
2915 : }
2916 :
2917 0 : if (!hoveredGraphicsItem) {
2918 0 : if (std::get<0>(_oldHoverInfo)) {
2919 0 : std::get<0>(_oldHoverInfo)->setZValue(std::get<1>(_oldHoverInfo));
2920 0 : static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>())->setZValue(std::get<1>(_oldHoverInfo));
2921 0 : QGraphicsRectItem* graphicsRectItem1 = qgraphicsitem_cast<QGraphicsRectItem*>(std::get<0>(_oldHoverInfo));
2922 : QGraphicsRectItem* graphicsRectItem2
2923 0 : = qgraphicsitem_cast<QGraphicsRectItem*>(static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>()));
2924 0 : if (graphicsRectItem1) {
2925 0 : graphicsRectItem1->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
2926 : }
2927 0 : if (graphicsRectItem2) {
2928 0 : graphicsRectItem2->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
2929 : }
2930 0 : std::get<0>(_oldHoverInfo) = nullptr;
2931 0 : std::get<1>(_oldHoverInfo) = -1;
2932 : }
2933 0 : return;
2934 : }
2935 0 : QGraphicsItem* pairItem = static_cast<QGraphicsItem*>(hoveredGraphicsItem->data(5).value<void*>());
2936 0 : if (!pairItem) {
2937 0 : if (std::get<0>(_oldHoverInfo)) {
2938 0 : std::get<0>(_oldHoverInfo)->setZValue(std::get<1>(_oldHoverInfo));
2939 0 : static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>())->setZValue(std::get<1>(_oldHoverInfo));
2940 0 : QGraphicsRectItem* graphicsRectItem1 = qgraphicsitem_cast<QGraphicsRectItem*>(std::get<0>(_oldHoverInfo));
2941 : QGraphicsRectItem* graphicsRectItem2
2942 0 : = qgraphicsitem_cast<QGraphicsRectItem*>(static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>()));
2943 0 : if (graphicsRectItem1) {
2944 0 : graphicsRectItem1->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
2945 : }
2946 0 : if (graphicsRectItem2) {
2947 0 : graphicsRectItem2->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
2948 : }
2949 0 : std::get<0>(_oldHoverInfo) = nullptr;
2950 0 : std::get<1>(_oldHoverInfo) = -1;
2951 : }
2952 0 : return;
2953 : }
2954 :
2955 0 : if (std::get<0>(_oldHoverInfo) == hoveredGraphicsItem) {
2956 0 : return;
2957 : }
2958 :
2959 0 : if (std::get<0>(_oldHoverInfo)) {
2960 0 : std::get<0>(_oldHoverInfo)->setZValue(std::get<1>(_oldHoverInfo));
2961 0 : static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>())->setZValue(std::get<1>(_oldHoverInfo));
2962 0 : QGraphicsRectItem* graphicsRectItem1 = qgraphicsitem_cast<QGraphicsRectItem*>(std::get<0>(_oldHoverInfo));
2963 : QGraphicsRectItem* graphicsRectItem2
2964 0 : = qgraphicsitem_cast<QGraphicsRectItem*>(static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>()));
2965 0 : if (graphicsRectItem1) {
2966 0 : graphicsRectItem1->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
2967 : }
2968 0 : if (graphicsRectItem2) {
2969 0 : graphicsRectItem2->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
2970 : }
2971 :
2972 0 : std::get<0>(_oldHoverInfo) = nullptr;
2973 0 : std::get<1>(_oldHoverInfo) = -1;
2974 : }
2975 :
2976 0 : std::get<1>(_oldHoverInfo) = hoveredGraphicsItem->zValue();
2977 0 : std::get<0>(_oldHoverInfo) = hoveredGraphicsItem;
2978 :
2979 : // Give items the top z value
2980 0 : hoveredGraphicsItem->setZValue(_globalZValue + 1);
2981 0 : pairItem->setZValue(_globalZValue + 1);
2982 :
2983 0 : QGraphicsRectItem* graphicsRectItem1 = qgraphicsitem_cast<QGraphicsRectItem*>(hoveredGraphicsItem);
2984 0 : QGraphicsRectItem* graphicsRectItem2 = qgraphicsitem_cast<QGraphicsRectItem*>(pairItem);
2985 0 : if (graphicsRectItem1) {
2986 0 : std::get<2>(_oldHoverInfo) = graphicsRectItem1->brush().color();
2987 0 : if (std::get<2>(_oldHoverInfo) != activeTheme().selectionColor) {
2988 0 : graphicsRectItem1->setBrush(QBrush(activeTheme().backgroundColor));
2989 : }
2990 : }
2991 0 : if (graphicsRectItem2) {
2992 0 : std::get<2>(_oldHoverInfo) = graphicsRectItem2->brush().color();
2993 0 : if (std::get<2>(_oldHoverInfo) != activeTheme().selectionColor) {
2994 0 : graphicsRectItem2->setBrush(QBrush(activeTheme().backgroundColor));
2995 : }
2996 : }
2997 0 : }
2998 :
2999 : //---------------------------------------------------------
3000 : // Timeline::swapMeta
3001 : //---------------------------------------------------------
3002 :
3003 0 : void Timeline::swapMeta(unsigned row, bool switchUp)
3004 : {
3005 : // Attempt to switch row up or down, skipping non visible rows
3006 0 : if (switchUp && row != 0) {
3007 : // traverse backwards until visible one is found
3008 0 : auto swap = _metas.begin() + correctMetaRow(row) - 1;
3009 0 : while (!std::get<2>(*swap)) {
3010 0 : swap--;
3011 : }
3012 0 : iter_swap(_metas.begin() + correctMetaRow(row), swap);
3013 0 : } else if (!switchUp && row != nmetas() - 2) {
3014 : // traverse forwards until visible one is found
3015 0 : auto swap = _metas.begin() + correctMetaRow(row) + 1;
3016 0 : while (!std::get<2>(*swap)) {
3017 0 : swap++;
3018 : }
3019 0 : iter_swap(_metas.begin() + correctMetaRow(row), swap);
3020 : }
3021 :
3022 0 : updateGrid();
3023 0 : }
3024 :
3025 : //---------------------------------------------------------
3026 : // Timeline::numToStaff
3027 : //---------------------------------------------------------
3028 :
3029 0 : Staff* Timeline::numToStaff(int staff)
3030 : {
3031 0 : if (!score()) {
3032 0 : return nullptr;
3033 : }
3034 :
3035 0 : size_t staffIdx = static_cast<size_t>(staff);
3036 :
3037 0 : const std::vector<Staff*>& staves = score()->staves();
3038 0 : if (staffIdx < staves.size()) {
3039 0 : return staves.at(staffIdx);
3040 : } else {
3041 0 : return nullptr;
3042 : }
3043 : }
3044 :
3045 : //---------------------------------------------------------
3046 : // Timeline::toggleShow
3047 : //---------------------------------------------------------
3048 :
3049 0 : void Timeline::toggleShow(int staff)
3050 : {
3051 0 : if (!score()) {
3052 0 : return;
3053 : }
3054 :
3055 0 : QList<Part*> parts = getParts();
3056 0 : if (staff < 0 || staff >= parts.size()) {
3057 0 : return;
3058 : }
3059 :
3060 0 : Part* part = parts.at(staff);
3061 :
3062 0 : bool newShow = !part->show();
3063 : TranslatableString actionName = newShow
3064 0 : ? TranslatableString("undoableAction", "Show instrument")
3065 0 : : TranslatableString("undoableAction", "Hide instrument");
3066 :
3067 0 : m_notation->undoStack()->prepareChanges(actionName);
3068 0 : part->undoChangeProperty(Pid::VISIBLE, newShow);
3069 0 : m_notation->undoStack()->commitChanges();
3070 0 : m_notation->notationChanged().send(muse::RectF());
3071 0 : }
3072 :
3073 : //---------------------------------------------------------
3074 : // Timeline::showContextMenu
3075 : //---------------------------------------------------------
3076 :
3077 0 : void Timeline::contextMenuEvent(QContextMenuEvent* event)
3078 : {
3079 0 : QMenu* contextMenu = new QMenu(muse::qtrc("notation/timeline", "Context menu"), this);
3080 0 : if (_rowNames->cursorIsOn() == "instrument") {
3081 0 : QAction* edit_instruments = new QAction(muse::qtrc("notation/timeline", "Edit instruments"), this);
3082 0 : connect(edit_instruments, &QAction::triggered, this, &Timeline::requestInstrumentDialog);
3083 0 : contextMenu->addAction(edit_instruments);
3084 0 : contextMenu->exec(QCursor::pos());
3085 0 : } else if (_rowNames->cursorIsOn() == "meta" || cursorIsOn(event->pos()) == "meta") {
3086 0 : for (auto it = _metas.begin(); it != _metas.end(); ++it) {
3087 0 : std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
3088 0 : QString row_name = std::get<0>(meta);
3089 0 : if (row_name != muse::qtrc("notation/timeline", "Measures")) {
3090 0 : QAction* action = new QAction(row_name, this);
3091 0 : action->setCheckable(true);
3092 0 : action->setChecked(std::get<2>(meta));
3093 0 : connect(action, &QAction::triggered, this, &Timeline::toggleMetaRow);
3094 0 : contextMenu->addAction(action);
3095 : }
3096 0 : }
3097 0 : contextMenu->addSeparator();
3098 0 : QAction* hide_all = new QAction(muse::qtrc("notation/timeline", "Hide all"), this);
3099 0 : connect(hide_all, &QAction::triggered, this, &Timeline::toggleMetaRow);
3100 0 : contextMenu->addAction(hide_all);
3101 0 : QAction* show_all = new QAction(muse::qtrc("notation/timeline", "Show all"), this);
3102 0 : connect(show_all, &QAction::triggered, this, &Timeline::toggleMetaRow);
3103 0 : contextMenu->addAction(show_all);
3104 0 : contextMenu->exec(QCursor::pos());
3105 : }
3106 0 : }
3107 :
3108 : //---------------------------------------------------------
3109 : // Timeline::toggleMetaRow
3110 : //---------------------------------------------------------
3111 :
3112 0 : void Timeline::toggleMetaRow()
3113 : {
3114 0 : QAction* action = qobject_cast<QAction*>(QObject::sender());
3115 0 : if (!action) {
3116 0 : return;
3117 : }
3118 :
3119 0 : QString targetText = action->text();
3120 :
3121 0 : if (targetText == muse::qtrc("notation/timeline", "Hide all")) {
3122 0 : for (auto it = _metas.begin(); it != _metas.end(); ++it) {
3123 0 : QString metaText = std::get<0>(*it);
3124 0 : if (metaText != muse::qtrc("notation/timeline", "Measures")) {
3125 0 : std::get<2>(*it) = false;
3126 : }
3127 0 : }
3128 0 : updateGrid();
3129 0 : return;
3130 0 : } else if (targetText == muse::qtrc("notation/timeline", "Show all")) {
3131 0 : for (auto it = _metas.begin(); it != _metas.end(); ++it) {
3132 0 : std::get<2>(*it) = true;
3133 : }
3134 0 : updateGrid();
3135 0 : return;
3136 : }
3137 :
3138 0 : bool checked = action->isChecked();
3139 : // Find target text in metas and toggle visibility to the checked status of action
3140 0 : for (auto it = _metas.begin(); it != _metas.end(); ++it) {
3141 0 : QString metaText = std::get<0>(*it);
3142 0 : if (metaText == targetText) {
3143 0 : std::get<2>(*it) = checked;
3144 0 : updateGrid();
3145 0 : break;
3146 : }
3147 0 : }
3148 0 : }
3149 :
3150 : //---------------------------------------------------------
3151 : // Timeline::nmetas
3152 : //---------------------------------------------------------
3153 :
3154 0 : unsigned Timeline::nmetas() const
3155 : {
3156 0 : unsigned total = 0;
3157 0 : if (_collapsedMeta) {
3158 0 : return 2;
3159 : }
3160 0 : for (auto it = _metas.begin(); it != _metas.end(); ++it) {
3161 0 : std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
3162 0 : if (std::get<2>(meta)) {
3163 0 : total++;
3164 : }
3165 0 : }
3166 0 : return total;
3167 : }
3168 :
3169 : //---------------------------------------------------------
3170 : // Timeline::correctMetaRow
3171 : //---------------------------------------------------------
3172 :
3173 0 : unsigned Timeline::correctMetaRow(unsigned row)
3174 : {
3175 0 : unsigned count = 0;
3176 0 : auto it = _metas.begin();
3177 0 : while (row >= count) {
3178 0 : if (!std::get<2>(*it)) {
3179 0 : row++;
3180 : }
3181 0 : count++;
3182 0 : ++it;
3183 : }
3184 0 : return row;
3185 : }
3186 :
3187 : //---------------------------------------------------------
3188 : // Timeline::correctMetaRow
3189 : //---------------------------------------------------------
3190 :
3191 0 : QString Timeline::cursorIsOn(const QPoint& cursorPos)
3192 : {
3193 0 : QGraphicsItem* graphicsItem = scene()->itemAt(cursorPos, transform());
3194 0 : if (!graphicsItem) {
3195 0 : return "";
3196 : }
3197 :
3198 0 : auto it = _metaRows.begin();
3199 0 : for (; it != _metaRows.end(); ++it) {
3200 0 : if ((*it).first == graphicsItem) {
3201 0 : break;
3202 : }
3203 : }
3204 0 : if (it != _metaRows.end()) {
3205 0 : return "meta";
3206 : }
3207 0 : QList<QGraphicsItem*> graphicsItemList = scene()->items(cursorPos);
3208 0 : for (QGraphicsItem* currGraphicsItem : graphicsItemList) {
3209 0 : Measure* currMeasure = static_cast<Measure*>(currGraphicsItem->data(2).value<void*>());
3210 0 : int stave = currGraphicsItem->data(0).value<int>();
3211 0 : const Staff* st = numToStaff(stave);
3212 0 : if (currMeasure && !(st && st->show())) {
3213 0 : return "invalid";
3214 : }
3215 : }
3216 0 : return "instrument";
3217 0 : }
3218 :
3219 : //---------------------------------------------------------
3220 : // Timeline::activeTheme
3221 : //---------------------------------------------------------
3222 :
3223 0 : const TimelineTheme& Timeline::activeTheme() const
3224 : {
3225 0 : if (uiConfiguration()->currentTheme().codeKey == muse::ui::DARK_THEME_CODE) {
3226 0 : return _darkTheme;
3227 : }
3228 :
3229 0 : return _lightTheme;
3230 : }
3231 :
3232 : //---------------------------------------------------------
3233 : // Timeline::updateTimelineTheme
3234 : //---------------------------------------------------------
3235 :
3236 0 : void Timeline::updateTimelineTheme()
3237 : {
3238 0 : const QBrush backgroundBrush = QBrush(activeTheme().backgroundColor);
3239 0 : scene()->setBackgroundBrush(backgroundBrush);
3240 0 : _rowNames->scene()->setBackgroundBrush(backgroundBrush);
3241 0 : updateGrid();
3242 0 : }
3243 :
3244 : //---------------------------------------------------------
3245 : // Timeline::requestInstrumentDialog
3246 : //---------------------------------------------------------
3247 :
3248 0 : void Timeline::requestInstrumentDialog()
3249 : {
3250 0 : commandDispatcher()->dispatch(INSTRUMENTS_SELECT_COMMAND);
3251 0 : }
3252 :
3253 0 : INotationInteractionPtr Timeline::interaction() const
3254 : {
3255 0 : return m_notation ? m_notation->interaction() : nullptr;
3256 : }
3257 :
3258 0 : Score* Timeline::score() const
3259 : {
3260 0 : return m_notation ? m_notation->elements()->msScore() : nullptr;
3261 : }
3262 :
3263 0 : TRowLabels* Timeline::labelsColumn() const
3264 : {
3265 0 : return _rowNames;
3266 : }
|