//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2002-2012 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 "rest.h"
#include "score.h"
#include "xml.h"
#include "style.h"
#include "utils.h"
#include "tuplet.h"
#include "sym.h"
#include "stafftext.h"
#include "articulation.h"
#include "chord.h"
#include "note.h"
#include "measure.h"
#include "undo.h"
#include "staff.h"
#include "harmony.h"
#include "segment.h"
#include "stafftype.h"
#include "icon.h"
#include "image.h"
 
namespace Ms {
 
//---------------------------------------------------------
//    Rest
//--------------------------------------------------------
 
Rest::Rest(Score* s)
  : ChordRest(s)
      {
      setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE | ElementFlag::ON_STAFF);
      _beamMode  = Beam::Mode::NONE;
      _gap       = false;
      _sym       = SymId::restQuarter;
      }
 
Rest::Rest(Score* s, const TDuration& d)
  : ChordRest(s)
      {
      setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE | ElementFlag::ON_STAFF);
      _beamMode  = Beam::Mode::NONE;
      _sym       = SymId::restQuarter;
      _gap       = false;
      setDurationType(d);
      if (d.fraction().isValid())
            setDuration(d.fraction());
      }
 
Rest::Rest(const Rest& r, bool link)
   : ChordRest(r, link)
      {
      if (link)
            score()->undo(new Link(const_cast<Rest*>(&r), this));
      _gap     = r._gap;
      _sym     = r._sym;
      dotline  = r.dotline;
      _mmWidth = r._mmWidth;
      }
 
//---------------------------------------------------------
//   Rest::draw
//---------------------------------------------------------
 
void Rest::draw(QPainter* painter) const
      {
      if (
         (staff() && staff()->isTabStaff(tick())
         // in tab staff, do not draw rests is rests are off OR if dur. symbols are on
         && (!staff()->staffType(tick())->showRests() || staff()->staffType(tick())->genDurations())
         && (!measure() || !measure()->isMMRest()))        // show multi measure rest always
         || generated()
            )
            return;
      qreal _spatium = spatium();
 
      painter->setPen(curColor());
 
      if (measure() && measure()->isMMRest()) {
            //only on voice 1
            if (track() % VOICES)
                  return;
            Measure* m = measure();
            int n      = m->mmRestCount();
            qreal pw   = _spatium * .7;
            QPen pen(painter->pen());
            pen.setWidthF(pw);
            painter->setPen(pen);
 
            qreal w  = _mmWidth;
            qreal y  = 0.0;
            qreal x1 = 0.0;
            qreal x2 =  w;
            pw *= .5;
            painter->drawLine(QLineF(x1 + pw, y, x2 - pw, y));
 
            // draw vertical lines:
            pen.setWidthF(_spatium * .2);
            painter->setPen(pen);
            painter->drawLine(QLineF(x1, y-_spatium, x1, y+_spatium));
            painter->drawLine(QLineF(x2, y-_spatium, x2, y+_spatium));
 
            std::vector<SymId>&& s = toTimeSigString(QString("%1").arg(n));
            y  = -_spatium * 1.5 - staff()->height() *.5;
            qreal x = center(x1, x2);
            x -= symBbox(s).width() * .5;
            drawSymbols(s, painter, QPointF(x, y));
            }
      else {
            drawSymbol(_sym, painter);
            int dots = durationType().dots();
            if (dots) {
                  qreal y = dotline * _spatium * .5;
                  qreal dnd = point(score()->styleS(StyleIdx::dotNoteDistance)) * mag();
                  qreal ddd = point(score()->styleS(StyleIdx::dotDotDistance)) * mag();
                  for (int i = 1; i <= dots; ++i) {
                        qreal x = symWidth(_sym) + dnd + ddd * (i - 1);
                        drawSymbol(SymId::augmentationDot, painter, QPointF(x, y));
                        }
                  }
            }
      }
 
//---------------------------------------------------------
//   setUserOff, overriden from Element
//    (- raster vertical position in spatium units) -> no
//    - half rests and whole rests outside the staff are
//      replaced by special symbols with ledger lines
//---------------------------------------------------------
 
