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

    GUI-enabled patch for automatic repeat detector.

diff --git a/libmscore/beam.cpp b/libmscore/beam.cpp
index 70b3da4..7435207 100644
--- a/libmscore/beam.cpp
+++ b/libmscore/beam.cpp
@@ -1802,6 +1802,8 @@ void Beam::layout2(QList<ChordRest*>crl, SpannerSegmentType, int frag)
 
 void Beam::write(Xml& xml, P_ID id) const
       {
+      if (xml.ignoreBeams)
+            return;
       xml.tag(id, getProperty(id), propertyDefault(id));
       }
 
@@ -1811,7 +1813,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 19e4ae8..e5bd159 100644
--- a/libmscore/chordrest.cpp
+++ b/libmscore/chordrest.cpp
@@ -185,10 +185,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 1c81b12..d6b5b85 100644
--- a/libmscore/measure.cpp
+++ b/libmscore/measure.cpp
@@ -1814,7 +1814,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)
@@ -1845,7 +1845,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 04f3cb3..3c6a099 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 de3c002..baa4fb4 100644
--- a/mscore/CMakeLists.txt
+++ b/mscore/CMakeLists.txt
@@ -47,7 +47,7 @@ QT4_WRAP_UI (ui_headers
       instrwizard.ui timesigwizard.ui newwizard.ui aboutbox.ui aboutmusicxmlbox.ui
       chordedit.ui transposedialog.ui tempoproperties.ui
       lineproperties.ui excerptsdialog.ui lyrics.ui stafftext.ui
-      tupletdialog.ui tupletproperties.ui
+      tupletdialog.ui tupletproperties.ui repeatdetectordialog.ui
       glissandoprop.ui articulation.ui metaedit.ui palette.ui
       dynamicproperties.ui textline.ui slurproperties.ui debugger.ui
       select.ui synthcontrol.ui splitstaff.ui cellproperties.ui beam.ui keyedit.ui
@@ -89,7 +89,7 @@ QT4_WRAP_CPP (mocs
       editstringdata.h editraster.h mediadialog.h chordeditor.h chordview.h album.h layer.h
       webpage.h inspector.h inspectorBase.h inspectorBeam.h masterpalette.h
       inspectorGroupElement.h inspectorImage.h waveview.h helpBrowser.h
-      inspectorLasso.h
+      inspectorLasso.h repeatdetectordialog.h
       ${OMR_MOCS}
       ${SCRIPT_MOCS}
       )
@@ -185,6 +185,7 @@ add_executable ( ${ExecutableName}
       inspectorBase.cpp inspectorBeam.cpp masterpalette.cpp
       inspectorGroupElement.cpp dragdrop.cpp inspectorImage.cpp
       waveview.cpp musicxmlsupport.cpp helpBrowser.cpp inspectorLasso.cpp
+      repeatdetector.cpp repeatdetectordialog.cpp
       ${OMR_FILES}
       ${AUDIO}
       ${SCRIPT_FILES}
diff --git a/mscore/actions.cpp b/mscore/actions.cpp
index 2eb4296..80f2a90 100644
--- a/mscore/actions.cpp
+++ b/mscore/actions.cpp
@@ -2100,6 +2100,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 6c36900..a8fd362 100644
--- a/mscore/musescore.cpp
+++ b/mscore/musescore.cpp
@@ -78,6 +78,8 @@
 #include "pluginCreator.h"
 #include "plugins.h"
 #include "helpBrowser.h"
+#include "repeatdetector.h"
+#include "repeatdetectordialog.h"
 
 #include "libmscore/mscore.h"
 #include "libmscore/system.h"
@@ -400,6 +402,7 @@ MuseScore::MuseScore()
       savePaletteDialog     = 0;
       saveDrumsetDialog     = 0;
       savePluginDialog      = 0;
+      repeatDetectorDialog  = 0;
 
       editRasterDialog      = 0;
       inChordEditor         = false;
@@ -751,6 +754,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()));
@@ -4408,6 +4413,13 @@ printf("set preferences to <%s>\n", qPrintable(name));
                         }
                   }
             }
