/* massXpert - the true massist's program.
   --------------------------------------
   Copyright(C) 2006,2007 Filippo Rusconi

   http://www.massxpert.org/massXpert

   This file is part of the massXpert project.

   The massxpert project is the successor to the "GNU polyxmass"
   project that is an official GNU project package(see
   www.gnu.org). The massXpert project is not endorsed by the GNU
   project, although it is released ---in its entirety--- under the
   GNU General Public License. A huge part of the code in massXpert
   is actually a C++ rewrite of code in GNU polyxmass. As such
   massXpert was started at the Centre National de la Recherche
   Scientifique(FRANCE), that granted me the formal authorization to
   publish it under this Free Software License.

   This software is free software; you can redistribute it and/or
   modify it under the terms of the GNU  General Public
   License version 3, as published by the Free Software Foundation.


   This software 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 software; if not, write to the

   Free Software Foundation, Inc.,

   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/


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


/////////////////////// Local includes
#include "mzLabInputOligomerTableViewDlg.hpp"
#include "mzLabWnd.hpp"
#include "mzLabInputOligomerTableViewModel.hpp"
#include "fragmentOligomer.hpp"
#include "application.hpp"


namespace massXpert
{

  MzLabInputOligomerTableViewDlg::MzLabInputOligomerTableViewDlg
  (QWidget *parent,
   QString name,
   MassType massType,
   bool isFragment)
    : QDialog(parent),
      m_name(name),
      m_isFragment(isFragment),
      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 = MXT_MASS_NONE;

    mpa_proxyModel = 0;
    mpa_oligomerTableViewModel = 0;

    setupTableView();

    if(m_isFragment)
      m_ui.isFragmentCheckBox->setChecked(true);

    if(m_massType == MXT_MASS_MONO)
      {
        m_ui.monoCheckBox->setChecked(true);
        m_ui.avgCheckBox->setChecked(false);
      }
    else if(m_massType == MXT_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.isFragmentCheckBox,
            SIGNAL(stateChanged(int)),
            this,
            SLOT(isFragmentCheckBoxStateChanged(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)
      {
        if(m_isFragment)
          {
            FragmentOligomer *iterOligomer =
              dynamic_cast<FragmentOligomer *>(list.at(iter));

            FragmentOligomer *oligomer = new FragmentOligomer(*iterOligomer);

            mpa_oligomerTableViewModel->addOligomer(oligomer);
          }
        else
          {
            Oligomer *oligomer = new Oligomer(*list.at(iter));

            mpa_oligomerTableViewModel->addOligomer(oligomer);
          }
      }

    return mpa_oligomerTableViewModel->rowCount();
  }


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


  bool
  MzLabInputOligomerTableViewDlg::isFragment()
  {
    return m_isFragment;
  }


  void
  MzLabInputOligomerTableViewDlg::setFragment(bool fragment)
  {
    m_ui.isFragmentCheckBox->setChecked(fragment);
  }


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


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


  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 = MXT_MASS_MONO;
        m_ui.monoCheckBox->setCheckState(Qt::Checked);
        m_ui.avgCheckBox->setCheckState(Qt::Unchecked);
      }
    else if(msgBox.clickedButton() == avgButton)
      {
        m_massType = MXT_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 = MXT_MASS_MONO;
        m_ui.avgCheckBox->setCheckState(Qt::Unchecked);
      }
    else
      {
        m_massType = MXT_MASS_AVG;
        m_ui.avgCheckBox->setCheckState(Qt::Checked);
      }
  }


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


  void
  MzLabInputOligomerTableViewDlg::isFragmentCheckBoxStateChanged(int state)
  {
    if (state == Qt::Checked)
      m_isFragment = true;
    else
      m_isFragment = false;
  }


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

    double mono = 0;
    double avg = 0;

    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);

            FragmentOligomer *fragOligomer =
              dynamic_cast<FragmentOligomer *>(m_oligomerList.at(iter));

            int result = 0;

            if (fragOligomer)
              result = fragOligomer->deionize();
            else
              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 == MXT_MASS_MONO)
              {
                oligomer->incrementMass(mono, m_massType);

                if(result == 1)
                  {
                    if (fragOligomer)
                      fragOligomer->ionize();
                    else
                      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)
                  {
                    if (fragOligomer)
                      fragOligomer->ionize();
                    else
                      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(GlobElideText(formula.text()));
        dlgName.append(" )");

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

        if(!dlg)
          return;

        // The newly created dialog that it is opened to store
        // fragmentation oligomer data.
        if(m_isFragment)
          dlg->setFragment(true);

        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);

            FragmentOligomer *fragOligomer =
              dynamic_cast<FragmentOligomer *>(m_oligomerList.at(iter));

            int result = 0;

            if (fragOligomer)
              result = fragOligomer->deionize();
            else
              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 == MXT_MASS_MONO)
              {
                oligomer->incrementMass(mass, m_massType);

                if(result == 1)
                  {
                    if (fragOligomer)
                      fragOligomer->ionize();
                    else
                      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)
                  {
                    if (fragOligomer)
                      fragOligomer->ionize();
                    else
                      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', MXP_OLIGOMER_DEC_PLACES);
        QString dlgName = m_name;
        dlgName.append(" + ( ");
        dlgName.append(GlobElideText(massString));
        dlgName.append(" )");

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

        if(!dlg)
          return;

        // The newly created dialog that it is opened to store
        // fragmentation oligomer data.
        if(m_isFragment)
          dlg->setFragment(true);

        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', MXP_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(GlobElideText(thresholdString));
        dlgName.append(" )");

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

        if(!dlg)
          return;

        // The newly created dialog that it is opened to store
        // fragmentation oligomer data.
        if(m_isFragment)
          dlg->setFragment(true);

        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);

            FragmentOligomer *fragOligomer =
              dynamic_cast<FragmentOligomer *>(m_oligomerList.at(iter));

            int charge = 0;

            if (fragOligomer)
              charge = fragOligomer->charge();
            else
              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;

            if (fragOligomer)
              result = fragOligomer->setCharge(charge);
            else
              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 == MXT_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(GlobElideText(incrementString));
        dlgName.append(" )");

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

        if(!dlg)
          return;

        // The newly created dialog that it is opened to store
        // fragmentation oligomer data.
        if(m_isFragment)
          dlg->setFragment(true);

        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 IonizeRule &ionizeRule,
                                                  bool inPlace)
  {
    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. Provision to check
            // if the oligomer is of the fragmentation kind or not,
            // since ionizations are not performed equal depending on
            // the case.

            FragmentOligomer *fragOligomer =
              dynamic_cast<FragmentOligomer *>(oligomer);

            if (fragOligomer)
              fragOligomer->ionize(localIonizeRule);
            else
              oligomer->ionize(localIonizeRule);

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

            if (m_massType == MXT_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, m_isFragment);

        if(!dlg)
          return;

        // The newly created dialog that it is opened to store
        // fragmentation oligomer data.
        if(m_isFragment)
          dlg->setFragment(true);

        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 seqEdWndCastToInt = text.toInt(&ok);

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

        QGraphicsBlurEffect *effect = new QGraphicsBlurEffect();

        m_ui.seqEditorWndPtrLineEdit->
          setGraphicsEffect(effect);

        return mp_sequenceEditorWnd;
      }

    Application *application = static_cast<Application *>(qApp);

    if(application->isSequenceEditorWnd
       (reinterpret_cast<SequenceEditorWnd *>(seqEdWndCastToInt)))
      {
        mp_sequenceEditorWnd =
          reinterpret_cast<SequenceEditorWnd *>(seqEdWndCastToInt);

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

        m_ui.seqEditorWndPtrLineEdit->
          setGraphicsEffect(effect);

        return mp_sequenceEditorWnd;
      }
  }

} // namespace massXpert
