//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2002-2011 Werner Schweer
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2
//  as published by the Free Software Foundation and appearing in
//  the file LICENCE.GPL
//=============================================================================
 
#include "chordrest.h"
#include "chord.h"
#include "xml.h"
#include "style.h"
#include "system.h"
#include "measure.h"
#include "staff.h"
#include "tuplet.h"
#include "score.h"
#include "sym.h"
#include "slur.h"
#include "beam.h"
#include "breath.h"
#include "barline.h"
#include "articulation.h"
#include "tempo.h"
#include "tempotext.h"
#include "note.h"
#include "arpeggio.h"
#include "dynamic.h"
#include "stafftext.h"
#include "sig.h"
#include "clef.h"
#include "lyrics.h"
#include "segment.h"
#include "stafftype.h"
#include "undo.h"
#include "stem.h"
#include "harmony.h"
#include "figuredbass.h"
#include "icon.h"
#include "utils.h"
#include "keysig.h"
#include "page.h"
#include "hook.h"
 
namespace Ms {
 
//---------------------------------------------------------
//   hasArticulation
//---------------------------------------------------------
 
Articulation* ChordRest::hasArticulation(const Articulation* aa)
      {
      SymId id = aa->symId();
      for (Articulation* a : _articulations) {
            if (id == a->symId())
                  return a;
            }
      return 0;
      }
 
//---------------------------------------------------------
//   ChordRest
//---------------------------------------------------------
 
ChordRest::ChordRest(Score* s)
   : DurationElement(s)
      {
      _staffMove   = 0;
      _beam        = 0;
      _tabDur      = 0;
      _up          = true;
      _beamMode    = Beam::Mode::AUTO;
      _small       = false;
      _crossMeasure = CrossMeasure::UNKNOWN;
      }
 
ChordRest::ChordRest(const ChordRest& cr, bool link)
   : DurationElement(cr)
      {
      _durationType = cr._durationType;
      _staffMove    = cr._staffMove;
      _beam         = 0;
      _tabDur       = 0;  // tab sur. symb. depends upon context: can't be
                          // simply copied from another CR
 
      for (Articulation* a : cr._articulations) {    // make deep copy
            Articulation* na = new Articulation(*a);
            if (link)
                  na->linkTo(a);
            na->setParent(this);
            na->setTrack(track());
            _articulations.append(na);
            }
 
      _beamMode     = cr._beamMode;
      _up           = cr._up;
      _small        = cr._small;
      _crossMeasure = cr._crossMeasure;
 
      for (Lyrics* l : cr._lyrics) {        // make deep copy
            Lyrics* nl = new Lyrics(*l);
            if (link)
                  nl->linkTo(l);
            nl->setParent(this);
            nl->setTrack(track());
            _lyrics.push_back(nl);
            }
      }
 
//---------------------------------------------------------
//   undoUnlink
//---------------------------------------------------------
 
void ChordRest::undoUnlink()
      {
      DurationElement::undoUnlink();
      for (Articulation* a : _articulations)
            a->undoUnlink();
      for (Lyrics* l : _lyrics)
            l->undoUnlink();
      }
 
//---------------------------------------------------------
//   ChordRest
//---------------------------------------------------------
 
ChordRest::~ChordRest()
      {
      qDeleteAll(_articulations);
      qDeleteAll(_lyrics);
      qDeleteAll(_el);
      delete _tabDur;
      }
 
//---------------------------------------------------------
//   scanElements
//---------------------------------------------------------
 
void ChordRest::scanElements(void* data, void (*func)(void*, Element*), bool all)
      {
      if (_beam && (_beam->elements().front() == this)
       && !measure()->slashStyle(staffIdx()))
            _beam->scanElements(data, func, all);
      for (Articulation* a : _articulations)
            func(data, a);
      for (Lyrics* l : _lyrics)
            l->scanElements(data, func, all);
      DurationElement* de = this;
      while (de->tuplet() && de->tuplet()->elements().front() == de) {
            de->tuplet()->scanElements(data, func, all);
            de = de->tuplet();
            }
      if (_tabDur)
            func(data, _tabDur);
      }
 
//---------------------------------------------------------
//   writeProperties
//---------------------------------------------------------
 
void ChordRest::writeProperties(XmlWriter& xml) const
      {
      DurationElement::writeProperties(xml);
 
      //
      // Beam::Mode default:
      //    REST  - Beam::Mode::NONE
      //    CHORD - Beam::Mode::AUTO
      //
      if ((type() == ElementType::REST && _beamMode != Beam::Mode::NONE)
         || (type() == ElementType::CHORD && _beamMode != Beam::Mode::AUTO)) {
            QString s;
            switch(_beamMode) {
                  case Beam::Mode::AUTO:    s = "auto"; break;
                  case Beam::Mode::BEGIN:   s = "begin"; break;
                  case Beam::Mode::MID:     s = "mid"; break;
                  case Beam::Mode::END:     s = "end"; break;
                  case Beam::Mode::NONE:    s = "no"; break;
                  case Beam::Mode::BEGIN32: s = "begin32"; break;
                  case Beam::Mode::BEGIN64: s = "begin64"; break;
                  case Beam::Mode::INVALID: s = "?"; break;
                  }
            xml.tag("BeamMode", s);
            }
      writeProperty(xml, P_ID::SMALL);
      if (actualDurationType().dots())
            xml.tag("dots", actualDurationType().dots());
      writeProperty(xml, P_ID::STAFF_MOVE);
 
      if (actualDurationType().isValid())
            xml.tag("durationType", actualDurationType().name());
 
      if (!duration().isZero() && (!actualDurationType().fraction().isValid()
         || (actualDurationType().fraction() != duration()))) {
            xml.tag("duration", duration());
            //xml.tagE("duration z=\"%d\" n=\"%d\"", duration().numerator(), duration().denominator());
            }
 
      for (const Articulation* a : _articulations)
            a->write(xml);
 
#ifndef NDEBUG
      if (_beam && (MScore::testMode || !_beam->generated()))
            xml.tag("Beam", _beam->id());
#else
      if (_beam && !_beam->generated())
            xml.tag("Beam", _beam->id());
#endif
      for (Lyrics* lyrics : _lyrics)
            lyrics->write(xml);
      if (!isGrace()) {
            Fraction t(globalDuration());
            if (staff())
                  t /= staff()->timeStretch(xml.curTick());
            xml.incCurTick(t.ticks());
            }
      for (auto i : score()->spanner()) {     // TODO: dont search whole list
            Spanner* s = i.second;
            if (s->generated() || !s->isSlur() || toSlur(s)->broken() || !xml.canWrite(s))
                  continue;
 
            if (s->startElement() == this) {
                  int id = xml.spannerId(s);
                  xml.tagE(QString("Slur type=\"start\" id=\"%1\"").arg(id));
                  }
            else if (s->endElement() == this) {
                  int id = xml.spannerId(s);
                  xml.tagE(QString("Slur type=\"stop\" id=\"%1\"").arg(id));
                  }
            }
      }
 
//---------------------------------------------------------
//   readProperties
//---------------------------------------------------------
 
bool ChordRest::readProperties(XmlReader& e)
      {
      const QStringRef& tag(e.name());
 
      if (tag == "durationType") {
            setDurationType(e.readElementText());
            if (actualDurationType().type() != TDuration::DurationType::V_MEASURE) {
                  if (score()->mscVersion() < 112 && (type() == ElementType::REST) &&
                              // for backward compatibility, convert V_WHOLE rests to V_MEASURE
                              // if long enough to fill a measure.
                              // OTOH, freshly created (un-initialized) rests have numerator == 0 (< 4/4)
                              // (see Fraction() constructor in fraction.h; this happens for instance
                              // when pasting selection from clipboard): they should not be converted
                              duration().numerator() != 0 &&
                              // rest durations are initialized to full measure duration when
                              // created upon reading the <Rest> tag (see Measure::read() )
                              // so a V_WHOLE rest in a measure of 4/4 or less => V_MEASURE
                              (actualDurationType()==TDuration::DurationType::V_WHOLE && duration() <= Fraction(4, 4)) ) {
                        // old pre 2.0 scores: convert
                        setDurationType(TDuration::DurationType::V_MEASURE);
                        }
                  else  // not from old score: set duration fraction from duration type
                        setDuration(actualDurationType().fraction());
                  }
            else {
                  if (score()->mscVersion() <= 114) {
                        SigEvent event = score()->sigmap()->timesig(e.tick());
                        setDuration(event.timesig());
                        }
                  }
            }
      else if (tag == "BeamMode") {
            QString val(e.readElementText());
            Beam::Mode bm = Beam::Mode::AUTO;
            if (val == "auto")
                  bm = Beam::Mode::AUTO;
            else if (val == "begin")
                  bm = Beam::Mode::BEGIN;
            else if (val == "mid")
                  bm = Beam::Mode::MID;
            else if (val == "end")
                  bm = Beam::Mode::END;
            else if (val == "no")
                  bm = Beam::Mode::NONE;
            else if (val == "begin32")
                  bm = Beam::Mode::BEGIN32;
            else if (val == "begin64")
                  bm = Beam::Mode::BEGIN64;
            else
                  bm = Beam::Mode(val.toInt());
            _beamMode = Beam::Mode(bm);
            }
      else if (tag == "Articulation") {
            Articulation* atr = new Articulation(score());
            atr->setTrack(track());
            atr->read(e);
            add(atr);
            }
      else if (tag == "leadingSpace" || tag == "trailingSpace") {
            qDebug("ChordRest: %s obsolete", tag.toLocal8Bit().data());
            e.skipCurrentElement();
            }
      else if (tag == "Beam") {
            int id = e.readInt();
            Beam* beam = e.findBeam(id);
            if (beam)
                  beam->add(this);        // also calls this->setBeam(beam)
            else
                  qDebug("Beam id %d not found", id);
            }
      else if (tag == "small")
            _small = e.readInt();
      else if (tag == "duration")
            setDuration(e.readFraction());
      else if (tag == "ticklen") {      // obsolete (version < 1.12)
            int mticks = score()->sigmap()->timesig(e.tick()).timesig().ticks();
            int i = e.readInt();
            if (i == 0)
                  i = mticks;
            if ((type() == ElementType::REST) && (mticks == i)) {
                  setDurationType(TDuration::DurationType::V_MEASURE);
                  setDuration(Fraction::fromTicks(i));
                  }
            else {
                  Fraction f = Fraction::fromTicks(i);
                  setDuration(f);
                  setDurationType(TDuration(f));
                  }
            }
      else if (tag == "dots")
            setDots(e.readInt());
      else if (tag == "move")
            _staffMove = e.readInt();
      else if (tag == "Slur") {
            int id = e.intAttribute("id");
            if (id == 0)
                  id = e.intAttribute("number");                  // obsolete
            Spanner* spanner = e.findSpanner(id);
            QString atype(e.attribute("type"));
 
            if (!spanner) {
                  if (atype == "stop") {
                        SpannerValues sv;
                        sv.spannerId = id;
                        sv.track2    = track();
                        sv.tick2     = e.tick();
                        e.addSpannerValues(sv);
                        }
                  else if (atype == "start")
                        qDebug("spanner: start without spanner");
                  }
            else {
                  if (atype == "start") {
                        if (spanner->ticks() > 0 && spanner->tick() == -1) // stop has been read first
                              spanner->setTicks(spanner->ticks() - e.tick() - 1);
                        spanner->setTick(e.tick());
                        spanner->setTrack(track());
                        if (spanner->type() == ElementType::SLUR)
                              spanner->setStartElement(this);
                        if (e.pasteMode()) {
                              for (ScoreElement* e : spanner->linkList()) {
                                    if (e == spanner)
                                          continue;
                                    Spanner* ls = static_cast<Spanner*>(e);
                                    ls->setTick(spanner->tick());
                                    for (ScoreElement* ee : linkList()) {
                                          ChordRest* cr = static_cast<ChordRest*>(ee);
                                          if (cr->score() == ee->score() && cr->staffIdx() == ls->staffIdx()) {
                                                ls->setTrack(cr->track());
                                                if (ls->type() == ElementType::SLUR)
                                                      ls->setStartElement(cr);
                                                break;
                                                }
                                          }
                                    }
                              }
                        }
                  else if (atype == "stop") {
                        spanner->setTick2(e.tick());
                        spanner->setTrack2(track());
                        if (spanner->type() == ElementType::SLUR)
                              spanner->setEndElement(this);
                        Chord* start = static_cast<Chord*>(spanner->startElement());
                        if (start)
                              spanner->setTrack(start->track());
                        if (e.pasteMode()) {
                              for (ScoreElement* e : spanner->linkList()) {
                                    if (e == spanner)
                                          continue;
                                    Spanner* ls = static_cast<Spanner*>(e);
                                    ls->setTick2(spanner->tick2());
                                    for (ScoreElement* ee : linkList()) {
                                          ChordRest* cr = static_cast<ChordRest*>(ee);
                                          if (cr->score() == ee->score() && cr->staffIdx() == ls->staffIdx()) {
                                                ls->setTrack2(cr->track());
                                                if (ls->type() == ElementType::SLUR)
                                                      ls->setEndElement(cr);
                                                break;
                                                }
                                          }
                                    }
                              }
                        }
                  else
                        qDebug("ChordRest::read(): unknown Slur type <%s>", qPrintable(atype));
                  }
            e.readNext();
            }
      else if (tag == "Lyrics" /*|| tag == "FiguredBass"*/) {
            Element* element = Element::name2Element(tag, score());
            element->setTrack(e.track());
            element->read(e);
            add(element);
            }
      else if (tag == "pos") {
            QPointF pt = e.readPoint();
            setUserOff(pt * spatium());
            }
      else if (tag == "offset")
            DurationElement::readProperties(e);
      else if (DurationElement::readProperties(e))
            return true;
      else
            return false;
      return true;
      }
 
//---------------------------------------------------------
//   setSmall
//---------------------------------------------------------
 
void ChordRest::setSmall(bool val)
      {
      _small = val;
      }
 
//---------------------------------------------------------
//   undoSetSmall
//---------------------------------------------------------
 
void ChordRest::undoSetSmall(bool val)
      {
      undoChangeProperty(P_ID::SMALL, val);
      }
 
//---------------------------------------------------------
//   layoutArticulations
//---------------------------------------------------------
 
void ChordRest::layoutArticulations()
      {
      if (parent() == 0 || _articulations.empty())
            return;
      qreal _spatium = spatium();
//      bool scale     = !staff()->isDrumStaff(tick());
      qreal pld      = staff()->lineDistance(tick());
      qreal _spStaff = _spatium * pld;    // scaled to staff line distance for vert. pos. within a staff
 
      if (isChord() && _articulations.size() == 1) {
            toChord(this)->layoutArticulation(_articulations[0]);
            return;
            }
 
      qreal x         = centerX();
      qreal distance0 = score()->styleP(StyleIdx::propertyDistance);
      qreal distance1 = score()->styleP(StyleIdx::propertyDistanceHead);
      qreal distance2 = score()->styleP(StyleIdx::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 (isChord()) {
            Chord* chord = toChord(this);
            Stem* stem   = chord->stem();
            if (stem) {
                  qreal y = stem->pos().y() + pos().y();
                  if (up() && stem->stemLen() < 0.0)
                        y += stem->stemLen();
                  else if (!up() && stem->stemLen() > 0.0)
                        y -= stem->stemLen();
 
                  if (beam()) {
                        qreal bw = score()->styleS(StyleIdx::beamWidth).val() * _spatium;
                        y += up() ? -bw : bw;
                        }
                  if (up())
                        staffTopY = qMin(staffTopY, qreal(y - 0.5 * _spatium));
                  else
                        staffBotY = qMax(staffBotY, qreal(y + 0.5 * _spatium));
                  }
            }
 
      staffTopY = qMin(staffTopY, chordTopY - distance0 - 0.5 * _spatium);
      staffBotY = qMax(staffBotY, chordBotY + distance0 + 0.5 * _spatium);
 
      qreal dy = 0.0;
 
      int n = _articulations.size();
      for (int i = 0; i < n; ++i) {
            Articulation* a = _articulations.at(i);
            //
            // determine Direction
            //
            if (a->direction() != Direction::AUTO) {
                  a->setUp(a->direction() == Direction::UP);
                  }
            else {
                  if (a->anchor() == ArticulationAnchor::CHORD)
                        a->setUp(!up());
                  else
                        a->setUp(a->anchor() == ArticulationAnchor::TOP_STAFF || a->anchor() == ArticulationAnchor::TOP_CHORD);
                  }
            }
 
      //
      //    pass 1
      //    place tenuto and staccato
      //
 
      for (Articulation* a : _articulations) {
            a->layout();
            ArticulationAnchor aa = a->anchor();
 
            if (!(a->isTenuto() || a->isStaccato()))
                  continue;
 
            if (aa != ArticulationAnchor::CHORD && aa != ArticulationAnchor::TOP_CHORD && aa != ArticulationAnchor::BOTTOM_CHORD)
                  continue;
 
            bool bottom;
            if ((aa == ArticulationAnchor::CHORD) && measure()->hasVoices(a->staffIdx()))
                  bottom = !up();
            else
                  bottom = (aa == ArticulationAnchor::BOTTOM_CHORD) || (aa == ArticulationAnchor::CHORD && up());
            bool headSide = bottom == up();
 
            dy += distance1;
            qreal y;
            Chord* chord = toChord(this);
            if (bottom) {
                  int line = downLine();
                  y = chordBotY + dy;
                  if (!headSide && isChord() && chord->stem()) {
                        Stem* stem = chord->stem();
                        y          = chordTopY + stem->stemLen();
                        if (chord->beam())
                              y += score()->styleS(StyleIdx::beamWidth).val() * _spatium * .5;
                        // aligning horizontally to stem makes sense only for staccato
                        // and only if no other articulations on this side
                        //x = stem->pos().x();
                        int line   = lrint((y+0.5*_spStaff) / _spStaff);
                        if (line < staff()->lines(tick()))  // align between staff lines
                              y = line * _spStaff + _spatium * .5;
                        else
                              y += _spatium;
                        }
                  else {
                        int lines = (staff()->lines(tick()) - 1) * 2;
                        if (line < lines)
                              y = ((line & ~1) + 3) * _spStaff;
                        else
                              y = line * _spStaff + 2 * _spatium;
                        y *= .5;
                        }
                  }
            else {
                  int line = upLine();
                  y        = chordTopY - dy;
                  if (!headSide && type() == ElementType::CHORD && chord->stem()) {
                        Stem* stem = chord->stem();
                        y          = chordBotY + stem->stemLen();
                        if (chord->beam())
                              y -= score()->styleS(StyleIdx::beamWidth).val() * _spatium * .5;
                        // aligning horizontally to stem makes sense only for staccato
                        // and only if no other articulations on this side
                        //x = stem->pos().x();
                        int line   = lrint((y-0.5*_spStaff) / _spStaff);
                        if (line >= 0)    // align between staff lines
                              y = line * _spStaff - _spatium * .5;
                        else
                              y -= _spatium;
                        }
                  else {
                        if (line > 0)
                              y = (((line+1) & ~1) - 3) * _spStaff;
                        else
                              y = line * _spStaff - 2 * _spatium;
                        y *= .5;
                        }
                  }
            dy += _spatium * .5;
            a->setPos(x, y);
            }
 
      // reserve space for slur
      bool botGap = false;
      bool topGap = false;
 
      if (botGap)
            chordBotY += _spatium;
      if (topGap)
            chordTopY -= _spatium;
 
      //
      //    pass 2
      //    place all articulations with anchor at chord/rest
      //
      n = _articulations.size();
      for (int i = 0; i < n; ++i) {
            Articulation* a = _articulations.at(i);
            a->layout();
            ArticulationAnchor aa = a->anchor();
            if (a->isTenuto() || a->isStaccato())
                  continue;
 
            if (aa != ArticulationAnchor::CHORD && aa != ArticulationAnchor::TOP_CHORD && aa != ArticulationAnchor::BOTTOM_CHORD)
                  continue;
 
            // for tenuto and staccate check for staff line collision
            bool staffLineCT = a->isTenuto() || a->isStaccato();
 
            bool bottom = (aa == ArticulationAnchor::BOTTOM_CHORD) || (aa == ArticulationAnchor::CHORD && up());
 
            dy += distance1;
            if (bottom) {
                  qreal y = chordBotY + dy;
                  if (staffLineCT && (y <= staffBotY -.1 - dy)) {
                        qreal l = y / _spStaff;
                        qreal delta = fabs(l - round(l));
                        if (delta < 0.4) {
                              y  += _spatium * .5;
                              dy += _spatium * .5;
                              }
                        }
                  a->setPos(x, y); // - a->bbox().y() + a->bbox().height() * .5);
                  }
            else {
                  qreal y = chordTopY - dy;
                  if (staffLineCT && (y >= (staffTopY +.1 + dy))) {
                        qreal l = y / _spStaff;
                        qreal delta = fabs(l - round(l));
                        if (delta < 0.4) {
                              y  -= _spatium * .5;
                              dy += _spatium * .5;
                              }
                        }
                  a->setPos(x, y); // + a->bbox().y() - a->bbox().height() * .5);
                  }
            }
 
      //
      //    pass 3
      //    now place all articulations with staff top or bottom anchor
      //
      qreal dyTop = staffTopY;
      qreal dyBot = staffBotY;
 
      for (Articulation* a : _articulations) {
            ArticulationAnchor aa = a->anchor();
            if (aa == ArticulationAnchor::TOP_STAFF || aa == ArticulationAnchor::BOTTOM_STAFF) {
                  if (a->up()) {
                        a->setPos(x, dyTop);
                        dyTop -= distance0;
                        }
                  else {
                        a->setPos(x, dyBot);
                        dyBot += distance0;
                        }
                  }
            a->adjustReadPos();
            }
      }
 
//---------------------------------------------------------
//   drop
//---------------------------------------------------------
 
Element* ChordRest::drop(EditData& data)
      {
      Element* e       = data.element;
      Measure* m       = measure();
      bool fromPalette = (e->track() == -1);
      switch (e->type()) {
            case ElementType::BREATH:
                  {
                  Breath* b = toBreath(e);
                  b->setPos(QPointF());
                  int track = staffIdx() * VOICES;
                  b->setTrack(track);
 
                  // find start tick of next note in staff
#if 0
                  int bt = tick() + actualTicks();    // this could make sense if we allowed breath marks in voice > 1
#else
                  Segment* next = segment()->nextCR(track);
                  int bt = next ? next->tick() : score()->lastSegment()->tick();
#endif
 
                  // TODO: insert automatically in all staves?
 
                  Segment* seg = m->undoGetSegment(SegmentType::Breath, bt);
                  b->setParent(seg);
                  score()->undoAddElement(b);
                  }
                  return e;
 
            case ElementType::BAR_LINE:
                  if (data.control())
                        score()->splitMeasure(segment());
                  else {
                        BarLine* bl = toBarLine(e);
                        bl->setPos(QPointF());
                        bl->setTrack(staffIdx() * VOICES);
                        bl->setGenerated(false);
 
                        if (tick() == m->tick())
                              return m->drop(data);
 
                        BarLine* obl = 0;
                        for (Staff* st  : staff()->staffList()) {
                              Score* score = st->score();
                              Measure* measure = score->tick2measure(m->tick());
                              Segment* seg = measure->undoGetSegmentR(SegmentType::BarLine, rtick());
                              BarLine* l;
                              if (obl == 0)
                                    obl = l = bl->clone();
                              else
                                    l = toBarLine(obl->linkedClone());
                              l->setTrack(st->idx() * VOICES);
                              l->setScore(score);
                              l->setParent(seg);
                              score->undoAddElement(l);
                              }
                        }
                  delete e;
                  return 0;
 
            case ElementType::CLEF:
                  score()->cmdInsertClef(toClef(e), this);
                  break;
 
            case ElementType::TIMESIG:
                  if (measure()->system()) {
                        EditData ndd = data;
                        // adding from palette sets pos, but normal paste does not
                        if (!fromPalette)
                              ndd.pos = pagePos();
                        // convert page-relative pos to score-relative
                        ndd.pos += measure()->system()->page()->pos();
                        return measure()->drop(ndd);
                        }
                  else {
                        delete e;
                        return 0;
                        }
 
            case ElementType::TEMPO_TEXT:
            case ElementType::DYNAMIC:
            case ElementType::FRET_DIAGRAM:
            case ElementType::TREMOLOBAR:
            case ElementType::SYMBOL:
                  e->setTrack(track());
                  e->setParent(segment());
                  score()->undoAddElement(e);
                  return e;
 
            case ElementType::NOTE:
                  {
                  Note* note = static_cast<Note*>(e);
                  NoteVal nval;
                  nval.pitch = note->pitch();
                  nval.headGroup = note->headGroup();
                  nval.fret = note->fret();
                  nval.string = note->string();
                  score()->setNoteRest(segment(), track(), nval, data.duration, Direction::AUTO);
                  delete e;
                  }
                  break;
 
            case ElementType::HARMONY:
                  {
                  // transpose
                  Harmony* harmony = static_cast<Harmony*>(e);
                  Interval interval = staff()->part()->instrument(tick())->transpose();
                  if (!score()->styleB(StyleIdx::concertPitch) && !interval.isZero()) {
                        interval.flip();
                        int rootTpc = transposeTpc(harmony->rootTpc(), interval, true);
                        int baseTpc = transposeTpc(harmony->baseTpc(), interval, true);
                        score()->undoTransposeHarmony(harmony, rootTpc, baseTpc);
                        }
                  // render
                  harmony->render();
                  }
                  // fall through
            case ElementType::TEXT:
            case ElementType::STAFF_TEXT:
            case ElementType::SYSTEM_TEXT:
            case ElementType::STAFF_STATE:
            case ElementType::INSTRUMENT_CHANGE:
                  if (e->isInstrumentChange() && part()->instruments()->find(tick()) != part()->instruments()->end()) {
                        qDebug()<<"InstrumentChange already exists at tick = "<<tick();
                        delete e;
                        return 0;
                        }
                  // fall through
            case ElementType::REHEARSAL_MARK:
                  e->setParent(segment());
                  e->setTrack((track() / VOICES) * VOICES);
                  score()->undoAddElement(e);
                  return e;
 
            case ElementType::FIGURED_BASS:
                  {
                  bool bNew;
                  FiguredBass * fb = static_cast<FiguredBass *>(e);
                  fb->setParent( segment() );
                  fb->setTrack( (track() / VOICES) * VOICES );
                  fb->setTicks( duration().ticks() );
                  fb->setOnNote(true);
                  FiguredBass::addFiguredBassToSegment(segment(),
                        fb->track(), fb->ticks(), &bNew);
                  if (bNew)
                        score()->undoAddElement(e);
                  return e;
                  }
 
            case ElementType::IMAGE:
                  e->setParent(segment());
                  score()->undoAddElement(e);
                  return e;
 
            case ElementType::ICON:
                  {
                  switch(static_cast<Icon*>(e)->iconType()) {
                        case IconType::SBEAM:
                              undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::BEGIN));
                              break;
                        case IconType::MBEAM:
                              undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::MID));
                              break;
                        case IconType::NBEAM:
                              undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::NONE));
                              break;
                        case IconType::BEAM32:
                              undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::BEGIN32));
                              break;
                        case IconType::BEAM64:
                              undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::BEGIN64));
                              break;
                        case IconType::AUTOBEAM:
                              undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::AUTO));
                              break;
                        default:
                              break;
                        }
                  }
                  delete e;
                  break;
 
            case ElementType::KEYSIG:
                  {
                  KeySig* ks    = static_cast<KeySig*>(e);
                  KeySigEvent k = ks->keySigEvent();
                  delete ks;
 
                  // apply only to this stave
                  score()->undoChangeKeySig(staff(), tick(), k);
                  }
                  break;
 
            default:
                  qDebug("cannot drop %s", e->name());
                  delete e;
                  return 0;
            }
      return 0;
      }
 
