//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2002-2013 Werner Schweer
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2
//  as published by the Free Software Foundation and appearing in
//  the file LICENCE.GPL
//=============================================================================

#include "timeline.h"
#include "navigator.h"
#include "musescore.h"
#include "libmscore/score.h"
#include "libmscore/page.h"
#include "preferences.h"
#include "libmscore/mscore.h"
#include "libmscore/system.h"
#include "libmscore/measurebase.h"
#include "libmscore/measure.h"
#include "libmscore/chord.h"
#include "libmscore/staff.h"
#include "libmscore/rest.h"
#include "libmscore/part.h"
#include "libmscore/tempo.h"
#include "libmscore/keysig.h"
#include "libmscore/timesig.h"
#include "libmscore/key.h"
#include "libmscore/tempotext.h"
#include "libmscore/text.h"
#include "libmscore/rehearsalmark.h"
#include "libmscore/barline.h"
#include "libmscore/jump.h"
#include "libmscore/marker.h"
#include "texttools.h"
#include "mixer/mixer.h"
#include "tourhandler.h"

namespace Ms {
//---------------------------------------------------------
//   MuseScore::showTimeline
//---------------------------------------------------------

void MuseScore::showTimeline(bool visible)
{
    QSplitter* split = static_cast<QSplitter*>(_timeline->widget());
    Timeline* time = static_cast<Timeline*>(split->widget(1));

    QAction* act = getAction("toggle-timeline");
    if (!time) {
        time = new Timeline(_timeline);
        time->setScore(0);
        time->setScoreView(cv);
    }
    connect(_timeline, SIGNAL(visibilityChanged(bool)), act, SLOT(setChecked(bool)));
    connect(_timeline, SIGNAL(closed(bool)), act, SLOT(setChecked(bool)));
    reDisplayDockWidget(_timeline, visible);

    getAction("toggle-timeline")->setChecked(visible);
    if (visible) {
        TourHandler::startTour("timeline-tour");
    }
}

//---------------------------------------------------------
//   TDockWidget
//---------------------------------------------------------

TDockWidget::TDockWidget(QWidget* w)
    : QDockWidget(w)
{
    setFocusPolicy(Qt::NoFocus);
    _grid = new QSplitter();
    setWidget(_grid);
    _grid->setHandleWidth(0);
    setAllowedAreas(Qt::DockWidgetAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea));
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    setWindowTitle(tr("Timeline"));
    setObjectName("Timeline");
}

//---------------------------------------------------------
//   TDockWidget::closeEvent
//---------------------------------------------------------

void TDockWidget::closeEvent(QCloseEvent* event)
{
    emit closed(false);
    QWidget::closeEvent(event);
}

//---------------------------------------------------------
//   TRowLabels
//---------------------------------------------------------

TRowLabels::TRowLabels(TDockWidget* dockWidget, Timeline* time, QGraphicsView* w)
    : QGraphicsView(w)
{
    setFocusPolicy(Qt::NoFocus);
    _scrollArea = dockWidget;
    parent = time;
    setScene(new QGraphicsScene);
    scene()->setBackgroundBrush(time->activeTheme().backgroundColor);
    setSceneRect(0, 0, 50, time->height());

    setMinimumWidth(0);

    QSplitter* split = _scrollArea->grid();
    QList<int> sizes;
    // TODO: Replace 70 with hard coded value
    sizes << 70 << 10000;
    split->setSizes(sizes);

    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setContentsMargins(0, 0, 0, 0);
    setAlignment(Qt::Alignment((Qt::AlignLeft | Qt::AlignTop)));

    connect(verticalScrollBar(), SIGNAL(valueChanged(int)), time->verticalScrollBar(), SLOT(setValue(int)));
    connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(restrictScroll(int)));
    connect(this, SIGNAL(moved(QPointF)), time, SLOT(mouseOver(QPointF)));

    static const char* udArrow[] = {
        "10 18 2 1",
        "# c #000000",
        ". c #d3d3d3",
        "..........",
        "..........",
        "....##....",
        "...####...",
        "..##..##..",
        "..#....#..",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..#....#..",
        "..##..##..",
        "...####...",
        "....##....",
        "..........",
        ".........."
    };

    static const char* uArrow[] = {
        "10 18 2 1",
        "# c #000000",
        ". c #d3d3d3",
        "..........",
        "..........",
        "....##....",
        "...####...",
        "..##..##..",
        "..#....#..",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        ".........."
    };

    static const char* dArrow[] = {
        "10 18 2 1",
        "# c #000000",
        ". c #d3d3d3",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..........",
        "..#....#..",
        "..##..##..",
        "...####...",
        "....##....",
        "..........",
        ".........."
    };

    static const char* cuArrow[] = {
        "9 18 2 1",
        "# c #000000",
        ". c #d3d3d3",
        ".........",
        ".........",
        ".........",
        ".........",
        "....#....",
        "...###...",
        "..#.#.#..",
        ".#..#..#.",
        "....#....",
        "....#....",
        "....#....",
        "....#....",
        ".........",
        ".........",
        ".........",
        ".........",
        ".........",
        "........."
    };

    static const char* cdArrow[] = {
        "9 18 2 1",
        "# c #000000",
        ". c #d3d3d3",
        ".........",
        ".........",
        ".........",
        ".........",
        ".........",
        "....#....",
        "....#....",
        "....#....",
        "....#....",
        "....#....",
        ".#..#..#.",
        "..#.#.#..",
        "...###...",
        "....#....",
        ".........",
        ".........",
        ".........",
        "........."
    };

    static const char* openEye[] = {
        "11 18 2 1",
        "# c #000000",
        ". c #d3d3d3",
        "...........",
        "...........",
        "...........",
        "...........",
        "...####....",
        ".##....##..",
        ".#......#..",
        "#........#.",
        "#...##...#.",
        "#...##...#.",
        "#........#.",
        ".#......#..",
        ".##....##..",
        "...####....",
        "...........",
        "...........",
        "...........",
        "..........."
    };

    static const char* closedEye[] = {
        "11 18 2 1",
        "# c #000000",
        ". c #d3d3d3",
        "...........",
        "...........",
        "...........",
        "...........",
        "...........",
        "...........",
        "...........",
        "..######...",
        "##..##..##.",
        "##..##..##.",
        "..######...",
        "...........",
        "...........",
        "...........",
        "...........",
        "...........",
        "...........",
        "..........."
    };

    _mouseoverMap[MouseOverValue::COLLAPSE_DOWN_ARROW] = new QPixmap(cdArrow);
    _mouseoverMap[MouseOverValue::COLLAPSE_UP_ARROW] = new QPixmap(cuArrow);
    _mouseoverMap[MouseOverValue::MOVE_DOWN_ARROW] = new QPixmap(dArrow);
    _mouseoverMap[MouseOverValue::MOVE_UP_DOWN_ARROW] = new QPixmap(udArrow);
    _mouseoverMap[MouseOverValue::MOVE_UP_ARROW] = new QPixmap(uArrow);
    _mouseoverMap[MouseOverValue::OPEN_EYE] = new QPixmap(openEye);
    _mouseoverMap[MouseOverValue::CLOSED_EYE] = new QPixmap(closedEye);

    std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
    _oldItemInfo = tmp;

    connect(this, SIGNAL(requestContextMenu(QContextMenuEvent*)), parent, SLOT(contextMenuEvent(QContextMenuEvent*)));
}

//---------------------------------------------------------
//   TRowLabels::restrictScroll
//---------------------------------------------------------

void TRowLabels::restrictScroll(int value)
{
    if (value > parent->verticalScrollBar()->maximum()) {
        verticalScrollBar()->setValue(parent->verticalScrollBar()->maximum());
    }
    for (std::vector<std::pair<QGraphicsItem*, int> >::iterator it = _metaLabels.begin();
         it != _metaLabels.end(); ++it) {
        std::pair<QGraphicsItem*, int> pairGraphicInt = *it;

        QGraphicsItem* graphicsItem = pairGraphicInt.first;

        QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
        QGraphicsLineItem* graphicsLineItem = qgraphicsitem_cast<QGraphicsLineItem*>(graphicsItem);
        QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
        int y = pairGraphicInt.second * 20;
        int scrollbarValue = verticalScrollBar()->value();

        if (graphicsRectItem) {
            QRectF rectf = graphicsRectItem->rect();
            rectf.setY(qreal(scrollbarValue + y));
            rectf.setHeight(20);
            graphicsRectItem->setRect(rectf);
        } else if (graphicsLineItem) {
            QLineF linef = graphicsLineItem->line();
            linef.setLine(linef.x1(), y + scrollbarValue + 1, linef.x2(), y + scrollbarValue + 1);
            graphicsLineItem->setLine(linef);
        } else if (graphicsPixmapItem) {
            graphicsPixmapItem->setY(qreal(scrollbarValue + y + 1));
        } else {
            graphicsItem->setY(qreal(scrollbarValue + y));
        }
    }
    viewport()->update();
}

//---------------------------------------------------------
//   TRowLabels::updateLabels
//---------------------------------------------------------

void TRowLabels::updateLabels(std::vector<std::pair<QString, bool> > labels, int height)
{
    scene()->clear();
    _metaLabels.clear();
    if (labels.empty()) {
        return;
    }

    unsigned numMetas = parent->nmetas();
    int maxWidth = -1;
    int measureWidth = 0;
    for (unsigned row = 0; row < labels.size(); row++) {
        // Draw instrument name rectangle
        int ypos = (row < numMetas) ? row * height + verticalScrollBar()->value() : row * height + 3;
        QGraphicsRectItem* graphicsRectItem = new QGraphicsRectItem(0, ypos, width(), height);
        QGraphicsTextItem* graphicsTextItem = new QGraphicsTextItem(labels[row].first);

        if (row == numMetas - 1) {
            measureWidth = graphicsTextItem->boundingRect().width();
        }
        maxWidth = std::max(maxWidth, int(graphicsTextItem->boundingRect().width()));

        QFontMetrics f(QApplication::font());
        QString partName = f.elidedText(labels[row].first, Qt::ElideRight, width());
        graphicsTextItem->setPlainText(partName);
        graphicsTextItem->setX(0);
        graphicsTextItem->setY(ypos);
        if (labels[row].second) {
            graphicsTextItem->setDefaultTextColor(parent->activeTheme().labelsColor1);
        } else {
            graphicsTextItem->setDefaultTextColor(parent->activeTheme().labelsColor2);
        }
        graphicsRectItem->setPen(QPen(parent->activeTheme().labelsColor2));
        graphicsRectItem->setBrush(QBrush(parent->activeTheme().labelsColor3));
        graphicsTextItem->setZValue(-1);
        graphicsRectItem->setZValue(-1);

        graphicsRectItem->setData(0, QVariant::fromValue<bool>(false));
        graphicsTextItem->setData(0, QVariant::fromValue<bool>(false));

        MouseOverValue mouseOverArrow = MouseOverValue::NONE;
        if (numMetas - 1 == row && (numMetas > 2 || parent->collapsed())) {
            // Measures meta
            if (parent->collapsed()) {
                mouseOverArrow = MouseOverValue::COLLAPSE_DOWN_ARROW;
            } else {
                mouseOverArrow = MouseOverValue::COLLAPSE_UP_ARROW;
            }
        } else if (row < numMetas - 1) {
            if (row != 0 && row + 1 <= numMetas - 2) {
                mouseOverArrow = MouseOverValue::MOVE_UP_DOWN_ARROW;
            } else if (row == 0 && row + 1 < numMetas - 1) {
                mouseOverArrow = MouseOverValue::MOVE_DOWN_ARROW;
            } else if (row == numMetas - 2 && row != 0) {
                mouseOverArrow = MouseOverValue::MOVE_UP_ARROW;
            }
        } else if (numMetas <= row) {
            if (parent->numToStaff(row - numMetas)
                && parent->numToStaff(row - numMetas)->show()) {
                mouseOverArrow = MouseOverValue::OPEN_EYE;
            } else {
                mouseOverArrow = MouseOverValue::CLOSED_EYE;
            }
        }
        graphicsTextItem->setData(1, QVariant::fromValue<MouseOverValue>(mouseOverArrow));
        graphicsRectItem->setData(1, QVariant::fromValue<MouseOverValue>(mouseOverArrow));

        graphicsTextItem->setData(2, QVariant::fromValue<unsigned>(row));
        graphicsRectItem->setData(2, QVariant::fromValue<unsigned>(row));

        scene()->addItem(graphicsRectItem);
        scene()->addItem(graphicsTextItem);
        if (row < numMetas) {
            std::pair<QGraphicsItem*, int> p1 = std::make_pair(graphicsRectItem, row);
            std::pair<QGraphicsItem*, int> p2 = std::make_pair(graphicsTextItem, row);
            _metaLabels.push_back(p1);
            _metaLabels.push_back(p2);
            graphicsRectItem->setZValue(1);
            graphicsTextItem->setZValue(2);
        }
    }
    QGraphicsLineItem* graphicsLineItem = new QGraphicsLineItem(0,
                                                                height * numMetas + verticalScrollBar()->value() + 1,
                                                                std::max(maxWidth + 20, 70),
                                                                height * numMetas + verticalScrollBar()->value() + 1);
    graphicsLineItem->setPen(QPen(QColor(150, 150, 150), 4));
    graphicsLineItem->setZValue(0);
    graphicsLineItem->setData(0, QVariant::fromValue<bool>(false));
    scene()->addItem(graphicsLineItem);

    std::pair<QGraphicsItem*, int> graphicsLineItemPair = std::make_pair(graphicsLineItem, numMetas);
    _metaLabels.push_back(graphicsLineItemPair);

    setSceneRect(0, 0, maxWidth, parent->getHeight() + parent->horizontalScrollBar()->height());

    std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
    _oldItemInfo = tmp;

    setMinimumWidth(measureWidth + 9);
    setMaximumWidth(std::max(maxWidth + 20, 70));
    mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));
}

