15.1.287. tablet_qt/menu/helpmenu.cpp

/*
    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS 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.

    CamCOPS 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 CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/

#include "helpmenu.h"

#include <QSqlDriver>
#include <QTextStream>
#include <QtNetwork/QSslSocket>
#include <QtSql/QtSqlVersion>

#include "common/platform.h"
#include "common/uiconst.h"
#include "common/urlconst.h"
#include "db/databasemanager.h"
#include "db/dbfunc.h"
#include "db/whichdb.h"
#include "dialogs/scrollmessagebox.h"
#include "lib/datetime.h"
#include "lib/filefunc.h"
#include "lib/stringfunc.h"
#include "lib/uifunc.h"
#include "menulib/menuitem.h"
#include "tasks/demoquestionnaire.h"
#include "version/camcopsversion.h"

HelpMenu::HelpMenu(CamcopsApp& app) :
    MenuWindow(app, uifunc::iconFilename(uiconst::ICON_INFO))
{
}

QString HelpMenu::title() const
{
    return tr("Help");
}

void HelpMenu::makeItems()
{
    // You can't point a standard web browser at a Qt resource file.
    // (They are not necessarily actual files on disk.)
    // Creating an HtmlMenuItem that points to Sphinx documentation just looks
    // rubbish.
    // Copying lots of Qt resource files to the filesystem would be possible,
    // but would require care about when to do it (not too often because that's
    // inefficient -- currently 1.9Mb and growing; then you need a change
    // control mechanism). Lots of hassle.
    // The best thing is probably to use the online docs.
    // If the user has registered wih a server, we could point them to their
    // own server, but perhaps a canonical set of docs is simplest. It's
    // certainly better if we need to update something quickly.
    m_items = {
        MenuItem(
            tr("Online CamCOPS documentation"),
            UrlMenuItem(urlconst::CAMCOPS_URL),
            uifunc::iconFilename(uiconst::ICON_INFO)
        ),
        // CAMCOPS_URL and CAMCOPS_DOCS_URL are almost the same these days.
        // MenuItem(tr("Visit") + " " + urlconst::CAMCOPS_URL,
        //          UrlMenuItem(urlconst::CAMCOPS_DOCS_URL),
        //          uifunc::iconFilename(uiconst::ICON_CAMCOPS)),
        MAKE_TASK_MENU_ITEM(
            DemoQuestionnaire::DEMOQUESTIONNAIRE_TABLENAME, m_app
        ),
        MenuItem(
            tr("Show software versions and computer information"),
            std::bind(&HelpMenu::softwareVersions, this)
        ),
        MenuItem(tr("About Qt"), std::bind(&HelpMenu::aboutQt, this)),
        MenuItem(
            tr("View device ID and database details"),
            std::bind(&HelpMenu::showDeviceIdAndDbDetails, this)
        ),
        MenuItem(
            tr("Licence details"), UrlMenuItem(urlconst::CAMCOPS_LICENCES_URL)
        ),
        MenuItem(
            tr("View terms and conditions of use"),
            std::bind(&HelpMenu::viewTermsConditions, this)
        ),
    };
}

void HelpMenu::softwareVersions() const
{
    QStringList versions;
    const QString newline = "";
    const bool host64 = platform::isHost64Bit();
    const bool build64 = platform::isBuild64Bit();

    // ------------------------------------------------------------------------
    // CamCOPS
    // ------------------------------------------------------------------------
    versions.append(tr("<b>CamCOPS client version:</b> %1")
                        .arg(camcopsversion::CAMCOPS_CLIENT_VERSION.toString())
    );
    versions.append(
        tr("CamCOPS client change date: %1")
            .arg(camcopsversion::CAMCOPS_CLIENT_CHANGEDATE.toString(Qt::ISODate
            ))
    );
    versions.append(
        tr("CamCOPS executable is %1-bit").arg(build64 ? "64" : "32")
    );
    versions.append(tr("Compiler: %1").arg(platform::COMPILER_NAME_VERSION));
    versions.append(tr("Compiled at: %1").arg(platform::COMPILED_WHEN));
    versions.append(newline);

    // ------------------------------------------------------------------------
    // Host
    // ------------------------------------------------------------------------
    const Dpi ldpi = m_app.qtLogicalDotsPerInch();
    const Dpi pdpi = m_app.qtPhysicalDotsPerInch();
    versions.append(tr("<b>Current computer (host)</b> is %1-bit")
                        .arg(host64 ? "64" : "32"));
    versions.append(tr("Host operating system: %1").arg(platform::OS_CLASS));
    versions.append(
        tr("Host computer type: %1").arg(QSysInfo::prettyProductName())
    );
    versions.append(
        tr("Host CPU architecture: %1").arg(QSysInfo::currentCpuArchitecture())
    );
    versions.append(tr("Host kernel type: %1").arg(QSysInfo::kernelType()));
    versions.append(
        tr("Host kernel version: %1").arg(QSysInfo::kernelVersion())
    );
    versions.append(tr("Host name: %1").arg(QSysInfo::machineHostName()));
    versions.append(
        tr("Logical dots per inch (DPI): %1").arg(ldpi.description())
    );
    versions.append(
        tr("Physical dots per inch (DPI): %1").arg(pdpi.description())
    );
    versions.append(newline);

    // ------------------------------------------------------------------------
    // Qt
    // ------------------------------------------------------------------------
    versions.append(tr("<b>Qt version:</b> %1").arg(QT_VERSION_STR));
    versions.append(tr("Qt build architecture: %1").arg(QSysInfo::buildAbi()));
    versions.append(newline);

    // ------------------------------------------------------------------------
    // SQLite
    // ------------------------------------------------------------------------
    // http://stackoverflow.com/questions/12685563/how-to-find-out-version-sqlite-in-qt
    // We can't #include <sqlite3.h>; that's the system version.
    // The Qt driver (in qsql_sqlite.cpp) uses SQLITE_VERSION_NUMBER but
    // doesn't expose it. So we have to ask the database itself.
    DatabaseManager& db = m_app.sysdb();
    QString sqlite_version
        = db.fetchFirstValue("SELECT sqlite_version()").toString();
    versions.append(
        tr("<b>Embedded SQLite version:</b> %1").arg(sqlite_version)
    );
    QString sqlite_info;
    QTextStream s(&sqlite_info);
    QSqlDriver* driver = db.driver();
    s << tr("... supported database features (0 no, 1 yes):")
      << " Transactions " << driver->hasFeature(QSqlDriver::Transactions)
      << "; QuerySize " << driver->hasFeature(QSqlDriver::QuerySize)
      << "; BLOB " << driver->hasFeature(QSqlDriver::BLOB) << "; Unicode "
      << driver->hasFeature(QSqlDriver::Unicode) << "; PreparedQueries "
      << driver->hasFeature(QSqlDriver::PreparedQueries)
      << "; NamedPlaceholders "
      << driver->hasFeature(QSqlDriver::NamedPlaceholders)
      << "; PositionalPlaceholders "
      << driver->hasFeature(QSqlDriver::PositionalPlaceholders)
      << "; LastInsertId " << driver->hasFeature(QSqlDriver::LastInsertId)
      << "; BatchOperations "
      << driver->hasFeature(QSqlDriver::BatchOperations) << "; SimpleLocking "
      << driver->hasFeature(QSqlDriver::SimpleLocking)
      << "; LowPrecisionNumbers "
      << driver->hasFeature(QSqlDriver::LowPrecisionNumbers)
      << "; EventNotifications "
      << driver->hasFeature(QSqlDriver::EventNotifications) << "; FinishQuery "
      << driver->hasFeature(QSqlDriver::FinishQuery) << "; MultipleResultSets "
      << driver->hasFeature(QSqlDriver::MultipleResultSets) << "; CancelQuery "
      << driver->hasFeature(QSqlDriver::CancelQuery);
    versions.append(sqlite_info);
#ifdef USE_SQLCIPHER
    QString sqlcipher_version
        = db.fetchFirstValue("PRAGMA cipher_version").toString();
    QString cipher_provider
        = db.fetchFirstValue("PRAGMA cipher_provider").toString();
    QString cipher_provider_version
        = db.fetchFirstValue("PRAGMA cipher_provider_version").toString();
    versions.append(
        tr("<b>SQLCipher version:</b> %1 (cipher provider: "
           "%2, version: %3)")
            .arg(sqlcipher_version, cipher_provider, cipher_provider_version)
    );
#endif
    versions.append(newline);

    // ------------------------------------------------------------------------
    // OpenSSL
    // ------------------------------------------------------------------------
    // http://stackoverflow.com/questions/23320480
    //      SSLEAY_VERSION
    // http://stackoverflow.com/questions/39480724/use-openssl-in-qt-c
    // https://www.openssl.org/docs/manmaster/crypto/OPENSSL_VERSION_NUMBER.html
    //      OPENSSL_VERSION_NUMBER
    //      OpenSSL_version
    //      OpenSSL_version_num
    // ... all available within QtNetwork/private/qssql*.h, but not exposed.
    // However, we have this:
    versions.append(
        tr("<b>Supports SSL:</b> %1").arg(QSslSocket::supportsSsl())
    );
    versions.append(tr("<b>Compile-time OpenSSL version:</b> %1")
                        .arg(QSslSocket::sslLibraryBuildVersionString()));
    versions.append(tr("<b>Run-time OpenSSL version:</b> %1")
                        .arg(QSslSocket::sslLibraryVersionString()));

    uifunc::alert(versions.join("<br>"), tr("Software versions"));
}

void HelpMenu::aboutQt()
{
    QMessageBox::aboutQt(this);
}

void HelpMenu::showDeviceIdAndDbDetails() const
{
    QStringList lines{
        tr("<b>Device ID:</b> %1").arg(m_app.deviceId()),
        tr("<b>Main database:</b> %1")
            .arg(m_app.dbFullPath(dbfunc::DATA_DATABASE_FILENAME)),
        tr("<b>System database:</b> %1")
            .arg(m_app.dbFullPath(dbfunc::SYSTEM_DATABASE_FILENAME)),
    };
    uifunc::alert(
        stringfunc::joinHtmlLines(lines),
        tr("Device/installation ID; databases")
    );
}

void HelpMenu::viewTermsConditions()
{
    QString title = tr("You agreed to these terms and conditions at: %1")
                        .arg(datetime::shortDateTime(m_app.agreedTermsAt()));

    ScrollMessageBox::information(
        this, title, m_app.getCurrentTermsConditions()
    );
}