    //=============================================================================
    //  MuseScore
    //  Music Composition & Notation
    //
    //  Copyright (C) 2009-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 "repeatlist.h"
    #include "score.h"
    #include "measure.h"
    #include "tempo.h"
    #include "volta.h"
    #include "segment.h"
    #include "marker.h"
    #include "jump.h"

    namespace Ms {

    //---------------------------------------------------------
    //   searchVolta
    //    return volta at tick
    //---------------------------------------------------------

    Volta* Score::searchVolta(int tick) const
          {
          for (const std::pair<int,Spanner*>& p : _spanner.map()) {
                Spanner* s = p.second;
                if (s->type() != ElementType::VOLTA)
                      continue;
                Volta* volta = static_cast<Volta*>(s);
                if (tick >= volta->tick() && tick < volta->tick2())
                      return volta;
                }
          return 0;
          }

    //---------------------------------------------------------
    //   searchLabel
    //---------------------------------------------------------

    Measure* Score::searchLabel(const QString& s)
          {
          if (s == "start")
                return firstMeasure();
          else if (s == "end")
                return lastMeasure();
          for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
                for (auto e : m->el()) {
                      if (e->type() == ElementType::MARKER) {
                            const Marker* marker = static_cast<const Marker*>(e);
                            if (marker->label() == s)
                                  return m;
                            }
                      }
                }
          return 0;
          }

    //---------------------------------------------------------
    //   searchLabelWithinSectionFirst
    //---------------------------------------------------------

    Measure* Score::searchLabelWithinSectionFirst(const QString& s, Measure* sectionStartMeasure, Measure* sectionEndMeasure)
          {
          if (s == "start")
                return sectionStartMeasure;
          else if (s == "end")
                return sectionEndMeasure;
          for (Measure* m = sectionStartMeasure; m && (m != sectionEndMeasure->nextMeasure()); m = m->nextMeasure()) {
                for (auto e : m->el()) {
                      if (e->type() == ElementType::MARKER) {
                            const Marker* marker = static_cast<const Marker*>(e);
                            if (marker->label() == s)
                                  return m;
                            }
                      }
                }

          // if did not find label within section, then search for label in entire score
          return searchLabel(s);
          }

    //---------------------------------------------------------
    //   RepeatLoop
    //---------------------------------------------------------

    struct RepeatLoop {
          enum class LoopType : char { REPEAT, JUMP };

          LoopType type;
          Measure* m;   // start measure of LoopType::REPEAT
          int count;
          QString stop, cont;

          RepeatLoop() {}
          RepeatLoop(Measure* _m)  {
                m     = _m;
                count = 0;
                type  = LoopType::REPEAT;
                }
          RepeatLoop(const QString s, const QString c)
             : stop(s), cont(c)
                {
                m    = 0;
                type = LoopType::JUMP;
                }
          };

    //---------------------------------------------------------
    //   RepeatSegment
    //---------------------------------------------------------

    RepeatSegment::RepeatSegment()
          {
          tick       = 0;
          len        = 0;
          utick      = 0;
          utime      = 0.0;
          timeOffset = 0.0;
          }

    //---------------------------------------------------------
    //   RepeatList
    //---------------------------------------------------------

    RepeatList::RepeatList(Score* s)
          {
          _score = s;
          idx1  = 0;
          idx2  = 0;
          }

    //---------------------------------------------------------
    //   ticks
    //---------------------------------------------------------

    int RepeatList::ticks()
          {
          if (length() > 0) {
                RepeatSegment* s = last();
                return s->utick + s->len;
                }
          return 0;
          }

    //---------------------------------------------------------
    //   update
    //---------------------------------------------------------

    void RepeatList::update()
          {
          const TempoMap* tl = _score->tempomap();

          int utick = 0;
          qreal t  = 0;

          for(RepeatSegment* s : *this) {
                s->utick      = utick;
                s->utime      = t;
                qreal ct      = tl->tick2time(s->tick);
                s->timeOffset = t - ct;
                utick        += s->len;
                t            += tl->tick2time(s->tick + s->len) - ct;
                }
          }

    //---------------------------------------------------------
    //   utick2tick
    //---------------------------------------------------------

    int RepeatList::utick2tick(int tick) const
          {
          unsigned n = size();
          if (n == 0)
                return tick;
          if (tick < 0)
                return 0;
          unsigned ii = (idx1 < n) && (tick >= at(idx1)->utick) ? idx1 : 0;
          for (unsigned i = ii; i < n; ++i) {
                if ((tick >= at(i)->utick) && ((i + 1 == n) || (tick < at(i+1)->utick))) {
                      idx1 = i;
                      return tick - (at(i)->utick - at(i)->tick);
                      }
                }
          if (MScore::debugMode) {
                qFatal("tick %d not found in RepeatList", tick);
                }
          return 0;
          }

    //---------------------------------------------------------
    //   tick2utick
    //---------------------------------------------------------

    int RepeatList::tick2utick(int tick) const
          {
          for (const RepeatSegment* s : *this) {
                if (tick >= s->tick && tick < (s->tick + s->len))
                      return s->utick + (tick - s->tick);
                }
          return last()->utick + (tick - last()->tick);
          }

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

    qreal RepeatList::utick2utime(int tick) const
          {
          unsigned n = size();
          unsigned ii = (idx1 < n) && (tick >= at(idx1)->utick) ? idx1 : 0;
          for (unsigned i = ii; i < n; ++i) {
                if ((tick >= at(i)->utick) && ((i + 1 == n) || (tick < at(i+1)->utick))) {
                      int t     = tick - (at(i)->utick - at(i)->tick);
                      qreal tt = _score->tempomap()->tick2time(t) + at(i)->timeOffset;
                      return tt;
                      }
                }
          return 0.0;
          }

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

    int RepeatList::utime2utick(qreal t) const
          {
          unsigned n = size();
          unsigned ii = (idx2 < n) && (t >= at(idx2)->utime) ? idx2 : 0;
          for (unsigned i = ii; i < n; ++i) {
                if ((t >= at(i)->utime) && ((i + 1 == n) || (t < at(i+1)->utime))) {
                      idx2 = i;
                      return _score->tempomap()->time2tick(t - at(i)->timeOffset) + (at(i)->utick - at(i)->tick);
                      }
                }
          if (MScore::debugMode) {
                qFatal("time %f not found in RepeatList", t);
                }
          return 0;
          }

    //---------------------------------------------------------
    //   dump
    //---------------------------------------------------------

    void RepeatList::dump() const
          {
    #if 0
          qDebug("==Dump Repeat List:==");
          foreach(const RepeatSegment* s, *this) {
                qDebug("%p  tick: %3d(%d) %3d(%d) len %d(%d) beats  %f + %f", s,
                   s->utick / MScore::division,
                   s->utick / MScore::division / 4,
                   s->tick / MScore::division,
                   s->tick / MScore::division / 4,
                   s->len / MScore::division,
                   s->len / MScore::division / 4,
                   s->utime, s->timeOffset);
                }
    #endif
          }

    //---------------------------------------------------------
    //   unwind
    //    implements:
    //          - repeats
    //          - volta
    //          - d.c. al fine
    //          - d.s. al fine
    //          - d.s. al coda
    //---------------------------------------------------------

    void RepeatList::unwind()
          {
          qDeleteAll(*this);
          clear();
          Measure* fm = _score->firstMeasure();
          if (!fm)
                return;

     //qDebug("unwind===================");

          for (Measure* m = fm; m; m = m->nextMeasure())
                m->setPlaybackCount(0);

          MeasureBase* sectionStartMeasureBase = NULL; // NULL indicates haven't discovered starting Measure of section
          MeasureBase* sectionEndMeasureBase = NULL;

          // partition score by section breaks and unwind individual sections seperately
          // note: section breaks may occur on non-Measure frames, so must seach list of all MeasureBases
          for (MeasureBase* mb = _score->first(); mb; mb = mb->next()) {

                // unwindSection only deals with real Measures, so sectionEndMeasureBase and sectionStartMeasureBase will only point to real Measures
                if (mb->isMeasure()) {
                      sectionEndMeasureBase = mb; // ending measure of section is the most recently encountered actual Measure

                      // starting measure of section will be the first non-NULL actual Measure encountered
                      if (sectionStartMeasureBase == NULL)
                            sectionStartMeasureBase = mb;
                      }

                // if found section break or reached final MeasureBase of score, then unwind
                if (mb->sectionBreak() || !mb->nextMeasure()) {

                      // only unwind if section starts and ends with actual real measure
                      if (sectionStartMeasureBase && sectionEndMeasureBase) {
                            unwindSection(reinterpret_cast<Measure*>(sectionStartMeasureBase), reinterpret_cast<Measure*>(sectionEndMeasureBase));
                            sectionStartMeasureBase = 0; // reset to NULL to indicate that don't know starting Measure of next section after starting new section
                            sectionEndMeasureBase   = 0;
                            }
                      else {
                            qDebug( "Will not unroll a section that doesn't start or end with an actual measure. sectionStartMeasureBase = %p, sectionEndMeasureBase = %p",
                                        sectionStartMeasureBase, sectionEndMeasureBase);
                            }
                      }
                }

          update();
          dump();
          }

    //---------------------------------------------------------
    //   unwindSection
    //    unwinds from sectionStartMeasure through sectionEndMeasure
    //    appends repeat segments to rs
    //---------------------------------------------------------

    void RepeatList::unwindSection(Measure* sectionStartMeasure, Measure* sectionEndMeasure)
          {
    //      qDebug("unwind %d-measure section starting %p through %p", sectionEndMeasure->no()+1, sectionStartMeasure, sectionEndMeasure);

          QList<Jump*> jumps; // take the jumps only once so store them

          rs         = new RepeatSegment;
          rs->tick   = sectionStartMeasure->tick(); // prepare initial repeat segment for start of this section

          Measure* endRepeat  = 0; // measure where the current repeat should stop
          Measure* continueAt = 0; // measure where the playback should continue after the repeat (To coda)
          Measure* m          = 0;
          int loop            = 0; // keeps track of how many times have repeated a :| (Repeat::END)
          int repeatCount     = 0;
          bool isGoto         = false;
          bool playRepeats    = false;

          for (Measure* nm = sectionStartMeasure; nm; ) {
                m = nm;
                m->setPlaybackCount(m->playbackCount() + 1);
                bool doJump = false;          // process jump after endrepeat

                // during any DC or DS, will take last time through repeat
                if (isGoto && !playRepeats && m->repeatEnd())
                      loop = m->repeatCount() - 1;

                if (endRepeat) {
                      Volta* volta = _score->searchVolta(m->tick());
                      if (volta && !volta->hasEnding(m->playbackCount())) {
                            // skip measure
                            if (rs->tick < m->tick()) {
                                  rs->len = m->tick() - rs->tick;
                                  append(rs);
                                  rs = new RepeatSegment;
                                  }
                            rs->tick = m->endTick();
                            }
                      else if (m->repeatJump()) {
                            doJump = true;
                            isGoto = false;
                            }
                      }
                else if (m->repeatJump())     // Jumps are only accepted outside of other repeats
                      doJump = true;

                if (isGoto && (endRepeat == m)) {
                      if (continueAt == 0)
                            break;
                      rs->len = m->endTick() - rs->tick;
                      append(rs);
                      rs       = new RepeatSegment;
                      rs->tick = continueAt->tick();
                      nm       = continueAt;
                      isGoto   = false;
                      endRepeat = 0;
                      continue;
                      }
                else if (m->repeatEnd()) {
                      if (endRepeat == m) {
                            ++loop;
                            if (loop >= repeatCount) {
                                  endRepeat = 0;
                                  loop = 0;
                                  }
                            else {
                                  nm = jumpToStartRepeat(m);
                                  continue;
                                  }
                            }
                      else if (endRepeat == 0) {
                            if (m->playbackCount() >= m->repeatCount())
                                  break;
                            endRepeat   = m;
                            repeatCount = m->repeatCount();
                            loop        = 1;
                            nm          = jumpToStartRepeat(m);
                            continue;
                            }
                      else {
                            ++loop;
                            if (loop < repeatCount) {
                                  nm = jumpToStartRepeat(m);
                                  continue;
                                  }
                            }
                      }
                if (doJump && !isGoto) {
                      Jump* jump = 0;
                      foreach (Element* e, m->el()) {
                            if (e->isJump()) {
                                  jump = toJump(e);
                                  break;
                                  }
                            }
                      // jump only once
                      if (jumps.contains(jump)) {
                            if (endRepeat == _score->searchLabelWithinSectionFirst(jump->playUntil(), sectionStartMeasure, sectionEndMeasure))
                                  endRepeat = 0;

                            nm = m->nextMeasure();
                            if (nm == sectionEndMeasure->nextMeasure())
                                  break;
                            else
                                  continue;
                            }
                      jumps.append(jump);
                      if (jump) {
                            nm          = _score->searchLabelWithinSectionFirst(jump->jumpTo()    , sectionStartMeasure, sectionEndMeasure);
                            endRepeat   = _score->searchLabelWithinSectionFirst(jump->playUntil() , sectionStartMeasure, sectionEndMeasure);
                            continueAt  = _score->searchLabelWithinSectionFirst(jump->continueAt(), sectionStartMeasure, sectionEndMeasure);

                            if (nm && endRepeat) {
                                  isGoto      = true;
                                  playRepeats = jump->playRepeats();
                                  rs->len = m->endTick() - rs->tick;
                                  append(rs);
                                  rs = new RepeatSegment;
                                  rs->tick  = nm->tick();
                                  continue;
                                  }
                            }
                      else
                            qDebug("Jump not found");
                      }

                // keep looping until reach end of score or end of the section
                nm = m->nextMeasure();
                if (nm == sectionEndMeasure->nextMeasure())
                      break;
                }

          // append the final repeat segment of that section
          if (rs) {
                rs->len = m->endTick() - rs->tick;
                if (rs->len)
                      append(rs);
                else
                      delete rs;
                }
          }

    //---------------------------------------------------------
    //   jumpToStartRepeat
    //---------------------------------------------------------

    Measure* RepeatList::jumpToStartRepeat(Measure* m)
          {
          // finalize the previous repeat segment
          rs->len = m->tick() + m->ticks() - rs->tick;
          append(rs);

          // search backwards until find start of repeat
          while (true) {

                if (m->repeatStart())
                      break;

                if (m == _score->firstMeasure())
                      break;

                if (m->prevMeasure()->sectionBreak())
                      break;

                m = m->prevMeasure();
                }

          // initialize the next repeat segment
          rs        = new RepeatSegment;
          rs->tick  = m->tick();
          return m;
          }

    }

↑ V595 The 'jump' pointer was utilized before it was verified against nullptr. Check lines: 429, 439.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: rs.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: type, m, count.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: count.