//---------------------------------------------------------
//   TRowLabels::resizeEvent
//---------------------------------------------------------

void TRowLabels::resizeEvent(QResizeEvent*)
{
    std::vector<std::pair<QString, bool> > labels = parent->getLabels();
    updateLabels(labels, 20);
}

//---------------------------------------------------------
//   TRowLabels::mousePressEvent
//---------------------------------------------------------

void TRowLabels::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::RightButton) {
        return;
    }

    QPointF scenePt = mapToScene(event->pos());
    unsigned numMetas = parent->nmetas();

    // Check if mouse position in scene is on the last meta
    QPointF measureMetaTl = QPointF(0, (numMetas - 1) * 20 + verticalScrollBar()->value());
    QPointF measureMetaBr = QPointF(width(), numMetas * 20 + verticalScrollBar()->value());
    if (QRectF(measureMetaTl, measureMetaBr).contains(scenePt) && (numMetas > 2 || parent->collapsed())) {
        if (std::get<0>(_oldItemInfo)) {
            std::pair<QGraphicsItem*, int> p = std::make_pair(std::get<0>(_oldItemInfo), std::get<2>(_oldItemInfo));
            std::vector<std::pair<QGraphicsItem*, int> >::iterator it = std::find(_metaLabels.begin(),
                                                                                  _metaLabels.end(), p);
            if (it != _metaLabels.end()) {
                _metaLabels.erase(it);
            }
            scene()->removeItem(std::get<0>(_oldItemInfo));
        }
        std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
        _oldItemInfo = tmp;
        mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));

        parent->setCollapsed(!parent->collapsed());
        parent->updateGrid();
    } else {
        // Check if pixmap was selected
        if (QGraphicsItem* graphicsItem = scene()->itemAt(scenePt, transform())) {
            QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
            if (graphicsPixmapItem) {
                unsigned row = graphicsPixmapItem->data(2).value<unsigned>();
                if (row == numMetas - 1) {
                    return;
                } else if (row < numMetas - 1) {
                    // Find mid point between up and down arrow
                    qreal midPoint = graphicsPixmapItem->boundingRect().height() / 2
                                     + graphicsPixmapItem->scenePos().y();
                    if (scenePt.y() > midPoint) {
                        emit swapMeta(row, false);
                    } else {
                        emit swapMeta(row, true);
                    }
                } else if (row >= numMetas) {
                    parent->toggleShow(row - numMetas);
                }
            } else {
                _dragging = true;
                setCursor(Qt::SizeAllCursor);
                _oldLoc = QPoint(int(scenePt.x()), int(scenePt.y()));
            }
        } else {
            _dragging = true;
            setCursor(Qt::SizeAllCursor);
            _oldLoc = QPoint(int(scenePt.x()), int(scenePt.y()));
        }
    }
}

//---------------------------------------------------------
//   TRowLabels::mouseMoveEvent
//---------------------------------------------------------

void TRowLabels::mouseMoveEvent(QMouseEvent* event)
{
    QPointF scenePt = mapToScene(event->pos());
    if (_dragging) {
        setCursor(Qt::SizeAllCursor);
        int yOffset = int(_oldLoc.y()) - int(scenePt.y());
        verticalScrollBar()->setValue(verticalScrollBar()->value() + yOffset);
    } else {
        mouseOver(scenePt);
    }
    emit moved(QPointF(-1, -1));
}

//---------------------------------------------------------
//   TRowLabels::mouseReleaseEvent
//---------------------------------------------------------

void TRowLabels::mouseReleaseEvent(QMouseEvent* event)
{
    if (QGraphicsItem* graphicsItem = scene()->itemAt(mapToScene(event->pos()), transform())) {
        QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
        if (graphicsPixmapItem) {
            setCursor(Qt::PointingHandCursor);
        } else {
            setCursor(Qt::ArrowCursor);
        }
    } else {
        setCursor(Qt::ArrowCursor);
    }
    _dragging = false;
}

//---------------------------------------------------------
//   TRowLabels::leaveEvent
//---------------------------------------------------------

void TRowLabels::leaveEvent(QEvent*)
{
    if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
        mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));
    }
}

//---------------------------------------------------------
//   TRowLabels::contextMenuEvent
//---------------------------------------------------------

void TRowLabels::contextMenuEvent(QContextMenuEvent* event)
{
    emit requestContextMenu(event);
}

//---------------------------------------------------------
//   TRowLabels::mouseOver
//---------------------------------------------------------

void TRowLabels::mouseOver(QPointF scenePt)
{
    // Handle drawing of arrows
    if (QGraphicsItem* graphicsItem = scene()->itemAt(scenePt, transform())) {
        QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
        if (graphicsPixmapItem) {
            setCursor(Qt::PointingHandCursor);
            return;
        }

        MouseOverValue mouseOverArrow = graphicsItem->data(1).value<MouseOverValue>();
        if (mouseOverArrow != MouseOverValue::NONE) {
            QPixmap* pixmapArrow = _mouseoverMap[mouseOverArrow];
            QGraphicsPixmapItem* graphicsPixmapItemArrow = new QGraphicsPixmapItem(*pixmapArrow);
            unsigned row = graphicsItem->data(2).value<unsigned>();

            QString tooltip;
            switch (mouseOverArrow) {
            case MouseOverValue::COLLAPSE_DOWN_ARROW:
                tooltip = tr("Expand meta rows");
                break;
            case MouseOverValue::COLLAPSE_UP_ARROW:
                tooltip = tr("Collapse meta rows");
                break;
            case MouseOverValue::MOVE_DOWN_ARROW:
                tooltip = tr("Move meta row down one");
                break;
            case MouseOverValue::MOVE_UP_ARROW:
                tooltip = tr("Move meta row up one");
                break;
            case MouseOverValue::MOVE_UP_DOWN_ARROW:
                tooltip = tr("Move meta row up/down one");
                break;
            case MouseOverValue::OPEN_EYE:
                tooltip = tr("Hide instrument in score");
                break;
            case MouseOverValue::CLOSED_EYE:
                tooltip = tr("Show instrument in score");
                break;
            default:
                tooltip = "";
                break;
            }

            graphicsPixmapItemArrow->setToolTip(tooltip);
            if (mouseOverArrow == MouseOverValue::OPEN_EYE || mouseOverArrow == MouseOverValue::CLOSED_EYE) {
                graphicsPixmapItemArrow->setData(0, QVariant::fromValue<bool>(false));
            } else {
                graphicsPixmapItemArrow->setData(0, QVariant::fromValue<bool>(true));
            }
            graphicsPixmapItemArrow->setData(1, QVariant::fromValue<MouseOverValue>(mouseOverArrow));
            graphicsPixmapItemArrow->setData(2, QVariant::fromValue<unsigned>(row));

            // Draw arrow at correct location
            if (row < parent->nmetas()) {
                graphicsPixmapItemArrow->setPos(width() - 12, verticalScrollBar()->value() + 1 + row * 20);
                graphicsPixmapItemArrow->setZValue(3);
            } else {
                graphicsPixmapItemArrow->setPos(width() - 13, row * 20 + 5);
                graphicsPixmapItemArrow->setZValue(-1);
            }

            if (std::get<2>(_oldItemInfo) == row && std::get<1>(_oldItemInfo) == mouseOverArrow) {
                // DO NOTHING
            } else {
                if (std::get<0>(_oldItemInfo)) {
                    std::pair<QGraphicsItem*,
                              int> p = std::make_pair(std::get<0>(_oldItemInfo), std::get<2>(_oldItemInfo));
                    std::vector<std::pair<QGraphicsItem*, int> >::iterator it = std::find(
                        _metaLabels.begin(), _metaLabels.end(), p);
                    if (it != _metaLabels.end()) {
                        _metaLabels.erase(it);
                    }
                    scene()->removeItem(std::get<0>(_oldItemInfo));
                }
                std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(graphicsPixmapItemArrow, mouseOverArrow,
                                                                               row);
                _oldItemInfo = tmp;
                if (mouseOverArrow != MouseOverValue::OPEN_EYE && mouseOverArrow != MouseOverValue::CLOSED_EYE) {
                    std::pair<QGraphicsItem*, int> p = std::make_pair(graphicsPixmapItemArrow, row);
                    _metaLabels.push_back(p);
                }
                scene()->addItem(graphicsPixmapItemArrow);
            }
        } else {
            if (std::get<0>(_oldItemInfo)) {
                scene()->removeItem(std::get<0>(_oldItemInfo));
            }
            std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
            _oldItemInfo = tmp;
        }
    } else {
        if (std::get<0>(_oldItemInfo)) {
            std::pair<QGraphicsItem*, int> p = std::make_pair(std::get<0>(_oldItemInfo), std::get<2>(_oldItemInfo));
            std::vector<std::pair<QGraphicsItem*, int> >::iterator it = std::find(_metaLabels.begin(),
                                                                                  _metaLabels.end(), p);
            if (it != _metaLabels.end()) {
                _metaLabels.erase(it);
            }
            scene()->removeItem(std::get<0>(_oldItemInfo));
        }
        std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned> tmp(nullptr, MouseOverValue::NONE, -1);
        _oldItemInfo = tmp;
    }
    if (QGraphicsItem* graphicsItem = scene()->itemAt(scenePt, transform())) {
        QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);
        if (graphicsPixmapItem) {
            setCursor(Qt::PointingHandCursor);
        } else {
            setCursor(Qt::ArrowCursor);
        }
    } else {
        setCursor(Qt::ArrowCursor);
    }
}

//---------------------------------------------------------
//   TRiwLabels::cursorIsOn
//---------------------------------------------------------

QString TRowLabels::cursorIsOn()
{
    QPointF scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
    QGraphicsItem* graphicsItem = scene()->itemAt(scenePos, transform());
    if (graphicsItem) {
        auto it = _metaLabels.begin();
        for (; it != _metaLabels.end(); ++it) {
            if ((*it).first == graphicsItem) {
                break;
            }
        }
        if (it != _metaLabels.end()) {
            return "meta";
        } else {
            return "instrument";
        }
    } else {
        return "";
    }
}

//---------------------------------------------------------
//   Timeline
//---------------------------------------------------------

