    //=============================================================================
    //  MuseScore
    //  Music Composition & Notation
    //
    //  Copyright (C) 2002-2011 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
    //=============================================================================

    /**
     \file
     Implementation of class Score (partial).
    */

    #include <assert.h>
    #include "score.h"
    #include "key.h"
    #include "sig.h"
    #include "clef.h"
    #include "tempo.h"
    #include "measure.h"
    #include "page.h"
    #include "undo.h"
    #include "system.h"
    #include "select.h"
    #include "segment.h"
    #include "xml.h"
    #include "text.h"
    #include "note.h"
    #include "chord.h"
    #include "rest.h"
    #include "slur.h"
    #include "staff.h"
    #include "part.h"
    #include "style.h"
    #include "tuplet.h"
    #include "lyrics.h"
    #include "pitchspelling.h"
    #include "line.h"
    #include "volta.h"
    #include "repeat.h"
    #include "ottava.h"
    #include "barline.h"
    #include "box.h"
    #include "utils.h"
    #include "excerpt.h"
    #include "stafftext.h"
    #include "repeatlist.h"
    #include "keysig.h"
    #include "beam.h"
    #include "stafftype.h"
    #include "tempotext.h"
    #include "articulation.h"
    #include "revisions.h"
    #include "tiemap.h"
    #include "layoutbreak.h"
    #include "harmony.h"
    #include "mscore.h"
    #ifdef OMR
    #include "omr/omr.h"
    #endif
    #include "bracket.h"
    #include "audio.h"
    #include "instrtemplate.h"
    #include "cursor.h"
    #include "sym.h"
    #include "rehearsalmark.h"
    #include "breath.h"
    #include "instrchange.h"

    namespace Ms {

    MasterScore* gscore;                 ///< system score, used for palettes etc.

    bool scriptDebug     = false;
    bool noSeq           = false;
    bool noMidi          = false;
    bool midiInputTrace  = false;
    bool midiOutputTrace = false;
    bool showRubberBand  = true;

    //---------------------------------------------------------
    //   MeasureBaseList
    //---------------------------------------------------------

    MeasureBaseList::MeasureBaseList()
          {
          _first = 0;
          _last  = 0;
          _size  = 0;
          };

    //---------------------------------------------------------
    //   push_back
    //---------------------------------------------------------

    void MeasureBaseList::push_back(MeasureBase* e)
          {
          ++_size;
          if (_last) {
                _last->setNext(e);
                e->setPrev(_last);
                e->setNext(0);
                }
          else {
                _first = e;
                e->setPrev(0);
                e->setNext(0);
                }
          _last = e;
          }

    //---------------------------------------------------------
    //   push_front
    //---------------------------------------------------------

    void MeasureBaseList::push_front(MeasureBase* e)
          {
          ++_size;
          if (_first) {
                _first->setPrev(e);
                e->setNext(_first);
                e->setPrev(0);
                }
          else {
                _last = e;
                e->setPrev(0);
                e->setNext(0);
                }
          _first = e;
          }

    //---------------------------------------------------------
    //   add
    //    insert e before e->next()
    //---------------------------------------------------------

    void MeasureBaseList::add(MeasureBase* e)
          {
          MeasureBase* el = e->next();
          if (el == 0) {
                push_back(e);
                return;
                }
          if (el == _first) {
                push_front(e);
                return;
                }
          ++_size;
          e->setPrev(el->prev());
          el->prev()->setNext(e);
          el->setPrev(e);
          }

    //---------------------------------------------------------
    //   remove
    //---------------------------------------------------------

    void MeasureBaseList::remove(MeasureBase* el)
          {
          --_size;
          if (el->prev())
                el->prev()->setNext(el->next());
          else
                _first = el->next();
          if (el->next())
                el->next()->setPrev(el->prev());
          else
                _last = el->prev();
          }

    //---------------------------------------------------------
    //   insert
    //---------------------------------------------------------

    void MeasureBaseList::insert(MeasureBase* fm, MeasureBase* lm)
          {
          ++_size;
          for (MeasureBase* m = fm; m != lm; m = m->next())
                ++_size;
          MeasureBase* pm = fm->prev();
          if (pm)
                pm->setNext(fm);
          else
                _first = fm;
          MeasureBase* nm = lm->next();
          if (nm)
                nm->setPrev(lm);
          else
                _last = lm;
          }

    //---------------------------------------------------------
    //   remove
    //---------------------------------------------------------

    void MeasureBaseList::remove(MeasureBase* fm, MeasureBase* lm)
          {
          --_size;
          for (MeasureBase* m = fm; m != lm; m = m->next())
                --_size;
          MeasureBase* pm = fm->prev();
          MeasureBase* nm = lm->next();
          if (pm)
                pm->setNext(nm);
          else
                _first = nm;
          if (nm)
                nm->setPrev(pm);
          else
                _last = pm;
          }

    //---------------------------------------------------------
    //   change
    //---------------------------------------------------------

    void MeasureBaseList::change(MeasureBase* ob, MeasureBase* nb)
          {
          nb->setPrev(ob->prev());
          nb->setNext(ob->next());
          if (ob->prev())
                ob->prev()->setNext(nb);
          if (ob->next())
                ob->next()->setPrev(nb);
          if (ob == _last)
                _last = nb;
          if (ob == _first)
                _first = nb;
          if (nb->type() == ElementType::HBOX || nb->type() == ElementType::VBOX
             || nb->type() == ElementType::TBOX || nb->type() == ElementType::FBOX)
                nb->setSystem(ob->system());
          foreach(Element* e, nb->el())
                e->setParent(nb);
          }

    //---------------------------------------------------------
    //   Score
    //---------------------------------------------------------

    Score::Score()
       : ScoreElement(this), _is(this), _selection(this), _selectionFilter(this)
          {
          _masterScore = 0;
          Layer l;
          l.name          = "default";
          l.tags          = 1;
          _layer.append(l);
          _layerTags[0]   = "default";

          _scoreFont = ScoreFont::fontFactory("emmentaler");

          _pos[int(POS::CURRENT)] = 0;
          _pos[int(POS::LEFT)]    = 0;
          _pos[int(POS::RIGHT)]   = 0;
          _fileDivision           = MScore::division;
          _style  = MScore::defaultStyle();
    //      accInfo = tr("No selection");     // ??
          accInfo = "No selection";
          }

    Score::Score(MasterScore* parent)
       : Score{}
          {
          _masterScore = parent;
          if (MScore::defaultStyleForParts())
                _style = *MScore::defaultStyleForParts();
          else {
                // inherit most style settings from parent
                _style = parent->style();

                static const StyleIdx styles[] = {
                      StyleIdx::pageWidth,
                      StyleIdx::pageHeight,
                      StyleIdx::pagePrintableWidth,
                      StyleIdx::pageEvenLeftMargin,
                      StyleIdx::pageOddLeftMargin,
                      StyleIdx::pageEvenTopMargin,
                      StyleIdx::pageEvenBottomMargin,
                      StyleIdx::pageOddTopMargin,
                      StyleIdx::pageOddBottomMargin,
                      StyleIdx::pageTwosided,
                      StyleIdx::spatium
                      };
                // but borrow defaultStyle page layout settings
                for (auto i : styles)
                      _style.set(i, MScore::defaultStyle().value(i));
                // and force some style settings that just make sense for parts
                style().set(StyleIdx::concertPitch, false);
                style().set(StyleIdx::createMultiMeasureRests, true);
                style().set(StyleIdx::dividerLeft, false);
                style().set(StyleIdx::dividerRight, false);
                }
          _synthesizerState = parent->_synthesizerState;
          }

    Score::Score(MasterScore* parent, const MStyle& s)
       : Score{parent}
          {
          _style  = s;
          }

    //---------------------------------------------------------
    //   ~Score
    //---------------------------------------------------------

    Score::~Score()
          {
          foreach(MuseScoreView* v, viewer)
                v->removeScore();
          // deselectAll();
          for (MeasureBase* m = _measures.first(); m;) {
                MeasureBase* nm = m->next();
                delete m;
                m = nm;
                }
          qDeleteAll(_parts);
          qDeleteAll(_staves);
          qDeleteAll(_systems);
    //      qDeleteAll(_pages);
          _masterScore = 0;
          }

    //---------------------------------------------------------
    //   elementAdjustReadPos
    //---------------------------------------------------------

    static void elementAdjustReadPos(void*, Element* e)
          {
          if (e->isMovable())
                e->adjustReadPos();
          }

    //---------------------------------------------------------
    //   addMeasure
    //---------------------------------------------------------

    void Score::addMeasure(MeasureBase* m, MeasureBase* pos)
          {
          m->setNext(pos);
          _measures.add(m);
          }

    //---------------------------------------------------------
    //    fixTicks
    //    update:
    //      - measure ticks
    //      - tempo map
    //      - time signature map
    //---------------------------------------------------------

    /**
     This is needed after
          - inserting or removing a measure
          - changing the sigmap
          - after inserting/deleting time (changes the sigmap)
    */

    void Score::fixTicks()
          {
          int tick = 0;
          Measure* fm = firstMeasure();
          if (fm == 0)
                return;

          for (Staff* staff : _staves)
                staff->clearTimeSig();

          Fraction sig(fm->len());
          Fraction nomSig(fm->timesig());

          if (isMaster()) {
                tempomap()->clear();
                sigmap()->clear();
                sigmap()->add(0, SigEvent(fm->len(),  fm->timesig(), 0));
                }

          for (MeasureBase* mb = first(); mb; mb = mb->next()) {
                if (mb->type() != ElementType::MEASURE) {
                      mb->setTick(tick);
                      continue;
                      }
                Measure* m       = toMeasure(mb);
                int mtick        = m->tick();
                int diff         = tick - mtick;
                int measureTicks = m->ticks();
                m->moveTicks(diff);
                if (m->mmRest())
                      m->mmRest()->moveTicks(diff);

                //
                //  implement section break rest
                //
                if (isMaster() && m->sectionBreak() && m->pause() != 0.0)
                      setPause(m->tick() + m->ticks() - 1, m->pause());

                //
                // implement fermata as a tempo change
                //

                for (Segment* s = m->first(); s; s = s->next()) {
                      if (isMaster() && s->segmentType() == SegmentType::Breath) {
                            qreal length = 0.0;
                            int tick = s->tick();
                            // find longest pause
                            for (int i = 0, n = ntracks(); i < n; ++i) {
                                  Element* e = s->element(i);
                                  if (e && e->type() == ElementType::BREATH) {
                                        Breath* b = toBreath(e);
                                        length = qMax(length, b->pause());
                                        }
                                  }
                            if (length != 0.0)
                                  setPause(tick, length);
                            }
                      else if (s->segmentType() == SegmentType::TimeSig) {
                            for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                                  TimeSig* ts = toTimeSig(s->element(staffIdx * VOICES));
                                  if (ts)
                                        staff(staffIdx)->addTimeSig(ts);
                                  }
                            }
                      // TODO: all done in doLayout, getNextMeasure, collectSystem... Do we keep?
                      else if (isMaster() && (s->segmentType() == SegmentType::ChordRest)) {
                            for (Element* e : s->annotations()) {
                                  if (e->type() == ElementType::TEMPO_TEXT) {
                                        TempoText* tt = toTempoText(e);
                                        if (tt->isRelative())
                                              tt->updateRelative();
                                        setTempo(tt->segment(), tt->tempo());
                                        }
                                  }
                            qreal stretch = 0.0;
                            for (unsigned i = 0; i < s->elist().size(); ++i) {
                                  Element* e = s->elist().at(i);
                                  if (!e)
                                        continue;
                                  ChordRest* cr = toChordRest(e);
                                  int nn = cr->articulations().size();
                                  for (int ii = 0; ii < nn; ++ii)
                                        stretch = qMax(cr->articulations().at(ii)->timeStretch(), stretch);
                                  if (stretch != 0.0 && stretch != 1.0) {
                                        qreal otempo = tempomap()->tempo(cr->tick());
                                        qreal ntempo = otempo / stretch;
                                        setTempo(cr->tick(), ntempo);
                                        int etick = cr->tick() + cr->actualTicks() - 1;
                                        auto e = tempomap()->find(etick);
                                        if (e == tempomap()->end())
                                              setTempo(etick, otempo);
                                        break;
                                        }
                                  }
                            }
                      }

                // update time signature map
                // create event if measure len and time signature are different
                // even if they are equivalent 4/4 vs 2/2
                // also check if nominal time signature has changed

                if (isMaster() && (!sig.identical(m->len()) || !nomSig.identical(m->timesig()))) {
                      sig = m->len();
                      nomSig = m->timesig();
                      sigmap()->add(tick, SigEvent(sig, nomSig,  m->no()));
                      }

                tick += measureTicks;
                }
          // Now done in getNextMeasure(), do we keep?
          if (tempomap()->empty())
                tempomap()->setTempo(0, 2.0);
          }

    //---------------------------------------------------------
    //   validSegment
    //---------------------------------------------------------

    static bool validSegment(Segment* s, int startTrack, int endTrack)
          {
          for (int track = startTrack; track < endTrack; ++track) {
                if (s->element(track))
                      return true;
                }
          return false;
          }

    //---------------------------------------------------------
    //   pos2measure
    //     Return measure for canvas relative position \a p.
    //---------------------------------------------------------

    Measure* Score::pos2measure(const QPointF& p, int* rst, int* pitch, Segment** seg, QPointF* offset) const
          {
          Measure* m = searchMeasure(p);
          if (m == 0)
                return 0;

          System* s = m->system();
          qreal y   = p.y() - s->canvasPos().y();

          int i;
          for (i = 0; i < nstaves();) {
                SysStaff* stff = s->staff(i);
                if (!stff->show() || !staff(i)->show()) {
                      ++i;
                      continue;
                      }
                int ni = i;
                for (;;) {
                      ++ni;
                      if (ni == nstaves() || (s->staff(ni)->show() && staff(ni)->show()))
                            break;
                      }

                qreal sy2;
                if (ni != nstaves()) {
                      SysStaff* nstaff = s->staff(ni);
                      qreal s1y2 = stff->bbox().y() + stff->bbox().height();
                      sy2 = s1y2 + (nstaff->bbox().y() - s1y2)/2;
                      }
                else
                      sy2 = s->page()->height() - s->pos().y();   // s->height();
                if (y > sy2) {
                      i   = ni;
                      continue;
                      }
                break;
                }

          // search for segment + offset
          QPointF pppp = p - m->canvasPos();
          int strack = i * VOICES;
          if (!staff(i))
                return 0;
          int etrack = staff(i)->part()->nstaves() * VOICES + strack;

          SysStaff* sstaff = m->system()->staff(i);
          SegmentType st = SegmentType::ChordRest;
          for (Segment* segment = m->first(st); segment; segment = segment->next(st)) {
                if (!validSegment(segment, strack, etrack))
                      continue;
                Segment* ns = segment->next(st);
                for (; ns; ns = ns->next(st)) {
                      if (validSegment(ns, strack, etrack))
                            break;
                      }
                if (!ns || (pppp.x() < (segment->x() + (ns->x() - segment->x())/2.0))) {
                      *rst = i;
                      if (pitch) {
                            Staff* s = _staves[i];
                            int tick = segment->tick();
                            ClefType clef = s->clef(tick);
                            *pitch = y2pitch(pppp.y() - sstaff->bbox().y(), clef, s->spatium(tick));
                            }
                      if (offset)
                            *offset = pppp - QPointF(segment->x(), sstaff->bbox().y());
                      if (seg)
                            *seg = segment;
                      return m;
                      }
                }
          return 0;
          }

    //---------------------------------------------------------
    //   dragPosition
    //    on input:
    //          p   - canvas relative drag position
    //          rst - current staff index
    //          seg - current segment
    //    on output:
    //          rst - new staff index for drag position
    //          seg - new segment for drag position
    //---------------------------------------------------------

    void Score::dragPosition(const QPointF& p, int* rst, Segment** seg) const
          {
          Measure* m = searchMeasure(p);
          if (m == 0)
                return;

          System* s = m->system();
          qreal y   = p.y() - s->canvasPos().y();

          int i;
          for (i = 0; i < nstaves();) {
                SysStaff* stff = s->staff(i);
                if (!stff->show() || !staff(i)->show()) {
                      ++i;
                      continue;
                      }
                int ni = i;
                for (;;) {
                      ++ni;
                      if (ni == nstaves() || (s->staff(ni)->show() && staff(ni)->show()))
                            break;
                      }

                qreal sy2;
                if (ni != nstaves()) {
                      SysStaff* nstaff = s->staff(ni);
                      qreal s1y2       = stff->bbox().y() + stff->bbox().height();
                      if (i == *rst)
                            sy2 = s1y2 + (nstaff->bbox().y() - s1y2);
                      else if (ni == *rst)
                            sy2 = s1y2;
                      else
                            sy2 = s1y2 + (nstaff->bbox().y() - s1y2) * .5;
                      }
                else
                      sy2 = s->page()->height() - s->pos().y();
                if (y > sy2) {
                      i   = ni;
                      continue;
                      }
                break;
                }

          // search for segment + offset
          QPointF pppp = p - m->canvasPos();
          int strack   = i * VOICES;
          if (!staff(i))
                return;
          int etrack = staff(i)->part()->nstaves() * VOICES + strack;

          SegmentType st = SegmentType::ChordRest;
          for (Segment* segment = m->first(st); segment; segment = segment->next(st)) {
                if (!validSegment(segment, strack, etrack))
                      continue;
                Segment* ns = segment->next(st);
                for (; ns; ns = ns->next(st)) {
                      if (validSegment(ns, strack, etrack))
                            break;
                      }
                if (!ns) {
                      *rst = i;
                      *seg = segment;
                      return;
                      }
                if (*seg == segment) {
                      if (pppp.x() < (segment->x() + (ns->x() - segment->x()))) {
                            *rst = i;
                            *seg = segment;
                            return;
                            }
                      }
                else if (*seg == ns) {
                      if (pppp.x() <= segment->x()) {
                            *rst = i;
                            *seg = segment;
                            return;
                            }
                      }
                else {
                      if (pppp.x() < (segment->x() + (ns->x() - segment->x())/2.0)) {
                            *rst = i;
                            *seg = segment;
                            return;
                            }
                      }
                }
          return;
          }

    //---------------------------------------------------------
    //   setShowInvisible
    //---------------------------------------------------------

    void Score::setShowInvisible(bool v)
          {
          _showInvisible = v;
    //      rebuildBspTree();
          setUpdateAll();
    //      update();
          }

    //---------------------------------------------------------
    //   setShowUnprintable
    //---------------------------------------------------------

    void Score::setShowUnprintable(bool v)
          {
          _showUnprintable = v;
          setUpdateAll();
    //      update();
          }

    //---------------------------------------------------------
    //   setShowFrames
    //---------------------------------------------------------

    void Score::setShowFrames(bool v)
          {
          _showFrames = v;
          setUpdateAll();
    //      update();
          }

    //---------------------------------------------------------
    //   setShowPageborders
    //---------------------------------------------------------

    void Score::setShowPageborders(bool v)
          {
          _showPageborders = v;
          setUpdateAll();
    //      update();
          }

    //---------------------------------------------------------
    //   setMarkIrregularMeasures
    //---------------------------------------------------------

    void Score::setMarkIrregularMeasures(bool v)
          {
          _markIrregularMeasures = v;
          setUpdateAll();
    //      update();
          }

    //---------------------------------------------------------
    //   dirty
    //---------------------------------------------------------

    bool Score::dirty() const
          {
          return !undoStack()->isClean();
          }

    //---------------------------------------------------------
    //   spell
    //---------------------------------------------------------

    void Score::spell()
          {
          for (int i = 0; i < nstaves(); ++i) {
                std::vector<Note*> notes;
                for (Segment* s = firstSegment(SegmentType::All); s; s = s->next1()) {
                      int strack = i * VOICES;
                      int etrack = strack + VOICES;
                      for (int track = strack; track < etrack; ++track) {
                            Element* e = s->element(track);
                            if (e && e->type() == ElementType::CHORD)
                                  notes.insert(notes.end(),
                                     toChord(e)->notes().begin(),
                                     toChord(e)->notes().end());
                            }
                      }
                spellNotelist(notes);
                }
          }

    void Score::spell(int startStaff, int endStaff, Segment* startSegment, Segment* endSegment)
          {
          for (int i = startStaff; i < endStaff; ++i) {
                std::vector<Note*> notes;
                for (Segment* s = startSegment; s && s != endSegment; s = s->next()) {
                      int strack = i * VOICES;
                      int etrack = strack + VOICES;
                      for (int track = strack; track < etrack; ++track) {
                            Element* e = s->element(track);
                            if (e && e->type() == ElementType::CHORD)
                                  notes.insert(notes.end(),
                                     toChord(e)->notes().begin(),
                                     toChord(e)->notes().end());
                            }
                      }
                spellNotelist(notes);
                }
          }

    //---------------------------------------------------------
    //   prevNote
    //---------------------------------------------------------

    Note* prevNote(Note* n)
          {
          Chord* chord = n->chord();
          Segment* seg = chord->segment();
          const std::vector<Note*> nl = chord->notes();
          auto i = std::find(nl.begin(), nl.end(), n);
          if (i != nl.begin())
                return *(i-1);
          int staff      = n->staffIdx();
          int startTrack = staff * VOICES + n->voice() - 1;
          int endTrack   = 0;
          while (seg) {
                if (seg->segmentType() == SegmentType::ChordRest) {
                      for (int track = startTrack; track >= endTrack; --track) {
                            Element* e = seg->element(track);
                            if (e && e->type() == ElementType::CHORD)
                                  return toChord(e)->upNote();
                            }
                      }
                seg = seg->prev1();
                startTrack = staff * VOICES + VOICES - 1;
                }
          return n;
          }

    //---------------------------------------------------------
    //   nextNote
    //---------------------------------------------------------

    static Note* nextNote(Note* n)
          {
          Chord* chord = n->chord();
          const std::vector<Note*> nl = chord->notes();
          auto i = std::find(nl.begin(), nl.end(), n);
          if (i != nl.end()) {
                ++i;
                if (i != nl.end())
                      return *i;
                }
          Segment* seg   = chord->segment();
          int staff      = n->staffIdx();
          int startTrack = staff * VOICES + n->voice() + 1;
          int endTrack   = staff * VOICES + VOICES;
          while (seg) {
                if (seg->segmentType() == SegmentType::ChordRest) {
                      for (int track = startTrack; track < endTrack; ++track) {
                            Element* e = seg->element(track);
                            if (e && e->type() == ElementType::CHORD) {
                                  return ((Chord*)e)->downNote();
                                  }
                            }
                      }
                seg = seg->next1();
                startTrack = staff * VOICES;
                }
          return n;
          }

    //---------------------------------------------------------
    //   spell
    //---------------------------------------------------------

    void Score::spell(Note* note)
          {
          std::vector<Note*> notes;

          notes.push_back(note);
          Note* nn = nextNote(note);
          notes.push_back(nn);
          nn = nextNote(nn);
          notes.push_back(nn);
          nn = nextNote(nn);
          notes.push_back(nn);

          nn = prevNote(note);
          notes.insert(notes.begin(), nn);
          nn = prevNote(nn);
          notes.insert(notes.begin(), nn);
          nn = prevNote(nn);
          notes.insert(notes.begin(), nn);

          int opt = Ms::computeWindow(notes, 0, 7);
          note->setTpc(Ms::tpc(3, note->pitch(), opt));
          }

    //---------------------------------------------------------
    //   appendPart
    //---------------------------------------------------------

    void Score::appendPart(Part* p)
          {
          _parts.append(p);
          }

    //---------------------------------------------------------
    //   searchPage
    //    p is in canvas coordinates
    //---------------------------------------------------------

    Page* Score::searchPage(const QPointF& p) const
          {
          for (Page* page : pages()) {
                if (page->bbox().translated(page->pos()).contains(p))
                      return page;
                }
          return 0;
          }

    //---------------------------------------------------------
    //   searchSystem
    //    return list of systems as there may be more than
    //    one system in a row
    //    p is in canvas coordinates
    //---------------------------------------------------------

    QList<System*> Score::searchSystem(const QPointF& pos) const
          {
          QList<System*> systems;
          Page* page = searchPage(pos);
          if (page == 0)
                return systems;
          qreal y = pos.y() - page->pos().y();  // transform to page relative
          const QList<System*>* sl = &page->systems();
          qreal y2;
          int n = sl->size();
          for (int i = 0; i < n; ++i) {
                System* s = sl->at(i);
                System* ns = 0;               // next system row
                int ii = i + 1;
                for (; ii < n; ++ii) {
                      ns = sl->at(ii);
                      if (ns->y() != s->y())
                            break;
                      }
                if ((ii == n) || (ns == 0))
                      y2 = page->height();
                else {
                      qreal sy2 = s->y() + s->bbox().height();
                      y2         = sy2 + (ns->y() - sy2) * .5;
                      }
                if (y < y2) {
                      systems.append(s);
                      for (int ii = i+1; ii < n; ++ii) {
                            if (sl->at(ii)->y() != s->y())
                                  break;
                            systems.append(sl->at(ii));
                            }
                      return systems;
                      }
                }
          return systems;
          }

    //---------------------------------------------------------
    //   searchMeasure
    //    p is in canvas coordinates
    //---------------------------------------------------------

    Measure* Score::searchMeasure(const QPointF& p) const
          {
          QList<System*> systems = searchSystem(p);
          for (System* system : systems) {
                qreal x = p.x() - system->canvasPos().x();
                for (MeasureBase* mb : system->measures()) {
                      if (mb->isMeasure() && (x < (mb->x() + mb->bbox().width())))
                            return toMeasure(mb);
                      }
                }
          return 0;
          }

    //---------------------------------------------------------
    //    getNextValidInputSegment
    //    - s is of type SegmentType::ChordRest
    //---------------------------------------------------------

    static Segment* getNextValidInputSegment(Segment* s, int track, int voice)
          {
          if (s == 0)
                return 0;
          Q_ASSERT(s->segmentType() == SegmentType::ChordRest);
          // Segment* s1 = s;
          ChordRest* cr1;
          for (Segment* s1 = s; s1; s1 = s1->prev(SegmentType::ChordRest)) {
                cr1 = toChordRest(s1->element(track + voice));
                if (cr1)
                      break;
                }
          int nextTick = (cr1 == 0) ? s->measure()->tick() : cr1->tick() + cr1->actualTicks();

          static const SegmentType st { SegmentType::ChordRest };
          while (s) {
                if (s->element(track + voice))
                      break;
                if (voice && s->tick() == nextTick)
                      return s;
    #if 0
                int v;
                for (v = 0; v < VOICES; ++v) {
                      if (s->element(track + v))
                            break;
                      }
                if ((v != VOICES) && voice) {
                      int ntick;
                      bool skipChord = false;
                      bool ns        = false;
                      for (Segment* s1 = s->measure()->first(st); s1; s1 = s1->next(st)) {
                            ChordRest* cr = toChordRest(s1->element(track + voice));
                            if (cr) {
                                  if (ns)
                                        return s1;
                                  ntick = s1->tick() + cr->actualTicks();
                                  skipChord = true;
                                  }
                            if (s1 == s)
                                  ns = true;
                            if (skipChord) {
                                  if (s->tick() >= ntick)
                                        skipChord = false;
                                  }
                            if (!skipChord && ns)
                                  return s1;
                            }
                      if (!skipChord)
                            return s;
                      }
    #endif
                s = s->next(st);
                }
          return s;
          }

    //---------------------------------------------------------
    //   getPosition
    //    return true if valid position found
    //---------------------------------------------------------

    bool Score::getPosition(Position* pos, const QPointF& p, int voice) const
          {
          Measure* measure = searchMeasure(p);
          if (measure == 0)
                return false;

          pos->fret = FRET_NONE;
          //
          //    search staff
          //
          pos->staffIdx      = 0;
          SysStaff* sstaff   = 0;
          System* system     = measure->system();
          qreal y           = p.y() - system->pagePos().y();
          for (; pos->staffIdx < nstaves(); ++pos->staffIdx) {
                Staff* st = staff(pos->staffIdx);
                if (st->invisible() || !st->part()->show())
                      continue;
                qreal sy2;
                SysStaff* ss = system->staff(pos->staffIdx);
                SysStaff* nstaff = 0;

                // find next visible staff
                for (int i = pos->staffIdx + 1; i < nstaves(); ++i) {
                      Staff* st = staff(i);
                      if (st->invisible() || !st->part()->show())
                            continue;
                      nstaff = system->staff(i);
                      break;
                      }

                if (nstaff) {
                      qreal s1y2 = ss->bbox().bottom();
                      sy2        = system->page()->canvasPos().y() + s1y2 + (nstaff->bbox().y() - s1y2) * .5;
                      }
                else
                      sy2 = system->page()->canvasPos().y() + system->page()->height() - system->pagePos().y();   // system->height();
                if (y < sy2) {
                      sstaff = ss;
                      break;
                      }
                }
          if (sstaff == 0)
                return false;

          //
          //    search segment
          //
          QPointF pppp(p - measure->canvasPos());
          qreal x         = pppp.x();
          Segment* segment = 0;
          pos->segment     = 0;

          // int track = pos->staffIdx * VOICES + voice;
          int track = pos->staffIdx * VOICES;

          for (segment = measure->first(SegmentType::ChordRest); segment;) {
                segment = getNextValidInputSegment(segment, track, voice);
                if (segment == 0)
                      break;
                Segment* ns = getNextValidInputSegment(segment->next(SegmentType::ChordRest), track, voice);

                qreal x1 = segment->x();
                qreal x2;
                qreal d;
                if (ns) {
                      x2    = ns->x();
                      d     = x2 - x1;
                      }
                else {
                      x2    = measure->bbox().width();
                      d     = (x2 - x1) * 2.0;
                      x     = x1;
                      pos->segment = segment;
                      break;
                      }

                if (x < (x1 + d * .5)) {
                      x = x1;
                      pos->segment = segment;
                      break;
                      }
                segment = ns;
                }
          if (segment == 0)
                return false;
          //
          // TODO: restrict to reasonable values (pitch 0-127)
          //
          Staff* s    = staff(pos->staffIdx);
          qreal mag   = s->mag(segment->tick());
          int tick    = segment->tick();
          // in TABs, step from one string to another; in other staves, step on and between lines
          qreal lineDist = s->staffType(tick)->lineDistance().val() * (s->isTabStaff(measure->tick()) ? 1 : .5) * mag * spatium();

          pos->line  = lrint((pppp.y() - sstaff->bbox().y()) / lineDist);
          if (s->isTabStaff(measure->tick())) {
                if (pos->line < -1 || pos->line > s->lines(tick)+1)
                      return false;
                if (pos->line < 0)
                      pos->line = 0;
                else if (pos->line >= s->lines(tick))
                      pos->line = s->lines(tick) - 1;
                }
          else {
                int minLine   = absStep(0);
                ClefType clef = s->clef(pos->segment->tick());
                minLine       = relStep(minLine, clef);
                int maxLine   = absStep(127);
                maxLine       = relStep(maxLine, clef);

                if (pos->line > minLine || pos->line < maxLine)
                      return false;
                }

          y         = sstaff->y() + pos->line * lineDist;
          pos->pos  = QPointF(x, y) + measure->canvasPos();
          return true;
          }

    //---------------------------------------------------------
    //   checkHasMeasures
    //---------------------------------------------------------

    bool Score::checkHasMeasures() const
          {
          Page* page = pages().front();
          const QList<System*>* sl = &page->systems();
          if (sl == 0 || sl->empty() || sl->front()->measures().empty()) {
                qDebug("first create measure, then repeat operation");
                return false;
                }
          return true;
          }

    #if 0
    //---------------------------------------------------------
    //   moveBracket
    //    columns are counted from right to left
    //---------------------------------------------------------

    void Score::moveBracket(int staffIdx, int srcCol, int dstCol)
          {
          for (System* system : systems())
                system->moveBracket(staffIdx, srcCol, dstCol);
          }
    #endif

    //---------------------------------------------------------
    //   spatiumHasChanged
    //---------------------------------------------------------

    static void spatiumHasChanged(void* data, Element* e)
          {
          qreal* val = (qreal*)data;
          e->spatiumChanged(val[0], val[1]);
          }

    //---------------------------------------------------------
    //   spatiumChanged
    //---------------------------------------------------------

    void Score::spatiumChanged(qreal oldValue, qreal newValue)
          {
          qreal data[2];
          data[0] = oldValue;
          data[1] = newValue;
          scanElements(data, spatiumHasChanged, true);
          for (Staff* staff : _staves)
                staff->spatiumChanged(oldValue, newValue);
          _noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, newValue / SPATIUM20);
          }

    //---------------------------------------------------------
    //   updateStyle
    //---------------------------------------------------------

    static void updateStyle(void*, Element* e)
          {
          e->styleChanged();
          }

    //---------------------------------------------------------
    //   styleChanged
    //    must be called after every style change
    //---------------------------------------------------------

    void Score::styleChanged()
          {
          scanElements(0, updateStyle);
          setLayoutAll();
          }

    //---------------------------------------------------------
    //   getCreateMeasure
    //    - return Measure for tick
    //    - create new Measure(s) if there is no measure for
    //      this tick
    //---------------------------------------------------------

    Measure* Score::getCreateMeasure(int tick)
          {
          Measure* last = lastMeasure();
          if (last == 0 || ((last->tick() + last->ticks()) <= tick)) {
                int lastTick  = last ? (last->tick()+last->ticks()) : 0;
                while (tick >= lastTick) {
                      Measure* m = new Measure(this);
                      Fraction ts = sigmap()->timesig(lastTick).timesig();
    // qDebug("getCreateMeasure %d  %d/%d", tick, ts.numerator(), ts.denominator());
                      m->setTick(lastTick);
                      m->setTimesig(ts);
                      m->setLen(ts);
                      measures()->add(static_cast<MeasureBase*>(m));
                      lastTick += ts.ticks();
                      }
                }
          return tick2measure(tick);
          }

    //---------------------------------------------------------
    //   addElement
    //---------------------------------------------------------

    /**
     Add \a element to its parent.

     Several elements (clef, keysig, timesig) need special handling, as they may cause
     changes throughout the score.
    */

    void Score::addElement(Element* element)
          {
          Element* parent = element->parent();
          element->triggerLayout();

    //      qDebug("Score(%p) Element(%p)(%s) parent %p(%s)",
    //         this, element, element->name(), parent, parent ? parent->name() : "");

          ElementType et = element->type();
          if (et == ElementType::MEASURE
             || (et == ElementType::HBOX && element->parent()->type() != ElementType::VBOX)
             || et == ElementType::VBOX
             || et == ElementType::TBOX
             || et == ElementType::FBOX
             ) {
                measures()->add(static_cast<MeasureBase*>(element));
                return;
                }

          if (parent)
                parent->add(element);

          switch (et) {
                case ElementType::BEAM:
                      {
                      Beam* b = toBeam(element);
                      int n = b->elements().size();
                      for (int i = 0; i < n; ++i)
                            b->elements().at(i)->setBeam(b);
                      }
                      break;

                case ElementType::SLUR:
                      addLayoutFlags(LayoutFlag::PLAY_EVENTS);
                      // fall through

                case ElementType::VOLTA:
                case ElementType::TRILL:
                case ElementType::PEDAL:
                case ElementType::TEXTLINE:
                case ElementType::HAIRPIN:
                      {
                      Spanner* spanner = static_cast<Spanner*>(element);
                      if (et == ElementType::TEXTLINE && spanner->anchor() == Spanner::Anchor::NOTE)
                            break;
                      addSpanner(spanner);
                      for (SpannerSegment* ss : spanner->spannerSegments()) {
                            if (ss->system())
                                  ss->system()->add(ss);
                            }
                      }
                      break;

                case ElementType::OTTAVA:
                      {
                      Ottava* o = toOttava(element);
                      addSpanner(o);
                      foreach(SpannerSegment* ss, o->spannerSegments()) {
                            if (ss->system())
                                  ss->system()->add(ss);
                            }
                      cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
                      o->staff()->updateOttava();
                      _playlistDirty = true;
                      }
                      break;

                case ElementType::DYNAMIC:
                      cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
                      _playlistDirty = true;
                      break;

                case ElementType::TEMPO_TEXT:
                      {
                      TempoText* tt = toTempoText(element);
                      if (tt->isRelative())
                            tt->updateRelative();
                      setTempo(tt->segment(), tt->tempo());
                      }
                      break;

                case ElementType::INSTRUMENT_CHANGE: {
                      InstrumentChange* ic = toInstrumentChange(element);
                      int tickStart = ic->segment()->tick();
                      auto i = ic->part()->instruments()->upper_bound(tickStart);
                      int tickEnd;
                      if (i == ic->part()->instruments()->end())
                            tickEnd = -1;
                      else
                            tickEnd = i->first;
                      Interval oldV = ic->part()->instrument(tickStart)->transpose();
                      ic->part()->setInstrument(ic->instrument(), tickStart);
                      transpositionChanged(ic->part(), oldV, tickStart, tickEnd);
                      masterScore()->rebuildMidiMapping();
                      cmdState()._instrumentsChanged = true;
                      }
                      break;

                case ElementType::CHORD:
                      setPlaylistDirty();
                      // create playlist does not work here bc. tremolos may not be complete
                      // createPlayEvents(toChord(element));
                      break;

                case ElementType::NOTE:
                case ElementType::TREMOLO:
                case ElementType::ARTICULATION:
                case ElementType::ARPEGGIO:
                      {
                      Element* cr = parent;
                      if (cr->isChord())
                            createPlayEvents(toChord(cr));
                      }
                      break;

                default:
                      break;
                }
          setLayout(element->tick());
          }

    //---------------------------------------------------------
    //   removeElement
    ///   Remove \a element from its parent.
    ///   Several elements (clef, keysig, timesig) need special handling, as they may cause
    ///   changes throughout the score.
    //---------------------------------------------------------

    void Score::removeElement(Element* element)
          {
          Element* parent = element->parent();
          setLayout(element->tick());

    //      qDebug("Score(%p) Element(%p)(%s) parent %p(%s)",
    //         this, element, element->name(), parent, parent ? parent->name() : "");

          // special for MEASURE, HBOX, VBOX
          // their parent is not static

          ElementType et = element->type();

          if (et == ElementType::MEASURE
             || (et == ElementType::HBOX && !parent->isVBox())
             || et == ElementType::VBOX
             || et == ElementType::TBOX
             || et == ElementType::FBOX
                ) {
                MeasureBase* mb = toMeasureBase(element);
                measures()->remove(mb);
                System* system = mb->system();
                Page* page = system->page();
                if (element->isVBox() && system->measures().size() == 1) {
                      auto i = std::find(page->systems().begin(), page->systems().end(), system);
                      page->systems().erase(i);
                      }
    //            setLayout(mb->tick());
                return;
                }

          if (et == ElementType::BEAM) {          // beam parent does not survive layout
                element->setParent(0);
                parent = 0;
                }

          if (parent)
                parent->remove(element);

          switch (et) {
                case ElementType::BEAM:
                      for (ChordRest* cr : toBeam(element)->elements())
                            cr->setBeam(0);
                      break;

                case ElementType::SLUR:
                      addLayoutFlags(LayoutFlag::PLAY_EVENTS);
                      // fall through

                case ElementType::VOLTA:
                case ElementType::TRILL:
                case ElementType::PEDAL:
                case ElementType::TEXTLINE:
                case ElementType::HAIRPIN:
                      {
                      Spanner* spanner = static_cast<Spanner*>(element);
                      if (et == ElementType::TEXTLINE && spanner->anchor() == Spanner::Anchor::NOTE)
                            break;
                      removeSpanner(spanner);
    //TODO: system is not valid anymore after removing measure
    //                  for (SpannerSegment* ss : spanner->spannerSegments()) {
    //                        if (ss->system())
    //                              ss->system()->remove(ss);
    //                        }
                      }
                      break;

                case ElementType::OTTAVA:
                      {
                      Ottava* o = toOttava(element);
                      removeSpanner(o);
                      foreach(SpannerSegment* ss, o->spannerSegments()) {
                            if (ss->system())
                                  ss->system()->remove(ss);
                            }
                      o->staff()->updateOttava();
                      cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
                      _playlistDirty = true;
                      }
                      break;

                case ElementType::DYNAMIC:
                      cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
                      _playlistDirty = true;
                      break;

                case ElementType::CHORD:
                case ElementType::REST:
                      {
                      ChordRest* cr = toChordRest(element);
                      if (cr->beam())
                            cr->beam()->remove(cr);
                      for (Lyrics* lyr : cr->lyrics())
                            lyr->removeFromScore();
                      // TODO: check for tuplet?
                      }
                      break;
                case ElementType::TEMPO_TEXT:
                      {
                      TempoText* tt = toTempoText(element);
                      int tick = tt->segment()->tick();
                      tempomap()->delTempo(tick);
                      }
                      break;
                case ElementType::INSTRUMENT_CHANGE: {
                      InstrumentChange* ic = toInstrumentChange(element);
                      int tickStart = ic->segment()->tick();
                      auto i = ic->part()->instruments()->upper_bound(tickStart);
                      int tickEnd;
                      if (i == ic->part()->instruments()->end())
                            tickEnd = -1;
                      else
                            tickEnd = i->first;
                      Interval oldV = ic->part()->instrument(tickStart)->transpose();
                      ic->part()->removeInstrument(tickStart);
                      transpositionChanged(ic->part(), oldV, tickStart, tickEnd);
                      masterScore()->rebuildMidiMapping();
                      cmdState()._instrumentsChanged = true;
                      }
                      break;

                case ElementType::TREMOLO:
                case ElementType::ARTICULATION:
                case ElementType::ARPEGGIO:
                      {
                      Element* cr = element->parent();
                      if (cr->isChord())
                            createPlayEvents(toChord(cr));
                      }
                      break;

                default:
                      break;
                }
          setLayout(element->tick());
          }

    //---------------------------------------------------------
    //   firstMeasure
    //---------------------------------------------------------

    Measure* Score::firstMeasure() const
          {
          MeasureBase* mb = _measures.first();
          while (mb && mb->type() != ElementType::MEASURE)
                mb = mb->next();

          return toMeasure(mb);
          }

    //---------------------------------------------------------
    //   firstMeasureMM
    //---------------------------------------------------------

    Measure* Score::firstMeasureMM() const
          {
          Measure* m = firstMeasure();
          if (m && styleB(StyleIdx::createMultiMeasureRests) && m->hasMMRest())
                return m->mmRest();
          return m;
          }

    //---------------------------------------------------------
    //   firstMM
    //---------------------------------------------------------

    MeasureBase* Score::firstMM() const
          {
          MeasureBase* m = _measures.first();
          if (m
             && m->type() == ElementType::MEASURE
             && styleB(StyleIdx::createMultiMeasureRests)
             && toMeasure(m)->hasMMRest()) {
                return toMeasure(m)->mmRest();
                }
          return m;
          }

    //---------------------------------------------------------
    //   measure
    //---------------------------------------------------------

    MeasureBase* Score::measure(int idx) const
          {
          MeasureBase* mb = _measures.first();
          for (int i = 0; i < idx; ++i) {
                mb = mb->next();
                if (mb == 0)
                      return 0;
                }
          return mb;
          }

    //---------------------------------------------------------
    //   lastMeasure
    //---------------------------------------------------------

    Measure* Score::lastMeasure() const
          {
          MeasureBase* mb = _measures.last();
          while (mb && mb->type() != ElementType::MEASURE)
                mb = mb->prev();
          return toMeasure(mb);
          }

    //---------------------------------------------------------
    //   lastMeasureMM
    //---------------------------------------------------------

    Measure* Score::lastMeasureMM() const
          {
          Measure* m = lastMeasure();
          if (m && styleB(StyleIdx::createMultiMeasureRests)) {
                Measure* m1 = const_cast<Measure*>(toMeasure(m->mmRest1()));
                if (m1)
                      return m1;
                }
          return m;
          }

    //---------------------------------------------------------
    //   endTick
    //---------------------------------------------------------

    int Score::endTick() const
          {
          Measure* m = lastMeasure();
          return m ? m->endTick() : 0;
          }

    //---------------------------------------------------------
    //   firstSegment
    //---------------------------------------------------------

    Segment* Score::firstSegment(SegmentType segType) const
          {
          Segment* seg;
          Measure* m = firstMeasure();
          if (!m)
                seg = 0;
          else {
                seg = m->first();
                if (seg && !(seg->segmentType() & segType))
                      seg = seg->next1(segType);
                }

    #ifdef SCRIPT_INTERFACE
          // if called from QML/JS, tell QML engine not to garbage collect this object
    //      if (seg)
    //            QQmlEngine::setObjectOwnership(seg, QQmlEngine::CppOwnership);
    #endif
          return seg;
          }

    //---------------------------------------------------------
    //   firstSegmentMM
    //---------------------------------------------------------

    Segment* Score::firstSegmentMM(SegmentType segType) const
          {
          Measure* m = firstMeasureMM();
          return m ? m->first(segType) : 0;
          }

    //---------------------------------------------------------
    //   lastSegment
    //---------------------------------------------------------

    Segment* Score::lastSegment() const
          {
          Measure* m = lastMeasure();
          return m ? m->last() : 0;
          }

    //---------------------------------------------------------
    //   utick2utime
    //---------------------------------------------------------

    qreal Score::utick2utime(int tick) const
          {
          return repeatList()->utick2utime(tick);
          }

    //---------------------------------------------------------
    //   utime2utick
    //---------------------------------------------------------

    int Score::utime2utick(qreal utime) const
          {
          return repeatList()->utime2utick(utime);
          }

    //---------------------------------------------------------
    //   inputPos
    //---------------------------------------------------------

    int Score::inputPos() const
          {
          return _is.tick();
          }

    //---------------------------------------------------------
    //   scanElements
    //    scan all elements
    //---------------------------------------------------------

    void Score::scanElements(void* data, void (*func)(void*, Element*), bool all)
          {
          for (MeasureBase* mb = first(); mb; mb = mb->next()) {
                mb->scanElements(data, func, all);
                if (mb->type() == ElementType::MEASURE) {
                      Measure* m = toMeasure(mb);
                      Measure* mmr = m->mmRest();
                      if (mmr)
                            mmr->scanElements(data, func, all);
                      }
                }
          for (Page* page : pages()) {
                for (System* s :page->systems())
                      s->scanElements(data, func, all);
                func(data, page);
                }
          }

    //---------------------------------------------------------
    //   scanElementsInRange
    //---------------------------------------------------------

    void Score::scanElementsInRange(void* data, void (*func)(void*, Element*), bool all)
          {
          Segment* startSeg = _selection.startSegment();
          for (Segment* s = startSeg; s && s !=_selection.endSegment(); s = s->next1()) {
                s->scanElements(data, func, all);
                Measure* m = s->measure();
                if (m && s == m->first()) {
                      Measure* mmr = m->mmRest();
                      if (mmr)
                            mmr->scanElements(data, func, all);
                      }
                }
          for (Element* e : _selection.elements()) {
                if (e->isSpanner()) {
                      Spanner* spanner = static_cast<Spanner*>(e);
                      for (SpannerSegment* ss : spanner->spannerSegments()) {
                            ss->scanElements(data, func, all);
                            }
                      }
                }
          }

    //---------------------------------------------------------
    //   setSelection
    //---------------------------------------------------------

    void Score::setSelection(const Selection& s)
          {
          deselectAll();
          _selection = s;

          foreach(Element* e, _selection.elements())
                e->setSelected(true);
          }

    //---------------------------------------------------------
    //   getText
    //---------------------------------------------------------

    Text* Score::getText(SubStyle subStyle)
          {
          MeasureBase* m = first();
          if (m && m->type() == ElementType::VBOX) {
                for (Element* e : m->el()) {
                      if (e->type() == ElementType::TEXT && toText(e)->subStyle() == subStyle)
                            return toText(e);
                      }
                }
          return 0;
          }

    //---------------------------------------------------------
    //   metaTag
    //---------------------------------------------------------

    QString Score::metaTag(const QString& s) const
          {
          if (_metaTags.contains(s))
                return _metaTags.value(s);
          return _masterScore->_metaTags.value(s);
          }

    //---------------------------------------------------------
    //   setMetaTag
    //---------------------------------------------------------

    void Score::setMetaTag(const QString& tag, const QString& val)
          {
          _metaTags.insert(tag, val);
          }

    //---------------------------------------------------------
    //   addExcerpt
    //---------------------------------------------------------

    void MasterScore::addExcerpt(Excerpt* ex)
          {
          Score* score = ex->partScore();

          for (Staff* s : score->staves()) {
                LinkedStaves* ls = s->linkedStaves();
                if (ls == 0)
                      continue;
                for (Staff* ps : ls->staves()) {
                      if (ps->score() == this) {
                            ex->parts().append(ps->part());
                            break;
                            }
                      }
                }
          if (ex->tracks().isEmpty()) {                         // SHOULDN'T HAPPEN, protected in the UI
                QMultiMap<int, int> tracks;
                for (Staff* s : score->staves()) {
                      LinkedStaves* ls = s->linkedStaves();
                      if (ls == 0)
                            continue;
                      for (Staff* ps : ls->staves()) {
                            if (ps->primaryStaff()) {
                                  for (int i = 0; i < VOICES; i++)
                                        tracks.insert(ps->idx() * VOICES + i % VOICES, s->idx() * VOICES + i % VOICES);
                                  break;
                                  }
                            }
                      }
                ex->setTracks(tracks);
                }
          excerpts().append(ex);
          setExcerptsChanged(true);
          }

    //---------------------------------------------------------
    //   removeExcerpt
    //---------------------------------------------------------

    void MasterScore::removeExcerpt(Excerpt* ex)
          {
          if (excerpts().removeOne(ex)) {
                setExcerptsChanged(true);
                // delete ex;
                }
          else
                qDebug("removeExcerpt:: ex not found");
          }

    //---------------------------------------------------------
    //   clone
    //---------------------------------------------------------

    MasterScore* MasterScore::clone()
          {
          QBuffer buffer;
          buffer.open(QIODevice::WriteOnly);
          XmlWriter xml(this, &buffer);
          xml.header();

          xml.stag("museScore version=\"" MSC_VERSION "\"");
          write(xml, false);
          xml.etag();

          buffer.close();

          XmlReader r(this, buffer.buffer());
          MasterScore* score = new MasterScore(style());
          score->read1(r, true);

          score->addLayoutFlags(LayoutFlag::FIX_PITCH_VELO);
          score->doLayout();
          score->scanElements(0, elementAdjustReadPos);  //??
          return score;
          }

    //---------------------------------------------------------
    //   setSynthesizerState
    //---------------------------------------------------------

    void Score::setSynthesizerState(const SynthesizerState& s)
          {
          // TODO: make undoable
          _synthesizerState = s;
          }

    //---------------------------------------------------------
    //   removeAudio
    //---------------------------------------------------------

    void Score::removeAudio()
          {
          delete _audio;
          _audio = 0;
          }

    //---------------------------------------------------------
    //   appendScore
    //---------------------------------------------------------

    bool Score::appendScore(Score* score, bool addPageBreak, bool addSectionBreak)
          {
          if (parts().size() < score->parts().size() || staves().size() < score->staves().size()) {
                qDebug("Score to append has %d parts and %d staves, but this score only has %d parts and %d staves.", score->parts().size(), score->staves().size(), parts().size(), staves().size());
                return false;
                }

          if (!last()) {
                qDebug("This score doesn't have any MeasureBase objects.");
                return false;
                }

          TieMap tieMap;
          int tickOfAppend = last()->endTick();

          // apply Page/Section Breaks if desired
          if (addPageBreak) {
                if (!last()->pageBreak()) {
                      last()->undoSetBreak(false, LayoutBreak::Type::LINE); // remove line break if exists
                      last()->undoSetBreak(true, LayoutBreak::Type::PAGE);  // apply page break
                      }
                }
          else if (!last()->lineBreak() && !last()->pageBreak())
                last()->undoSetBreak(true, LayoutBreak::Type::LINE);
          if (addSectionBreak && !last()->sectionBreak())
                last()->undoSetBreak(true, LayoutBreak::Type::SECTION);

          // match concert pitch states
          if (styleB(StyleIdx::concertPitch) != score->styleB(StyleIdx::concertPitch))
                score->cmdConcertPitchChanged(styleB(StyleIdx::concertPitch), true);

          // convert any "generated" initial clefs into real "non-generated" clefs if clef type changes
          Measure* fm = score->firstMeasure();
          if (fm) {
                Segment* seg = fm->findSegmentR(SegmentType::HeaderClef, 0);
                if (seg) {
                      for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
                            int track    = staffIdx * VOICES;
                            Staff* staff = score->staff(staffIdx);
                            Clef* clef   = toClef(seg->element(track));

                            // if the first clef of score to append is generated and
                            // if the first clef of score to append is of different type than clef at final tick of first score
                            if (clef && clef->generated() && clef->clefType() != this->staff(staffIdx)->clef(tickOfAppend)) {
                                  // then convert that generated clef into a real non-generated clef
                                  // so that its different type will be copied to joined score
                                  score->undoChangeClef(staff, seg, clef->clefType());
                                  }
                            }
                      }
                }

          // clone the measures
          MeasureBaseList* ml = &score->_measures;
          for (MeasureBase* mb = ml->first(); mb; mb = mb->next()) {
                MeasureBase* nmb;
                if (mb->type() == ElementType::MEASURE)
                      nmb = toMeasure(mb)->cloneMeasure(this, &tieMap);
                else
                      nmb = mb->clone();
                nmb->setNext(0);
                nmb->setPrev(0);
                nmb->setScore(this);
                _measures.add(nmb);
                }
          fixTicks();
          Measure* firstAppendedMeasure = tick2measure(tickOfAppend);

          // if the appended score has less staves,
          // make sure the measures have full measure rest
          for (Measure* m = firstAppendedMeasure; m; m = m->nextMeasure()) {
                for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
                      Fraction f;
                      for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
                            for (int v = 0; v < VOICES; ++v) {
                                  ChordRest* cr = toChordRest(s->element(staffIdx * VOICES + v));
                                  if (cr == 0)
                                        continue;
                                  f += cr->actualFraction();
                                  }
                            }
                      if (f.isZero())
                            addRest(m->tick(), staffIdx*VOICES, TDuration(TDuration::DurationType::V_MEASURE), 0);
                      }
                }

          // adjust key signatures
          if (firstAppendedMeasure) {
                Segment* seg = firstAppendedMeasure->getSegment(SegmentType::KeySig, tickOfAppend);
                for (Staff* st : score->staves()) {
                      int staffIdx = st->idx();
                      Staff* joinedStaff = staff(staffIdx);
                      // special case for initial "C" key signature - these have no explicit element
                      if (!seg->element(staffIdx * VOICES)) {
                            // no need to create new initial "C" key sig
                            // if staff already ends in that key
                            if (joinedStaff->key(tickOfAppend - 1) == Key::C)
                                  continue;
                            Key key = Key::C;
                            KeySig* ks = new KeySig(this);
                            ks->setTrack(staffIdx * VOICES);
                            ks->setKey(key);
                            ks->setParent(seg);
                            addElement(ks);
                            }
                      // other key signatures (initial other than "C", non-initial)
                      for (auto k : *(st->keyList())) {
                            int tick = k.first;
                            KeySigEvent key = k.second;
                            joinedStaff->setKey(tick + tickOfAppend, key);
                            }
                      }
                }

          // clone the spanners
          for (auto sp : score->spanner()) {
                Spanner* spanner = sp.second;
                Spanner* ns = static_cast<Spanner*>(spanner->clone());
                ns->setScore(this);
                ns->setParent(0);
                ns->setTick(spanner->tick() + tickOfAppend);
                ns->setTick2(spanner->tick2() + tickOfAppend);
                if (ns->type() == ElementType::SLUR) {
                      // set start/end element for slur
                      ns->setStartElement(0);
                      ns->setEndElement(0);
                      Measure* sm = tick2measure(ns->tick());
                      if (sm)
                            ns->setStartElement(sm->findChordRest(ns->tick(), ns->track()));
                      Measure * em = tick2measure(ns->tick2());
                      if (em)
                            ns->setEndElement(em->findChordRest(ns->tick2(), ns->track2()));
                      if (!ns->startElement())
                            qDebug("clone Slur: no start element");
                      if (!ns->endElement())
                            qDebug("clone Slur: no end element");
                      }
                addElement(ns);
                }
          setLayoutAll();
          return true;
          }

    //---------------------------------------------------------
    //   splitStaff
    //---------------------------------------------------------

    void Score::splitStaff(int staffIdx, int splitPoint)
          {
    //      qDebug("split staff %d point %d", staffIdx, splitPoint);

          //
          // create second staff
          //
          Staff* s  = staff(staffIdx);
          Part*  p  = s->part();
          Staff* ns = new Staff(this);
          ns->setPart(p);
          // convert staffIdx from score-relative to part-relative
          int staffIdxPart = staffIdx - p->staff(0)->idx();
          undoInsertStaff(ns, staffIdxPart + 1, false);

          Clef* clef = new Clef(this);
          clef->setClefType(ClefType::F);
          clef->setTrack((staffIdx+1) * VOICES);
          Segment* seg = firstMeasure()->getSegment(SegmentType::HeaderClef, 0);
          clef->setParent(seg);
          undoAddElement(clef);
          clef->layout();

          undoChangeKeySig(ns, 0, s->keySigEvent(0));

          masterScore()->rebuildMidiMapping();
          cmdState()._instrumentsChanged = true;
          doLayout();

          //
          // move notes
          //
          select(0, SelectType::SINGLE, 0);
          int strack = staffIdx * VOICES;
          int dtrack = (staffIdx + 1) * VOICES;

          for (Segment* s = firstSegment(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
                for (int voice = 0; voice < VOICES; ++voice) {
                      Element* e = s->element(strack + voice);
                      if (!(e && e->isChord()))
                            continue;
                      Chord* c = toChord(e);
                      QList<Note*> removeNotes;
                      foreach(Note* note, c->notes()) {
                            if (note->pitch() >= splitPoint)
                                  continue;
                            Chord* chord = toChord(s->element(dtrack + voice));
                            Q_ASSERT(!chord || (chord->isChord()));
                            if (chord == 0) {
                                  chord = new Chord(*c);
                                  qDeleteAll(chord->notes());
                                  chord->notes().clear();
                                  chord->setTrack(dtrack + voice);
                                  undoAddElement(chord);
                                  }
                            Note* nnote = new Note(*note);
                            nnote->setTrack(dtrack + voice);
                            chord->add(nnote);
                            nnote->updateLine();
                            removeNotes.append(note);
                            }
                      c->sortNotes();
                      for (Note* note : removeNotes) {
                            undoRemoveElement(note);
                            Chord* chord = note->chord();
                            if (chord->notes().empty()) {
                                  for (auto sp : spanner()) {
                                        Slur* slur = static_cast<Slur*>(sp.second);
                                        if (slur->type() != ElementType::SLUR)
                                              continue;
                                        if (slur->startCR() == chord) {
                                              slur->undoChangeProperty(P_ID::TRACK, slur->track()+VOICES);
                                              for (ScoreElement* ee : slur->linkList()) {
                                                    Slur* lslur = static_cast<Slur*>(ee);
                                                    lslur->setStartElement(0);
                                                    }
                                              }
                                        if (slur->endCR() == chord) {
                                              slur->undoChangeProperty(P_ID::SPANNER_TRACK2, slur->track2()+VOICES);
                                              for (ScoreElement* ee : slur->linkList()) {
                                                    Slur* lslur = static_cast<Slur*>(ee);
                                                    lslur->setEndElement(0);
                                                    }
                                              }
                                        }
                                  undoRemoveElement(chord);
                                  }
                            }
                      }
                }
          //
          // make sure that the timeline for dtrack
          // has no gaps
          //
          int ctick  = 0;
          for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
                for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
                      ChordRest* cr = toChordRest(s->element(dtrack));
                      if (cr == 0)
                            continue;
                      int rest = s->tick() - ctick;
                      if (rest) {
                            // insert Rest
                            Segment* s = tick2segment(ctick);
                            if (s == 0) {
                                  qDebug("no segment at %d", ctick);
                                  continue;
                                  }
                            setRest(ctick, dtrack, Fraction::fromTicks(rest), false, 0);
                            }
                      ctick = s->tick() + cr->actualTicks();
                      }
                int rest = m->tick() + m->ticks() - ctick;
                if (rest) {
                      setRest(ctick, dtrack, Fraction::fromTicks(rest), false, 0);
                      ctick += rest;
                      }
                }
          //
          // same for strack
          //
          ctick  = 0;
          for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
                for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
                      ChordRest* cr = toChordRest(s->element(strack));
                      if (cr == 0)
                            continue;
                      int rest = s->tick() - ctick;
                      if (rest) {
                            // insert Rest
                            Segment* s = tick2segment(ctick);
                            if (s == 0) {
                                  qDebug("no segment at %d", ctick);
                                  continue;
                                  }
                            setRest(ctick, strack, Fraction::fromTicks(rest), false, 0);
                            }
                      ctick = s->tick() + cr->actualTicks();
                      }
                int rest = m->tick() + m->ticks() - ctick;
                if (rest) {
                      setRest(ctick, strack, Fraction::fromTicks(rest), false, 0);
                      ctick += rest;
                      }
                }
          }

    //---------------------------------------------------------
    //   cmdRemovePart
    //---------------------------------------------------------

    void Score::cmdRemovePart(Part* part)
          {
          int sidx   = staffIdx(part);
          int n      = part->nstaves();

          for (int i = 0; i < n; ++i)
                cmdRemoveStaff(sidx);

          undoRemovePart(part, sidx);
          }

    //---------------------------------------------------------
    //   insertPart
    //---------------------------------------------------------

    void Score::insertPart(Part* part, int idx)
          {
          int staff = 0;
          for (QList<Part*>::iterator i = _parts.begin(); i != _parts.end(); ++i) {
                if (staff >= idx) {
                      _parts.insert(i, part);
                      return;
                      }
                staff += (*i)->nstaves();
                }
          _parts.push_back(part);
          }

    //---------------------------------------------------------
    //   removePart
    //---------------------------------------------------------

    void Score::removePart(Part* part)
          {
          _parts.removeAt(_parts.indexOf(part));
          }

    //---------------------------------------------------------
    //   insertStaff
    //---------------------------------------------------------

    void Score::insertStaff(Staff* staff, int ridx)
          {
          staff->part()->insertStaff(staff, ridx);

          int idx = staffIdx(staff->part()) + ridx;
          _staves.insert(idx, staff);

          for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) {
                Spanner* s = i->second;
                if (s->systemFlag())
                      continue;
                if (s->staffIdx() >= idx) {
                      int t = s->track() + VOICES;
                      if (t >= ntracks())
                            t = ntracks() - 1;
                      s->setTrack(t);
                      for (SpannerSegment* ss : s->spannerSegments())
                            ss->setTrack(t);
                      if (s->track2() != -1) {
                            t = s->track2() + VOICES;
                            s->setTrack2(t < ntracks() ? t : s->track());
                            }
                      }
                }
    #if 0
          for (Spanner* s : staff->score()->unmanagedSpanners()) {
                if (s->systemFlag())
                      continue;
                if (s->staffIdx() >= idx) {
                      int t = s->track() + VOICES;
                      s->setTrack(t < ntracks() ? t : ntracks() - 1);
                      if (s->track2() != -1) {
                            t = s->track2() + VOICES;
                            s->setTrack2(t < ntracks() ? t : s->track());
                            }
                      }
                }
    #endif
          }

    //---------------------------------------------------------
    //   removeStaff
    //---------------------------------------------------------

    void Score::removeStaff(Staff* staff)
          {
          int idx = staff->idx();
          for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) {
                Spanner* s = i->second;
                if (s->staffIdx() > idx) {
                      int t = s->track() - VOICES;
                      if (t < 0)
                            t = 0;
                      s->setTrack(t);
                      for (SpannerSegment* ss : s->spannerSegments())
                            ss->setTrack(t);
                      if (s->track2() != -1) {
                            t = s->track2() - VOICES;
                            s->setTrack2(t >= 0 ? t : s->track());
                            }
                      }
                }
    #if 0
          for (Spanner* s : staff->score()->unmanagedSpanners()) {
                if (s->staffIdx() > idx) {
                      int t = s->track() - VOICES;
                      s->setTrack(t >= 0 ? t : 0);
                      if (s->track2() != -1) {
                            t = s->track2() - VOICES;
                            s->setTrack2(t >= 0 ? t : s->track());
                            }
                      }
                }
    #endif
          _staves.removeAll(staff);
          staff->part()->removeStaff(staff);
          }

    //---------------------------------------------------------
    //   adjustBracketsDel
    //---------------------------------------------------------

    void Score::adjustBracketsDel(int sidx, int eidx)
          {
          for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                Staff* staff = _staves[staffIdx];
                for (BracketItem* bi : staff->brackets()) {
                      int span = bi->bracketSpan();
                      if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx))
                            continue;
                      if ((sidx >= staffIdx) && (eidx <= (staffIdx + span)))
                            bi->undoChangeProperty(P_ID::BRACKET_SPAN, span - (eidx-sidx));
                      }
    #if 0 // TODO
                int span = staff->barLineSpan();
                if ((sidx >= staffIdx) && (eidx <= (staffIdx + span))) {
                      int newSpan = span - (eidx-sidx) + 1;
                      int lastSpannedStaffIdx = staffIdx + newSpan - 1;
                      int tick = 0;
                      undoChangeBarLineSpan(staff, newSpan, 0, (_staves[lastSpannedStaffIdx]->lines(0)-1)*2);
                      }
    #endif
                }
          }

    //---------------------------------------------------------
    //   adjustBracketsIns
    //---------------------------------------------------------

    void Score::adjustBracketsIns(int sidx, int eidx)
          {
          for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
                Staff* staff = _staves[staffIdx];
                for (BracketItem* bi : staff->brackets()) {
                      int span = bi->bracketSpan();
                      if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx))
                            continue;
                      if ((sidx >= staffIdx) && (eidx < (staffIdx + span)))
                            bi->undoChangeProperty(P_ID::BRACKET_SPAN, span + (eidx-sidx));
                      }
    #if 0 // TODO
                int span = staff->barLineSpan();
                if ((sidx >= staffIdx) && (eidx < (staffIdx + span))) {
                      int idx = staffIdx + span - 1;
                      if (idx >= _staves.size())
                            idx = _staves.size() - 1;
                      undoChangeBarLineSpan(staff, span, 0, (_staves[idx]->lines()-1)*2);
                      }
    #endif
                }
          }

    //---------------------------------------------------------
    //   adjustKeySigs
    //---------------------------------------------------------

    void Score::adjustKeySigs(int sidx, int eidx, KeyList km)
          {
          for (int staffIdx = sidx; staffIdx < eidx; ++staffIdx) {
                Staff* staff = _staves[staffIdx];
                for (auto i = km.begin(); i != km.end(); ++i) {
                      int tick = i->first;
                      Measure* measure = tick2measure(tick);
                      if (!measure)
                            continue;
                      if (staff->isDrumStaff(tick))
                            continue;
                      KeySigEvent oKey = i->second;
                      KeySigEvent nKey = oKey;
                      int diff = -staff->part()->instrument(tick)->transpose().chromatic;
                      if (diff != 0 && !styleB(StyleIdx::concertPitch) && !oKey.custom() && !oKey.isAtonal())
                            nKey.setKey(transposeKey(nKey.key(), diff));
                      staff->setKey(tick, nKey);
                      KeySig* keysig = new KeySig(this);
                      keysig->setTrack(staffIdx * VOICES);
                      keysig->setKeySigEvent(nKey);
                      Segment* s = measure->getSegment(SegmentType::KeySig, tick);
                      s->add(keysig);
                      }
                }
          }

    //---------------------------------------------------------
    //   cmdRemoveStaff
    //---------------------------------------------------------

    void Score::cmdRemoveStaff(int staffIdx)
          {
          Staff* s = staff(staffIdx);
          adjustBracketsDel(staffIdx, staffIdx+1);

          QList<Spanner*> sl;
          for (auto i = _spanner.cbegin(); i != _spanner.cend(); ++i) {
                Spanner* s = i->second;
                if (s->staffIdx() == staffIdx && (staffIdx != 0 || !s->systemFlag()))
                      sl.append(s);
                }
          for (Spanner* s : _unmanagedSpanner) {
                if (s->staffIdx() == staffIdx && (staffIdx != 0 || !s->systemFlag()))
                      sl.append(s);
                }
          for (auto i : sl) {
                i->undoUnlink();
                undo(new RemoveElement(i));
                }

          undoRemoveStaff(s);

          // remove linked staff and measures in linked staves in excerpts
          // unlink staff in the same score
          if (s->linkedStaves()) {
                Staff* sameScoreLinkedStaff = nullptr;
                auto staves = s->linkedStaves()->staves();
                for (Staff* staff : staves) {
                      if (staff == s)
                            continue;
                      Score* lscore = staff->score();
                      if (lscore != this) {
                            lscore->undoRemoveStaff(staff);
                            s->score()->undo(new UnlinkStaff(s, staff));
                            if (staff->part()->nstaves() == 0) {
                                  int pIndex    = lscore->staffIdx(staff->part());
                                  lscore->undoRemovePart(staff->part(), pIndex);
                                  }
                            }
                      else // linked staff in the same score
                           sameScoreLinkedStaff = staff;
                      }
                if (sameScoreLinkedStaff)
                      s->score()->undo(new UnlinkStaff(sameScoreLinkedStaff, s)); // once should be enough
                }
          }

    //---------------------------------------------------------
    //   sortStaves
    //---------------------------------------------------------

    void Score::sortStaves(QList<int>& dst)
          {
          systems().clear();  //??
          _parts.clear();
          Part* curPart = 0;
          QList<Staff*> dl;
          foreach (int idx, dst) {
                Staff* staff = _staves[idx];
                if (staff->part() != curPart) {
                      curPart = staff->part();
                      curPart->staves()->clear();
                      _parts.push_back(curPart);
                      }
                curPart->staves()->push_back(staff);
                dl.push_back(staff);
                }
          _staves = dl;

          for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
                m->sortStaves(dst);
                if (m->hasMMRest())
                      m->mmRest()->sortStaves(dst);
                }
          for (auto i : _spanner.map()) {
                Spanner* sp = i.second;
                if (sp->systemFlag())
                      continue;
                int voice    = sp->voice();
                int staffIdx = sp->staffIdx();
                int idx = dst.indexOf(staffIdx);
                if (idx >=0) {
                      sp->setTrack(idx * VOICES + voice);
                      if (sp->track2() != -1)
                            sp->setTrack2(idx * VOICES +(sp->track2() % VOICES)); // at least keep the voice...
                      }
                }
          }

    //---------------------------------------------------------
    //   cmdConcertPitchChanged
    //---------------------------------------------------------

    void Score::cmdConcertPitchChanged(bool flag, bool /*useDoubleSharpsFlats*/)
          {
          undoChangeStyleVal(StyleIdx::concertPitch, flag);       // change style flag

          for (Staff* staff : _staves) {
                if (staff->staffType(0)->group() == StaffGroup::PERCUSSION)       // TODO
                      continue;
                // if this staff has no transposition, and no instrument changes, we can skip it
                Interval interval = staff->part()->instrument()->transpose();
                if (interval.isZero() && staff->part()->instruments()->size() == 1)
                      continue;
                if (!flag)
                      interval.flip();

                int staffIdx   = staff->idx();
                int startTrack = staffIdx * VOICES;
                int endTrack   = startTrack + VOICES;

                transposeKeys(staffIdx, staffIdx + 1, 0, lastSegment()->tick(), interval, true, !flag);

                for (Segment* segment = firstSegment(SegmentType::ChordRest); segment; segment = segment->next1(SegmentType::ChordRest)) {
                      interval = staff->part()->instrument(segment->tick())->transpose();
                      if (!flag)
                            interval.flip();
                      for (Element* e : segment->annotations()) {
                            if (!e->isHarmony() || (e->track() < startTrack) || (e->track() >= endTrack))
                                  continue;
                            Harmony* h  = toHarmony(e);
                            int rootTpc = transposeTpc(h->rootTpc(), interval, true);
                            int baseTpc = transposeTpc(h->baseTpc(), interval, true);
                            for (ScoreElement* e : h->linkList()) {
                                  // don't transpose all links
                                  // just ones resulting from mmrests
                                  Harmony* he = static_cast<Harmony*>(e);    // toHarmony() does not work as e is an ScoreElement
                                  if (he->staff() == h->staff())
                                        undoTransposeHarmony(he, rootTpc, baseTpc);
                                  }
                            }
                      }
                }
          }

    //---------------------------------------------------------
    //   addAudioTrack
    //---------------------------------------------------------

    void Score::addAudioTrack()
          {
          // TODO
          }

    //---------------------------------------------------------
    //   padToggle
    //---------------------------------------------------------

    void Score::padToggle(Pad n)
          {
          int oldDots = _is.duration().dots();
          switch (n) {
                case Pad::NOTE00:
                      _is.setDuration(TDuration::DurationType::V_LONG);
                      break;
                case Pad::NOTE0:
                      _is.setDuration(TDuration::DurationType::V_BREVE);
                      break;
                case Pad::NOTE1:
                      _is.setDuration(TDuration::DurationType::V_WHOLE);
                      break;
                case Pad::NOTE2:
                      _is.setDuration(TDuration::DurationType::V_HALF);
                      break;
                case Pad::NOTE4:
                      _is.setDuration(TDuration::DurationType::V_QUARTER);
                      break;
                case Pad::NOTE8:
                      _is.setDuration(TDuration::DurationType::V_EIGHTH);
                      break;
                case Pad::NOTE16:
                      _is.setDuration(TDuration::DurationType::V_16TH);
                      break;
                case Pad::NOTE32:
                      _is.setDuration(TDuration::DurationType::V_32ND);
                      break;
                case Pad::NOTE64:
                      _is.setDuration(TDuration::DurationType::V_64TH);
                      break;
                case Pad::NOTE128:
                      _is.setDuration(TDuration::DurationType::V_128TH);
                      break;
                case Pad::REST:
                      _is.setRest(!_is.rest());
                      break;
                case Pad::DOT:
                      if ((_is.duration().dots() == 1) || (_is.duration() == TDuration::DurationType::V_1024TH))
                            _is.setDots(0);
                      else
                            _is.setDots(1);
                      break;
                case Pad::DOTDOT:
                      if ((_is.duration().dots() == 2)
                         || (_is.duration() == TDuration::DurationType::V_512TH)
                         || (_is.duration() == TDuration::DurationType::V_1024TH))
                            _is.setDots(0);
                      else
                            _is.setDots(2);
                      break;
                case Pad::DOT3:
                      if ((_is.duration().dots() == 3)
                         || (_is.duration() == TDuration::DurationType::V_256TH)
                         || (_is.duration() == TDuration::DurationType::V_512TH)
                         || (_is.duration() == TDuration::DurationType::V_1024TH))
                            _is.setDots(0);
                      else
                            _is.setDots(3);
                      break;
                case Pad::DOT4:
                      if ((_is.duration().dots() == 4)
                         || (_is.duration() == TDuration::DurationType::V_128TH)
                         || (_is.duration() == TDuration::DurationType::V_256TH)
                         || (_is.duration() == TDuration::DurationType::V_512TH)
                         || (_is.duration() == TDuration::DurationType::V_1024TH))
                            _is.setDots(0);
                      else
                            _is.setDots(4);
                      break;
                }
          if (n >= Pad::NOTE00 && n <= Pad::NOTE128) {
                _is.setDots(0);
                //
                // if in "note enter" mode, reset
                // rest flag
                //
                if (noteEntryMode()) {
                      if (usingNoteEntryMethod(NoteEntryMethod::RHYTHM)) {
                            switch (oldDots) {
                                  case 1:
                                        padToggle(Pad::DOT);
                                        break;
                                  case 2:
                                        padToggle(Pad::DOTDOT);
                                        break;
                                  }
                            NoteVal nval;
                            if (_is.rest()) {
                                  // Enter a rest
                                  nval = NoteVal();
                                  }
                            else {
                                  // Enter a note on the middle staff line
                                  Staff* s = staff(_is.track() / VOICES);
                                  int tick = _is.tick();
                                  ClefType clef = s->clef(tick);
                                  Key key = s->key(tick);
                                  nval = NoteVal(line2pitch(4, clef, key));
                                  }
                            setNoteRest(_is.segment(), _is.track(), nval, _is.duration().fraction());
                            _is.moveToNextInputPos();
                            }
                      else
                            _is.setRest(false);
                      }
                }

          if (noteEntryMode() || !selection().isSingle())
                return;

          //do not allow to add a dot on a full measure rest
          Element* e = selection().element();
          if (e && e->isRest()) {
                Rest* r = toRest(e);
                if (r->isFullMeasureRest())
                      _is.setDots(0);
                }

          // on measure rest, select the first actual rest
          ChordRest* cr = selection().cr();
          if (cr && cr->isRest() && cr->measure()->isMMRest()) {
                Measure* m = cr->measure()->mmRestFirst();
                if (m)
                      cr = m->findChordRest(0, 0);
                }

          if (!cr)
                return;

          if (cr->isChord() && (toChord(cr)->isGrace())) {
                //
                // handle appoggiatura and acciaccatura
                //
                undoChangeChordRestLen(cr, _is.duration());
                }
          else
                changeCRlen(cr, _is.duration());
          }

    //---------------------------------------------------------
    //   deselect
    //---------------------------------------------------------

    void Score::deselect(Element* el)
          {
          addRefresh(el->abbox());
          _selection.remove(el);
          setSelectionChanged(true);
          update();
          }

    //---------------------------------------------------------
    //   select
    //    staffIdx is valid, if element is of type MEASURE
    //---------------------------------------------------------

    void Score::select(Element* e, SelectType type, int staffIdx)
          {
          if (e && (e->isNote() || e->isRest())) {
                Element* ee = e;
                if (ee->isNote())
                      ee = ee->parent();
                int tick = toChordRest(ee)->segment()->tick();
                if (playPos() != tick)
                      setPlayPos(tick);
                }
          if (MScore::debugMode)
                qDebug("select element <%s> type %d(state %d) staff %d",
                   e ? e->name() : "", int(type), int(selection().state()), e ? e->staffIdx() : -1);

          switch (type) {
                case SelectType::SINGLE:
                      selectSingle(e, staffIdx);
                      break;
                case SelectType::ADD:
                      selectAdd(e);
                      break;
                case SelectType::RANGE:
                      selectRange(e, staffIdx);
                      break;
                }
          setSelectionChanged(true);
          }

    //---------------------------------------------------------
    //   selectSingle
    //    staffIdx is valid, if element is of type MEASURE
    //---------------------------------------------------------

    void Score::selectSingle(Element* e, int staffIdx)
          {
          SelState selState = _selection.state();
          deselectAll();
          if (e == 0) {
                selState = SelState::NONE;
                setUpdateAll();
                }
          else {
                if (e->isMeasure()) {
                      select(e, SelectType::RANGE, staffIdx);
                      return;
                      }
                addRefresh(e->abbox());
                _selection.add(e);
                _is.setTrack(e->track());
                selState = SelState::LIST;
                if (e->type() == ElementType::NOTE) {
                      e = e->parent();
                      }
                if (e->isChordRest()) {
                      _is.setLastSegment(_is.segment());
                      _is.setSegment(toChordRest(e)->segment());
                      }
                }
          _selection.setActiveSegment(0);
          _selection.setActiveTrack(0);

          _selection.setState(selState);
          }

    //---------------------------------------------------------
    //   selectAdd
    //---------------------------------------------------------

    void Score::selectAdd(Element* e)
          {
          SelState selState = _selection.state();

          if (_selection.isRange()) {
                select(0, SelectType::SINGLE, 0);
                return;
                }

          if (e->isMeasure()) {
                Measure* m = toMeasure(e);
                int tick  = m->tick();
                if (_selection.isNone()) {
                      _selection.setRange(m->tick2segment(tick),
                                          m == lastMeasure() ? 0 : m->last(),
                                          0,
                                          nstaves());
                      }
                else {
                      select(0, SelectType::SINGLE, 0);
                      return;
                      }
                setUpdateAll();
                selState = SelState::RANGE;
                _selection.updateSelectedElements();
                }
          else { // None or List
                addRefresh(e->abbox());
                if (_selection.elements().contains(e))
                      _selection.remove(e);
                else {
                      selState = SelState::LIST;
                      _selection.add(e);
                      }
                }
          _selection.setState(selState);
          }

    //---------------------------------------------------------
    //   selectRange
    //    staffIdx is valid, if element is of type MEASURE
    //---------------------------------------------------------

    void Score::selectRange(Element* e, int staffIdx)
          {
          int activeTrack = e->track();
          // current selection is range extending to end of score?
          bool endRangeSelected = selection().isRange() && selection().endSegment() == nullptr;
          if (e->type() == ElementType::MEASURE) {
                Measure* m = toMeasure(e);
                int tick  = m->tick();
                int etick = tick + m->ticks();
                activeTrack = staffIdx * VOICES;
                if (_selection.isNone()
                   || (_selection.isList() && !_selection.isSingle())) {
                            if (_selection.isList())
                                  deselectAll();
                      _selection.setRange(m->tick2segment(tick),
                                          m == lastMeasure() ? 0 : m->last(),
                                          staffIdx,
                                          staffIdx + 1);
                      }
                else if (_selection.isRange()) {
                      _selection.extendRangeSelection(m->tick2segment(tick),
                                                      m == lastMeasure() ? 0 : m->last(),
                                                      staffIdx,
                                                      tick,
                                                      etick);
                      }
                else if (_selection.isSingle()) {
                      Element* oe = selection().element();
                      if (oe->isNote() || oe->isChordRest()) {
                            if (oe->isNote())
                                  oe = oe->parent();

                            ChordRest* cr = toChordRest(oe);
                            int oetick = cr->segment()->tick();
                            Segment* startSegment = cr->segment();
                            Segment* endSegment = m->last();
                            if (tick < oetick) {
                                  startSegment = m->tick2segment(tick);
                                  if (etick <= oetick)
                                        endSegment = cr->nextSegmentAfterCR(SegmentType::ChordRest
                                                                                        | SegmentType::EndBarLine
                                                                                        | SegmentType::Clef);

                                  }
                            int staffStart = staffIdx;
                            int endStaff = staffIdx + 1;
                            if (staffStart > cr->staffIdx())
                                  staffStart = cr->staffIdx();
                            else if (cr->staffIdx() >= endStaff)
                                  endStaff = cr->staffIdx() + 1;
                            _selection.setRange(startSegment, endSegment, staffStart, endStaff);
                            }
                      else {
                            deselectAll();
                            _selection.setRange(m->tick2segment(tick),
                                                m == lastMeasure() ? 0 : m->last(),
                                                staffIdx,
                                                staffIdx + 1);
                            }
                      }
                else {
                      qDebug("SELECT_RANGE: measure: sel state %d", int(_selection.state()));
                      return;
                      }
                }
          else if (e->type() == ElementType::NOTE || e->isChordRest()) {
                if (e->type() == ElementType::NOTE)
                      e = e->parent();
                ChordRest* cr = toChordRest(e);

                if (_selection.isNone()
                    || (_selection.isList() && !_selection.isSingle())) {
                      if (_selection.isList())
                            deselectAll();
                      _selection.setRange(cr->segment(),
                                          cr->nextSegmentAfterCR(SegmentType::ChordRest
                                                                 | SegmentType::EndBarLine
                                                                 | SegmentType::Clef),
                                          e->staffIdx(),
                                          e->staffIdx() + 1);
                      activeTrack = cr->track();
                      }
                else if (_selection.isSingle()) {
                      Element* oe = _selection.element();
                      if (oe && (oe->type() == ElementType::NOTE || oe->type() == ElementType::REST)) {
                            if (oe->type() == ElementType::NOTE)
                                  oe = oe->parent();
                            ChordRest* ocr = toChordRest(oe);

                            Segment* endSeg = tick2segmentMM(ocr->segment()->tick() + ocr->actualTicks());
                            if (!endSeg)
                                  endSeg = ocr->segment()->next();

                            _selection.setRange(ocr->segment(),
                                                endSeg,
                                                oe->staffIdx(),
                                                oe->staffIdx() + 1);
                            _selection.extendRangeSelection(cr);

                            }
                      else {
                            select(e, SelectType::SINGLE, 0);
                            return;
                            }
                      }
                else if (_selection.isRange()) {
                      _selection.extendRangeSelection(cr);
                      }
                else {
                      qDebug("sel state %d", int(_selection.state()));
                      return;
                      }
                if (!endRangeSelected && !_selection.endSegment())
                      _selection.setEndSegment(cr->segment()->nextCR());
                if (!_selection.startSegment())
                      _selection.setStartSegment(cr->segment());
                }
          else {
                select(e, SelectType::SINGLE, staffIdx);
                return;
                }

          _selection.setActiveTrack(activeTrack);

          // doing this in note entry mode can clear selection
          if (_selection.startSegment() && !noteEntryMode())
                setPlayPos(_selection.startSegment()->tick());

          _selection.updateSelectedElements();
          }

    //---------------------------------------------------------
    //   collectMatch
    //---------------------------------------------------------

    void Score::collectMatch(void* data, Element* e)
          {
          ElementPattern* p = static_cast<ElementPattern*>(data);
          if (p->type != int(e->type()))
                return;

          if (p->type == int(ElementType::NOTE)) {
                if (p->subtype < 0) {
                      if (!(toNote(e)->chord()->isGrace()))
                            return;
                      }
                else if ((toNote(e)->chord()->isGrace()) || (p->subtype != e->subtype()))
                      return;
                }
          else if (p->subtypeValid && p->subtype != e->subtype())
                return;

          if ((p->staffStart != -1)
             && ((p->staffStart > e->staffIdx()) || (p->staffEnd <= e->staffIdx())))
                return;

          if (p->voice != -1 && p->voice != e->voice())
                return;

          if (p->system) {
                Element* ee = e;
                do {
                      if (ee->type() == ElementType::SYSTEM) {
                            if (p->system != ee)
                                  return;
                            break;
                            }
                      ee = ee->parent();
                      } while (ee);
                }
          p->el.append(e);
          }

    //---------------------------------------------------------
    //   collectNoteMatch
    //---------------------------------------------------------

    void Score::collectNoteMatch(void* data, Element* e)
          {
          NotePattern* p = static_cast<NotePattern*>(data);
          if (!e->isNote())
                return;
          Note* n = toNote(e);
          if (p->type != NoteType::INVALID && p->type != n->noteType())
                return;
          if (p->pitch != -1 && p->pitch != n->pitch())
                return;
          if (p->tpc != Tpc::TPC_INVALID && p->tpc != n->tpc())
                return;
          if (p->notehead != NoteHead::Group::HEAD_INVALID && p->notehead != n->headGroup())
                return;
          if (p->duration.type() != TDuration::DurationType::V_INVALID && p->duration != n->chord()->actualDurationType())
                return;
          if ((p->staffStart != -1)
             && ((p->staffStart > e->staffIdx()) || (p->staffEnd <= e->staffIdx())))
                return;
          if (p->voice != -1 && p->voice != e->voice())
                return;
          if (p->system && (p->system != n->chord()->segment()->system()))
                return;
          p->el.append(n);
          }

    //---------------------------------------------------------
    //   selectSimilar
    //---------------------------------------------------------

    void Score::selectSimilar(Element* e, bool sameStaff)
          {
          ElementType type = e->type();
          Score* score = e->score();

          ElementPattern pattern;
          pattern.type = int(type);
          pattern.subtype = 0;
          pattern.subtypeValid = false;
          if (type == ElementType::NOTE) {
                if (toNote(e)->chord()->isGrace())
                      pattern.subtype = -1; // hack
                else
                      pattern.subtype = e->subtype();
                }
          pattern.staffStart = sameStaff ? e->staffIdx() : -1;
          pattern.staffEnd = sameStaff ? e->staffIdx() + 1 : -1;
          pattern.voice   = -1;
          pattern.system  = 0;

          score->scanElements(&pattern, collectMatch);

          score->select(0, SelectType::SINGLE, 0);
          for (Element* e : pattern.el)
                score->select(e, SelectType::ADD, 0);
          }

    //---------------------------------------------------------
    //   selectSimilarInRange
    //---------------------------------------------------------

    void Score::selectSimilarInRange(Element* e)
          {
          ElementType type = e->type();
          Score* score = e->score();

          ElementPattern pattern;
          pattern.type    = int(type);
          pattern.subtype = 0;
          pattern.subtypeValid = false;
          if (type == ElementType::NOTE) {
                if (toNote(e)->chord()->isGrace())
                      pattern.subtype = -1; //hack
                else
                      pattern.subtype = e->subtype();
                pattern.subtypeValid = true;
                }
          pattern.staffStart = selection().staffStart();
          pattern.staffEnd = selection().staffEnd();
          pattern.voice   = -1;
          pattern.system  = 0;

          score->scanElementsInRange(&pattern, collectMatch);

          score->select(0, SelectType::SINGLE, 0);
          for (Element* e : pattern.el)
                score->select(e, SelectType::ADD, 0);
          }

    //---------------------------------------------------------
    //   lassoSelect
    //---------------------------------------------------------

    void Score::lassoSelect(const QRectF& bbox)
          {
          select(0, SelectType::SINGLE, 0);
          QRectF fr(bbox.normalized());
          foreach(Page* page, pages()) {
                QRectF pr(page->bbox());
                QRectF frr(fr.translated(-page->pos()));
                if (pr.right() < frr.left())
                      continue;
                if (pr.left() > frr.right())
                      break;

                QList<Element*> el = page->items(frr);
                for (int i = 0; i < el.size(); ++i) {
                      Element* e = el.at(i);
                      if (frr.contains(e->abbox())) {
                            if (e->type() != ElementType::MEASURE && e->selectable())
                                  select(e, SelectType::ADD, 0);
                            }
                      }
                }
          }

    //---------------------------------------------------------
    //   lassoSelectEnd
    //---------------------------------------------------------

    void Score::lassoSelectEnd()
          {
          int noteRestCount     = 0;
          Segment* startSegment = 0;
          Segment* endSegment   = 0;
          int startStaff        = 0x7fffffff;
          int endStaff          = 0;
          const ChordRest* endCR = 0;

          if (_selection.elements().empty()) {
                _selection.setState(SelState::NONE);
                setUpdateAll();
                return;
                }
          _selection.setState(SelState::LIST);

          foreach(const Element* e, _selection.elements()) {
                if (e->type() != ElementType::NOTE && e->type() != ElementType::REST)
                      continue;
                ++noteRestCount;
                if (e->type() == ElementType::NOTE)
                      e = e->parent();
                Segment* seg = static_cast<const ChordRest*>(e)->segment();
                if ((startSegment == 0) || (*seg < *startSegment))
                      startSegment = seg;
                if ((endSegment == 0) || (*seg > *endSegment)) {
                      endSegment = seg;
                      endCR = static_cast<const ChordRest*>(e);
                      }
                int idx = e->staffIdx();
                if (idx < startStaff)
                      startStaff = idx;
                if (idx > endStaff)
                      endStaff = idx;
                }
          if (noteRestCount > 0) {
                endSegment = endCR->nextSegmentAfterCR(SegmentType::ChordRest
                   | SegmentType::EndBarLine
                   | SegmentType::Clef);
                _selection.setRange(startSegment, endSegment, startStaff, endStaff+1);
                if (!_selection.isRange())
                      _selection.setState(SelState::RANGE);
                _selection.updateSelectedElements();
                }
          setUpdateAll();
          }

    //---------------------------------------------------------
    //   addLyrics
    //---------------------------------------------------------

    void Score::addLyrics(int tick, int staffIdx, const QString& txt)
          {
          if (txt.trimmed().isEmpty())
                return;
          Measure* measure = tick2measure(tick);
          Segment* seg     = measure->findSegment(SegmentType::ChordRest, tick);
          if (seg == 0) {
                qDebug("no segment found for lyrics<%s> at tick %d",
                   qPrintable(txt), tick);
                return;
                }

          bool lyricsAdded = false;
          for (int voice = 0; voice < VOICES; ++voice) {
                int track = staffIdx * VOICES + voice;
                ChordRest* cr = toChordRest(seg->element(track));
                if (cr) {
                      Lyrics* l = new Lyrics(this);
                      l->setXmlText(txt);
                      l->setTrack(track);
                      cr->add(l);
                      lyricsAdded = true;
                      break;
                      }
                }
          if (!lyricsAdded) {
                qDebug("no chord/rest for lyrics<%s> at tick %d, staff %d",
                   qPrintable(txt), tick, staffIdx);
                }
          }

    //---------------------------------------------------------
    //   setTempo
    //    convenience function to access TempoMap
    //---------------------------------------------------------

    void Score::setTempo(Segment* segment, qreal tempo)
          {
          setTempo(segment->tick(), tempo);
          }

    void Score::setTempo(int tick, qreal tempo)
          {
          tempomap()->setTempo(tick, tempo);
          _playlistDirty = true;
          }

    //---------------------------------------------------------
    //   removeTempo
    //---------------------------------------------------------

    void Score::removeTempo(int tick)
          {
          tempomap()->delTempo(tick);
          _playlistDirty = true;
          }

    //---------------------------------------------------------
    //   setPause
    //---------------------------------------------------------

    void Score::setPause(int tick, qreal seconds)
          {
          tempomap()->setPause(tick, seconds);
          _playlistDirty = true;
          }

    //---------------------------------------------------------
    //   tempo
    //---------------------------------------------------------

    qreal Score::tempo(int tick) const
          {
          return tempomap()->tempo(tick);
          }

    //---------------------------------------------------------
    //   loWidth
    //---------------------------------------------------------

    qreal Score::loWidth() const
          {
          return styleD(StyleIdx::pageWidth) * DPI;
          }

    //---------------------------------------------------------
    //   loHeight
    //---------------------------------------------------------

    qreal Score::loHeight() const
          {
          return styleD(StyleIdx::pageHeight) * DPI;
          }

    //---------------------------------------------------------
    //   cmdSelectAll
    //---------------------------------------------------------

    void Score::cmdSelectAll()
          {
          if (_measures.size() == 0)
                return;
          deselectAll();
          Measure* first = firstMeasureMM();
          if (!first)
                return;
          Measure* last = lastMeasureMM();
          selectRange(first, 0);
          selectRange(last, nstaves() - 1);
          setUpdateAll();
          update();
          }

    //---------------------------------------------------------
    //   cmdSelectSection
    //---------------------------------------------------------

    void Score::cmdSelectSection()
          {
          Segment* s = _selection.startSegment();
          if (s == 0)
                return;
          MeasureBase* sm = s->measure();
          MeasureBase* em = sm;
          while (sm->prev()) {
                if (sm->prev()->sectionBreak())
                      break;
                sm = sm->prev();
                }
          while (em->next()) {
                if (em->sectionBreak())
                      break;
                em = em->next();
                }
          while (sm && sm->type() != ElementType::MEASURE)
                sm = sm->next();
          while (em && em->type() != ElementType::MEASURE)
                em = em->next();
          if (sm == 0 || em == 0)
                return;

          _selection.setRange(toMeasure(sm)->first(), toMeasure(em)->last(), 0, nstaves());
          }

    //---------------------------------------------------------
    //   undo
    //---------------------------------------------------------

    void Score::undo(UndoCommand* cmd, EditData* ed) const
          {
          undoStack()->push(cmd, ed);
          }

    //---------------------------------------------------------
    //   linkId
    //---------------------------------------------------------

    int Score::linkId()
          {
          return (masterScore()->_linkId)++;
          }

    // val is a used link id
    void Score::linkId(int val)
          {
          Score* s = masterScore();
          if (val >= s->_linkId)
                s->_linkId = val + 1;   // update unused link id
          }

    //---------------------------------------------------------
    //   scoreList
    //    return a list of scores containing the root score
    //    and all part scores (if there are any)
    //---------------------------------------------------------

    QList<Score*> Score::scoreList()
          {
          QList<Score*> scores;
          Score* root = masterScore();
          scores.append(root);
          for (const Excerpt* ex : root->excerpts()) {
                if (ex->partScore())
                      scores.append(ex->partScore());
                }
          return scores;
          }

    //---------------------------------------------------------
    //   switchLayer
    //---------------------------------------------------------

    bool Score::switchLayer(const QString& s)
          {
          int layerIdx = 0;
          for (const Layer& l : layer()) {
                if (s == l.name) {
                      setCurrentLayer(layerIdx);
                      return true;
                      }
                ++layerIdx;
                }
          return false;
          }

    //---------------------------------------------------------
    //   appendPart
    //---------------------------------------------------------

    void Score::appendPart(const QString& name)
          {
          static InstrumentTemplate defaultInstrument;
          InstrumentTemplate* t;

          t = searchTemplate(name);
          if (t == 0) {
                qDebug("appendPart: <%s> not found", qPrintable(name));
                t = &defaultInstrument;
                }

          if (t->channel.empty()) {
                Channel a;
                a.chorus = 0;
                a.reverb = 0;
                a.name   = "normal";
                a.bank   = 0;
                a.volume = 100;
                a.pan    = 64; // actually 63.5 for center
                t->channel.append(a);
                }
          Part* part = new Part(this);
          part->initFromInstrTemplate(t);
          int n = nstaves();
          for (int i = 0; i < t->nstaves(); ++i) {
                Staff* staff = new Staff(this);
                staff->setPart(part);
                staff->setLines(0, t->staffLines[i]);
                staff->setSmall(0, t->smallStaff[i]);
                if (i == 0) {
                      staff->setBracketType(0, t->bracket[0]);
                      staff->setBracketSpan(0, t->nstaves());
                      }
                undoInsertStaff(staff, i);
                }
          part->staves()->front()->setBarLineSpan(part->nstaves());
          undoInsertPart(part, n);
          fixTicks();
          masterScore()->rebuildMidiMapping();
          }

    //---------------------------------------------------------
    //   appendMeasures
    //---------------------------------------------------------

    void Score::appendMeasures(int n)
          {
          for (int i = 0; i < n; ++i)
                insertMeasure(ElementType::MEASURE, 0, false);
          }

    #ifdef SCRIPT_INTERFACE
    //---------------------------------------------------------
    //   addText
    //---------------------------------------------------------

    void Score::addText(const QString& type, const QString& txt)
          {
          MeasureBase* measure = first();
          if (measure == 0 || measure->type() != ElementType::VBOX) {
                insertMeasure(ElementType::VBOX, measure);
                measure = first();
                }
          Text* text = new Text(this);
          if (type == "title")
                text->initSubStyle(SubStyle::TITLE);
          else if (type == "subtitle")
                text->initSubStyle(SubStyle::SUBTITLE);
          text->setParent(measure);
          text->setXmlText(txt);
          undoAddElement(text);
          }

    //---------------------------------------------------------
    //   newCursor
    //---------------------------------------------------------

    Cursor* Score::newCursor()
          {
          return new Cursor(this);
          }
    #endif

    //---------------------------------------------------------
    //   addSpanner
    //---------------------------------------------------------

    void Score::addSpanner(Spanner* s)
          {
          _spanner.addSpanner(s);
          }

    //---------------------------------------------------------
    //   removeSpanner
    //---------------------------------------------------------

    void Score::removeSpanner(Spanner* s)
          {
          _spanner.removeSpanner(s);
          }

    //---------------------------------------------------------
    //   isSpannerStartEnd
    //    does is spanner start or end at tick position tick
    //    for track ?
    //---------------------------------------------------------

    bool Score::isSpannerStartEnd(int tick, int track) const
          {
          for (auto i : _spanner.map()) {
                if (i.second->track() != track)
                      continue;
                if (i.second->tick() == tick || i.second->tick2() == tick)
                      return true;
                }
          return false;
          }

    void Score::insertTime(int tick, int len)
          {
          for (Staff* staff : staves())
                staff->insertTime(tick, len);
          for (Part* part : parts())
                part->insertTime(tick, len);
          }

    //---------------------------------------------------------
    //   addUnmanagedSpanner
    //---------------------------------------------------------

    void Score::addUnmanagedSpanner(Spanner* s)
          {
          _unmanagedSpanner.insert(s);
          }

    //---------------------------------------------------------
    //   removeSpanner
    //---------------------------------------------------------

    void Score::removeUnmanagedSpanner(Spanner* s)
          {
          _unmanagedSpanner.erase(s);
          }

    //---------------------------------------------------------
    //   setPos
    //---------------------------------------------------------

    void Score::setPos(POS pos, int tick)
          {
          if (tick < 0)
                tick = 0;
          if (tick != _pos[int(pos)])
                _pos[int(pos)] = tick;
          // even though tick position might not have changed, layout might have
          // so we should update cursor here
          // however, we must be careful not to call setPos() again while handling posChanged, or recursion results
          emit posChanged(pos, unsigned(tick));
          }

    //---------------------------------------------------------
    //   uniqueStaves
    //---------------------------------------------------------

    QList<int> Score::uniqueStaves() const
          {
          QList<int> sl;

          for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
                Staff* s = staff(staffIdx);
                if (s->linkedStaves()) {
                      bool alreadyInList = false;
                      for (int idx : sl) {
                            if (s->linkedStaves()->staves().contains(staff(idx))) {
                                  alreadyInList = true;
                                  break;
                                  }
                            }
                      if (alreadyInList)
                            continue;
                      }
                sl.append(staffIdx);
                }
          return sl;
          }

    //---------------------------------------------------------
    //   findCR
    //    find chord/rest <= tick in track
    //---------------------------------------------------------

    ChordRest* Score::findCR(int tick, int track) const
          {
          Measure* m = tick2measureMM(tick);
          if (!m) {
                qDebug("findCR: no measure for tick %d", tick);
                return nullptr;
                }
          // attach to first rest all spanner when mmRest
          if (m->isMMRest())
                tick = m->tick();
          Segment* s = m->first(SegmentType::ChordRest);
          for (Segment* ns = s; ; ns = ns->next(SegmentType::ChordRest)) {
                if (ns == 0 || ns->tick() > tick)
                      break;
                Element* el = ns->element(track);
                if (el && el->isRest() && toRest(el)->isGap())
                      continue;
                else if (el)
                      s = ns;
                }
          Element* el = s->element(track);
          if (el && el->isRest() && toRest(el)->isGap())
                s = 0;
          if (s)
                return toChordRest(s->element(track));
          return nullptr;
          }

    //---------------------------------------------------------
    //   findCRinStaff
    //    find last chord/rest on staff that ends before tick
    //---------------------------------------------------------

    ChordRest* Score::findCRinStaff(int tick, int staffIdx) const
          {
          int ptick = tick - 1;
          Measure* m = tick2measureMM(ptick);
          if (!m) {
                qDebug("findCRinStaff: no measure for tick %d", ptick);
                return nullptr;
                }
          // attach to first rest all spanner when mmRest
          if (m->isMMRest())
                ptick = m->tick();
          Segment* s = m->first(SegmentType::ChordRest);
          int strack = staffIdx * VOICES;
          int etrack = strack + VOICES;
          int actualTrack = strack;

          int lastTick = -1;
          for (Segment* ns = s; ; ns = ns->next(SegmentType::ChordRest)) {
                if (ns == 0 || ns->tick() > ptick)
                      break;
                // found a segment; now find longest cr on this staff that does not overlap tick
                for (int t = strack; t < etrack; ++t) {
                      ChordRest* cr = toChordRest(ns->element(t));
                      if (cr) {
                            int endTick = cr->tick() + cr->actualTicks();
                            // allow fudge factor for tuplets
                            // TODO: replace with fraction-based calculation
                            int fudge = cr->tuplet() ? 5 : 0;
                            if (endTick + fudge >= lastTick && endTick - fudge <= tick) {
                                  s = ns;
                                  actualTrack = t;
                                  lastTick = endTick;
                                  }
                            }
                      }
                }
          if (s)
                return toChordRest(s->element(actualTrack));
          return nullptr;
          }

    //---------------------------------------------------------
    //   setSoloMute
    //   called once at opening file, adds soloMute marks
    //---------------------------------------------------------

    void MasterScore::setSoloMute()
          {
          for (int i = 0; i < _midiMapping.size(); i++) {
                Channel* b = _midiMapping[i].articulation;
                if (b->solo) {
                      b->soloMute = false;
                      for (int j = 0; j < _midiMapping.size(); j++) {
                            Channel* a = _midiMapping[j].articulation;
                            bool sameMidiMapping = _midiMapping[i].port == _midiMapping[j].port && _midiMapping[i].channel == _midiMapping[j].channel;
                            a->soloMute = (i != j && !a->solo && !sameMidiMapping);
                            a->solo     = (i == j || a->solo || sameMidiMapping);
                            }
                      }
                }
          }

    //---------------------------------------------------------
    //   setImportedFilePath
    //---------------------------------------------------------

    void Score::setImportedFilePath(const QString& filePath)
          {
          _importedFilePath = filePath;
          }

    #if 0
    //---------------------------------------------------------
    //   title
    //---------------------------------------------------------

    QString Score::title()
          {
          QString fn;
          Text* t = getText(SubStyle::TITLE);
          if (t)
                fn = QTextDocumentFragment::fromHtml(t->xmlText()).toPlainText().replace("&amp;","&").replace("&gt;",">").replace("&lt;","<").replace("&quot;", "\"");

          if (fn.isEmpty())
                fn = metaTag("workTitle");

          if (fn.isEmpty())
                fn = masterScore()->fileInfo()->completeBaseName();

          if (fn.isEmpty())
                fn = "Untitled";

          return fn.simplified();
          }

    //---------------------------------------------------------
    //   subtitle
    //---------------------------------------------------------

    QString Score::subtitle()
          {
          QString fn;
          Text* t = getText(SubStyle::SUBTITLE);
          if (t)
                fn = QTextDocumentFragment::fromHtml(t->xmlText()).toPlainText().replace("&amp;","&").replace("&gt;",">").replace("&lt;","<").replace("&quot;", "\"");

          return fn.simplified();
          }

    //---------------------------------------------------------
    //   composer
    //---------------------------------------------------------

    QString Score::composer()
          {
          QString fn;
          Text* t = getText(SubStyle::COMPOSER);
          if (t)
                fn = QTextDocumentFragment::fromHtml(t->xmlText()).toPlainText().replace("&amp;","&").replace("&gt;",">").replace("&lt;","<").replace("&quot;", "\"");

          if (fn.isEmpty())
                fn = metaTag("composer");

          return fn.simplified();
          }

    //---------------------------------------------------------
    //   poet
    //---------------------------------------------------------

    QString Score::poet()
          {
          QString fn;
          Text* t = getText(SubStyle::POET);
          if (t)
                fn = QTextDocumentFragment::fromHtml(t->xmlText()).toPlainText().replace("&amp;","&").replace("&gt;",">").replace("&lt;","<").replace("&quot;", "\"");

          if (fn.isEmpty())
                fn = metaTag("lyricist");

          if (fn.isEmpty())
                fn = "";

          return fn.simplified();
          }
    #endif

    //---------------------------------------------------------
    //   nmeasure
    //---------------------------------------------------------

    int Score::nmeasures()
          {
          int n = 0;
          for (Measure* m = firstMeasure(); m; m = m->nextMeasure())
                n++;
          return n;
          }

    //---------------------------------------------------------
    //   hasLyrics
    //---------------------------------------------------------

    bool Score::hasLyrics()
          {
          SegmentType st = SegmentType::ChordRest;
          for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
                for (int i = 0; i < ntracks(); ++i) {
                      ChordRest* cr = toChordRest(seg->element(i));
                      if (cr && !cr->lyrics().empty())
                            return true;
                      }
                }
          return false;
          }

    //---------------------------------------------------------
    //   hasHarmonies
    //---------------------------------------------------------

    bool Score::hasHarmonies()
          {
          SegmentType st = SegmentType::ChordRest;
          for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
                for (Element* e : seg->annotations()) {
                      if (e->type() == ElementType::HARMONY)
                            return true;
                      }
                }
          return false;
          }

    //---------------------------------------------------------
    //   lyricCount
    //---------------------------------------------------------

    int Score::lyricCount()
          {
          int count = 0;
          SegmentType st = SegmentType::ChordRest;
          for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
                for (int i = 0; i < ntracks(); ++i) {
                      ChordRest* cr = toChordRest(seg->element(i));
                      if (cr)
                            count += cr->lyrics().size();
                      }
                }
          return count;
          }

    //---------------------------------------------------------
    //   harmonyCount
    //---------------------------------------------------------

    int Score::harmonyCount()
          {
          int count = 0;
          SegmentType st = SegmentType::ChordRest;
          for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
                for (Element* e : seg->annotations()) {
                      if (e->type() == ElementType::HARMONY)
                            count++;
                      }
                }
          return count;
          }

    //---------------------------------------------------------
    //   extractLyrics
    //---------------------------------------------------------

    QString Score::extractLyrics()
          {
          QString result;
          updateRepeatList(true);
          setPlaylistDirty();
          SegmentType st = SegmentType::ChordRest;
          for (int track = 0; track < ntracks(); track += VOICES) {
                bool found = false;
                unsigned maxLyrics = 1;
                for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
                      m->setPlaybackCount(0);
                      }
                // follow the repeat segments
                for (const RepeatSegment* rs : *repeatList()) {
                      int startTick  = rs->tick;
                      int endTick    = startTick + rs->len;
                      for (Measure* m = tick2measure(startTick); m; m = m->nextMeasure()) {
                            int playCount = m->playbackCount();
                            for (Segment* seg = m->first(st); seg; seg = seg->next(st)) {
                                  // consider voice 1 only
                                  ChordRest* cr = toChordRest(seg->element(track));
                                  if (!cr || cr->lyrics().empty())
                                        continue;
                                  if (cr->lyrics().size() > maxLyrics)
                                        maxLyrics = cr->lyrics().size();
                                  if (playCount >= int(cr->lyrics().size()))
                                        continue;
                                  Lyrics* l = cr->lyrics(playCount, Element::Placement::BELOW);  // TODO: ABOVE
                                  if (!l)
                                        continue;
                                  found = true;
                                  QString lyric = l->plainText().trimmed();
                                  if (l->syllabic() == Lyrics::Syllabic::SINGLE || l->syllabic() == Lyrics::Syllabic::END)
                                        result += lyric + " ";
                                  else if (l->syllabic() == Lyrics::Syllabic::BEGIN || l->syllabic() == Lyrics::Syllabic::MIDDLE)
                                        result += lyric;
                                  }
                            m->setPlaybackCount(m->playbackCount() + 1);
                            if (m->tick() + m->ticks() >= endTick)
                                  break;
                            }
                      }
                // consider remaning lyrics
                for (unsigned lyricsNumber = 0; lyricsNumber < maxLyrics; lyricsNumber++) {
                      for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
                            unsigned playCount = m->playbackCount();
                            if (lyricsNumber >= playCount) {
                                  for (Segment* seg = m->first(st); seg; seg = seg->next(st)) {
                                        // consider voice 1 only
                                        ChordRest* cr = toChordRest(seg->element(track));
                                        if (!cr || cr->lyrics().empty())
                                              continue;
                                        if (cr->lyrics().size() > maxLyrics)
                                              maxLyrics = cr->lyrics().size();
                                        if (lyricsNumber >= cr->lyrics().size())
                                              continue;
                                        Lyrics* l = cr->lyrics(lyricsNumber, Element::Placement::BELOW);  // TODO
                                        if (!l)
                                              continue;
                                        found = true;
                                        QString lyric = l->plainText().trimmed();
                                        if (l->syllabic() == Lyrics::Syllabic::SINGLE || l->syllabic() == Lyrics::Syllabic::END)
                                              result += lyric + " ";
                                        else if (l->syllabic() == Lyrics::Syllabic::BEGIN || l->syllabic() == Lyrics:: Syllabic::MIDDLE)
                                              result += lyric;
                                        }
                                  }
                            }
                      }
                if (found)
                      result += "\n\n";
                }
          return result.trimmed();
          }

    //---------------------------------------------------------
    //   keysig
    //---------------------------------------------------------

    int Score::keysig()
          {
          Key result = Key::C;
          for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
                Staff* st = staff(staffIdx);
                Key key = st->key(0);
                if (st->staffType(0)->group() == StaffGroup::PERCUSSION || st->keySigEvent(0).custom() || st->keySigEvent(0).isAtonal())       // ignore percussion and custom / atonal key
                      continue;
                result = key;
                int diff = st->part()->instrument()->transpose().chromatic;
                if (!styleB(StyleIdx::concertPitch) && diff)
                      result = transposeKey(key, diff);
                break;
                }
          return int(result);
          }

    //---------------------------------------------------------
    //   duration
    //---------------------------------------------------------

    int Score::duration()
          {
          updateRepeatList(true);
          RepeatSegment* rs = repeatList()->last();
          return lrint(utick2utime(rs->utick + rs->len));
          }

    //---------------------------------------------------------
    //   createRehearsalMarkText
    //---------------------------------------------------------

    QString Score::createRehearsalMarkText(RehearsalMark* current) const
          {
          int tick = current->segment()->tick();
          RehearsalMark* before = 0;
          RehearsalMark* after = 0;
          for (Segment* s = firstSegment(SegmentType::All); s; s = s->next1()) {
                for (Element* e : s->annotations()) {
                      if (e && e->type() == ElementType::REHEARSAL_MARK) {
                            if (s->tick() < tick)
                                  before = toRehearsalMark(e);
                            else if (s->tick() > tick) {
                                  after = toRehearsalMark(e);
                                  break;
                                  }
                            }
                      }
                if (after)
                      break;
                }
          QString s = "A";
          QString s1 = before ? before->xmlText() : "";
          QString s2 = after ? after->xmlText()  : "";
          if (s1.isEmpty())
                return s;
          s = nextRehearsalMarkText(before, current);     // try to sequence
          if (s == current->xmlText()) {
                // no sequence detected (or current happens to be correct)
                return s;
                }
          else if (s == s2) {
                // next in sequence already present
                if (s1[0].isLetter()) {
                      if (s1.size() == 2)
                            s = s1[0] + QChar::fromLatin1(s1[1].toLatin1() + 1);  // BB, BC, CC
                      else
                            s = s1 + QChar::fromLatin1('1');                      // B, B1, C
                      }
                else {
                      s = s1 + QChar::fromLatin1('A');                            // 2, 2A, 3
                      }
                }
          return s;
          }

    //---------------------------------------------------------
    //   nextRehearsalMarkText
    //    finds next rehearsal in sequence established by previous
    //     Alphabetic sequences:
    //      A, B, ..., Y, Z, AA, BB, ..., YY, ZZ
    //      a, b, ..., y, z, aa, bb, ..., yy, zz
    //     Numeric sequences:
    //      1, 2, 3, ...
    //      If number of previous rehearsal mark matches measure number, assume use of measure numbers throughout
    //---------------------------------------------------------

    QString Score::nextRehearsalMarkText(RehearsalMark* previous, RehearsalMark* current) const
          {
          QString previousText = previous->xmlText();
          QString fallback = current ? current->xmlText() : previousText + "'";

          if (previousText.length() == 1 && previousText[0].isLetter()) {
                // single letter sequence
                if (previousText == "Z")
                      return "AA";
                else if (previousText == "z")
                      return "aa";
                else
                      return QChar::fromLatin1(previousText[0].toLatin1() + 1);
                }
          else if (previousText.length() == 2 && previousText[0].isLetter() && previousText[1].isLetter()) {
                // double letter sequence
                if (previousText[0] == previousText[1]) {
                      // repeated letter sequence
                      if (previousText.toUpper() != "ZZ") {
                            QString c = QChar::fromLatin1(previousText[0].toLatin1() + 1);
                            return c + c;
                            }
                      else {
                            return fallback;
                            }
                      }
                else {
                      return fallback;
                      }
                }
          else {
                // try to interpret as number
                bool ok;
                int n = previousText.toInt(&ok);
                if (!ok) {
                      return fallback;
                      }
                else if (current && n == previous->segment()->measure()->no() + 1) {
                      // use measure number
                      n = current->segment()->measure()->no() + 1;
                      return QString("%1").arg(n);
                      }
                else {
                      // use number sequence
                      n = previousText.toInt() + 1;
                      return QString("%1").arg(n);
                      }
                }
          }

    //---------------------------------------------------------
    //   changeVoice
    //    moves selected notes into specified voice if possible
    //---------------------------------------------------------

    void Score::changeVoice(int voice)
          {
          startCmd();
          QList<Element*> el;
          QList<Element*> oel = selection().elements();     // make copy
          for (Element* e : oel) {
                if (e->type() == ElementType::NOTE) {
                      Note* note   = toNote(e);
                      Chord* chord = note->chord();

                      // TODO - handle ties; for now we skip tied notes
                      if (note->tieFor() || note->tieBack())
                            continue;

                      // move grace notes with main chord only
                      if (chord->isGrace())
                            continue;

                      if (chord->voice() != voice) {
                            Segment* s       = chord->segment();
                            Measure* m       = s->measure();
                            int notes        = chord->notes().size();
                            int dstTrack     = chord->staffIdx() * VOICES + voice;
                            ChordRest* dstCR = toChordRest(s->element(dstTrack));
                            Chord* dstChord  = nullptr;

                            if (excerpt() && excerpt()->tracks().key(dstTrack, -1) == -1)
                                  break;

                            // set up destination chord

                            if (dstCR && dstCR->type() == ElementType::CHORD && dstCR->globalDuration() == chord->globalDuration()) {
                                  // existing chord in destination with correct duration;
                                  //   can simply move note in
                                  dstChord = toChord(dstCR);
                                  }

                            else if (dstCR && dstCR->type() == ElementType::REST && dstCR->globalDuration() == chord->globalDuration()) {
                                  // existing rest in destination with correct duration;
                                  //   replace with chord, then move note in
                                  //   this case allows for tuplets, unlike the more general case below
                                  dstChord = new Chord(this);
                                  dstChord->setTrack(dstTrack);
                                  dstChord->setDurationType(chord->durationType());
                                  dstChord->setDuration(chord->duration());
                                  dstChord->setTuplet(dstCR->tuplet());
                                  dstChord->setParent(s);
                                  undoRemoveElement(dstCR);
                                  }

                            else if (!chord->tuplet()) {
                                  // rests or gap in destination
                                  //   insert new chord if the rests / gap are long enough
                                  //   then move note in
                                  ChordRest* pcr = nullptr;
                                  ChordRest* ncr = nullptr;
                                  for (Segment* s2 = m->first(SegmentType::ChordRest); s2; s2 = s2->next()) {
                                        if (s2->segmentType() != SegmentType::ChordRest)
                                              continue;
                                        ChordRest* cr2 = toChordRest(s2->element(dstTrack));
                                        if (!cr2 || cr2->type() == ElementType::REST)
                                              continue;
                                        if (s2->tick() < s->tick()) {
                                              pcr = cr2;
                                              continue;
                                              }
                                        else if (s2->tick() >= s->tick()) {
                                              ncr = cr2;
                                              break;
                                              }
                                        }
                                  int gapStart = pcr ? pcr->tick() + pcr->actualTicks() : m->tick();
                                  int gapEnd = ncr ? ncr->tick() : m->tick() + m->ticks();
                                  if (gapStart <= s->tick() && gapEnd >= s->tick() + chord->actualTicks()) {
                                        // big enough gap found
                                        dstChord = new Chord(this);
                                        dstChord->setTrack(dstTrack);
                                        dstChord->setDurationType(chord->durationType());
                                        dstChord->setDuration(chord->duration());
                                        dstChord->setParent(s);
                                        // makeGapVoice will not back-fill an empty voice
                                        if (voice && !dstCR)
                                              expandVoice(s, /*m->first(SegmentType::ChordRest,*/ dstTrack);
                                        makeGapVoice(s, dstTrack, chord->actualFraction(), s->tick());
                                        }
                                  }

                            // move note to destination chord
                            if (dstChord) {
                                  // create & add new note
                                  Note* newNote = new Note(*note);
                                  newNote->setSelected(false);
                                  newNote->setParent(dstChord);
                                  undoAddElement(newNote);
                                  el.append(newNote);
                                  // add new chord if one was created
                                  if (dstChord != dstCR)
                                        undoAddCR(dstChord, m, s->tick());
                                  // remove original note
                                  if (notes > 1) {
                                        undoRemoveElement(note);
                                        }
                                  else if (notes == 1) {
                                        // create rest to leave behind
                                        Rest* r = new Rest(this);
                                        r->setTrack(chord->track());
                                        r->setDurationType(chord->durationType());
                                        r->setDuration(chord->duration());
                                        r->setTuplet(chord->tuplet());
                                        r->setParent(s);
                                        // if there were grace notes, move them
                                        for (Chord* gc : chord->graceNotes()) {
                                              Chord* ngc = new Chord(*gc);
                                              undoRemoveElement(gc);
                                              ngc->setParent(dstChord);
                                              ngc->setTrack(dstChord->track());
                                              undoAddElement(ngc);
                                              }
                                        // remove chord, replace with rest
                                        undoRemoveElement(chord);
                                        undoAddCR(r, m, s->tick());
                                        }
                                  }
                            }
                      }
                }

          if (!el.empty())
                selection().clear();
          for (Element* e : el)
                select(e, SelectType::ADD, -1);
          setLayoutAll();
          endCmd();
          }

    #if 0
    //---------------------------------------------------------
    //   cropPage - crop a single page score to the content
    ///    margins will be applied on the 4 sides
    //---------------------------------------------------------

    void Score::cropPage(qreal margins)
          {
          if (npages() == 1) {
                Page* page = pages()[0];
                if (page) {
                      QRectF ttbox = page->tbbox();

                      qreal margin = margins / INCH;
                      f.setSize(QSizeF((ttbox.width() / DPI) + 2 * margin, (ttbox.height()/ DPI) + 2 * margin));

                      qreal offset = curFormat->oddLeftMargin() - ttbox.x() / DPI;
                      if (offset < 0)
                            offset = 0.0;
                      f.setOddLeftMargin(margin + offset);
                      f.setEvenLeftMargin(margin + offset);
                      f.setOddBottomMargin(margin);
                      f.setOddTopMargin(margin);
                      f.setEvenBottomMargin(margin);
                      f.setEvenTopMargin(margin);

                      undoChangePageFormat(&f, spatium(), pageNumberOffset());
                      }
                }
          }
    #endif

    //---------------------------------------------------------
    //   getProperty
    //---------------------------------------------------------

    QVariant Score::getProperty(P_ID id) const
          {
          switch (id) {
                default:
                      qDebug("Score::getProperty: unhandled id");
                      return QVariant();
                }
          }

    //---------------------------------------------------------
    //   setProperty
    //---------------------------------------------------------

    bool Score::setProperty(P_ID id, const QVariant& /*v*/)
          {
          switch (id) {
                default:
                      qDebug("Score::setProperty: unhandled id");
                      break;
                }
          setLayoutAll();
          return true;
          }

    //---------------------------------------------------------
    //   propertyDefault
    //---------------------------------------------------------

    QVariant Score::propertyDefault(P_ID id) const
          {
          switch (id) {
                default:
                      return QVariant();
                }
          }

    //---------------------------------------------------------
    //   setStyle
    //---------------------------------------------------------

    void Score::setStyle(const MStyle& s)
          {
          style() = s;
          }

    //---------------------------------------------------------
    //   MasterScore
    //---------------------------------------------------------

    MasterScore::MasterScore()
       : Score()
          {
          _tempomap    = new TempoMap;
          _sigmap      = new TimeSigMap();
          _repeatList  = new RepeatList(this);
          _revisions   = new Revisions;
          setMasterScore(this);

    #if defined(Q_OS_WIN)
          metaTags().insert("platform", "Microsoft Windows");
    #elif defined(Q_OS_MAC)
          metaTags().insert("platform", "Apple Macintosh");
    #elif defined(Q_OS_LINUX)
          metaTags().insert("platform", "Linux");
    #else
          metaTags().insert("platform", "Unknown");
    #endif
          metaTags().insert("movementNumber", "");
          metaTags().insert("movementTitle", "");
          metaTags().insert("workNumber", "");
          metaTags().insert("workTitle", "");
          metaTags().insert("arranger", "");
          metaTags().insert("composer", "");
          metaTags().insert("lyricist", "");
          metaTags().insert("poet", "");
          metaTags().insert("translator", "");
          metaTags().insert("source", "");
          metaTags().insert("copyright", "");
          metaTags().insert("creationDate", QDate::currentDate().toString(Qt::ISODate));
          }

    MasterScore::MasterScore(const MStyle& s)
       : MasterScore{}
          {
          _movements = new Movements;
          _movements->push_back(this);
          setStyle(s);
          }

    MasterScore::~MasterScore()
          {
          delete _revisions;
          delete _repeatList;
          delete _sigmap;
          delete _tempomap;
          qDeleteAll(_excerpts);
          }

    //---------------------------------------------------------
    //   setMovements
    //---------------------------------------------------------

    void MasterScore::setMovements(Movements* m)
          {
          _movements = m;
          if (_movements)
                _movements->push_back(this);
          }

    //---------------------------------------------------------
    //   isSavable
    //---------------------------------------------------------

    bool MasterScore::isSavable() const
          {
          // TODO: check if file can be created if it does not exist
          return fileInfo()->isWritable() || !fileInfo()->exists();
          }

    //---------------------------------------------------------
    //   setTempomap
    //---------------------------------------------------------

    void MasterScore::setTempomap(TempoMap* tm)
          {
          delete _tempomap;
          _tempomap = tm;
          }

    //---------------------------------------------------------
    //   removeOmr
    //---------------------------------------------------------

    void MasterScore::removeOmr()
          {
          _showOmr = false;
    #ifdef OMR
          delete _omr;
    #endif
          _omr = 0;
          }

    //---------------------------------------------------------
    //   setName
    //---------------------------------------------------------

    void MasterScore::setName(const QString& ss)
          {
          QString s(ss);
          s.replace('/', '_');    // for sanity
          if (!(s.endsWith(".mscz") || s.endsWith(".mscx")))
                s += ".mscz";
          info.setFile(s);
          }

    //---------------------------------------------------------
    //   title
    //---------------------------------------------------------

    QString MasterScore::title() const
          {
          return fileInfo()->completeBaseName();
          }

    QString Score::title() const
          {
          return _excerpt->title();
          }

    //---------------------------------------------------------
    //   addRefresh
    //---------------------------------------------------------

    void Score::addRefresh(const QRectF& r)
          {
          _updateState.refresh |= r;
          cmdState().setUpdateMode(UpdateMode::Update);
          }

    //---------------------------------------------------------
    //   staffIdx
    //
    ///  Return index for the first staff of \a part.
    //---------------------------------------------------------

    int Score::staffIdx(const Part* part) const
          {
          int idx = 0;
          for (Part* p : _parts) {
                if (p == part)
                      break;
                idx += p->nstaves();
                }
          return idx;
          }

    //---------------------------------------------------------
    //   setUpdateAll
    //---------------------------------------------------------

    void MasterScore::setUpdateAll()
          {
          _cmdState.setUpdateMode(UpdateMode::UpdateAll);
          }

    //---------------------------------------------------------
    //   setLayoutAll
    //---------------------------------------------------------

    void MasterScore::setLayoutAll()
          {
          _cmdState.setTick(0);
          _cmdState.setTick(measures()->last() ? measures()->last()->endTick() : 0);
          }

    //---------------------------------------------------------
    //   setLayout
    //---------------------------------------------------------

    void MasterScore::setLayout(int t)
          {
          _cmdState.setTick(t);
          }

    //---------------------------------------------------------
    //   isTopScore
    //---------------------------------------------------------

    bool Score::isTopScore() const
          {
          return !(isMaster() && static_cast<const MasterScore*>(this)->prev());
          }

    //---------------------------------------------------------
    //   Movements
    //---------------------------------------------------------

    Movements::Movements()
       : std::vector<MasterScore*>()
          {
          _undo = new UndoStack();
          }

    Movements::~Movements()
          {
          qDeleteAll(_pages);
          delete _undo;
          }

    }

↑ V560 A part of conditional expression is always false: sl == 0.