/* 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 <QSysInfo>
#include <QString>
#include <QtGui>
#include <QtXml>
#include <QtGlobal>
#include <QAction>
#include <QInputDialog>
#include <QFileDialog>
#include <QMessageBox>
#include <QList>
#include <QApplication>
#include <QDebug>


/////////////////////// Local includes

// For the install directory defines
#include "../config.h"

#include <massxpert/gui/MainWindow.hpp>

#include <massxpert/nongui/PolChemDefSpec.hpp>
#include <massxpert/nongui/PolChemDefCatParser.hpp>

#include <massxpert/gui/ConfigSettingsDlg.hpp>

#include <massxpert/gui/PolChemDefWnd.hpp>
#include <massxpert/gui/CalculatorWnd.hpp>
#include <massxpert/gui/SequenceEditorWnd.hpp>
#include <massxpert/gui/MzLabWnd.hpp>
#include <massxpert/gui/AboutMassXpertDlg.hpp>
#include <massxpert/gui/MassListSorterDlg.hpp>
#include <massxpert/gui/SeqToolsDlg.hpp>
#include <massxpert/nongui/ConfigSetting.hpp>


namespace msXpSmassXpert
{


MainWindow::MainWindow(const QString &moduleName) : m_moduleName{moduleName}
{
  if(m_moduleName.isEmpty())
    qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

  mpa_configSettings = new ConfigSettings(m_moduleName);


  setupConfigSettings();
  setupWindow();
}


MainWindow::~MainWindow()
{
  // qDebug() << __FILE__ << __LINE__
  // << "Enter ~MainWindow.";

  // qDebug() << __FILE__ << __LINE__
  // << "Start deletion of m_polChemDefCatList items.";

  // List of all the polymer chemistry definition specifications that
  // were found in the different catalogue files on the system.
  while(!m_polChemDefCatList.isEmpty())
    delete m_polChemDefCatList.takeFirst();

  // qDebug() << __FILE__ << __LINE__
  // << "End deletion of m_polChemDefCatList items.";

  delete mpa_configSettings;

  // qDebug() << __FILE__ << __LINE__
  // << "Exit ~MainWindow.";
}


void
MainWindow::readSettings()
{
  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);

  settings.beginGroup("MainWindow");
  restoreGeometry(settings.value("geometry").toByteArray());
  settings.endGroup();
}


void
MainWindow::writeSettings()
{
  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);

  settings.beginGroup("MainWindow");
  settings.setValue("geometry", saveGeometry());
  settings.endGroup();
}


void
MainWindow::closeEvent(QCloseEvent *event)
{
  writeSettings();

  QList<AbstractMainTaskWindow *> childList =
    dynamic_cast<QObject *>(this)->findChildren<AbstractMainTaskWindow *>();

  for(int iter = 0; iter < childList.size(); ++iter)
    {
      childList.at(iter)->close();
    }

  event->accept();

  return;
}

void
MainWindow::setupWindow()
{
  mp_lastFocusedSeqEdWnd = 0;

  createActions();
  createMenus();
  createStatusBar();

  setWindowIcon(QApplication::windowIcon());

  readSettings();

  setWindowTitle(m_moduleName);

  // At this point, try to check if we should remind the user to
  // cite the paper.

  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);
  settings.beginGroup("Globals");

  int runNumber = settings.value("runNumber", 1).toInt();

  if(runNumber == 15)
    {
      // The proper application name will be set by default value parameter.
      AboutMassXpertDlg *dlg =
        new AboutMassXpertDlg(static_cast<QWidget *>(this));

      dlg->showHowToCiteTab();

      dlg->show();

      settings.setValue("runNumber", 1);
    }
  else
    {
      settings.setValue("runNumber", ++runNumber);
    }

  settings.endGroup();
}


bool
MainWindow::setupConfigSettings()
{
  // Initialize the user specifications.
  m_userSpec.setUserName();

  // Construct the file name of the configuration settings file, according
  // to the paradigms of the file system and knowing that this file should
  // be in a home-based path.
  m_configSettingsFilePath =
    QString(QDir::homePath() + "/." + m_moduleName + "/configSettings.ini");

  // Initialize the configuration directories.

  bool result = false;

  if(!initializeSystemConfig())
    {
      result = setConfigSettingsManually();

      if(!result)
        {
          QMessageBox::critical(
            0,
            QString("%1 - Configuration Settings").arg(m_moduleName),
            QString("%1@%2\n"
                    "Failed to configure %3.\n"
                    "The software will not work as "
                    "intended.")
              .arg(__FILE__)
              .arg(__LINE__)
              .arg(m_moduleName),
            QMessageBox::Ok);
        }
    }

  if(!initializeUserConfig())
    {
      qDebug() << __FILE__ << __LINE__
               << "User configuration settings did not succeed.";
    }

  initializeDecimalPlacesOptions();

  PolChemDefCatParser parser;
  parser.setConfigSysUser(POLCHEMDEF_CAT_PARSE_BOTH_CONFIG);
  parser.setPendingMode(POLCHEMDEF_CAT_PARSE_APPEND_CONFIG);

  if(parser.parseFiles(mpa_configSettings, &m_polChemDefCatList) == -1)
    {
      QMessageBox::critical(
        0,
        QString("%1 - Configuration Settings").arg(m_moduleName),
        tr("%1@%2\n"
           "Failed to parse the polymer chemistry "
           "definition catalogues.\n"
           "The software will not work as intended.")
          .arg(__FILE__)
          .arg(__LINE__),
        QMessageBox::Ok);
    }

  return true;
}


bool
MainWindow::initializeSystemConfig()
{
  // First off, some general concepts.

  // When the software is built, a configuration process will
  // provide values for a number of #defined variables, depending on
  // the platform on which the software is built.

  // For example on MS-Windows, the following variables are defined:

  /*
   * #define MASSXPERT_BIN_DIR C:/Program Files/msXpertSuite
   * #define MASSXPERT_DATA_DIR C:/Program Files/msxpertsuite/data/massxpert
   * #define MASSXPERT_DOC_DIR C:/Program Files/msxpertsuite/doc/massxpert
   *
   * This means that for the massxpert module, the data will be copied as
   * C:/Program Files/msxpertsuite/data/massxpert, with the data immediately
   * in that directory, not in a 'data' subdirectory.
   */

  // On GNU/Linux, instead, the following variables are defined:

  /*
   * #define MASSXPERT_BIN_DIR /usr/local/bin
   * #define MASSXPERT_DATA_DIR /usr/local/share/msxpertsuite-massxpert/data
   * #define MASSXPERT_DOC_DIR: /usr/local/share/doc/msxpertsuite-massxpert/doc
   *
   * This means that for the massxpert module, the data will be copied as
   * /usr/local/share/msxpertsuite/massxpert with the data immediately in
   * that directory, not in a 'data' subdirectory.
   */

  /* On Mac OS X, instead, the software is built and then copied into a Mac
   * Bundle massXpert.app, which installs anywhere as long as its internal
   * structure is conserved. There is an API to identify from which location
   * the bundle is used (double-clicking onto the massXpert.app icon). We
   * use that API to get the bundle directory, which might be something like
   * /Applications/massXpert.app if the bundle was dropped into
   * /Applications.
   */

