<commit_before>/* Copyright (c) 2010-2013, AOYAMA Kazuharu
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QDir>
#include <QTextCodec>
#include <QDateTime>
#include <TWebApplication>
#include <TSystemGlobal>
#include <TAppSettings>
#include <cstdlib>

#define DEFAULT_INTERNET_MEDIA_TYPE   "text/plain"
#define DEFAULT_DATABASE_ENVIRONMENT  "product"

static QTextCodec *searchCodec(const char *name)
{
    QTextCodec *c = QTextCodec::codecForName(name);
    return (c) ? c : QTextCodec::codecForLocale();
}

/*!
  \class TWebApplication
  \brief The TWebApplication class provides an event loop for
  TreeFrog applications.
*/

/*!
  Constructor.
*/
TWebApplication::TWebApplication(int &argc, char **argv)
#ifdef TF_USE_GUI_MODULE
    : QApplication(argc, argv),
#else
    : QCoreApplication(argc, argv),
#endif
      dbEnvironment(DEFAULT_DATABASE_ENVIRONMENT),
      sqlSettings(0),
      mongoSetting(0),
      redisSetting(0),
      loggerSetting(0),
      validationSetting(0),
      mediaTypes(0),
      codecInternal(0),
      codecHttp(0),
      appServerId(-1),
      mpm(Invalid)
{
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
    installNativeEventFilter(new TNativeEventFilter);
#endif

    // parse command-line args
    webRootAbsolutePath = ".";
    QStringList args = arguments();
    args.removeFirst();
    for (QStringListIterator i(args); i.hasNext(); ) {
        const QString &arg = i.next();
        if (arg.startsWith('-')) {
            if (arg == "-e" && i.hasNext()) {
                dbEnvironment = i.next();
            }
            if (arg == "-i" && i.hasNext()) {
                appServerId = i.next().toInt();
            }
        } else {
            if (QDir(arg).exists()) {
                webRootAbsolutePath = arg;
                if (!webRootAbsolutePath.endsWith(QDir::separator()))
                    webRootAbsolutePath += QDir::separator();
            }
        }
    }

    QDir webRoot(webRootAbsolutePath);
    if (webRoot.exists()) {
        webRootAbsolutePath = webRoot.absolutePath() + QDir::separator();
    }

    // Sets application name
    QString appName = QDir(webRootAbsolutePath).dirName();
    if (!appName.isEmpty()) {
        setApplicationName(appName);
    }

    // Creates settings objects
    TAppSettings::instantiate(appSettingsFilePath());
    loggerSetting = new QSettings(configPath() + "logger.ini", QSettings::IniFormat, this);
    validationSetting = new QSettings(configPath() + "validation.ini", QSettings::IniFormat, this);
    mediaTypes = new QSettings(configPath() + "initializers" + QDir::separator() + "internet_media_types.ini", QSettings::IniFormat, this);

    // Gets codecs
    codecInternal = searchCodec(Tf::appSettings()->value(Tf::InternalEncoding).toByteArray().trimmed().data());
    codecHttp = searchCodec(Tf::appSettings()->value(Tf::HttpOutputEncoding).toByteArray().trimmed().data());

    // Sets codecs for INI files
    loggerSetting->setIniCodec(codecInternal);
    validationSetting->setIniCodec(codecInternal);
    mediaTypes->setIniCodec(codecInternal);

    // SQL DB settings
    QString dbsets = Tf::appSettings()->value(Tf::SqlDatabaseSettingsFiles).toString().trimmed();
    if (dbsets.isEmpty()) {
        dbsets = Tf::appSettings()->readValue("DatabaseSettingsFiles").toString().trimmed();
    }
    QStringList files = dbsets.split(QLatin1Char(' '), QString::SkipEmptyParts);
    for (QListIterator<QString> it(files); it.hasNext(); ) {
        const QString &f = it.next();
        QSettings *set = new QSettings(configPath() + f, QSettings::IniFormat, this);
        set->setIniCodec(codecInternal);
        sqlSettings.append(set);
    }

    // MongoDB settings
    QString mongoini = Tf::appSettings()->value(Tf::MongoDbSettingsFile).toString().trimmed();
    if (!mongoini.isEmpty()) {
        QString mnginipath = configPath() + mongoini;
        if (QFile(mnginipath).exists())
            mongoSetting = new QSettings(mnginipath, QSettings::IniFormat, this);
    }

    // Redis settings
    QString redisini = Tf::appSettings()->value(Tf::RedisSettingsFile).toString().trimmed();
    if (!redisini.isEmpty()) {
        QString redisinipath = configPath() + redisini;
        if (QFile(redisinipath).exists())
            redisSetting = new QSettings(redisinipath, QSettings::IniFormat, this);
    }
}

TWebApplication::~TWebApplication()
{ }

/*!
  Enters the main event loop and waits until exit() is called. Returns the
  value that was set to exit() (which is 0 if exit() is called via quit()).
*/
int TWebApplication::exec()
{
    resetSignalNumber();

#ifdef TF_USE_GUI_MODULE
    int ret = QApplication::exec();
#else
    int ret = QCoreApplication::exec();
#endif

    QEventLoop eventLoop;
    while (eventLoop.processEvents()) { }
    return ret;
}

/*!
  Returns true if the web root directory exists; otherwise returns false.
*/
bool TWebApplication::webRootExists() const
{
    return !webRootAbsolutePath.isEmpty() && QDir(webRootAbsolutePath).exists();
}

/*!
  Returns the absolute path of the public directory.
*/
QString TWebApplication::publicPath() const
{
    return webRootPath() + "public" + QDir::separator();
}

/*!
  Returns the absolute path of the config directory.
*/
QString TWebApplication::configPath() const
{
    return webRootPath() + "config" + QDir::separator();
}

/*!
  Returns the absolute path of the library directory.
*/
QString TWebApplication::libPath() const
{
    return webRootPath()+ "lib" + QDir::separator();
}

/*!
  Returns the absolute path of the log directory.
*/
QString TWebApplication::logPath() const
{
    return webRootPath() + "log" + QDir::separator();
}

/*!
  Returns the absolute path of the plugin directory.
*/
QString TWebApplication::pluginPath() const
{
    return webRootPath() + "plugin" + QDir::separator();
}

/*!
  Returns the absolute path of the tmp directory.
*/
QString TWebApplication::tmpPath() const
{
    return webRootPath() + "tmp" + QDir::separator();
}

/*!
  Returns true if the file of the application settings exists;
  otherwise returns false.
*/
bool TWebApplication::appSettingsFileExists() const
{
    return !Tf::appSettings()->appIniSettings->allKeys().isEmpty();
}

/*!
  Returns the absolute file path of the application settings.
*/
QString TWebApplication::appSettingsFilePath() const
{
    return configPath() + "application.ini";
}

/*!
  Returns a reference to the QSettings object for settings of the
  SQL database \a databaseId.
*/
QSettings &TWebApplication::sqlDatabaseSettings(int databaseId) const
{
    return *sqlSettings[databaseId];
}

/*!
  Returns the number of SQL database settings files set by the setting
  \a DatabaseSettingsFiles in the application.ini.
*/
int TWebApplication::sqlDatabaseSettingsCount() const
{
    return sqlSettings.count();
}

/*!
  Returns true if SQL database is available; otherwise returns false.
*/
bool TWebApplication::isSqlDatabaseAvailable() const
{
    return sqlSettings.count() > 0;
}

/*!
  Returns a reference to the QSettings object for settings of the
  MongoDB system.
*/
QSettings &TWebApplication::mongoDbSettings() const
{
    return *mongoSetting;
}

/*!
  Returns true if MongoDB settings is available; otherwise returns false.
*/
bool TWebApplication::isMongoDbAvailable() const
{
    return (bool)mongoSetting;
}

/*!
  Returns a reference to the QSettings object for settings of the
  Redis system.
*/
QSettings &TWebApplication::redisSettings() const
{
    return *redisSetting;
}

/*!
  Returns true if Redis settings is available; otherwise returns false.
*/
bool TWebApplication::isRedisAvailable() const
{
    return (bool)redisSetting;
}

/*!
  Returns the internet media type associated with the file extension
  \a ext.
*/
QByteArray TWebApplication::internetMediaType(const QString &ext, bool appendCharset)
{
    if (ext.isEmpty())
        return QByteArray();

    QString type = mediaTypes->value(ext, DEFAULT_INTERNET_MEDIA_TYPE).toString();
    if (appendCharset && type.startsWith("text", Qt::CaseInsensitive)) {
        type += "; charset=" + Tf::app()->codecForHttpOutput()->name();
    }
    return type.toLatin1();
}

/*!
  Returns the error message for validation of the given \a rule. These messages
  are defined in the validation.ini.
*/
QString TWebApplication::validationErrorMessage(int rule) const
{
    validationSetting->beginGroup("ErrorMessage");
    QString msg = validationSetting->value(QString::number(rule)).toString();
    validationSetting->endGroup();
    return msg;
}

/*!
  Returns the module name for multi-processing that is set by the setting
  \a MultiProcessingModule in the application.ini.
*/
TWebApplication::MultiProcessingModule TWebApplication::multiProcessingModule() const
{
    if (mpm == Invalid) {
        QString str = Tf::appSettings()->value(Tf::MultiProcessingModule).toString().toLower();
        if (str == "thread") {
            mpm = Thread;
//        } else if (str == "prefork") {
//            mpm = Prefork;
        } else if (str == "hybrid") {
            mpm = Hybrid;
        } else {
            tError("Unsupported MPM: %s", qPrintable(str));
        }
    }
    return mpm;
}

/*!
  Returns the maximum number of application servers, which is set in the
  application.ini.
*/
int TWebApplication::maxNumberOfAppServers(int defaultValue) const
{
    QString mpmstr = Tf::appSettings()->value(Tf::MultiProcessingModule).toString().toLower();
    int num = Tf::appSettings()->readValue(QLatin1String("MPM.") + mpmstr + ".MaxAppServers").toInt();

    if (num > 0) {
        return num;
    }

    // For compatibility
    int mpm = multiProcessingModule();
    switch (mpm) {
    case Thread:
        num = 1;
        break;

    case Hybrid:
        num = Tf::appSettings()->readValue(QLatin1String("MPM.") + mpmstr + ".MaxServers").toInt();
        break;

    default:
        break;
    }

    return (num > 0) ? num : defaultValue;
}

/*!
  Returns the absolute file path of the routes config.
*/
QString TWebApplication::routesConfigFilePath() const
{
    return configPath() + "routes.cfg";
}