void Rest::setUserOff(const QPointF& o)
      {
      qreal _spatium = spatium();
      int line = lrint(o.y()/_spatium);
 
      if (_sym == SymId::restWhole && (line <= -2 || line >= 3))
            _sym = SymId::restWholeLegerLine;
      else if (_sym == SymId::restWholeLegerLine && (line > -2 && line < 4))
            _sym = SymId::restWhole;
      else if (_sym == SymId::restHalf && (line <= -3 || line >= 3))
            _sym = SymId::restHalfLegerLine;
      else if (_sym == SymId::restHalfLegerLine && (line > -3 && line < 3))
            _sym = SymId::restHalf;
 
      Element::setUserOff(o);
      }
 
//---------------------------------------------------------
//   drag
//---------------------------------------------------------
 
QRectF Rest::drag(EditData& ed)
      {
      QPointF s(ed.delta);
      QRectF r(abbox());
 
      // Limit horizontal drag range
      static const qreal xDragRange = spatium() * 5;
      if (fabs(s.x()) > xDragRange)
            s.rx() = xDragRange * (s.x() < 0 ? -1.0 : 1.0);
      setUserOff(QPointF(s.x(), s.y()));
      layout();
      score()->rebuildBspTree();
      return abbox() | r;
      }
 
//---------------------------------------------------------
//   acceptDrop
//---------------------------------------------------------
 
bool Rest::acceptDrop(EditData& data) const
      {
      Element* e = data.element;
      ElementType type = e->type();
      if (
            (type == ElementType::ICON && static_cast<Icon*>(e)->iconType() == IconType::SBEAM)
         || (type == ElementType::ICON && static_cast<Icon*>(e)->iconType() == IconType::MBEAM)
         || (type == ElementType::ICON && static_cast<Icon*>(e)->iconType() == IconType::NBEAM)
         || (type == ElementType::ICON && static_cast<Icon*>(e)->iconType() == IconType::BEAM32)
         || (type == ElementType::ICON && static_cast<Icon*>(e)->iconType() == IconType::BEAM64)
         || (type == ElementType::ICON && static_cast<Icon*>(e)->iconType() == IconType::AUTOBEAM)
         || (type == ElementType::ARTICULATION && static_cast<Articulation*>(e)->isFermata())
         || (type == ElementType::CLEF)
         || (type == ElementType::KEYSIG)
         || (type == ElementType::TIMESIG)
         || (type == ElementType::SYSTEM_TEXT)
         || (type == ElementType::STAFF_TEXT)
         || (type == ElementType::BAR_LINE)
         || (type == ElementType::BREATH)
         || (type == ElementType::CHORD)
         || (type == ElementType::NOTE)
         || (type == ElementType::STAFF_STATE)
         || (type == ElementType::INSTRUMENT_CHANGE)
         || (type == ElementType::DYNAMIC)
         || (type == ElementType::HARMONY)
         || (type == ElementType::TEMPO_TEXT)
         || (type == ElementType::STAFF_TEXT)
         || (type == ElementType::REHEARSAL_MARK)
         || (type == ElementType::FRET_DIAGRAM)
         || (type == ElementType::TREMOLOBAR)
         || (type == ElementType::IMAGE)
         || (type == ElementType::SYMBOL)
         || (type == ElementType::REPEAT_MEASURE && durationType().type() == TDuration::DurationType::V_MEASURE)
         ) {
            return true;
            }
      return false;
      }
 
//---------------------------------------------------------
//   drop
//---------------------------------------------------------
 