#if defined(Q_WS_MAC)

#if 0

			CFURLRef bundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());

			CFStringRef macPath =
				CFURLCopyFileSystemPath(bundleRef, kCFURLPOSIXPathStyle);

			QString bundleDir =
				CFStringGetCStringPtr(macPath, CFStringGetSystemEncoding());

			CFRelease(bundleRef);
			CFRelease(macPath);
#endif


#endif

  // Now, it might be of interest for the user to install the
  // software in another location than the canonical one. In this
  // case, the program should still be able to access the data.

  // This is what this function is for. This function will try to
  // establish if the data that were built along the software
  // package are actually located in the cnaonical places listed
  // above. If not, this function returns false. The caller might
  // then take actions to let the user manually instruct the program
  // of where the data are located.

  // All this procedure is setup so as to let the user install the
  // software wherever she wants.

  // Settings are set according to a well-defined manner:
  //
  // 1. The QSettings-based configuration is tested first because the user
  // might have moved the data away from the original locations, and then
  // set the new location in the QSettings configuration system.
  //
  // 2. If the above solution fails, then we test the true config.h-based
  // configuration, since this is where the data are installed initially.

  // Handy variables
  QDir testDir;
  QDir finalDir;
  QString dirString;
  ConfigSetting *configSetting = nullptr;

  // Let's store the number of errors, so that we can return true or false.
  int errorCount = 0;


  /////////////////// MASSXPERT_DATA_DIR ///////////////////
  /////////////////// MASSXPERT_DATA_DIR ///////////////////
  /////////////////// MASSXPERT_DATA_DIR ///////////////////

  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);
  settings.beginGroup("systemDataConfigSettings");

  // The main system data directory ////////////////////////////////

  // From the QSettings config file.

  dirString = settings.value("dataDir").toString();
  testDir.setPath(dirString);

  if(!dirString.isEmpty() && testDir.exists() && testDir.isAbsolute())
    {
      // Ah ah, the user already configured a new directory. Fine, let's go
      // with it.
      finalDir = testDir;
    }
  else
    {
      if(QSysInfo::productType() == "osx" || QSysInfo::productType() == "macos")
        {
          // Well, if we are on MacOSX, then we need to know where the bundle is
          // located. Then, we'll craft the directory by appending
          // "Contents/Resources/data".
          dirString = QString("%1/../Resources/massxpert/data")
                        .arg(QCoreApplication::applicationDirPath());
          testDir.setPath(dirString);
          // QMessageBox::information(this,
          //"dir path",
          // QCoreApplication::applicationDirPath(), QMessageBox::Ok);
        }
      else
        {
          /////// From the config.h file

          // On UNIX, that would be
          // /usr/local/share/msxpertsuite-<modulename>/data
          // that is, /usr/local/share/msxpertsuite-massxpert/data, for example.
          dirString = QString("%1").arg(MASSXPERT_DATA_DIR);
          testDir.setPath(dirString);
          // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
          //<< "From the config.h file:" << testDir.absolutePath();
        }

      // The path must be absolute and must exist.

      if(!dirString.isEmpty() && testDir.exists() && testDir.isAbsolute())
        {
          // Fantastic, the initial installation directory exists and that is
          // fine.
          finalDir = testDir;
        }
      else
        {
          // Well, even that failed, we need to craft a dummy ConfigSetting
          // instance with the initial install directory to later let the user
          // configure that directory location anew.

          // We'll tell the caller that at least one directory was definitely
          // not found.
          ++errorCount;

          finalDir = testDir;
        }
    }

  // QMessageBox::information(
  // this, "dataDir path", finalDir.absolutePath(), QMessageBox::Ok);

  // QMessageBox::information(this,
  //"module",
  // m_moduleName, QMessageBox::Ok);

  // And now craft the ConfigSetting instance:

  configSetting             = new ConfigSetting(m_moduleName);
  configSetting->m_userType = msXpS::UserType::USER_TYPE_SYSTEM;
  configSetting->m_key      = "dataDir";
  configSetting->m_value    = finalDir.absolutePath();
  configSetting->m_title    = "System's main data directory";
  configSetting->m_comment =
    "The system's main data directory should be the one set.";
  mpa_configSettings->append(configSetting);


  // The polymer chemistry definitions directory where sits the polymer
  // chemistry definition catalogue ////////////////////////////////

  // From the QSettings config file.

  dirString = settings.value("polChemDefsDir").toString();
  testDir.setPath(dirString);

  if(!dirString.isEmpty() && testDir.exists() && testDir.isAbsolute())
    {
      // Ah ah, the user already configured a new directory. Fine, let's go
      // with it.
      finalDir = testDir;
      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "From the configSettings file:" << testDir.absolutePath();
    }
  else
    {
      if(QSysInfo::productType() == "osx" || QSysInfo::productType() == "macos")
        {
          // Well, if we are on MacOSX, then we need to know where the bundle is
          // located. Then, we'll craft the directory by appending
          // "Contents/Resources/data".
          dirString = QString("%1/../Resources/massxpert/data/polChemDefs")
                        .arg(QCoreApplication::applicationDirPath());
          testDir.setPath(dirString);
          // QMessageBox::information(this,
          //"dir path",
          // QCoreApplication::applicationDirPath(), QMessageBox::Ok);
        }
      else
        {
          /////// From the config.h file

          // That would be
          // /usr/local/share/msxpertsuite/<modulename>/polChemDefs
          // that is, /usr/local/share/msxpertsuite/massxpert/polChemDefs, for
          // example.
          dirString =
            QString("%1/%2").arg(MASSXPERT_DATA_DIR).arg("polChemDefs");
          testDir.setPath(dirString);
          // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
          //<< "From the config.h file:" << testDir.absolutePath();
        }

      if(!dirString.isEmpty() && testDir.exists() && testDir.isAbsolute())
        {
          // Fantastic, the initial installation directory exists and that is
          // fine.
          finalDir = testDir;
        }
      else
        {
          // Well, even that failed, we need to craft a dummy ConfigSetting
          // instance with the initial install directory to later let the user
          // configure that directory location anew.

          // We'll tell the caller that at least one directory was definitely
          // not found.
          ++errorCount;
          finalDir = testDir;
        }
    }

  // QMessageBox::information(
  // this, "dataDir/polChemDefs path", finalDir.absolutePath(),
  // QMessageBox::Ok);

  // And now craft the ConfigSetting instance:

  configSetting             = new ConfigSetting(m_moduleName);
  configSetting->m_userType = msXpS::UserType::USER_TYPE_SYSTEM;
  configSetting->m_key      = "polChemDefsDir";
  configSetting->m_value    = finalDir.absolutePath();
  configSetting->m_title =
    "System's main polymer chemistry definitions directory";
  configSetting->m_comment =
    "The system's main polymer chemistry definitions directory should be the "
    "one set.";
  mpa_configSettings->append(configSetting);


  // The polymer sequences directory ////////////////////////////////

  // From the QSettings config file.

  dirString = settings.value("polSeqsDir").toString();
  testDir.setPath(dirString);

  if(!dirString.isEmpty() && testDir.exists() && testDir.isAbsolute())
    {
      // Ah ah, the user already configured a new directory. Fine, let's go
      // with it.
      finalDir = testDir;
      // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
      //<< "From the configSettings file:" << testDir.absolutePath();
    }
  else
    {
      if(QSysInfo::productType() == "osx" || QSysInfo::productType() == "macos")
        {
          // Well, if we are on MacOSX, then we need to know where the bundle is
          // located. Then, we'll craft the directory by appending
          // "Contents/Resources/data".
          dirString = QString("%1/../Resources/massxpert/data/polSeqs")
                        .arg(QCoreApplication::applicationDirPath());
          testDir.setPath(dirString);
          // QMessageBox::information(this,
          //"dir path",
          // QCoreApplication::applicationDirPath(), QMessageBox::Ok);
        }
      else
        {
          /////// From the config.h file

          // That would be /usr/local/share/msxpertsuite/<modulename>/polSeqs
          // that is, /usr/local/share/msxpertsuite/massxpert/polSeqs, for
          // example.
          dirString = QString("%1/%2").arg(MASSXPERT_DATA_DIR).arg("polSeqs");
          testDir.setPath(dirString);
          // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
          //<< "From the config.h file:" << testDir.absolutePath();
        }

      // The path must be absolute and must exist.

      if(!dirString.isEmpty() && testDir.exists() && testDir.isAbsolute())
        {
          // Fantastic, the initial installation directory exists and that is
          // fine.
          finalDir = testDir;
        }
      else
        {
          // Well, even that failed, we need to craft a dummy ConfigSetting
          // instance with the initial install directory to later let the user
          // configure that directory location anew.

          // We'll tell the caller that at least one directory was definitely
          // not found.
          ++errorCount;
          finalDir = testDir;
        }
    }

  // QMessageBox::information(
  // this, "dataDir/polSeqs path", finalDir.absolutePath(), QMessageBox::Ok);

  // And now craft the ConfigSetting instance:

  configSetting             = new ConfigSetting(m_moduleName);
  configSetting->m_userType = msXpS::UserType::USER_TYPE_SYSTEM;
  configSetting->m_key      = "polSeqsDir";
  configSetting->m_value    = finalDir.absolutePath();
  configSetting->m_title    = "System's main polymer sequences directory";
  configSetting->m_comment =
    "The system's main polymer sequences directory should be the one set.";
  mpa_configSettings->append(configSetting);


  // The documentation directory ////////////////////////////////

  // From the QSettings config file.

  dirString = settings.value("docDir").toString();
  testDir.setPath(dirString);

  if(!dirString.isEmpty() && testDir.exists() && testDir.isAbsolute())
    {
      // Ah ah, the user already configured a new directory. Fine, let's go
      // with it.
      finalDir = testDir;
    }
  else
    {
      if(QSysInfo::productType() == "osx" || QSysInfo::productType() == "macos")
        {
          // Well, if we are on MacOSX, then we need to know where the bundle is
          // located. Then, we'll craft the directory by appending
          // "Contents/Resources/doc".
          dirString = QString("%1/../Resources/massxpert/doc")
                        .arg(QCoreApplication::applicationDirPath());
          testDir.setPath(dirString);
          // QMessageBox::information(this,
          //"dir path",
          // QCoreApplication::applicationDirPath(), QMessageBox::Ok);
        }
      else
        {
          /////// From the config.h file

          // That would be /usr/local/share/doc/msxpertsuite
          // that is, /usr/local/share/doc/msxpertsuite, for example.
          dirString = QString("%1").arg(MASSXPERT_DOC_DIR);
          testDir.setPath(dirString);
        }

      // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
      //<< "massxpert doc dir: " << QString("%1").arg(MASSXPERT_DOC_DIR);

      // The path must be absolute and must exist.

      if(!dirString.isEmpty() && testDir.exists() && testDir.isAbsolute())
        {
          // Fantastic, the initial installation directory exists and that is
          // fine.
          finalDir = testDir;
        }
      else
        {
          // Well, even that failed, we need to craft a dummy ConfigSetting
          // instance with the initial install directory to later let the user
          // configure that directory location anew.

          // We'll tell the caller that at least one directory was definitely
          // not found.
          ++errorCount;

          finalDir = testDir;
          // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()";
        }
    }

  // QMessageBox::information(
  // this, "doc dir path", finalDir.absolutePath(), QMessageBox::Ok);

  // And now craft the ConfigSetting instance:

  configSetting             = new ConfigSetting(m_moduleName);
  configSetting->m_userType = msXpS::UserType::USER_TYPE_SYSTEM;
  configSetting->m_key      = "docDir";
  configSetting->m_value    = finalDir.absolutePath();
  configSetting->m_title    = "System's main documentation directory";
  configSetting->m_comment =
    "The system's main documentation directory should be the one set.";
  mpa_configSettings->append(configSetting);

  // Finally we can close the group.
  settings.endGroup();

  if(errorCount > 0)
    return false;
  else
    return true;
}


