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

#include "chord.h"
#include "note.h"
#include "xml.h"
#include "style.h"
#include "segment.h"
#include "text.h"
#include "measure.h"
#include "system.h"
#include "tuplet.h"
#include "hook.h"
#include "tie.h"
#include "arpeggio.h"
#include "score.h"
#include "tremolo.h"
#include "glissando.h"
#include "staff.h"
#include "part.h"
#include "utils.h"
#include "articulation.h"
#include "undo.h"
#include "chordline.h"
#include "lyrics.h"
#include "navigate.h"
#include "stafftype.h"
#include "stem.h"
#include "mscore.h"
#include "accidental.h"
#include "noteevent.h"
#include "pitchspelling.h"
#include "stemslash.h"
#include "ledgerline.h"
#include "drumset.h"
#include "key.h"
#include "sym.h"
#include "stringdata.h"
#include "beam.h"
#include "slur.h"
#include "fingering.h"

namespace Ms {

//---------------------------------------------------------
//   LedgerLineData
//---------------------------------------------------------

struct LedgerLineData {
      int   line;
      qreal minX, maxX;
      bool  visible;
      bool  accidental;
      };

//---------------------------------------------------------
//   upNote
//---------------------------------------------------------

Note* Chord::upNote() const
      {
      Q_ASSERT(!_notes.empty());

      Note* result = _notes.back();
      if (!staff())
            return result;

      const Staff* stf = staff();
      const StaffType* st  = stf->staffType(tick());
      if (st->isDrumStaff()) {
            for (Note* n : _notes) {
                  if (n->line() < result->line()) {
                        result = n;
                        }
                  }
            }
      else if (st->isTabStaff()) {
            int line = st->lines() - 1;      // start at bottom line
            int noteLine;
            // scan each note: if TAB strings are not in sequential order,
            // visual order of notes might not correspond to pitch order
            for (Note* n : _notes) {
                  noteLine = st->physStringToVisual(n->string());
                  if (noteLine < line) {
                        line   = noteLine;
                        result = n;
                        }
                  }
            }
      return result;
      }

//---------------------------------------------------------
//   downNote
//---------------------------------------------------------

Note* Chord::downNote() const
      {
      Q_ASSERT(!_notes.empty());

      Note* result = _notes.front();
      if (!staff())
            return result;

      const Staff* stf = staff();
      const StaffType* st  = stf->staffType(tick());
      if (st->isDrumStaff()) {
            for (Note* n : _notes) {
                  if (n->line() > result->line()) {
                        result = n;
                        }
                  }
            }
      else if (st->isTabStaff()) {
            int line        = 0;      // start at top line
            int noteLine;
            // scan each note: if TAB strings are not in sequential order,
            // visual order of notes might not correspond to pitch order
            for (Note* n : _notes) {
                  noteLine = st->physStringToVisual(n->string());
                  if (noteLine > line) {
                        line = noteLine;
                        result = n;
                        }
                  }
            }
      return result;
      }

//---------------------------------------------------------
//   upLine / downLine
//---------------------------------------------------------

int Chord::upLine() const
      {
      return (staff() && staff()->isTabStaff(tick())) ? upString()*2 : upNote()->line();
      }

int Chord::downLine() const
      {
      return (staff() && staff()->isTabStaff(tick())) ? downString()*2 : downNote()->line();
      }

//---------------------------------------------------------
//   upString / downString
//
//    return the topmost / bottommost string used by chord
//    Top and bottom refer to the DRAWN position, not the position in the instrument
//    (i.e., upside-down TAB are taken into account)
//
//    If no staff, always return 0
//    If staf is not a TAB, always returns TOP and BOTTOM staff lines
//---------------------------------------------------------

int Chord::upString() const
      {
      // if no staff or staff not a TAB, return 0 (=topmost line)
      if(!staff() || !staff()->isTabStaff(tick()))
            return 0;
      const Staff* st = staff();
      const StaffType* tab = st->staffType(tick());
      int       line = tab->lines() - 1;      // start at bottom line
      int                     noteLine;
      // scan each note: if TAB strings are not in sequential order,
      // visual order of notes might not correspond to pitch order
      size_t n = _notes.size();
      for (size_t i = 0; i < n; ++i) {
            noteLine = tab->physStringToVisual(_notes.at(i)->string());
            if (noteLine < line)
                  line = noteLine;
            }
      return line;
      }

int Chord::downString() const
      {
      if (!staff())                              // if no staff, return 0
            return 0;
      if (!staff()->isTabStaff(tick()))                // if staff not a TAB, return bottom line
            return staff()->lines(tick())-1;
      const Staff* st = staff();
      const StaffType* tab = st->staffType(tick());
      int line = 0;         // start at top line
      int noteLine;
      size_t n = _notes.size();
      for (size_t i = 0; i < n; ++i) {
            noteLine = tab->physStringToVisual(_notes.at(i)->string());
            if (noteLine > line)
                  line = noteLine;
            }
      return line;
      }

//---------------------------------------------------------
//   Chord
//---------------------------------------------------------

Chord::Chord(Score* s)
   : ChordRest(s)
      {
      _ledgerLines      = 0;
      _stem             = 0;
      _hook             = 0;
      _stemDirection    = Direction::AUTO;
      _arpeggio         = 0;
      _tremolo          = 0;
      _endsGlissando    = false;
      _noteType         = NoteType::NORMAL;
      _stemSlash        = 0;
      _noStem           = false;
      _playEventType    = PlayEventType::Auto;
      _crossMeasure     = CrossMeasure::UNKNOWN;
      _graceIndex   = 0;
      }

Chord::Chord(const Chord& c, bool link)
   : ChordRest(c, link)
      {
      if (link)
            score()->undo(new Link(this, const_cast<Chord*>(&c)));
      _ledgerLines = 0;

      for (Note* onote : c._notes) {
            Note* nnote = new Note(*onote, link);
            add(nnote);
            }
      for (Chord* gn : c.graceNotes()) {
            Chord* nc = new Chord(*gn, link);
            add(nc);
            }
      for (Articulation* a : c._articulations) {    // make deep copy
            Articulation* na = new Articulation(*a);
            if (link)
                  na->linkTo(a);
            na->setParent(this);
            na->setTrack(track());
            _articulations.append(na);
            }
      _stem          = 0;
      _hook          = 0;
      _endsGlissando = false;
      _arpeggio      = 0;
      _stemSlash     = 0;
      _tremolo       = 0;

      _graceIndex     = c._graceIndex;
      _noStem         = c._noStem;
      _playEventType  = c._playEventType;
      _stemDirection  = c._stemDirection;
      _noteType       = c._noteType;
      _crossMeasure   = CrossMeasure::UNKNOWN;

      if (c._stem)
            add(new Stem(*(c._stem)));
      if (c._hook)
            add(new Hook(*(c._hook)));
      if (c._stemSlash)
            add(new StemSlash(*(c._stemSlash)));
      if (c._arpeggio) {
            Arpeggio* a = new Arpeggio(*(c._arpeggio));
            add(a);
            if (link)
                  score()->undo(new Link(a, const_cast<Arpeggio*>(c._arpeggio)));
            }
      if (c._tremolo) {
            Tremolo* t = new Tremolo(*(c._tremolo));
            if (link) {
                  score()->undo(new Link(t, const_cast<Tremolo*>(c._tremolo)));
                  if (c._tremolo->twoNotes()) {
                        if (c._tremolo->chord1() == &c)
                              t->setChords(this, nullptr);
                        else
                              t->setChords(nullptr, this);
                        }
                  }
            add(t);
            }

      for (Element* e : c.el()) {
            if (e->isChordLine()) {
                  ChordLine* cl = toChordLine(e);
                  ChordLine* ncl = new ChordLine(*cl);
                  add(ncl);
                  if (link)
                        score()->undo(new Link(ncl, cl));
                  }
            }
      }

//---------------------------------------------------------
//   undoUnlink
//---------------------------------------------------------

void Chord::undoUnlink()
      {
      ChordRest::undoUnlink();
      for (Note* n : _notes)
            n->undoUnlink();
      for (Chord* gn : graceNotes())
            gn->undoUnlink();
      for (Articulation* a : _articulations)
            a->undoUnlink();
/*      if (_glissando)
            _glissando->undoUnlink(); */
      if (_arpeggio)
            _arpeggio->undoUnlink();
      if (_tremolo && !_tremolo->twoNotes())
            _tremolo->undoUnlink();

      for (Element* e : el()) {
            if (e->type() == ElementType::CHORDLINE)
                  e->undoUnlink();
            }
      }

//---------------------------------------------------------
//   ~Chord
//---------------------------------------------------------

Chord::~Chord()
      {
      qDeleteAll(_articulations);
      delete _arpeggio;
      if (_tremolo && _tremolo->chord1() == this) {
            if (_tremolo->chord2())
                  _tremolo->chord2()->setTremolo(0);
            delete _tremolo;
            }
      delete _stemSlash;
      delete _stem;
      delete _hook;
      for (LedgerLine* ll = _ledgerLines; ll;) {
            LedgerLine* llNext = ll->next();
            delete ll;
            ll = llNext;
            }
      qDeleteAll(_graceNotes);
      qDeleteAll(_notes);
      }

//---------------------------------------------------------
//   noteHeadWidth
//---------------------------------------------------------

qreal Chord::noteHeadWidth() const
      {
      qreal nhw = score()->noteHeadWidth();
      if (_noteType != NoteType::NORMAL)
            nhw *= score()->styleD(Sid::graceNoteMag);
      return nhw * mag();
      }

//---------------------------------------------------------
//   stemPosX
//    return Chord coordinates. Based on nominal notehead
//---------------------------------------------------------

qreal Chord::stemPosX() const
      {
      const Staff* stf = staff();
      const StaffType* st = stf ? stf->staffType(tick()) : 0;
      if (st && st->isTabStaff())
            return st->chordStemPosX(this) * spatium();
      return _up ? noteHeadWidth() : 0.0;
      }

//---------------------------------------------------------
//   stemPos
//    return page coordinates
//---------------------------------------------------------

QPointF Chord::stemPos() const
      {
      QPointF p(pagePos());

      const Staff* stf = staff();
      const StaffType* st = stf ? stf->staffType(tick()) : 0;
      if (st && st->isTabStaff())
            return st->chordStemPos(this) * spatium() + p;

      if (_up) {
            qreal nhw = _notes.size() == 1 ? downNote()->bboxRightPos() : noteHeadWidth();
            p.rx() += nhw;
            p.ry() += downNote()->pos().y();
            }
      else
            p.ry() += upNote()->pos().y();
      return p;
      }

//---------------------------------------------------------
//   stemPosBeam
//    return stem position of note on beam side
//    return page coordinates
//---------------------------------------------------------

QPointF Chord::stemPosBeam() const
      {
      qreal _spatium = spatium();
      QPointF p(pagePos());

      const Staff* stf = staff();
      const StaffType* st = stf ? stf->staffType(tick()) : 0;

      if (st && st->isTabStaff())
            return st->chordStemPosBeam(this) * _spatium + p;

      if (_up) {
            qreal nhw = noteHeadWidth();
            p.rx() += nhw;
            p.ry() += upNote()->pos().y();
            }
      else
            p.ry() += downNote()->pos().y();

      return p;
      }

//---------------------------------------------------------
//   setTremolo
//---------------------------------------------------------

void Chord::setTremolo(Tremolo* tr)
      {
      if (_tremolo && tr && tr == _tremolo)
            return;

      if (_tremolo) {
            if (_tremolo->twoNotes()) {
                  TDuration d;
                  const Fraction f = ticks();
                  if (f.numerator() > 0)
                        d = TDuration(f);
                  else {
                        d = _tremolo->durationType();
                        const int dots = d.dots();
                        d = d.shift(1);
                        d.setDots(dots);
                        }

                  setDurationType(d);
                  Chord* other = _tremolo->chord1() == this ? _tremolo->chord2() : _tremolo->chord1();
                  _tremolo = nullptr;
                  if (other)
                        other->setTremolo(nullptr);
                  }
            else
                  _tremolo = nullptr;
            }

      if (tr) {
            if (tr->twoNotes()) {
                  TDuration d = tr->durationType();
                  if (!d.isValid()) {
                        d = durationType();
                        const int dots = d.dots();
                        d = d.shift(-1);
                        d.setDots(dots);
                        tr->setDurationType(d);
                        }

                  setDurationType(d);
                  Chord* other = tr->chord1() == this ? tr->chord2() : tr->chord1();
                  _tremolo = tr;
                  if (other)
                        other->setTremolo(tr);
                  }
            else
                  _tremolo = tr;
            }
      else {
            _tremolo = nullptr;
            }
      }

//---------------------------------------------------------
//   add
//---------------------------------------------------------

void Chord::add(Element* e)
      {
      e->setParent(this);
      e->setTrack(track());
      switch(e->type()) {
            case ElementType::NOTE:
                  {
                  Note* note = toNote(e);
                  bool found = false;

                  // _notes should be sorted by line position,
                  // but it's often not yet possible since line is unknown
                  // use pitch instead, and line as a second sort criteria.

                  for (unsigned idx = 0; idx < _notes.size(); ++idx) {
                        if (note->pitch() <= _notes[idx]->pitch()) {
                              if (note->pitch() == _notes[idx]->pitch() && note->line() > _notes[idx]->line())
                                    _notes.insert(_notes.begin()+idx+1, note);
                              else
                                    _notes.insert(_notes.begin()+idx, note);
                              found = true;
                              break;
                              }
                        }
                  if (!found)
                        _notes.push_back(note);
                  note->connectTiedNotes();
                  if (voice() && measure() && note->visible())
                        measure()->setHasVoices(staffIdx(), true);
                  }
                  break;
            case ElementType::ARPEGGIO:
                  _arpeggio = toArpeggio(e);
                  break;
            case ElementType::TREMOLO:
                  setTremolo(toTremolo(e));
                  break;
            case ElementType::GLISSANDO:
                  _endsGlissando = true;
                  break;
            case ElementType::STEM:
                  Q_ASSERT(!_stem);
                  _stem = toStem(e);
                  break;
            case ElementType::HOOK:
                  _hook = toHook(e);
                  break;
            case ElementType::CHORDLINE:
                  el().push_back(e);
                  break;
            case ElementType::STEM_SLASH:
                  Q_ASSERT(!_stemSlash);
                  _stemSlash = toStemSlash(e);
                  break;
            case ElementType::CHORD:
                  {
                  Chord* gc = toChord(e);
                  Q_ASSERT(gc->noteType() != NoteType::NORMAL);
                  int idx = gc->graceIndex();
                  gc->setFlag(ElementFlag::MOVABLE, true);
                  _graceNotes.insert(_graceNotes.begin() + idx, gc);
                  }
                  break;
            case ElementType::LEDGER_LINE:
                  qFatal("Chord::add ledgerline");
                  break;
            case ElementType::ARTICULATION:
                  {
                  Articulation* a = toArticulation(e);
                  if (a->layoutCloseToNote()) {
                        auto i = _articulations.begin();
                        while (i != _articulations.end() && (*i)->layoutCloseToNote())
                              i++;
                        _articulations.insert(i, a);
                        }
                  else
                        _articulations.push_back(a);
                  }
                  break;
            default:
                  ChordRest::add(e);
                  break;
            }
      }

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

void Chord::remove(Element* e)
      {
      if (!e)
            return;

      switch (e->type()) {
            case ElementType::NOTE:
                  {
                  Note* note = toNote(e);
                  auto i = std::find(_notes.begin(), _notes.end(), note);
                  if (i != _notes.end()) {
                        _notes.erase(i);
                        note->disconnectTiedNotes();
                        for (Spanner* s : note->spannerBack())
                              note->removeSpannerBack(s);
                        for (Spanner* s : note->spannerFor())
                              note->removeSpannerFor(s);
                        }
                  else
                        qDebug("Chord::remove() note %p not found!", e);
                  if (voice() && measure() && note->visible())
                        measure()->checkMultiVoices(staffIdx());
                  }
                  break;

            case ElementType::ARPEGGIO:
                  _arpeggio = 0;
                  break;
            case ElementType::TREMOLO:
                  setTremolo(nullptr);
                  break;
            case ElementType::GLISSANDO:
                  _endsGlissando = false;
                  break;
            case ElementType::STEM:
                  _stem = 0;
                  break;
            case ElementType::HOOK:
                  _hook = 0;
                  break;
            case ElementType::STEM_SLASH:
                  Q_ASSERT(_stemSlash);
                  if (_stemSlash->selected() && score())
                        score()->deselect(_stemSlash);
                  _stemSlash = 0;
                  break;
            case ElementType::CHORDLINE:
                  el().remove(e);
                  break;
            case ElementType::CHORD:
                  {
                  auto i = std::find(_graceNotes.begin(), _graceNotes.end(), toChord(e));
                  Chord* grace = *i;
                  grace->setGraceIndex(i - _graceNotes.begin());
                  _graceNotes.erase(i);
                  }
                  break;
            case ElementType::ARTICULATION:
                  {
                  Articulation* a = toArticulation(e);
                  if (!_articulations.removeOne(a))
                        qDebug("ChordRest::remove(): articulation not found");
                  }
                  break;
            default:
                  ChordRest::remove(e);
                  break;
            }
      }

//---------------------------------------------------------
//   maxHeadWidth
//---------------------------------------------------------

qreal Chord::maxHeadWidth() const
      {
      // determine max head width in chord
      qreal hw = 0;
      for (const Note* n : _notes) {
            qreal t = n->headWidth();
            if (t > hw)
                  hw = t;
            }
      return hw;
      }

//---------------------------------------------------------
//   addLedgerLines
//---------------------------------------------------------

void Chord::addLedgerLines()
      {
      // initialize for palette
      int track          = 0;                   // the track lines belong to
      // the line pos corresponding to the bottom line of the staff
      int lineBelow      = 8;                   // assuming 5-lined "staff"
      qreal lineDistance = 1;
      qreal mag         = 1;
      bool staffVisible  = true;

      if (segment()) { //not palette
            Fraction tick = segment()->tick();
            int idx       = staffIdx() + staffMove();
            track         = staff2track(idx);
            Staff* st     = score()->staff(idx);
            lineBelow     = (st->lines(tick) - 1) * 2;
            lineDistance  = st->lineDistance(tick);
            mag           = staff()->mag(tick);
            staffVisible  = !staff()->invisible();
            }

      // need ledger lines?
      if (downLine() <= lineBelow + 1 && upLine() >= -1)
            return;

      // the extra length of a ledger line with respect to notehead (half of it on each side)
      qreal extraLen = score()->styleP(Sid::ledgerLineLength) * mag * 0.5;
      qreal hw;
      qreal minX, maxX;                         // note extrema in raster units
      int   minLine, maxLine;
      bool  visible = false;
      qreal x;

      // scan chord notes, collecting visibility and x and y extrema
      // NOTE: notes are sorted from bottom to top (line no. decreasing)
      // notes are scanned twice from outside (bottom or top) toward the staff
      // each pass stops at the first note without ledger lines
      size_t n = _notes.size();
      for (size_t j = 0; j < 2; j++) {             // notes are scanned twice...
            int from, delta;
            vector<LedgerLineData> vecLines;
            hw = 0.0;
            minX  = maxX = 0;
            minLine = 0;
            maxLine = lineBelow;
            if (j == 0) {                       // ...once from lowest up...
                  from  = 0;
                  delta = +1;
                  }
            else {
                  from = int(n)-1;                   // ...once from highest down
                  delta = -1;
                  }
            for (int i = from; i < int(n) && i >= 0 ; i += delta) {
                  Note* note = _notes.at(i);
                  int l = note->line();

                  // if 1st pass and note not below staff or 2nd pass and note not above staff
                  if ((!j && l <= lineBelow + 1) || (j && l >= -1))
                        break;                  // stop this pass
                  // round line number to even number toward 0
                  if (l < 0)
                        l = (l + 1) & ~ 1;
                  else
                        l = l & ~ 1;

                  if (note->visible())          // if one note is visible,
                        visible = true;         // all lines between it and the staff are visible
                  hw = qMax(hw, note->headWidth());

                  //
                  // Experimental:
                  //  shorten ledger line to avoid collisions with accidentals
                  //
                  // bool accid = (note->accidental() && note->line() >= (l-1) && note->line() <= (l+1) );
                  //
                  // TODO : do something with this accid flag in the following code!
                  //

                  // check if note horiz. pos. is outside current range
                  // if more length on the right, increase range
//                  note->layout();

                  //ledger lines need the leftmost point of the notehead with a respect of bbox
                  x = note->pos().x() + note->bboxXShift();
                  if (x - extraLen < minX) {
                        minX  = x - extraLen;
                        // increase width of all lines between this one and the staff
                        for (auto& d : vecLines) {
                              if (!d.accidental && ((l < 0 && d.line >= l) || (l > 0 && d.line <= l)) )
                                    d.minX = minX ;
                              }
                        }
                  // same for left side
                  if (x + hw + extraLen > maxX) {
                        maxX = x + hw + extraLen;
                        for (auto& d : vecLines)
                              if ( (l < 0 && d.line >= l) || (l > 0 && d.line <= l) )
                                    d.maxX = maxX;
                        }

                  LedgerLineData lld;
                  // check if note vert. pos. is outside current range
                  // and, if so, add data for new line(s)
                  if (l < minLine) {
                        for (int i1 = l; i1 < minLine; i1 += 2) {
                              lld.line = i1;
                              if (lineDistance != 1.0)
                                    lld.line *= lineDistance;
                              lld.minX = minX;
                              lld.maxX = maxX;
                              lld.visible = visible;
                              lld.accidental = false;
                              vecLines.push_back(lld);
                              }
                        minLine = l;
                        }
                  if (l > maxLine) {
                        for (int i1 = maxLine+2; i1 <= l; i1 += 2) {
                              lld.line = i1;
                              if (lineDistance != 1.0)
                                    lld.line *= lineDistance;
                              lld.minX = minX;
                              lld.maxX = maxX;
                              lld.visible = visible;
                              lld.accidental = false;
                              vecLines.push_back(lld);
                              }
                        maxLine = l;
                        }
                  }
            if (minLine < 0 || maxLine > lineBelow) {
                  qreal _spatium = spatium();
                  qreal stepDistance = 0.5;     // staff() ? staff()->lineDistance() * 0.5 : 0.5;
                  for (auto lld : vecLines) {
                        LedgerLine* h = new LedgerLine(score());
                        h->setParent(this);
                        h->setTrack(track);
                        h->setVisible(lld.visible && staffVisible);
                        h->setLen(lld.maxX - lld.minX);
                        h->setPos(lld.minX, lld.line * _spatium * stepDistance);
                        h->setNext(_ledgerLines);
                        _ledgerLines = h;
                        }
                  }
            }
      for (LedgerLine* ll = _ledgerLines; ll; ll = ll->next())
            ll->layout();
      }

//-----------------------------------------------------------------------------
//   computeUp
//    rules:
//      single note:
//          All notes beneath the middle line: upward stems
//          All notes on or above the middle line: downward stems
//      two notes:
//          If the interval above the middle line is greater than the interval
//             below the middle line: downward stems
//          If the interval below the middle line is greater than the interval
//             above the middle line: upward stems
//          If the two notes are the same distance from the middle line:
//             stem can go in either direction. but most engravers prefer
//             downward stems
//      > two notes:
//          If the interval of the highest note above the middle line is greater
//             than the interval of the lowest note below the middle line:
//             downward stems
//          If the interval of the lowest note below the middle line is greater
//             than the interval of the highest note above the middle line:
//             upward stem
//          If the highest and the lowest notes are the same distance from
//          the middle line:, use these rules to determine stem direction:
//             - If the majority of the notes are above the middle:
//               downward stems
//             - If the majority of the notes are below the middle:
//               upward stems
//    TABlatures:
//       stems beside staves:
//          All stems are up / down according to TAB::stemsDown() setting
//       stems through staves:
//          Same rules as per pitched staves
//-----------------------------------------------------------------------------

void Chord::computeUp()
      {
      Q_ASSERT(!_notes.empty());
      const Staff* st = staff();
      const StaffType* tab = st ? st->staffType(tick()) : 0;
      bool tabStaff  = tab && tab->isTabStaff();
      // TAB STAVES
      if (tabStaff) {
            // if no stems or stem beside staves
            if (tab->slashStyle() || !tab->stemThrough()) {
                  // if measure has voices, set stem direction according to voice
                  if (measure()->hasVoices(staffIdx()))
                        _up = !(track() % 2);
                  else                          // if only voice 1,
                        // unconditionally set to down if not stems or according to TAB stem direction otherwise
                        // (even with no stems, stem direction controls position of slurs and ties)
                        _up = tab->slashStyle() ? false : !tab->stemsDown();
                  return;
                  }
            // if TAB has stems through staves, chain into standard processing
            }

      // PITCHED STAVES (or TAB with stems through staves)
      if (_stemDirection != Direction::AUTO)
            _up = _stemDirection == Direction::UP;
      else if (!parent())
            // hack for palette and drumset editor
            _up = upNote()->line() > 4;
      else if (_noteType != NoteType::NORMAL) {
            //
            // stem direction for grace notes
            //
            if (measure()->hasVoices(staffIdx()))
                  _up = !(track() % 2);
            else
                  _up = true;
            }
      else if (staffMove())
            _up = staffMove() > 0;
      else if (measure()->hasVoices(staffIdx()))
            _up = !(track() % 2);
      else {
            int   dnMaxLine   = staff()->middleLine(tick());
            int   ud          = (tabStaff ? upString() * 2 : upNote()->line() ) - dnMaxLine;
            // standard case: if only 1 note or cross beaming
            if (_notes.size() == 1 || staffMove()) {
                  if (staffMove() > 0)
                        _up = true;
                  else if (staffMove() < 0)
                        _up = false;
                  else
                        _up = ud > 0;
                  }
            // if more than 1 note, compare extrema (topmost and bottommost notes)
            else {
                  int dd = (tabStaff ? downString() * 2 : downNote()->line() ) - dnMaxLine;
                  // if extrema symmetrical, average directions of intermediate notes
                  if (-ud == dd) {
                        int up = 0;
                        size_t n = _notes.size();
                        for (size_t i = 0; i < n; ++i) {
                              const Note* currentNote = _notes.at(i);
                              int l = tabStaff ? currentNote->string() * 2 : currentNote->line();
                              if (l <= dnMaxLine)
                                    --up;
                              else
                                    ++up;
                              }
                        _up = up > 0;
                        }
                  // if extrema not symmetrical, set _up to prevailing
                  else
                        _up = dd > -ud;
                  }
            }
      }

//---------------------------------------------------------
//   selectedNote
//---------------------------------------------------------

Note* Chord::selectedNote() const
      {
      Note* note = 0;
      size_t n = _notes.size();
      for (size_t i = 0; i < n; ++i) {
            Note* currentNote = _notes.at(i);
            if (currentNote->selected()) {
                  if (note)
                        return 0;
                  note = currentNote;
                  }
            }
      return note;
      }

//---------------------------------------------------------
//   Chord::write
//---------------------------------------------------------

void Chord::write(XmlWriter& xml) const
      {
      for (Chord* c : _graceNotes) {
            c->write(xml);
            }
      writeBeam(xml);
      xml.stag(this);
      ChordRest::writeProperties(xml);
      for (const Articulation* a : _articulations)
            a->write(xml);
      switch (_noteType) {
            case NoteType::NORMAL:
                  break;
            case NoteType::ACCIACCATURA:
                  xml.tagE("acciaccatura");
                  break;
            case NoteType::APPOGGIATURA:
                  xml.tagE("appoggiatura");
                  break;
            case NoteType::GRACE4:
                  xml.tagE("grace4");
                  break;
            case NoteType::GRACE16:
                  xml.tagE("grace16");
                  break;
            case NoteType::GRACE32:
                  xml.tagE("grace32");
                  break;
            case NoteType::GRACE8_AFTER:
                  xml.tagE("grace8after");
                  break;
            case NoteType::GRACE16_AFTER:
                  xml.tagE("grace16after");
                  break;
            case NoteType::GRACE32_AFTER:
                  xml.tagE("grace32after");
                  break;
            default:
                  break;
            }

      if (_noStem)
            xml.tag("noStem", _noStem);
      else if (_stem && (_stem->isUserModified() || (_stem->userLen() != 0.0)))
            _stem->write(xml);
      if (_hook && _hook->isUserModified())
            _hook->write(xml);
      if (_stemSlash && _stemSlash->isUserModified())
            _stemSlash->write(xml);
      writeProperty(xml, Pid::STEM_DIRECTION);
      for (Note* n : _notes)
            n->write(xml);
      if (_arpeggio)
            _arpeggio->write(xml);
      if (_tremolo && tremoloChordType() != TremoloChordType::TremoloSecondNote)
            _tremolo->write(xml);
      for (Element* e : el())
            e->write(xml);
      xml.etag();
      }

//---------------------------------------------------------
//   Chord::read
//---------------------------------------------------------

void Chord::read(XmlReader& e)
      {
      while (e.readNextStartElement()) {
            if (readProperties(e))
                  ;
            else
                  e.unknown();
            }
      }

//---------------------------------------------------------
//   readProperties
//---------------------------------------------------------

bool Chord::readProperties(XmlReader& e)
      {
      const QStringRef& tag(e.name());

      if (tag == "Note") {
            Note* note = new Note(score());
            // the note needs to know the properties of the track it belongs to
            note->setTrack(track());
            note->setChord(this);
            note->read(e);
            add(note);
            }
      else if (ChordRest::readProperties(e))
            ;
      else if (tag == "Stem") {
            Stem* s = new Stem(score());
            s->read(e);
            add(s);
            }
      else if (tag == "Hook") {
            _hook = new Hook(score());
            _hook->read(e);
            add(_hook);
            }
      else if (tag == "appoggiatura") {
            _noteType = NoteType::APPOGGIATURA;
            e.readNext();
            }
      else if (tag == "acciaccatura") {
            _noteType = NoteType::ACCIACCATURA;
            e.readNext();
            }
      else if (tag == "grace4") {
            _noteType = NoteType::GRACE4;
            e.readNext();
            }
      else if (tag == "grace16") {
            _noteType = NoteType::GRACE16;
            e.readNext();
            }
      else if (tag == "grace32") {
            _noteType = NoteType::GRACE32;
            e.readNext();
            }
      else if (tag == "grace8after") {
            _noteType = NoteType::GRACE8_AFTER;
            e.readNext();
            }
      else if (tag == "grace16after") {
            _noteType = NoteType::GRACE16_AFTER;
            e.readNext();
            }
      else if (tag == "grace32after") {
            _noteType = NoteType::GRACE32_AFTER;
            e.readNext();
            }
      else if (tag == "StemSlash") {
            StemSlash* ss = new StemSlash(score());
            ss->read(e);
            add(ss);
            }
      else if (readProperty(tag, e, Pid::STEM_DIRECTION))
            ;
      else if (tag == "noStem")
            _noStem = e.readInt();
      else if (tag == "Arpeggio") {
            _arpeggio = new Arpeggio(score());
            _arpeggio->setTrack(track());
            _arpeggio->read(e);
            _arpeggio->setParent(this);
            }
      else if (tag == "Tremolo") {
            _tremolo = new Tremolo(score());
            _tremolo->setTrack(track());
            _tremolo->read(e);
            _tremolo->setParent(this);
            _tremolo->setDurationType(durationType());
            }
      else if (tag == "tickOffset")       // obsolete
            ;
      else if (tag == "ChordLine") {
            ChordLine* cl = new ChordLine(score());
            cl->read(e);
            add(cl);
            }
      else
            return false;
      return true;
      }

//---------------------------------------------------------
//   upPos
//---------------------------------------------------------

qreal Chord::upPos() const
      {
      return upNote()->pos().y();
      }

//---------------------------------------------------------
//   downPos
//---------------------------------------------------------

qreal Chord::downPos() const
      {
      return downNote()->pos().y();
      }

//---------------------------------------------------------
//   centerX
//    return x position for attributes
//---------------------------------------------------------

qreal Chord::centerX() const
      {
      // TAB 'notes' are always centered on the stem
      const Staff* st = staff();
      if (st->isTabStaff(tick()))
            return st->staffType(tick())->chordStemPosX(this) * spatium();

      const Note* note = up() ? upNote() : downNote();
      qreal x = note->pos().x() + note->noteheadCenterX();
      if (note->mirror())
                  x += (note->headBodyWidth()) * (up() ? -1.0 : 1.0);
      return x;
      }

//---------------------------------------------------------
//   scanElements
//---------------------------------------------------------

void Chord::scanElements(void* data, void (*func)(void*, Element*), bool all)
      {
      for (Articulation* a : _articulations)
            func(data, a);
      if (_hook)
            func(data, _hook );
      if (_stem)
            func(data, _stem);
      if (_stemSlash)
            func(data, _stemSlash);
      if (_arpeggio)
            func(data, _arpeggio);
      if (_tremolo && (tremoloChordType() != TremoloChordType::TremoloSecondNote))
            func(data, _tremolo);
      const Staff* st = staff();
      if ((st && st->showLedgerLines(tick())) || !st)       // also for palette
            for (LedgerLine* ll = _ledgerLines; ll; ll = ll->next())
                  func(data, ll);
      size_t n = _notes.size();
      for (size_t i = 0; i < n; ++i)
            _notes.at(i)->scanElements(data, func, all);
      for (Chord* chord : _graceNotes)
            chord->scanElements(data, func, all);
      for (Element* e : el())
            e->scanElements(data, func, all);
      ChordRest::scanElements(data, func, all);
      }

//---------------------------------------------------------
//   processSiblings
//---------------------------------------------------------

void Chord::processSiblings(std::function<void(Element*)> func) const
      {
      if (_hook)
            func(_hook);
      if (_stem)
            func(_stem);
      if (_stemSlash)
            func(_stemSlash);
      if (_arpeggio)
            func(_arpeggio);
      if (_tremolo)
            func(_tremolo);
      for (LedgerLine* ll = _ledgerLines; ll; ll = ll->next())
            func(ll);
      for (Articulation* a : _articulations)
            func(a);
      for (Note* note : _notes)
            func(note);
      for (Element* e : el())
            func(e);
      for (Chord* chord : _graceNotes)    // process grace notes last, needed for correct shape calculation
            func(chord);
      }

//---------------------------------------------------------
//   setTrack
//---------------------------------------------------------

void Chord::setTrack(int val)
      {
      ChordRest::setTrack(val);
      processSiblings([val] (Element* e) { e->setTrack(val); } );
      }

//---------------------------------------------------------
//   setScore
//---------------------------------------------------------

void Chord::setScore(Score* s)
      {
      ChordRest::setScore(s);
      processSiblings([s] (Element* e) { e->setScore(s); } );
      }

//-----------------------------------------------------------------------------
//   hookAdjustment
//    Adjustment to the length of the stem in order to accomodate hooks
//    This function replaces this bit of code:
//      switch (hookIdx) {
//            case 3: normalStemLen += small() ? .5  : 0.75; break; //32nd notes
//            case 4: normalStemLen += small() ? 1.0 : 1.5;  break; //64th notes
//            case 5: normalStemLen += small() ? 1.5 : 2.25; break; //128th notes
//            }
//    which was not sufficient for two reasons:
//      1. It only lengthened the stem for 3, 4, or 5 hooks.
//      2. It was too general to produce good results for all combinations of factors.
//    This provides a way to take a number of factors into account. Further tweaking may be in order.
//-----------------------------------------------------------------------------

qreal hookAdjustment(QString font, int hooks, bool up, bool small)
      {
      bool fallback = MScore::useFallbackFont && (hooks > 5);

      if (font == "Emmentaler" && !fallback) {
            if (up) {
                  if (hooks > 2)
                        return (hooks - 2) * (small ? .75 : 1);
                  }
            else {
                  if (hooks == 3)
                        return (small ? .75 : 1);
                  else if (hooks > 3)
                        return (hooks - 2) * (small ? .5 : .75);
                  }
            }
      else if (font == "Gonville" && !fallback) {
            if (up) {
                  if (hooks > 2)
                        return (hooks - 2) * (small ? .5 : .75);
                  }
            else {
                  if (hooks > 1)
                        return (hooks - 1) * (small ? .5 : .75);
                  }
            }
      else if (font == "MuseJazz") {
            if (hooks > 2)
                  return (hooks - 2) * (small ? .75 : 1);
            }
      else {
            if (hooks > 2)
                  return (hooks - 2) * (small ? .5 : .75);
            }
      return 0;
      }

//-----------------------------------------------------------------------------
//   defaultStemLength
///   Get the default stem length for this chord
//-----------------------------------------------------------------------------

qreal Chord::defaultStemLength() const
      {
      Note* downnote;
      qreal stemLen;
      qreal _spatium     = spatium();
      int hookIdx        = durationType().hooks();
      downnote           = downNote();
      int ul             = upLine();
      int dl             = downLine();
      const Staff* st    = staff();
      qreal lineDistance = st ? st->lineDistance(tick()) : 1.0;

      const StaffType* tab = (st && st->isTabStaff(tick())) ? st->staffType(tick()) : nullptr;
      if (tab) {
            // require stems only if TAB is not stemless and this chord has a stem
            if (!tab->slashStyle() && _stem) {
                  // if stems are beside staff, apply special formatting
                  if (!tab->stemThrough()) {
                        // process stem:
                        return tab->chordStemLength(this) * _spatium;
                        }
                  }
            }
      else if (lineDistance != 1.0) {
            // convert to actual distance from top of staff in sp
            // ul *= lineDistance;
            // dl *= lineDistance;
            }

      if (tab && !tab->onLines()) {       // if TAB and frets above strings, move 1 position up
            --ul;
            --dl;
            }
      bool shortenStem = score()->styleB(Sid::shortenStem);
      if (hookIdx >= 2 || _tremolo)
            shortenStem = false;

      Spatium progression = score()->styleS(Sid::shortStemProgression);
      qreal shortest      = score()->styleS(Sid::shortestStem).val();
      if (hookIdx) {
            if (up()) {
                  if (shortest < 3.0)
                        shortest = 3.0;
                  }
            else {
                  if (shortest < 3.5)
                        shortest = 3.5;
                  }
            }

      qreal normalStemLen = small() ? 2.5 : 3.5;
      normalStemLen += hookAdjustment(score()->styleSt(Sid::MusicalSymbolFont), hookIdx, up(), small());
      if (hookIdx && tab == 0) {
            if (up() && durationType().dots()) {
                  //
                  // avoid collision of dot with hook
                  //
                  if (!(ul & 1))
                        normalStemLen += .5;
                  shortenStem = false;
                  }
            }

      if (isGrace()) {
            // grace notes stems are not subject to normal
            // stem rules
            stemLen =  qAbs(ul - dl) * .5;
            stemLen += normalStemLen * score()->styleD(Sid::graceNoteMag);
            if (up())
                  stemLen *= -1;
            }
      else {
            // normal note (not grace)
            qreal staffHeight = st ? st->lines(tick()) - 1 : 4;
            if (!tab)
                  staffHeight *= lineDistance;
            qreal staffHlfHgt = staffHeight * 0.5;
            if (up()) {                   // stem up
                  qreal dy  = dl * .5;                      // note-side vert. pos.
                  qreal sel = ul * .5 - normalStemLen;      // stem end vert. pos

                  // if stem ends above top line (with some exceptions), shorten it
                  if (shortenStem && (sel < 0.0) && (hookIdx == 0 || tab || !downnote->mirror()))
                        sel -= sel  * progression.val();
                  if (sel > staffHlfHgt)                    // if stem ends below ('>') staff mid position,
                        sel = staffHlfHgt;                  // stretch it to mid position
                  stemLen = sel - dy;                       // actual stem length
                  if (-stemLen < shortest)                  // is stem too short,
                        stemLen = -shortest;                // lengthen it to shortest possible length
                  }
            else {                        // stem down
                  qreal uy  = ul * .5;                      // note-side vert. pos.
                  qreal sel = dl * .5 + normalStemLen;      // stem end vert. pos.

                  // if stem ends below bottom line (with some exceptions), shorten it
                  if (shortenStem && (sel > staffHeight) && (hookIdx == 0 || tab || downnote->mirror()))
                        sel -= (sel - staffHeight)  * progression.val();
                  if (sel < staffHlfHgt)                    // if stem ends above ('<') staff mid position,
                        sel = staffHlfHgt;                  // stretch it to mid position
                  stemLen = sel - uy;                       // actual stem length
                  if (stemLen < shortest)                   // if stem too short,
                        stemLen = shortest;                 // lengthen it to shortest possible position
                  }
            }

      // adjust stem len for tremolo
      if (_tremolo && !_tremolo->twoNotes() && !_tremolo->placeMidStem()) {
            // Use the old algorithm for stem lengthening. It not always
            // optimal but still performs better when not placing the tremolo
            // at stem middle. TODO: rework minAbsStemLen() to perform
            // correctly in this case too.

            // hook up odd lines
            static const int tab1[2][2][2][4] = {
                  { { { 0, 0, 0,  1 },  // stem - down - even - lines
                      { 0, 0, 0,  2 }   // stem - down - odd - lines
                      },
                    { { 0, 0, 0, -1 },  // stem - up - even - lines
                      { 0, 0, 0, -2 }   // stem - up - odd - lines
                      }
                    },
                  { { { 0, 0, 1, 2 },   // hook - down - even - lines
                      { 0, 0, 1, 2 }    // hook - down - odd - lines
                      },
                    { { 0, 0, -1, -2 }, // hook - up - even - lines
                      { 0, 0, -1, -2 }  // hook - up - odd - lines
                      }
                    }
                  };
            int odd = (up() ? upLine() : downLine()) & 1;
            int n = tab1[hookIdx ? 1 : 0][up() ? 1 : 0][odd][_tremolo->lines()-1];
            stemLen += n * .5;
            }

      if (tab)
            stemLen *= lineDistance;

      const qreal sgn = up() ? -1.0 : 1.0;
      qreal stemLenPoints = stemLen * _spatium;
      const qreal minAbsStemLen = minAbsStemLength();
      if (sgn * stemLenPoints < minAbsStemLen)
            stemLenPoints = sgn * minAbsStemLen;

      return stemLenPoints;
      }

//---------------------------------------------------------
//   minAbsStemLength
//---------------------------------------------------------

qreal Chord::minAbsStemLength() const
      {
      if (!_tremolo || _tremolo->twoNotes() || !_tremolo->placeMidStem())
            return 0.0;

      int beamLvl = beams();
      const bool hasHook = (beamLvl > 0) && !beam();
      if (hasHook)
            ++beamLvl; // reserve more space for stem with both hook and tremolo
      const qreal beamDist = beam() ? beam()->beamDist() : (0.5 * spatium());
      const qreal tremoloSpacing = 0.5 * spatium(); // TODO: style setting

      return beamLvl * beamDist + _tremolo->height() + 2 * tremoloSpacing;
      }

//---------------------------------------------------------
//   layoutStem1
///   Layout _stem and _stemSlash
//
//    Called before layout spacing of notes.
//    Create stem if necessary.
//---------------------------------------------------------

void Chord::layoutStem1()
      {
      const Staff* stf = staff();
      const StaffType* st = stf ? stf->staffType(tick()) : 0;
      if (durationType().hasStem() && !(_noStem || (measure() && measure()->slashStyle(staffIdx())) || (st && st->isTabStaff() && st->slashStyle()))) {
            if (!_stem) {
                  Stem* stem = new Stem(score());
                  stem->setParent(this);
                  stem->setGenerated(true);
                  score()->undoAddElement(stem);
                  }
            if ((_noteType == NoteType::ACCIACCATURA) && !(beam() && beam()->elements().front() != this)) {
                  if (!_stemSlash)
                        add(new StemSlash(score()));
                  }
            else if (_stemSlash)
                  remove(_stemSlash);

            qreal stemWidth5 = _stem->lineWidth() * .5 * mag();
            _stem->rxpos()   = stemPosX() + (up() ? -stemWidth5 : +stemWidth5);
            _stem->setLen(defaultStemLength());
            }
      else {
            if (_stem)
                  score()->undoRemoveElement(_stem);
            if (_stemSlash)
                  score()->undoRemoveElement(_stemSlash);
            }
      }

//-----------------------------------------------------------------------------
//   layoutStem
///   Layout chord tremolo stem and hook.
//
//    hook: sets position
//-----------------------------------------------------------------------------

void Chord::layoutStem()
      {
      for (Chord* c : _graceNotes)
            c->layoutStem();
      if (_beam)
            return;

      // create hooks for unbeamed chords

      int hookIdx  = durationType().hooks();

      if (hookIdx && !(noStem() || measure()->slashStyle(staffIdx()))) {
            if (!hook()) {
                  Hook* hook = new Hook(score());
                  hook->setParent(this);
                  hook->setGenerated(true);
                  score()->undoAddElement(hook);
                  }
            hook()->setHookType(up() ? hookIdx : -hookIdx);
            }
      else if (hook())
            score()->undoRemoveElement(hook());

      //
      // TAB
      //
      const Staff* st = staff();
      const StaffType* tab = st ? st->staffType(tick()) : 0;
      if (tab && tab->isTabStaff()) {
            // if stemless TAB
            if (tab->slashStyle()) {
                  // if 'grid' duration symbol of MEDIALFINAL type, it is time to compute its width
                  if (_tabDur != nullptr && _tabDur->beamGrid() == TabBeamGrid::MEDIALFINAL)
                        _tabDur->layout2();
                  // in all other stemless cases, do nothing
                  return;
                  }
            // not a stemless TAB; if stems are beside staff, apply special formatting
            if (!tab->stemThrough()) {
                  if (_stem) { // (duplicate code with defaultStemLength())
                        // process stem:
                        _stem->setLen(tab->chordStemLength(this) * spatium());
                        // process hook
                        hookIdx = durationType().hooks();
                        if (!up())
                              hookIdx = -hookIdx;
                        if (hookIdx && _hook) {
                              _hook->setHookType(hookIdx);
#if 0
                              _hook->layout();
                              QPointF p(_stem->hookPos());
                              if (up()) {
                                    p.ry() -= _hook->bbox().top();
                                    p.rx() -= _stem->width();
                                    }
                              else {
                                    p.ry() -= _hook->bbox().bottom();
                                    p.rx() -= _stem->width();
                                    }
                              _hook->setPos(p);
#endif
                              }
                        }
                  return;
                  }
            // if stems are through staff, use standard formatting
            }

      //
      // NON-TAB (or TAB with stems through staff)
      //
      if (_stem) {
            if (_hook) {
                  _hook->layout();
                  QPointF p(_stem->hookPos());
                  if (up()) {
                        p.ry() -= _hook->bbox().top();
                        p.rx() -= _stem->width();
                        }
                  else {
                        p.ry() -= _hook->bbox().bottom();
                        p.rx() -= _stem->width();
                        }
                  _hook->setPos(p);
                  }
            if (_stemSlash)
                  _stemSlash->layout();
            }

      //-----------------------------------------
      //    process tremolo
      //-----------------------------------------

//      if (_tremolo)
//            _tremolo->layout();
      }

//---------------------------------------------------------
//    underBeam: true, if grace note is placed under a beam.
//---------------------------------------------------------

bool Chord::underBeam() const
      {
      if (_noteType == NoteType::NORMAL)
          return false;
      const Chord* cr = toChord(parent());
      Beam* beam = cr->beam();
      if(!beam || !cr->beam()->up())
            return false;
      int s = beam->elements().count();
      if (isGraceBefore()){
            if (beam->elements()[0] != cr)
                return true;
            }
      if (isGraceAfter()){
            if (beam->elements()[s - 1] != cr)
                return true;
            }
      return false;
      }

//---------------------------------------------------------
//   layout2
//    Called after horizontal positions of all elements
//    are fixed.
//---------------------------------------------------------

void Chord::layout2()
      {
      for (Chord* c : _graceNotes)
            c->layout2();

      qreal mag = staff()->mag(tick());

      //
      // position after-chord grace notes
      // room for them has been reserved in Chord::layout()
      //

      QVector<Chord*> gna = graceNotesAfter();
      if (!gna.empty()) {
            qreal minNoteDist = score()->styleP(Sid::minNoteDistance) * mag * score()->styleD(Sid::graceNoteMag);
            // position grace notes from the rightmost to the leftmost
            // get segment (of whatever type) at the end of this chord; if none, get measure last segment
            Segment* s = measure()->tick2segment(segment()->tick() + actualTicks(), SegmentType::All);
            if (s == nullptr)
                  s = measure()->last();
            if (s == segment())           // if our segment is the last, no adjacent segment found
                  s = nullptr;
            // start from the right (if next segment found, x of it relative to this chord;
            // chord right space otherwise)
            Chord* last = gna.last();
            qreal xOff =  s ? (s->pos().x() - s->staffShape(last->vStaffIdx()).left()) - (segment()->pos().x() + pos().x()) : _spaceRw;
            // final distance: if near to another chord, leave minNoteDist at right of last grace
            // else leave note-to-barline distance;
            xOff -= (s != nullptr && s->segmentType() != SegmentType::ChordRest)
                  ? score()->styleP(Sid::noteBarDistance) * mag
                  : minNoteDist;
            // scan grace note list from the end
            int n = gna.size();
            for (int i = n-1; i >= 0; i--) {
                  Chord* g = gna.value(i);
                  xOff -= g->_spaceRw;                  // move to left by grace note left space (incl. grace own width)
                  g->rxpos() = xOff;
                  xOff -= minNoteDist + g->_spaceLw;    // move to left by grace note right space and inter-grace distance
                  }
            }
      }

//---------------------------------------------------------
//   updatePercussionNotes
//---------------------------------------------------------

static void updatePercussionNotes(Chord* c, const Drumset* drumset)
      {
      for (Chord* ch : c->graceNotes())
            updatePercussionNotes(ch, drumset);
      std::vector<Note*> lnotes(c->notes());  // we need a copy!
      for (Note* note : lnotes) {
            if (!drumset)
                  note->setLine(0);
            else {
                  int pitch = note->pitch();
                  if (!drumset->isValid(pitch)) {
                        note->setLine(0);
                        qWarning("unmapped drum note %d", pitch);
                        }
                  else if (!note->fixed()) {
                        note->undoChangeProperty(Pid::HEAD_GROUP, int(drumset->noteHead(pitch)));
                        note->setLine(drumset->line(pitch));
                        }
                  }
            }
      }

//---------------------------------------------------------
//   cmdUpdateNotes
//---------------------------------------------------------

void Chord::cmdUpdateNotes(AccidentalState* as)
      {
      // TAB_STAFF is different, as each note has to be fretted
      // in the context of the all of the chords of the whole segment

      const Staff* st = staff();
      StaffGroup staffGroup = st->staffType(tick())->group();
      if (staffGroup == StaffGroup::TAB) {
            const Instrument* instrument = part()->instrument();
            for (Chord* ch : graceNotes())
                  instrument->stringData()->fretChords(ch);
            instrument->stringData()->fretChords(this);
            return;
            }

      // PITCHED_ and PERCUSSION_STAFF can go note by note

      if (staffGroup == StaffGroup::STANDARD) {
            for (Chord* ch : graceNotesBefore()) {
                  std::vector<Note*> notes(ch->notes());  // we need a copy!
                  for (Note* note : notes)
                        note->updateAccidental(as);
                  ch->sortNotes();
                  }
            std::vector<Note*> lnotes(notes());  // we need a copy!
            for (Note* note : lnotes) {
                  if (note->tieBack() && note->tpc() == note->tieBack()->startNote()->tpc()) {
                        // same pitch
                        if (note->accidental() && note->accidental()->role() == AccidentalRole::AUTO) {
                              // not courtesy
                              // TODO: remove accidental only if note is not
                              // on new system
                              score()->undoRemoveElement(note->accidental());
                              }
                        }
                  note->updateAccidental(as);
                  }
            for (Chord* ch : graceNotesAfter()) {
                  std::vector<Note*> notes(ch->notes());  // we need a copy!
                  for (Note* note : notes)
                        note->updateAccidental(as);
                  ch->sortNotes();
                  }
            }
      else if (staffGroup == StaffGroup::PERCUSSION) {
            const Instrument* instrument = part()->instrument();
            const Drumset* drumset = instrument->drumset();
            if (!drumset)
                  qWarning("no drumset");
            updatePercussionNotes(this, drumset);
            }

      sortNotes();
      }

//---------------------------------------------------------
//   pagePos
//---------------------------------------------------------

QPointF Chord::pagePos() const
      {
      if (isGrace()) {
            QPointF p(pos());
            if (parent() == 0)
                  return p;
            p.rx() = pageX();

            const Chord* pc = static_cast<const Chord*>(parent());
            System* system = pc->segment()->system();
            if (!system)
                  return p;
            p.ry() += system->staffYpage(vStaffIdx());
            return p;
            }
      return Element::pagePos();
      }

//---------------------------------------------------------
//   layout
//---------------------------------------------------------

void Chord::layout()
      {
      if (_notes.empty())
            return;
      if (staff() && staff()->isTabStaff(tick()))
            layoutTablature();
      else
            layoutPitched();
      }

//---------------------------------------------------------
//   layoutPitched
//---------------------------------------------------------

void Chord::layoutPitched()
      {
      int gi = 0;
      for (Chord* c : _graceNotes) {
            // HACK: graceIndex is not well-maintained on add & remove
            // so rebuild now
            c->setGraceIndex(gi++);
            if (c->isGraceBefore())
                  c->layoutPitched();
            }
      QVector<Chord*> graceNotesBefore = Chord::graceNotesBefore();
      int gnb = graceNotesBefore.size();

      // lay out grace notes after separately so they are processed left to right
      // (they are normally stored right to left)

      QVector<Chord*> gna = graceNotesAfter();
      for (Chord* c : gna)
            c->layoutPitched();

      qreal _spatium         = spatium();
      qreal mag_             = staff() ? staff()->mag(tick()) : 1.0;    // palette elements do not have a staff
      qreal dotNoteDistance  = score()->styleP(Sid::dotNoteDistance)  * mag_;
      qreal minNoteDistance  = score()->styleP(Sid::minNoteDistance)  * mag_;
      qreal minTieLength     = score()->styleP(Sid::MinTieLength)     * mag_;

      qreal graceMag         = score()->styleD(Sid::graceNoteMag);
      qreal chordX           = (_noteType == NoteType::NORMAL) ? ipos().x() : 0.0;

      while (_ledgerLines) {
            LedgerLine* l = _ledgerLines->next();
            delete _ledgerLines;
            _ledgerLines = l;
            }

      qreal lll    = 0.0;         // space to leave at left of chord
      qreal rrr    = 0.0;         // space to leave at right of chord
      qreal lhead  = 0.0;         // amount of notehead to left of chord origin
      Note* upnote = upNote();

      delete _tabDur;   // no TAB? no duration symbol! (may happen when converting a TAB into PITCHED)
      _tabDur = 0;

      if (!segment()) {
            //
            // hack for use in palette
            //
            size_t n = _notes.size();
            for (size_t i = 0; i < n; i++) {
                  Note* note = _notes.at(i);
                  note->layout();
                  qreal x = 0.0;
                  qreal y = note->line() * _spatium * .5;
                  note->setPos(x, y);
                  }
            computeUp();
            layoutStem1();
            if (_stem) { //false when dragging notes from drum palette
                  qreal stemWidth5 = _stem->lineWidth() * .5;
                  _stem->rxpos()   = up() ? (upNote()->headBodyWidth() - stemWidth5) : stemWidth5;
                  }
            addLedgerLines();
            return;
            }

      //-----------------------------------------
      //  process notes
      //-----------------------------------------

      for (Note* note : _notes) {
            note->layout();

            qreal x1 = note->pos().x() + chordX;
            qreal x2 = x1 + note->headWidth();
            lll      = qMax(lll, -x1);
            rrr      = qMax(rrr, x2);
            // track amount of space due to notehead only
            lhead    = qMax(lhead, -x1);

            Accidental* accidental = note->accidental();
            if (accidental && !note->fixed()) {
                  // convert x position of accidental to segment coordinate system
                  qreal x = accidental->pos().x() + note->pos().x() + chordX;
                  // distance from accidental to note already taken into account
                  // but here perhaps we create more padding in *front* of accidental?
                  x -= score()->styleP(Sid::accidentalDistance) * mag_;
                  lll = qMax(lll, -x);
                  }

            // allow extra space for shortened ties
            // this code must be kept synchronized
            // with the tie positioning code in Tie::slurPos()
            // but the allocation of space needs to be performed here
            Tie* tie;
            tie = note->tieBack();
            if (tie) {
                  tie->calculateDirection();
                  qreal overlap = 0.0;
                  bool shortStart = false;
                  Note* sn = tie->startNote();
                  Chord* sc = sn->chord();
                  if (sc && sc->measure() == measure() && sc == prevChordRest(this)) {
                        if (sc->notes().size() > 1 || (sc->stem() && sc->up() == tie->up())) {
                              shortStart = true;
                              if (sc->width() > sn->width()) {
                                    // chord with second?
                                    // account for noteheads further to right
                                    qreal snEnd = sn->x() + sn->bboxRightPos();
                                    qreal scEnd = snEnd;
                                    for (unsigned i = 0; i < sc->notes().size(); ++i)
                                          scEnd = qMax(scEnd, sc->notes().at(i)->x() + sc->notes().at(i)->bboxRightPos());
                                    overlap += scEnd - snEnd;
                                    }
                              else
                                    overlap -= sn->headWidth() * 0.12;
                              }
                        else
                              overlap += sn->headWidth() * 0.35;
                        if (notes().size() > 1 || (stem() && !up() && !tie->up())) {
                              // for positive offset:
                              //    use available space
                              // for negative x offset:
                              //    space is allocated elsewhere, so don't re-allocate here
                              if (note->ipos().x() != 0.0)
                                    overlap += qAbs(note->ipos().x());
                              else
                                    overlap -= note->headWidth() * 0.12;
                              }
                        else {
                              if (shortStart)
                                    overlap += note->headWidth() * 0.15;
                              else
                                    overlap += note->headWidth() * 0.35;
                              }
                        qreal d = qMax(minTieLength - overlap, 0.0);
                        lll = qMax(lll, d);
                        }
                  }

            // clear layout for note-based fingerings
            for (Element* e : note->el()) {
                  if (e->isFingering()) {
                        Fingering* f = toFingering(e);
                        if (f->layoutType() == ElementType::NOTE) {
                              f->setPos(QPointF());
                              f->setbbox(QRectF());
                              }
                        }
                  }

            }

      //-----------------------------------------
      //  create ledger lines
      //-----------------------------------------

      addLedgerLines();

      if (_arpeggio) {
            qreal arpeggioDistance = score()->styleP(Sid::ArpeggioNoteDistance) * mag_;
            _arpeggio->layout();    // only for width() !
            _arpeggio->setHeight(0.0);
            lll        += _arpeggio->width() + arpeggioDistance + chordX;
            qreal y1   = upnote->pos().y() - upnote->headHeight() * .5;
            _arpeggio->setPos(-lll, y1);
            // _arpeggio->layout() called in layoutArpeggio2()

            // handle the special case of _arpeggio->span() > 1
            // in layoutArpeggio2() after page layout has done so we
            // know the y position of the next staves
            }

      // allocate enough room for glissandi
      if (_endsGlissando) {
            if (!rtick().isZero()                           // if not at beginning of measure
                        || graceNotesBefore.size() > 0)     // or there are graces before
                  lll += _spatium * 0.5 + minTieLength;
            // special case of system-initial glissando final note is handled in Glissando::layout() itself
            }

      if (dots()) {
            qreal x = dotPosX() + dotNoteDistance
               + (dots()-1) * score()->styleP(Sid::dotDotDistance) * mag_;
            x += symWidth(SymId::augmentationDot);
            rrr = qMax(rrr, x);
            }

      if (_hook) {
            if (beam())
                  score()->undoRemoveElement(_hook);
            else {
                  _hook->layout();
                  if (up() && stem()) {
                        // hook position is not set yet
                        qreal x = _hook->bbox().right() + stem()->hookPos().x() + chordX;
                        rrr = qMax(rrr, x);
                        }
                  }
            }

#if 0
      if (!_articulations.isEmpty()) {
            // TODO: allocate space to avoid "staircase" effect
            // but we would need to determine direction in order to get correct symid & bbox
            // another alternative is to limit the width contribution of the articulation in layoutArticulations2()
            //qreal aWidth = 0.0;
            for (Articulation* a : articulations())
                  a->layout();      // aWidth = qMax(aWidth, a->width());
            //qreal w = width();
            //qreal aExtra = (qMax(aWidth, w) - w) * 0.5;
            //lll = qMax(lll, aExtra);
            //rrr = qMax(rrr, aExtra);
            }
#endif

      _spaceLw = lll;
      _spaceRw = rrr;

      if (gnb) {
              qreal xl = -(_spaceLw + minNoteDistance) - chordX;
              for (int i = gnb-1; i >= 0; --i) {
                    Chord* g = graceNotesBefore.value(i);
                    xl -= g->_spaceRw/* * 1.2*/;
                    g->setPos(xl, 0);
                    xl -= g->_spaceLw + minNoteDistance * graceMag;
                    }
              if (-xl > _spaceLw)
                    _spaceLw = -xl;
              }
       if (!gna.empty()) {
            qreal xr = _spaceRw;
            int n = gna.size();
            for (int i = 0; i <= n - 1; i++) {
                  Chord* g = gna.value(i);
                  xr += g->_spaceLw + g->_spaceRw + minNoteDistance * graceMag;
                  }
           if (xr > _spaceRw)
                 _spaceRw = xr;
           }

      for (Element* e : el()) {
            if (e->type() == ElementType::SLUR)     // we cannot at this time as chordpositions are not fixed
                  continue;
            e->layout();
            if (e->type() == ElementType::CHORDLINE) {
                  QRectF tbbox = e->bbox().translated(e->pos());
                  qreal lx = tbbox.left() + chordX;
                  qreal rx = tbbox.right() + chordX;
                  if (-lx > _spaceLw)
                        _spaceLw = -lx;
                  if (rx > _spaceRw)
                        _spaceRw = rx;
                  }
            }

      for (Note* note : _notes)
            note->layout2();

      // align note-based fingerings
      std::vector<Fingering*> alignNote;
      qreal xNote = 10000.0;
      for (Note* note : _notes) {
            bool leftFound = false;
            for (Element* e : note->el()) {
                  if (e->isFingering() && e->autoplace()) {
                        Fingering* f = toFingering(e);
                        if (f->layoutType() == ElementType::NOTE && f->tid() == Tid::LH_GUITAR_FINGERING) {
                              alignNote.push_back(f);
                              if (!leftFound) {
                                    leftFound = true;
                                    qreal xf = f->ipos().x();
                                    xNote = qMin(xNote, xf);
                                    }
                              }
                        }
                  }
            }
      for (Fingering* f : alignNote)
            f->rxpos() = xNote;
      }

//---------------------------------------------------------
//   layoutTablature
//---------------------------------------------------------

void Chord::layoutTablature()
      {
      qreal _spatium          = spatium();
      qreal dotNoteDistance   = score()->styleP(Sid::dotNoteDistance);
      qreal minNoteDistance   = score()->styleP(Sid::minNoteDistance);
      qreal minTieLength      = score()->styleP(Sid::MinTieLength);

      for (Chord* c : _graceNotes)
            c->layoutTablature();

      while (_ledgerLines) {
            LedgerLine* l = _ledgerLines->next();
            delete _ledgerLines;
            _ledgerLines = l;
            }

      qreal lll         = 0.0;                  // space to leave at left of chord
      qreal rrr         = 0.0;                  // space to leave at right of chord
      Note* upnote      = upNote();
      qreal headWidth   = symWidth(SymId::noteheadBlack);
      const Staff* st   = staff();
      const StaffType* tab = st->staffType(tick());
      qreal lineDist    = tab->lineDistance().val() *_spatium;
      qreal stemX       = tab->chordStemPosX(this) *_spatium;
      int   ledgerLines = 0;
      qreal llY         = 0.0;

      size_t numOfNotes = _notes.size();
      qreal minY        = 1000.0;               // just a very large value
      for (size_t i = 0; i < numOfNotes; ++i) {
            Note* note = _notes.at(i);
            note->layout();
            // set headWidth to max fret text width
            qreal fretWidth = note->bbox().width();
            if (headWidth < fretWidth)
                  headWidth = fretWidth;
            // centre fret string on stem
            qreal x = stemX - fretWidth*0.5;
            qreal y = note->fixed() ? note->line() * lineDist / 2 : tab->physStringToYOffset(note->string()) * _spatium;
            note->setPos(x, y);
            if (y < minY)
                  minY  = y;
            int   currLedgerLines   = tab->numOfTabLedgerLines(note->string());
            if (currLedgerLines > ledgerLines) {
                  ledgerLines = currLedgerLines;
                  llY         = y;
                  }

            // allow extra space for shortened ties; this code must be kept synchronized
            // with the tie positioning code in Tie::slurPos()
            // but the allocation of space needs to be performed here
            Tie* tie;
            tie = note->tieBack();
            if (tie) {
                  tie->calculateDirection();
                  qreal overlap = 0.0;          // how much tie can overlap start and end notes
                  bool shortStart = false;      // whether tie should clear start note or not
                  Note* startNote = tie->startNote();
                  Chord* startChord = startNote->chord();
                  if (startChord && startChord->measure() == measure() && startChord == prevChordRest(this)) {
                        qreal startNoteWidth = startNote->width();
                        // overlap into start chord?
                        // if in start chord, there are several notes or stem and tie in same direction
                        if (startChord->notes().size() > 1 || (startChord->stem() && startChord->up() == tie->up())) {
                              // clear start note (1/8 of fret mark width)
                              shortStart = true;
                              overlap -= startNoteWidth * 0.125;
                              }
                        else        // overlap start note (by ca. 1/3 of fret mark width)
                              overlap += startNoteWidth * 0.35;
                        // overlap into end chord (this)?
                        // if several notes or neither stem or tie are up
                        if (notes().size() > 1 || (stem() && !up() && !tie->up())) {
                              // for positive offset:
                              //    use available space
                              // for negative x offset:
                              //    space is allocated elsewhere, so don't re-allocate here
                              if (note->ipos().x() != 0.0)              // this probably does not work for TAB, as
                                    overlap += qAbs(note->ipos().x());  // _pos is used to centre the fret on the stem
                              else
                                    overlap -= fretWidth * 0.125;
                              }
                        else {
                              if (shortStart)
                                    overlap += fretWidth * 0.15;
                              else
                                    overlap += fretWidth * 0.35;
                              }
                        qreal d = qMax(minTieLength - overlap, 0.0);
                        lll = qMax(lll, d);
                        }
                  }
            }

      // create ledger lines, if required (in some historic styles)
      if (ledgerLines > 0) {
// there seems to be no need for widening 'ledger lines' beyond fret mark widths; more 'on the field'
// tests and usage will show if this depends on the metrics of the specific fonts used or not.
//            qreal extraLen    = score()->styleS(Sid::ledgerLineLength).val() * _spatium;
            qreal extraLen    = 0;
            qreal llX         = stemX - (headWidth + extraLen) * 0.5;
            for (int i = 0; i < ledgerLines; i++) {
                  LedgerLine* ldgLin = new LedgerLine(score());
                  ldgLin->setParent(this);
                  ldgLin->setTrack(track());
                  ldgLin->setVisible(visible());
                  ldgLin->setLen(headWidth + extraLen);
                  ldgLin->setPos(llX, llY);
                  ldgLin->setNext(_ledgerLines);
                  _ledgerLines = ldgLin;
                  ldgLin->layout();
                  llY += lineDist / ledgerLines;
                  }
            headWidth += extraLen;        // include ledger lines extra width in chord width
            }

      // horiz. spacing: leave half width at each side of the (potential) stem
      qreal halfHeadWidth = headWidth * 0.5;
      if (lll < stemX - halfHeadWidth)
            lll = stemX - halfHeadWidth;
      if (rrr < stemX + halfHeadWidth)
            rrr = stemX + halfHeadWidth;
      // align dots to the widest fret mark (not needed in all TAB styles, but harmless anyway)
      if (segment())
            segment()->setDotPosX(staffIdx(), headWidth);
      // if tab type is stemless or chord is stemless (possible when imported from MusicXML)
      // or measure is stemless
      // or duration longer than half (if halves have stems) or duration longer than crochet
      // remove stems
      if (tab->slashStyle() || _noStem || measure()->slashStyle(staffIdx()) || durationType().type() <
         (tab->minimStyle() != TablatureMinimStyle::NONE ? TDuration::DurationType::V_HALF : TDuration::DurationType::V_QUARTER) ) {
            if (_stem)
                  score()->undo(new RemoveElement(_stem));
            if (_hook)
                  score()->undo(new RemoveElement(_hook));
            if (_beam)
                  score()->undo(new RemoveElement(_beam));
            }
      // if stem is required but missing, add it;
      // set stem position (stem length is set in Chord:layoutStem() )
      else {
            if (_stem == 0) {
                  Stem* stem = new Stem(score());
                  stem->setParent(this);
                  score()->undo(new AddElement(stem));
                  }
            _stem->setPos(tab->chordStemPos(this) * _spatium);
            if (_hook) {
                  if (beam())
                        score()->undoRemoveElement(_hook);
                  else {
                        _hook->layout();
                        if (rrr < stemX + _hook->width())
                              rrr = stemX + _hook->width();

                        QPointF p(_stem->hookPos());
                        if (up()) {
                              p.ry() -= _hook->bbox().top();
                              p.rx() -= _stem->width();
                              }
                        else {
                              p.ry() -= _hook->bbox().bottom();
                              p.rx() -= _stem->width();
                              }
                        _hook->setPos(p);
                        }
                  }
            }
      if (!tab->genDurations()                         // if tab is not set for duration symbols
            || track2voice(track())                    // or not in first voice
            || (isGrace()                              // no tab duration symbols if grace notes
                && beamMode() == Beam::Mode::AUTO)) {  // and beammode == AUTO
                                                       //
            delete _tabDur;   // delete an existing duration symbol
            _tabDur = 0;
            }
      else {
            //
            // tab duration symbols
            //
            // check duration of prev. CR segm
            ChordRest * prevCR = prevChordRest(this);
            // if no previous CR
            // OR symbol repeat set to ALWAYS
            // OR symbol repeat condition is triggered
            // OR duration type and/or number of dots is different from current CR
            // OR chord beam mode not AUTO
            // OR previous CR is a rest
            // AND no not-stem
            // set a duration symbol (trying to re-use existing symbols where existing to minimize
            // symbol creation and deletion)
            TablatureSymbolRepeat symRepeat = tab->symRepeat();
            if (  (prevCR == 0
                  || symRepeat == TablatureSymbolRepeat::ALWAYS
                  || (symRepeat == TablatureSymbolRepeat::MEASURE && measure() != prevCR->measure())
                  || (symRepeat == TablatureSymbolRepeat::SYSTEM && measure()->system() != prevCR->measure()->system())
                  || beamMode() != Beam::Mode::AUTO
                  || prevCR->durationType().type() != durationType().type()
                  || prevCR->dots() != dots()
                  || prevCR->tuplet() != tuplet()
                  || prevCR->type() == ElementType::REST)
                        && !noStem() ) {
                  // symbol needed; if not exist, create; if exists, update duration
                  if (!_tabDur)
                        _tabDur = new TabDurationSymbol(score(), tab, durationType().type(), dots());
                  else
                        _tabDur->setDuration(durationType().type(), dots(), tab);
                  _tabDur->setParent(this);
//                  _tabDur->setMag(mag());           // useless to set grace mag: graces have no dur. symbol
                  _tabDur->layout();
                  if (minY < 0) {                     // if some fret extends above tab body (like bass strings)
                        _tabDur->rypos() += minY;     // raise duration symbol
                        _tabDur->bbox().translate(0, minY);
                        }
                  }
            else {                              // symbol not needed: if exists, delete
                  delete _tabDur;
                  _tabDur = 0;
                  }
            }                             // end of if(duration_symbols)

      if (_arpeggio) {
            qreal headHeight = upnote->headHeight();
            _arpeggio->layout();
            lll += _arpeggio->width() + _spatium * .5;
            qreal y = upNote()->pos().y() - headHeight * .5;
            qreal h = downNote()->pos().y() + downNote()->headHeight() - y;
            _arpeggio->setHeight(h);
            _arpeggio->setPos(-lll, y);

            // handle the special case of _arpeggio->span() > 1
            // in layoutArpeggio2() after page layout has done so we
            // know the y position of the next staves
            }

      // allocate enough room for glissandi
      if (_endsGlissando) {
            if (!rtick().isZero())                        // if not at beginning of measure
                  lll += (0.5 + score()->styleS(Sid::MinTieLength).val()) * _spatium;
            // special case of system-initial glissando final note is handled in Glissando::layout() itself
            }

      if (_hook) {
            if (beam())
                  score()->undoRemoveElement(_hook);
            else if(tab == 0) {
                  _hook->layout();
                  if (up()) {
                        // hook position is not set yet
                        qreal x = _hook->bbox().right() + stem()->hookPos().x();
                        rrr = qMax(rrr, x);
                        }
                  }
            }

      if (dots()) {
            qreal x = 0.0;
            // if stems are beside staff, dots are placed near to stem
            if (!tab->stemThrough()) {
                  // if there is an unbeamed hook, dots should start after the hook
                  if (_hook && !beam())
                        x = _hook->width() + dotNoteDistance;
                  // if not, dots should start at a fixed distance right after the stem
                  else
                        x = STAFFTYPE_TAB_DEFAULTDOTDIST_X * _spatium;
                  if (segment())
                        segment()->setDotPosX(staffIdx(), x);
                  }
            // if stems are through staff, use dot position computed above on fret mark widths
            else
                  x = dotPosX() + dotNoteDistance
                        + (dots()-1) * score()->styleS(Sid::dotDotDistance).val() * _spatium;
            x += symWidth(SymId::augmentationDot);
            rrr = qMax(rrr, x);
            }

#if 0
      if (!_articulations.isEmpty()) {
            // TODO: allocate space? see layoutPitched()
            for (Articulation* a : articulations())
                  a->layout();
            }
#endif

      _spaceLw = lll;
      _spaceRw = rrr;

      qreal graceMag = score()->styleD(Sid::graceNoteMag);

      QVector<Chord*> graceNotesBefore = Chord::graceNotesBefore();
      int nb = graceNotesBefore.size();
      if (nb) {
              qreal xl = -(_spaceLw + minNoteDistance);
              for (int i = nb-1; i >= 0; --i) {
                    Chord* c = graceNotesBefore.value(i);
                    xl -= c->_spaceRw/* * 1.2*/;
                    c->setPos(xl, 0);
                    xl -= c->_spaceLw + minNoteDistance * graceMag;
                    }
              if (-xl > _spaceLw)
                    _spaceLw = -xl;
              }
       QVector<Chord*> gna = graceNotesAfter();
       int na = gna.size();
       if (na) {
           // get factor for start distance after main note. Values found by testing.
           qreal fc;
           switch (durationType().type()) {
                 case TDuration::DurationType::V_LONG:    fc = 3.8; break;
                 case TDuration::DurationType::V_BREVE:   fc = 3.8; break;
                 case TDuration::DurationType::V_WHOLE:   fc = 3.8; break;
                 case TDuration::DurationType::V_HALF:    fc = 3.6; break;
                 case TDuration::DurationType::V_QUARTER: fc = 2.1; break;
                 case TDuration::DurationType::V_EIGHTH:  fc = 1.4; break;
                 case TDuration::DurationType::V_16TH:    fc = 1.2; break;
                 default: fc = 1;
                 }
           qreal xr = fc * (_spaceRw + minNoteDistance);
           for (int i = 0; i <= na - 1; i++) {
                 Chord* c = gna.value(i);
                 xr += c->_spaceLw * (i == 0 ? 1.3 : 1);
                 c->setPos(xr, 0);
                 xr += c->_spaceRw + minNoteDistance * graceMag;
                 }
           if (xr > _spaceRw)
                 _spaceRw = xr;
           }
      for (Element* e : el()) {
            e->layout();
            if (e->type() == ElementType::CHORDLINE) {
                  QRectF tbbox = e->bbox().translated(e->pos());
                  qreal lx = tbbox.left();
                  qreal rx = tbbox.right();
                  if (-lx > _spaceLw)
                        _spaceLw = -lx;
                  if (rx > _spaceRw)
                        _spaceRw = rx;
                  }
            }

      for (size_t i = 0; i < numOfNotes; ++i)
            _notes.at(i)->layout2();
      QRectF bb;
      processSiblings([&bb] (Element* e) { bb |= e->bbox().translated(e->pos()); } );
      if (_tabDur)
            bb |= _tabDur->bbox().translated(_tabDur->pos());
      setbbox(bb);
      }

//---------------------------------------------------------
//   crossMeasureSetup
//---------------------------------------------------------

void Chord::crossMeasureSetup(bool on)
      {
      if (!on) {
            if (_crossMeasure != CrossMeasure::UNKNOWN) {
                  _crossMeasure = CrossMeasure::UNKNOWN;
                  layoutStem1();
                  }
            return;
            }
      if (_crossMeasure == CrossMeasure::UNKNOWN) {
            CrossMeasure tempCross = CrossMeasure::NONE;  // assume no cross-measure modification
            // if chord has only one note and note is tied forward
            if (notes().size() == 1 && _notes[0]->tieFor()) {
                  Chord* tiedChord = _notes[0]->tieFor()->endNote()->chord();
                  // if tied note belongs to another measure and to a single-note chord
                  if (tiedChord->measure() != measure() && tiedChord->notes().size() == 1) {
                        // get total duration
                        std::vector<TDuration> durList = toDurationList(
                                    actualDurationType().fraction() +
                                    tiedChord->actualDurationType().fraction(), true);
                        // if duration can be expressed as a single duration
                        // apply cross-measure modification
                        if (durList.size() == 1) {
                              _crossMeasure = tempCross = CrossMeasure::FIRST;
                              _crossMeasureTDur = durList[0];
                              layoutStem1();
                              }
                        }
                  _crossMeasure = tempCross;
                  tiedChord->setCrossMeasure(tempCross == CrossMeasure::FIRST ?
                              CrossMeasure::SECOND : CrossMeasure::NONE);
                  }
            }
      }

//---------------------------------------------------------
//   layoutArpeggio2
//    called after layout of page
//---------------------------------------------------------

void Chord::layoutArpeggio2()
      {
      if (!_arpeggio)
            return;
      qreal y           = upNote()->pagePos().y() - upNote()->headHeight() * .5;
      int span          = _arpeggio->span();
      int btrack        = track() + (span - 1) * VOICES;
      ChordRest* bchord = toChordRest(segment()->element(btrack));
      Note* dnote       = (bchord && bchord->type() == ElementType::CHORD) ? toChord(bchord)->downNote() : downNote();

      qreal h = dnote->pagePos().y() + dnote->headHeight() * .5 - y;
      _arpeggio->setHeight(h);
      _arpeggio->layout();

#if 0 // collect notes for arpeggio
      QList<Note*> notes;
      int n = _notes.size();
      for (int j = n - 1; j >= 0; --j) {
            Note* note = _notes[j];
            if (note->tieBack())
                  continue;
            notes.prepend(note);
            }

      for (int i = 1; i < span; ++i) {
            ChordRest* c = toChordRest(segment()->element(track() + i * VOICES));
            if (c && c->type() == CHORD) {
                  QList<Note*> nl = toChord(c)->notes();
                  int n = nl.size();
                  for (int j = n - 1; j >= 0; --j) {
                        Note* note = nl[j];
                        if (note->tieBack())
                              continue;
                        notes.prepend(note);
                        }
                  }
            }
#endif
      }

//---------------------------------------------------------
//   findNote
//---------------------------------------------------------

Note* Chord::findNote(int pitch) const
      {
      size_t ns = _notes.size();
      for (size_t i = 0; i < ns; ++i) {
            Note* n = _notes.at(i);
            if (n->pitch() == pitch)
                  return n;
            }
      return 0;
      }

//---------------------------------------------------------
//   drop
//---------------------------------------------------------

Element* Chord::drop(EditData& data)
      {
      Element* e = data.dropElement;
      switch (e->type()) {
            case ElementType::ARTICULATION:
                  {
                  Articulation* atr = toArticulation(e);
                  Articulation* oa = hasArticulation(atr);
                  if (oa) {
                        delete atr;
                        atr = 0;
                        // if attribute is already there, remove
                        // score()->cmdRemove(oa); // unexpected behaviour?
                        score()->select(oa, SelectType::SINGLE, 0);
                        }
                  else {
                        atr->setParent(this);
                        atr->setTrack(track());
                        score()->undoAddElement(atr);
                        }
                  return atr;
                  }

            case ElementType::CHORDLINE:
                  e->setParent(this);
                  e->setTrack(track());
                  score()->undoAddElement(e);
                  break;

            case ElementType::TREMOLO:
                  {
                  Tremolo* t = toTremolo(e);
                  if (t->twoNotes()) {
                        Segment* s = segment()->next();
                        while (s) {
                              if (s->element(track()) && s->element(track())->isChord())
                                    break;
                              s = s->next();
                              }
                        if (s == 0) {
                              qDebug("no segment for second note of tremolo found");
                              delete e;
                              return 0;
                              }
                        Chord* ch2 = toChord(s->element(track()));
                        if (ch2->ticks() != ticks()) {
                              qDebug("no matching chord for second note of tremolo found");
                              delete e;
                              return 0;
                             }
                        t->setChords(this, ch2);
                        }
                  }
                  if (tremolo())
                        score()->undoRemoveElement(tremolo());
                  e->setParent(this);
                  e->setTrack(track());
                  score()->undoAddElement(e);
                  break;

            case ElementType::ARPEGGIO:
                  {
                  Arpeggio* a = toArpeggio(e);
                  if (arpeggio())
                        score()->undoRemoveElement(arpeggio());
                  a->setTrack(track());
                  a->setParent(this);
                  a->setHeight(spatium() * 5);   //DEBUG
                  score()->undoAddElement(a);
                  }
                  return e;

            default:
                  return ChordRest::drop(data);
            }
      return 0;
      }

//---------------------------------------------------------
//   dotPosX
//---------------------------------------------------------

qreal Chord::dotPosX() const
      {
      if (parent())
            return segment()->dotPosX(staffIdx());
      return -1000.0;
      }

//---------------------------------------------------------
//   localSpatiumChanged
//---------------------------------------------------------

void Chord::localSpatiumChanged(qreal oldValue, qreal newValue)
      {
      ChordRest::localSpatiumChanged(oldValue, newValue);
      for (Element* e : graceNotes())
            e->localSpatiumChanged(oldValue, newValue);
      if (_hook)
            _hook->localSpatiumChanged(oldValue, newValue);
      if (_stem)
            _stem->localSpatiumChanged(oldValue, newValue);
      if (_stemSlash)
            _stemSlash->localSpatiumChanged(oldValue, newValue);
      if (arpeggio())
            arpeggio()->localSpatiumChanged(oldValue, newValue);
      if (_tremolo && (tremoloChordType() != TremoloChordType::TremoloSecondNote))
            _tremolo->localSpatiumChanged(oldValue, newValue);
      for (Element* e : articulations())
            e->localSpatiumChanged(oldValue, newValue);
      for (Note* note : notes())
            note->localSpatiumChanged(oldValue, newValue);
      }

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

QVariant Chord::getProperty(Pid propertyId) const
      {
      switch (propertyId) {
            case Pid::NO_STEM:        return noStem();
            case Pid::SMALL:          return small();
            case Pid::STEM_DIRECTION: return QVariant::fromValue<Direction>(stemDirection());
            default:
                  return ChordRest::getProperty(propertyId);
            }
      }

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

QVariant Chord::propertyDefault(Pid propertyId) const
      {
      switch (propertyId) {
            case Pid::NO_STEM:        return false;
            case Pid::SMALL:          return false;
            case Pid::STEM_DIRECTION: return QVariant::fromValue<Direction>(Direction::AUTO);
            default:
                  return ChordRest::propertyDefault(propertyId);
            }
      }

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

bool Chord::setProperty(Pid propertyId, const QVariant& v)
      {
      switch (propertyId) {
            case Pid::NO_STEM:
                  setNoStem(v.toBool());
                  break;
            case Pid::SMALL:
                  setSmall(v.toBool());
                  break;
            case Pid::STEM_DIRECTION:
                  setStemDirection(v.value<Direction>());
                  break;
            default:
                  return ChordRest::setProperty(propertyId, v);
            }
      triggerLayout();
      return true;
      }

//---------------------------------------------------------
//   hasArticulation
//---------------------------------------------------------

Articulation* Chord::hasArticulation(const Articulation* aa)
      {
      for (Articulation* a : _articulations) {
            if (a->subtype() == aa->subtype())
                  return a;
            }
      return 0;
      }

//---------------------------------------------------------
//   reset
//---------------------------------------------------------

void Chord::reset()
      {
      undoChangeProperty(Pid::STEM_DIRECTION, QVariant::fromValue<Direction>(Direction::AUTO));
      undoChangeProperty(Pid::BEAM_MODE, int(Beam::Mode::AUTO));
      score()->createPlayEvents(this);
      ChordRest::reset();
      }

//---------------------------------------------------------
//   slash
//---------------------------------------------------------

bool Chord::slash()
      {
      Note* n = upNote();
      return n->fixed();
      }

//---------------------------------------------------------
//   setSlash
//---------------------------------------------------------

void Chord::setSlash(bool flag, bool stemless)
      {
      int line = 0;
      NoteHead::Group head = NoteHead::Group::HEAD_SLASH;

      if (!flag) {
            // restore to normal
            undoChangeProperty(Pid::NO_STEM, false);
            undoChangeProperty(Pid::SMALL, false);
            undoChangeProperty(Pid::OFFSET, QPointF());
            for (Note* n : _notes) {
                  n->undoChangeProperty(Pid::HEAD_GROUP, int(NoteHead::Group::HEAD_NORMAL));
                  n->undoChangeProperty(Pid::FIXED, false);
                  n->undoChangeProperty(Pid::FIXED_LINE, 0);
                  n->undoChangeProperty(Pid::PLAY, true);
                  n->undoChangeProperty(Pid::VISIBLE, true);
                  if (staff()->isDrumStaff(tick())) {
                        const Drumset* ds = part()->instrument()->drumset();
                        int pitch = n->pitch();
                        if (ds && ds->isValid(pitch)) {
                              undoChangeProperty(Pid::STEM_DIRECTION, QVariant::fromValue<Direction>(ds->stemDirection(pitch)));
                              n->undoChangeProperty(Pid::HEAD_GROUP, int(ds->noteHead(pitch)));
                              }
                        }
                  }
            return;
            }

      // set stem to auto (mostly important for rhythmic notation on drum staves)
      undoChangeProperty(Pid::STEM_DIRECTION, QVariant::fromValue<Direction>(Direction::AUTO));

      // make stemless if asked
      if (stemless) {
            undoChangeProperty(Pid::NO_STEM, true);
            undoChangeProperty(Pid::BEAM_MODE, int(Beam::Mode::NONE));
            }

      // voice-dependent attributes - line, size, offset, head
      if (track() % VOICES < 2) {
            // use middle line
            line = staff()->middleLine(tick());
            }
      else {
            // set small
            undoChangeProperty(Pid::SMALL, true);
            // set outside the staff
            qreal y = 0.0;
            if (track() % 2) {
                  line = staff()->bottomLine(tick()) + 1;
                  y    = 0.5 * spatium();
                  }
            else {
                  line = -1;
                  if (!staff()->isDrumStaff(tick()))
                        y = -0.5 * spatium();
                  }
            // for non-drum staves, add an additional offset
            // for drum staves, no offset, but use normal head
            if (!staff()->isDrumStaff(tick()))
                  // undoChangeProperty(Pid::OFFSET, QPointF(0.0, y));
                  rypos() += y;
            else
                  head = NoteHead::Group::HEAD_NORMAL;
            }

      size_t ns = _notes.size();
      for (size_t i = 0; i < ns; ++i) {
            Note* n = _notes[i];
            n->undoChangeProperty(Pid::HEAD_GROUP, static_cast<int>(head));
            n->undoChangeProperty(Pid::FIXED, true);
            n->undoChangeProperty(Pid::FIXED_LINE, line);
            n->undoChangeProperty(Pid::PLAY, false);
            // hide all but first notehead
            if (i)
                  n->undoChangeProperty(Pid::VISIBLE, false);
            }
      }

//---------------------------------------------------------
//  updateEndsGlissando
//    sets/resets the chord _endsGlissando according any glissando (or more)
//    end into this chord or no.
//---------------------------------------------------------

void Chord::updateEndsGlissando()
      {
      _endsGlissando = false;       // assume no glissando ends here
      // scan all chord notes for glissandi ending on this chord
      for (Note* note : notes()) {
            for (Spanner* sp : note->spannerBack())
                  if (sp->type() == ElementType::GLISSANDO) {
                        _endsGlissando = true;
                        return;
                        }
            }
      }

//---------------------------------------------------------
//   removeMarkings
//    - this is normally called after cloning a chord to tie a note over the barline
//    - there is no special undo handling; the assumption is that undo will simply remove the cloned chord
//    - two note tremolos are converted into simple notes
//    - single note tremolos are optionally retained
//---------------------------------------------------------

void Chord::removeMarkings(bool keepTremolo)
      {
      if (tremolo() && !keepTremolo)
            remove(tremolo());
      if (arpeggio())
            remove(arpeggio());
      qDeleteAll(graceNotes());
      graceNotes().clear();
      qDeleteAll(articulations());
      articulations().clear();
      for (Note* n : notes()) {
            for (Element* e : n->el())
                  n->remove(e);
            }
      ChordRest::removeMarkings(keepTremolo);
      }

//---------------------------------------------------------
//   mag
//---------------------------------------------------------

qreal Chord::mag() const
      {
      qreal m = staff() ? staff()->mag(tick()) : 1.0;
      if (small())
            m *= score()->styleD(Sid::smallNoteMag);
      if (_noteType != NoteType::NORMAL)
            m *= score()->styleD(Sid::graceNoteMag);
      return m;
      }

//---------------------------------------------------------
//   segment
//---------------------------------------------------------

Segment* Chord::segment() const
      {
      Element* e = parent();
      for (; e && e->type() != ElementType::SEGMENT; e = e->parent())
            ;
      return toSegment(e);
      }

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

Measure* Chord::measure() const
      {
      Element* e = parent();
      for (; e && e->type() != ElementType::MEASURE; e = e->parent())
            ;
      return toMeasure(e);
      }

//---------------------------------------------------------
//   graceNotesBefore
//---------------------------------------------------------

QVector<Chord*> Chord::graceNotesBefore() const
      {
      QVector<Chord*> cl;
      for (Chord* c : _graceNotes) {
            Q_ASSERT(c->noteType() != NoteType::NORMAL && c->noteType() != NoteType::INVALID);
            if (c->noteType() & (
                 NoteType::ACCIACCATURA
               | NoteType::APPOGGIATURA
               | NoteType::GRACE4
               | NoteType::GRACE16
               | NoteType::GRACE32)) {
                  cl.push_back(c);
                  }
            }
      return cl;
      }

//---------------------------------------------------------
//   graceNotesAfter
//---------------------------------------------------------

QVector<Chord*> Chord::graceNotesAfter() const
      {
      QVector<Chord*> cl;
      for (int i = _graceNotes.size() - 1; i >= 0; i--) {
            Chord* c = _graceNotes[i];
            Q_ASSERT(c->noteType() != NoteType::NORMAL && c->noteType() != NoteType::INVALID);
            if (c->noteType() & (NoteType::GRACE8_AFTER | NoteType::GRACE16_AFTER | NoteType::GRACE32_AFTER))
                  cl.push_back(c);
            }
      return cl;
      }

//---------------------------------------------------------
//   sortNotes
//---------------------------------------------------------

void Chord::sortNotes()
      {
      std::sort(notes().begin(), notes().end(),
         [](const Note* a,const Note* b)->bool { return b->line() < a->line(); }
         );
     }

//---------------------------------------------------------
//   nextTiedChord
//    Return next chord if all notes in this chord are tied to it.
//    Set backwards=true to return the previous chord instead.
//
//    Note: the next chord might have extra notes that are not tied
//    back to this one. Set sameSize=true to return 0 in this case.
//---------------------------------------------------------

Chord* Chord::nextTiedChord(bool backwards, bool sameSize)
      {
      Segment* nextSeg = backwards ? segment()->prev1(SegmentType::ChordRest) : segment()->next1(SegmentType::ChordRest);
      if (!nextSeg)
            return 0;
      ChordRest* nextCR = nextSeg->nextChordRest(track(), backwards);
      if (!nextCR || !nextCR->isChord())
            return 0;
      Chord* next = toChord(nextCR);
      if (sameSize && notes().size() != next->notes().size())
            return 0; // sizes don't match so some notes can't be tied
      if (tuplet() != next->tuplet())
            return 0; // next chord belongs to a different tuplet
      for (Note* n : _notes) {
            Tie* tie = backwards ? n->tieBack() : n->tieFor();
            if (!tie)
                  return 0; // not tied
            Note* nn = backwards ? tie->startNote() : tie->endNote();
            if (!nn || nn->chord() != next)
                  return 0; // tied to note in wrong voice, or tied over rest
            }
      return next; // all notes in this chord are tied to notes in next chord
      }

//---------------------------------------------------------
//   toGraceAfter
//---------------------------------------------------------

void Chord::toGraceAfter()
      {
      switch (noteType()) {
            case NoteType::APPOGGIATURA:  setNoteType(NoteType::GRACE8_AFTER);  break;
            case NoteType::GRACE16:       setNoteType(NoteType::GRACE16_AFTER); break;
            case NoteType::GRACE32:       setNoteType(NoteType::GRACE32_AFTER); break;
            default: break;
            }
      }

//---------------------------------------------------------
//   tremoloChordType
//---------------------------------------------------------

TremoloChordType Chord::tremoloChordType() const
      {
      if (_tremolo && _tremolo->twoNotes()) {
            if (_tremolo->chord1() == this)
                  return TremoloChordType::TremoloFirstNote;
            else if (_tremolo->chord2() == this)
                  return TremoloChordType::TremoloSecondNote;
            else
                  qFatal("Chord::tremoloChordType(): inconsistency %p - %p, this is %p", _tremolo->chord1(), _tremolo->chord2(), this);
            }
      return TremoloChordType::TremoloSingle;
      }

//---------------------------------------------------------
//   nextElement
//---------------------------------------------------------

Element* Chord::nextElement()
      {
      Element* e = score()->selection().element();
      if (!e && !score()->selection().elements().isEmpty())
            e = score()->selection().elements().first();

      switch(e->type()) {
            case ElementType::SYMBOL:
            case ElementType::IMAGE:
            case ElementType::FINGERING:
            case ElementType::TEXT:
            case ElementType::BEND: {
                  Note* n = toNote(e->parent());
                  if(n == _notes.front()) {
                        if (_arpeggio)
                              return _arpeggio;
                        else if (_tremolo)
                              return _tremolo;
                        break;
                        }
                  for (auto &i : _notes) {
                        if (i == n) {
                              return *(&i-1);
                              }
                        }
                  break;
                  }

            case ElementType::GLISSANDO_SEGMENT:
            case ElementType::TIE_SEGMENT: {
                  SpannerSegment* s = toSpannerSegment(e);
                  Spanner* sp = s->spanner();
                  Element* elSt = sp->startElement();
                  Q_ASSERT(elSt->type() == ElementType::NOTE);
                  Note* n = toNote(elSt);
                  Q_ASSERT(n != NULL);
                  if (n == _notes.front()) {
                        if (_arpeggio)
                              return _arpeggio;
                        else if (_tremolo)
                              return _tremolo;
                        break;
                        }
                  for (auto &i : _notes) {
                        if (i == n) {
                              return *(&i-1);
                              }
                        }
                  break;
                  }
            case ElementType::ARPEGGIO:
                  if (_tremolo)
                        return _tremolo;
                  break;

            case ElementType::ACCIDENTAL:
                  e = e->parent();
                  // fall through

            case ElementType::NOTE: {
                  if (e == _notes.front()) {
                        if (_arpeggio)
                              return _arpeggio;
                        else if (_tremolo)
                              return _tremolo;
                        break;
                        }
                  for (auto &i : _notes) {
                        if (i == e)
                              return *(&i -1);
                        }
                  }
                  break;

            case ElementType::CHORD:
                  return _notes.back();

            default:
                  break;
            }

      return ChordRest::nextElement();
      }

//---------------------------------------------------------
//   prevElement
//---------------------------------------------------------

Element* Chord::prevElement()
      {
      Element* e = score()->selection().element();
      if (!e && !score()->selection().elements().isEmpty())
            e = score()->selection().elements().last();
      switch (e->type()) {
            case ElementType::NOTE: {
                  if (e == _notes.back())
                        break;
                  Note* prevNote = nullptr;
                  for (auto &i : _notes) {
                        if (i == e) {
                              prevNote = *(&i+1);
                              }
                        }
                  Element* next = prevNote->lastElementBeforeSegment();
                  return next;
                  }

            case ElementType::CHORD:
                  return _notes.front();

            case ElementType::TREMOLO:
                  if (_arpeggio)
                        return _arpeggio;
                  // fall through

            case ElementType::ARPEGGIO: {
                  Note* n = _notes.front();
                  Element* elN = n->lastElementBeforeSegment();
                  Q_ASSERT(elN != NULL);
                  return elN;
                  }

            default:
                  break;
            }
      return ChordRest::prevElement();
      }

//---------------------------------------------------------
//   lastElementBeforeSegment
//---------------------------------------------------------

Element* Chord::lastElementBeforeSegment()
      {
      if (_tremolo) {
            return _tremolo;
            }
      else if (_arpeggio) {
            return _arpeggio;
            }
      else {
            Note* n = _notes.front();
            Element* elN = n->lastElementBeforeSegment();
            Q_ASSERT(elN != NULL);
            return elN;
            }
      }

//---------------------------------------------------------
//   nextSegmentElement
//---------------------------------------------------------

Element* Chord::nextSegmentElement()
      {
      for (int v = track() + 1; staffIdx() == v/VOICES; ++v) {
            Element* e = segment()->element(v);
            if (e) {
                  if (e->type() == ElementType::CHORD)
                        return toChord(e)->notes().back();

                  return e;
                  }
            }

      return ChordRest::nextSegmentElement();
      }

//---------------------------------------------------------
//   prevSegmentElement
//---------------------------------------------------------

Element* Chord::prevSegmentElement()
      {
      Element* el = score()->selection().element();
      if (!el && !score()->selection().elements().isEmpty() )
            el = score()->selection().elements().first();
      Element* e = segment()->lastInPrevSegments(el->staffIdx());
      if (e) {
            if (e->isChord())
                  return toChord(e)->notes().front();
            return e;
            }

      return ChordRest::prevSegmentElement();
      }

//---------------------------------------------------------
//   accessibleExtraInfo
//---------------------------------------------------------

QString Chord::accessibleExtraInfo() const
      {
      QString rez = "";

      for (const Chord* c : graceNotes()) {
            if (!score()->selectionFilter().canSelect(c))
                  continue;
            for (const Note* n : c->notes())
                  rez = QString("%1 %2").arg(rez).arg(n->screenReaderInfo());
            }

      if (arpeggio() && score()->selectionFilter().canSelect(arpeggio()))
            rez = QString("%1 %2").arg(rez).arg(arpeggio()->screenReaderInfo());

      if (tremolo() && score()->selectionFilter().canSelect(tremolo()))
            rez = QString("%1 %2").arg(rez).arg(tremolo()->screenReaderInfo());

      foreach (Element* e, el()) {
            if (!score()->selectionFilter().canSelect(e))
                  continue;
            rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
            }

      return QString("%1 %2").arg(rez).arg(ChordRest::accessibleExtraInfo());
      }

//---------------------------------------------------------
//   shape
//    does not contain articulations
//---------------------------------------------------------

Shape Chord::shape() const
      {
      Shape shape;
      if (_hook && _hook->addToSkyline())
            shape.add(_hook->shape().translated(_hook->pos()));
      if (_stem && _stem->addToSkyline()) {
            // stem direction is not known soon enough for cross staff beamed notes
            if (!(beam() && (staffMove() || beam()->cross())))
                  shape.add(_stem->shape().translated(_stem->pos()));
            }
      if (_stemSlash && _stemSlash->addToSkyline())
            shape.add(_stemSlash->shape().translated(_stemSlash->pos()));
      if (_arpeggio && _arpeggio->addToSkyline())
            shape.add(_arpeggio->shape().translated(_arpeggio->pos()));
//      if (_tremolo)
//            shape.add(_tremolo->shape().translated(_tremolo->pos()));
      for (Note* note : _notes) {
            shape.add(note->shape().translated(note->pos()));
            for (Element* e : note->el()) {
                  if (!e->addToSkyline())
                        continue;
                  if (e->isFingering() && toFingering(e)->layoutType() == ElementType::CHORD && e->bbox().isValid())
                        shape.add(e->bbox().translated(e->pos() + note->pos()));
                  }
            }
      for (Element* e : el()) {
            if (e->addToSkyline())
                  shape.add(e->shape().translated(e->pos()));
            }
      for (Chord* chord : _graceNotes)    // process grace notes last, needed for correct shape calculation
            shape.add(chord->shape().translated(chord->pos()));
      shape.add(ChordRest::shape());      // add lyrics
      for (LedgerLine* l = _ledgerLines; l; l = l->next())
            shape.add(l->shape().translated(l->pos()));
      if (_spaceLw || _spaceRw)
            shape.addHorizontalSpacing(Shape::SPACING_GENERAL, -_spaceLw, _spaceRw);
      return shape;
      }

//---------------------------------------------------------
//   layoutArticulations
//    layout tenuto and staccato
//    called before layouting slurs
//---------------------------------------------------------

void Chord::layoutArticulations()
      {
      for (Chord* gc : graceNotes())
            gc->layoutArticulations();

      if (_articulations.empty())
            return;
      const Staff* st = staff();
      const StaffType* staffType = st->staffType(tick());
      qreal mag            = (staffType->small() ? score()->styleD(Sid::smallStaffMag) : 1.0) * staffType->userMag();
      qreal _spatium       = score()->spatium() * mag;
      qreal _spStaff       = _spatium * staffType->lineDistance().val();

      //
      //    determine direction
      //    place tenuto and staccato
      //

      Articulation* prevArticulation = nullptr;
      for (Articulation* a : _articulations) {
            if (a->anchor() == ArticulationAnchor::CHORD) {
                  if (measure()->hasVoices(a->staffIdx()))
                        a->setUp(up()); // if there are voices place articulation at stem
                  else if (a->symId() >= SymId::articMarcatoAbove && a->symId() <= SymId::articMarcatoTenutoBelow)
                        a->setUp(true); // Gould, p. 117: strong accents above staff
                  else if (isGrace() && up() && !a->layoutCloseToNote() && downNote()->line() < 6)
                        a->setUp(true); // keep articulation close to grace note
                  else
                        a->setUp(!up()); // place articulation at note head
                  }
            else
                  a->setUp(a->anchor() == ArticulationAnchor::TOP_STAFF || a->anchor() == ArticulationAnchor::TOP_CHORD);

            if (!a->layoutCloseToNote())
                  continue;

            bool bottom = !a->up();  // true: articulation is below chord;  false: articulation is above chord
            a->layout();             // must be done after assigning direction, or else symId is not reliable

            bool headSide = bottom == up();
            qreal x = centerX();
            qreal y = 0.0;

            if (bottom) {
                  if (!headSide && stem()) {
                        y = upPos() + stem()->stemLen();
                        if (beam())
                              y += score()->styleS(Sid::beamWidth).val() * _spatium * .5;
                        int line   = lrint((y + 0.5 * _spStaff) / _spStaff);
                        if (line < staffType->lines())  // align between staff lines
                              y = line * _spStaff + _spatium * .5;
                        else
                              y += _spatium;
                        if (a->isStaccato() && articulations().size() == 1) {
                              if (_up)
                                    x = downNote()->bboxRightPos() - stem()->width() * .5;
                              else
                                    x = stem()->width() * .5;
                              }
                        }
                  else {
                        int line = downLine();
                        int lines = (staffType->lines() - 1) * 2;
                        if (line < lines)
                              y = ((line & ~1) + 3) * _spStaff;
                        else
                              y = line * _spStaff + 2 * _spatium;
                        y *= .5;
                        }
                  if (prevArticulation && (prevArticulation->up() == a->up()))
                        y += _spatium;
                  y -= a->height() * .5;        // center symbol
                  }
            else {
                  if (!headSide && stem()) {
                        y = downPos() + stem()->stemLen();
                        if (beam())
                              y -= score()->styleS(Sid::beamWidth).val() * _spatium * .5;
                        int line   = lrint((y-0.5*_spStaff) / _spStaff);
                        if (line >= 0)  // align between staff lines
                              y = line * _spStaff - _spatium * .5;
                        else
                              y -= _spatium;
                        if (a->isStaccato() && articulations().size() == 1) {
                              if (_up)
                                    x = downNote()->bboxRightPos() - stem()->width() * .5;
                              else
                                    x = stem()->width() * .5;
                              }
                        }
                  else {
                        int line = upLine();
                        if (line > 0)
                              y = (((line+1) & ~1) - 3) * _spStaff;
                        else
                              y = line * _spStaff - 2 * _spatium;
                        y *= .5;
                        }
                  if (prevArticulation && (prevArticulation->up() == a->up()))
                        y -= _spatium;
                  y += a->height() * .5;        // center symbol
                  }
            a->setPos(x, y);
            prevArticulation = a;
//            measure()->system()->staff(a->staffIdx())->skyline().add(a->shape().translated(a->pos() + segment()->pos() + measure()->pos()));
            }
      }

//---------------------------------------------------------
//   layoutArticulations2
//    Called after layouting systems
//    Tentatively layout all articulations
//    To be finished after laying out slurs
//---------------------------------------------------------

void Chord::layoutArticulations2()
      {
      for (Chord* gc : graceNotes())
            gc->layoutArticulations2();

      if (_articulations.empty())
            return;
      qreal _spatium  = spatium();
      qreal x         = centerX();
      qreal distance0 = score()->styleP(Sid::propertyDistance);
      qreal distance2 = score()->styleP(Sid::propertyDistanceStem);

      qreal chordTopY = upPos();    // note position of highest note
      qreal chordBotY = downPos();  // note position of lowest note

      qreal staffTopY = -distance2;
      qreal staffBotY = staff()->height() + distance2;

      // avoid collisions of staff articulations with chord notes:
      // gap between note and staff articulation is distance0 + 0.5 spatium

      if (stem()) {
            qreal y = stem()->pos().y() + pos().y() + stem()->stemLen();
            if (beam()) {
                  qreal bw = score()->styleS(Sid::beamWidth).val() * _spatium;
                  y += up() ? -bw : bw;
                  }
            if (up())
                  chordTopY = y;
            else
                  chordBotY = y;
            }

      //
      //    place all articulations with anchor at chord/rest
      //
      qreal distance1 = score()->styleP(Sid::propertyDistanceHead);
      chordTopY -= up() ? 0.5 * _spatium : distance1;
      chordBotY += up() ? distance1 : 0.5 * _spatium;
      for (Articulation* a : _articulations) {
            ArticulationAnchor aa = a->anchor();
            if (aa != ArticulationAnchor::CHORD && aa != ArticulationAnchor::TOP_CHORD && aa != ArticulationAnchor::BOTTOM_CHORD)
                  continue;

            if (a->up()) {
                  if (!a->layoutCloseToNote()) {
                        a->layout();
                        a->setPos(x, chordTopY);
                        a->doAutoplace();
                        }
                  chordTopY = a->y() - a->height() - 0.5 * _spatium;
                  }
            else {
                  if (!a->layoutCloseToNote()) {
                        a->layout();
                        a->setPos(x, chordBotY);
                        a->doAutoplace();
                        }
                  chordBotY = a->y() + a->height() + 0.5 * _spatium;
                  }
            }
      //
      //    now place all articulations with staff top or bottom anchor
      //

      staffTopY = qMin(staffTopY, chordTopY - distance0 - 0.5 * _spatium);
      staffBotY = qMax(staffBotY, chordBotY + distance0 + 0.5 * _spatium);
      for (Articulation* a : _articulations) {
            ArticulationAnchor aa = a->anchor();
            if (aa == ArticulationAnchor::TOP_STAFF || aa == ArticulationAnchor::BOTTOM_STAFF) {
                  a->layout();
                  if (a->up()) {
                        a->setPos(x, staffTopY);
                        staffTopY -= distance0;
                        }
                  else {
                        a->setPos(x, staffBotY);
                        staffBotY += distance0;
                        }
                  a->doAutoplace();
                  }
            }
      for (Articulation* a : _articulations) {
            if (a->addToSkyline()) {
                  // the segment shape has already been calculated
                  // so measure width and spacing is already determined
                  // in line mode, we cannot add to segment shape without throwing this off
                  // but adding to skyline is always good
                  Segment* s = segment();
                  Measure* m = s->measure();
                  QRectF r = a->bbox().translated(a->pos() + pos());
                  // TODO: limit to width of chord
                  // this avoids "staircase" effect due to space not having been allocated already
                  // ANOTHER alternative is to allocate the space in layoutPitched() / layoutTablature()
                  //qreal w = qMin(r.width(), width());
                  //r.translate((r.width() - w) * 0.5, 0.0);
                  //r.setWidth(w);
                  if (!score()->lineMode())
                        s->staffShape(staffIdx()).add(r);
                  r.translate(s->pos() + m->pos());
                  m->system()->staff(vStaffIdx())->skyline().add(r);
                  }
            }
      }

//---------------------------------------------------------
//   layoutArticulations3
//    Called after layouting slurs
//    Fix up articulations that need to go outside the slur
//---------------------------------------------------------

void Chord::layoutArticulations3(Slur* slur)
      {
      SlurSegment* ss;
      if (this == slur->startCR())
            ss = slur->frontSegment();
      else if (this == slur->endCR())
            ss = slur->backSegment();
      else
            return;
      Segment* s = segment();
      Measure* m = measure();
      SysStaff* sstaff = m->system() ? m->system()->staff(vStaffIdx()) : nullptr;
      for (Articulation* a : _articulations) {
            if (a->layoutCloseToNote() || !a->autoplace() || !slur->addToSkyline())
                  continue;
            Shape aShape = a->shape().translated(a->pos() + pos() + s->pos() + m->pos());
            Shape sShape = ss->shape().translated(ss->pos());
            if (aShape.intersects(sShape)) {
                  qreal d = score()->styleS(Sid::articulationMinDistance).val() * spatium();
                  if (slur->up()) {
                        d += qMax(aShape.minVerticalDistance(sShape), 0.0);
                        a->rypos() -= d;
                        aShape.translateY(-d);
                        }
                  else {
                        d += qMax(sShape.minVerticalDistance(aShape), 0.0);
                        a->rypos() += d;
                        aShape.translateY(d);
                        }
                  if (sstaff && a->addToSkyline())
                        sstaff->skyline().add(aShape);
                  }
            }
      }

}