Element* Rest::drop(EditData& data)
      {
      Element* e = data.element;
      switch (e->type()) {
            case ElementType::ARTICULATION:
                  {
                  Articulation* a = toArticulation(e);
                  if (!a->isFermata() || !score()->addArticulation(this, a)) {
                        delete e;
                        e = 0;
                        }
                  }
                  return e;
 
            case ElementType::CHORD:
                  {
                  Chord* c              = static_cast<Chord*>(e);
                  Note* n               = c->upNote();
                  Direction dir = c->stemDirection();
                  // score()->select(0, SelectType::SINGLE, 0);
                  NoteVal nval;
                  nval.pitch = n->pitch();
                  nval.headGroup = n->headGroup();
                  Fraction d = score()->inputState().duration().fraction();
                  if (!d.isZero()) {
                        Segment* seg = score()->setNoteRest(segment(), track(), nval, d, dir);
                        if (seg) {
                              ChordRest* cr = static_cast<ChordRest*>(seg->element(track()));
                              if (cr)
                                    score()->nextInputPos(cr, true);
                              }
                        }
                  delete e;
                  }
                  break;
            case ElementType::REPEAT_MEASURE:
                  delete e;
                  if (durationType().type() == TDuration::DurationType::V_MEASURE) {
                        measure()->cmdInsertRepeatMeasure(staffIdx());
                        }
                  break;
 
            case ElementType::SYMBOL:
            case ElementType::IMAGE:
                  e->setParent(this);
                  score()->undoAddElement(e);
                  return e;
 
            default:
                  return ChordRest::drop(data);
            }
      return 0;
      }
 
//---------------------------------------------------------
//   getSymbol
//---------------------------------------------------------
 
SymId Rest::getSymbol(TDuration::DurationType type, int line, int lines, int* yoffset)
      {
      *yoffset = 2;
      switch(type) {
            case TDuration::DurationType::V_LONG:
                  return SymId::restLonga;
            case TDuration::DurationType::V_BREVE:
                  return SymId::restDoubleWhole;
            case TDuration::DurationType::V_MEASURE:
                  if (duration() >= Fraction(2, 1))
                        return SymId::restDoubleWhole;
                  // fall through
            case TDuration::DurationType::V_WHOLE:
                  *yoffset = 1;
                  return (line <= -2 || line >= (lines - 1)) ? SymId::restWholeLegerLine : SymId::restWhole;
            case TDuration::DurationType::V_HALF:
                  return (line <= -3 || line >= (lines - 2)) ? SymId::restHalfLegerLine : SymId::restHalf;
            case TDuration::DurationType::V_QUARTER:
                  return SymId::restQuarter;
            case TDuration::DurationType::V_EIGHTH:
                  return SymId::rest8th;
            case TDuration::DurationType::V_16TH:
                  return SymId::rest16th;
            case TDuration::DurationType::V_32ND:
                  return SymId::rest32nd;
            case TDuration::DurationType::V_64TH:
                  return SymId::rest64th;
            case TDuration::DurationType::V_128TH:
                  return SymId::rest128th;
            case TDuration::DurationType::V_256TH:
                  return SymId::rest256th;
            case TDuration::DurationType::V_512TH:
                  return SymId::rest512th;
            case TDuration::DurationType::V_1024TH:
                  return SymId::rest1024th;
            default:
                  qDebug("unknown rest type %d", int(type));
                  return SymId::restQuarter;
            }
      }
 
//---------------------------------------------------------
//   layout
//---------------------------------------------------------
 
