//=============================================================================
//  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.h"
#include "tourhandler.h"

namespace Ms {

//---------------------------------------------------------
//   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");
      }

//---------------------------------------------------------
//   closeEvent
//---------------------------------------------------------

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

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

TRowLabels::TRowLabels(TDockWidget* dock_widget, Timeline* time, QGraphicsView* w)
  : QGraphicsView(w)
      {
      setFocusPolicy(Qt::NoFocus);
      scrollArea = dock_widget;
      parent = time;
      setScene(new QGraphicsScene);
      scene()->setBackgroundBrush(Qt::lightGray);
      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(restrict_scroll(int)));
      connect(this, SIGNAL(moved(QPointF)), time, SLOT(mouseOver(QPointF)));

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

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

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

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

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

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

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


      mouseover_map[MouseOverValue::COLLAPSE_DOWN_ARROW] = new QPixmap(cd_arrow);
      mouseover_map[MouseOverValue::COLLAPSE_UP_ARROW] = new QPixmap(cu_arrow);
      mouseover_map[MouseOverValue::MOVE_DOWN_ARROW] = new QPixmap(d_arrow);
      mouseover_map[MouseOverValue::MOVE_UP_DOWN_ARROW] = new QPixmap(ud_arrow);
      mouseover_map[MouseOverValue::MOVE_UP_ARROW] = new QPixmap(u_arrow);
      mouseover_map[MouseOverValue::OPEN_EYE] = new QPixmap(open_eye);
      mouseover_map[MouseOverValue::CLOSED_EYE] = new QPixmap(closed_eye);

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

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

//---------------------------------------------------------
//   restrict_scroll
//---------------------------------------------------------

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

            QGraphicsItem* graphics_item = pair_graphic_int.first;

            QGraphicsRectItem* graphics_rect_item = qgraphicsitem_cast<QGraphicsRectItem*>(graphics_item);
            QGraphicsLineItem* graphics_line_item = qgraphicsitem_cast<QGraphicsLineItem*>(graphics_item);
            QGraphicsPixmapItem* graphics_pixmap_item = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphics_item);
            int y = pair_graphic_int.second * 20;
            int scrollbar_value = verticalScrollBar()->value();

            if (graphics_rect_item) {
                  QRectF rectf = graphics_rect_item->rect();
                  rectf.setY(qreal(scrollbar_value + y));
                  rectf.setHeight(20);
                  graphics_rect_item->setRect(rectf);
                  }
            else if (graphics_line_item) {
                  QLineF linef = graphics_line_item->line();
                  linef.setLine(linef.x1(), y + scrollbar_value + 1, linef.x2(), y + scrollbar_value + 1);
                  graphics_line_item->setLine(linef);
                  }
            else if (graphics_pixmap_item)
                  graphics_pixmap_item->setY(qreal(scrollbar_value + y + 1));
            else
                  graphics_item->setY(qreal(scrollbar_value + y));
            }
      viewport()->update();

      }

//---------------------------------------------------------
//   updateLabels
//---------------------------------------------------------

void TRowLabels::updateLabels(std::vector<std::pair<QString, bool>> labels, int height)
      {

      scene()->clear();
      meta_labels.clear();
      if (labels.empty())
            return;

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

            if (row == num_metas - 1)
                  measure_width = graphics_text_item->boundingRect().width();
            max_width = max(max_width, int(graphics_text_item->boundingRect().width()));

            QFontMetrics f(QApplication::font());
            QString part_name = f.elidedText(labels[row].first, Qt::ElideRight, width());
            graphics_text_item->setPlainText(part_name);
            graphics_text_item->setX(0);
            graphics_text_item->setY(ypos);
            if (labels[row].second)
                  graphics_text_item->setDefaultTextColor(QColor(Qt::black));
            else
                  graphics_text_item->setDefaultTextColor(QColor(150, 150, 150));
            graphics_rect_item->setPen(QPen(QColor(150, 150, 150)));
            graphics_rect_item->setBrush(QBrush(QColor(211, 211, 211)));
            graphics_text_item->setZValue(-1);
            graphics_rect_item->setZValue(-1);

            graphics_rect_item->setData(0, QVariant::fromValue<bool>(false));
            graphics_text_item->setData(0, QVariant::fromValue<bool>(false));

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

            graphics_text_item->setData(2, QVariant::fromValue<unsigned int>(row));
            graphics_rect_item->setData(2, QVariant::fromValue<unsigned int>(row));

            scene()->addItem(graphics_rect_item);
            scene()->addItem(graphics_text_item);
            if (row < num_metas) {
                  std::pair<QGraphicsItem*, int> p1(graphics_rect_item, row);
                  std::pair<QGraphicsItem*, int> p2(graphics_text_item, row);
                  meta_labels.push_back(p1);
                  meta_labels.push_back(p2);
                  graphics_rect_item->setZValue(1);
                  graphics_text_item->setZValue(2);
                  }
            }
      QGraphicsLineItem* graphics_line_item = new QGraphicsLineItem(0,
                                                    height * num_metas + verticalScrollBar()->value() + 1,
                                                    max(max_width + 20, 70),
                                                    height * num_metas + verticalScrollBar()->value() + 1);
      graphics_line_item->setPen(QPen(QColor(150, 150, 150), 4));
      graphics_line_item->setZValue(0);
      graphics_line_item->setData(0, QVariant::fromValue<bool>(false));
      scene()->addItem(graphics_line_item);

      std::pair<QGraphicsItem*, int> graphics_line_item_pair(graphics_line_item, num_metas);
      meta_labels.push_back(graphics_line_item_pair);

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

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

      setMinimumWidth(measure_width + 9);
      setMaximumWidth(max(max_width + 20, 70));
      mouseOver(mapToScene(mapFromGlobal(QCursor::pos())));
      }

//---------------------------------------------------------
//   resizeEvent
//---------------------------------------------------------

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

//---------------------------------------------------------
//   mousePressEvent
//---------------------------------------------------------

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

      QPointF scene_pt = mapToScene(event->pos());
      unsigned int num_metas = parent->nmetas();

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

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

                  }
            else {
                  dragging = true;
                  this->setCursor(Qt::SizeAllCursor);
                  old_loc = QPoint(int(scene_pt.x()), int(scene_pt.y()));
                  }
            }
      }

//---------------------------------------------------------
//   mouseMoveEvent
//---------------------------------------------------------

void TRowLabels::mouseMoveEvent(QMouseEvent* event)
      {
      QPointF scene_pt = mapToScene(event->pos());
      if (dragging) {
            this->setCursor(Qt::SizeAllCursor);
            int y_offset = int(old_loc.y()) - int(scene_pt.y());
            verticalScrollBar()->setValue(verticalScrollBar()->value() + y_offset);
            }
      else
            mouseOver(scene_pt);
      emit moved(QPointF(-1, -1));
      }

//---------------------------------------------------------
//   mouseReleaseEvent
//---------------------------------------------------------

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

//---------------------------------------------------------
//   leaveEvent
//---------------------------------------------------------

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

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