bool
MainWindow::initializeUserConfig()
{
  // First off, some general concepts.
  //
  // massXpert should let the user define any polymer chemistry of
  // his requirement, without needing the root priviledges for
  // installation of these new data.
  //
  // This is why massXpert allows for personal data directories
  // modelled on the same filesystem scheme as for the system data
  // directory.
  //
  // When the program is run, it check if the .massxpert directory
  // exists in the home directory of the user running the
  // program. If that directory does not exist, it is created, along
  // with its polChemDefs directory.
  //
  // On MS-Windows sytems, the home directory is located at:
  //
  // C:\Users\<username>
  //
  // On GNU/Linux and UNIX-based systems, the home directory is:
  //
  // /home/<username>
  //
  // Thus, respectively, the user has at its disposal the following
  // personal user data directories:
  //
  // C:\Users\<username>\.massxpert\polChemDefs
  //
  // and
  //
  // /home/<username>/.massxpert/polChemDefs
  //
  // Same thing for polSeqs.

  // Handy variables
  QDir testDir;
  QDir finalDir;
  ConfigSetting *configSetting = nullptr;

  // Let's store the number of errors, so that we can return true or false.
  int errorCount = 0;

  /////////////////// $HOME/.<modulename> ///////////////////
  /////////////////// $HOME/.<modulename> ///////////////////
  /////////////////// $HOME/.<modulename> ///////////////////

  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);
  settings.beginGroup("userDataConfigSettings");

  // We could start by checking if the canonical $HOME/.<moduleName>
  // directory is there, but in fact it might be there but not used, with
  // the user having configured already a new location within the config
  // settings file. So we start by checking that first.

  testDir.setPath(settings.value("dataDir").toString());

  if(!testDir.path().isEmpty() && testDir.exists())
    {
      // Ah ah, the user already configured a new directory. Fine, let's go
      // with it.
      finalDir = testDir;
    }
  else
    {
      // No, the user has not configured an alternative directory, so let's
      // look if the canonical one is there:

      // The data are installed by default to the user's home directory
      // that would be /home/<username>/.massxpert.

      testDir.setPath(QDir::homePath() + "/." + m_moduleName);

      if(testDir.exists())
        {
          // Great, the user has a personal directory, and that is the canonical
          // one, which means she has not moved it from its original place.

          // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
          //<< "The user .massxpert directory exists";

          finalDir = testDir;
        }
      else
        {
          // There is no such user data directory, which is normal if the user
          // has never run the program yet. Let's create that directory and tell
          // the caller that at least one directory was definitely not found.

          ++errorCount;

          if(!testDir.mkpath(testDir.absolutePath()))
            {
              qFatal(
                "Fatal error at %s@%d -- %s(). "
                "Directory %s did not exist and its creation failed."
                "Program aborted.",
                __FILE__,
                __LINE__,
                __FUNCTION__,
                testDir.absolutePath().toLatin1().data());
            }
          else
            {
              // At last, we could get a working directory...

              // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
              //<< "The" << QDir::homePath() + "/." + m_moduleName
              //<< "directory was successfully created.";

              finalDir = testDir;
            }
        }
    }

  // And now craft the ConfigSetting instance:

  configSetting             = new ConfigSetting(m_moduleName);
  configSetting->m_userType = msXpS::UserType::USER_TYPE_USER;
  configSetting->m_key      = "dataDir";
  configSetting->m_value    = finalDir.absolutePath();
  configSetting->m_title    = "Users's main data directory";
  configSetting->m_comment =
    "The user's main data directory should be the one set.";
  mpa_configSettings->append(configSetting);


  // The polymer chemistry definitions directory where sits the polymer
  // chemistry definition catalogue ////////////////////////////////

  // We could start by checking if the canonical $HOME/.<moduleName>
  // directory is there, but in fact it might be there but not used, with
  // the user having configured already a new location within the config
  // settings file. So we start by checking that first.

  testDir.setPath(settings.value("polChemDefsDir").toString());

  if(!testDir.path().isEmpty() && testDir.exists())
    {
      // Ah ah, the user already configured a new directory. Fine, let's go
      // with it.

      // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
      //<< "The polChemDefsDir (" << testDir.path()
      //<< "directory was found.";

      finalDir = testDir;
    }
  else
    {
      // No, the user has not configured an alternative directory, so let's
      // look if the canonical one is there:

      // The data are installed by default to the user's home directory
      // that would be /home/<username>/.massxpert/polChemDefs.

      testDir.setPath(QDir::homePath() + "/." + m_moduleName + "/polChemDefs");

      if(testDir.exists())
        {
          // Great, the user has a personal directory, and that is the canonical
          // one, which means she has not moved it from its original place.

          // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
          //<< "The user polChemDefs directory found.";

          finalDir = testDir;
        }
      else
        {
          // There is no such user data directory, which is normal if the user
          // has never run the program yet. Let's create that directory and tell
          // the caller that at least one directory was definitely not found.
          ++errorCount;

          if(!testDir.mkpath(testDir.absolutePath()))
            {
              qFatal(
                "Fatal error at %s@%d -- %s(). "
                "Directory %s did not exist and its creation failed."
                "Program aborted.",
                __FILE__,
                __LINE__,
                __FUNCTION__,
                testDir.absolutePath().toLatin1().data());
            }
          else
            {
              // At last, we could get a working directory...

              // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
              //<< "The" << QDir::homePath() + "/." + m_moduleName
              //<< "directory was successfully created.";

              finalDir = testDir;
            }
        }
    }

  // And now craft the ConfigSetting instance:

  configSetting             = new ConfigSetting(m_moduleName);
  configSetting->m_userType = msXpS::UserType::USER_TYPE_USER;
  configSetting->m_key      = "polChemDefsDir";
  configSetting->m_value    = finalDir.absolutePath();
  configSetting->m_title =
    "User's main polymer chemistry definitions directory";
  configSetting->m_comment =
    "The user's main polymer chemistry definitions directory should be the one "
    "set.";
  mpa_configSettings->append(configSetting);


  // The polymer sequences directory ////////////////////////////////

  // We could start by checking if the canonical $HOME/.<moduleName>
  // directory is there, but in fact it might be there but not used, with
  // the user having configured already a new location within the config
  // settings file. So we start by checking that first.

  testDir.setPath(settings.value("polSeqsDir").toString());

  if(!testDir.path().isEmpty() && testDir.exists())
    {
      // Ah ah, the user already configured a new directory. Fine, let's go
      // with it.

      // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
      //<< "The polSeqsDir (" << testDir.path()
      //<< "directory was found.";

      finalDir = testDir;
    }
  else
    {
      // No, the user has not configured an alternative directory, so let's
      // look if the canonical one is there:

      // The data are installed by default to the user's home directory
      // that would be /home/<username>/.massxpert/polChemDefs.

      testDir.setPath(QDir::homePath() + "/." + m_moduleName + "/polSeqs");

      if(testDir.exists())
        {
          // Great, the user has a personal directory, and that is the canonical
          // one, which means she has not moved it from its original place.

          // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
          //<< "The user polSeqs directory found.";

          finalDir = testDir;
        }
      else
        {
          // There is no such user data directory, which is normal if the user
          // has never run the program yet. Let's create that directory and tell
          // the caller that at least one directory was definitely not found.
          ++errorCount;

          if(!testDir.mkpath(testDir.absolutePath()))
            {
              qFatal(
                "Fatal error at %s@%d -- %s(). "
                "Directory %s did not exist and its creation failed."
                "Program aborted.",
                __FILE__,
                __LINE__,
                __FUNCTION__,
                testDir.absolutePath().toLatin1().data());
            }
          else
            {
              // At last, we could get a working directory...

              // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
              //<< "The" << QDir::homePath() + "/." + m_moduleName
              //<< "directory was successfully created.";

              finalDir = testDir;
            }
        }
    }

  // And now craft the ConfigSetting instance:

  configSetting             = new ConfigSetting(m_moduleName);
  configSetting->m_userType = msXpS::UserType::USER_TYPE_USER;
  configSetting->m_key      = "polSeqsDir";
  configSetting->m_value    = finalDir.absolutePath();
  configSetting->m_title    = "User's polymer sequences directory";
  configSetting->m_comment =
    "The user's polymer sequences directory should be the one set.";
  mpa_configSettings->append(configSetting);

  // Finally we can close the settings group.
  settings.endGroup();

  return true;
}