void Rest::layout()
      {
      if (_gap)
            return;
      for (Element* e : el())
            e->layout();
      if (measure() && measure()->isMMRest()) {
            static const qreal verticalLineWidth = .2;
            qreal _spatium = spatium();
            qreal h        = _spatium * (2 + verticalLineWidth);
            qreal w        = _mmWidth + _spatium * verticalLineWidth*.5;
            bbox().setRect(-_spatium * verticalLineWidth*.5, -h * .5, w, h);
 
            // text
            qreal y  = -_spatium * 2.5 - staff()->height() *.5;
            addbbox(QRectF(0, y, w, _spatium * 2));         // approximation
            return;
            }
 
      rxpos() = 0.0;
      if (staff() && staff()->isTabStaff(tick())) {
            StaffType* tab = staff()->staffType(tick());
            // if rests are shown and note values are shown as duration symbols
            if (tab->showRests() && tab->genDurations()) {
                  TDuration::DurationType type = durationType().type();
                  int                     dots = durationType().dots();
                  // if rest is whole measure, convert into actual type and dot values
                  if (type == TDuration::DurationType::V_MEASURE) {
                        int       ticks = measure()->ticks();
                        TDuration dur   = TDuration(Fraction::fromTicks(ticks)).type();
                        type = dur.type();
                        dots = dur.dots();
                        }
                  // symbol needed; if not exist, create, if exists, update duration
                  if (!_tabDur)
                        _tabDur = new TabDurationSymbol(score(), tab, type, dots);
                  else
                        _tabDur->setDuration(type, dots, tab);
                  _tabDur->setParent(this);
// needed?        _tabDur->setTrack(track());
                  _tabDur->layout();
                  setbbox(_tabDur->bbox());
                  setPos(0.0, 0.0);             // no rest is drawn: reset any position might be set for it
                  return;
                  }
            // if no rests or no duration symbols, delete any dur. symbol and chain into standard staff mngmt
            // this is to ensure horiz space is reserved for rest, even if they are not diplayed
            // Rest::draw() will skip their drawing, if not needed
            if(_tabDur) {
                  delete _tabDur;
                  _tabDur = 0;
                  }
            }
 
      dotline = Rest::getDotline(durationType().type());
 
      qreal _spatium = spatium();
      qreal yOff     = userOff().y();
      Staff* stf     = staff();
      StaffType*  st = stf->staffType(tick());
      qreal lineDist = st ? st->lineDistance().val() : 1.0;
      int userLine   = yOff == 0.0 ? 0 : lrint(yOff / (lineDist * _spatium));
      int lines      = st ? st->lines() : 5;
      int lineOffset = computeLineOffset(lines);
 
      int yo;
      _sym = getSymbol(durationType().type(), lineOffset / 2 + userLine, lines, &yo);
      layoutArticulations();
      rypos() = (qreal(yo) + qreal(lineOffset) * .5) * lineDist * _spatium;
      setbbox(symBbox(_sym));
      }
 
//---------------------------------------------------------
//   getDotline
//---------------------------------------------------------
 
int Rest::getDotline(TDuration::DurationType durationType)
      {
      int dl = -1;
      switch(durationType) {
            case TDuration::DurationType::V_64TH:
            case TDuration::DurationType::V_32ND:
                  dl = -3;
                  break;
            case TDuration::DurationType::V_1024TH:
            case TDuration::DurationType::V_512TH:
            case TDuration::DurationType::V_256TH:
            case TDuration::DurationType::V_128TH:
                  dl = -5;
                  break;
            default:
                  dl = -1;
                  break;
            }
      return dl;
      }
 
//---------------------------------------------------------
//   computeLineOffset
//---------------------------------------------------------
 