Timeline::Timeline(TDockWidget* dockWidget, QWidget* parent)
    : QGraphicsView(parent)
{
    setFocusPolicy(Qt::NoFocus);
    setAlignment(Qt::Alignment((Qt::AlignLeft | Qt::AlignTop)));
    setAttribute(Qt::WA_OpaquePaintEvent);

    // theming
    _lightTheme.backgroundColor      = QColor(192, 192, 192);
    _lightTheme.labelsColor1         = QColor(Qt::black);
    _lightTheme.labelsColor2         = QColor(150, 150, 150);
    _lightTheme.labelsColor3         = QColor(211, 211, 211);
    _lightTheme.gridColor1           = QColor(150, 150, 150);
    _lightTheme.gridColor2           = QColor(211, 211, 211);
    _lightTheme.measureMetaColor     = QColor(0, 0, 0);
    _lightTheme.selectionColor       = QColor(173, 216, 230);
    _lightTheme.nonVisiblePenColor   = QColor(100, 150, 250);
    _lightTheme.nonVisibleBrushColor = QColor(192, 192, 192);
    _lightTheme.colorBoxColor        = QColor(Qt::darkGray);
    _lightTheme.metaValuePenColor    = QColor(Qt::black);
    _lightTheme.metaValueBrushColor  = QColor(Qt::gray);

    _darkTheme.backgroundColor       = QColor(35, 35, 35);
    _darkTheme.labelsColor1          = QColor(225, 225, 225);
    _darkTheme.labelsColor2          = QColor(55, 55, 55);
    _darkTheme.labelsColor3          = QColor(70, 70, 70);
    _darkTheme.gridColor1            = QColor(50, 50, 50);
    _darkTheme.gridColor2            = QColor(75, 75, 75);
    _darkTheme.measureMetaColor      = QColor(200, 200, 200);
    _darkTheme.selectionColor        = QColor(55, 70, 75);
    _darkTheme.nonVisiblePenColor    = QColor(40, 60, 80);
    _darkTheme.nonVisibleBrushColor  = QColor(55, 55, 55);
    _darkTheme.colorBoxColor         = QColor(Qt::gray);
    _darkTheme.metaValuePenColor     = QColor(Qt::lightGray);
    _darkTheme.metaValueBrushColor   = QColor(Qt::darkGray);

    _scrollArea = dockWidget;
    QSplitter* split = static_cast<QSplitter*>(_scrollArea->widget());

    _rowNames = new TRowLabels(dockWidget, this);
    split->addWidget(_rowNames);
    split->addWidget(this);
    split->setChildrenCollapsible(false);
    split->setStretchFactor(0, 0);
    split->setStretchFactor(1, 0);

    setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);

    setScene(new QGraphicsScene);
    setSceneRect(0, 0, 100, 100);
    scene()->setBackgroundBrush(QBrush(activeTheme().backgroundColor));

    connect(verticalScrollBar(),SIGNAL(valueChanged(int)),_rowNames->verticalScrollBar(),SLOT(setValue(int)));
    connect(verticalScrollBar(),SIGNAL(valueChanged(int)),this,SLOT(handleScroll(int)));
    connect(_rowNames, SIGNAL(swapMeta(uint,bool)), this, SLOT(swapMeta(uint,bool)));
    connect(this, SIGNAL(moved(QPointF)), _rowNames, SLOT(mouseOver(QPointF)));

    std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t1(tr("Tempo"), &Ms::Timeline::tempoMeta, true);
    std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t2(tr("Time Signature"), &Ms::Timeline::timeMeta,
                                                                          true);
    std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t3(tr("Rehearsal Mark"),
                                                                          &Ms::Timeline::rehearsalMeta, true);
    std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t4(tr("Key Signature"), &Ms::Timeline::keyMeta,
                                                                          true);
    std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t5(tr("Barlines"), &Ms::Timeline::barlineMeta,
                                                                          true);
    std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t6(tr("Jumps and Markers"),
                                                                          &Ms::Timeline::jumpMarkerMeta, true);
    std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t7(tr("Measures"), &Ms::Timeline::measureMeta,
                                                                          true);
    _metas.push_back(t1);
    _metas.push_back(t2);
    _metas.push_back(t3);
    _metas.push_back(t4);
    _metas.push_back(t5);
    _metas.push_back(t6);
    _metas.push_back(t7);

    std::tuple<QGraphicsItem*, int, QColor> ohi(nullptr, -1, QColor());
    _oldHoverInfo = ohi;
    std::tuple<int, qreal, Element*, Element*, bool> ri(0, 0, nullptr, nullptr, false);
    _repeatInfo = ri;

    static const char* startRepeat[] = {
        "7 14 2 1",
        "# c #000000",
        ". c None",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#.##",
        "##.#.##",
        "##.#...",
        "##.#...",
        "##.#.##",
        "##.#.##",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#..."
    };

    static const char* endRepeat[] = {
        "7 14 2 1",
        "# c #000000",
        ". c None",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "##.#.##",
        "##.#.##",
        "...#.##",
        "...#.##",
        "##.#.##",
        "##.#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##"
    };

    static const char* endBarline[] = {
        "7 14 2 1",
        "# c #000000",
        ". c None",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##",
        "...#.##"
    };

    static const char* doubleBarline[] = {
        "7 14 2 1",
        "# c #000000",
        ". c None",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#..",
        "..#.#.."
    };

    static const char* reverseEndBarline[] = {
        "7 14 2 1",
        "# c #000000",
        ". c None",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#...",
        "##.#..."
    };

    static const char* heavyBarline[] = {
        "6 14 2 1",
        "# c #000000",
        ". c None",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##..",
        "..##.."
    };

    static const char* doubleHeavyBarline[] = {
        "7 14 2 1",
        "# c #000000",
        ". c None",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##.",
        ".##.##."
    };

    QPixmap* startRepeatPixmap = new QPixmap(startRepeat);
    QPixmap* endRepeatPixmap = new QPixmap(endRepeat);
    QPixmap* endBarlinePixmap = new QPixmap(endBarline);
    QPixmap* doubleBarlinePixmap = new QPixmap(doubleBarline);
    QPixmap* reverseEndBarlinePixmap = new QPixmap(reverseEndBarline);
    QPixmap* heavyBarlinePixmap = new QPixmap(heavyBarline);
    QPixmap* doubleHeavyBarlinePixmap = new QPixmap(doubleHeavyBarline);

    _barlines[BarLine::userTypeName(BarLineType::START_REPEAT)] = startRepeatPixmap;
    _barlines[BarLine::userTypeName(BarLineType::END_REPEAT)] = endRepeatPixmap;
    _barlines[BarLine::userTypeName(BarLineType::END)] = endBarlinePixmap;
    _barlines[BarLine::userTypeName(BarLineType::DOUBLE)] = doubleBarlinePixmap;
    _barlines[BarLine::userTypeName(BarLineType::REVERSE_END)] = reverseEndBarlinePixmap;
    _barlines[BarLine::userTypeName(BarLineType::HEAVY)] = heavyBarlinePixmap;
    _barlines[BarLine::userTypeName(BarLineType::DOUBLE_HEAVY)] = doubleHeavyBarlinePixmap;
}

//---------------------------------------------------------
//   Timeline::drawGrid
//---------------------------------------------------------

void Timeline::drawGrid(int globalRows, int globalCols)
{
    scene()->clear();
    _metaRows.clear();

    if (globalRows == 0 || globalCols == 0) {
        return;
    }
    int stagger = 0;
    unsigned numMetas = nmetas();
    setMinimumHeight(_gridHeight * (numMetas + 1) + 5 + horizontalScrollBar()->height());
    setMinimumWidth(_gridWidth * 3);
    _globalZValue = 1;

    // Draw grid
    Measure* currMeasure = _score->firstMeasure();
    QList<Part*> partList = getParts();
    for (int col = 0; col < globalCols; col++) {
        for (int row = 0; row < globalRows; row++) {
            QGraphicsRectItem* graphicsRectItem = new QGraphicsRectItem(col * _gridWidth,
                                                                        _gridHeight * (row + numMetas) + 3,
                                                                        _gridWidth,
                                                                        _gridHeight);

            setMetaData(graphicsRectItem, row, ElementType::INVALID, currMeasure, false, 0);

            QString translateMeasure = tr("Measure");
            QChar initialLetter = translateMeasure[0];
            QTextDocument doc;
            QString partName = "";
            if (partList.size() > row) {
                doc.setHtml(partList.at(row)->longName());
                partName = doc.toPlainText();
            }
            if (partName.isEmpty() && partList.size() > row) {
                partName = partList.at(row)->instrumentName();
            }

            graphicsRectItem->setToolTip(initialLetter + QString(" ") + QString::number(currMeasure->no() + 1) + QString(
                                             ", ") + partName);
            graphicsRectItem->setPen(QPen(activeTheme().backgroundColor));
            graphicsRectItem->setBrush(QBrush(colorBox(graphicsRectItem)));
            graphicsRectItem->setZValue(-3);
            scene()->addItem(graphicsRectItem);
        }

        currMeasure = currMeasure->nextMeasure();
    }
    setSceneRect(0, 0, getWidth(), getHeight());

    //Draw meta rows and separator
    QGraphicsLineItem* graphicsLineItemSeparator = new QGraphicsLineItem(0,
                                                                         _gridHeight * numMetas + verticalScrollBar()->value() + 1,
                                                                         getWidth() - 1,
                                                                         _gridHeight * numMetas + verticalScrollBar()->value()
                                                                         + 1);
    graphicsLineItemSeparator->setPen(QPen(activeTheme().gridColor1, 4));
    graphicsLineItemSeparator->setZValue(-2);
    scene()->addItem(graphicsLineItemSeparator);
    std::pair<QGraphicsItem*, int> pairGraphicsIntSeparator = std::make_pair(graphicsLineItemSeparator, numMetas);
    _metaRows.push_back(pairGraphicsIntSeparator);

    for (unsigned row = 0; row < numMetas; row++) {
        QGraphicsRectItem* metaRow = new QGraphicsRectItem(0,
                                                           _gridHeight * row + verticalScrollBar()->value(),
                                                           getWidth(),
                                                           _gridHeight);
        metaRow->setBrush(QBrush(activeTheme().gridColor2));
        metaRow->setPen(QPen(activeTheme().gridColor1));
        metaRow->setData(0, QVariant::fromValue<int>(-1));

        scene()->addItem(metaRow);

        std::pair<QGraphicsItem*, int> pairGraphicsIntMeta = std::make_pair(metaRow, row);
        _metaRows.push_back(pairGraphicsIntMeta);
    }

    int xPos = 0;

    // Create stagger array if _collapsedMeta is false
#if (!defined (_MSCVER) && !defined (_MSC_VER))
    int staggerArr[numMetas];
    for (unsigned row = 0; row < numMetas; row++) {
        staggerArr[row] = 0;
    }
#else
    // MSVC does not support VLA. Replace with std::vector. If profiling determines that the
    // heap allocation is slow, an optimization might be used.
    std::vector<int> staggerArr(numMetas, 0);    // Default initialized, loop not required
#endif

    bool noKey = true;
    std::get<4>(_repeatInfo) = false;

    for (Measure* cm = _score->firstMeasure(); cm; cm = cm->nextMeasure()) {
        for (Segment* currSeg = cm->first(); currSeg; currSeg = currSeg->next()) {
            // Toggle noKey if initial key signature is found
            if (currSeg->isKeySigType() && cm == _score->firstMeasure()) {
                if (noKey && currSeg->tick().isZero()) {
                    noKey = false;
                }
            }

            // If no initial key signature is found, add key signature
            if (cm == _score->firstMeasure() && noKey
                && (currSeg->isTimeSigType() || currSeg->isChordRestType())) {
                if (getMetaRow(tr("Key Signature")) != numMetas) {
                    if (_collapsedMeta) {
                        keyMeta(0, &stagger, xPos);
                    } else {
                        keyMeta(0, &staggerArr[getMetaRow(tr("Key Signature"))], xPos);
                    }
                }
                noKey = false;
            }
            int row = 0;
            for (auto it = _metas.begin(); it != _metas.end(); ++it) {
                std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
                if (!std::get<2>(meta)) {
                    continue;
                }
                void (Timeline::* func)(Segment*, int*, int) = std::get<1>(meta);
                if (_collapsedMeta) {
                    (this->*func)(currSeg, &stagger, xPos);
                } else {
                    (this->*func)(currSeg, &staggerArr[row], xPos);
                }
                row++;
            }
        }
        // Handle all jumps here
        if (getMetaRow(tr("Jumps and Markers")) != numMetas) {
            ElementList measureElementsList = cm->el();
            for (Element* element : measureElementsList) {
                std::get<3>(_repeatInfo) = element;
                if (element->isMarker()) {
                    jumpMarkerMeta(0, &stagger, xPos);
                }
            }
            for (Element* element : measureElementsList) {
                if (element->isJump()) {
                    std::get<2>(_repeatInfo) = element;
                    if (_collapsedMeta) {
                        jumpMarkerMeta(0, &stagger, xPos);
                    } else {
                        jumpMarkerMeta(0, &std::get<0>(_repeatInfo), xPos);
                    }
                }
            }
        }
        stagger = 0;
        std::get<0>(_repeatInfo) = 0;

        for (unsigned row = 0; row < numMetas; row++) {
            staggerArr[row] = 0;
        }
        xPos += _gridWidth;
        std::get<4>(_repeatInfo) = false;
    }
    drawSelection();
}

//---------------------------------------------------------
//   Timeline::tempoMeta
//---------------------------------------------------------

void Timeline::tempoMeta(Segment* seg, int* stagger, int pos)
{
    // Find position of tempoMeta in metas
    int row = getMetaRow(tr("Tempo"));

    // Add all tempo texts in this segment
    const std::vector<Element*> annotations = seg->annotations();
    for (Element* element : annotations) {
        if (element->isTempoText()) {
            TempoText* text = toTempoText(element);
            qreal x = pos + (*stagger) * _spacing;
            if (addMetaValue(x, pos, text->plainText(), row, ElementType::TEMPO_TEXT, element, 0, seg->measure())) {
                (*stagger)++;
                _globalZValue++;
            }
        }
    }
}

//---------------------------------------------------------
//   Timeline::timeMeta
//---------------------------------------------------------

void Timeline::timeMeta(Segment* seg, int* stagger, int pos)
{
    if (!seg->isTimeSigType()) {
        return;
    }

    int x = pos + (*stagger) * _spacing;

    // Find position of timeMeta in metas
    int row = getMetaRow(tr("Time Signature"));

    TimeSig* originalTimeSig = toTimeSig(seg->element(0));
    if (!originalTimeSig) {
        return;
    }

    // Check if same across all staves
    const int nrows = _score->staves().size();
    bool same = true;
    for (int track = 0; track < nrows; track++) {
        const TimeSig* currTimeSig = toTimeSig(seg->element(track * VOICES));
        if (!currTimeSig) {
            same = false;
            break;
        }
        if (*currTimeSig == *originalTimeSig) {
            continue;
        }
        same = false;
        break;
    }
    if (!same) {
        return;
    }
    QString text = QString::number(originalTimeSig->numerator()) + QString("/") + QString::number(
        originalTimeSig->denominator());

    if (addMetaValue(x, pos, text, row, ElementType::TIMESIG, 0, seg, seg->measure())) {
        (*stagger)++;
        _globalZValue++;
    }
}