//---------------------------------------------------------
//   mouseOver
//---------------------------------------------------------

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

            MouseOverValue mouse_over_arrow = graphics_item->data(1).value<MouseOverValue>();
            if (mouse_over_arrow != MouseOverValue::NONE) {
                  QPixmap* pixmap_arrow = mouseover_map[mouse_over_arrow];
                  QGraphicsPixmapItem* graphics_pixmap_item_arrow = new QGraphicsPixmapItem(*pixmap_arrow);
                  unsigned int row = graphics_item->data(2).value<unsigned int>();

                  QString tooltip;
                  switch (mouse_over_arrow) {
                        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;
                        }

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

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


                  if (std::get<2>(old_item_info) == row && std::get<1>(old_item_info) == mouse_over_arrow) {
                        //DO NOTHING
                        }
                  else {
                        if (std::get<0>(old_item_info)) {
                              std::pair<QGraphicsItem*, int> p(std::get<0>(old_item_info), std::get<2>(old_item_info));
                              std::vector<std::pair<QGraphicsItem*, int>>::iterator it = std::find(meta_labels.begin(), meta_labels.end(), p);
                              if (it != meta_labels.end())
                                    meta_labels.erase(it);
                              scene()->removeItem(std::get<0>(old_item_info));
                              }
                        std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned int> tmp(graphics_pixmap_item_arrow, mouse_over_arrow, row);
                        old_item_info = tmp;
                        if (mouse_over_arrow != MouseOverValue::OPEN_EYE && mouse_over_arrow != MouseOverValue::CLOSED_EYE) {
                              std::pair<QGraphicsItem*, int> p(graphics_pixmap_item_arrow, row);
                              meta_labels.push_back(p);
                              }
                        scene()->addItem(graphics_pixmap_item_arrow);
                        }

                  }
            else {
                  if (std::get<0>(old_item_info))
                        scene()->removeItem(std::get<0>(old_item_info));
                  std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned int> tmp(nullptr, MouseOverValue::NONE, -1);
                  old_item_info = tmp;
                  }
            }
      else {
            if (std::get<0>(old_item_info)) {
                  std::pair<QGraphicsItem*, int> p(std::get<0>(old_item_info), std::get<2>(old_item_info));
                  std::vector<std::pair<QGraphicsItem*, int>>::iterator it = std::find(meta_labels.begin(), meta_labels.end(), p);
                  if (it != meta_labels.end())
                        meta_labels.erase(it);
                  scene()->removeItem(std::get<0>(old_item_info));
                  }
            std::tuple<QGraphicsPixmapItem*, MouseOverValue, unsigned int> tmp(nullptr, MouseOverValue::NONE, -1);
            old_item_info = tmp;
            }
      if (QGraphicsItem* graphics_item = scene()->itemAt(scene_pt, transform())) {
            QGraphicsPixmapItem* graphics_pixmap_item = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphics_item);
            if (graphics_pixmap_item)
                  this->setCursor(Qt::PointingHandCursor);
            else
                  this->setCursor(Qt::ArrowCursor);
            }
      else
            this->setCursor(Qt::ArrowCursor);
      }

//---------------------------------------------------------
//   cursorIsOn
//---------------------------------------------------------

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

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

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

      scrollArea = dock_widget;
      QSplitter* split = static_cast<QSplitter*>(scrollArea->widget());

      row_names = new TRowLabels(dock_widget, this);
      split->addWidget(row_names);
      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(Qt::lightGray);

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

      std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t1(tr("Tempo"), &Ms::Timeline::tempo_meta, true);
      std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t2(tr("Time Signature"), &Ms::Timeline::time_meta, true);
      std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t3(tr("Rehearsal Mark"), &Ms::Timeline::rehearsal_meta, true);
      std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t4(tr("Key Signature"), &Ms::Timeline::key_meta, true);
      std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t5(tr("Barlines"), &Ms::Timeline::barline_meta, true);
      std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t6(tr("Jumps and Markers"), &Ms::Timeline::jump_marker_meta, true);
      std::tuple<QString, void (Timeline::*)(Segment*, int*, int), bool> t7(tr("Measures"), &Ms::Timeline::measure_meta, 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());
      old_hover_info = ohi;
      std::tuple<int, qreal, Element*, Element*, bool> ri(0, 0, nullptr, nullptr, false);
      repeat_info = ri;

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

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

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

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

      QPixmap* left_repeat_pixmap = new QPixmap(left_repeat);
      QPixmap* right_repeat_pixmap = new QPixmap(right_repeat);
      QPixmap* final_barline_pixmap = new QPixmap(final_barline);
      QPixmap* double_barline_pixmap = new QPixmap(double_barline);

      barlines["Start repeat"] = left_repeat_pixmap;
      barlines["End repeat"] = right_repeat_pixmap;
      barlines["Final barline"] = final_barline_pixmap;
      barlines["Double barline"] = double_barline_pixmap;
      }

//---------------------------------------------------------
//   drawGrid
//---------------------------------------------------------