QString
MainWindow::configSettingsFilePath() const
{
  return m_configSettingsFilePath;
}


const msXpSlibmass::UserSpec &
MainWindow::userSpec() const
{
  return m_userSpec;
}


const ConfigSettings *
MainWindow::configSettings() const
{
  return mpa_configSettings;
}


bool
MainWindow::setConfigSettingsManually()
{

  qDebug() << __FILE__ << __LINE__ << "Starting manual configuration.";

  // Apparently, the system configuration directory was not found to
  // be of a canonical structure. This might be due to the
  // relocation of the package. Let the user tell where the package
  // is located.
  ConfigSettingsDlg dlg(
    m_moduleName, mpa_configSettings, m_configSettingsFilePath);

  int result = dlg.exec();

  if(result)
    return true;
  else
    return false;
}


void
MainWindow::initializeDecimalPlacesOptions()
{
  // The decimal places should be read from the configuration
  // settings.

  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);

  settings.beginGroup("decimalPlacesOptions");

  msXpS::ATOM_DEC_PLACES = settings.value("ATOM_DEC_PLACES", 10).toInt();
  // qDebug() << __FILE__ << __LINE__
  // << "ATOM_DEC_PLACES" << ATOM_DEC_PLACES;

  msXpS::OLIGOMER_DEC_PLACES = settings.value("OLIGOMER_DEC_PLACES", 5).toInt();
  // qDebug() << __FILE__ << __LINE__
  // << "OLIGOMER_DEC_PLACES" << OLIGOMER_DEC_PLACES;

  msXpS::POLYMER_DEC_PLACES = settings.value("POLYMER_DEC_PLACES", 3).toInt();
  // qDebug() << __FILE__ << __LINE__
  // << "POLYMER_DEC_PLACES" << POLYMER_DEC_PLACES;

  msXpS::PH_PKA_DEC_PLACES = settings.value("PH_PKA_DEC_PLACES", 2).toInt();
  // qDebug() << __FILE__ << __LINE__
  // << "PH_PKA_DEC_PLACES" << PH_PKA_DEC_PLACES;

  settings.endGroup();
}