//---------------------------------------------------------
//   Timeline::rehearsalMeta
//---------------------------------------------------------

void Timeline::rehearsalMeta(Segment* seg, int* stagger, int pos)
{
    int row = getMetaRow(tr("Rehearsal Mark"));

    for (Element* element : seg->annotations()) {
        int x = pos + (*stagger) * _spacing;
        if (element->isRehearsalMark()) {
            RehearsalMark* rehersal_mark = toRehearsalMark(element);
            if (!rehersal_mark) {
                continue;
            }

            if (addMetaValue(x, pos, rehersal_mark->plainText(), row, ElementType::REHEARSAL_MARK, element, 0,
                             seg->measure())) {
                (*stagger)++;
                _globalZValue++;
            }
        }
    }
}

//---------------------------------------------------------
//   Timeline::keyMeta
//---------------------------------------------------------

void Timeline::keyMeta(Segment* seg, int* stagger, int pos)
{
    // If seg is nullptr, handle initial key signature
    if (seg && !seg->isKeySigType()) {
        return;
    }

    int row = getMetaRow(tr("Key Signature"));
    std::map<Key, int> keyFrequencies;
    QList<Staff*> staves = _score->staves();

    int track = 0;
    for (Staff* stave : staves) {
        if (!stave->show()) {
            track += VOICES;
            continue;
        }

        // Ignore unpitched staves
        if ((seg && !stave->isPitchedStaff(seg->tick())) || (!seg && !stave->isPitchedStaff(Fraction(0,1)))) {
            track += VOICES;
            continue;
        }

        // Add corrected key signature to map
        // Atonal -> Key::INVALID
        // Custom -> Key::NUM_OF
        const KeySig* currKeySig = nullptr;
        if (seg) {
            currKeySig = toKeySig(seg->element(track));
        }

        Key globalKey;
        if (seg) {
            globalKey = stave->key(seg->tick());
        } else {
            globalKey = stave->key(Fraction(0,1));
        }
        if (currKeySig) {
            if (currKeySig->generated()) {
                return;
            }
            globalKey = currKeySig->key();
        }

        if (currKeySig && currKeySig->isAtonal()) {
            globalKey = Key::INVALID;
        } else if (currKeySig && currKeySig->isCustom()) {
            globalKey = Key::NUM_OF;
        } else {
            const Interval currInterval = stave->part()->instrument()->transpose();
            globalKey = transposeKey(globalKey, currInterval, stave->part()->preferSharpFlat());
        }

        std::map<Key, int>::iterator it = keyFrequencies.find(globalKey);
        if (it != keyFrequencies.end()) {
            keyFrequencies[globalKey]++;
        } else {
            keyFrequencies[globalKey] = 1;
        }

        track += VOICES;
    }

    // Change key into QString
    Key newKey = Key::C;
    int maxKeyFreq = 0;
    for (std::map<Key, int>::iterator iter = keyFrequencies.begin(); iter != keyFrequencies.end(); ++iter) {
        if (iter->second > maxKeyFreq) {
            newKey = iter->first;
            maxKeyFreq = iter->second;
        }
    }
    QString keyText;
    QString tooltip;
    if (newKey == Key::INVALID) {
        keyText = "X";
        tooltip = qApp->translate("MuseScore", keyNames[15]);
    } else if (newKey == Key::NUM_OF) {
        keyText = "?";
        tooltip = tr("Custom Key Signature");
    } else if (int(newKey) == 0) {
        keyText = "\u266E";
        tooltip = qApp->translate("MuseScore", keyNames[14]);
    } else if (int(newKey) < 0) {
        keyText = QString::number(abs(int(newKey))) + "\u266D";
        tooltip = qApp->translate("MuseScore", keyNames[(7 + int(newKey)) * 2 + 1]);
    } else {
        keyText = QString::number(abs(int(newKey))) + "\u266F";
        tooltip = qApp->translate("MuseScore", keyNames[(int(newKey) - 1) * 2]);
    }

    int x = pos + (*stagger) * _spacing;
    Measure* measure = seg ? seg->measure() : 0;
    if (addMetaValue(x, pos, keyText, row, ElementType::KEYSIG, 0, seg, measure, tooltip)) {
        (*stagger)++;
        _globalZValue++;
    }
}

//---------------------------------------------------------
//   Timeline::barLineMeta
//---------------------------------------------------------

void Timeline::barlineMeta(Segment* seg, int* stagger, int pos)
{
    if (!seg->isBeginBarLineType() && !seg->isEndBarLineType() && !seg->isBarLine()
        && !seg->isStartRepeatBarLineType()) {
        return;
    }

    // Find position of repeat_meta in metas
    int row = getMetaRow(tr("Barlines"));

    QString repeatText = "";
    BarLine* barline = toBarLine(seg->element(0));

    if (barline) {
        switch (barline->barLineType()) {
        case BarLineType::START_REPEAT:
        case BarLineType::END_REPEAT:
        case BarLineType::DOUBLE:
        case BarLineType::END:
            repeatText = BarLine::userTypeName(barline->barLineType());
        case BarLineType::END_START_REPEAT:
            // actually an end repeat followed by a start repeat, so nothing needs to be done here
            break;
        default:
            break;
        }
        _isBarline = true;
    } else {
        return;
    }

    Measure* measure = seg->measure();
    ElementType elementType = ElementType::BAR_LINE;
    Element* element = nullptr;

    if (repeatText == "") {
        _isBarline = false;
        return;
    }

    int x = pos + (*stagger) * _spacing;
    if (addMetaValue(x, pos, repeatText, row, elementType, element, seg, measure)) {
        (*stagger)++;
        _globalZValue++;
    }
    _isBarline = false;
}

//---------------------------------------------------------
//   Timeline::jumpMarkerMeta
//---------------------------------------------------------

void Timeline::jumpMarkerMeta(Segment* seg, int* stagger, int pos)
{
    if (seg) {
        return;
    }

    // Find position of repeat_meta in metas
    int row = getMetaRow(tr("Jumps and Markers"));

    QString text = "";
    Element* element = nullptr;
    if (std::get<2>(_repeatInfo)) {
        element = std::get<2>(_repeatInfo);
    } else if (std::get<3>(_repeatInfo)) {
        element = std::get<3>(_repeatInfo);
    }

    Measure* measure;
    ElementType elementType;
    if (std::get<2>(_repeatInfo)) {
        Jump* jump = toJump(std::get<2>(_repeatInfo));
        text = jump->plainText();
        measure = jump->measure();
        elementType = ElementType::JUMP;
    } else {
        Marker* marker = toMarker(std::get<3>(_repeatInfo));
        QList<TextFragment> tf_list = marker->fragmentList();
        for (TextFragment tf : tf_list) {
            text.push_back(tf.text);
        }
        measure = marker->measure();
        if (marker->markerType() == Marker::Type::FINE || marker->markerType() == Marker::Type::TOCODA) {
            elementType = ElementType::MARKER;
            std::get<2>(_repeatInfo) = std::get<3>(_repeatInfo);
            std::get<3>(_repeatInfo) = nullptr;
        } else {
            elementType = ElementType::MARKER;
        }
    }

    if (text == "") {
        std::get<2>(_repeatInfo) = nullptr;
        std::get<3>(_repeatInfo) = nullptr;
        return;
    }

    int x = pos + (*stagger) * _spacing;
    if (addMetaValue(x, pos, text, row, elementType, element, seg, measure)) {
        (*stagger)++;
        _globalZValue++;
    }
    std::get<2>(_repeatInfo) = nullptr;
    std::get<3>(_repeatInfo) = nullptr;
}

//---------------------------------------------------------
//   Timeline::measureMeta
//---------------------------------------------------------

void Timeline::measureMeta(Segment*, int*, int pos)
{
    // Increment decided by zoom level
    int incrementValue = 1;
    int halfway = (_maxZoom + _minZoom) / 2;
    if (_gridWidth <= _maxZoom && _gridWidth > halfway) {
        incrementValue = 1;
    } else if (_gridWidth <= halfway && _gridWidth > _minZoom) {
        incrementValue = 5;
    } else {
        incrementValue = 10;
    }

    int currMeasureNumber = pos / _gridWidth;
    if (currMeasureNumber == _globalMeasureNumber) {
        return;
    }

    _globalMeasureNumber = currMeasureNumber;
    // Check if 1 or 5*n
    if (currMeasureNumber + 1 != 1 && (currMeasureNumber + 1) % incrementValue != 0) {
        return;
    }

    // Find position of measureMeta in metas
    int row = getMetaRow(tr("Measures"));

    // Adjust number
    Measure* currMeasure;
    for (currMeasure = _score->firstMeasure(); currMeasureNumber != 0;
         currMeasureNumber--, currMeasure = currMeasure->nextMeasure()) {
    }

    // Add measure number
    QString measureNumber = (currMeasure->irregular()) ? "( )" : QString::number(currMeasure->no() + 1);
    QGraphicsTextItem* graphicsTextItem = new QGraphicsTextItem(measureNumber);
    graphicsTextItem->setDefaultTextColor(activeTheme().measureMetaColor);
    graphicsTextItem->setX(pos);
    graphicsTextItem->setY(_gridHeight * row + verticalScrollBar()->value());

    QFont f = graphicsTextItem->font();
    f.setPointSizeF(7.0);
    graphicsTextItem->setFont(f);

    // Center text
    qreal remainingWidth  = _gridWidth - graphicsTextItem->boundingRect().width();
    qreal remainingHeight = _gridHeight - graphicsTextItem->boundingRect().height();
    graphicsTextItem->setX(graphicsTextItem->x() + remainingWidth / 2);
    graphicsTextItem->setY(graphicsTextItem->y() + remainingHeight / 2);

    int endOfText = graphicsTextItem->x() + graphicsTextItem->boundingRect().width();
    int endOfGrid = getWidth();
    if (endOfText <= endOfGrid) {
        scene()->addItem(graphicsTextItem);

        std::pair<QGraphicsItem*, int> pairMeasureText = std::make_pair(graphicsTextItem, row);
        _metaRows.push_back(pairMeasureText);
    }
}

//---------------------------------------------------------
//   Timeline::getMetaRow
//---------------------------------------------------------

unsigned Timeline::getMetaRow(QString targetText)
{
    if (_collapsedMeta) {
        if (targetText == tr("Measures")) {
            return 1;
        } else {
            return 0;
        }
    }
    int row = 0;
    for (auto it = _metas.begin(); it != _metas.end(); ++it) {
        std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
        QString metaText = std::get<0>(meta);
        bool visible = std::get<2>(meta);
        if (metaText == targetText && visible) {
            break;
        } else if (!visible) {
            continue;
        }
        row++;
    }
    return row;
}

//---------------------------------------------------------
//   Timeline::addMetaValue
//---------------------------------------------------------