/*!
  Returns the absolute file path of the system log, which is set by the
  setting \a SystemLog.FilePath in the application.ini.
*/
QString TWebApplication::systemLogFilePath() const
{
    QFileInfo fi(Tf::appSettings()->value(Tf::SystemLogFilePath, "log/treefrog.log").toString());
    return (fi.isAbsolute()) ? fi.absoluteFilePath() : webRootPath() + fi.filePath();
}

/*!
  Returns the absolute file path of the access log, which is set by the
  setting \a AccessLog.FilePath in the application.ini.
*/
QString TWebApplication::accessLogFilePath() const
{
    QString name = Tf::appSettings()->value(Tf::AccessLogFilePath).toString().trimmed();
    if (name.isEmpty())
        return name;

    QFileInfo fi(name);
    return (fi.isAbsolute()) ? fi.absoluteFilePath() : webRootPath() + fi.filePath();
}

/*!
  Returns the absolute file path of the SQL query log, which is set by the
  setting \a SqlQueryLogFile in the application.ini.
*/
QString TWebApplication::sqlQueryLogFilePath() const
{
    QString path = Tf::appSettings()->value(Tf::SqlQueryLogFile).toString();
    if (!path.isEmpty()) {
        QFileInfo fi(path);
        path = (fi.isAbsolute()) ? fi.absoluteFilePath() : webRootPath() + fi.filePath();
    }
    return path;
}

void TWebApplication::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == timer.timerId()) {
        if (signalNumber() >= 0) {
            tSystemDebug("TWebApplication trapped signal  number:%d", signalNumber());
            //timer.stop();   /* Don't stop this timer */
            exit(signalNumber());
        }
    } else {
#ifdef TF_USE_GUI_MODULE
        QApplication::timerEvent(event);
#else
        QCoreApplication::timerEvent(event);
#endif
    }
}

/*!
  \fn QString TWebApplication::webRootPath() const
  Returns the absolute path of the web root directory.
*/

/*!
  \fn QSettings &TWebApplication::appSettings() const
  Returns a reference to the QSettings object for settings of the
  web application, which file is the application.ini.
*/

/*!
  \fn QSettings &TWebApplication::loggerSettings () const
  Returns a reference to the QSettings object for settings of the
  logger, which file is logger.ini.
*/

/*!
  \fn QSettings &TWebApplication::validationSettings () const
  Returns a reference to the QSettings object for settings of the
  validation, which file is validation.ini.
*/

/*!
  \fn QTextCodec *TWebApplication::codecForInternal() const
  Returns a pointer to the codec used internally, which is set by the
  setting \a InternalEncoding in the application.ini. This codec is used
  by QObject::tr() and toLocal8Bit() functions.
*/

/*!
  \fn QTextCodec *TWebApplication::codecForHttpOutput() const
  Returns a pointer to the codec of the HTTP output stream used by an
  action view, which is set by the setting \a HttpOutputEncoding in
  the application.ini.
*/

/*!
  \fn QString TWebApplication::databaseEnvironment() const
  Returns the database environment, which string is used to load
  the settings in database.ini.
  \sa setDatabaseEnvironment(const QString &environment)
*/

/*!
  \fn void TWebApplication::watchConsoleSignal();
  Starts watching console signals i.e.\ registers a routine to handle the
  console signals.
*/

/*!
  \fn void TWebApplication::ignoreConsoleSignal();
  Ignores console signals, i.e.\ delivery of console signals will have no effect
  on the process.
*/

/*!
  \fn void TWebApplication::watchUnixSignal(int sig, bool watch);
  Starts watching the UNIX signal, i.e.\ registers a routine to handle the
  signal \a sig.
  \sa ignoreUnixSignal()
*/

/*!
  \fn void TWebApplication::ignoreUnixSignal(int sig, bool ignore)
  Ignores UNIX signals, i.e.\ delivery of the signal will have no effect on
  the process.
  \sa watchUnixSignal()
*/

/*!
  \fn void TWebApplication::timerEvent(QTimerEvent *)
  Reimplemented from QObject::timerEvent().
*/

/*!
  \fn int TWebApplication::signalNumber()
  Returns the integral number of the received signal.
*/
<commit_msg>fix a bug of segmentation fault on OS X.<commit_after>/* Copyright (c) 2010-2013, AOYAMA Kazuharu
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QDir>
#include <QTextCodec>
#include <QDateTime>
#include <TWebApplication>
#include <TSystemGlobal>
#include <TAppSettings>
#include <cstdlib>

#define DEFAULT_INTERNET_MEDIA_TYPE   "text/plain"
#define DEFAULT_DATABASE_ENVIRONMENT  "product"

static QTextCodec *searchCodec(const char *name)
{
    QTextCodec *c = QTextCodec::codecForName(name);
    return (c) ? c : QTextCodec::codecForLocale();
}

/*!
  \class TWebApplication
  \brief The TWebApplication class provides an event loop for
  TreeFrog applications.
*/

/*!
  Constructor.
*/
TWebApplication::TWebApplication(int &argc, char **argv)
#ifdef TF_USE_GUI_MODULE
    : QApplication(argc, argv),
#else
    : QCoreApplication(argc, argv),
#endif
      dbEnvironment(DEFAULT_DATABASE_ENVIRONMENT),
      sqlSettings(0),
      mongoSetting(0),
      redisSetting(0),
      loggerSetting(0),
      validationSetting(0),
      mediaTypes(0),
      codecInternal(0),
      codecHttp(0),
      appServerId(-1),
      mpm(Invalid)
{
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
    installNativeEventFilter(new TNativeEventFilter);
#endif

    // parse command-line args
    webRootAbsolutePath = ".";
    QStringList args = arguments();
    args.removeFirst();
    for (QStringListIterator i(args); i.hasNext(); ) {
        const QString &arg = i.next();
        if (arg.startsWith('-')) {
            if (arg == "-e" && i.hasNext()) {
                dbEnvironment = i.next();
            }
            if (arg == "-i" && i.hasNext()) {
                appServerId = i.next().toInt();
            }
        } else {
            if (QDir(arg).exists()) {
                webRootAbsolutePath = arg;
                if (!webRootAbsolutePath.endsWith(QDir::separator()))
                    webRootAbsolutePath += QDir::separator();
            }
        }
    }

    QDir webRoot(webRootAbsolutePath);
    if (webRoot.exists()) {
        webRootAbsolutePath = webRoot.absolutePath() + QDir::separator();
    }

    // Sets application name
    QString appName = QDir(webRootAbsolutePath).dirName();
    if (!appName.isEmpty()) {
        setApplicationName(appName);
    }

    // Creates settings objects
    TAppSettings::instantiate(appSettingsFilePath());
    loggerSetting = new QSettings(configPath() + "logger.ini", QSettings::IniFormat, this);
    validationSetting = new QSettings(configPath() + "validation.ini", QSettings::IniFormat, this);
    mediaTypes = new QSettings(configPath() + "initializers" + QDir::separator() + "internet_media_types.ini", QSettings::IniFormat, this);

    // Gets codecs
    codecInternal = searchCodec(Tf::appSettings()->value(Tf::InternalEncoding).toByteArray().trimmed().data());
    codecHttp = searchCodec(Tf::appSettings()->value(Tf::HttpOutputEncoding).toByteArray().trimmed().data());

    // Sets codecs for INI files
    loggerSetting->setIniCodec(codecInternal);
    validationSetting->setIniCodec(codecInternal);
    mediaTypes->setIniCodec(codecInternal);

    // SQL DB settings
    QString dbsets = Tf::appSettings()->value(Tf::SqlDatabaseSettingsFiles).toString().trimmed();
    if (dbsets.isEmpty()) {
        dbsets = Tf::appSettings()->readValue("DatabaseSettingsFiles").toString().trimmed();
    }
    QStringList files = dbsets.split(QLatin1Char(' '), QString::SkipEmptyParts);
    for (QListIterator<QString> it(files); it.hasNext(); ) {
        const QString &f = it.next();
        QSettings *set = new QSettings(configPath() + f, QSettings::IniFormat, this);
        set->setIniCodec(codecInternal);
        sqlSettings.append(set);
    }

    // MongoDB settings
    QString mongoini = Tf::appSettings()->value(Tf::MongoDbSettingsFile).toString().trimmed();
    if (!mongoini.isEmpty()) {
        QString mnginipath = configPath() + mongoini;
        if (QFile(mnginipath).exists())
            mongoSetting = new QSettings(mnginipath, QSettings::IniFormat, this);
    }

    // Redis settings
    QString redisini = Tf::appSettings()->value(Tf::RedisSettingsFile).toString().trimmed();
    if (!redisini.isEmpty()) {
        QString redisinipath = configPath() + redisini;
        if (QFile(redisinipath).exists())
            redisSetting = new QSettings(redisinipath, QSettings::IniFormat, this);
    }
}

TWebApplication::~TWebApplication()
{ }

/*!
  Enters the main event loop and waits until exit() is called. Returns the
  value that was set to exit() (which is 0 if exit() is called via quit()).
*/
int TWebApplication::exec()
{
    resetSignalNumber();

#ifdef TF_USE_GUI_MODULE
    int ret = QApplication::exec();
#else
    int ret = QCoreApplication::exec();
#endif

    QEventLoop eventLoop;
    while (eventLoop.processEvents()) { }
    return ret;
}

/*!
  Returns true if the web root directory exists; otherwise returns false.
*/
bool TWebApplication::webRootExists() const
{
    return !webRootAbsolutePath.isEmpty() && QDir(webRootAbsolutePath).exists();
}

/*!
  Returns the absolute path of the public directory.
*/
QString TWebApplication::publicPath() const
{
    return webRootPath() + "public" + QDir::separator();
}

/*!
  Returns the absolute path of the config directory.
*/
QString TWebApplication::configPath() const
{
    return webRootPath() + "config" + QDir::separator();
}

/*!
  Returns the absolute path of the library directory.
*/
QString TWebApplication::libPath() const
{
    return webRootPath()+ "lib" + QDir::separator();
}

/*!
  Returns the absolute path of the log directory.
*/
QString TWebApplication::logPath() const
{
    return webRootPath() + "log" + QDir::separator();
}

/*!
  Returns the absolute path of the plugin directory.
*/
QString TWebApplication::pluginPath() const
{
    return webRootPath() + "plugin" + QDir::separator();
}

/*!
  Returns the absolute path of the tmp directory.
*/
QString TWebApplication::tmpPath() const
{
    return webRootPath() + "tmp" + QDir::separator();
}

/*!
  Returns true if the file of the application settings exists;
  otherwise returns false.
*/
bool TWebApplication::appSettingsFileExists() const
{
    return !Tf::appSettings()->appIniSettings->allKeys().isEmpty();
}

/*!
  Returns the absolute file path of the application settings.
*/
QString TWebApplication::appSettingsFilePath() const
{
    return configPath() + "application.ini";
}