void Timeline::drawGrid(int global_rows, int global_cols)
      {
      scene()->clear();
      meta_rows.clear();

      if (global_rows == 0 || global_cols == 0) return;
      int stagger = 0;
      unsigned int num_metas = nmetas();
      setMinimumHeight(grid_height * (num_metas + 1) + 5 + horizontalScrollBar()->height());
      setMinimumWidth(grid_width * 3);
      global_z_value = 1;

      //Draw grid
      Measure* curr_measure = _score->firstMeasure();
      QList<Part*> part_list = getParts();
      for (int col = 0; col < global_cols; col++) {
            for (int row = 0; row < global_rows; row++) {
                  QGraphicsRectItem* graphics_rect_item = new QGraphicsRectItem(col * grid_width,
                                                                                grid_height * (row + num_metas) + 3,
                                                                                grid_width,
                                                                                grid_height);

                  setMetaData(graphics_rect_item, row, ElementType::INVALID, curr_measure, false, 0);

                  QString translate_measure = tr("Measure");
                  QChar initial_letter = translate_measure[0];
                  QTextDocument doc;
                  QString part_name = "";
                  if (part_list.size() > row) {
                        doc.setHtml(part_list.at(row)->longName());
                        part_name = doc.toPlainText();
                        }
                  if (part_name.isEmpty() && part_list.size() > row)
                        part_name = part_list.at(row)->instrumentName();

                  graphics_rect_item->setToolTip(initial_letter + QString(" ") + QString::number(curr_measure->no() + 1) + QString(", ") + part_name);
                  graphics_rect_item->setPen(QPen(QColor(Qt::lightGray)));
                  graphics_rect_item->setBrush(QBrush(colorBox(graphics_rect_item)));
                  graphics_rect_item->setZValue(-3);
                  scene()->addItem(graphics_rect_item);
                  }

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

      //Draw meta rows and separator
      QGraphicsLineItem* graphics_line_item_separator = new QGraphicsLineItem(0,
                                                                              grid_height * num_metas + verticalScrollBar()->value() + 1,
                                                                              getWidth() - 1,
                                                                              grid_height * num_metas + verticalScrollBar()->value() + 1);
      graphics_line_item_separator->setPen(QPen(QColor(150, 150, 150), 4));
      graphics_line_item_separator->setZValue(-2);
      scene()->addItem(graphics_line_item_separator);
      std::pair<QGraphicsItem*, int> pair_graphics_int_separator(graphics_line_item_separator, num_metas);
      meta_rows.push_back(pair_graphics_int_separator);

      for (unsigned int row = 0; row < num_metas; row++) {
            QGraphicsRectItem* meta_row = new QGraphicsRectItem(0,
                                                                grid_height * row + verticalScrollBar()->value(),
                                                                getWidth(),
                                                                grid_height);
            meta_row->setBrush(QBrush(QColor(211,211,211)));
            meta_row->setPen(QPen(QColor(150, 150, 150)));
            meta_row->setData(0, QVariant::fromValue<int>(-1));

            scene()->addItem(meta_row);

            std::pair<QGraphicsItem*, int> pair_graphics_int_meta(meta_row, row);
            meta_rows.push_back(pair_graphics_int_meta);
            }

      int x_pos = 0;

      //Create stagger array if collapsed_meta is false
#if (!defined (_MSCVER) && !defined (_MSC_VER))
      int stagger_arr[num_metas];
      for (unsigned int row = 0; row < num_metas; row++)
         stagger_arr[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> stagger_arr(num_metas, 0);  // Default initialized, loop not required
#endif

      bool no_key = true;
      std::get<4>(repeat_info) = false;

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

                  //If no initial key signature is found, add key signature
                  if (cm == _score->firstMeasure() && no_key &&
                      (curr_seg->isTimeSigType() || curr_seg->isChordRestType())) {

                        if (getMetaRow(tr("Key Signature")) != num_metas) {
                              if (collapsed_meta)
                                    key_meta(0, &stagger, x_pos);
                              else
                                    key_meta(0, &stagger_arr[getMetaRow(tr("Key Signature"))], x_pos);
                              }
                        no_key = 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 (collapsed_meta)
                              (this->*func)(curr_seg, &stagger, x_pos);
                        else
                              (this->*func)(curr_seg, &stagger_arr[row], x_pos);
                        row++;
                        }
                  }
            //Handle all jumps here
            if (getMetaRow(tr("Jumps and Markers")) != num_metas) {
                  ElementList measure_elements_list = cm->el();
                  for (Element* element : measure_elements_list) {
                        std::get<3>(repeat_info) = element;
                        if (element->isMarker())
                              jump_marker_meta(0, &stagger, x_pos);
                        }
                  for (Element* element : measure_elements_list) {
                        if (element->isJump()) {
                              std::get<2>(repeat_info) = element;
                              if (collapsed_meta)
                                    jump_marker_meta(0, &stagger, x_pos);
                              else
                                    jump_marker_meta(0, &std::get<0>(repeat_info), x_pos);
                              }
                        }
                  }
            stagger = 0;
            std::get<0>(repeat_info) = 0;

            for (unsigned int row = 0; row < num_metas; row++) {
                  stagger_arr[row] = 0;
                  }
            x_pos += grid_width;
            std::get<4>(repeat_info) = false;
            }
      drawSelection();
      }

//---------------------------------------------------------
//   tempo_meta
//---------------------------------------------------------

void Timeline::tempo_meta(Segment* seg, int* stagger, int pos)
      {
      //Find position of tempo_meta 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)++;
                        global_z_value++;
                        }
                  }
            }
      }

//---------------------------------------------------------
//   time_meta
//---------------------------------------------------------

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

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

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

      TimeSig* original_time_sig = toTimeSig(seg->element(0));
      if (!original_time_sig)
            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* curr_time_sig = toTimeSig(seg->element(track * VOICES));
            if (!curr_time_sig) {
                  same = false;
                  break;
                  }
            if (*curr_time_sig == *original_time_sig) {
                  continue;
                  }
            same = false;
            break;
            }
      if (!same)
            return;
      QString text = QString::number(original_time_sig->numerator()) + QString("/") + QString::number(original_time_sig->denominator());

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

//---------------------------------------------------------
//   rehearsal_meta
//---------------------------------------------------------

void Timeline::rehearsal_meta(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)++;
                        global_z_value++;
                        }
                  }
            }
      }

//---------------------------------------------------------
//   key_meta
//---------------------------------------------------------

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

      int row = getMetaRow(tr("Key Signature"));
      std::map<Key, int> key_frequencies;
      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* curr_key_sig = nullptr;
            if (seg)
                  curr_key_sig = toKeySig(seg->element(track));

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

            if (curr_key_sig && curr_key_sig->isAtonal())
                  global_key = Key::INVALID;
            else if (curr_key_sig && curr_key_sig->isCustom())
                  global_key = Key::NUM_OF;
            else {
                  const Interval curr_interval = stave->part()->instrument()->transpose();
                  global_key = transposeKey(global_key, curr_interval);
                  }

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

            track += VOICES;
            }

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

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

//---------------------------------------------------------
//   repeat_meta
//---------------------------------------------------------

void Timeline::barline_meta(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 repeat_text = "";
      BarLine* barline = toBarLine(seg->element(0));

      if (barline) {
            switch (barline->barLineType()) {
                  case BarLineType::START_REPEAT:
                        repeat_text = QString("Start repeat");
                        break;
                  case BarLineType::END_REPEAT:
                        repeat_text = QString("End repeat");
                        break;
                  case BarLineType::END_START_REPEAT:
                        repeat_text = QString("End-start repeat");
                        break;
                  case BarLineType::DOUBLE:
                        repeat_text = QString("Double barline");
                        break;
                  case BarLineType::END:
                        repeat_text = QString("Final barline");
                        break;
                  default:
                        break;
                  }
            is_barline = true;
            }
      else
            return;

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

      if (repeat_text == "") {
            is_barline = false;
            return;
            }

      int x = pos + (*stagger) * spacing;
      if (addMetaValue(x, pos, repeat_text, row, element_type, element, seg, measure)) {
            (*stagger)++;
            global_z_value++;
            }
      is_barline = false;
      }

//---------------------------------------------------------
//   jump_marker_meta
//---------------------------------------------------------

void Timeline::jump_marker_meta(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>(repeat_info))
            element = std::get<2>(repeat_info);
      else if (std::get<3>(repeat_info))
            element = std::get<3>(repeat_info);

      Measure* measure;
      ElementType element_type;
      if (std::get<2>(repeat_info)) {
            Jump* jump = toJump(std::get<2>(repeat_info));
            text = jump->plainText();
            measure = jump->measure();
            element_type = ElementType::JUMP;
            }
      else {
            Marker* marker = toMarker(std::get<3>(repeat_info));
            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) {
                  element_type = ElementType::MARKER;
                  std::get<2>(repeat_info) = std::get<3>(repeat_info);
                  std::get<3>(repeat_info) = nullptr;
                  }
            else
                  element_type = ElementType::MARKER;
            }

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

      int x = pos + (*stagger) * spacing;
      if (addMetaValue(x, pos, text, row, element_type, element, seg, measure)) {
            (*stagger)++;
            global_z_value++;
            }
      std::get<2>(repeat_info) = nullptr;
      std::get<3>(repeat_info) = nullptr;
      }