bool Timeline::addMetaValue(int x, int pos, QString metaText, int row, ElementType elementType, Element* element,
                            Segment* seg, Measure* measure, QString tooltip)
{
    QGraphicsTextItem* graphicsTextItem = new QGraphicsTextItem(metaText);
    qreal textWidth = graphicsTextItem->boundingRect().width();

    QGraphicsPixmapItem* graphicsPixmapItem = nullptr;
    if (_isBarline) {
        graphicsPixmapItem = new QGraphicsPixmapItem(*_barlines[metaText]);
    }

    if (graphicsPixmapItem) {
        textWidth = 10;
        if (textWidth > _gridWidth) {
            textWidth = _gridWidth;
            if (metaText == BarLine::userTypeName(BarLineType::END_REPEAT) && std::get<4>(_repeatInfo)) {
                textWidth /= 2;
            }
        }
    }

    if (textWidth + x > getWidth()) {
        textWidth = getWidth() - x;
    }

    // Adjust x for end repeats
    if ((metaText == BarLine::userTypeName(BarLineType::END_REPEAT) ||
        metaText == BarLine::userTypeName(BarLineType::END) ||
        metaText == BarLine::userTypeName(BarLineType::DOUBLE) ||
        metaText == BarLine::userTypeName(BarLineType::REVERSE_END) ||
        metaText == BarLine::userTypeName(BarLineType::HEAVY) ||
        metaText == BarLine::userTypeName(BarLineType::DOUBLE_HEAVY) ||
        std::get<2>(_repeatInfo))
      && !_collapsedMeta) {
        if (std::get<0>(_repeatInfo) > 0) {
            x = pos + _gridWidth - std::get<1>(_repeatInfo) + std::get<0>(_repeatInfo) * _spacing;
        } else {
            x = pos + _gridWidth - textWidth;
            std::get<1>(_repeatInfo) = textWidth;
        }
        // Check if extending past left side
        if (x < 0) {
            textWidth = textWidth + x;
            x = 0;
        }
    }

    // Return if past width
    if (x >= getWidth()) {
        return false;
    }

    QGraphicsItem* itemToAdd;
    if (graphicsPixmapItem) {
        // Exact values required for repeat pixmap to work visually
        if (textWidth != 10) {
            graphicsPixmapItem = new QGraphicsPixmapItem();
        }
        if (metaText == BarLine::userTypeName(BarLineType::START_REPEAT)) {
            std::get<4>(_repeatInfo) = true;
        }
        graphicsPixmapItem->setX(x + 2);
        graphicsPixmapItem->setY(_gridHeight * row + verticalScrollBar()->value() + 3);
        itemToAdd = graphicsPixmapItem;
    } else if (metaText == "\uE047" || metaText == "\uE048") {
        graphicsTextItem->setX(x);
        graphicsTextItem->setY(_gridHeight * row + verticalScrollBar()->value() - 2);
        itemToAdd = graphicsTextItem;
    } else if (row == 0) {
        graphicsTextItem->setX(x);
        graphicsTextItem->setY(_gridHeight * row + verticalScrollBar()->value() - 6);
        itemToAdd = graphicsTextItem;
    } else {
        graphicsTextItem->setX(x);
        graphicsTextItem->setY(_gridHeight * row + verticalScrollBar()->value() - 1);
        itemToAdd = graphicsTextItem;
    }

    QFontMetrics f(QApplication::font());
    QString partName = f.elidedText(graphicsTextItem->toPlainText(),
                                    Qt::ElideRight,
                                    textWidth);

    // Set tool tip if elided
    if (tooltip != "") {
        graphicsTextItem->setToolTip(tooltip);
    } else if (partName != metaText) {
        graphicsTextItem->setToolTip(graphicsTextItem->toPlainText());
    }
    graphicsTextItem->setPlainText(partName);

    // Make text fit within rectangle
    while (graphicsTextItem->boundingRect().width() > textWidth
           && graphicsTextItem->toPlainText() != "") {
        QString text = graphicsTextItem->toPlainText();
        text.chop(1);
        graphicsTextItem->setPlainText(text);
    }

    QGraphicsRectItem* graphicsRectItem = new QGraphicsRectItem(x,
                                                                _gridHeight * row + verticalScrollBar()->value(),
                                                                textWidth,
                                                                _gridHeight);
    if (tooltip != "") {
        graphicsRectItem->setToolTip(tooltip);
    } else if (partName != metaText || graphicsPixmapItem) {
        graphicsRectItem->setToolTip(metaText);
    }

    setMetaData(graphicsRectItem, -1, elementType, measure, true, element, itemToAdd, seg);
    setMetaData(itemToAdd, -1, elementType, measure, true, element, graphicsRectItem, seg);

    graphicsRectItem->setZValue(_globalZValue);
    itemToAdd->setZValue(_globalZValue);

    graphicsRectItem->setPen(QPen(activeTheme().metaValuePenColor));
    graphicsRectItem->setBrush(QBrush(activeTheme().metaValueBrushColor));

    scene()->addItem(graphicsRectItem);
    scene()->addItem(itemToAdd);

    std::pair<QGraphicsItem*, int> pairTimeRect = std::make_pair(graphicsRectItem, row);
    std::pair<QGraphicsItem*, int> pairTimeText = std::make_pair(itemToAdd, row);
    _metaRows.push_back(pairTimeRect);
    _metaRows.push_back(pairTimeText);

    if (metaText == BarLine::userTypeName(BarLineType::END_REPEAT)) {
        std::get<0>(_repeatInfo)++;
    }

    return true;
}

//---------------------------------------------------------
//   Timeline::setMetaData
//---------------------------------------------------------

void Timeline::setMetaData(QGraphicsItem* gi, int staff, ElementType et, Measure* m, bool full_measure, Element* e,
                           QGraphicsItem* pairItem, Segment* seg)
{
    // full_measure true for meta values
    // pr is null for grid items, set for meta values
    // seg is set if key meta
    gi->setData(0, QVariant::fromValue<int>(staff));
    gi->setData(1, QVariant::fromValue<ElementType>(et));
    gi->setData(2, QVariant::fromValue<void*>(m));
    gi->setData(3, QVariant::fromValue<bool>(full_measure));
    gi->setData(4, QVariant::fromValue<void*>(e));
    gi->setData(5, QVariant::fromValue<void*>(pairItem));
    gi->setData(6, QVariant::fromValue<void*>(seg));
}

//---------------------------------------------------------
//   Timeline::getWidth
//---------------------------------------------------------

int Timeline::getWidth() const
{
    if (_score) {
        return int(_score->nmeasures() * _gridWidth);
    } else {
        return 0;
    }
}

//---------------------------------------------------------
//   Timeline::getHeight
//---------------------------------------------------------

int Timeline::getHeight() const
{
    if (_score) {
        return int((nstaves() + nmetas()) * _gridHeight + 3);
    } else {
        return 0;
    }
}

//---------------------------------------------------------
//   Timeline::correctStave
//---------------------------------------------------------

int Timeline::correctStave(int stave)
{
    // Find correct stave (skipping hidden staves)
    QList<Staff*> list = _score->staves();
    int count = 0;
    while (stave >= count) {
        if (count >= list.size()) {
            count = list.size() - 1;
            return count;
        }
        if (!list.at(count)->show()) {
            stave++;
        }
        count++;
    }
    return stave;
}

//---------------------------------------------------------
//   Timeline::correctPart
//---------------------------------------------------------

int Timeline::correctPart(int stave)
{
    // Find correct stave (skipping hidden staves)
    QList<Staff*> list = _score->staves();
    int count = correctStave(stave);
    return getParts().indexOf(list.at(count)->part());
}

//---------------------------------------------------------
//   Timeline::getParts
//---------------------------------------------------------

QList<Part*> Timeline::getParts()
{
    QList<Part*> realPartList = _score->parts();
    QList<Part*> partList;
    for (Part* p : realPartList) {
        for (int i = 0; i < p->nstaves(); i++) {
            partList.append(p);
        }
    }

    return partList;
}

//---------------------------------------------------------
//   Timeline::changeSelection
//---------------------------------------------------------

void Timeline::changeSelection(SelState)
{
    scene()->blockSignals(true);
    scene()->clearSelection();

    QRectF selectionRect = _selectionPath.boundingRect();
    if (selectionRect == QRectF()) {
        return;
    }

    int nmeta = nmetas();

    // Get borders of the current viewport
    int leftBorder = horizontalScrollBar()->value();
    int rightBorder = horizontalScrollBar()->value() + viewport()->width();
    int topBorder = verticalScrollBar()->value() + nmeta * _gridHeight;
    int bottomBorder = verticalScrollBar()->value() + viewport()->height();

    bool selectionExtendsUp = false,    selectionExtendsLeft = false;
    bool selectionExtendsRight = false, selectionExtendsDown = false;

    // Figure out which directions the selection extends
    if (selectionRect.top() < topBorder) {
        selectionExtendsUp = true;
    }
    if (selectionRect.left() < leftBorder - 1) {
        selectionExtendsLeft = true;
    }
    if (selectionRect.right() > rightBorder) {
        selectionExtendsRight = true;
    }
    if (selectionRect.bottom() > bottomBorder) {
        selectionExtendsDown = true;
    }

    if (selectionExtendsDown
        && _oldSelectionRect.bottom() != selectionRect.bottom()
        && !_metaValue) {
        int newScrollbarValue = int(verticalScrollBar()->value() + selectionRect.bottom() - bottomBorder);
        verticalScrollBar()->setValue(newScrollbarValue);
    } else if (selectionExtendsUp
               && !selectionExtendsDown
               && _oldSelectionRect.bottom() != selectionRect.bottom()
               && !_metaValue
               && _oldSelectionRect.contains(selectionRect)) {
        int newScrollbarValue = int(verticalScrollBar()->value() + selectionRect.bottom() - bottomBorder);
        verticalScrollBar()->setValue(newScrollbarValue);
    }

    if (selectionExtendsRight
        && _oldSelectionRect.right() != selectionRect.right()) {
        int newScrollbarValue = int(horizontalScrollBar()->value() + selectionRect.right() - rightBorder);
        horizontalScrollBar()->setValue(newScrollbarValue);
    }
    if (selectionExtendsUp
        && _oldSelectionRect.top() != selectionRect.top()
        && !_metaValue) {
        int newScrollbarValue = int(selectionRect.top()) - nmeta * _gridHeight;
        verticalScrollBar()->setValue(newScrollbarValue);
    }
    if (selectionExtendsLeft
        && _oldSelectionRect.left() != selectionRect.left()) {
        int newScrollbarValue = int(selectionRect.left());
        horizontalScrollBar()->setValue(newScrollbarValue);
    }

    if (selectionExtendsLeft
        && !selectionExtendsRight
        && _oldSelectionRect.right() != selectionRect.right()
        && _oldSelectionRect.contains(selectionRect)) {
        int newScrollbarValue = int(horizontalScrollBar()->value() + selectionRect.right() - rightBorder);
        horizontalScrollBar()->setValue(newScrollbarValue);
    }
    if (selectionExtendsRight
        && !selectionExtendsLeft
        && _oldSelectionRect.left() != selectionRect.left()
        && _oldSelectionRect.contains(selectionRect)) {
        int newScrollbarValue = int(selectionRect.left());
        horizontalScrollBar()->setValue(newScrollbarValue);
    }

    if (selectionExtendsDown
        && !selectionExtendsUp
        && _oldSelectionRect.top() != selectionRect.top()
        && !_metaValue
        && _oldSelectionRect.contains(selectionRect)) {
        int newScrollbarValue = int(selectionRect.top()) - nmeta * _gridHeight;
        verticalScrollBar()->setValue(newScrollbarValue);
    }

    _oldSelectionRect = selectionRect;

    _metaValue = false;
    scene()->blockSignals(false);
}

//---------------------------------------------------------
//   Timeline::drawSelection
//---------------------------------------------------------

void Timeline::drawSelection()
{
    if (!_score) {
        return;
    }

    _selectionPath = QPainterPath();
    _selectionPath.setFillRule(Qt::WindingFill);

    std::set<std::tuple<Measure*, int, ElementType> > metaLabelsSet;

    const Selection& selection = _score->selection();
    const QList<Element*>& el = selection.elements();
    for (Element* element : el) {
        if (element->tick() == Fraction(-1,1)) {
            continue;
        } else {
            switch (element->type()) {
            case ElementType::INSTRUMENT_NAME:
            case ElementType::VBOX:
            case ElementType::HBOX:
            case ElementType::TEXT:
            case ElementType::TIE_SEGMENT:
            case ElementType::SLUR_SEGMENT:
            case ElementType::TIE:
            case ElementType::SLUR:
                continue;
                break;
            default: break;
            }
        }

        int staffIdx;
        Fraction tick = element->tick();
        Measure* measure = _score->tick2measure(tick);
        staffIdx = element->staffIdx();
        if (numToStaff(staffIdx) && !numToStaff(staffIdx)->show()) {
            continue;
        }

        if ((element->isTempoText()
             || element->isKeySig()
             || element->isTimeSig()
             || element->isRehearsalMark()
             || element->isJump()
             || element->isMarker())
            && !element->generated()) {
            staffIdx = -1;
        }

        if (element->isBarLine()) {
            staffIdx = -1;
            BarLine* barline = toBarLine(element);
            if (barline &&
              (barline->barLineType() == BarLineType::END_REPEAT ||
               barline->barLineType() == BarLineType::DOUBLE ||
               barline->barLineType() == BarLineType::REVERSE_END ||
               barline->barLineType() == BarLineType::HEAVY ||
               barline->barLineType() == BarLineType::DOUBLE_HEAVY ||
               barline->barLineType() == BarLineType::END) &&
              measure != _score->lastMeasure()) {
               if (measure->prevMeasure()) {
                   measure = measure->prevMeasure();
                }
            }
        }

        // element->type() for meta rows, invalid for everything else
        ElementType elementType = (staffIdx == -1) ? element->type() : ElementType::INVALID;

        // If has a multi measure rest, find the count and add each measure to it
        // ws: If style flag Sid::createMultiMeasureRests is not set, then
        // measure->mmRest() is not valid

        if (measure->mmRest() && measure->score()->styleB(Sid::createMultiMeasureRests)) {
            int mmrestCount = measure->mmRest()->mmRestCount();
            Measure* tmpMeasure = measure;
            for (int mmrestMeasure = 0; mmrestMeasure < mmrestCount; mmrestMeasure++) {
                std::tuple<Measure*, int, ElementType> tmp(tmpMeasure, staffIdx, elementType);
                metaLabelsSet.insert(tmp);
                tmpMeasure = tmpMeasure->nextMeasure();
            }
        } else {
            std::tuple<Measure*, int, ElementType> tmp(measure, staffIdx, elementType);
            metaLabelsSet.insert(tmp);
        }
    }

    QList<QGraphicsItem*> graphicsItemList = scene()->items();
    for (QGraphicsItem* graphicsItem : graphicsItemList) {
        int stave = graphicsItem->data(0).value<int>();
        ElementType elementType = graphicsItem->data(1).value<ElementType>();
        Measure* measure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());

        std::tuple<Measure*, int, ElementType> targetTuple(measure, stave, elementType);
        std::set<std::tuple<Measure*, int, ElementType> >::iterator it;
        it = metaLabelsSet.find(targetTuple);

        if (stave == -1 && it != metaLabelsSet.end()) {
            //Make sure the element is correct
            QList<Element*> elementList = _score->selection().elements();
            Element* targetElement = static_cast<Element*>(graphicsItem->data(4).value<void*>());
            Segment* seg = static_cast<Segment*>(graphicsItem->data(6).value<void*>());

            if (targetElement) {
                for (Element* element : elementList) {
                    if (element == targetElement) {
                        QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
                        if (graphicsRectItem) {
                            graphicsRectItem->setBrush(QBrush(activeTheme().selectionColor));
                        }
                    }
                }
            } else if (seg) {
                for (Element* element : elementList) {
                    QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
                    if (graphicsRectItem) {
                        for (int track = 0; track < _score->nstaves() * VOICES; track++) {
                            if (element == seg->element(track)) {
                                graphicsRectItem->setBrush(QBrush(activeTheme().selectionColor));
                            }
                        }
                    }
                }
            } else {
                QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
                if (graphicsRectItem) {
                    graphicsRectItem->setBrush(QBrush(activeTheme().selectionColor));
                }
            }
        }
        // Change color from gray to only blue
        else if (it != metaLabelsSet.end()) {
            QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
            graphicsRectItem->setBrush(QBrush(QColor(graphicsRectItem->brush().color().red(),
                                                     graphicsRectItem->brush().color().green(),
                                                     255)));
            _selectionPath.addRect(graphicsRectItem->rect());
        }
    }

    QGraphicsPathItem* graphicsPathItem = new QGraphicsPathItem(_selectionPath.simplified());
    if (selection.isRange()) {
        graphicsPathItem->setPen(QPen(QColor(0, 0, 255), 3));
    } else {
        graphicsPathItem->setPen(QPen(QColor(0, 0, 0), 1));
    }

    graphicsPathItem->setBrush(Qt::NoBrush);
    graphicsPathItem->setZValue(-1);
    scene()->addItem(graphicsPathItem);

    if (std::get<0>(_oldHoverInfo)) {
        std::get<0>(_oldHoverInfo) = nullptr;
        std::get<1>(_oldHoverInfo) = -1;
    }
}