//---------------------------------------------------------
//   setBeam
//---------------------------------------------------------
 
void ChordRest::setBeam(Beam* b)
      {
      _beam = b;
      }
 
//---------------------------------------------------------
//   setDurationType
//---------------------------------------------------------
 
void ChordRest::setDurationType(TDuration::DurationType t)
      {
      _durationType.setType(t);
      _crossMeasure = CrossMeasure::UNKNOWN;
      }
 
void ChordRest::setDurationType(const QString& s)
      {
      _durationType.setType(s);
      _crossMeasure = CrossMeasure::UNKNOWN;
      }
 
void ChordRest::setDurationType(int ticks)
      {
      _durationType.setVal(ticks);
      _crossMeasure = CrossMeasure::UNKNOWN;
      }
 
void ChordRest::setDurationType(TDuration v)
      {
      _durationType = v;
      _crossMeasure = CrossMeasure::UNKNOWN;
      }
 
//---------------------------------------------------------
//   durationUserName
//---------------------------------------------------------
 
QString ChordRest::durationUserName() const
      {
      QString tupletType = "";
      if (tuplet()) {
              switch (tuplet()->ratio().numerator()) {
                  case 2:
                        tupletType = QObject::tr("Duplet");
                        break;
                  case 3:
                        tupletType = QObject::tr("Triplet");
                        break;
                  case 4:
                        tupletType = QObject::tr("Quadruplet");
                        break;
                  case 5:
                        tupletType = QObject::tr("Quintuplet");
                        break;
                  case 6:
                        tupletType = QObject::tr("Sextuplet");
                        break;
                  case 7:
                        tupletType = QObject::tr("Septuplet");
                        break;
                  case 8:
                        tupletType = QObject::tr("Octuplet");
                        break;
                  case 9:
                        tupletType = QObject::tr("Nonuplet");
                        break;
                  default:
                        tupletType = QObject::tr("Custom Tuplet");
                  }
            }
      QString dotString = "";
      if(!tupletType.isEmpty())
          dotString += " ";
 
      switch (dots()) {
            case 1:
                  dotString += QObject::tr("Dotted %1").arg(durationType().durationTypeUserName()).trimmed();
                  break;
            case 2:
                  dotString += QObject::tr("Double dotted %1").arg(durationType().durationTypeUserName()).trimmed();
                  break;
            case 3:
                  dotString += QObject::tr("Triple dotted %1").arg(durationType().durationTypeUserName()).trimmed();
                  break;
            case 4:
                  dotString += QObject::tr("Quadruple dotted %1").arg(durationType().durationTypeUserName()).trimmed();
                  break;
            default:
                  dotString += durationType().durationTypeUserName();
            }
      return QString("%1%2").arg(tupletType).arg(dotString);
      }
 