//---------------------------------------------------------
//   measure_meta
//---------------------------------------------------------

void Timeline::measure_meta(Segment* , int* , int pos)
      {
      //Increment decided by zoom level
      int increment_value = 1;
      int halfway = (max_zoom + min_zoom) / 2;
      if (grid_width <= max_zoom && grid_width > halfway)
            increment_value = 1;
      else if (grid_width <= halfway && grid_width > min_zoom)
            increment_value = 5;
      else
            increment_value = 10;

      int curr_measure_number = pos / grid_width;
      if (curr_measure_number == global_measure_number)
            return;

      global_measure_number = curr_measure_number;
      //Check if 1 or 5*n
      if (curr_measure_number + 1 != 1 && (curr_measure_number + 1) % increment_value != 0)
            return;

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

      //Adjust number
      Measure* curr_measure;
      for (curr_measure = _score->firstMeasure(); curr_measure_number != 0; curr_measure_number--, curr_measure = curr_measure->nextMeasure()) {

            }

      //Add measure number
      QString measure_number = (curr_measure->irregular())? "( )" : QString::number(curr_measure->no() + 1);
      QGraphicsTextItem* graphics_text_item = new QGraphicsTextItem(measure_number);
      graphics_text_item->setDefaultTextColor(QColor(0, 0, 0));
      graphics_text_item->setX(pos);
      graphics_text_item->setY(grid_height * row + verticalScrollBar()->value());

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

      //Center text
      qreal remaining_width =  grid_width - graphics_text_item->boundingRect().width();
      qreal remaining_height =  grid_height - graphics_text_item->boundingRect().height();
      graphics_text_item->setX(graphics_text_item->x() + remaining_width / 2);
      graphics_text_item->setY(graphics_text_item->y() + remaining_height / 2);

      int end_of_text = graphics_text_item->x() + graphics_text_item->boundingRect().width();
      int end_of_grid = getWidth();
      if (end_of_text <= end_of_grid) {
            scene()->addItem(graphics_text_item);

            std::pair<QGraphicsItem*, int> pair_measure_text(graphics_text_item, row);
            meta_rows.push_back(pair_measure_text);
            }
      }

//---------------------------------------------------------
//   getMetaRow
//---------------------------------------------------------

unsigned int Timeline::getMetaRow(QString target_text)
      {
      if (collapsed_meta) {
            if (target_text == 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 meta_text = std::get<0>(meta);
            bool visible = std::get<2>(meta);
            if (meta_text == target_text && visible)
                  break;
            else if (!visible)
                  continue;
            row++;
            }
      return row;
      }

//---------------------------------------------------------
//   addMetaValue
//---------------------------------------------------------

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

      QGraphicsPixmapItem* graphics_pixmap_item = nullptr;
      if (is_barline)
            graphics_pixmap_item = new QGraphicsPixmapItem(*barlines[meta_text]);

      if (graphics_pixmap_item) {
            text_width = 10;
            if (text_width > grid_width) {
                  text_width = grid_width;
                  if (meta_text == QString("End repeat") && std::get<4>(repeat_info))
                        text_width /= 2;
                  }
            }

      if (text_width + x > getWidth())
            text_width = getWidth() - x;

      //Adjust x for end repeats
      if ((meta_text == QString("End repeat") || meta_text == QString("Final barline") || meta_text == QString("Double barline") || std::get<2>(repeat_info)) && !collapsed_meta) {
            if (std::get<0>(repeat_info) > 0)
                  x = pos + grid_width - std::get<1>(repeat_info) + std::get<0>(repeat_info) * spacing;
            else {
                  x = pos + grid_width - text_width;
                  std::get<1>(repeat_info) = text_width;
                  }
            //Check if extending past left side
            if (x < 0) {
                  text_width = text_width + x;
                  x = 0;
                  }
            }

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

      QGraphicsItem* item_to_add;
      if (graphics_pixmap_item) {
            //Exact values required for repeat pixmap to work visually
            if (text_width != 10)
                  graphics_pixmap_item = new QGraphicsPixmapItem();
            if (meta_text == QString("Start repeat"))
                  std::get<4>(repeat_info) = true;
            graphics_pixmap_item->setX(x + 2);
            graphics_pixmap_item->setY(grid_height * row + verticalScrollBar()->value() + 3);
            item_to_add = graphics_pixmap_item;
            }
      else if (meta_text == "\uE047" || meta_text == "\uE048") {
            graphics_text_item->setX(x);
            graphics_text_item->setY(grid_height * row + verticalScrollBar()->value() - 2);
            item_to_add = graphics_text_item;
            }
      else if (row == 0 ) {
            graphics_text_item->setX(x);
            graphics_text_item->setY(grid_height * row + verticalScrollBar()->value() - 6);
            item_to_add = graphics_text_item;
            }
      else {
            graphics_text_item->setX(x);
            graphics_text_item->setY(grid_height * row + verticalScrollBar()->value() - 1);
            item_to_add = graphics_text_item;
            }

      QFontMetrics f(QApplication::font());
      QString part_name = f.elidedText(graphics_text_item->toPlainText(),
                                       Qt::ElideRight,
                                       text_width);

      //Set tool tip if elided
      if (tooltip != "")
            graphics_text_item->setToolTip(tooltip);
      else if (part_name != meta_text)
            graphics_text_item->setToolTip(graphics_text_item->toPlainText());
      graphics_text_item->setPlainText(part_name);

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

      QGraphicsRectItem* graphics_rect_item = new QGraphicsRectItem(x,
                                                                    grid_height * row + verticalScrollBar()->value(),
                                                                    text_width,
                                                                    grid_height);
      if (tooltip != "")
            graphics_rect_item->setToolTip(tooltip);
      else if (part_name != meta_text)
            graphics_rect_item->setToolTip(meta_text);
      else if (graphics_pixmap_item)
            graphics_rect_item->setToolTip(tr(meta_text.toLatin1().constData()));

      setMetaData(graphics_rect_item, -1, element_type, measure, true, element, item_to_add, seg);
      setMetaData(item_to_add, -1, element_type, measure, true, element, graphics_rect_item, seg);

      graphics_rect_item->setZValue(global_z_value);
      item_to_add->setZValue(global_z_value);

      graphics_rect_item->setPen(QPen(Qt::black));
      graphics_rect_item->setBrush(QBrush(Qt::gray));

      scene()->addItem(graphics_rect_item);
      scene()->addItem(item_to_add);

      std::pair<QGraphicsItem*, int> pair_time_rect(graphics_rect_item, row);
      std::pair<QGraphicsItem*, int> pair_time_text(item_to_add, row);
      meta_rows.push_back(pair_time_rect);
      meta_rows.push_back(pair_time_text);

      if (meta_text == QString("End repeat"))
            std::get<0>(repeat_info)++;

      return true;
      }

//---------------------------------------------------------
//   setMetaData
//---------------------------------------------------------