/*!
  Returns a reference to the QSettings object for settings of the
  SQL database \a databaseId.
*/
QSettings &TWebApplication::sqlDatabaseSettings(int databaseId) const
{
    return *sqlSettings[databaseId];
}

/*!
  Returns the number of SQL database settings files set by the setting
  \a DatabaseSettingsFiles in the application.ini.
*/
int TWebApplication::sqlDatabaseSettingsCount() const
{
    return sqlSettings.count();
}

/*!
  Returns true if SQL database is available; otherwise returns false.
*/
bool TWebApplication::isSqlDatabaseAvailable() const
{
    return sqlSettings.count() > 0;
}

/*!
  Returns a reference to the QSettings object for settings of the
  MongoDB system.
*/
QSettings &TWebApplication::mongoDbSettings() const
{
    return *mongoSetting;
}

/*!
  Returns true if MongoDB settings is available; otherwise returns false.
*/
bool TWebApplication::isMongoDbAvailable() const
{
    return (bool)mongoSetting;
}

/*!
  Returns a reference to the QSettings object for settings of the
  Redis system.
*/
QSettings &TWebApplication::redisSettings() const
{
    return *redisSetting;
}

/*!
  Returns true if Redis settings is available; otherwise returns false.
*/
bool TWebApplication::isRedisAvailable() const
{
    return (bool)redisSetting;
}

/*!
  Returns the internet media type associated with the file extension
  \a ext.
*/
QByteArray TWebApplication::internetMediaType(const QString &ext, bool appendCharset)
{
    if (ext.isEmpty())
        return QByteArray();

    QString type = mediaTypes->value(ext, DEFAULT_INTERNET_MEDIA_TYPE).toString();
    if (appendCharset && type.startsWith("text", Qt::CaseInsensitive)) {
        type += "; charset=" + Tf::app()->codecForHttpOutput()->name();
    }
    return type.toLatin1();
}

/*!
  Returns the error message for validation of the given \a rule. These messages
  are defined in the validation.ini.
*/
QString TWebApplication::validationErrorMessage(int rule) const
{
    validationSetting->beginGroup("ErrorMessage");
    QString msg = validationSetting->value(QString::number(rule)).toString();
    validationSetting->endGroup();
    return msg;
}

/*!
  Returns the module name for multi-processing that is set by the setting
  \a MultiProcessingModule in the application.ini.
*/
TWebApplication::MultiProcessingModule TWebApplication::multiProcessingModule() const
{
    if (mpm == Invalid) {
        QString str = Tf::appSettings()->value(Tf::MultiProcessingModule).toString().toLower();
        if (str == "thread") {
            mpm = Thread;
        } else if (str == "hybrid") {
#ifdef Q_OS_LINUX
            mpm = Hybrid;
#else
            tSystemWarn("Unsupported MPM: hybrid  (Linux only)");
            tWarn("Unsupported MPM: hybrid  (Linux only)");
            mpm = Thread;
#endif
        } else {
            tSystemWarn("Unsupported MPM: %s", qPrintable(str));
            tWarn("Unsupported MPM: %s", qPrintable(str));
            mpm = Thread;
        }
    }
    return mpm;
}

/*!
  Returns the maximum number of application servers, which is set in the
  application.ini.
*/
int TWebApplication::maxNumberOfAppServers(int defaultValue) const
{
    QString mpmstr = Tf::appSettings()->value(Tf::MultiProcessingModule).toString().toLower();
    int num = Tf::appSettings()->readValue(QLatin1String("MPM.") + mpmstr + ".MaxAppServers").toInt();

    if (num > 0) {
        return num;
    }

    // For compatibility
    int mpm = multiProcessingModule();
    switch (mpm) {
    case Thread:
        num = 1;
        break;

    case Hybrid:
        num = Tf::appSettings()->readValue(QLatin1String("MPM.") + mpmstr + ".MaxServers").toInt();
        break;

    default:
        break;
    }

    return (num > 0) ? num : defaultValue;
}

/*!
  Returns the absolute file path of the routes config.
*/
QString TWebApplication::routesConfigFilePath() const
{
    return configPath() + "routes.cfg";
}

/*!
  Returns the absolute file path of the system log, which is set by the
  setting \a SystemLog.FilePath in the application.ini.
*/
QString TWebApplication::systemLogFilePath() const
{
    QFileInfo fi(Tf::appSettings()->value(Tf::SystemLogFilePath, "log/treefrog.log").toString());
    return (fi.isAbsolute()) ? fi.absoluteFilePath() : webRootPath() + fi.filePath();
}

/*!
  Returns the absolute file path of the access log, which is set by the
  setting \a AccessLog.FilePath in the application.ini.
*/
QString TWebApplication::accessLogFilePath() const
{
    QString name = Tf::appSettings()->value(Tf::AccessLogFilePath).toString().trimmed();
    if (name.isEmpty())
        return name;

    QFileInfo fi(name);
    return (fi.isAbsolute()) ? fi.absoluteFilePath() : webRootPath() + fi.filePath();
}

/*!
  Returns the absolute file path of the SQL query log, which is set by the
  setting \a SqlQueryLogFile in the application.ini.
*/
QString TWebApplication::sqlQueryLogFilePath() const
{
    QString path = Tf::appSettings()->value(Tf::SqlQueryLogFile).toString();
    if (!path.isEmpty()) {
        QFileInfo fi(path);
        path = (fi.isAbsolute()) ? fi.absoluteFilePath() : webRootPath() + fi.filePath();
    }
    return path;
}

void TWebApplication::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == timer.timerId()) {
        if (signalNumber() >= 0) {
            tSystemDebug("TWebApplication trapped signal  number:%d", signalNumber());
            //timer.stop();   /* Don't stop this timer */
            exit(signalNumber());
        }
    } else {
#ifdef TF_USE_GUI_MODULE
        QApplication::timerEvent(event);
#else
        QCoreApplication::timerEvent(event);
#endif
    }
}

/*!
  \fn QString TWebApplication::webRootPath() const
  Returns the absolute path of the web root directory.
*/

/*!
  \fn QSettings &TWebApplication::appSettings() const
  Returns a reference to the QSettings object for settings of the
  web application, which file is the application.ini.
*/

/*!
  \fn QSettings &TWebApplication::loggerSettings () const
  Returns a reference to the QSettings object for settings of the
  logger, which file is logger.ini.
*/

/*!
  \fn QSettings &TWebApplication::validationSettings () const
  Returns a reference to the QSettings object for settings of the
  validation, which file is validation.ini.
*/

/*!
  \fn QTextCodec *TWebApplication::codecForInternal() const
  Returns a pointer to the codec used internally, which is set by the
  setting \a InternalEncoding in the application.ini. This codec is used
  by QObject::tr() and toLocal8Bit() functions.
*/

/*!
  \fn QTextCodec *TWebApplication::codecForHttpOutput() const
  Returns a pointer to the codec of the HTTP output stream used by an
  action view, which is set by the setting \a HttpOutputEncoding in
  the application.ini.
*/

/*!
  \fn QString TWebApplication::databaseEnvironment() const
  Returns the database environment, which string is used to load
  the settings in database.ini.
  \sa setDatabaseEnvironment(const QString &environment)
*/

/*!
  \fn void TWebApplication::watchConsoleSignal();
  Starts watching console signals i.e.\ registers a routine to handle the
  console signals.
*/

/*!
  \fn void TWebApplication::ignoreConsoleSignal();
  Ignores console signals, i.e.\ delivery of console signals will have no effect
  on the process.
*/

/*!
  \fn void TWebApplication::watchUnixSignal(int sig, bool watch);
  Starts watching the UNIX signal, i.e.\ registers a routine to handle the
  signal \a sig.
  \sa ignoreUnixSignal()
*/

/*!
  \fn void TWebApplication::ignoreUnixSignal(int sig, bool ignore)
  Ignores UNIX signals, i.e.\ delivery of the signal will have no effect on
  the process.
  \sa watchUnixSignal()
*/

/*!
  \fn void TWebApplication::timerEvent(QTimerEvent *)
  Reimplemented from QObject::timerEvent().
*/

/*!
  \fn int TWebApplication::signalNumber()
  Returns the integral number of the received signal.
*/
<|endoftext|>


<commit_before>#include <bitcoin/utility/threads.hpp>

namespace libbitcoin {

void run_service(service_ptr service)
{
    service->run();
}

thread_core::thread_core()
{
    service_ = std::make_shared<io_service>();
    // use std::ref here
    work_ = std::make_shared<io_service::work>(std::ref(*service_));
    runner_ = std::thread(run_service, service_);
}

thread_core::~thread_core()
{
    service_->stop();
    runner_.join();
}

service_ptr thread_core::service()
{
    return service_;
}

strand_ptr thread_core::create_strand()
{
    return std::make_shared<io_service::strand>(*service_);
}

threaded_service::threaded_service()
  : strand_(core_.create_strand())
{
}

service_ptr threaded_service::service()
{
    return core_.service();
}

strand_ptr threaded_service::strand()
{
    return strand_;
}

} // libbitcoin

<commit_msg>concurrency hint for boost io_servie (1)<commit_after>#include <bitcoin/utility/threads.hpp>

namespace libbitcoin {

void run_service(service_ptr service)
{
    service->run();
}

thread_core::thread_core()
{
    service_ = std::make_shared<io_service>(1);
    // use std::ref here
    work_ = std::make_shared<io_service::work>(std::ref(*service_));
    runner_ = std::thread(run_service, service_);
}

thread_core::~thread_core()
{
    service_->stop();
    runner_.join();
}

service_ptr thread_core::service()
{
    return service_;
}

strand_ptr thread_core::create_strand()
{
    return std::make_shared<io_service::strand>(*service_);
}

threaded_service::threaded_service()
  : strand_(core_.create_strand())
{
}

service_ptr threaded_service::service()
{
    return core_.service();
}

strand_ptr threaded_service::strand()
{
    return strand_;
}

} // libbitcoin

<|endoftext|>


<commit_before>// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <fstream>
#include <vector>

// Include GLEW
#include <GL/glew.h>

// Include GLFW
#include <glfw3.h>
GLFWwindow* window;

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

void error_callback(int error, const char* description) {
  fputs(description, stderr);
}