void
MainWindow::setLastFocusedSeqEdWnd(SequenceEditorWnd *wnd)
{
  mp_lastFocusedSeqEdWnd = wnd;
  // qDebug() << __FILE__<< __LINE__ << wnd;
}


QList<PolChemDefSpec *> *
MainWindow::polChemDefCatList()
{
  return &m_polChemDefCatList;
}


QList<PolChemDef *> *
MainWindow::polChemDefList()
{
  return &m_polChemDefList;
}


PolChemDefSpec *
MainWindow::polChemDefSpecName(const QString &name)
{
  PolChemDefSpec *polChemDefSpec = 0;

  for(int iter = 0; iter < m_polChemDefCatList.size(); ++iter)
    {
      polChemDefSpec = m_polChemDefCatList.at(iter);

      if(polChemDefSpec->name() == name)
        return polChemDefSpec;
    }

  return 0;
}


PolChemDefSpec *
MainWindow::polChemDefSpecFilePath(const QString &filePath)
{
  PolChemDefSpec *polChemDefSpec = 0;

  for(int iter = 0; iter < m_polChemDefCatList.size(); ++iter)
    {
      polChemDefSpec = m_polChemDefCatList.at(iter);

      if(polChemDefSpec->filePath() == filePath)
        return polChemDefSpec;
    }

  return 0;
}