int Rest::computeLineOffset(int lines)
      {
      Segment* s = segment();
      bool offsetVoices = s && measure() && measure()->hasVoices(staffIdx());
      if (offsetVoices && voice() == 0) {
            // do not offset voice 1 rest if there exists a matching invisible rest in voice 2;
            Element* e = s->element(track() + 1);
            if (e && e->isRest() && (!e->visible() || toRest(e)->isGap())) {
                  Rest* r = static_cast<Rest*>(e);
                  if (r->globalDuration() == globalDuration()) {
                        offsetVoices = false;
                        }
                  }
            }
#if 0
      if (offsetVoices && staff()->mergeMatchingRests()) {
            // automatically merge matching rests in voices 1 & 2 if nothing in any other voice
            // this is not always the right thing to do do, but is useful in choral music
            // and perhaps could be made enabled via a staff property
            // so choral staves can be treated differently than others
            bool matchFound = false;
            bool nothingElse = true;
            int baseTrack = staffIdx() * VOICES;
            for (int v = 0; v < VOICES; ++v) {
                  if (v == voice())
                        continue;
                  Element* e = s->element(baseTrack + v);
                  if (v <= 1) {
                        // try to find match in other voice (1 or 2)
                        if (e && e->type() == ElementType::REST) {
                              Rest* r = static_cast<Rest*>(e);
                              if (r->globalDuration() == globalDuration()) {
                                    matchFound = true;
                                    continue;
                                    }
                              }
                        // no match found; no sense looking for anything else
                        break;
                        }
                  else {
                        // if anything in another voice, do not merge
                        if (e) {
                              nothingElse = false;
                              break;
                              }
                        }
                  }
            if (matchFound && nothingElse)
                  offsetVoices = false;
            }
#endif
 
      int lineOffset    = 0;
      int assumedCenter = 4;
      int actualCenter  = (lines - 1);
      int centerDiff    = actualCenter - assumedCenter;
 
      if (offsetVoices) {
            // move rests in a multi voice context
            bool up = (voice() == 0) || (voice() == 2);     // TODO: use style values
            switch(durationType().type()) {
                  case TDuration::DurationType::V_LONG:
                        lineOffset = up ? -3 : 5;
                        break;
                  case TDuration::DurationType::V_BREVE:
                        lineOffset = up ? -3 : 5;
                        break;
                  case TDuration::DurationType::V_MEASURE:
                        if (duration() >= Fraction(2, 1))   // breve symbol
                              lineOffset = up ? -3 : 5;
                        else
                              lineOffset = up ? -4 : 6;     // whole symbol
                        break;
                  case TDuration::DurationType::V_WHOLE:
                        lineOffset = up ? -4 : 6;
                        break;
                  case TDuration::DurationType::V_HALF:
                        lineOffset = up ? -4 : 4;
                        break;
                  case TDuration::DurationType::V_QUARTER:
                        lineOffset = up ? -4 : 4;
                        break;
                  case TDuration::DurationType::V_EIGHTH:
                        lineOffset = up ? -4 : 4;
                        break;
                  case TDuration::DurationType::V_16TH:
                        lineOffset = up ? -6 : 4;
                        break;
                  case TDuration::DurationType::V_32ND:
                        lineOffset = up ? -6 : 6;
                        break;
                  case TDuration::DurationType::V_64TH:
                        lineOffset = up ? -8 : 6;
                        break;
                  case TDuration::DurationType::V_128TH:
                        lineOffset = up ? -8 : 8;
                        break;
                  case TDuration::DurationType::V_1024TH:
                  case TDuration::DurationType::V_512TH:
                  case TDuration::DurationType::V_256TH:
                        lineOffset = up ? -10 : 6;
                        break;
                  default:
                        break;
                  }
 
            // adjust offsets for staves with other than five lines
            if (lines != 5) {
                  lineOffset += centerDiff;
                  if (centerDiff & 1) {
                        // round to line
                        if (lines == 2 && staff() && staff()->lineDistance(tick()) < 2.0)
                              ;                                         // leave alone
                        else if (lines <= 6)
                              lineOffset += lineOffset > 0 ? -1 : 1;    // round inward
                        else
                              lineOffset += lineOffset > 0 ? 1 : -1;    // round outward
                        }
                  }
            }
      else {
            // Gould says to center rests on middle line or space
            // but subjectively, many rests look strange centered on a space
            // so we do it for 2-line staves only
            if (centerDiff & 1 && lines != 2)
                  centerDiff += 1;  // round down
 
            lineOffset = centerDiff;
            switch(durationType().type()) {
                  case TDuration::DurationType::V_LONG:
                  case TDuration::DurationType::V_BREVE:
                  case TDuration::DurationType::V_MEASURE:
                  case TDuration::DurationType::V_WHOLE:
                        if (lineOffset & 1)
                              lineOffset += 1;  // always round to nearest line
                        else if (lines <= 3)
                              lineOffset += 2;  // special case - move down for 1-line or 3-line staff
                        break;
                  case TDuration::DurationType::V_HALF:
                        if (lineOffset & 1)
                              lineOffset += 1;  // always round to nearest line
                        break;
                  default:
                        break;
                  }
            }
      // DEBUG: subtract this off only to be added back in layout()?
      // that would throw off calculation of when ledger lines are needed
      //if (staff())
      //      lineOffset -= staff()->staffType()->stepOffset();
      return lineOffset;
      }
 
//---------------------------------------------------------
//   centerX
//---------------------------------------------------------
 
qreal Rest::centerX() const
      {
      return symWidth(_sym) * .5;
      }
 
//---------------------------------------------------------
//   upPos
//---------------------------------------------------------
 