GLuint LoadShaders(const char* vertex_file_path,
                   const char* fragment_file_path) {
  // Create the shaders
  GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
  GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

  // Read the Vertex Shader code from the file
  std::string VertexShaderCode;
  std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
  if (VertexShaderStream.is_open()) {
    std::string Line = "";
    while (getline(VertexShaderStream, Line)) VertexShaderCode += "\n" + Line;
    VertexShaderStream.close();
  }

  // Read the Fragment Shader code from the file
  std::string FragmentShaderCode;
  std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
  if (FragmentShaderStream.is_open()) {
    std::string Line = "";
    while (getline(FragmentShaderStream, Line))
      FragmentShaderCode += "\n" + Line;
    FragmentShaderStream.close();
  }

  GLint Result = GL_FALSE;
  int InfoLogLength;

  // Compile Vertex Shader
  printf("Compiling shader : %s\n", vertex_file_path);
  char const* VertexSourcePointer = VertexShaderCode.c_str();
  glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL);
  glCompileShader(VertexShaderID);

  // Check Vertex Shader
  glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
  glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  std::vector<char> VertexShaderErrorMessage(InfoLogLength);
  glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL,
                     &VertexShaderErrorMessage[0]);
  fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);

  // Compile Fragment Shader
  printf("Compiling shader : %s\n", fragment_file_path);
  char const* FragmentSourcePointer = FragmentShaderCode.c_str();
  glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL);
  glCompileShader(FragmentShaderID);

  // Check Fragment Shader
  glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
  glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
  glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL,
                     &FragmentShaderErrorMessage[0]);
  fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);

  // Link the program
  fprintf(stdout, "Linking program\n");
  GLuint ProgramID = glCreateProgram();
  glAttachShader(ProgramID, VertexShaderID);
  glAttachShader(ProgramID, FragmentShaderID);
  glLinkProgram(ProgramID);

  // Check the program
  glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
  glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  std::vector<char> ProgramErrorMessage(max(InfoLogLength, int(1)));
  glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
  fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);

  glDeleteShader(VertexShaderID);
  glDeleteShader(FragmentShaderID);

  return ProgramID;
}

int main(void) {
  glfwSetErrorCallback(error_callback);

  // Initialise GLFW
  if (!glfwInit()) {
    fprintf(stderr, "Failed to initialize GLFW\n");
    return -1;
  }

  glfwWindowHint(GLFW_SAMPLES, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  // Open a window and create its OpenGL context
  window = glfwCreateWindow(640, 480, "Tutorial 01. Or is it?", NULL, NULL);
  if (window == NULL) {
    fprintf(stderr,
            "Failed to open GLFW window. If you have an Intel GPU, they are "
            "not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
    glfwTerminate();
    return -1;
  }
  glfwMakeContextCurrent(window);

  glewExperimental = GL_TRUE;
  // Initialize GLEW
  if (glewInit() != GLEW_OK) {
    fprintf(stderr, "Failed to initialize GLEW\n");
    return -1;
  }

  // get version info
  const GLubyte* renderer = glGetString (GL_RENDERER); // get renderer string
  const GLubyte* version = glGetString (GL_VERSION); // version as a string
  printf ("Renderer: %s\n", renderer);
  printf ("OpenGL version supported %s\n", version);

  // tell GL to only draw onto a pixel if the shape is closer to the viewer
  glEnable (GL_DEPTH_TEST); // enable depth-testing
  glDepthFunc (GL_LESS); // depth-testing interprets a smaller value as "closer"

  // Ensure we can capture the escape key being pressed below
  glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

  // Dark blue background
  glClearColor(.0f, .0f, 0.4f, 0.0f);

  GLuint VertexArrayID = 0;
  glGenVertexArrays (1, &VertexArrayID);
  glBindVertexArray (VertexArrayID);

  // Create and compile our GLSL program from the shaders
  GLuint programID = LoadShaders("SimpleVertexShader.vertexshader",
                                 "SimpleFragmentShader.fragmentshader");

  GLuint MatrixID = glGetUniformLocation(programID, "MVP");

  // Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
  glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
  // Camera matrix
  glm::mat4 View       = glm::lookAt(
      glm::vec3(4,3,3), // Camera is at (4,3,3), in World Space
      glm::vec3(0,0,0), // and looks at the origin
      glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
  );
  // Model matrix : an identity matrix (model will be at the origin)
  glm::mat4 Model      = glm::mat4(1.0f);  // Changes for each model !
  // Our ModelViewProjection : multiplication of our 3 matrices
  glm::mat4 MVP        = Projection * View * Model; // Remember, matrix multiplication is the other way around

  // An array of 3 vectors which represents 3 vertices
  static const GLfloat g_vertex_buffer_data[] = {
      -0.5f, -0.5f,  0.0f,
       0.5f, -0.5f,  0.0f,
       0.0f,  0.5f,  0.0f,

      -0.5f, -0.5f,  0.0f,
       0.5f, -0.5f,  0.0f,
       0.0f, -0.5f,  0.0f,
  };

  GLuint vertexbuffer;
  glGenBuffers(1, &vertexbuffer);
  glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
  glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data),
               g_vertex_buffer_data, GL_STATIC_DRAW);

  do {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(programID);
    // Send our transformation to the currently bound shader,
    // in the "MVP" uniform
    // For each model you render, since the MVP will be different (at least the M part)
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);

    glEnableVertexAttribArray (0);
    glBindBuffer (GL_ARRAY_BUFFER, vertexbuffer);
    glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, NULL);

    glBindVertexArray(VertexArrayID);

    // Draw the triangle !
    glDrawArrays(GL_TRIANGLES, 0,
                 3);  // Starting from vertex 0; 3 vertices total -> 1 triangle

    glDisableVertexAttribArray(0);

    // Swap buffers
    glfwSwapBuffers(window);
    glfwPollEvents();

  }  // Check if the ESC key was pressed or the window was closed
  while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
         glfwWindowShouldClose(window) == 0);

  // Cleanup VBO and shader
  glDeleteBuffers(1, &vertexbuffer);
  glDeleteProgram(programID);
  glDeleteVertexArrays(1, &VertexArrayID);

  // Close OpenGL window and terminate GLFW
  glfwTerminate();

  return 0;
}

<commit_msg>Square<commit_after>// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <fstream>
#include <vector>

// Include GLEW
#include <GL/glew.h>

// Include GLFW
#include <glfw3.h>
GLFWwindow* window;

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

void error_callback(int error, const char* description) {
  fputs(description, stderr);
}

GLuint LoadShaders(const char* vertex_file_path,
                   const char* fragment_file_path) {
  // Create the shaders
  GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
  GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

  // Read the Vertex Shader code from the file
  std::string VertexShaderCode;
  std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
  if (VertexShaderStream.is_open()) {
    std::string Line = "";
    while (getline(VertexShaderStream, Line)) VertexShaderCode += "\n" + Line;
    VertexShaderStream.close();
  }

  // Read the Fragment Shader code from the file
  std::string FragmentShaderCode;
  std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
  if (FragmentShaderStream.is_open()) {
    std::string Line = "";
    while (getline(FragmentShaderStream, Line))
      FragmentShaderCode += "\n" + Line;
    FragmentShaderStream.close();
  }

  GLint Result = GL_FALSE;
  int InfoLogLength;

  // Compile Vertex Shader
  printf("Compiling shader : %s\n", vertex_file_path);
  char const* VertexSourcePointer = VertexShaderCode.c_str();
  glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL);
  glCompileShader(VertexShaderID);

  // Check Vertex Shader
  glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
  glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  std::vector<char> VertexShaderErrorMessage(InfoLogLength);
  glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL,
                     &VertexShaderErrorMessage[0]);
  fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);

  // Compile Fragment Shader
  printf("Compiling shader : %s\n", fragment_file_path);
  char const* FragmentSourcePointer = FragmentShaderCode.c_str();
  glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL);
  glCompileShader(FragmentShaderID);

  // Check Fragment Shader
  glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
  glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
  glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL,
                     &FragmentShaderErrorMessage[0]);
  fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);

  // Link the program
  fprintf(stdout, "Linking program\n");
  GLuint ProgramID = glCreateProgram();
  glAttachShader(ProgramID, VertexShaderID);
  glAttachShader(ProgramID, FragmentShaderID);
  glLinkProgram(ProgramID);

  // Check the program
  glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
  glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  std::vector<char> ProgramErrorMessage(max(InfoLogLength, int(1)));
  glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
  fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);

  glDeleteShader(VertexShaderID);
  glDeleteShader(FragmentShaderID);

  return ProgramID;
}

int main(void) {
  glfwSetErrorCallback(error_callback);

  // Initialise GLFW
  if (!glfwInit()) {
    fprintf(stderr, "Failed to initialize GLFW\n");
    return -1;
  }

  glfwWindowHint(GLFW_SAMPLES, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  // Open a window and create its OpenGL context
  window = glfwCreateWindow(640, 480, "Tutorial 01. Or is it?", NULL, NULL);
  if (window == NULL) {
    fprintf(stderr,
            "Failed to open GLFW window. If you have an Intel GPU, they are "
            "not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
    glfwTerminate();
    return -1;
  }
  glfwMakeContextCurrent(window);

  glewExperimental = GL_TRUE;
  // Initialize GLEW
  if (glewInit() != GLEW_OK) {
    fprintf(stderr, "Failed to initialize GLEW\n");
    return -1;
  }

  // get version info
  const GLubyte* renderer = glGetString (GL_RENDERER); // get renderer string
  const GLubyte* version = glGetString (GL_VERSION); // version as a string
  printf ("Renderer: %s\n", renderer);
  printf ("OpenGL version supported %s\n", version);

  // tell GL to only draw onto a pixel if the shape is closer to the viewer
  glEnable (GL_DEPTH_TEST); // enable depth-testing
  glDepthFunc (GL_LESS); // depth-testing interprets a smaller value as "closer"

  // Ensure we can capture the escape key being pressed below
  glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

  // Dark blue background
  glClearColor(.0f, .0f, 0.4f, 0.0f);

  GLuint VertexArrayID = 0;
  glGenVertexArrays (1, &VertexArrayID);
  glBindVertexArray (VertexArrayID);

  // Create and compile our GLSL program from the shaders
  GLuint programID = LoadShaders("SimpleVertexShader.vertexshader",
                                 "SimpleFragmentShader.fragmentshader");

  GLuint MatrixID = glGetUniformLocation(programID, "MVP");

  // Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
  glm::mat4 Projection = glm::perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
  // Camera matrix
  glm::mat4 View       = glm::lookAt(
      glm::vec3(4,3,3), // Camera is at (4,3,3), in World Space
      glm::vec3(0,0,0), // and looks at the origin
      glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
  );
  // Model matrix : an identity matrix (model will be at the origin)
  glm::mat4 Model      = glm::mat4(1.0f);  // Changes for each model !
  // Our ModelViewProjection : multiplication of our 3 matrices
  glm::mat4 MVP        = Projection * View * Model; // Remember, matrix multiplication is the other way around

  // An array of 3 vectors which represents 3 vertices
  static const GLfloat g_vertex_buffer_data[] = {
      -0.5f, -0.5f,  0.0f,
       0.5f, -0.5f,  0.0f,
       0.0f,  0.5f,  0.0f,

      -0.5f, -0.5f,  0.0f,
       0.5f, -0.5f,  0.0f,
       0.0f, -1.5f,  0.0f,
  };

  GLuint vertexbuffer;
  glGenBuffers(1, &vertexbuffer);
  glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
  glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data),
               g_vertex_buffer_data, GL_STATIC_DRAW);

  do {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(programID);
    // Send our transformation to the currently bound shader,
    // in the "MVP" uniform
    // For each model you render, since the MVP will be different (at least the M part)
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);

    glEnableVertexAttribArray (0);
    glBindBuffer (GL_ARRAY_BUFFER, vertexbuffer);
    glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, NULL);

    glBindVertexArray(VertexArrayID);

    glDrawArrays(GL_TRIANGLES, 0, 6);

    glDisableVertexAttribArray(0);

    // Swap buffers
    glfwSwapBuffers(window);
    glfwPollEvents();

  }  // Check if the ESC key was pressed or the window was closed
  while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
         glfwWindowShouldClose(window) == 0);

  // Cleanup VBO and shader
  glDeleteBuffers(1, &vertexbuffer);
  glDeleteProgram(programID);
  glDeleteVertexArrays(1, &VertexArrayID);

  // Close OpenGL window and terminate GLFW
  glfwTerminate();

  return 0;
}

