/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


/////////////////////// Qt includes
#include <QMessageBox>
#include <QCloseEvent>
#include <QDebug>
#include <QInputDialog>
#include <QClipboard>
#include <QGraphicsBlurEffect>
#include <QApplication>


/////////////////////// Local includes
#include <massxpert/gui/MzLabInputOligomerTableViewDlg.hpp>
#include <massxpert/gui/MzLabWnd.hpp>
#include <massxpert/gui/MzLabInputOligomerTableViewModel.hpp>
#include <massxpert/gui/MainWindow.hpp>


namespace msXpSmassXpert
{

MzLabInputOligomerTableViewDlg::MzLabInputOligomerTableViewDlg(
  QWidget *parent, QString name, msXpS::MassType massType)
  : QDialog(parent), m_name(name), m_massType(massType)
{
  Q_ASSERT(parent);

  mp_mzLabWnd          = static_cast<MzLabWnd *>(parent);
  mp_sequenceEditorWnd = 0;

  m_ui.setupUi(this);

  // Set the delimiter to '$'.
  m_ui.delimiterLineEdit->setText("$");

  m_previousMassType = msXpS::MassType::MASS_NONE;

  mpa_proxyModel             = 0;
  mpa_oligomerTableViewModel = 0;

  setupTableView();

  if(m_massType == msXpS::MassType::MASS_MONO)
    {
      m_ui.monoCheckBox->setChecked(true);
      m_ui.avgCheckBox->setChecked(false);
    }
  else if(m_massType == msXpS::MassType::MASS_AVG)
    {
      m_ui.avgCheckBox->setChecked(true);
      m_ui.monoCheckBox->setChecked(false);
    }

  m_ui.nameLineEdit->setText(m_name);
  setWindowTitle(tr("massXpert: mz Lab - %1").arg(m_name));

  connect(m_ui.monoCheckBox,
          SIGNAL(stateChanged(int)),
          this,
          SLOT(monoMassCheckBoxStateChanged(int)));

  connect(m_ui.avgCheckBox,
          SIGNAL(stateChanged(int)),
          this,
          SLOT(avgMassCheckBoxStateChanged(int)));

  connect(m_ui.exportToClipboardPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(exportToClipboard()));

  connect(m_ui.getFromClipboardPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(getFromClipboard()));

  connect(m_ui.seqEditorWndPtrLineEdit,
          SIGNAL(textChanged(const QString &)),
          this,
          SLOT(connectSeqEditorWnd(const QString &)));
}


MzLabInputOligomerTableViewDlg::~MzLabInputOligomerTableViewDlg()
{
  while(m_oligomerList.size())
    delete m_oligomerList.takeFirst();

  delete mpa_oligomerTableViewModel;
}


void
MzLabInputOligomerTableViewDlg::closeEvent(QCloseEvent *event)
{
  QDialog::closeEvent(event);
}


void
MzLabInputOligomerTableViewDlg::setName(const QString &name)
{
  m_name = name;

  m_ui.nameLineEdit->setText(m_name);
  setWindowTitle(tr("massXpert: mz Lab - %1").arg(m_name));
}


QString
MzLabInputOligomerTableViewDlg::name()
{
  return m_name;
}


const OligomerList &
MzLabInputOligomerTableViewDlg::oligomerList()
{
  return m_oligomerList;
}


SequenceEditorWnd *
MzLabInputOligomerTableViewDlg::sequenceEditorWnd()
{
  return mp_sequenceEditorWnd;
}


void
MzLabInputOligomerTableViewDlg::setupTableView()
{
  // Model stuff all thought for sorting.
  mpa_oligomerTableViewModel =
    new MzLabInputOligomerTableViewModel(&m_oligomerList, this);

  mpa_proxyModel = new MzLabInputOligomerTableViewSortProxyModel(this);
  mpa_proxyModel->setSourceModel(mpa_oligomerTableViewModel);
  mpa_proxyModel->setFilterKeyColumn(-1);

  m_ui.oligomerTableView->setModel(mpa_proxyModel);
  m_ui.oligomerTableView->setSourceModel(mpa_oligomerTableViewModel);
  m_ui.oligomerTableView->setOligomerList(&m_oligomerList);

  m_ui.oligomerTableView->setMzLabWnd(mp_mzLabWnd);
  m_ui.oligomerTableView->setParentDlg(this);

  mpa_oligomerTableViewModel->setTableView(m_ui.oligomerTableView);
  mpa_oligomerTableViewModel->setMzLabWnd(mp_mzLabWnd);
  mpa_oligomerTableViewModel->setParentDlg(this);
}


QString
MzLabInputOligomerTableViewDlg::textFieldDelimiter()
{
  // Get the delimiterLineEdit-contained string.

  QString delimiter = m_ui.delimiterLineEdit->text();

  if(delimiter.isEmpty())
    {
      delimiter = "$";
    }
  return delimiter;
}


int
MzLabInputOligomerTableViewDlg::duplicateOligomerData(const OligomerList &list)
{
  int size = list.size();

  for(int iter = 0; iter < size; ++iter)
    {
      Oligomer *oligomer = new Oligomer(*list.at(iter));
      mpa_oligomerTableViewModel->addOligomer(oligomer);
    }

  return mpa_oligomerTableViewModel->rowCount();
}


msXpS::MassType
MzLabInputOligomerTableViewDlg::massType()
{
  //    qDebug() << __FILE__ << __LINE__ << "m_massType:" << m_massType;
  return m_massType;
}


msXpS::MassType
MzLabInputOligomerTableViewDlg::previousMassType()
{
  return m_previousMassType;
}


void
MzLabInputOligomerTableViewDlg::setPreviousMassType(msXpS::MassType type)
{
  m_previousMassType = type;
}


msXpS::MassType
MzLabInputOligomerTableViewDlg::massTypeQuery()
{
  // Ask the user what should be the mass type of this dialog window.
  // Craft a specialized QMessageBox.

  QMessageBox msgBox;
  msgBox.setText(tr("Which type of masses are dealt with here?"));

  QPushButton *monoButton =
    msgBox.addButton(tr("mono masses"), QMessageBox::ActionRole);

  QPushButton *avgButton =
    msgBox.addButton(tr("avg masses"), QMessageBox::ActionRole);

  msgBox.exec();

  if(msgBox.clickedButton() == monoButton)
    {
      m_massType = msXpS::MassType::MASS_MONO;
      m_ui.monoCheckBox->setCheckState(Qt::Checked);
      m_ui.avgCheckBox->setCheckState(Qt::Unchecked);
    }
  else if(msgBox.clickedButton() == avgButton)
    {
      m_massType = msXpS::MassType::MASS_AVG;
      m_ui.avgCheckBox->setCheckState(Qt::Checked);
      m_ui.monoCheckBox->setCheckState(Qt::Unchecked);
    }

  return m_massType;
}


void
MzLabInputOligomerTableViewDlg::monoMassCheckBoxStateChanged(int state)
{
  if(state == Qt::Checked)
    {
      m_massType = msXpS::MassType::MASS_MONO;
      m_ui.avgCheckBox->setCheckState(Qt::Unchecked);
    }
  else
    {
      m_massType = msXpS::MassType::MASS_AVG;
      m_ui.avgCheckBox->setCheckState(Qt::Checked);
    }
}


void
MzLabInputOligomerTableViewDlg::avgMassCheckBoxStateChanged(int state)
{
  if(state == Qt::Checked)
    {
      m_massType = msXpS::MassType::MASS_AVG;
      m_ui.monoCheckBox->setCheckState(Qt::Unchecked);
    }
  else
    {
      m_massType = msXpS::MassType::MASS_MONO;
      m_ui.monoCheckBox->setCheckState(Qt::Checked);
    }
}


void
MzLabInputOligomerTableViewDlg::applyFormula(
  const msXpSlibmass::Formula &formula, bool inPlace)
{
  const PolChemDef *polChemDef = mp_mzLabWnd->polChemDef();

  double mono = 0;
  double avg  = 0;

  msXpSlibmass::Formula local(formula);

  if(!local.accountMasses(polChemDef->atomList(), &mono, &avg))
    {
      QMessageBox::warning(this,
                           tr("massXpert: mz Lab"),
                           tr("%1@%2\n"
                              "Failed accounting for formula '%3'.")
                             .arg(__FILE__)
                             .arg(__LINE__)
                             .arg(formula.text()));
      return;
    }

  if(inPlace)
    {
      // Just iterate in the oligomers and perform the computation.

      int count = m_oligomerList.count();

      if(!count)
        return;

      for(int iter = 0; iter < count; ++iter)
        {
          // First the mass
          QModelIndex index = mpa_oligomerTableViewModel->index(
            iter, MZ_LAB_INPUT_OLIGO_MASS_COLUMN);

          Q_ASSERT(index.isValid());

          Oligomer *oligomer = m_oligomerList.at(iter);

          int result = 0;

          result = oligomer->deionize();

          // The deionization does not change the oligomer's member
          // IonizeRule, we do not need to store it. Further, we
          // want to know if something happened during the
          // deionize() call, as we'll have to reionize the oligomer
          // after having incremented the mass.

          if(m_massType == msXpS::MassType::MASS_MONO)
            {
              oligomer->incrementMass(mono, m_massType);

              if(result == 1)
                {
                  oligomer->ionize();
                }

              // Fake call to setData, because we want the model to
              // be updated with a call to dataChanged(index,
              // index).
              mpa_oligomerTableViewModel->setData(index,
                                                  QVariant(oligomer->mono()));
            }
          else
            {
              oligomer->incrementMass(avg, m_massType);

              if(result == 1)
                {
                  oligomer->ionize();
                }

              // Fake call to setData, because we want the model to
              // be updated with a call to dataChanged(index,
              // index).
              mpa_oligomerTableViewModel->setData(index,
                                                  QVariant(oligomer->avg()));
            }
        }
    }
  else
    {
      QString dlgName = m_name;
      dlgName.append(" + ( ");
      dlgName.append(msXpS::elideText(formula.text()));
      dlgName.append(" )");

      MzLabInputOligomerTableViewDlg *dlg =
        mp_mzLabWnd->newInputList(dlgName, m_massType);

      if(!dlg)
        return;

      int count = dlg->duplicateOligomerData(m_oligomerList);

      if(count != m_oligomerList.count())
        qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

      dlg->applyFormula(formula, true);
    }
}


void
MzLabInputOligomerTableViewDlg::applyMass(double mass, bool inPlace)
{
  if(inPlace)
    {
      // Just iterate in the oligomers and perform the computation.

      int count = m_oligomerList.count();

      if(!count)
        return;

      for(int iter = 0; iter < count; ++iter)
        {
          // First the mass
          QModelIndex index = mpa_oligomerTableViewModel->index(
            iter, MZ_LAB_INPUT_OLIGO_MASS_COLUMN);
          Q_ASSERT(index.isValid());

          Oligomer *oligomer = m_oligomerList.at(iter);

          int result = 0;

          result = oligomer->deionize();

          // The deionization does not change the oligomer's member
          // IonizeRule, we do not need to store it. Further, we
          // want to know if something happened during the
          // deionize() call, as we'll have to reionize the oligomer
          // after having incremented the mass.

          if(m_massType == msXpS::MassType::MASS_MONO)
            {
              oligomer->incrementMass(mass, m_massType);

              if(result == 1)
                {
                  oligomer->ionize();
                }

              // Fake call to setData, because we want the model to
              // be updated with a call to dataChanged(index,
              // index).
              mpa_oligomerTableViewModel->setData(index,
                                                  QVariant(oligomer->mono()));
            }
          else
            {
              oligomer->incrementMass(mass, m_massType);

              if(result == 1)
                {
                  oligomer->ionize();
                }

              // Fake call to setData, because we want the model to
              // be updated with a call to dataChanged(index,
              // index).
              mpa_oligomerTableViewModel->setData(index,
                                                  QVariant(oligomer->avg()));
            }
        }
    }
  else
    {
      QString massString;
      massString.setNum(mass, 'f', msXpS::OLIGOMER_DEC_PLACES);
      QString dlgName = m_name;
      dlgName.append(" + ( ");
      dlgName.append(msXpS::elideText(massString));
      dlgName.append(" )");

      MzLabInputOligomerTableViewDlg *dlg =
        mp_mzLabWnd->newInputList(dlgName, m_massType);

      if(!dlg)
        return;

      int count = dlg->duplicateOligomerData(m_oligomerList);

      if(count != m_oligomerList.count())
        qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

      dlg->applyMass(mass, true);
    }
}


void
MzLabInputOligomerTableViewDlg::applyThreshold(double threshold,
                                               bool inPlace,
                                               bool onMz)
{
  // If onMz is false, then the threshold is to be applied on the
  // deionized oligomers' mass. Otherwise, the threshold is to be
  // applied to the m/z value of the ionized oligomer.

  if(inPlace)
    {
      // Just iterate in the oligomers and perform the computation.

      int count = m_oligomerList.count();

      if(!count)
        return;

      // Use count as an index, now, so we have to first decrement
      // it by one unit.
      --count;

      while(count > -1)
        {
          QModelIndex index = mpa_oligomerTableViewModel->index(
            count, MZ_LAB_INPUT_OLIGO_MASS_COLUMN);
          if(!index.isValid())
            qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

          Oligomer *oligomer = m_oligomerList.at(count);

          if(onMz)
            {
              // We do not have to deionize the oligomer, as we are
              // working on m/z values.

              if(oligomer->mass(m_massType) > threshold)
                {
                }
              else
                {
                  // We have to remove the item.
                  mpa_oligomerTableViewModel->removeOligomers(count, count);
                }
            }
          else
            {
              // We first have to deionize the oligomer, because the
              // calculation is on M values.

              // The deionization does not change the oligomer's
              // member IonizeRule, we do not need to store
              // it. Further, we want to know if something happened
              // during the deionize() call, as we'll have to
              // reionize the oligomer after having incremented the
              // mass.

              int result = oligomer->deionize();

              if(oligomer->mass(m_massType) > threshold)
                {
                  if(result == 1)
                    oligomer->ionize();
                }
              else
                {
                  // We have to remove the item.
                  mpa_oligomerTableViewModel->removeOligomers(count, count);
                }
            }

          --count;
        }
    }
  else
    {
      QString thresholdString;
      thresholdString.setNum(threshold, 'f', msXpS::OLIGOMER_DEC_PLACES);
      QString dlgName = m_name;
      dlgName.append(" + ");
      if(onMz)
        dlgName.append(tr("m/z threshold "));
      else
        dlgName.append(tr("m threshold "));
      dlgName.append("( ");
      dlgName.append(msXpS::elideText(thresholdString));
      dlgName.append(" )");

      MzLabInputOligomerTableViewDlg *dlg =
        mp_mzLabWnd->newInputList(dlgName, m_massType);

      if(!dlg)
        return;

      int count = dlg->duplicateOligomerData(m_oligomerList);

      if(count != m_oligomerList.count())
        qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

      dlg->applyThreshold(threshold, true, onMz);
    }
}


void
MzLabInputOligomerTableViewDlg::applyChargeIncrement(int increment,
                                                     bool inPlace)
{
  if(!increment)
    return;

  if(inPlace)
    {
      // Just iterate in the oligomers and perform the computation.

      int count = m_oligomerList.count();

      if(!count)
        return;

      for(int iter = 0; iter < count; ++iter)
        {
          QModelIndex massIndex = mpa_oligomerTableViewModel->index(
            iter, MZ_LAB_INPUT_OLIGO_MASS_COLUMN);
          Q_ASSERT(massIndex.isValid());

          QModelIndex chargeIndex = mpa_oligomerTableViewModel->index(
            iter, MZ_LAB_INPUT_OLIGO_CHARGE_COLUMN);
          Q_ASSERT(chargeIndex.isValid());

          Oligomer *oligomer = m_oligomerList.at(iter);

          int charge = 0;

          charge = oligomer->charge();

          if(charge == -1)
            {
              QMessageBox::warning(this,
                                   tr("massXpert: mz Lab"),
                                   tr("%1@%2\n"
                                      "Failed to get oligomer's charge.")
                                     .arg(__FILE__)
                                     .arg(__LINE__),
                                   QMessageBox::Ok);

              return;
            }

          charge += increment;

          if(charge < 0)
            charge = 0;

          int result = 0;

          result = oligomer->setCharge(charge);

          if(result == -1)
            {
              QMessageBox::warning(this,
                                   tr("massXpert: mz Lab"),
                                   tr("%1@%2\n"
                                      "Failed to set oligomer's charge.")
                                     .arg(__FILE__)
                                     .arg(__LINE__),
                                   QMessageBox::Ok);

              return;
            }

          // Update the mass value depending on the mass type.

          if(m_massType == msXpS::MassType::MASS_MONO)
            {
              // Fake call to setData, because we want the model to
              // be updated with a call to dataChanged(index,
              // index). First, we update the mass column and then
              // the charge column. The two calls are required.
              mpa_oligomerTableViewModel->setData(massIndex, QVariant());
              mpa_oligomerTableViewModel->setData(chargeIndex, QVariant());
            }
          else
            {

              // Fake call to setData, because we want the model to
              // be updated with a call to dataChanged(index,
              // index). First, we update the mass column and then
              // the charge column. The two calls are required.
              mpa_oligomerTableViewModel->setData(massIndex, QVariant());
              mpa_oligomerTableViewModel->setData(chargeIndex, QVariant());
            }
        }
    }
  else
    {
      QString incrementString;
      incrementString.setNum(increment);
      QString dlgName = m_name;
      dlgName.append(" + z ");
      dlgName.append("( ");
      dlgName.append(msXpS::elideText(incrementString));
      dlgName.append(" )");

      MzLabInputOligomerTableViewDlg *dlg =
        mp_mzLabWnd->newInputList(dlgName, m_massType);

      if(!dlg)
        return;

      int count = dlg->duplicateOligomerData(m_oligomerList);

      if(count != m_oligomerList.count())
        qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

      dlg->applyChargeIncrement(increment, true);
    }
}


void
MzLabInputOligomerTableViewDlg::applyIonizeRule(
  const msXpSlibmass::IonizeRule &ionizeRule, bool inPlace)
{
  msXpSlibmass::IonizeRule localIonizeRule = ionizeRule;

  PolChemDef *polChemDef = mp_mzLabWnd->polChemDef();

  if(!localIonizeRule.validate(polChemDef->atomList()))
    {
      QMessageBox::warning(this,
                           tr("massXpert:mz Lab - Input list"),
                           tr("%1@%2\n"
                              "Failed to validate ionization rule.")
                             .arg(__FILE__)
                             .arg(__LINE__),
                           QMessageBox::Ok);

      return;
    }

  if(inPlace)
    {
      // Just iterate in the oligomers and perform the computation.

      int count = m_oligomerList.count();

      if(!count)
        return;

      for(int iter = 0; iter < count; ++iter)
        {
          QModelIndex massIndex = mpa_oligomerTableViewModel->index(
            iter, MZ_LAB_INPUT_OLIGO_MASS_COLUMN);
          Q_ASSERT(massIndex.isValid());

          QModelIndex chargeIndex = mpa_oligomerTableViewModel->index(
            iter, MZ_LAB_INPUT_OLIGO_CHARGE_COLUMN);
          Q_ASSERT(chargeIndex.isValid());

          Oligomer *oligomer = m_oligomerList.at(iter);

          // All we do is reionize the oligomer.

          oligomer->ionize(localIonizeRule);

          // Update the mass value depending on the mass type.

          if(m_massType == msXpS::MassType::MASS_MONO)
            {
              // Fake call to setData, because we want the model to
              // be updated with a call to dataChanged(index,
              // index). First, we update the mass column and then
              // the charge column. The two calls are required.
              mpa_oligomerTableViewModel->setData(massIndex, QVariant());
              mpa_oligomerTableViewModel->setData(chargeIndex, QVariant());
            }
          else
            {
              // Fake call to setData, because we want the model to
              // be updated with a call to dataChanged(index,
              // index). First, we update the mass column and then
              // the charge column. The two calls are required.
              mpa_oligomerTableViewModel->setData(massIndex, QVariant());
              mpa_oligomerTableViewModel->setData(chargeIndex, QVariant());
            }
        }
    }
  else
    {
      QString dlgName = m_name;
      dlgName.append(" + ");
      dlgName.append(tr("ioniz. "));
      dlgName.append("( ");
      dlgName.append(localIonizeRule.formula());
      dlgName.append(", z ");
      dlgName.append(QString().setNum(localIonizeRule.charge()));
      dlgName.append(", lev. ");
      dlgName.append(QString().setNum(localIonizeRule.level()));
      dlgName.append(" )");

      MzLabInputOligomerTableViewDlg *dlg =
        mp_mzLabWnd->newInputList(dlgName, m_massType);

      if(!dlg)
        return;

      int count = dlg->duplicateOligomerData(m_oligomerList);

      if(count != m_oligomerList.count())
        qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

      dlg->applyIonizeRule(localIonizeRule, true);
    }
}


void
MzLabInputOligomerTableViewDlg::exportToClipboard()
{
  // We are asked to prepare a list of (m/z,z) pairs according to
  // what is displayed in the TableView. Ask the table view to
  // prepare a string.

  QString *text = m_ui.oligomerTableView->selectedDataAsPlainText();

  QClipboard *clipboard = QApplication::clipboard();

  clipboard->setText(*text, QClipboard::Clipboard);

  delete text;
}


void
MzLabInputOligomerTableViewDlg::getFromClipboard()
{
  // There must be something like this in the clipboard:

  // 1050.15540333$3$cl-0#10+0#22#z=3$[89-100][186-202]
  // (mass$charge$name$coordinates)

  // or

  // 1050.15540333$3$cl-0#10+0#22#z=3 (mass$charge$name)

  // or

  // 1050.15540333$3 (mass$charge)

  // or

  // 1050.15540333 (mass)

  // We should delegate this work to to the table view itself,
  // exactly as was done with the dropped data.

  m_ui.oligomerTableView->pasteEvent();
}


SequenceEditorWnd *
MzLabInputOligomerTableViewDlg::connectSeqEditorWnd(const QString &text)
{
  // The user is editing the pointer of the sequence editor window
  // to which she wants to connect.

  // First convert text to SequenceEditorWnd*
  bool ok                   = false;
  quintptr pointerCastToInt = text.toLongLong(&ok, 10);

  if(pointerCastToInt == 0 || ok == false)
    {
      mp_sequenceEditorWnd = 0;

      QGraphicsBlurEffect *effect = new QGraphicsBlurEffect();

      m_ui.seqEditorWndPtrLineEdit->setGraphicsEffect(effect);

      return mp_sequenceEditorWnd;
    }

  const MainWindow *p_mainWnd = mp_mzLabWnd->mainWindow();

  if(p_mainWnd->isSequenceEditorWnd(
       reinterpret_cast<SequenceEditorWnd *>(pointerCastToInt)))
    {
      mp_sequenceEditorWnd =
        reinterpret_cast<SequenceEditorWnd *>(pointerCastToInt);

      return mp_sequenceEditorWnd;
    }
  else
    {
      mp_sequenceEditorWnd        = 0;
      QGraphicsBlurEffect *effect = new QGraphicsBlurEffect();

      m_ui.seqEditorWndPtrLineEdit->setGraphicsEffect(effect);

      return mp_sequenceEditorWnd;
    }
}

} // namespace msXpSmassXpert