//---------------------------------------------------------
//   Timeline::mousePressEvent
//---------------------------------------------------------

void Timeline::mousePressEvent(QMouseEvent* event)
{
    if (!_score) {
        return;
    }

    if (event->button() == Qt::RightButton) {
        return;
    }

    // Set as clicked
    _mousePressed = true;
    scene()->clearSelection();

    QPointF scenePt = mapToScene(event->pos());
    // Set as old location
    _oldLoc = QPoint(int(scenePt.x()), int(scenePt.y()));
    QList<QGraphicsItem*> graphicsItemList = scene()->items(scenePt);
    // Find highest z value for rect
    int maxZValue = -4;
    QGraphicsItem* currGraphicsItem = nullptr;
    for (QGraphicsItem* graphicsItem : graphicsItemList) {
        QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
        if (graphicsRectItem && graphicsItem->zValue() > maxZValue) {
            currGraphicsItem = graphicsItem;
            maxZValue = graphicsItem->zValue();
        }
    }
    if (currGraphicsItem) {
        int stave = currGraphicsItem->data(0).value<int>();
        Measure* currMeasure = static_cast<Measure*>(currGraphicsItem->data(2).value<void*>());
        if (numToStaff(stave) && !numToStaff(stave)->show()) {
            return;
        }

        if (!currMeasure) {
            int nmeta = nmetas();
            int bottomOfMeta = nmeta * _gridHeight + verticalScrollBar()->value();

            // Handle measure box clicks
            if (scenePt.y() > (nmeta - 1) * _gridHeight + verticalScrollBar()->value()
                && scenePt.y() < bottomOfMeta) {
                QRectF tmp(scenePt.x(), 0, 3, nmeta * _gridHeight + nstaves() * _gridHeight);
                QList<QGraphicsItem*> gl = scene()->items(tmp);
                Measure* measure = nullptr;

                for (QGraphicsItem* graphicsItem : gl) {
                    measure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());
                    //-3 z value is the grid square values
                    if (graphicsItem->zValue() == -3 && measure) {
                        break;
                    }
                }
                if (measure) {
                    _cv->adjustCanvasPosition(measure, false);
                }
            }
            if (scenePt.y() < bottomOfMeta) {
                return;
            }

            QList<QGraphicsItem*> gl = items(event->pos());
            for (QGraphicsItem* graphicsItem : gl) {
                currMeasure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());
                stave = graphicsItem->data(0).value<int>();
                if (currMeasure) {
                    break;
                }
            }
            if (!currMeasure) {
                _score->select(0, SelectType::SINGLE, 0);
                return;
            }
        }

        bool metaValueClicked = currGraphicsItem->data(3).value<bool>();

        scene()->clearSelection();
        if (metaValueClicked) {
            _metaValue = true;
            _oldSelectionRect = QRect();

            _cv->adjustCanvasPosition(currMeasure, false, 0);
            verticalScrollBar()->setValue(0);

            Segment* seg = static_cast<Segment*>(currGraphicsItem->data(6).value<void*>());

            if (seg) {
                _score->deselectAll();
                for (int track = 0; track < _score->nstaves() * VOICES; track++) {
                    Element* element = seg->element(track);
                    if (element) {
                        _score->select(seg->element(track), SelectType::ADD);
                    }
                }
            } else {
                // Also select the elements that they correspond to
                ElementType elementType = currGraphicsItem->data(1).value<ElementType>();
                SegmentType segmentType = SegmentType::Invalid;
                if (elementType == ElementType::KEYSIG) {
                    segmentType = SegmentType::KeySig;
                } else if (elementType == ElementType::TIMESIG) {
                    segmentType = SegmentType::TimeSig;
                }

                if (segmentType != SegmentType::Invalid) {
                    Segment* currSeg = currMeasure->first();
                    for (; currSeg && currSeg->segmentType() != segmentType; currSeg = currSeg->next()) {
                    }
                    if (currSeg) {
                        _score->deselectAll();
                        for (int j = 0; j < _score->nstaves(); j++) {
                            Element* element = currSeg->firstElement(j);
                            if (element) {
                                _score->select(element, SelectType::ADD);
                            }
                        }
                    }
                } else {
                    _score->deselectAll();
                    _score->select(currMeasure, SelectType::ADD, 0);
                    // Select just the element for tempo_text
                    Element* element = static_cast<Element*>(currGraphicsItem->data(4).value<void*>());
                    _score->deselectAll();
                    _score->select(element);
                }
            }
        } else {
            // Handle cell clicks
            if (event->modifiers() == Qt::ShiftModifier) {
                if (currMeasure->mmRest()) {
                    currMeasure = currMeasure->mmRest();
                } else if (currMeasure->mmRestCount() == -1) {
                    currMeasure = currMeasure->prevMeasureMM();
                }
                _score->select(currMeasure, SelectType::RANGE, stave);
                _score->setUpdateAll();
            } else if (event->modifiers() == Qt::ControlModifier) {
                if (_score->selection().isNone()) {
                    if (currMeasure->mmRest()) {
                        currMeasure = currMeasure->mmRest();
                    } else if (currMeasure->mmRestCount() == -1) {
                        currMeasure = currMeasure->prevMeasureMM();
                    }

                    _score->select(currMeasure, SelectType::RANGE, 0);
                    _score->select(currMeasure, SelectType::RANGE, _score->nstaves() - 1);
                    _score->setUpdateAll();
                } else {
                    _score->deselectAll();
                }
            } else {
                if (currMeasure->mmRest()) {
                    currMeasure = currMeasure->mmRest();
                } else if (currMeasure->mmRestCount() == -1) {
                    currMeasure = currMeasure->prevMeasureMM();
                }
                _score->select(currMeasure, SelectType::SINGLE, stave);
            }
            _cv->adjustCanvasPosition(currMeasure, false, stave);
        }
        mscore->endCmd();
        _score->update();
    } else {
        _score->deselectAll();
        mscore->endCmd();
        _score->update();
    }
    _score->setUpdateAll();
}

//---------------------------------------------------------
//   Timeline::mouseMoveEvent
//---------------------------------------------------------

void Timeline::mouseMoveEvent(QMouseEvent* event)
{
    QPointF newLoc = mapToScene(event->pos());
    if (!_mousePressed) {
        if (cursorIsOn() == "meta") {
            setCursor(Qt::ArrowCursor);
            mouseOver(newLoc);
        } else if (cursorIsOn() == "invalid") {
            setCursor(Qt::ForbiddenCursor);
        } else {
            setCursor(Qt::ArrowCursor);
        }

        emit moved(QPointF(-1, -1));
        return;
    }

    if (state == ViewState::NORMAL) {
        if (event->modifiers() == Qt::ShiftModifier) {
            // Slight wiggle room for selection (Same as score)
            if (abs(newLoc.x() - _oldLoc.x()) > 2
                || abs(newLoc.y() - _oldLoc.y()) > 2) {
                _score->deselectAll();
                updateGrid();
                state = ViewState::LASSO;
                _selectionBox = new QGraphicsRectItem();
                _selectionBox->setRect(_oldLoc.x(), _oldLoc.y(), 0, 0);
                _selectionBox->setPen(QPen(QColor(0, 0, 255), 2));
                _selectionBox->setBrush(QBrush(QColor(0, 0, 255, 50)));
                scene()->addItem(_selectionBox);
            }
        } else {
            state = ViewState::DRAG;
            setCursor(Qt::SizeAllCursor);
        }
    }

    if (state == ViewState::LASSO) {
        QRect tmp = QRect((_oldLoc.x() < newLoc.x()) ? _oldLoc.x() : newLoc.x(),
                          (_oldLoc.y() < newLoc.y()) ? _oldLoc.y() : newLoc.y(),
                          abs(newLoc.x() - _oldLoc.x()),
                          abs(newLoc.y() - _oldLoc.y()));
        _selectionBox->setRect(tmp);
    } else if (state == ViewState::DRAG) {
        int x_offset = int(_oldLoc.x()) - int(newLoc.x());
        int yOffset = int(_oldLoc.y()) - int(newLoc.y());
        horizontalScrollBar()->setValue(horizontalScrollBar()->value() + x_offset);
        verticalScrollBar()->setValue(verticalScrollBar()->value() + yOffset);
    }
    emit moved(QPointF(-1, -1));
}

//---------------------------------------------------------
//   Timeline::mouseReleaseEvent
//---------------------------------------------------------

void Timeline::mouseReleaseEvent(QMouseEvent*)
{
    _mousePressed = false;
    if (state == ViewState::LASSO) {
        scene()->removeItem(_selectionBox);
        _score->deselectAll();

        int width, height;
        QPoint loc = mapFromScene(_selectionBox->rect().topLeft());
        width = int(_selectionBox->rect().width());
        height = int(_selectionBox->rect().height());

        QList<QGraphicsItem*> graphicsItemList = items(QRect(loc.x(), loc.y(), width, height));
        // Find top left and bottom right to create selection
        QGraphicsItem* tlGraphicsItem = nullptr;
        QGraphicsItem* brGraphicsItem = nullptr;
        for (QGraphicsItem* graphicsItem : graphicsItemList) {
            Measure* currMeasure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());
            if (!currMeasure) {
                continue;
            }
            int stave = graphicsItem->data(0).value<int>();
            if (stave == -1) {
                continue;
            }

            if (!tlGraphicsItem && !brGraphicsItem) {
                tlGraphicsItem = graphicsItem;
                brGraphicsItem = graphicsItem;
                continue;
            }

            if (graphicsItem->boundingRect().top() < tlGraphicsItem->boundingRect().top()) {
                tlGraphicsItem = graphicsItem;
            }
            if (graphicsItem->boundingRect().left() < tlGraphicsItem->boundingRect().left()) {
                tlGraphicsItem = graphicsItem;
            }

            if (graphicsItem->boundingRect().bottom() > brGraphicsItem->boundingRect().bottom()) {
                brGraphicsItem = graphicsItem;
            }
            if (graphicsItem->boundingRect().right() > brGraphicsItem->boundingRect().right()) {
                brGraphicsItem = graphicsItem;
            }
        }

        // Select single tlGraphicsItem and then range brGraphicsItem
        if (tlGraphicsItem && brGraphicsItem) {
            Measure* tlMeasure = static_cast<Measure*>(tlGraphicsItem->data(2).value<void*>());
            int tlStave = tlGraphicsItem->data(0).value<int>();
            Measure* brMeasure = static_cast<Measure*>(brGraphicsItem->data(2).value<void*>());
            int brStave = brGraphicsItem->data(0).value<int>();
            if (tlMeasure && brMeasure) {
                // Focus selection of mmRests here
                if (tlMeasure->mmRest()) {
                    tlMeasure = tlMeasure->mmRest();
                } else if (tlMeasure->mmRestCount() == -1) {
                    tlMeasure = tlMeasure->prevMeasureMM();
                }
                if (brMeasure->mmRest()) {
                    brMeasure = brMeasure->mmRest();
                } else if (brMeasure->mmRestCount() == -1) {
                    brMeasure = brMeasure->prevMeasureMM();
                }

                _score->select(tlMeasure, SelectType::SINGLE, tlStave);
                _score->select(brMeasure, SelectType::RANGE, brStave);
            }
            _cv->adjustCanvasPosition(tlMeasure, false, tlStave);
        }

        _score->update();
        mscore->endCmd();
    } else if (state == ViewState::DRAG) {
        setCursor(Qt::ArrowCursor);
        mscore->endCmd();
    }
    state = ViewState::NORMAL;
}