<|endoftext|>


<commit_before>#include "vast/source/test.h"

#include "vast/event.h"
#include "vast/util/meta.h"
#include "vast/util/hash/murmur.h"

namespace vast {
namespace source {

namespace {

result<distribution> make_distribution(type const& t)
{
  auto a = t.find_attribute(type::attribute::default_);
  if (! a)
    return {};

  auto lparen = a->value.find('(');
  auto rparen = a->value.find(')', lparen);
  if (lparen == std::string::npos || rparen == std::string::npos)
    return error{"invalid parenthesis"};

  auto name = a->value.substr(0, lparen);
  auto parms = a->value.substr(lparen, rparen - lparen + 1);
  auto v = to<vector>(parms, type::real{}, ",", "(", ")");
  if (! v)
    return v.error();

  if (v->size() != 2)
    return error{"all distributions require two parameters"};

  auto p0 = *get<real>((*v)[0]);
  auto p1 = *get<real>((*v)[1]);

  if (name == "uniform")
  {
    if (is<type::integer>(t))
      return {std::uniform_int_distribution<integer>{
                static_cast<integer>(p0), static_cast<integer>(p1)}};
    else if (is<type::boolean>(t) || is<type::count>(t) || is<type::string>(t))
      return {std::uniform_int_distribution<count>{
                static_cast<count>(p0), static_cast<count>(p1)}};
    else
      return {std::uniform_real_distribution<long double>{p0, p1}};
  }

  if (name == "normal")
    return {std::normal_distribution<long double>{p0, p1}};

  if (name == "pareto")
    return {util::pareto_distribution<long double>{p0, p1}};

  return error{"unknown distribution: ", name};
}

struct blueprint_factory
{
  blueprint_factory(test::blueprint& bp)
    : blueprint_{bp}
  {
  }

  template <typename T>
  trial<void> operator()(T const& t)
  {
    auto dist = make_distribution(t);
    if (dist)
    {
      blueprint_.data.push_back(type{t}.make());
      blueprint_.dists.push_back(std::move(*dist));
      return nothing;
    }

    if (dist.empty())
    {
      blueprint_.data.push_back(nil);
      return nothing;
    }

    return dist.error();
  }

  trial<void> operator()(type::record const& r)
  {
    for (auto& f : r.fields())
    {
      auto okay = visit(*this, f.type);
      if (! okay)
        return okay;
    }

    return nothing;
  }

  test::blueprint& blueprint_;
};

template <typename RNG>
struct sampler
{
  sampler(RNG& gen)
    : gen_{gen}
  {
  }

  template <typename D>
  long double operator()(D& dist)
  {
    return static_cast<long double>(dist(gen_));
  }

  RNG& gen_;
};

// Randomizes data according to a list of distributions and a source of
// randomness.
template <typename RNG>
struct randomizer
{
  randomizer(std::vector<distribution>& dists, RNG& gen)
    : dists_{dists},
      gen_{gen}
  {
  }

  template <typename T>
  auto operator()(T&)
    -> util::disable_if_t<
         std::is_same<T, integer>::value
         || std::is_same<T, count>::value
         || std::is_same<T, real>::value
         || std::is_same<T, time_point>::value
         || std::is_same<T, time_duration>::value
       >
  {
    // For types we don't know how to randomize, we just crank the wheel.
    sample();
  }

  void operator()(none)
  {
    // Do nothing.
  }

  void operator()(boolean& b)
  {
    lcg64 gen{static_cast<count>(sample())};
    std::uniform_int_distribution<count> unif{0, 1};
    b = unif(gen);
  }

  template <typename T>
  auto operator()(T& x)
    -> std::enable_if_t<
         std::is_same<T, integer>::value
         || std::is_same<T, count>::value
         || std::is_same<T, real>::value
       >
  {
    x = static_cast<T>(sample());
  }

  template <typename T>
  auto operator()(T& x)
    -> std::enable_if_t<
         std::is_same<T, time_point>::value
         || std::is_same<T, time_duration>::value
       >
  {
    x += time_duration::fractional(sample());
  }

  void operator()(std::string& str)
  {
    auto x = static_cast<uint64_t>(sample());

    // Generate printable characters only.
    lcg64 gen{x};
    std::uniform_int_distribution<char> unif{32, 126};

    str.resize(x % 256);
    for (auto& c : str)
      c = unif(gen);
  }

  void operator()(address& addr)
  {
    // We hash the generated sample into a 128-bit digest to spread out the
    // bits over the entire domain of an IPv6 address.
    auto x = sample();
    uint32_t bytes[4];
    util::detail::murmur3<128>(&x, sizeof(x), 0, bytes);

    // P[ip == v6] = 0.5
    std::uniform_int_distribution<uint8_t> unif{0, 1};
    auto version = unif(gen_) ? address::ipv4 : address::ipv6;
    addr = {bytes, version, address::network};
  }

  void operator()(subnet& sn)
  {
    address addr;
    (*this)(addr);

    std::uniform_int_distribution<uint8_t> unif{0, 128};
    sn = {std::move(addr), unif(gen_)};
  }

  void operator()(port& p)
  {
    using port_type = std::underlying_type_t<port::port_type>;
    std::uniform_int_distribution<port_type> unif{0, 3};
    p.number(static_cast<port::number_type>(sample()));
    p.type(static_cast<port::port_type>(unif(gen_)));
  }

  void operator()(record& r)
  {
    for (auto& d : r)
      visit(*this, d);
  }

  auto sample()
  {
    return visit(sampler<RNG>{gen_}, dists_[i++]);
  }

  std::vector<distribution> dists_;
  size_t i = 0;
  RNG& gen_;
};

} // namespace <anonymous>

test::test(schema sch, event_id id, uint64_t events)
  : schema_{std::move(sch)},
    id_{id},
    events_{events},
    generator_{std::random_device{}()},
    next_{schema_.begin()}
{
  assert(events_ > 0);
}

result<event> test::extract()
{
  if (schema_.empty())
    return error{"must have at least one type in schema"};

  assert(next_ != schema_.end());

  if (blueprints_.empty())
  {
    for (auto& t : schema_)
    {
      blueprint bp;
      auto attempt = visit(blueprint_factory{bp}, t);
      if (! attempt)
        return attempt.error();

      if (auto r = get<type::record>(t))
      {
        auto u = bp.data.unflatten(*r);
        if (! u)
          return u.error();

        bp.data = std::move(*u);
      }

      assert(! bp.data.empty());
      blueprints_[t] = std::move(bp);
    }
  }

  auto& bp = blueprints_[*next_];
  randomizer<std::mt19937_64>{bp.dists, generator_}(bp.data);
  auto d = is<type::record>(*next_) ? data{bp.data} : bp.data[0];

  event e{{std::move(d), *next_}};
  e.timestamp(now());
  e.id(id_++);

  if (++next_ == schema_.end())
    next_ = schema_.begin();

  assert(events_ > 0);
  --events_;
  return std::move(e);
}

bool test::done() const
{
  return events_ == 0;
}

std::string test::describe() const
{
  return "test-source";
}

} // namespace source
} // namespace vast
<commit_msg>Change semantics of string sampling.<commit_after>#include "vast/source/test.h"

#include "vast/event.h"
#include "vast/util/meta.h"
#include "vast/util/hash/murmur.h"

namespace vast {
namespace source {

namespace {

result<distribution> make_distribution(type const& t)
{
  auto a = t.find_attribute(type::attribute::default_);
  if (! a)
    return {};

  auto lparen = a->value.find('(');
  auto rparen = a->value.find(')', lparen);
  if (lparen == std::string::npos || rparen == std::string::npos)
    return error{"invalid parenthesis"};

  auto name = a->value.substr(0, lparen);
  auto parms = a->value.substr(lparen, rparen - lparen + 1);
  auto v = to<vector>(parms, type::real{}, ",", "(", ")");
  if (! v)
    return v.error();

  if (v->size() != 2)
    return error{"all distributions require two parameters"};

  auto p0 = *get<real>((*v)[0]);
  auto p1 = *get<real>((*v)[1]);

  if (name == "uniform")
  {
    if (is<type::integer>(t))
      return {std::uniform_int_distribution<integer>{
                static_cast<integer>(p0), static_cast<integer>(p1)}};
    else if (is<type::boolean>(t) || is<type::count>(t) || is<type::string>(t))
      return {std::uniform_int_distribution<count>{
                static_cast<count>(p0), static_cast<count>(p1)}};
    else
      return {std::uniform_real_distribution<long double>{p0, p1}};
  }

  if (name == "normal")
    return {std::normal_distribution<long double>{p0, p1}};

  if (name == "pareto")
    return {util::pareto_distribution<long double>{p0, p1}};

  return error{"unknown distribution: ", name};
}

struct blueprint_factory
{
  blueprint_factory(test::blueprint& bp)
    : blueprint_{bp}
  {
  }

  template <typename T>
  trial<void> operator()(T const& t)
  {
    auto dist = make_distribution(t);
    if (dist)
    {
      blueprint_.data.push_back(type{t}.make());
      blueprint_.dists.push_back(std::move(*dist));
      return nothing;
    }

    if (dist.empty())
    {
      blueprint_.data.push_back(nil);
      return nothing;
    }

    return dist.error();
  }

