/* 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 "MainWindow.hpp"

#include "../nongui/PolChemDefSpec.hpp"
#include "../nongui/PolChemDefCatParser.hpp"

#include "ConfigSettingsDlg.hpp"

#include "PolChemDefWnd.hpp"
#include "CalculatorWnd.hpp"
#include "SequenceEditorWnd.hpp"
#include "MzLabWnd.hpp"
#include "MassListSorterDlg.hpp"
#include "SeqToolsDlg.hpp"
#include "../nongui/ConfigSetting.hpp"


namespace msxps
{

namespace massxpert
{


  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.
        AboutDlg *dlg = new AboutDlg(static_cast<QWidget *>(this));

        dlg->showHowToCiteTab();

        dlg->show();

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

    settings.endGroup();
  }


  void
  MainWindow::showAboutDlg()
  {
    // The application name will be set automatically by default parameter
    // value.
    AboutDlg *dlg = new AboutDlg(this, "massXpert");

    dlg->show();
  }


  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 BIN_DIR C:/Program Files/msXpertSuite
     * #define DATA_DIR C:/Program Files/msxpertsuite/data/massxpert
     * #define 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 BIN_DIR /usr/local/bin
     * #define DATA_DIR /usr/local/share/msxpertsuite-massxpert/data
     * #define 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;


    /////////////////// DATA_DIR ///////////////////
    /////////////////// DATA_DIR ///////////////////
    /////////////////// 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(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 = 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(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 = 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(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 = 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(DOC_DIR);
            testDir.setPath(dirString);
          }

        // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
        //<< "massxpert doc dir: " << QString("%1").arg(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 = 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 = 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 = 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 = 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 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");

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

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

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

    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", 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", 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.
    AboutDlg *dlg = new AboutDlg(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(QIcon(":/images/icons/svg/help-information-icon.svg"),
                  tr("&About"),
                  dynamic_cast<QObject *>(this));
    aboutAct->setStatusTip(tr("Show the application's About box"));
    aboutAct->setShortcut(tr("Ctrl+H"));
    aboutAct->setStatusTip(tr("Show the application's About box"));
    connect(aboutAct, SIGNAL(triggered()), this, SLOT(showAboutDlg()));

    aboutQtAct =
      new QAction(QIcon(":/images/icons/svg/help-qt-information-icon.svg"),
                  tr("About &Qt"),
                  dynamic_cast<QObject *>(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 massxpert

} // namespace msxps