void Timeline::setMetaData(QGraphicsItem* gi, int staff, ElementType et, Measure* m, bool full_measure, Element* e, QGraphicsItem* pair_item, 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*>(pair_item));
      gi->setData(6, QVariant::fromValue<void*>(seg));
      }

//---------------------------------------------------------
//   getWidth
//---------------------------------------------------------

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

//---------------------------------------------------------
//   getHeight
//---------------------------------------------------------

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

//---------------------------------------------------------
//   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;
      }


//---------------------------------------------------------
//   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());
      }

//---------------------------------------------------------
//   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;
      }

//---------------------------------------------------------
//   changeSelection
//---------------------------------------------------------

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

      QRectF selection_rect = selection_path.boundingRect();
      if (selection_rect == QRectF())
            return;

      int nmeta = nmetas();

      //Get borders of the current viewport
      int left_border = horizontalScrollBar()->value();
      int right_border = horizontalScrollBar()->value() + viewport()->width();
      int top_border = verticalScrollBar()->value() + nmeta * grid_height;
      int bottom_border = verticalScrollBar()->value() + viewport()->height();

      bool selection_extends_up = false, selection_extends_left = false;
      bool selection_extends_right = false, selection_extends_down = false;

      //Figure out which directions the selection extends
      if (selection_rect.top() < top_border)
            selection_extends_up = true;
      if (selection_rect.left() < left_border - 1)
            selection_extends_left = true;
      if (selection_rect.right() > right_border)
            selection_extends_right = true;
      if (selection_rect.bottom() > bottom_border)
            selection_extends_down = true;

      if (selection_extends_down
          && old_selection_rect.bottom() != selection_rect.bottom()
          && !meta_value) {
            int new_scrollbar_value = int(verticalScrollBar()->value() + selection_rect.bottom() - bottom_border);
            verticalScrollBar()->setValue(new_scrollbar_value);
            }
      else if (selection_extends_up
               && !selection_extends_down
               && old_selection_rect.bottom() != selection_rect.bottom()
               && !meta_value
               && old_selection_rect.contains(selection_rect)) {
            int new_scrollbar_value = int(verticalScrollBar()->value() + selection_rect.bottom() - bottom_border);
            verticalScrollBar()->setValue(new_scrollbar_value);
            }

      if (selection_extends_right
          && old_selection_rect.right() != selection_rect.right()) {
            int new_scrollbar_value = int(horizontalScrollBar()->value() + selection_rect.right() - right_border);
            horizontalScrollBar()->setValue(new_scrollbar_value);
            }
      if (selection_extends_up
          && old_selection_rect.top() != selection_rect.top()
          && !meta_value) {
            int new_scrollbar_value = int(selection_rect.top()) - nmeta * grid_height;
            verticalScrollBar()->setValue(new_scrollbar_value);
            }
      if (selection_extends_left
          && old_selection_rect.left() != selection_rect.left()) {
            int new_scrollbar_value = int(selection_rect.left());
            horizontalScrollBar()->setValue(new_scrollbar_value);
            }

      if (selection_extends_left
          && !selection_extends_right
          && old_selection_rect.right() != selection_rect.right()
          && old_selection_rect.contains(selection_rect)) {
            int new_scrollbar_value = int(horizontalScrollBar()->value() + selection_rect.right() - right_border);
            horizontalScrollBar()->setValue(new_scrollbar_value);
            }
      if (selection_extends_right
          && !selection_extends_left
          && old_selection_rect.left() != selection_rect.left()
          && old_selection_rect.contains(selection_rect)) {
            int new_scrollbar_value = int(selection_rect.left());
            horizontalScrollBar()->setValue(new_scrollbar_value);
            }

      if (selection_extends_down
          && !selection_extends_up
          && old_selection_rect.top() != selection_rect.top()
          && !meta_value
          && old_selection_rect.contains(selection_rect)) {
            int new_scrollbar_value = int(selection_rect.top()) - nmeta * grid_height;
            verticalScrollBar()->setValue(new_scrollbar_value);
            }

      old_selection_rect = selection_rect;

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

//---------------------------------------------------------
//   drawSelection
//---------------------------------------------------------

void Timeline::drawSelection()
      {
      selection_path = QPainterPath();
      selection_path.setFillRule(Qt::WindingFill);

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

      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::END) &&
                      measure != _score->lastMeasure()) {
                        if (measure->prevMeasure())
                              measure = measure->prevMeasure();
                        }
                  }

            //element->type() for meta rows, invalid for everything else
            ElementType element_type = (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() ) {
            if (measure->mmRest() && measure->score()->styleB(Sid::createMultiMeasureRests)) {
                  int mmrest_count = measure->mmRest()->mmRestCount();
                  Measure* tmp_measure = measure;
                  for (int mmrest_measure = 0; mmrest_measure < mmrest_count; mmrest_measure++) {
                        std::tuple<Measure*, int, ElementType> tmp(tmp_measure, staffIdx, element_type);
                        meta_labels_set.insert(tmp);
                        tmp_measure = tmp_measure->nextMeasure();
                        }
                  }
            else {
                  std::tuple<Measure*, int, ElementType> tmp(measure, staffIdx, element_type);
                  meta_labels_set.insert(tmp);
                  }
            }

      QList<QGraphicsItem*> graphics_item_list = scene()->items();
      for (QGraphicsItem* graphics_item : graphics_item_list) {

            int stave = graphics_item->data(0).value<int>();
            ElementType element_type = graphics_item->data(1).value<ElementType>();
            Measure* measure = static_cast<Measure*>(graphics_item->data(2).value<void*>());

            std::tuple<Measure*, int, ElementType> target_tuple(measure, stave, element_type);
            std::set<std::tuple<Measure*, int, ElementType>>::iterator it;
            it = meta_labels_set.find(target_tuple);

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

                  if (target_element) {
                        for (Element* element : element_list) {
                              if (element == target_element) {
                                    QGraphicsRectItem* graphics_rect_item = qgraphicsitem_cast<QGraphicsRectItem*>(graphics_item);
                                    if (graphics_rect_item)
                                          graphics_rect_item->setBrush(QBrush(QColor(173,216,230)));
                                    }
                              }
                        }
                  else if (seg) {
                        for (Element* element : element_list) {
                              QGraphicsRectItem* graphics_rect_item = qgraphicsitem_cast<QGraphicsRectItem*>(graphics_item);
                              if (graphics_rect_item) {
                                    for (int track = 0; track < _score->nstaves() * VOICES; track++) {
                                          if (element == seg->element(track))
                                                graphics_rect_item->setBrush(QBrush(QColor(173,216,230)));
                                          }
                                    }
                              }
                        }
                  else {
                        QGraphicsRectItem* graphics_rect_item = qgraphicsitem_cast<QGraphicsRectItem*>(graphics_item);
                        if (graphics_rect_item)
                              graphics_rect_item->setBrush(QBrush(QColor(173,216,230)));
                        }
                  }
            //Change color from gray to only blue
            else if (it != meta_labels_set.end()) {
                  QGraphicsRectItem* graphics_rect_item = qgraphicsitem_cast<QGraphicsRectItem*>(graphics_item);
                  graphics_rect_item->setBrush(QBrush(QColor(graphics_rect_item->brush().color().red(),
                                                             graphics_rect_item->brush().color().green(),
                                                             255)));
                  selection_path.addRect(graphics_rect_item->rect());
                  }
            }

      QGraphicsPathItem* graphics_path_item = new QGraphicsPathItem(selection_path.simplified());
      if (selection.isRange())
            graphics_path_item->setPen(QPen(QColor(0, 0, 255), 3));
      else
            graphics_path_item->setPen(QPen(QColor(0, 0, 0), 1));

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

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