  trial<void> operator()(type::record const& r)
  {
    for (auto& f : r.fields())
    {
      auto okay = visit(*this, f.type);
      if (! okay)
        return okay;
    }

    return nothing;
  }

  test::blueprint& blueprint_;
};

template <typename RNG>
struct sampler
{
  sampler(RNG& gen)
    : gen_{gen}
  {
  }

  template <typename D>
  long double operator()(D& dist)
  {
    return static_cast<long double>(dist(gen_));
  }

  RNG& gen_;
};

// Randomizes data according to a list of distributions and a source of
// randomness.
template <typename RNG>
struct randomizer
{
  randomizer(std::vector<distribution>& dists, RNG& gen)
    : dists_{dists},
      gen_{gen}
  {
  }

  template <typename T>
  auto operator()(T&)
    -> util::disable_if_t<
         std::is_same<T, integer>::value
         || std::is_same<T, count>::value
         || std::is_same<T, real>::value
         || std::is_same<T, time_point>::value
         || std::is_same<T, time_duration>::value
       >
  {
    // For types we don't know how to randomize, we just crank the wheel.
    sample();
  }

  void operator()(none)
  {
    // Do nothing.
  }

  void operator()(boolean& b)
  {
    lcg64 gen{static_cast<count>(sample())};
    std::uniform_int_distribution<count> unif{0, 1};
    b = unif(gen);
  }

  template <typename T>
  auto operator()(T& x)
    -> std::enable_if_t<
         std::is_same<T, integer>::value
         || std::is_same<T, count>::value
         || std::is_same<T, real>::value
       >
  {
    x = static_cast<T>(sample());
  }

  template <typename T>
  auto operator()(T& x)
    -> std::enable_if_t<
         std::is_same<T, time_point>::value
         || std::is_same<T, time_duration>::value
       >
  {
    x += time_duration::fractional(sample());
  }

  void operator()(std::string& str)
  {
    lcg64 gen{static_cast<count>(sample())};
    std::uniform_int_distribution<size_t> unif_size{0, 256};
    std::uniform_int_distribution<char> unif_char{32, 126}; // Printable ASCII

    str.resize(unif_size(gen));
    for (auto& c : str)
      c = unif_char(gen);
  }

  void operator()(address& addr)
  {
    // We hash the generated sample into a 128-bit digest to spread out the
    // bits over the entire domain of an IPv6 address.
    auto x = sample();
    uint32_t bytes[4];
    util::detail::murmur3<128>(&x, sizeof(x), 0, bytes);

    // P[ip == v6] = 0.5
    std::uniform_int_distribution<uint8_t> unif{0, 1};
    auto version = unif(gen_) ? address::ipv4 : address::ipv6;
    addr = {bytes, version, address::network};
  }

  void operator()(subnet& sn)
  {
    address addr;
    (*this)(addr);

    std::uniform_int_distribution<uint8_t> unif{0, 128};
    sn = {std::move(addr), unif(gen_)};
  }

  void operator()(port& p)
  {
    using port_type = std::underlying_type_t<port::port_type>;
    std::uniform_int_distribution<port_type> unif{0, 3};
    p.number(static_cast<port::number_type>(sample()));
    p.type(static_cast<port::port_type>(unif(gen_)));
  }

  void operator()(record& r)
  {
    for (auto& d : r)
      visit(*this, d);
  }

  auto sample()
  {
    return visit(sampler<RNG>{gen_}, dists_[i++]);
  }

  std::vector<distribution> dists_;
  size_t i = 0;
  RNG& gen_;
};

} // namespace <anonymous>

test::test(schema sch, event_id id, uint64_t events)
  : schema_{std::move(sch)},
    id_{id},
    events_{events},
    generator_{std::random_device{}()},
    next_{schema_.begin()}
{
  assert(events_ > 0);
}

result<event> test::extract()
{
  if (schema_.empty())
    return error{"must have at least one type in schema"};

  assert(next_ != schema_.end());

  if (blueprints_.empty())
  {
    for (auto& t : schema_)
    {
      blueprint bp;
      auto attempt = visit(blueprint_factory{bp}, t);
      if (! attempt)
        return attempt.error();

      if (auto r = get<type::record>(t))
      {
        auto u = bp.data.unflatten(*r);
        if (! u)
          return u.error();

        bp.data = std::move(*u);
      }

      assert(! bp.data.empty());
      blueprints_[t] = std::move(bp);
    }
  }

  auto& bp = blueprints_[*next_];
  randomizer<std::mt19937_64>{bp.dists, generator_}(bp.data);
  auto d = is<type::record>(*next_) ? data{bp.data} : bp.data[0];

  event e{{std::move(d), *next_}};
  e.timestamp(now());
  e.id(id_++);

  if (++next_ == schema_.end())
    next_ = schema_.begin();

  assert(events_ > 0);
  --events_;
  return std::move(e);
}

bool test::done() const
{
  return events_ == 0;
}

std::string test::describe() const
{
  return "test-source";
}

} // namespace source
} // namespace vast
<|endoftext|>


<commit_before>#include "vattachmentlist.h"

#include <QtWidgets>

#include "vconfigmanager.h"
#include "utils/vutils.h"
#include "vbuttonwithwidget.h"
#include "vnote.h"
#include "vmainwindow.h"
#include "dialog/vconfirmdeletiondialog.h"
#include "dialog/vsortdialog.h"

extern VConfigManager *g_config;
extern VNote *g_vnote;

VAttachmentList::VAttachmentList(QWidget *p_parent)
    : QWidget(p_parent), m_file(NULL)
{
    setupUI();

    initActions();

    updateContent();
}

void VAttachmentList::setupUI()
{
    m_addBtn = new QPushButton(QIcon(":/resources/icons/add_attachment.svg"), "");
    m_addBtn->setToolTip(tr("Add"));
    m_addBtn->setProperty("FlatBtn", true);
    connect(m_addBtn, &QPushButton::clicked,
            this, &VAttachmentList::addAttachment);

    m_clearBtn = new QPushButton(QIcon(":/resources/icons/clear_attachment.svg"), "");
    m_clearBtn->setToolTip(tr("Clear"));
    m_clearBtn->setProperty("FlatBtn", true);
    connect(m_clearBtn, &QPushButton::clicked,
            this, [this]() {
                if (m_file && m_attachmentList->count() > 0) {
                    int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
                                                  tr("Are you sure to clear attachments of note "
                                                     "%2?")
                                                    .arg(g_config->c_dataTextStyle)
                                                    .arg(m_file->getName()),
                                                  tr("WARNING: "
                                                     "VNote will delete all the files in directory "
                                                     "%3."
                                                     "You could find deleted files in the recycle bin "
                                                     "of this notebook.
The operation is IRREVERSIBLE!")
                                                    .arg(g_config->c_warningTextStyle)
                                                    .arg(g_config->c_dataTextStyle)
                                                    .arg(m_file->fetchAttachmentFolderPath()),
                                                  QMessageBox::Ok | QMessageBox::Cancel,
                                                  QMessageBox::Ok,
                                                  g_vnote->getMainWindow(),
                                                  MessageBoxType::Danger);
                    if (ret == QMessageBox::Ok) {
                        if (!m_file->deleteAttachments()) {
                            VUtils::showMessage(QMessageBox::Warning,
                                                tr("Warning"),
                                                tr("Fail to clear attachments of note %2.")
                                                  .arg(g_config->c_dataTextStyle)
                                                  .arg(m_file->getName()),
                                                tr("Please maintain the configureation file manually."),
                                                QMessageBox::Ok,
                                                QMessageBox::Ok,
                                                g_vnote->getMainWindow());
                        }

                        m_attachmentList->clear();
                    }
                }
            });

    m_locateBtn = new QPushButton(QIcon(":/resources/icons/locate_attachment.svg"), "");
    m_locateBtn->setToolTip(tr("Open Folder"));
    m_locateBtn->setProperty("FlatBtn", true);
    connect(m_locateBtn, &QPushButton::clicked,
            this, [this]() {
                if (m_file && !m_file->getAttachmentFolder().isEmpty()) {
                    QUrl url = QUrl::fromLocalFile(m_file->fetchAttachmentFolderPath());
                    QDesktopServices::openUrl(url);
                }
            });

    m_numLabel = new QLabel();

    QHBoxLayout *btnLayout = new QHBoxLayout;
    btnLayout->addWidget(m_addBtn);
    btnLayout->addWidget(m_clearBtn);
    btnLayout->addWidget(m_locateBtn);
    btnLayout->addStretch();
    btnLayout->addWidget(m_numLabel);

    m_attachmentList = new QListWidget;
    m_attachmentList->setContextMenuPolicy(Qt::CustomContextMenu);
    m_attachmentList->setSelectionMode(QAbstractItemView::ExtendedSelection);
    m_attachmentList->setEditTriggers(QAbstractItemView::SelectedClicked);
    connect(m_attachmentList, &QListWidget::customContextMenuRequested,
            this, &VAttachmentList::handleContextMenuRequested);
    connect(m_attachmentList, &QListWidget::itemActivated,
            this, &VAttachmentList::handleItemActivated);
    connect(m_attachmentList->itemDelegate(), &QAbstractItemDelegate::commitData,
            this, &VAttachmentList::handleListItemCommitData);

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addLayout(btnLayout);
    mainLayout->addWidget(m_attachmentList);

    setLayout(mainLayout);
}

void VAttachmentList::initActions()
{
    m_openAct = new QAction(tr("&Open"), this);
    m_openAct->setToolTip(tr("Open current attachment file"));
    connect(m_openAct, &QAction::triggered,
            this, [this]() {
                QListWidgetItem *item = m_attachmentList->currentItem();
                handleItemActivated(item);
            });

    m_deleteAct = new QAction(QIcon(":/resources/icons/delete_attachment.svg"),
                              tr("&Delete"),
                              this);
    m_deleteAct->setToolTip(tr("Delete selected attachments"));
    connect(m_deleteAct, &QAction::triggered,
            this, &VAttachmentList::deleteSelectedItems);

    m_sortAct = new QAction(QIcon(":/resources/icons/sort.svg"),
                            tr("&Sort"),
                            this);
    m_sortAct->setToolTip(tr("Sort attachments manually"));
    connect(m_sortAct, &QAction::triggered,
            this, &VAttachmentList::sortItems);
}

void VAttachmentList::setFile(VNoteFile *p_file)
{
    m_file = p_file;
    updateContent();
}