PolChemDef *
MainWindow::polChemDefName(const QString &name)
{
  PolChemDef *polChemDef = 0;

  for(int iter = 0; iter < m_polChemDefList.size(); ++iter)
    {
      polChemDef = m_polChemDefList.at(iter);

      // qDebug() << __FILE__ << __LINE__
      // << "iterated polChemDef:" << polChemDef->name();

      if(polChemDef->name() == name)
        return polChemDef;
    }

  return 0;
}


void
MainWindow::polChemDefCatStringList(QStringList &stringList)
{
  PolChemDefSpec *polChemDefSpec = 0;

  for(int iter = 0; iter < m_polChemDefCatList.size(); ++iter)
    {
      polChemDefSpec = m_polChemDefCatList.at(iter);

      stringList << polChemDefSpec->filePath();
    }
}


bool
MainWindow::isSequenceEditorWnd(SequenceEditorWnd *seqEdWnd) const
{
  // Does this pointer correspond to a sequence editor window
  // currently opened?

  for(int iter = 0; iter < m_sequenceEditorWndList.size(); ++iter)
    {
      if(m_sequenceEditorWndList.at(iter) == seqEdWnd)
        return true;
    }

  return false;
}


void
MainWindow::openPolChemDef()
{
  // We are asked to open a polymer chemistry definition file, which
  // we'll do inside a PolChemDefWnd.
  QStringList stringList;
  QString filePath;
  bool ok;

  // Get the polchemdef catalogue contents.
  polChemDefCatStringList(stringList);

  // Ask the user to select one file or to click Cancel to browse.
  filePath = QInputDialog::getItem(this,
                                   tr("Select a polymer chemistry definition "
                                      "or click Cancel to browse"),
                                   tr("Polymer chemistry definition:"),
                                   stringList,
                                   0,
                                   false,
                                   &ok);

  if(!ok || filePath.isEmpty())
    {
      filePath = QFileDialog::getOpenFileName(this,
                                              tr("Open definition file"),
                                              QDir::homePath(),
                                              tr("XML files(*.xml *.XML)"));

      if(filePath.isNull() || filePath.isEmpty())
        return;

      if(!QFile::exists(filePath))
        return;
    }

  new PolChemDefWnd(this, filePath);
}


void
MainWindow::newPolChemDef()
{
  // We are asked to create a polymer chemistry definition file, which
  // we'll do inside a PolChemDefWnd.

  new PolChemDefWnd(this);
}


void
MainWindow::newCalculator()
{
  QStringList stringList;
  QString filePath;
  bool ok;

  // Open a calculator window, making sure that a polymer chemistry
  // definition is offered to open to the caller.

  // Get the polchemdef catalogue contents.
  polChemDefCatStringList(stringList);

  // Ask the user to select one file or to click Cancel to browse.
  filePath = QInputDialog::getItem(this,
                                   tr("Select a polymer chemistry definition "
                                      "or click Cancel to browse"),
                                   tr("Polymer chemistry definition:"),
                                   stringList,
                                   0,
                                   false,
                                   &ok);

  if(!ok || filePath.isEmpty())
    {
      // We could not get the filePath. Try by giving the user the
      // opportunity to select a file from the filesystem.
      filePath = QFileDialog::getOpenFileName(this,
                                              tr("Open definition file"),
                                              QDir::homePath(),
                                              tr("XML files(*.xml *.XML)"));

      if(filePath.isNull() || filePath.isEmpty())
        return;

      if(!QFile::exists(filePath))
        {
          QMessageBox::warning(this,
                               tr("massXpert"),
                               tr("File(%1) not found.").arg(filePath),
                               QMessageBox::Ok);

          return;
        }
    }

  // Open a calculator window without any polymer chemistry definition.

  new CalculatorWnd(this, filePath);
}


void
MainWindow::openSequence(const QString &fileName)
{
  QString filePath;
  QString name;

  if(fileName.isEmpty())
    {
      // Has the user configured a path to the polymer sequences?
      // That is, we should check for userPolSeqsDir.

      // Get the directory where the system data reside:
      const ConfigSetting *configSetting = mpa_configSettings->value(
        "polSeqsDir", msXpS::UserType::USER_TYPE_USER);
      QDir polSeqsDir(configSetting->m_value.toString());

      filePath =
        QFileDialog::getOpenFileName(this,
                                     tr("Open sequence file"),
                                     polSeqsDir.absolutePath(),
                                     tr("Sequence files(*.mxp *.mXp *.MXP)"
                                        ";; All files(*.*)"));
    }
  else
    filePath = fileName;

  if(filePath.isNull() || filePath.isEmpty())
    return;

  if(!QFile::exists(filePath))
    {
      QMessageBox::warning(this,
                           tr("massXpert"),
                           tr("File(%1) not found.").arg(filePath),
                           QMessageBox::Ok);

      return;
    }

  SequenceEditorWnd *sequenceEditorWnd = new SequenceEditorWnd(this);

  if(!sequenceEditorWnd->openSequence(filePath))
    {
      QMessageBox::warning(this,
                           tr("massXpert"),
                           tr("%1@%2\n"
                              "Failed to open sequence in the editor window.")
                             .arg(__FILE__)
                             .arg(__LINE__),
                           QMessageBox::Ok);

      sequenceEditorWnd->close();

      return;
    }

  // At this time we have a polymer chemistry definition window that
  // is fully initialized and functional. We can add its pointer to
  // the list of such windows that is stored in the application
  // object.
  m_sequenceEditorWndList.append(sequenceEditorWnd);

  // Finally connect the about to close signal to a function
  // that removes the sequence editor window pointer from the list.
  connect(sequenceEditorWnd,
          &SequenceEditorWnd::polymerSequenceWndAboutToClose,
          this,
          &MainWindow::delistSequenceEditorWnd);
}