//---------------------------------------------------------
//   add
//---------------------------------------------------------
 
void ChordRest::add(Element* e)
      {
      e->setParent(this);
      e->setTrack(track());
      switch (e->type()) {
            case ElementType::ARTICULATION:
                  {
                  Articulation* a = toArticulation(e);
                  _articulations.push_back(a);
                  if (a->timeStretch() != 1.0)
                        score()->fixTicks();          // update tempo map
                  }
                  break;
            case ElementType::LYRICS:
                  _lyrics.push_back(toLyrics(e));
                  break;
            default:
                  qFatal("ChordRest::add: unknown element %s", e->name());
                  break;
            }
      }
 
//---------------------------------------------------------
//   remove
//---------------------------------------------------------
 
void ChordRest::remove(Element* e)
      {
      switch (e->type()) {
            case ElementType::ARTICULATION:
                  {
                  Articulation* a = toArticulation(e);
                  if (!_articulations.removeOne(a))
                        qDebug("ChordRest::remove(): articulation not found");
                  if (a->timeStretch() != 1.0)
                        score()->fixTicks();           // update tempo map
                  }
                  break;
            case ElementType::LYRICS: {
                  toLyrics(e)->removeFromScore();
                  auto i = std::find(_lyrics.begin(), _lyrics.end(), toLyrics(e));
                  if (i != _lyrics.end())
                        _lyrics.erase(i);
                  else
                        qDebug("ChordRest::remove: %s %p not found", e->name(), e);
                  }
                  break;
            default:
                  qFatal("ChordRest::remove: unknown element <%s>", e->name());
            }
      }
 