//---------------------------------------------------------
//   Timeline::leaveEvent
//---------------------------------------------------------

void Timeline::leaveEvent(QEvent*)
{
    if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
        QPointF p = mapToScene(mapFromGlobal(QCursor::pos()));
        mouseOver(p);
    }
}

//---------------------------------------------------------
//   Timeline::wheelEvent
//---------------------------------------------------------

void Timeline::wheelEvent(QWheelEvent* event)
{
    if (event->modifiers().testFlag(Qt::ControlModifier)) {
        qreal originalCursorPos = mapToScene(mapFromGlobal(QCursor::pos())).x();
        int originalScrollValue = horizontalScrollBar()->value();
        qreal ratio = originalCursorPos / qreal(getWidth());

        if (event->angleDelta().y() > 0 && _gridWidth < _maxZoom) {
            _gridWidth++;
            updateGrid();
        } else if (event->angleDelta().y() < 0 && _gridWidth > _minZoom) {
            _gridWidth--;
            updateGrid();
        }

        // Attempt to keep mouse in original spot
        qreal newPos = qreal(getWidth()) * ratio;
        int offset = newPos - originalCursorPos;
        horizontalScrollBar()->setValue(originalScrollValue + offset);
    } else if (event->modifiers().testFlag(Qt::ShiftModifier)) {
        qreal numOfSteps = qreal(event->angleDelta().y()) / 2;
        horizontalScrollBar()->setValue(horizontalScrollBar()->value() - int(numOfSteps));
    } else {
        QGraphicsView::wheelEvent(event);
    }
}

//---------------------------------------------------------
//   Timeline::updateGrid
//---------------------------------------------------------

void Timeline::updateGrid()
{
    if (!isVisible()) {
        return;
    }

    if (_score && _score->firstMeasure()) {
        drawGrid(nstaves(), _score->nmeasures());
        updateView();
        drawSelection();
        mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));
        _rowNames->updateLabels(getLabels(), _gridHeight);
    }
    viewport()->update();
}

//---------------------------------------------------------
//   Timeline::setScore
//---------------------------------------------------------

void Timeline::setScore(Score* s)
{
    _score = s;
    scene()->clear();

    if (_score) {
        connect(_score, &QObject::destroyed, this, &Timeline::objectDestroyed, Qt::UniqueConnection);
        drawGrid(nstaves(), _score->nmeasures());
        changeSelection(SelState::NONE);
        _rowNames->updateLabels(getLabels(), _gridHeight);
    } else {
        // Clear timeline if no score is present
        QSplitter* sp = _scrollArea->grid();
        if (sp && sp->count() > 0) {
            TRowLabels* tRowLabels = static_cast<TRowLabels*>(sp->widget(0));
            std::vector<std::pair<QString, bool> > noLabels;
            tRowLabels->updateLabels(noLabels, 0);
        }
        _metaRows.clear();
        setSceneRect(0, 0, 0, 0);
    }

    viewport()->update();
}

//---------------------------------------------------------
//   Timeline::setScoreView
//---------------------------------------------------------

void Timeline::setScoreView(ScoreView* v)
{
    _cv = v;
    if (_cv) {
        connect(_cv, &ScoreView::sizeChanged, this, &Timeline::updateView, Qt::UniqueConnection);
        connect(_cv, &ScoreView::viewRectChanged, this, &Timeline::updateView, Qt::UniqueConnection);
        connect(_cv, &QObject::destroyed, this, &Timeline::objectDestroyed, Qt::UniqueConnection);
        updateView();
    }
}

//---------------------------------------------------------
//   Timeline::objectDestroyed
//---------------------------------------------------------

void Timeline::objectDestroyed(QObject* obj)
{
    if (_cv == obj) {
        setScoreView(nullptr);
    } else if (_score == obj) {
        setScore(nullptr);
    }
}

//---------------------------------------------------------
//   Timeline::updateView
//---------------------------------------------------------

void Timeline::updateView()
{
    if (!isVisible()) {
        return;
    }

    if (_cv && _score) {
        QRectF canvas = QRectF(_cv->matrix().inverted().mapRect(_cv->geometry()));

        std::set<std::pair<Measure*, int> > visibleItemsSet;

        // Find visible measures of score
        for (Measure* currMeasure = _score->firstMeasure(); currMeasure; currMeasure = currMeasure->nextMeasure()) {
            System* system = currMeasure->system();

            if (currMeasure->mmRest() && _score->styleB(Sid::createMultiMeasureRests)) {
                // Handle mmRests
                Measure* mmrestMeasure = currMeasure->mmRest();
                system = mmrestMeasure->system();
                if (!system) {
                    continue;
                }

                // Add all measures within mmRest to visibleItemsSet if mmRest_visible
                for (; currMeasure != mmrestMeasure->mmRestLast(); currMeasure = currMeasure->nextMeasure()) {
                    for (int staff = 0; staff < _score->staves().length(); staff++) {
                        if (!_score->staff(staff)->show()) {
                            continue;
                        }
                        QRectF staveRect = QRectF(system->canvasBoundingRect().left(),
                                                  system->staffCanvasYpage(staff),
                                                  system->width(),
                                                  system->staff(staff)->bbox().height());
                        QRectF showRect = mmrestMeasure->canvasBoundingRect().intersected(staveRect);

                        if (canvas.intersects(showRect)) {
                            std::pair<Measure*, int> p = std::make_pair(currMeasure, staff);
                            visibleItemsSet.insert(p);
                        }
                    }
                }

                // Handle last measure in mmRest
                for (int staff = 0; staff < _score->staves().length(); staff++) {
                    if (!_score->staff(staff)->show()) {
                        continue;
                    }
                    QRectF staveRect = QRectF(system->canvasBoundingRect().left(),
                                              system->staffCanvasYpage(staff),
                                              system->width(),
                                              system->staff(staff)->bbox().height());
                    QRectF showRect = mmrestMeasure->canvasBoundingRect().intersected(staveRect);

                    if (canvas.intersects(showRect)) {
                        std::pair<Measure*, int> p = std::make_pair(currMeasure, staff);
                        visibleItemsSet.insert(p);
                    }
                }
                continue;
            }

            if (!system) {
                continue;
            }

            for (int staff = 0; staff < _score->staves().length(); staff++) {
                if (!_score->staff(staff)->show()) {
                    continue;
                }
                QRectF staveRect = QRectF(system->canvasBoundingRect().left(),
                                          system->staffCanvasYpage(staff),
                                          system->width(),
                                          system->staff(staff)->bbox().height());
                QRectF showRect = currMeasure->canvasBoundingRect().intersected(staveRect);

                if (canvas.intersects(showRect)) {
                    std::pair<Measure*, int> p = std::make_pair(currMeasure, staff);
                    visibleItemsSet.insert(p);
                }
            }
        }

        // Find respective visible elements in timeline
        QPainterPath visiblePainterPath = QPainterPath();
        visiblePainterPath.setFillRule(Qt::WindingFill);
        for (QGraphicsItem* graphicsItem : scene()->items()) {
            int stave = graphicsItem->data(0).value<int>();
            Measure* measure = static_cast<Measure*>(graphicsItem->data(2).value<void*>());
            if (numToStaff(stave) && !numToStaff(stave)->show()) {
                continue;
            }

            std::pair<Measure*, int> tmp = std::make_pair(measure, stave);
            std::set<std::pair<Measure*, int> >::iterator it;
            it = visibleItemsSet.find(tmp);
            if (it != visibleItemsSet.end()) {
                visiblePainterPath.addRect(graphicsItem->boundingRect());
            }
        }

        QPainterPath nonVisiblePainterPath = QPainterPath();
        nonVisiblePainterPath.setFillRule(Qt::WindingFill);

        QRectF timelineRect = QRectF(0, 0, getWidth(), getHeight());
        nonVisiblePainterPath.addRect(timelineRect);

        nonVisiblePainterPath = nonVisiblePainterPath.subtracted(visiblePainterPath);

        QGraphicsPathItem* nonVisiblePathItem = new QGraphicsPathItem(nonVisiblePainterPath.simplified());

        QPen nonVisiblePen = QPen(activeTheme().nonVisiblePenColor);
        QBrush nonVisibleBrush = QBrush(activeTheme().nonVisibleBrushColor);
        nonVisiblePathItem->setPen(QPen(nonVisibleBrush.color()));
        nonVisiblePathItem->setBrush(nonVisibleBrush);
        nonVisiblePathItem->setZValue(-3);

        QGraphicsPathItem* visible = new QGraphicsPathItem(visiblePainterPath.simplified());
        visible->setPen(nonVisiblePen);
        visible->setBrush(Qt::NoBrush);
        visible->setZValue(-2);

        // Find old path, remove it
        for (QGraphicsItem* graphicsItem : scene()->items()) {
            if (graphicsItem->type() == QGraphicsPathItem().type()) {
                QGraphicsPathItem* oldPathItem = static_cast<QGraphicsPathItem*>(graphicsItem);
                QBrush oldBrush = oldPathItem->brush();
                QPen oldPen = oldPathItem->pen();
                if (oldBrush == nonVisibleBrush || oldPen == nonVisiblePen) {
                    scene()->removeItem(oldPathItem);
                }
            }
        }

        scene()->addItem(nonVisiblePathItem);
        scene()->addItem(visible);
    }
}

//---------------------------------------------------------
//   Timeline::nstaves
//---------------------------------------------------------

int Timeline::nstaves() const
{
    return _score->staves().size();
}

//---------------------------------------------------------
//   Timeline::colorBox
//---------------------------------------------------------

QColor Timeline::colorBox(QGraphicsRectItem* item)
{
    Measure* measure = static_cast<Measure*>(item->data(2).value<void*>());
    int stave = item->data(0).value<int>();
    for (Segment* seg = measure->first(); seg; seg = seg->next()) {
        if (!seg->isChordRestType()) {
            continue;
        }
        for (int track = stave * VOICES; track < stave * VOICES + VOICES; track++) {
            ChordRest* chordRest = seg->cr(track);
            if (chordRest) {
                ElementType crt = chordRest->type();
                if (crt == ElementType::CHORD || crt == ElementType::REPEAT_MEASURE) {
                    return activeTheme().colorBoxColor;
                }
            }
        }
    }
    return QColor(224, 224, 224);
}

//---------------------------------------------------------
//   Timeline::getLabels
//---------------------------------------------------------

std::vector<std::pair<QString, bool> > Timeline::getLabels()
{
    if (!_score) {
        std::vector<std::pair<QString, bool> > noLabels;
        return noLabels;
    }
    QList<Part*> partList = getParts();
    // Transfer them into a vector of qstrings and then add the meta row names
    std::vector<std::pair<QString, bool> > rowLabels;
    if (_collapsedMeta) {
        std::pair<QString, bool> first = std::make_pair("", true);
        std::pair<QString, bool> second = std::make_pair(tr("Measures"), true);
        rowLabels.push_back(first);
        rowLabels.push_back(second);
    } else {
        for (auto it = _metas.begin(); it != _metas.end(); ++it) {
            std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
            if (!std::get<2>(meta)) {
                continue;
            }
            std::pair<QString, bool> metaLabel = std::make_pair(std::get<0>(meta), true);
            rowLabels.push_back(metaLabel);
        }
    }

    for (int stave = 0; stave < partList.size(); stave++) {
        QTextDocument doc;
        QString partName = "";
        doc.setHtml(partList.at(stave)->longName());
        partName = doc.toPlainText();
        if (partName.isEmpty()) {
            partName = partList.at(stave)->instrumentName();
        }

        std::pair<QString, bool> instrumentLabel = std::make_pair(partName, partList.at(stave)->show());
        rowLabels.push_back(instrumentLabel);
    }
    return rowLabels;
}

//---------------------------------------------------------
//   Timeline::handleScroll
//---------------------------------------------------------

void Timeline::handleScroll(int value)
{
    if (!_score) {
        return;
    }
    for (auto it = _metaRows.begin(); it != _metaRows.end(); ++it) {
        std::pair<QGraphicsItem*, int> pairGraphicsInt = *it;

        QGraphicsItem* graphicsItem = pairGraphicsInt.first;
        QGraphicsRectItem* graphicsRectItem = qgraphicsitem_cast<QGraphicsRectItem*>(graphicsItem);
        QGraphicsLineItem* graphicsLineItem = qgraphicsitem_cast<QGraphicsLineItem*>(graphicsItem);
        QGraphicsPixmapItem* graphicsPixmapItem = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphicsItem);

        int rowY = pairGraphicsInt.second * _gridHeight;
        int scrollbarValue = value;

        if (graphicsRectItem) {
            QRectF rectf = graphicsRectItem->rect();
            rectf.setY(qreal(scrollbarValue + rowY));
            rectf.setHeight(_gridHeight);
            graphicsRectItem->setRect(rectf);
        } else if (graphicsLineItem) {
            QLineF linef = graphicsLineItem->line();
            linef.setLine(linef.x1(), rowY + scrollbarValue + 1, linef.x2(), rowY + scrollbarValue + 1);
            graphicsLineItem->setLine(linef);
        } else if (graphicsPixmapItem) {
            graphicsPixmapItem->setY(qreal(scrollbarValue + rowY + 3));
        } else {
            graphicsItem->setY(qreal(scrollbarValue + rowY));
        }
    }
    viewport()->update();
}

