commit e90f7c9
Author: Tommaso Cucinotta <tommaso.cucinotta@gmail.com>
Date:   Thu Jun 21 00:37:41 2012 +0100

    Patch for automatic repeat detector.

diff --git a/libmscore/beam.cpp b/libmscore/beam.cpp
index 0de5d46..fa9fa9e 100644
--- a/libmscore/beam.cpp
+++ b/libmscore/beam.cpp
@@ -1816,6 +1816,8 @@ void Beam::layout2(QList<ChordRest*>crl, SpannerSegmentType st, int frag)
 
 void Beam::write(Xml& xml, P_ID id) const
       {
+      if (xml.ignoreBeams)
+            return;
       xml.tag(id, getProperty(id), propertyDefault(id));
       }
 
@@ -1825,7 +1827,7 @@ void Beam::write(Xml& xml, P_ID id) const
 
 void Beam::write(Xml& xml) const
       {
-      if (_elements.isEmpty())
+      if (_elements.isEmpty() || xml.ignoreBeams)
             return;
       xml.stag(QString("Beam id=\"%1\"").arg(_id));
       Element::writeProperties(xml);
diff --git a/libmscore/chordrest.cpp b/libmscore/chordrest.cpp
index 2c4b0a5..1aee6dd 100644
--- a/libmscore/chordrest.cpp
+++ b/libmscore/chordrest.cpp
@@ -183,10 +183,10 @@ void ChordRest::writeProperties(Xml& xml) const
       foreach(Spanner* s, _spannerBack)
             xml.tagE(QString("Slur type=\"stop\" number=\"%1\"").arg(s->id()+1));
 #ifndef NDEBUG
-      if (_beam && (score()->testMode() || !_beam->generated()))
+      if (_beam && (score()->testMode() || !_beam->generated()) && !xml.ignoreBeams)
             xml.tag("Beam", _beam->id());
 #else
-      if (!xml.clipboardmode && _beam && !_beam->generated())
+      if (!xml.clipboardmode && _beam && !_beam->generated() && !xml.ignoreBeams)
             xml.tag("Beam", _beam->id());
 #endif
       foreach(Lyrics* lyrics, _lyricsList) {
diff --git a/libmscore/measure.cpp b/libmscore/measure.cpp
index b4cc028..9b02005 100644
--- a/libmscore/measure.cpp
+++ b/libmscore/measure.cpp
@@ -1811,7 +1811,7 @@ void Measure::write(Xml& xml, int staff, bool writeSystemElements) const
 
 void Measure::write(Xml& xml) const
       {
-      xml.stag(QString("Measure tick=\"%1\"").arg(tick()));
+      xml.stag(QString("Measure tick=\"%1\"").arg(tick() - xml.baseTick));
       xml.curTick = tick();
 
       if (_repeatFlags & RepeatStart)
@@ -1842,7 +1842,7 @@ void Measure::write(Xml& xml) const
                         if (e->isDurationElement())
                               static_cast<DurationElement*>(e)->writeTuplet(xml);
                         if (segment->tick() != xml.curTick) {
-                              xml.tag("tick", segment->tick());
+                              xml.tag("tick", segment->tick() - xml.baseTick);
                               xml.curTick = segment->tick();
                               }
                         e->write(xml);
diff --git a/libmscore/xml.cpp b/libmscore/xml.cpp
index 365ab8d..7c03510 100644
--- a/libmscore/xml.cpp
+++ b/libmscore/xml.cpp
@@ -37,6 +37,8 @@ Xml::Xml()
       curTrack      = -1;
       tickDiff      = 0;
       trackDiff     = 0;
+      baseTick      = 0;
+      ignoreBeams   = false;
       clipboardmode = false;
       excerptmode   = false;
       tupletId      = 1;
@@ -54,6 +56,8 @@ Xml::Xml(QIODevice* device)
       curTrack      = -1;
       tickDiff      = 0;
       trackDiff     = 0;
+      baseTick      = 0;
+      ignoreBeams   = false;
       clipboardmode = false;
       excerptmode   = false;
       tupletId      = 1;
diff --git a/libmscore/xml.h b/libmscore/xml.h
index c7ac02a..6e5d759 100644
--- a/libmscore/xml.h
+++ b/libmscore/xml.h
@@ -35,6 +35,9 @@ class Xml : public QTextStream {
       int tickDiff;
       int trackDiff;          // saved track is curTrack-trackDiff
 
+      int baseTick;		// subtracted to all reported tick
+      bool ignoreBeams;		// suppress Beams in output
+
       bool clipboardmode;     // used to modify write() behaviour
       bool excerptmode;       // true when writing a part
       bool writeOmr;          // false if writing into *.msc file
diff --git a/mscore/CMakeLists.txt b/mscore/CMakeLists.txt
index 38acf03..0f81915 100644
--- a/mscore/CMakeLists.txt
+++ b/mscore/CMakeLists.txt
@@ -178,6 +178,7 @@ add_executable ( ${ExecutableName}
       inspectorBase.cpp inspectorBeam.cpp masterpalette.cpp
       inspectorGroupElement.cpp dragdrop.cpp inspectorImage.cpp
       waveview.cpp musicxmlsupport.cpp
+      repeatdetector.cpp
       ${OMR_FILES}
       ${AUDIO}
       )
diff --git a/mscore/actions.cpp b/mscore/actions.cpp
index 25a8839..ff809e2 100644
--- a/mscore/actions.cpp
+++ b/mscore/actions.cpp
@@ -2426,6 +2426,14 @@ Shortcut Shortcut::sc[] = {
          "unset-visible",
          QT_TRANSLATE_NOOP("action","set unvisible")
          ),
+      Shortcut(
+         STATE_NORMAL | STATE_NOTE_ENTRY,
+         0,
+         "repeat-detector",
+         QT_TRANSLATE_NOOP("action","Repeat detector"),
+         QT_TRANSLATE_NOOP("action","Repeat Detector"),
+         QT_TRANSLATE_NOOP("action","Repeat Detector")
+         ),
       // xml==0  marks end of list
       Shortcut(0, 0, 0, 0)
       };
diff --git a/mscore/musescore.cpp b/mscore/musescore.cpp
index 97dbab2..5bc6a42 100644
--- a/mscore/musescore.cpp
+++ b/mscore/musescore.cpp
@@ -748,6 +748,8 @@ MuseScore::MuseScore()
       menuEdit->addSeparator();
       menuEdit->addAction(getAction("debugger"));
       menuEdit->addSeparator();
+      menuEdit->addAction(getAction("repeat-detector"));
+      menuEdit->addSeparator();
 
       menuProfiles = new QMenu(tr("Pr&ofiles"));
       connect(menuProfiles, SIGNAL(aboutToShow()), SLOT(showProfileMenu()));
diff --git a/mscore/repeatdetector.cpp b/mscore/repeatdetector.cpp
new file mode 100644
index 0000000..e1dbcc5
--- /dev/null
+++ b/mscore/repeatdetector.cpp
@@ -0,0 +1,134 @@
+#include "repeatdetector.h"
+
+#include "libmscore/score.h"
+#include "libmscore/segment.h"
+#include "libmscore/measure.h"
+#include "libmscore/chord.h"
+#include "libmscore/volta.h"
+#include "libmscore/spanner.h"
+#include "libmscore/system.h"
+#include "scoreview.h"
+#include <QBuffer>
+
+RepeatDetector::RepeatDetector()
+{
+}
+
+/** @TODO An improvement here might be to pass a spec of the notation items
+ ** to consider/ignore during the match (e.g., a set of booleans in a struct).
+ **/
+static bool cmpMeasure(Measure *ma, int ticka, Measure *mb, int tickb) {
+  QBuffer bufa;
+  if (!bufa.open(QIODevice::WriteOnly))
+    return false;
+  QBuffer bufb;
+  if (!bufb.open(QIODevice::WriteOnly))
+    return false;
+  Xml xmla;
+  xmla.setDevice(&bufa);
+  xmla.setCodec("utf8");
+  Xml xmlb;
+  xmlb.setDevice(&bufb);
+  xmlb.setCodec("utf8");
+  xmla.baseTick = ticka;
+  xmla.ignoreBeams = true;
+  ma->write(xmla);
+  xmlb.baseTick = tickb;
+  xmlb.ignoreBeams = true;
+  mb->write(xmlb);
+  qDebug() << "bufa: " << bufa.data() << "\n";
+  qDebug() << "bufb: " << bufb.data() << "\n";
+  //qDebug() << "CMP " << ma << " " << mb << " RETURNING: " << (bufa.data() == bufb.data());
+  return bufa.data() == bufb.data();
+}
+
+void RepeatDetector::detect(Score *score, int min_repeat_len, int max_volta_len) {
+  int i1 = 0;
+  for (Measure* m1 = score->firstMeasure(); m1; m1 = m1->nextMeasure(), ++i1) {
+    // Begin of new repeat segment forbidden at a measure where another repeat segment already begins or ends
+    if (m1->repeatFlags())
+      continue;
+    int ticka = m1->tick();
+    Measure *m2 = m1->nextMeasure();
+    int i2 = i1 + 1;
+    int repeat_len = 1;
+    while (repeat_len < min_repeat_len && m2) {
+      ++repeat_len;
+      m2 = m2->nextMeasure();
+      ++i2;
+    }
+    for (; m2; m2 = m2->nextMeasure(), ++i2, ++repeat_len) {
+      // New repeat segment cannot contain a measure where another repeat segment already begins or ends
+      if (m2->repeatFlags())
+	break;
+      int tickb = m2->tick();
+      Measure *m3 = m1;
+      Measure *m4 = m2;
+      int len = 0;
+      int i3 = i1;
+      int i4 = i2;
+      while (len < repeat_len && m3 && m4) {
+	bool eq = cmpMeasure(m3, ticka, m4, tickb);
+	qDebug() << "num: " << i1 << i2 << i3 << i4 << "eq:" << eq << ", len:" << len << ", repeat_len:" << repeat_len;
+	if (!eq)
+	  break;
+	++len;
+	m3 = m3->nextMeasure();
+	m4 = m4->nextMeasure();
+	++i3;
+	++i4;
+      }
+      if (len >= repeat_len - max_volta_len && m1 && m3) {
+	qDebug() << "i1: " << i1 << ", i2: " << i2 << ", len: " << len << ", repeat_len: " << repeat_len;
+	do {
+	  m1->setRepeatFlags(m1->repeatFlags() | RepeatStart);
+	  Measure *m2_prev = m2->prevMeasure();
+	  if (!m2_prev)
+	    break;
+	  m2_prev->setRepeatFlags(m2_prev->repeatFlags() | RepeatEnd);
+	
+	  for (MeasureBase *mb = m2; mb != m4;) {
+	    MeasureBase *tmp = mb;
+	    mb = mb->next();
+	    score->removeElement(tmp);
+	  }
+
+	  if (len == repeat_len)
+	    return;
+
+	  Volta *v1 = new Volta(score);
+	  v1->setSubtype(VOLTA_CLOSED);
+	  v1->setLen(score->spatium() * 7);
+	  v1->setText("1.");
+	  QList<int> il;
+	  il.append(1);
+	  v1->setEndings(il);
+	  v1->setTrack(0);
+	
+	  v1->setStartMeasure(m3);
+	  v1->setEndMeasure(m2_prev);
+	  m3->add(v1);
+
+	  Volta *v2 = new Volta(score);
+	  v2->setSubtype(VOLTA_CLOSED);
+	  v2->setLen(score->spatium() * 7);
+	  v2->setText("2.");
+	  il.clear();
+	  il.append(2);
+	  v2->setEndings(il);
+	  v2->setTrack(0);
+	
+	  Measure *m5 = m4;
+	  for (; len < repeat_len - 1; ++len)
+	    m5 = m5->nextMeasure();
+
+	  v2->setStartMeasure(m4);
+	  v2->setEndMeasure(m5);
+	  m4->add(v2);
+	
+	  return;
+	} while (0);
+      }
+    }
+  }
+}
diff --git a/mscore/repeatdetector.h b/mscore/repeatdetector.h
new file mode 100644
index 0000000..e50006e
--- /dev/null
+++ b/mscore/repeatdetector.h
@@ -0,0 +1,19 @@
+#ifndef __REPEAT_DETECTOR_H__
+#define __REPEAT_DETECTOR_H__
+
+class Score;
+class Segment;
+class Measure;
+class Chord;
+
+//---------------------------------------------------------
+//   RepeatDetector
+//---------------------------------------------------------
+
+class RepeatDetector {
+public:
+  RepeatDetector();
+  static void detect(Score *score, int min_repeat_len, int max_volta_len);
+};
+
+#endif
diff --git a/mscore/revision.h b/mscore/revision.h
index 2793db3..30d48be 100644
--- a/mscore/revision.h
+++ b/mscore/revision.h
@@ -1 +1 @@
-5524:5526M
\ No newline at end of file
+5654M
\ No newline at end of file
diff --git a/mscore/scoreview.cpp b/mscore/scoreview.cpp
index 61c2d09..54c2d73 100644
--- a/mscore/scoreview.cpp
+++ b/mscore/scoreview.cpp
@@ -76,6 +76,7 @@
 #include "libmscore/spanner.h"
 #include "libmscore/rehearsalmark.h"
 #include "libmscore/excerpt.h"
+#include "repeatdetector.h"
 
 #include "navigator.h"
 #include "inspector.h"
@@ -2919,6 +2920,12 @@ void ScoreView::cmd(const QAction* a)
             _score->endCmd();
             mscore->endCmd();
             }
+      else if (cmd == "repeat-detector") {
+            _score->startCmd();
+	     RepeatDetector::detect(_score, 4, 2);
+            _score->endCmd();
+            mscore->endCmd();
+            }
       else
             _score->cmd(a);
       _score->processMidiInput();