//---------------------------------------------------------
//   removeDeleteBeam
//      beamed - the chordrest is beamed (will get a (new) beam)
//          remove ChordRest from beam
//          delete beam if empty
//---------------------------------------------------------
 
void ChordRest::removeDeleteBeam(bool beamed)
      {
      if (_beam) {
            Beam* b = _beam;
            _beam->remove(this);
            if (b->empty())
                  score()->undoRemoveElement(b);
            }
      if (!beamed && isChord())
            toChord(this)->layoutStem();
      }
 
//---------------------------------------------------------
//   undoSetBeamMode
//---------------------------------------------------------
 
void ChordRest::undoSetBeamMode(Beam::Mode mode)
      {
      undoChangeProperty(P_ID::BEAM_MODE, int(mode));
      }
 
//---------------------------------------------------------
//   getProperty
//---------------------------------------------------------
 
QVariant ChordRest::getProperty(P_ID propertyId) const
      {
      switch (propertyId) {
            case P_ID::SMALL:      return QVariant(small());
            case P_ID::BEAM_MODE:  return int(beamMode());
            case P_ID::STAFF_MOVE: return staffMove();
            case P_ID::DURATION_TYPE: return QVariant::fromValue(actualDurationType());
            default:               return DurationElement::getProperty(propertyId);
            }
      }
 