//---------------------------------------------------------
//   mousePressEvent
//---------------------------------------------------------

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

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

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

      QPointF scene_pt = mapToScene(event->pos());
      //Set as old location
      old_loc = QPoint(int(scene_pt.x()), int(scene_pt.y()));
      QList<QGraphicsItem*> graphics_item_list = scene()->items(scene_pt);
      //Find highest z value for rect
      int max_z_value = -4;
      QGraphicsItem* curr_graphics_item = nullptr;
      for (QGraphicsItem* graphics_item: graphics_item_list) {
            QGraphicsRectItem* graphics_rect_item = qgraphicsitem_cast<QGraphicsRectItem*>(graphics_item);
            if (graphics_rect_item && graphics_item->zValue() > max_z_value) {
                  curr_graphics_item = graphics_item;
                  max_z_value = graphics_item->zValue();
                  }
            }
      if (curr_graphics_item) {
            int stave = curr_graphics_item->data(0).value<int>();
            Measure* curr_measure = static_cast<Measure*>(curr_graphics_item->data(2).value<void*>());
            if (numToStaff(stave) && !numToStaff(stave)->show())
                  return;

            if (!curr_measure) {
                  int nmeta = nmetas();
                  int bottom_of_meta = nmeta * grid_height + verticalScrollBar()->value();

                  //Handle measure box clicks
                  if (scene_pt.y() > (nmeta - 1) * grid_height + verticalScrollBar()->value() &&
                      scene_pt.y() < bottom_of_meta) {

                        QRectF tmp(scene_pt.x(), 0, 3, nmeta * grid_height + nstaves() * grid_height);
                        QList<QGraphicsItem*> gl = scene()->items(tmp);
                        Measure* measure = nullptr;

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

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

            bool meta_value_clicked = curr_graphics_item->data(3).value<bool>();

            scene()->clearSelection();
            if (meta_value_clicked) {

                  meta_value = true;
                  old_selection_rect = QRect();

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

                  Segment* seg = static_cast<Segment*>(curr_graphics_item->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 element_type = curr_graphics_item->data(1).value<ElementType>();
                        SegmentType segment_type = SegmentType::Invalid;
                        if (element_type == ElementType::KEYSIG)
                              segment_type = SegmentType::KeySig;
                        else if (element_type == ElementType::TIMESIG)
                              segment_type = SegmentType::TimeSig;

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

                        if (_score->selection().isNone()) {
                              if (curr_measure->mmRest())
                                    curr_measure = curr_measure->mmRest();
                              else if (curr_measure->mmRestCount() == -1)
                                    curr_measure = curr_measure->prevMeasureMM();

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

                  }
            mscore->endCmd();
            _score->update();
            }
      else {
            _score->deselectAll();
            mscore->endCmd();
            _score->update();
            }
      _score->setUpdateAll();
      }

//---------------------------------------------------------
//   mouseMoveEvent
//---------------------------------------------------------

void Timeline::mouseMoveEvent(QMouseEvent* event)
      {
      QPointF new_loc = mapToScene(event->pos());
      if (!mouse_pressed) {

            if (cursorIsOn() == "meta") {
                  this->setCursor(Qt::ArrowCursor);
                  mouseOver(new_loc);
                  }
            else if (cursorIsOn() == "invalid")
                  this->setCursor(Qt::ForbiddenCursor);
            else
                  this->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(new_loc.x() - old_loc.x()) > 2 ||
                      abs(new_loc.y() - old_loc.y()) > 2) {
                        _score->deselectAll();
                        updateGrid();
                        state = ViewState::LASSO;
                        selection_box = new QGraphicsRectItem();
                        selection_box->setRect(old_loc.x(), old_loc.y(), 0, 0);
                        selection_box->setPen(QPen(QColor(0, 0, 255), 2));
                        selection_box->setBrush(QBrush(QColor(0, 0, 255, 50)));
                        scene()->addItem(selection_box);
                        }
                  }
            else {
                  state = ViewState::DRAG;
                  this->setCursor(Qt::SizeAllCursor);
                  }
            }

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

//---------------------------------------------------------
//   mouseReleaseEvent
//---------------------------------------------------------

void Timeline::mouseReleaseEvent(QMouseEvent*)
      {
      mouse_pressed = false;
      if (state == ViewState::LASSO) {

            scene()->removeItem(selection_box);
            _score->deselectAll();

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

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

                  if (!tl_graphics_item && !br_graphics_item) {
                        tl_graphics_item = graphics_item;
                        br_graphics_item = graphics_item;
                        continue;
                        }

                  if (graphics_item->boundingRect().top() < tl_graphics_item->boundingRect().top())
                        tl_graphics_item = graphics_item;
                  if (graphics_item->boundingRect().left() < tl_graphics_item->boundingRect().left())
                        tl_graphics_item = graphics_item;

                  if (graphics_item->boundingRect().bottom() > br_graphics_item->boundingRect().bottom())
                        br_graphics_item = graphics_item;
                  if (graphics_item->boundingRect().right() > br_graphics_item->boundingRect().right())
                        br_graphics_item = graphics_item;

                  }

            //Select single tl_graphics_item and then range br_graphics_item
            if (tl_graphics_item && br_graphics_item) {
                  Measure* tl_measure = static_cast<Measure*>(tl_graphics_item->data(2).value<void*>());
                  int tl_stave = tl_graphics_item->data(0).value<int>();
                  Measure* br_measure = static_cast<Measure*>(br_graphics_item->data(2).value<void*>());
                  int br_stave = br_graphics_item->data(0).value<int>();
                  if (tl_measure && br_measure) {
                        //Focus selection of mmRests here
                        if (tl_measure->mmRest())
                              tl_measure = tl_measure->mmRest();
                        else if (tl_measure->mmRestCount() == -1)
                              tl_measure = tl_measure->prevMeasureMM();
                        if (br_measure->mmRest())
                              br_measure = br_measure->mmRest();
                        else if (br_measure->mmRestCount() == -1)
                              br_measure = br_measure->prevMeasureMM();

                        _score->select(tl_measure, SelectType::SINGLE, tl_stave);
                        _score->select(br_measure, SelectType::RANGE, br_stave);
                        }
                  _cv->adjustCanvasPosition(tl_measure, false, tl_stave);
                  }

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

//---------------------------------------------------------
//   leaveEvent
//---------------------------------------------------------

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

//---------------------------------------------------------
//   wheelEvent
//---------------------------------------------------------

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

            if (event->angleDelta().y() > 0 && grid_width < max_zoom) {
                  grid_width++;
                  updateGrid();
                  }
            else if (event->angleDelta().y() < 0 && grid_width > min_zoom) {
                  grid_width--;
                  updateGrid();
                  }

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

//---------------------------------------------------------
//   updateGrid
//---------------------------------------------------------

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

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

//---------------------------------------------------------
//   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);
            row_names->updateLabels(getLabels(), grid_height);
            }
      else {
            //Clear timeline if no score is present
            QSplitter* sp = scrollArea->grid();
            if (sp && sp->count() > 0) {
                  TRowLabels* t_row_labels = static_cast<TRowLabels*>(sp->widget(0));
                  std::vector<std::pair<QString, bool>> no_labels;
                  t_row_labels->updateLabels(no_labels, 0);
                  }
            meta_rows.clear();
            setSceneRect(0, 0, 0, 0);
            }

      viewport()->update();
      }

//---------------------------------------------------------
//   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();
            }
      }