void
MainWindow::openSampleSequence()
{
  // This menu is served to the user so that he can get immediate
  // access to the sample sequences even without knowing what the
  // structure of the data is on the filesystem. In particular, the
  // user might not be able to find the sample sequences on a UNIX
  // system or in a MacOSX bundle.

  QStringList stringList;
  QString filePath;
  QString name;

  // Get the directory where the system data reside:
  const ConfigSetting *configSetting =
    mpa_configSettings->value("polSeqsDir", msXpS::UserType::USER_TYPE_SYSTEM);

  QDir polSeqsDir(configSetting->m_value.toString());

  QStringList filters;
  filters << "*.mxp"
          << "*.MXP";

  polSeqsDir.setNameFilters(filters);

  QFileInfoList infoList =
    polSeqsDir.entryInfoList(filters, QDir::Files | QDir::Readable);

  for(int iter = 0; iter < infoList.size(); ++iter)
    {
      QFileInfo fileInfo(infoList.at(iter));
      stringList << fileInfo.absoluteFilePath();
    }

  bool ok = false;

  // Ask the user to select one file or to click Cancel to browse.
  filePath = QInputDialog::getItem(this,
                                   tr("Select a sample sequence file "
                                      "or click Cancel to browse"),
                                   tr("Sample sequence file:"),
                                   stringList,
                                   0,
                                   false,
                                   &ok);

  if(!ok || filePath.isEmpty())
    {
      // We could not get the filePath. Try by giving the user the
      // opportunity to select a file from the filesystem.
      filePath = QFileDialog::getOpenFileName(this,
                                              tr("Open sequence file"),
                                              QDir::homePath(),
                                              tr("mxp files(*.mxp *.MXP)"
                                                 ";; All files(*.*)"));
    }

  if(filePath.isNull() || filePath.isEmpty())
    return;

  if(!QFile::exists(filePath))
    {
      QMessageBox::warning(this,
                           tr("massXpert"),
                           tr("File %1 not found.").arg(filePath),
                           QMessageBox::Ok);

      return;
    }

  SequenceEditorWnd *sequenceEditorWnd = new SequenceEditorWnd(this);

  if(!sequenceEditorWnd->openSequence(filePath))
    {
      QMessageBox::warning(this,
                           tr("massXpert"),
                           tr("%1@%2\n"
                              "Failed to open sequence in the editor window.")
                             .arg(__FILE__)
                             .arg(__LINE__),
                           QMessageBox::Ok);

      return;
    }

  // At this time we have a polymer chemistry definition window that
  // is fully initialized and functional. We can add its pointer to
  // the list of such windows.

  m_sequenceEditorWndList.append(sequenceEditorWnd);
}


void
MainWindow::newSequence()
{
  QStringList stringList;

  // The user should first tell of what polymer chemistry definition
  // the sequence should be. Let him choose amongst the available
  // polymer chemistry definitions:
  // Get the polchemdef catalogue contents.

  polChemDefCatStringList(stringList);

  // Ask the user to select one file or to click Cancel to browse.
  bool ok;
  QString filePath =
    QInputDialog::getItem(this,
                          tr("Select a polymer chemistry definition "
                             "or click Cancel to browse"),
                          tr("Polymer chemistry definition:"),
                          stringList,
                          0,
                          false,
                          &ok);

  if(!ok || filePath.isEmpty())
    {
      // We could not get the filePath. Try by giving the user the
      // opportunity to select a file from the filesystem.
      filePath = QFileDialog::getOpenFileName(this,
                                              tr("Open definition file"),
                                              QDir::homePath(),
                                              tr("XML files(*.xml *.XML)"
                                                 ";; All files(*.*)"));

      if(filePath.isNull() || filePath.isEmpty())
        return;

      if(!QFile::exists(filePath))
        {
          QMessageBox::warning(this,
                               tr("massXpert"),
                               tr("File(%1) not found.").arg(filePath),
                               QMessageBox::Ok);

          return;
        }
    }

  // At this stage we should have a proper polymer chemistry
  // definition filePath.

  SequenceEditorWnd *sequenceEditorWnd = new SequenceEditorWnd(this);

  if(!sequenceEditorWnd->newSequence(filePath))
    {
      QMessageBox::warning(this,
                           tr("massXpert"),
                           tr("%1@%2\n"
                              "Failed to create sequence in the editor window.")
                             .arg(__FILE__)
                             .arg(__LINE__),
                           QMessageBox::Ok);

      sequenceEditorWnd->close();

      return;
    }

  // At this time we have a polymer chemistry definition window that
  // is fully initialized and functional. We can add its pointer to
  // the list of such windows.
  m_sequenceEditorWndList.append(sequenceEditorWnd);
}


void
MainWindow::delistSequenceEditorWnd(SequenceEditorWnd *wnd)
{
  qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()";

  for(int iter = 0; iter < m_sequenceEditorWndList.size(); ++iter)
    {
      if(m_sequenceEditorWndList.at(iter) == wnd)
        m_sequenceEditorWndList.takeAt(iter);
    }

  qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()";
}


void
MainWindow::mzLab()
{
  QStringList stringList;
  QString filePath;
  bool ok;

  // Make sure that a polymer chemistry definition is offered to
  // open to the caller.

  // Get the polchemdef catalogue contents.
  polChemDefCatStringList(stringList);

  // Ask the user to select one file or to click Cancel to browse.
  filePath = QInputDialog::getItem(this,
                                   tr("Select a polymer chemistry definition "
                                      "or click Cancel to browse"),
                                   tr("Polymer chemistry definition:"),
                                   stringList,
                                   0,
                                   false,
                                   &ok);

  if(!ok || filePath.isEmpty())
    {
      // We could not get the filePath. Try by giving the user the
      // opportunity to select a file from the filesystem.
      filePath = QFileDialog::getOpenFileName(this,
                                              tr("Open definition file"),
                                              QDir::homePath(),
                                              tr("XML files(*.xml *.XML)"));

      if(filePath.isNull() || filePath.isEmpty())
        return;

      if(!QFile::exists(filePath))
        {
          QMessageBox::warning(this,
                               tr("massXpert"),
                               tr("File(%1) not found.").arg(filePath),
                               QMessageBox::Ok);

          return;
        }
    }

  MzLabWnd *mzLabWnd = new MzLabWnd(this, filePath);

  mzLabWnd->show();
}


void
MainWindow::massListSorter()
{
  MassListSorterDlg *dialog = new MassListSorterDlg(this);

  dialog->show();
}


void
MainWindow::seqTools()
{
  SeqToolsDlg *dialog = new SeqToolsDlg(this);

  dialog->show();
}

void
MainWindow::about()
{
  // The application name will be set automatically by default parameter
  // value.
  AboutMassXpertDlg *dlg = new AboutMassXpertDlg(this);

  dlg->show();
}