//---------------------------------------------------------
//   Timeline::mouseOver
//---------------------------------------------------------

void Timeline::mouseOver(QPointF pos)
{
    // Choose item with the largest original Z value...
    QList<QGraphicsItem*> graphicsList = scene()->items(pos);
    QGraphicsItem* hoveredGraphicsItem = 0;
    int maxZValue = -1;
    for (QGraphicsItem* currGraphicsItem : graphicsList) {
        if (qgraphicsitem_cast<QGraphicsTextItem*>(currGraphicsItem)) {
            continue;
        }
        if (currGraphicsItem->zValue() >= maxZValue && currGraphicsItem->zValue() < _globalZValue) {
            hoveredGraphicsItem = currGraphicsItem;
            maxZValue = hoveredGraphicsItem->zValue();
        } else if (currGraphicsItem->zValue() > _globalZValue && std::get<1>(_oldHoverInfo) >= maxZValue) {
            hoveredGraphicsItem = currGraphicsItem;
            maxZValue = std::get<1>(_oldHoverInfo);
        }
    }

    if (!hoveredGraphicsItem) {
        if (std::get<0>(_oldHoverInfo)) {
            std::get<0>(_oldHoverInfo)->setZValue(std::get<1>(_oldHoverInfo));
            static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>())->setZValue(std::get<1>(
                                                                                                           _oldHoverInfo));
            QGraphicsRectItem* graphicsRectItem1 = qgraphicsitem_cast<QGraphicsRectItem*>(std::get<0>(_oldHoverInfo));
            QGraphicsRectItem* graphicsRectItem2
                = qgraphicsitem_cast<QGraphicsRectItem*>(static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5)
                                                                                     .value<void*>()));
            if (graphicsRectItem1) {
                graphicsRectItem1->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
            }
            if (graphicsRectItem2) {
                graphicsRectItem2->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
            }
            std::get<0>(_oldHoverInfo) = nullptr;
            std::get<1>(_oldHoverInfo) = -1;
        }
        return;
    }
    QGraphicsItem* pairItem = static_cast<QGraphicsItem*>(hoveredGraphicsItem->data(5).value<void*>());
    if (!pairItem) {
        if (std::get<0>(_oldHoverInfo)) {
            std::get<0>(_oldHoverInfo)->setZValue(std::get<1>(_oldHoverInfo));
            static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>())->setZValue(std::get<1>(
                                                                                                           _oldHoverInfo));
            QGraphicsRectItem* graphicsRectItem1 = qgraphicsitem_cast<QGraphicsRectItem*>(std::get<0>(_oldHoverInfo));
            QGraphicsRectItem* graphicsRectItem2
                = qgraphicsitem_cast<QGraphicsRectItem*>(static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5)
                                                                                     .value<void*>()));
            if (graphicsRectItem1) {
                graphicsRectItem1->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
            }
            if (graphicsRectItem2) {
                graphicsRectItem2->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
            }
            std::get<0>(_oldHoverInfo) = nullptr;
            std::get<1>(_oldHoverInfo) = -1;
        }
        return;
    }

    if (std::get<0>(_oldHoverInfo) == hoveredGraphicsItem) {
        return;
    }

    if (std::get<0>(_oldHoverInfo)) {
        std::get<0>(_oldHoverInfo)->setZValue(std::get<1>(_oldHoverInfo));
        static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).value<void*>())->setZValue(std::get<1>(
                                                                                                       _oldHoverInfo));
        QGraphicsRectItem* graphicsRectItem1 = qgraphicsitem_cast<QGraphicsRectItem*>(std::get<0>(_oldHoverInfo));
        QGraphicsRectItem* graphicsRectItem2
            = qgraphicsitem_cast<QGraphicsRectItem*>(static_cast<QGraphicsItem*>(std::get<0>(_oldHoverInfo)->data(5).
                                                                                 value<void*>()));
        if (graphicsRectItem1) {
            graphicsRectItem1->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
        }
        if (graphicsRectItem2) {
            graphicsRectItem2->setBrush(QBrush(std::get<2>(_oldHoverInfo)));
        }

        std::get<0>(_oldHoverInfo) = nullptr;
        std::get<1>(_oldHoverInfo) = -1;
    }

    std::get<1>(_oldHoverInfo) = hoveredGraphicsItem->zValue();
    std::get<0>(_oldHoverInfo) = hoveredGraphicsItem;

    // Give items the top z value
    hoveredGraphicsItem->setZValue(_globalZValue + 1);
    pairItem->setZValue(_globalZValue + 1);

    QGraphicsRectItem* graphicsRectItem1 = qgraphicsitem_cast<QGraphicsRectItem*>(hoveredGraphicsItem);
    QGraphicsRectItem* graphicsRectItem2 = qgraphicsitem_cast<QGraphicsRectItem*>(pairItem);
    if (graphicsRectItem1) {
        std::get<2>(_oldHoverInfo) = graphicsRectItem1->brush().color();
        if (std::get<2>(_oldHoverInfo) != QColor(173,216,230)) {
            graphicsRectItem1->setBrush(QBrush(activeTheme().backgroundColor));
        }
    }
    if (graphicsRectItem2) {
        std::get<2>(_oldHoverInfo) = graphicsRectItem2->brush().color();
        if (std::get<2>(_oldHoverInfo) != QColor(173,216,230)) {
            graphicsRectItem2->setBrush(QBrush(activeTheme().backgroundColor));
        }
    }
}

//---------------------------------------------------------
//   Timeline::swapMeta
//---------------------------------------------------------

void Timeline::swapMeta(unsigned row, bool switchUp)
{
    // Attempt to switch row up or down, skipping non visible rows
    if (switchUp && row != 0) {
        // traverse backwards until visible one is found
        auto swap = _metas.begin() + correctMetaRow(row) - 1;
        while (!std::get<2>(*swap)) {
            swap--;
        }
        iter_swap(_metas.begin() + correctMetaRow(row), swap);
    } else if (!switchUp && row != nmetas() - 2) {
        // traverse forwards until visible one is found
        auto swap = _metas.begin() + correctMetaRow(row) + 1;
        while (!std::get<2>(*swap)) {
            swap++;
        }
        iter_swap(_metas.begin() + correctMetaRow(row), swap);
    }

    updateGrid();
}

//---------------------------------------------------------
//   Timeline::numToStaff
//---------------------------------------------------------

Staff* Timeline::numToStaff(int staff)
{
    if (!_score) {
        return 0;
    }
    QList<Staff*> staves = _score->staves();
    if (staves.size() > staff && staff >= 0) {
        return staves.at(staff);
    } else {
        return 0;
    }
}

//---------------------------------------------------------
//   Timeline::toggleShow
//---------------------------------------------------------

void Timeline::toggleShow(int staff)
{
    if (!_score) {
        return;
    }
    QList<Part*> parts = getParts();
    if (parts.size() > staff && staff >= 0) {
        parts.at(staff)->setShow(!parts.at(staff)->show());
        parts.at(staff)->undoChangeProperty(Pid::VISIBLE, parts.at(staff)->show());
        _score->masterScore()->setLayoutAll();
        _score->masterScore()->update();
        mscore->endCmd();
    }
}

//---------------------------------------------------------
//   Timeline::showContextMenu
//---------------------------------------------------------

void Timeline::contextMenuEvent(QContextMenuEvent*)
{
    QMenu* contextMenu = new QMenu(tr("Context menu"), this);
    if (_rowNames->cursorIsOn() == "instrument") {
        QAction* edit_instruments = new QAction(tr("Edit Instruments"), this);
        connect(edit_instruments, SIGNAL(triggered()), this, SLOT(requestInstrumentDialog()));
        contextMenu->addAction(edit_instruments);
        contextMenu->exec(QCursor::pos());
    } else if (_rowNames->cursorIsOn() == "meta" || cursorIsOn() == "meta") {
        for (auto it = _metas.begin(); it != _metas.end(); ++it) {
            std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
            QString row_name = std::get<0>(meta);
            if (row_name != tr("Measures")) {
                QAction* action = new QAction(row_name, this);
                action->setCheckable(true);
                action->setChecked(std::get<2>(meta));
                connect(action, SIGNAL(triggered()), this, SLOT(toggleMetaRow()));
                contextMenu->addAction(action);
            }
        }
        contextMenu->addSeparator();
        QAction* hide_all = new QAction(tr("Hide all"), this);
        connect(hide_all, SIGNAL(triggered()), this, SLOT(toggleMetaRow()));
        contextMenu->addAction(hide_all);
        QAction* show_all = new QAction(tr("Show all"), this);
        connect(show_all, SIGNAL(triggered()), this, SLOT(toggleMetaRow()));
        contextMenu->addAction(show_all);
        contextMenu->exec(QCursor::pos());
    }
}

//---------------------------------------------------------
//   Timeline::toggleMetaRow
//---------------------------------------------------------

void Timeline::toggleMetaRow()
{
    QAction* action = qobject_cast<QAction*>(QObject::sender());
    if (action) {
        QString targetText = action->text();

        if (targetText == tr("Hide all")) {
            for (auto it = _metas.begin(); it != _metas.end(); ++it) {
                QString metaText = std::get<0>(*it);
                if (metaText != tr("Measures")) {
                    std::get<2>(*it) = false;
                }
            }
            updateGrid();
            return;
        } else if (targetText == tr("Show all")) {
            for (auto it = _metas.begin(); it != _metas.end(); ++it) {
                std::get<2>(*it) = true;
            }
            updateGrid();
            return;
        }

        bool checked = action->isChecked();
        // Find target text in metas and toggle visibility to the checked status of action
        for (auto it = _metas.begin(); it != _metas.end(); ++it) {
            QString metaText = std::get<0>(*it);
            if (metaText == targetText) {
                std::get<2>(*it) = checked;
                updateGrid();
                break;
            }
        }
    }
}

//---------------------------------------------------------
//   Timeline::nmetas
//---------------------------------------------------------

unsigned Timeline::nmetas() const
{
    unsigned total = 0;
    if (_collapsedMeta) {
        return 2;
    }
    for (auto it = _metas.begin(); it != _metas.end(); ++it) {
        std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> meta = *it;
        if (std::get<2>(meta)) {
            total++;
        }
    }
    return total;
}

//---------------------------------------------------------
//   Timeline::correctMetaRow
//---------------------------------------------------------

unsigned Timeline::correctMetaRow(unsigned row)
{
    unsigned count = 0;
    auto it = _metas.begin();
    while (row >= count) {
        if (!std::get<2>(*it)) {
            row++;
        }
        count++;
        ++it;
    }
    return row;
}

//---------------------------------------------------------
//   Timeline::correctMetaRow
//---------------------------------------------------------

QString Timeline::cursorIsOn()
{
    QPointF scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
    QGraphicsItem* graphicsItem = scene()->itemAt(scenePos, transform());
    if (graphicsItem) {
        auto it = _metaRows.begin();
        for (; it != _metaRows.end(); ++it) {
            if ((*it).first == graphicsItem) {
                break;
            }
        }
        if (it != _metaRows.end()) {
            return "meta";
        } else {
            QList<QGraphicsItem*> graphicsItemList = scene()->items(scenePos);
            for (QGraphicsItem* currGraphicsItem : graphicsItemList) {
                Measure* currMeasure = static_cast<Measure*>(currGraphicsItem->data(2).value<void*>());
                int stave = currGraphicsItem->data(0).value<int>();
                if (currMeasure && !numToStaff(stave)->show()) {
                    return "invalid";
                }
            }
            return "instrument";
        }
    } else {
        return "";
    }
}

//---------------------------------------------------------
//   Timeline::activeTheme
//---------------------------------------------------------

const TimelineTheme& Timeline::activeTheme() const
{
    if (preferences.isThemeDark()) {
        return _darkTheme;
    } else {
        return _lightTheme;
    }
}

//---------------------------------------------------------
//   Timeline::updateTimelineTheme
//---------------------------------------------------------

void Timeline::updateTimelineTheme()
{
    const QBrush backgroundBrush = QBrush(activeTheme().backgroundColor);
    scene()->setBackgroundBrush(backgroundBrush);
    _rowNames->scene()->setBackgroundBrush(backgroundBrush);
    updateGrid();
}

//---------------------------------------------------------
//   Timeline::requestInstrumentDialog
//---------------------------------------------------------

void Timeline::requestInstrumentDialog()
{
    QAction* act = getAction("instruments");
    mscore->cmd(act);
    if (mscore->getMixer()) {
        mscore->getMixer()->setScore(_score);
    }
}
}