//---------------------------------------------------------
//   setProperty
//---------------------------------------------------------
 
bool ChordRest::setProperty(P_ID propertyId, const QVariant& v)
      {
      switch (propertyId) {
            case P_ID::SMALL:
                  setSmall(v.toBool());
                  break;
            case P_ID::BEAM_MODE:
                  setBeamMode(Beam::Mode(v.toInt()));
                  break;
            case P_ID::STAFF_MOVE:
                  setStaffMove(v.toInt());
                  break;
            case P_ID::VISIBLE:
                  setVisible(v.toBool());
                  measure()->checkMultiVoices(staffIdx());
                  break;
            case P_ID::DURATION_TYPE:
                  setDurationType(v.value<TDuration>());
                  break;
            default:
                  return DurationElement::setProperty(propertyId, v);
            }
      triggerLayout();
      return true;
      }
 
//---------------------------------------------------------
//   propertyDefault
//---------------------------------------------------------
 
QVariant ChordRest::propertyDefault(P_ID propertyId) const
      {
      switch (propertyId) {
            case P_ID::SMALL:
                  return false;
            case P_ID::BEAM_MODE:
                  return int(Beam::Mode::AUTO);
            case P_ID::STAFF_MOVE:
                  return 0;
            default:
                  return DurationElement::propertyDefault(propertyId);
            }
      triggerLayout();
      }
 