void
MainWindow::createActions()
{
  // File/Config settings
  configSettingsAct = new QAction(tr("Config &settings"), this);
  configSettingsAct->setShortcut(tr("Ctrl+S"));
  configSettingsAct->setStatusTip(tr("Set the configuration"));
  connect(configSettingsAct,
          &QAction::triggered,
          this,
          &MainWindow::setConfigSettingsManually);

  // File/Exit
  exitAct = new QAction(tr("E&xit"), this);
  exitAct->setShortcut(tr("Ctrl+Q"));
  exitAct->setStatusTip(tr("Exit the application"));
  connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));

  // XpertDef Menu/Open PolChemDef
  openPolChemDefAct =
    new QAction(QIcon(":/images/open.png"), tr("&Open..."), this);
  openPolChemDefAct->setShortcut(tr("Ctrl+D,O"));
  openPolChemDefAct->setStatusTip(
    tr("Open an existing polymer chemistry definition file"));
  connect(openPolChemDefAct, SIGNAL(triggered()), this, SLOT(openPolChemDef()));


  // XpertDef Menu/New PolChemDef
  newPolChemDefAct =
    new QAction(QIcon(":/images/new.png"), tr("&New..."), this);
  newPolChemDefAct->setShortcut(tr("Ctrl+D,N"));
  newPolChemDefAct->setStatusTip(
    tr("Create a new polymer chemistry definition file"));
  connect(newPolChemDefAct, SIGNAL(triggered()), this, SLOT(newPolChemDef()));


  // XpertCalc Menu/New Calculator
  newCalculatorAct =
    new QAction(QIcon(":/images/new.png"), tr("&Open calculator"), this);
  newCalculatorAct->setShortcut(tr("Ctrl+C,O"));
  newCalculatorAct->setStatusTip(tr("Open a new calculator window"));
  connect(newCalculatorAct, SIGNAL(triggered()), this, SLOT(newCalculator()));


  // XpertEdit Menu/Open Sample Sequence
  openSampleSequenceAct =
    new QAction(QIcon(":/images/new.png"), tr("&Open sample sequence"), this);
  openSampleSequenceAct->setShortcut(tr("Ctrl+E,S"));
  openSampleSequenceAct->setStatusTip(
    tr("Open an sample polymer sequence file"));
  connect(openSampleSequenceAct,
          SIGNAL(triggered()),
          this,
          SLOT(openSampleSequence()));

  openSequenceAct =
    new QAction(QIcon(":/images/new.png"), tr("&Open sequence"), this);
  openSequenceAct->setShortcut(tr("Ctrl+E,O"));
  openSequenceAct->setStatusTip(tr("Open an existing polymer sequence file"));
  connect(openSequenceAct, SIGNAL(triggered()), this, SLOT(openSequence()));

  // XpertEdit Menu/New Sequence
  newSequenceAct =
    new QAction(QIcon(":/images/new.png"), tr("&New sequence"), this);
  newSequenceAct->setShortcut(tr("Ctrl+E,N"));
  newSequenceAct->setStatusTip(tr("Create a new polymer sequence file"));
  connect(newSequenceAct, SIGNAL(triggered()), this, SLOT(newSequence()));

  // XpertMiner Menu/mz Lab
  mzLabAct = new QAction(QIcon(":/images/new.png"), tr("&mz Lab"), this);
  mzLabAct->setShortcut(tr("Ctrl+M,Z"));
  mzLabAct->setStatusTip(tr("Open a new mz lab window"));
  connect(mzLabAct, SIGNAL(triggered()), this, SLOT(mzLab()));

  // XpertTools
  massListSorterAct =
    new QAction(QIcon(":/images/new.png"), tr("&mass List Sorter"), this);
  massListSorterAct->setShortcut(tr("Ctrl+T,M"));
  massListSorterAct->setStatusTip(tr("Sort masses in a list"));
  connect(massListSorterAct, SIGNAL(triggered()), this, SLOT(massListSorter()));

  seqToolsAct =
    new QAction(QIcon(":/images/new.png"), tr("&sequence Tools"), this);
  seqToolsAct->setShortcut(tr("Ctrl+T,S"));
  seqToolsAct->setStatusTip(tr("Tools for sequence manipulation"));
  connect(seqToolsAct, SIGNAL(triggered()), this, SLOT(seqTools()));

  // Help
  aboutAct = new QAction(tr("&About"), this);
  aboutAct->setShortcut(tr("Ctrl+H"));
  aboutAct->setStatusTip(tr("Show the application's About box"));
  connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));

  aboutQtAct = new QAction(tr("About &Qt"), this);
  aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
  connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
}

void
MainWindow::createMenus()
{
  fileMenu = menuBar()->addMenu(tr("&File"));
  fileMenu->addAction(configSettingsAct);
  fileMenu->addAction(exitAct);

  menuBar()->addSeparator();

  // XpertDef
  xpertDefMenu = menuBar()->addMenu(tr("Xpert&Def"));
  xpertDefMenu->addAction(openPolChemDefAct);
  xpertDefMenu->addAction(newPolChemDefAct);

  menuBar()->addSeparator();

  // XpertCalc
  xpertCalcMenu = menuBar()->addMenu(tr("Xpert&Calc"));
  xpertCalcMenu->addAction(newCalculatorAct);

  menuBar()->addSeparator();

  // XpertEdit
  xpertEditMenu = menuBar()->addMenu(tr("Xpert&Edit"));
  xpertEditMenu->addAction(openSequenceAct);
  xpertEditMenu->addAction(openSampleSequenceAct);
  xpertEditMenu->addAction(newSequenceAct);

  menuBar()->addSeparator();

  // XpertMiner
  xpertMinerMenu = menuBar()->addMenu(tr("Xpert&Miner"));
  xpertMinerMenu->addAction(mzLabAct);

  menuBar()->addSeparator();

  // Tools
  toolsMenu = menuBar()->addMenu(tr("&Tools"));
  toolsMenu->addAction(massListSorterAct);
  toolsMenu->addAction(seqToolsAct);

  menuBar()->addSeparator();

  // help
  helpMenu = menuBar()->addMenu(tr("&Help"));
  helpMenu->addAction(aboutAct);
  helpMenu->addAction(aboutQtAct);
}


void
MainWindow::createStatusBar()
{
  statusBar()->showMessage(tr("Ready"));
}

} // namespace msXpSmassXpert