+      else if (cmd == "repeat-detector") {
+            if (repeatDetectorDialog != 0)
+	          delete repeatDetectorDialog;
+            repeatDetectorDialog = new RepeatDetectorDialog(this, currentScoreView());
+            addDockWidget(Qt::RightDockWidgetArea, repeatDetectorDialog);
+            repeatDetectorDialog->show();
+            }
       else {
             if (cv) {
                   cv->setFocus();
diff --git a/mscore/musescore.h b/mscore/musescore.h
index d0a7d80..37b4945 100644
--- a/mscore/musescore.h
+++ b/mscore/musescore.h
@@ -83,6 +83,7 @@ class EditTools;
 class Sym;
 class MasterPalette;
 class PluginCreator;
+class RepeatDetectorDialog;
 
 struct PluginDescription;
 
@@ -245,6 +246,7 @@ class MuseScore : public QMainWindow {
       InsertMeasuresDialog* insertMeasuresDialog;
       MasterPalette* masterPalette;
       PluginCreator* pluginCreator;
+      RepeatDetectorDialog *repeatDetectorDialog;
 
       QMenu* _fileMenu;
       QMenu* menuEdit;
diff --git a/mscore/repeatdetector.cpp b/mscore/repeatdetector.cpp
new file mode 100644
index 0000000..4613a20
--- /dev/null
+++ b/mscore/repeatdetector.cpp
@@ -0,0 +1,156 @@
+#include <assert.h>
+#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(Score *score, int min_repeat_len, int max_volta_len)
+  : _score(score), _min_repeat_len(min_repeat_len), _max_volta_len(max_volta_len)
+{
+  m_beg = _score->firstMeasure();
+  m_end = 0;
+}
+
+
+void RepeatDetector::setScope(Measure *m_b, Measure *m_e)
+{
+  m_beg = m_b;
+  m_end = m_e;
+}
+
+
+/** @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::apply(DetectedRepeat const & dr) {
+  dr.m1->setRepeatFlags(dr.m1->repeatFlags() | RepeatStart);
+  dr.m2_prev->setRepeatFlags(dr.m2_prev->repeatFlags() | RepeatEnd);
+
+  for (MeasureBase *mb = dr.m2; mb != dr.m4;) {
+    MeasureBase *tmp = mb;
+    mb = mb->next();
+    _score->removeElement(tmp);
+  }
+
+  if (dr.len == dr.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(dr.m3);
+  v1->setEndMeasure(dr.m2_prev);
+  dr.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 = dr.m4;
+  for (int len = dr.len; len < dr.repeat_len - 1; ++len)
+    m5 = m5->nextMeasure();
+
+  v2->setStartMeasure(dr.m4);
+  v2->setEndMeasure(m5);
+  dr.m4->add(v2);
+}
+
+
+bool RepeatDetector::detect() {
+  int i1 = 0;
+  _detectedRepeats.clear();
+  DetectedRepeat dr;
+  for (dr.m1 = m_beg; dr.m1 != m_end; dr.m1 = dr.m1->nextMeasure(), ++i1) {
+    // Begin of new repeat segment forbidden at a measure where another repeat segment already begins or ends
+    if (dr.m1->repeatFlags())
+      continue;
+    int ticka = dr.m1->tick();
+    dr.m2 = dr.m1->nextMeasure();
+    int i2 = i1 + 1;
+    dr.repeat_len = 1;
+    while (dr.repeat_len < _min_repeat_len && dr.m2) {
+      ++dr.repeat_len;
+      dr.m2 = dr.m2->nextMeasure();
+      ++i2;
+    }
+    for (; dr.m2 != m_end; dr.m2 = dr.m2->nextMeasure(), ++i2, ++dr.repeat_len) {
+      // New repeat segment cannot contain a measure where another repeat segment already begins or ends
+      if (dr.m2->repeatFlags())
+	break;
+      int tickb = dr.m2->tick();
+      dr.m3 = dr.m1;
+      dr.m4 = dr.m2;
+      dr.len = 0;
+      int i3 = i1;
+      int i4 = i2;
+      while (dr.len < dr.repeat_len && dr.m3 && dr.m4) {
+	bool eq = cmpMeasure(dr.m3, ticka, dr.m4, tickb);
+	//qDebug() << "num: " << i1 << i2 << i3 << i4 << "eq:" << eq << ", len:" << dr.len << ", repeat_len:" << dr.repeat_len;
+	if (!eq)
+	  break;
+	++dr.len;
+	dr.m3 = dr.m3->nextMeasure();
+	dr.m4 = dr.m4->nextMeasure();
+	++i3;
+	++i4;
+      }
+      if (dr.len >= dr.repeat_len - _max_volta_len && dr.m1 && dr.m3) {
+	//qDebug() << "i1: " << i1 << ", i2: " << i2 << ", len: " << dr.len << ", repeat_len: " << dr.repeat_len;
+	dr.m2_prev = dr.m2->prevMeasure();
+	assert(dr.m2_prev);
+	_detectedRepeats.push_back(dr);
+      }
+    }
+  }
+  return _detectedRepeats.size() > 0;
+}
+
+
+std::vector<DetectedRepeat> const & RepeatDetector::detectedRepeats() {
+  return _detectedRepeats;
+}
diff --git a/mscore/repeatdetector.h b/mscore/repeatdetector.h
new file mode 100644
index 0000000..e842f5b
--- /dev/null
+++ b/mscore/repeatdetector.h
@@ -0,0 +1,36 @@
+#ifndef __REPEAT_DETECTOR_H__
+#define __REPEAT_DETECTOR_H__
+
+class Score;
+class Segment;
+class Measure;
+class Chord;
+#include <vector>
+
+//---------------------------------------------------------
+//   RepeatDetector
+//---------------------------------------------------------
+
+struct DetectedRepeat {
+  int len, repeat_len;
+  Measure *m1, *m2, *m3, *m4, *m1_prev, *m2_prev;
+};
+
+
+class RepeatDetector {
+  Score *_score;
+  Measure *m_beg, *m_end;
+  int _min_repeat_len;
+  int _max_volta_len;
+
+  std::vector<DetectedRepeat> _detectedRepeats;
+
+public:
+  RepeatDetector(Score *score, int min_repeat_len = 4, int max_volta_len = 2);
+  void setScope(Measure *m_beg, Measure *m_end);
+  bool detect();
+  std::vector<DetectedRepeat> const & detectedRepeats();
+  void apply(DetectedRepeat const & dr);
+};
+
+#endif
diff --git a/mscore/repeatdetectordialog.cpp b/mscore/repeatdetectordialog.cpp
new file mode 100644
index 0000000..868cded
--- /dev/null
+++ b/mscore/repeatdetectordialog.cpp
@@ -0,0 +1,173 @@
+//=============================================================================
+//  MuseScore
+//  Music Composition & Notation
+//  $Id:$
+//
+//  Copyright (C) 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 LICENSE.GPL
+//=============================================================================
+
+#include "repeatdetector.h"
+#include "repeatdetectordialog.h"
+#include "musescore.h"
+#include "scoreview.h"
+#include "libmscore/score.h"
+#include "libmscore/measure.h"
+
+//---------------------------------------------------------
+//   
+//---------------------------------------------------------
+RepeatDetectorWidget::RepeatDetectorWidget(QWidget* parent, ScoreView *scoreView)
+  : QWidget(parent), _scoreView(scoreView), _rd(0), _selectedRow(-1)
+      {
+      setupUi(this);
+      setObjectName("repeatdetector");
+      connect(repeatsTable, SIGNAL( cellClicked(int, int) ),
+	      this, SLOT( cellClicked(int, int) ) );
+      connect(applyButton, SIGNAL( clicked() ),
+	      this, SLOT( applyPushed() ) );
+      connect(okButton, SIGNAL( clicked() ),
+	      this, SLOT( okPushed() ) );
+      connect(findButton, SIGNAL( clicked() ),
+	      this, SLOT( findPushed() ) );
+      applyButton->setEnabled(false);
+      updateRepeatsTable();
+      }
+
+
+void RepeatDetectorWidget::okPushed() {
+  static_cast<RepeatDetectorDialog*>(parent())->hide();
+}
+
+
+void RepeatDetectorWidget::findPushed() {
+  updateRepeatsTable();
+}
+
+
+void RepeatDetectorWidget::cellClicked(int nRow, int nCol) {
+  if (nRow < (int)_rd->detectedRepeats().size()) {
+    DetectedRepeat dr = _rd->detectedRepeats()[nRow];
+    _scoreView->score()->select(dr.m1, SELECT_SINGLE);
+    _scoreView->score()->select(dr.m2_prev, SELECT_RANGE);
+    _scoreView->adjustCanvasPosition(dr.m1, false);
+    _scoreView->updateAll();
+    repeatsTable->setFocus();
+  }
+  _selectedRow = nRow;
+  applyButton->setEnabled(true);
+}
+
+
+void RepeatDetectorWidget::applyPushed() {
+  if (_selectedRow == -1) {
+    // @TODO assert
+    return;
+  }
+  _scoreView->score()->startCmd();
+    DetectedRepeat const & dr = _rd->detectedRepeats()[_selectedRow];
+  _rd->apply(dr);
+  _scoreView->score()->endCmd();
+  _scoreView->score()->select(dr.m1, SELECT_SINGLE);
+  _scoreView->score()->select(dr.m2_prev, SELECT_RANGE);
+  _scoreView->adjustCanvasPosition(dr.m1, false);
+  _scoreView->updateAll();
+  //_scoreView->updateAll();
+  updateRepeatsTable();
+}
+
+
+void RepeatDetectorWidget::updateRepeatsTable() {
+  Score *score = _scoreView->score();
+  while (repeatsTable->rowCount() > 0)
+    repeatsTable->removeRow(0);
+  applyButton->setEnabled(false);
+  if (_rd)
+    delete _rd;
+  int min_repeat_len = minLenEdit->text().toInt();
+  int max_volta_len = minEndEdit->text().toInt();
+  _rd = new RepeatDetector(score, min_repeat_len, max_volta_len);
+  if (_rd->detect()) {
+    std::vector<DetectedRepeat> const & drs = _rd->detectedRepeats();
+    std::vector<DetectedRepeat>::const_iterator it;
+    for (it = drs.begin(); it != drs.end(); ++it) {
+      int row = repeatsTable->rowCount();
+      repeatsTable->insertRow(row);
+      QTableWidgetItem *item;
+      item = new QTableWidgetItem();
+      item->setData(Qt::DisplayRole, it->m1->no() + 1);
+      repeatsTable->setItem(row, 0, item);
+      item = new QTableWidgetItem();
+      item->setData(Qt::DisplayRole, it->m2_prev->no() + 1);
+      repeatsTable->setItem(row, 1, item);
+      if (it->len == it->repeat_len) {
+	item = new QTableWidgetItem();
+	item->setData(Qt::DisplayRole, 99);
+	repeatsTable->setItem(row, 2, item);
+	item = new QTableWidgetItem();
+	item->setData(Qt::DisplayRole, 99);
+	repeatsTable->setItem(row, 3, item);
+	item = new QTableWidgetItem();
+	item->setData(Qt::DisplayRole, it->len);
+	repeatsTable->setItem(row, 4, item);
+      } else {
+	item = new QTableWidgetItem();
+	item->setData(Qt::DisplayRole, it->repeat_len);
+	repeatsTable->setItem(row, 2, item);
+	item = new QTableWidgetItem();
+	item->setData(Qt::DisplayRole, it->m2->no() - it->m3->no());
+	repeatsTable->setItem(row, 3, item);
+	item = new QTableWidgetItem();
+	item->setData(Qt::DisplayRole, it->repeat_len - (it->repeat_len - it->len));
+	repeatsTable->setItem(row, 4, item);
+      }
+      //repeatsTable->sortItems(4, Qt::DescendingOrder);
+    }
+  }
+}
+
+
+bool RepeatDetectorWidget::eventFilter(QObject *obj, QEvent *event) {
+  // if (event->type() == QEvent::Close)
+  //     return QWidget::eventFilter(obj, event);
+  // if (obj->isWidgetType()) {
+  //   QWidget *w = static_cast<QWidget *>(obj);
+  //   if (static_cast<QWidget *>(parent())->isAncestorOf(w))
+  //     return QWidget::eventFilter(obj, event);
+  // }
+  // printf("Event: %d\n", event->type());
+  // if (event->type() == QEvent::KeyPress
+  //     || event->type() == QEvent::KeyRelease
+  //     || event->type() == QEvent::MouseButtonPress
+  //     || event->type() == QEvent::MouseButtonRelease) {
+  //   return true;
+  // }
+  return QWidget::eventFilter(obj, event);
+}
+
+
+void RepeatDetectorWidget::hideEvent(QHideEvent* ev)
+      {
+      QWidget::hideEvent(ev);
+      //QApplication::instance()->removeEventFilter(this);
+      }
+
+
+void RepeatDetectorWidget::showEvent(QShowEvent* ev)
+      {
+      //QApplication::instance()->installEventFilter(this);
+      }
+
+
+RepeatDetectorDialog::RepeatDetectorDialog(QWidget *parent, ScoreView *scoreView)
+  : QDockWidget(tr("Repeat Detector"), parent), _scoreView(scoreView)
+{
+  _mainWidget = new RepeatDetectorWidget(this, scoreView);
+  _mainWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+  setWidget(_mainWidget);
+  setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+}
diff --git a/mscore/repeatdetectordialog.h b/mscore/repeatdetectordialog.h
new file mode 100644
index 0000000..4bd149c
--- /dev/null
+++ b/mscore/repeatdetectordialog.h
@@ -0,0 +1,63 @@
+//=============================================================================
+//  MuseScore
+//  Music Composition & Notation
+//  $Id:$
+//
+//  Copyright (C) 2011 Werner Schweer and others
+//
+//  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 LICENSE.GPL
+//=============================================================================
+
+#ifndef __REPEATDETECTORDIALOG_H__
+#define __REPEATDETECTORDIALOG_H__
+
+#include "ui_repeatdetectordialog.h"
+#include "repeatdetector.h"
+
+class ScoreView;
+
+//---------------------------------------------------------
+//   
+//---------------------------------------------------------
+
+class RepeatDetectorWidget : public QWidget, private Ui::RepeatDetectorBase {
+      Q_OBJECT
+
+      ScoreView *_scoreView;
+      RepeatDetector *_rd;
+      int _selectedRow;
+
+      virtual void hideEvent(QHideEvent*);
+      virtual void showEvent(QShowEvent*);
+
+ protected:
+      bool eventFilter(QObject *obj, QEvent *event);
+
+   private slots:
+
+      void cellClicked(int nRow, int nCol);
+      void applyPushed();
+      void okPushed();
+      void findPushed();
+
+   public:
+      RepeatDetectorWidget(QWidget* parent, ScoreView *scoreView);
+      void updateRepeatsTable();
+      };
+
+
+class RepeatDetectorDialog : public QDockWidget {
+  Q_OBJECT
+
+    RepeatDetectorWidget *_mainWidget;
+    ScoreView *_scoreView;
+
+ public:
+  RepeatDetectorDialog(QWidget *parent, ScoreView *scoreView);
+};
+
+
+#endif
diff --git a/mscore/repeatdetectordialog.ui b/mscore/repeatdetectordialog.ui
new file mode 100644
index 0000000..6f33d89
--- /dev/null
+++ b/mscore/repeatdetectordialog.ui
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RepeatDetectorBase</class>
+ <widget class="QWidget" name="RepeatDetectorBase">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>274</width>
+    <height>312</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>MuseScore: Repeat Detector</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <property name="sizeConstraint">
+    <enum>QLayout::SetMinimumSize</enum>
+   </property>
+   <item>
+    <widget class="QFrame" name="frame">
+     <property name="frameShape">
+      <enum>QFrame::StyledPanel</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Raised</enum>
+     </property>
+     <widget class="QWidget" name="horizontalLayoutWidget">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>241</width>
+        <height>36</height>
+       </rect>
+      </property>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <property name="sizeConstraint">
+        <enum>QLayout::SetDefaultConstraint</enum>
+       </property>
+       <item>
+        <widget class="QLabel" name="label">
+         <property name="toolTip">
+          <string>Minimum repeat length</string>
+         </property>
+         <property name="text">
+          <string>Min
+Len</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLineEdit" name="minLenEdit">
+         <property name="text">
+          <string>4</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_2">
+         <property name="toolTip">
+          <string>Maximum volta length</string>
+         </property>
+         <property name="text">
+          <string>Max
+Vol</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLineEdit" name="minEndEdit">
+         <property name="text">
+          <string>2</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="findButton">
+         <property name="text">
+          <string>Find</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <widget class="QSplitter" name="splitter">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="childrenCollapsible">
+      <bool>false</bool>
+     </property>
+     <widget class="QTableWidget" name="repeatsTable">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="editTriggers">
+       <set>QAbstractItemView::NoEditTriggers</set>
+      </property>
+      <property name="alternatingRowColors">
+       <bool>true</bool>
+      </property>
+      <property name="selectionBehavior">
+       <enum>QAbstractItemView::SelectRows</enum>
+      </property>
+      <property name="horizontalScrollMode">
+       <enum>QAbstractItemView::ScrollPerPixel</enum>
+      </property>
+      <attribute name="horizontalHeaderCascadingSectionResizes">
+       <bool>true</bool>
+      </attribute>
+      <attribute name="horizontalHeaderDefaultSectionSize">
+       <number>48</number>
+      </attribute>
+      <attribute name="horizontalHeaderMinimumSectionSize">
+       <number>30</number>
+      </attribute>
+      <attribute name="horizontalHeaderShowSortIndicator" stdset="0">
+       <bool>true</bool>
+      </attribute>
+      <attribute name="horizontalHeaderStretchLastSection">
+       <bool>false</bool>
+      </attribute>
+      <attribute name="verticalHeaderVisible">
+       <bool>false</bool>
+      </attribute>
+      <column>
+       <property name="text">
+        <string>Beg</string>
+       </property>
+       <property name="toolTip">
+        <string>Begin measure of repeating segment</string>
+       </property>
+      </column>
+      <column>
+       <property name="text">
+        <string>End</string>
+       </property>
+       <property name="toolTip">
+        <string>End measure of repeating segment</string>
+       </property>
+      </column>
+      <column>
+       <property name="text">
+        <string>Len</string>
+       </property>
+       <property name="toolTip">
+        <string>Repeating segment length (in measures)</string>
+       </property>
+      </column>
+      <column>
+       <property name="text">
+        <string>CEL</string>
+       </property>
+       <property name="toolTip">
+        <string>Custom ending length (in measures)</string>
+       </property>
+      </column>
+      <column>
+       <property name="text">
+        <string>Red</string>
+       </property>
+       <property name="toolTip">
+        <string>Reduction in score length (in measures)</string>
+       </property>
+      </column>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QPushButton" name="applyButton">
+       <property name="text">
+        <string>Apply</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>131</width>
+         <height>31</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="okButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>OK</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