//---------------------------------------------------------
//   objectDestroyed
//---------------------------------------------------------

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

//---------------------------------------------------------
//   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>> visible_items_set;

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

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

                        //Add all measures within mmRest to visible_items_set if mmRest_visible
                        for (; curr_measure != mmrest_measure->mmRestLast(); curr_measure = curr_measure->nextMeasure()) {
                              for (int staff = 0; staff < _score->staves().length(); staff++) {
                                    if (!_score->staff(staff)->show())
                                          continue;
                                    QRectF stave_rect = QRectF(system->canvasBoundingRect().left(),
                                                               system->staffCanvasYpage(staff),
                                                               system->width(),
                                                               system->staff(staff)->bbox().height());
                                    QRectF show_rect = mmrest_measure->canvasBoundingRect().intersected(stave_rect);

                                    if (canvas.intersects(show_rect)) {
                                          std::pair<Measure*, int> p(curr_measure, staff);
                                          visible_items_set.insert(p);
                                          }
                                    }
                              }

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

                              if (canvas.intersects(show_rect)) {
                                    std::pair<Measure*, int> p(curr_measure, staff);
                                    visible_items_set.insert(p);
                                    }
                              }
                        continue;
                        }

                  if (!system)
                        continue;

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

                        if (canvas.intersects(show_rect)) {
                              std::pair<Measure*, int> p(curr_measure, staff);
                              visible_items_set.insert(p);
                              }
                        }
                  }

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

                  std::pair<Measure*, int> tmp(measure, stave);
                  std::set<std::pair<Measure*, int>>::iterator it;
                  it = visible_items_set.find(tmp);
                  if (it != visible_items_set.end())
                        visible_painter_path.addRect(graphics_item->boundingRect());
                  }

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

            QRectF timeline_rect = QRectF(0, 0, getWidth(), getHeight());
            non_visible_painter_path.addRect(timeline_rect);

            non_visible_painter_path = non_visible_painter_path.subtracted(visible_painter_path);

            QGraphicsPathItem* non_visible_path_item = new QGraphicsPathItem(non_visible_painter_path.simplified());

            QPen non_visible_pen = QPen(QColor(100, 150, 250));
            QBrush non_visible_brush = QBrush(QColor(192, 192, 192, 180));
            non_visible_path_item->setPen(QPen(non_visible_brush.color()));
            non_visible_path_item->setBrush(non_visible_brush);
            non_visible_path_item->setZValue(-3);

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

            //Find old path, remove it
            for (QGraphicsItem* graphics_item : scene()->items()) {
                  if (graphics_item->type() == QGraphicsPathItem().type()) {
                        QGraphicsPathItem* old_path_item = static_cast<QGraphicsPathItem*>(graphics_item);
                        QBrush old_brush = old_path_item->brush();
                        QPen old_pen = old_path_item->pen();
                        if (old_brush == non_visible_brush || old_pen == non_visible_pen)
                              scene()->removeItem(old_path_item);
                        }
                  }

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

//---------------------------------------------------------
//   nstaves
//---------------------------------------------------------

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

//---------------------------------------------------------
//   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* chord_rest = seg->cr(track);
                  if (chord_rest) {
                        ElementType crt = chord_rest->type();
                        if (crt == ElementType::CHORD || crt == ElementType::REPEAT_MEASURE)
                              return QColor(Qt::gray);
                        }
                  }
            }
      return QColor(224,224,224);
      }

//---------------------------------------------------------
//   getLabels
//---------------------------------------------------------

std::vector<std::pair<QString, bool>> Timeline::getLabels()
      {
      if (!_score) {
            std::vector<std::pair<QString, bool>> no_labels;
            return no_labels;
            }
      QList<Part*> part_list = getParts();
      //transfer them into a vector of qstrings and then add the meta row names
      std::vector<std::pair<QString, bool>> row_labels;
      if (collapsed_meta) {
            std::pair<QString, bool> first("", true);
            std::pair<QString, bool> second(tr("Measures"), true);
            row_labels.push_back(first);
            row_labels.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> meta_label(std::get<0>(meta), true);
                  row_labels.push_back(meta_label);
                  }
            }

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

            std::pair<QString, bool> instrument_label(part_name, part_list.at(stave)->show());
            row_labels.push_back(instrument_label);
            }
      return row_labels;
      }

//---------------------------------------------------------
//   handle_scroll
//---------------------------------------------------------

void Timeline::handle_scroll(int value)
      {
      if (!_score)
            return;
      for (std::vector<std::pair<QGraphicsItem*, int>>::iterator it = meta_rows.begin();
           it != meta_rows.end(); ++it) {
            std::pair<QGraphicsItem*, int> pair_graphics_int = *it;

            QGraphicsItem* graphics_item = pair_graphics_int.first;
            QGraphicsRectItem* graphics_rect_item = qgraphicsitem_cast<QGraphicsRectItem*>(graphics_item);
            QGraphicsLineItem* graphics_line_item = qgraphicsitem_cast<QGraphicsLineItem*>(graphics_item);
            QGraphicsPixmapItem* graphics_pixmap_item = qgraphicsitem_cast<QGraphicsPixmapItem*>(graphics_item);

            int row_y = pair_graphics_int.second * grid_height;
            int scrollbar_value = value;

            if (graphics_rect_item) {
                  QRectF rectf = graphics_rect_item->rect();
                  rectf.setY(qreal(scrollbar_value + row_y));
                  rectf.setHeight(grid_height);
                  graphics_rect_item->setRect(rectf);
                  }
            else if (graphics_line_item) {
                  QLineF linef = graphics_line_item->line();
                  linef.setLine(linef.x1(), row_y + scrollbar_value + 1, linef.x2(), row_y + scrollbar_value + 1);
                  graphics_line_item->setLine(linef);
                  }
            else if (graphics_pixmap_item)
                  graphics_pixmap_item->setY(qreal(scrollbar_value + row_y + 3));
            else
                  graphics_item->setY(qreal(scrollbar_value + row_y));
            }
      viewport()->update();
      }

//---------------------------------------------------------
//   mouseOver
//---------------------------------------------------------