//---------------------------------------------------------
//   isGrace
//---------------------------------------------------------
 
bool ChordRest::isGrace() const
      {
      return isChord() && toChord(this)->isGrace();
      }
 
//---------------------------------------------------------
//   isGraceBefore
//---------------------------------------------------------
 
bool ChordRest::isGraceBefore() const
      {
      return isChord()
         && (toChord(this)->noteType() & (
           NoteType::ACCIACCATURA | NoteType::APPOGGIATURA | NoteType::GRACE4 | NoteType::GRACE16 | NoteType::GRACE32
           ));
      }
 
//---------------------------------------------------------
//   isGraceAfter
//---------------------------------------------------------
 
bool ChordRest::isGraceAfter() const
      {
      return isChord()
         && (toChord(this)->noteType() & (NoteType::GRACE8_AFTER | NoteType::GRACE16_AFTER | NoteType::GRACE32_AFTER));
      }
 
//---------------------------------------------------------
//   writeBeam
//---------------------------------------------------------
 
void ChordRest::writeBeam(XmlWriter& xml)
      {
      Beam* b = beam();
      if (b && b->elements().front() == this && (MScore::testMode || !b->generated())) {
            b->setId(xml.nextBeamId());
            b->write(xml);
            }
      }
 
//---------------------------------------------------------
//   nextSegmentAfterCR
//    returns first segment at tick CR->tick + CR->actualTicks
//    of given types
//---------------------------------------------------------
 