qreal Rest::upPos() const
      {
      return symBbox(_sym).y();
      }
 
//---------------------------------------------------------
//   downPos
//---------------------------------------------------------
 
qreal Rest::downPos() const
      {
      return symBbox(_sym).y() + symHeight(_sym);
      }
 
//---------------------------------------------------------
//   scanElements
//---------------------------------------------------------
 
void Rest::scanElements(void* data, void (*func)(void*, Element*), bool all)
      {
      ChordRest::scanElements(data, func, all);
      for (Element* e : el())
            e->scanElements(data, func, all);
      if (!isGap())
            func(data, this);
      }
 
//---------------------------------------------------------
//   setMMWidth
//---------------------------------------------------------
 
void Rest::setMMWidth(qreal val)
      {
      _mmWidth = val;
      layout();
      }
 
//---------------------------------------------------------
//   reset
//---------------------------------------------------------
 
void Rest::reset()
      {
      undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::NONE));
      ChordRest::reset();
      }
 
//---------------------------------------------------------
//   mag
//---------------------------------------------------------
 
qreal Rest::mag() const
      {
      qreal m = staff()->mag(tick());
      if (small())
            m *= score()->styleD(StyleIdx::smallNoteMag);
      return m;
      }
 
//---------------------------------------------------------
//   upLine
//---------------------------------------------------------
 
int Rest::upLine() const
      {
      qreal _spatium = spatium();
      return lrint((pos().y() + bbox().top() + _spatium) * 2 / _spatium);
      }
 
//---------------------------------------------------------
//   downLine
//---------------------------------------------------------
 
int Rest::downLine() const
      {
      qreal _spatium = spatium();
      return lrint((pos().y() + bbox().top() + _spatium) * 2 / _spatium);
      }
 
//---------------------------------------------------------
//   stemPos
//    point to connect stem
//---------------------------------------------------------
 
QPointF Rest::stemPos() const
      {
      return pagePos();
      }
 
//---------------------------------------------------------
//   stemPosBeam
//    return stem position of note on beam side
//    return canvas coordinates
//---------------------------------------------------------
 
QPointF Rest::stemPosBeam() const
      {
      QPointF p(pagePos());
      if (_up)
            p.ry() += bbox().top() + spatium() * 2;
      else
            p.ry() += bbox().bottom() - spatium() * 2;
      return p;
      }
 
//---------------------------------------------------------
//   stemPosX
//---------------------------------------------------------
 
qreal Rest::stemPosX() const
      {
      if (_up)
            return bbox().right();
      else
            return bbox().left();
      }
 
//---------------------------------------------------------
//   accent
//---------------------------------------------------------
 
bool Rest::accent()
      {
      return (voice() >= 2 && small());
      }
 
//---------------------------------------------------------
//   setAccent
//---------------------------------------------------------
 
void Rest::setAccent(bool flag)
      {
      undoChangeProperty(P_ID::SMALL, flag);
      if (voice() % 2 == 0) {
            if (flag) {
                  qreal yOffset = -(bbox().bottom());
                  if (durationType() >= TDuration::DurationType::V_HALF)
                        yOffset -= staff()->spatium(tick()) * 0.5;
                  undoChangeProperty(P_ID::USER_OFF, QPointF(0.0, yOffset));
                  }
            else {
                  undoChangeProperty(P_ID::USER_OFF, QPointF());
                  }
            }
      }
 
//---------------------------------------------------------
//   accessibleInfo
//---------------------------------------------------------
 
QString Rest::accessibleInfo() const
      {
      QString voice = QObject::tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
      return QObject::tr("%1; Duration: %2; %3").arg(Element::accessibleInfo()).arg(durationUserName()).arg(voice);
      }
 
//---------------------------------------------------------
//   accessibleInfo
//---------------------------------------------------------
 
QString Rest::screenReaderInfo() const
      {
      QString voice = QObject::tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
      return QString("%1 %2 %3").arg(Element::accessibleInfo()).arg(durationUserName()).arg(voice);
      }
 
//---------------------------------------------------------
//   add
//---------------------------------------------------------
 