void Timeline::mouseOver(QPointF pos)
      {
      //Choose item with the largest original Z value...
      QList<QGraphicsItem*> graphics_list = scene()->items(pos);
      QGraphicsItem* hovered_graphics_item = 0;
      int max_z_value = -1;
      for (QGraphicsItem* curr_graphics_item: graphics_list) {
            if (qgraphicsitem_cast<QGraphicsTextItem*>(curr_graphics_item))
                  continue;
            if (curr_graphics_item->zValue() >= max_z_value && curr_graphics_item->zValue() < global_z_value) {
                  hovered_graphics_item = curr_graphics_item;
                  max_z_value = hovered_graphics_item->zValue();
                  }
            else if (curr_graphics_item->zValue() > global_z_value && std::get<1>(old_hover_info) >= max_z_value){
                  hovered_graphics_item = curr_graphics_item;
                  max_z_value = std::get<1>(old_hover_info);
                  }
            }

      if (!hovered_graphics_item) {
            if (std::get<0>(old_hover_info)) {
                  std::get<0>(old_hover_info)->setZValue(std::get<1>(old_hover_info));
                  static_cast<QGraphicsItem*>(std::get<0>(old_hover_info)->data(5).value<void*>())->setZValue(std::get<1>(old_hover_info));
                  QGraphicsRectItem* graphics_rect_item1 = qgraphicsitem_cast<QGraphicsRectItem*>(std::get<0>(old_hover_info));
                  QGraphicsRectItem* graphics_rect_item2 = qgraphicsitem_cast<QGraphicsRectItem*>(static_cast<QGraphicsItem*>(std::get<0>(old_hover_info)->data(5).value<void*>()));
                  if (graphics_rect_item1)
                        graphics_rect_item1->setBrush(QBrush(std::get<2>(old_hover_info)));
                  if (graphics_rect_item2)
                        graphics_rect_item2->setBrush(QBrush(std::get<2>(old_hover_info)));
                  std::get<0>(old_hover_info) = nullptr;
                  std::get<1>(old_hover_info) = -1;
                  }
            return;
            }
      QGraphicsItem* pair_item = static_cast<QGraphicsItem*>(hovered_graphics_item->data(5).value<void*>());
      if (!pair_item) {
            if (std::get<0>(old_hover_info)) {
                  std::get<0>(old_hover_info)->setZValue(std::get<1>(old_hover_info));
                  static_cast<QGraphicsItem*>(std::get<0>(old_hover_info)->data(5).value<void*>())->setZValue(std::get<1>(old_hover_info));
                  QGraphicsRectItem* graphics_rect_item1 = qgraphicsitem_cast<QGraphicsRectItem*>(std::get<0>(old_hover_info));
                  QGraphicsRectItem* graphics_rect_item2 = qgraphicsitem_cast<QGraphicsRectItem*>(static_cast<QGraphicsItem*>(std::get<0>(old_hover_info)->data(5).value<void*>()));
                  if (graphics_rect_item1)
                        graphics_rect_item1->setBrush(QBrush(std::get<2>(old_hover_info)));
                  if (graphics_rect_item2)
                        graphics_rect_item2->setBrush(QBrush(std::get<2>(old_hover_info)));
                  std::get<0>(old_hover_info) = nullptr;
                  std::get<1>(old_hover_info) = -1;
                  }
            return;
            }

      if (std::get<0>(old_hover_info) == hovered_graphics_item)
            return;

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

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

      std::get<1>(old_hover_info) = hovered_graphics_item->zValue();
      std::get<0>(old_hover_info) = hovered_graphics_item;

      //Give items the top z value
      hovered_graphics_item->setZValue(global_z_value + 1);
      pair_item->setZValue(global_z_value + 1);

      QGraphicsRectItem* graphics_rect_item1 = qgraphicsitem_cast<QGraphicsRectItem*>(hovered_graphics_item);
      QGraphicsRectItem* graphics_rect_item2 = qgraphicsitem_cast<QGraphicsRectItem*>(pair_item);
      if (graphics_rect_item1) {
            std::get<2>(old_hover_info) = graphics_rect_item1->brush().color();
            if (std::get<2>(old_hover_info) != QColor(173,216,230))
                  graphics_rect_item1->setBrush(QBrush(Qt::lightGray));
            }
      if (graphics_rect_item2) {
            std::get<2>(old_hover_info) = graphics_rect_item2->brush().color();
            if (std::get<2>(old_hover_info) != QColor(173,216,230))
                  graphics_rect_item2->setBrush(QBrush(Qt::lightGray));
            }
      }

//---------------------------------------------------------
//   swapMeta
//---------------------------------------------------------

void Timeline::swapMeta(unsigned int row, bool switch_up)
      {
      //Attempt to switch row up or down, skipping non visible rows
      if (switch_up && 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 (!switch_up && 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();
      }

//---------------------------------------------------------
//   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;
      }

//---------------------------------------------------------
//   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();
            }
      }

//---------------------------------------------------------
//   showContextMenu
//---------------------------------------------------------

void Timeline::contextMenuEvent(QContextMenuEvent*)
      {
      QMenu* context_menu = new QMenu(tr("Context menu"), this);
      if (row_names->cursorIsOn() == "instrument") {
            QAction* edit_instruments = new QAction(tr("Edit Instruments"), this);
            connect(edit_instruments, SIGNAL(triggered()), this, SLOT(requestInstrumentDialog()));
            context_menu->addAction(edit_instruments);
            context_menu->exec(QCursor::pos());
            }
      else if (row_names->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()));
                        context_menu->addAction(action);
                        }
                  }
            context_menu->addSeparator();
            QAction* hide_all = new QAction(tr("Hide all"), this);
            connect(hide_all, SIGNAL(triggered()), this, SLOT(toggleMetaRow()));
            context_menu->addAction(hide_all);
            QAction* show_all = new QAction(tr("Show all"), this);
            connect(show_all, SIGNAL(triggered()), this, SLOT(toggleMetaRow()));
            context_menu->addAction(show_all);
            context_menu->exec(QCursor::pos());
            }
      }

//---------------------------------------------------------
//   toggleMetaRow
//---------------------------------------------------------

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

            if (target_text == tr("Hide all")) {
                  for (auto it = metas.begin(); it != metas.end(); ++it) {
                        QString meta_text = std::get<0>(*it);
                        if (meta_text != tr("Measures"))
                              std::get<2>(*it) = false;
                        }
                  updateGrid();
                  return;
                  }
            else if (target_text == 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 meta_text = std::get<0>(*it);
                  if (meta_text == target_text) {
                        std::get<2>(*it) = checked;
                        updateGrid();
                        break;
                        }
                  }
            }
      }

//---------------------------------------------------------
//   nmetas
//---------------------------------------------------------

unsigned int Timeline::nmetas()
      {
      unsigned int total = 0;
      if (collapsed_meta)
            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;
      }

//---------------------------------------------------------
//   correctMetaRow
//---------------------------------------------------------

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

//---------------------------------------------------------
//   correctMetaRow
//---------------------------------------------------------

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

//---------------------------------------------------------
//   requestInstrumentDialog
//---------------------------------------------------------

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

}