void VAttachmentList::updateContent()
{
    bool enableAdd = true, enableDelete = true, enableClear = true, enableLocate = true;
    m_attachmentList->clear();

    if (!m_file) {
        enableAdd = enableDelete = enableClear = enableLocate = false;
    } else {
        QString folder = m_file->getAttachmentFolder();
        const QVector<VAttachment> &attas = m_file->getAttachments();

        if (folder.isEmpty()) {
            Q_ASSERT(attas.isEmpty());
            enableDelete = enableClear = enableLocate = false;
        } else if (attas.isEmpty()) {
            enableDelete = enableClear = false;
        } else {
            fillAttachmentList(attas);
        }
    }

    m_addBtn->setEnabled(enableAdd);
    m_clearBtn->setEnabled(enableClear);
    m_locateBtn->setEnabled(enableLocate);

    int cnt = m_attachmentList->count();
    if (cnt > 0) {
        m_numLabel->setText(tr("%1 %2").arg(cnt).arg(cnt > 1 ? tr("Files") : tr("File")));
    } else {
        m_numLabel->setText("");
    }
}

void VAttachmentList::fillAttachmentList(const QVector<VAttachment> &p_attachments)
{
    Q_ASSERT(m_attachmentList->count() == 0);
    for (int i = 0; i < p_attachments.size(); ++i) {
        const VAttachment &atta = p_attachments[i];
        QListWidgetItem *item = new QListWidgetItem(atta.m_name);
        item->setFlags(item->flags() | Qt::ItemIsEditable);
        item->setData(Qt::UserRole, atta.m_name);

        m_attachmentList->addItem(item);
    }
}

void VAttachmentList::addAttachment()
{
    if (!m_file) {
        return;
    }

    static QString lastPath = QDir::homePath();
    QStringList files = QFileDialog::getOpenFileNames(g_vnote->getMainWindow(),
                                                      tr("Select Files As Attachments"),
                                                      lastPath);
    if (files.isEmpty()) {
        return;
    }

    // Update lastPath
    lastPath = QFileInfo(files[0]).path();

    int addedFiles = 0;
    for (int i = 0; i < files.size(); ++i) {
        if (!m_file->addAttachment(files[i])) {
            VUtils::showMessage(QMessageBox::Warning,
                                tr("Warning"),
                                tr("Fail to add attachment %1 for note %3.")
                                  .arg(files[i])
                                  .arg(g_config->c_dataTextStyle)
                                  .arg(m_file->getName()),
                                "",
                                QMessageBox::Ok,
                                QMessageBox::Ok,
                                g_vnote->getMainWindow());
        } else {
            ++addedFiles;
        }
    }

    updateContent();

    if (addedFiles > 0) {
        g_vnote->getMainWindow()->showStatusMessage(tr("Added %1 %2 as attachments")
                                                      .arg(addedFiles)
                                                      .arg(addedFiles > 1 ? tr("files") : tr("file")));
    }
}

void VAttachmentList::handleContextMenuRequested(QPoint p_pos)
{
    // @p_pos is the position in the coordinate of VAttachmentList, no m_attachmentList.
    QListWidgetItem *item = m_attachmentList->itemAt(m_attachmentList->mapFromParent(p_pos));
    QMenu menu(this);
    menu.setToolTipsVisible(true);

    if (!m_file) {
        return;
    }

    if (item) {
        if (!item->isSelected()) {
            m_attachmentList->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
        }

        if (m_attachmentList->selectedItems().size() == 1) {
            menu.addAction(m_openAct);
        }

        menu.addAction(m_deleteAct);
    }

    m_attachmentList->update();

    if (m_file->getAttachments().size() > 1) {
        if (!menu.actions().isEmpty()) {
            menu.addSeparator();
        }

        menu.addAction(m_sortAct);
    }

    if (!menu.actions().isEmpty()) {
        menu.exec(mapToGlobal(p_pos));
    }
}

void VAttachmentList::handleItemActivated(QListWidgetItem *p_item)
{
    if (p_item) {
        Q_ASSERT(m_file);

        QString name = p_item->text();
        QString folderPath = m_file->fetchAttachmentFolderPath();
        QUrl url = QUrl::fromLocalFile(QDir(folderPath).filePath(name));
        QDesktopServices::openUrl(url);
    }
}

void VAttachmentList::deleteSelectedItems()
{
    QVector<QString> names;
    const QList<QListWidgetItem *> selectedItems = m_attachmentList->selectedItems();

    if (selectedItems.isEmpty()) {
        return;
    }

    for (auto const & item : selectedItems) {
        names.push_back(item->text());
    }

    QString info = tr("Are you sure to delete these attachments of note "
                      "%2? "
                      "You could find deleted files in the recycle "
                      "bin of this notebook.
"
                      "Click \"Cancel\" to leave them untouched.")
                     .arg(g_config->c_dataTextStyle).arg(m_file->getName());
    VConfirmDeletionDialog dialog(tr("Confirm Deleting Attachments"),
                                  info,
                                  names,
                                  false,
                                  false,
                                  false,
                                  g_vnote->getMainWindow());
    if (dialog.exec()) {
        names = dialog.getConfirmedFiles();

        if (!m_file->deleteAttachments(names)) {
            VUtils::showMessage(QMessageBox::Warning,
                                tr("Warning"),
                                tr("Fail to delete attachments of note %2.")
                                  .arg(g_config->c_dataTextStyle)
                                  .arg(m_file->getName()),
                                tr("Please maintain the configureation file manually."),
                                QMessageBox::Ok,
                                QMessageBox::Ok,
                                g_vnote->getMainWindow());
        }

        updateContent();
    }
}

void VAttachmentList::sortItems()
{
    const QVector<VAttachment> &attas = m_file->getAttachments();
    if (attas.size() < 2) {
        return;
    }

    VSortDialog dialog(tr("Sort Attachments"),
                       tr("Sort attachments in the configuration file."),
                       g_vnote->getMainWindow());
    QTreeWidget *tree = dialog.getTreeWidget();
    tree->clear();
    tree->setColumnCount(1);
    tree->header()->setStretchLastSection(true);
    QStringList headers;
    headers <
setHeaderLabels(headers);

    for (int i = 0; i < attas.size(); ++i) {
        QTreeWidgetItem *item = new QTreeWidgetItem(tree, QStringList(attas[i].m_name));

        item->setData(0, Qt::UserRole, i);
    }

    dialog.treeUpdated();

    if (dialog.exec()) {
        int cnt = tree->topLevelItemCount();
        Q_ASSERT(cnt == attas.size());
        QVector<int> sortedIdx(cnt, -1);
        for (int i = 0; i < cnt; ++i) {
            QTreeWidgetItem *item = tree->topLevelItem(i);
            Q_ASSERT(item);
            sortedIdx[i] = item->data(0, Qt::UserRole).toInt();
        }

        m_file->sortAttachments(sortedIdx);
    }
}

void VAttachmentList::handleListItemCommitData(QWidget *p_itemEdit)
{
    QString text = reinterpret_cast<QLineEdit *>(p_itemEdit)->text();
    QListWidgetItem *item = m_attachmentList->currentItem();
    Q_ASSERT(item && item->text() == text);

    QString oldText = item->data(Qt::UserRole).toString();

    if (oldText == text) {
        return;
    }

    if (!(oldText.toLower() == text.toLower())
        && m_file->findAttachment(text, false) > -1) {
        // Name conflict.
        // Recover to old name.
        item->setText(oldText);
    } else {
        if (!m_file->renameAttachment(oldText, text)) {
            VUtils::showMessage(QMessageBox::Information,
                                tr("Rename Attachment"),
                                tr("Fail to rename attachment %2.")
                                  .arg(g_config->c_dataTextStyle)
                                  .arg(oldText),
                                "",
                                QMessageBox::Ok,
                                QMessageBox::Ok,
                                this);
            // Recover to old name.
            item->setText(oldText);
        } else {
            // Change the data.
            item->setData(Qt::UserRole, text);
        }
    }
}
<commit_msg>bug-fix: add attachment name check<commit_after>#include "vattachmentlist.h"

#include <QtWidgets>

#include "vconfigmanager.h"
#include "utils/vutils.h"
#include "vbuttonwithwidget.h"
#include "vnote.h"
#include "vmainwindow.h"
#include "dialog/vconfirmdeletiondialog.h"
#include "dialog/vsortdialog.h"

extern VConfigManager *g_config;
extern VNote *g_vnote;

VAttachmentList::VAttachmentList(QWidget *p_parent)
    : QWidget(p_parent), m_file(NULL)
{
    setupUI();

    initActions();

    updateContent();
}

void VAttachmentList::setupUI()
{
    m_addBtn = new QPushButton(QIcon(":/resources/icons/add_attachment.svg"), "");
    m_addBtn->setToolTip(tr("Add"));
    m_addBtn->setProperty("FlatBtn", true);
    connect(m_addBtn, &QPushButton::clicked,
            this, &VAttachmentList::addAttachment);

    m_clearBtn = new QPushButton(QIcon(":/resources/icons/clear_attachment.svg"), "");
    m_clearBtn->setToolTip(tr("Clear"));
    m_clearBtn->setProperty("FlatBtn", true);
    connect(m_clearBtn, &QPushButton::clicked,
            this, [this]() {
                if (m_file && m_attachmentList->count() > 0) {
                    int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
                                                  tr("Are you sure to clear attachments of note "
                                                     "%2?")
                                                    .arg(g_config->c_dataTextStyle)
                                                    .arg(m_file->getName()),
                                                  tr("WARNING: "
                                                     "VNote will delete all the files in directory "
                                                     "%3."
                                                     "You could find deleted files in the recycle bin "
                                                     "of this notebook.
The operation is IRREVERSIBLE!")
                                                    .arg(g_config->c_warningTextStyle)
                                                    .arg(g_config->c_dataTextStyle)
                                                    .arg(m_file->fetchAttachmentFolderPath()),
                                                  QMessageBox::Ok | QMessageBox::Cancel,
                                                  QMessageBox::Ok,
                                                  g_vnote->getMainWindow(),
                                                  MessageBoxType::Danger);
                    if (ret == QMessageBox::Ok) {
                        if (!m_file->deleteAttachments()) {
                            VUtils::showMessage(QMessageBox::Warning,
                                                tr("Warning"),
                                                tr("Fail to clear attachments of note %2.")
                                                  .arg(g_config->c_dataTextStyle)
                                                  .arg(m_file->getName()),
                                                tr("Please maintain the configureation file manually."),
                                                QMessageBox::Ok,
                                                QMessageBox::Ok,
                                                g_vnote->getMainWindow());
                        }

                        m_attachmentList->clear();
                    }
                }
            });

    m_locateBtn = new QPushButton(QIcon(":/resources/icons/locate_attachment.svg"), "");
    m_locateBtn->setToolTip(tr("Open Folder"));
    m_locateBtn->setProperty("FlatBtn", true);
    connect(m_locateBtn, &QPushButton::clicked,
            this, [this]() {
                if (m_file && !m_file->getAttachmentFolder().isEmpty()) {
                    QUrl url = QUrl::fromLocalFile(m_file->fetchAttachmentFolderPath());
                    QDesktopServices::openUrl(url);
                }
            });

    m_numLabel = new QLabel();

    QHBoxLayout *btnLayout = new QHBoxLayout;
    btnLayout->addWidget(m_addBtn);
    btnLayout->addWidget(m_clearBtn);
    btnLayout->addWidget(m_locateBtn);
    btnLayout->addStretch();
    btnLayout->addWidget(m_numLabel);

    m_attachmentList = new QListWidget;
    m_attachmentList->setContextMenuPolicy(Qt::CustomContextMenu);
    m_attachmentList->setSelectionMode(QAbstractItemView::ExtendedSelection);
    m_attachmentList->setEditTriggers(QAbstractItemView::SelectedClicked);
    connect(m_attachmentList, &QListWidget::customContextMenuRequested,
            this, &VAttachmentList::handleContextMenuRequested);
    connect(m_attachmentList, &QListWidget::itemActivated,
            this, &VAttachmentList::handleItemActivated);
    connect(m_attachmentList->itemDelegate(), &QAbstractItemDelegate::commitData,
            this, &VAttachmentList::handleListItemCommitData);

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addLayout(btnLayout);
    mainLayout->addWidget(m_attachmentList);

    setLayout(mainLayout);
}