Segment* ChordRest::nextSegmentAfterCR(SegmentType types) const
      {
      for (Segment* s = segment()->next1MM(types); s; s = s->next1MM(types)) {
            // chordrest ends at tick+actualTicks
            // we return the segment at or after the end of the chordrest
            if (s->tick() >= tick() + actualTicks())
                  return s;
            }
      return 0;
      }
 
//---------------------------------------------------------
//   setTrack
//---------------------------------------------------------
 
void ChordRest::setTrack(int val)
      {
      Element::setTrack(val);
      processSiblings([val] (Element* e) { e->setTrack(val); } );
      }
 
//---------------------------------------------------------
//   setScore
//---------------------------------------------------------
 
void ChordRest::setScore(Score* s)
      {
      Element::setScore(s);
      processSiblings([s] (Element* e) { e->setScore(s); } );
      }
 
//---------------------------------------------------------
//   processSiblings
//---------------------------------------------------------
 
void ChordRest::processSiblings(std::function<void(Element*)> func)
      {
      if (_beam)
            func(_beam);
      for (Articulation* a : _articulations)
            func(a);
      if (_tabDur)
            func(_tabDur);
      for (Lyrics* l : _lyrics)
            func(l);
      if (tuplet())
            func(tuplet());
      }
 
//---------------------------------------------------------
//   nextArticulationOrLyric
//---------------------------------------------------------
 
Element* ChordRest::nextArticulationOrLyric(Element* e)
      {  
      auto i = std::find(_articulations.begin(), _articulations.end(), e);
      if (i != _articulations.end()) {
            if (i != _articulations.end()-1) {
                  return *(i+1);
                  }
            else {
                  if (!_lyrics.empty())
                        return _lyrics[0];
                  else
                        return nullptr;
                  }
            }
      else {
            auto i = std::find(_lyrics.begin(), _lyrics.end(), e);
            if (i != _lyrics.end()) {
                  if (i != _lyrics.end()-1)
                      return *(i+1);
                  }
            }
      return nullptr;
      }
 
//---------------------------------------------------------
//   prevArticulationOrLyric
//---------------------------------------------------------
 
Element* ChordRest::prevArticulationOrLyric(Element* e)
      {
      auto i = std::find(_lyrics.begin(), _lyrics.end(), e);
      if (i != _lyrics.end()) {
            if (i != _lyrics.begin()) {
                  return *(i-1);
                  }
            else {
                  if (!_articulations.empty())
                        return _articulations.back();
                  else
                        return nullptr;
                  }
            }
      else {
            auto i = std::find(_articulations.begin(), _articulations.end(), e);
            if (i != _articulations.end()) {
                  if (i != _articulations.begin())
                        return *(i-1);
                  }
            }
      return nullptr;
      }
 
//---------------------------------------------------------
//   nextElement
//---------------------------------------------------------
 
Element* ChordRest::nextElement()
      {
      Element* e = score()->selection().element();
            if (!e && !score()->selection().elements().isEmpty())
                  e = score()->selection().elements().first();
      switch (e->type()) {
            case ElementType::ARTICULATION:
            case ElementType::LYRICS: {                  
                  Element* next = nextArticulationOrLyric(e);
                  if (next)
                        return next;
                  else
                        break;
                  }
            default: {                  
                  if (!_articulations.empty())
                        return _articulations[0];
                  else if (!_lyrics.empty())
                        return _lyrics[0];                
                  else 
                        break;
                  }
            }
      int staffId = e->staffIdx();
      return segment()->nextElement(staffId);
      }
 
//---------------------------------------------------------
//   prevElement
//---------------------------------------------------------
 