void Rest::add(Element* e)
      {
      e->setParent(this);
      e->setTrack(track());
 
      switch(e->type()) {
            case ElementType::SYMBOL:
            case ElementType::IMAGE:
                  el().push_back(e);
                  break;
            default:
                  ChordRest::add(e);
                  break;
            }
      }
 
//---------------------------------------------------------
//   remove
//---------------------------------------------------------
 
void Rest::remove(Element* e)
      {
      switch(e->type()) {
            case ElementType::SYMBOL:
            case ElementType::IMAGE:
                  if (!el().remove(e))
                        qDebug("Rest::remove(): cannot find %s", e->name());
                  break;
            default:
                  ChordRest::remove(e);
                  break;
            }
      }
 
//--------------------------------------------------
//   Rest::write
//---------------------------------------------------------
 
void Rest::write(XmlWriter& xml) const
      {
      if (_gap)
            return;
      xml.stag(name());
      ChordRest::writeProperties(xml);
      el().write(xml);
      xml.etag();
      }
 
//---------------------------------------------------------
//   Rest::read
//---------------------------------------------------------
 
void Rest::read(XmlReader& e)
      {
      while (e.readNextStartElement()) {
            const QStringRef& tag(e.name());
            if (tag == "Symbol") {
                  Symbol* s = new Symbol(score());
                  s->setTrack(track());
                  s->read(e);
                  add(s);
                  }
            else if (tag == "Image") {
                  if (MScore::noImages)
                        e.skipCurrentElement();
                  else {
                        Image* image = new Image(score());
                        image->setTrack(track());
                        image->read(e);
                        add(image);
                        }
                  }
            else if (ChordRest::readProperties(e))
                  ;
            else
                  e.unknown();
            }
      }
 
//---------------------------------------------------------
//   getProperty
//---------------------------------------------------------
 
QVariant Rest::getProperty(P_ID propertyId) const
      {
      switch (propertyId) {
            case P_ID::GAP:
                  return _gap;
            default:
                  return ChordRest::getProperty(propertyId);
            }
      }
 
//---------------------------------------------------------
//   propertyDefault
//---------------------------------------------------------
 
QVariant Rest::propertyDefault(P_ID propertyId) const
      {
      switch (propertyId) {
            case P_ID::GAP:
                  return false;
            default:
                  return ChordRest::propertyDefault(propertyId);
            }
      }
 
//---------------------------------------------------------
//   setProperty
//---------------------------------------------------------
 
bool Rest::setProperty(P_ID propertyId, const QVariant& v)
      {
      switch (propertyId) {
            case P_ID::GAP:
                  _gap = v.toBool();
                  score()->setLayout(tick());
                  break;
 
            case P_ID::USER_OFF:
                  score()->addRefresh(canvasBoundingRect());
                  setUserOff(v.toPointF());
                  layout();
                  score()->addRefresh(canvasBoundingRect());
                  if (beam())
                        score()->setLayout(tick());
                  break;
            default:
                  return ChordRest::setProperty(propertyId, v);
            }
      return true;
      }
 
//---------------------------------------------------------
//   nextElement
//---------------------------------------------------------
 
Element* Rest::nextElement()
      {
      return ChordRest::nextElement();
      }
 
//---------------------------------------------------------
//   prevElement
//---------------------------------------------------------
 
Element* Rest::prevElement()
      {
      return ChordRest::prevElement();
      }
 
//---------------------------------------------------------
//   shape
//---------------------------------------------------------
 
Shape Rest::shape() const
      {
      Shape shape;
      if (!_gap) {
            shape.add(ChordRest::shape());
            if (parent() && measure() && measure()->isMMRest())
                  shape.add(QRectF(0.0, 0.0, score()->styleP(StyleIdx::minMMRestWidth), height()));
            else
                  shape.add(bbox());
            }
      return shape;
      }
 
}

V501 There are identical sub-expressions '(type == ElementType::STAFF_TEXT)' to the left and to the right of the '||' operator.

V595 The 'staff()' pointer was utilized before it was verified against nullptr. Check lines: 341, 347.

V524 It is odd that the body of 'downLine' function is fully equivalent to the body of 'upLine' function.