void VAttachmentList::initActions()
{
    m_openAct = new QAction(tr("&Open"), this);
    m_openAct->setToolTip(tr("Open current attachment file"));
    connect(m_openAct, &QAction::triggered,
            this, [this]() {
                QListWidgetItem *item = m_attachmentList->currentItem();
                handleItemActivated(item);
            });

    m_deleteAct = new QAction(QIcon(":/resources/icons/delete_attachment.svg"),
                              tr("&Delete"),
                              this);
    m_deleteAct->setToolTip(tr("Delete selected attachments"));
    connect(m_deleteAct, &QAction::triggered,
            this, &VAttachmentList::deleteSelectedItems);

    m_sortAct = new QAction(QIcon(":/resources/icons/sort.svg"),
                            tr("&Sort"),
                            this);
    m_sortAct->setToolTip(tr("Sort attachments manually"));
    connect(m_sortAct, &QAction::triggered,
            this, &VAttachmentList::sortItems);
}

void VAttachmentList::setFile(VNoteFile *p_file)
{
    m_file = p_file;
    updateContent();
}

void VAttachmentList::updateContent()
{
    bool enableAdd = true, enableDelete = true, enableClear = true, enableLocate = true;
    m_attachmentList->clear();

    if (!m_file) {
        enableAdd = enableDelete = enableClear = enableLocate = false;
    } else {
        QString folder = m_file->getAttachmentFolder();
        const QVector<VAttachment> &attas = m_file->getAttachments();

        if (folder.isEmpty()) {
            Q_ASSERT(attas.isEmpty());
            enableDelete = enableClear = enableLocate = false;
        } else if (attas.isEmpty()) {
            enableDelete = enableClear = false;
        } else {
            fillAttachmentList(attas);
        }
    }

    m_addBtn->setEnabled(enableAdd);
    m_clearBtn->setEnabled(enableClear);
    m_locateBtn->setEnabled(enableLocate);

    int cnt = m_attachmentList->count();
    if (cnt > 0) {
        m_numLabel->setText(tr("%1 %2").arg(cnt).arg(cnt > 1 ? tr("Files") : tr("File")));
    } else {
        m_numLabel->setText("");
    }
}

void VAttachmentList::fillAttachmentList(const QVector<VAttachment> &p_attachments)
{
    Q_ASSERT(m_attachmentList->count() == 0);
    for (int i = 0; i < p_attachments.size(); ++i) {
        const VAttachment &atta = p_attachments[i];
        QListWidgetItem *item = new QListWidgetItem(atta.m_name);
        item->setFlags(item->flags() | Qt::ItemIsEditable);
        item->setData(Qt::UserRole, atta.m_name);

        m_attachmentList->addItem(item);
    }
}

void VAttachmentList::addAttachment()
{
    if (!m_file) {
        return;
    }

    static QString lastPath = QDir::homePath();
    QStringList files = QFileDialog::getOpenFileNames(g_vnote->getMainWindow(),
                                                      tr("Select Files As Attachments"),
                                                      lastPath);
    if (files.isEmpty()) {
        return;
    }

    // Update lastPath
    lastPath = QFileInfo(files[0]).path();

    int addedFiles = 0;
    for (int i = 0; i < files.size(); ++i) {
        if (!m_file->addAttachment(files[i])) {
            VUtils::showMessage(QMessageBox::Warning,
                                tr("Warning"),
                                tr("Fail to add attachment %1 for note %3.")
                                  .arg(files[i])
                                  .arg(g_config->c_dataTextStyle)
                                  .arg(m_file->getName()),
                                "",
                                QMessageBox::Ok,
                                QMessageBox::Ok,
                                g_vnote->getMainWindow());
        } else {
            ++addedFiles;
        }
    }

    updateContent();

    if (addedFiles > 0) {
        g_vnote->getMainWindow()->showStatusMessage(tr("Added %1 %2 as attachments")
                                                      .arg(addedFiles)
                                                      .arg(addedFiles > 1 ? tr("files") : tr("file")));
    }
}

void VAttachmentList::handleContextMenuRequested(QPoint p_pos)
{
    // @p_pos is the position in the coordinate of VAttachmentList, no m_attachmentList.
    QListWidgetItem *item = m_attachmentList->itemAt(m_attachmentList->mapFromParent(p_pos));
    QMenu menu(this);
    menu.setToolTipsVisible(true);

    if (!m_file) {
        return;
    }

    if (item) {
        if (!item->isSelected()) {
            m_attachmentList->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
        }

        if (m_attachmentList->selectedItems().size() == 1) {
            menu.addAction(m_openAct);
        }

        menu.addAction(m_deleteAct);
    }

    m_attachmentList->update();

    if (m_file->getAttachments().size() > 1) {
        if (!menu.actions().isEmpty()) {
            menu.addSeparator();
        }

        menu.addAction(m_sortAct);
    }

    if (!menu.actions().isEmpty()) {
        menu.exec(mapToGlobal(p_pos));
    }
}

void VAttachmentList::handleItemActivated(QListWidgetItem *p_item)
{
    if (p_item) {
        Q_ASSERT(m_file);

        QString name = p_item->text();
        QString folderPath = m_file->fetchAttachmentFolderPath();
        QUrl url = QUrl::fromLocalFile(QDir(folderPath).filePath(name));
        QDesktopServices::openUrl(url);
    }
}

void VAttachmentList::deleteSelectedItems()
{
    QVector<QString> names;
    const QList<QListWidgetItem *> selectedItems = m_attachmentList->selectedItems();

    if (selectedItems.isEmpty()) {
        return;
    }

    for (auto const & item : selectedItems) {
        names.push_back(item->text());
    }

    QString info = tr("Are you sure to delete these attachments of note "
                      "%2? "
                      "You could find deleted files in the recycle "
                      "bin of this notebook.
"
                      "Click \"Cancel\" to leave them untouched.")
                     .arg(g_config->c_dataTextStyle).arg(m_file->getName());
    VConfirmDeletionDialog dialog(tr("Confirm Deleting Attachments"),
                                  info,
                                  names,
                                  false,
                                  false,
                                  false,
                                  g_vnote->getMainWindow());
    if (dialog.exec()) {
        names = dialog.getConfirmedFiles();

        if (!m_file->deleteAttachments(names)) {
            VUtils::showMessage(QMessageBox::Warning,
                                tr("Warning"),
                                tr("Fail to delete attachments of note %2.")
                                  .arg(g_config->c_dataTextStyle)
                                  .arg(m_file->getName()),
                                tr("Please maintain the configureation file manually."),
                                QMessageBox::Ok,
                                QMessageBox::Ok,
                                g_vnote->getMainWindow());
        }

        updateContent();
    }
}

void VAttachmentList::sortItems()
{
    const QVector<VAttachment> &attas = m_file->getAttachments();
    if (attas.size() < 2) {
        return;
    }

    VSortDialog dialog(tr("Sort Attachments"),
                       tr("Sort attachments in the configuration file."),
                       g_vnote->getMainWindow());
    QTreeWidget *tree = dialog.getTreeWidget();
    tree->clear();
    tree->setColumnCount(1);
    tree->header()->setStretchLastSection(true);
    QStringList headers;
    headers <
setHeaderLabels(headers);

    for (int i = 0; i < attas.size(); ++i) {
        QTreeWidgetItem *item = new QTreeWidgetItem(tree, QStringList(attas[i].m_name));

        item->setData(0, Qt::UserRole, i);
    }

    dialog.treeUpdated();

    if (dialog.exec()) {
        int cnt = tree->topLevelItemCount();
        Q_ASSERT(cnt == attas.size());
        QVector<int> sortedIdx(cnt, -1);
        for (int i = 0; i < cnt; ++i) {
            QTreeWidgetItem *item = tree->topLevelItem(i);
            Q_ASSERT(item);
            sortedIdx[i] = item->data(0, Qt::UserRole).toInt();
        }

        m_file->sortAttachments(sortedIdx);
    }
}

void VAttachmentList::handleListItemCommitData(QWidget *p_itemEdit)
{
    QString text = reinterpret_cast<QLineEdit *>(p_itemEdit)->text();
    QListWidgetItem *item = m_attachmentList->currentItem();
    Q_ASSERT(item && item->text() == text);

    QString oldText = item->data(Qt::UserRole).toString();

    if (oldText == text) {
        return;
    }

    bool legalName = true;
    if (text.isEmpty()) {
        legalName = false;
    } else {
        QRegExp reg(VUtils::c_fileNameRegExp);
        if (!reg.exactMatch(text)) {
            legalName = false;
        }
    }

    if (!legalName) {
        // Recover to old name.
        item->setText(oldText);
        return;
    }

    if (!(oldText.toLower() == text.toLower())
        && m_file->findAttachment(text, false) > -1) {
        // Name conflict.
        // Recover to old name.
        item->setText(oldText);
    } else {
        if (!m_file->renameAttachment(oldText, text)) {
            VUtils::showMessage(QMessageBox::Information,
                                tr("Rename Attachment"),
                                tr("Fail to rename attachment %2.")
                                  .arg(g_config->c_dataTextStyle)
                                  .arg(oldText),
                                "",
                                QMessageBox::Ok,
                                QMessageBox::Ok,
                                this);
            // Recover to old name.
            item->setText(oldText);
        } else {
            // Change the data.
            item->setData(Qt::UserRole, text);
        }
    }
}
<|endoftext|>