Element* ChordRest::prevElement()
      {
      Element* e = score()->selection().element();
            if (!e && !score()->selection().elements().isEmpty())
                  e = score()->selection().elements().last();
      switch (e->type()) {
            case ElementType::ARTICULATION:
            case ElementType::LYRICS: {
                  Element* prev = prevArticulationOrLyric(e);
                  if (prev)
                        return prev;
                  else {
                        if (type() == ElementType::CHORD)
                              return static_cast<Chord*>(this)->lastElementBeforeSegment();
                        }
                  // fall through
                  }           
            default: {
                  break;                 
                  }
            }
      int staffId = e->staffIdx();
      return segment()->prevElement(staffId);
      }
 
//---------------------------------------------------------
//   lastElementBeforeSegment
//---------------------------------------------------------
 
Element* ChordRest::lastElementBeforeSegment()
      {
      if (!_lyrics.empty()) {
            return _lyrics.back();
            }
      else if (!_articulations.empty()) {
            return _articulations.back();
            }
      else {
            return nullptr;
            }
      }
 
//---------------------------------------------------------
//   nextSegmentElement
//---------------------------------------------------------
 
Element* ChordRest::nextSegmentElement()
      {
      return segment()->firstInNextSegments(staffIdx());
      }
 
//---------------------------------------------------------
//   prevSegmentElement
//---------------------------------------------------------
 
Element* ChordRest::prevSegmentElement()
      {
      return segment()->lastInPrevSegments(staffIdx());
      }
 
QString ChordRest::accessibleExtraInfo() const
      {
      QString rez = "";
      for (Articulation* a : articulations()) {
            if (!score()->selectionFilter().canSelect(a))
                  continue;
            rez = QString("%1 %2").arg(rez).arg(a->screenReaderInfo());
            }
 
      for (Element* l : lyrics()) {
            if (!score()->selectionFilter().canSelect(l))
                  continue;
            rez = QString("%1 %2").arg(rez).arg(l->screenReaderInfo());
            }
 
      if (segment()) {
            for (Element* e : segment()->annotations()) {
                  if (!score()->selectionFilter().canSelect(e))
                        continue;
                  if (e->staffIdx() == staffIdx() )
                        rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
                  }
 
            SpannerMap& smap = score()->spannerMap();
            auto spanners = smap.findOverlapping(tick(), tick());
            for (auto interval : spanners) {
                  Spanner* s = interval.value;
                  if (!score()->selectionFilter().canSelect(s))
                        continue;
                  if (s->type() == ElementType::VOLTA || //voltas are added for barlines
                      s->type() == ElementType::TIE    ) //ties are added in notes
                        continue;
 
                  Segment* seg = 0;
                  if (s->type() == ElementType::SLUR) {
                        if (s->tick() == tick() && s->track() == track())
                              rez = QObject::tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
                        if (s->tick2() == tick() && s->track2() == track())
                              rez = QObject::tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
                        }
                  else  {
                        if (s->tick() == tick() && s->staffIdx() == staffIdx())
                              rez = QObject::tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
                        seg = segment()->next1MM(SegmentType::ChordRest);
                        if (!seg)
                              continue;
                        if (s->tick2() == seg->tick() && s->staffIdx() == staffIdx())
                              rez = QObject::tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
                        }
                  }
            }
      return rez;
      }
 
//---------------------------------------------------------
//   shape
//---------------------------------------------------------
 
Shape ChordRest::shape() const
      {
      Shape shape;
      for (Articulation* a : _articulations)
            shape.add(a->bbox().translated(a->pos()));
      qreal margin = spatium() * .5;
      qreal x1 = 1000000.0;
      qreal x2 = -1000000.0;
      for (Lyrics* l : _lyrics) {
            if (l->autoplace())
                  l->rUserYoffset() = 0.0;
            // for horizontal spacing we only need the lyrics width:
            x1 = qMin(x1, l->bbox().x() - margin + l->pos().x());
            x2 = qMax(x2, x1 + l->bbox().width() + margin);
            }
      if (x2 > x1)
            shape.add(QRectF(x1, 1.0, x2-x1, 0.0));
      return shape;
      }
 
//---------------------------------------------------------
//   lyrics
//---------------------------------------------------------
 
Lyrics* ChordRest::lyrics(int no, Placement p) const
      {
      for (Lyrics* l : _lyrics) {
            if (l->placement() == p && l->no() == no)
                  return l;
            }
      return 0;
      }
 
//---------------------------------------------------------
//   lastVerse
//    return last verse number (starting from 0)
//    return -1 if there are no lyrics;
//---------------------------------------------------------
 
int ChordRest::lastVerse(Placement p) const
      {
      int lastVerse = -1;
 
      for (Lyrics* l : _lyrics) {
            if (l->placement() == p && l->no() > lastVerse)
                  lastVerse = l->no();
            }
 
      return lastVerse;
      }
 
//---------------------------------------------------------
//   flipLyrics
//---------------------------------------------------------
 
void ChordRest::flipLyrics(Lyrics* l)
      {
      Element::Placement p = l->placement();
      if (p == Element::Placement::ABOVE)
            p = Element::Placement::BELOW;
      else
            p = Element::Placement::ABOVE;
      int verses = lastVerse(p);
      l->undoChangeProperty(P_ID::VERSE, verses + 1);
      l->undoChangeProperty(P_ID::AUTOPLACE, true);
      l->undoChangeProperty(P_ID::PLACEMENT, int(p));
      }
 
//---------------------------------------------------------
//   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 ChordRest::removeMarkings(bool /* keepTremolo */)
      {
      qDeleteAll(el());
      qDeleteAll(articulations());
      qDeleteAll(lyrics());
      }
 
}
 

V547 Expression 'botGap' is always false.

V547 Expression 'topGap' is always false.

V779 Unreachable code detected. It is possible that an error is present.