<commit_before>#include <QtCore>
#include <QtWidgets>
#include <random>

#include "window.h"
#include "soundchanges.h"
#include "highlighter.h"
#include "affixerdialog.h"

Window::Window()
{
    QWidget *mainwidget = new QWidget;
    setCentralWidget(mainwidget);

    m_layout = new QHBoxLayout;
    m_leftlayout = new QVBoxLayout;
    m_midlayout = new QVBoxLayout;
    m_syllableseperatorlayout = new QHBoxLayout;
    mainwidget->setLayout(m_layout);

    QFont font("Courier", 10);
    font.setFixedPitch(true);

    m_categories = new QPlainTextEdit;
    m_categories->setFont(font);
    m_leftlayout->addWidget(m_categories, 2);

    m_rewrites = new QPlainTextEdit;
    m_leftlayout->addWidget(m_rewrites, 1);

    m_layout->addLayout(m_leftlayout);

    m_rules = new QPlainTextEdit;
    m_rules->setFont(font);
    m_highlighter = new Highlighter(m_rules->document());
    m_layout->addWidget(m_rules);

    m_words = new QPlainTextEdit;
    m_layout->addWidget(m_words);

    m_apply = new QPushButton("Apply");
    m_midlayout->addWidget(m_apply);

    m_showChangedWords = new QCheckBox("Show changed words");
    m_showChangedWords->setChecked(true);
    m_midlayout->addWidget(m_showChangedWords);

    m_reportChanges = new QCheckBox("Report which rules apply");
    m_midlayout->addWidget(m_reportChanges);

    m_doBackwards = new QCheckBox("Rewrite on output");
    m_midlayout->addWidget(m_doBackwards);

    const QChar arrow(0x2192);
    m_formatgroup = new QGroupBox("Output format");
    m_plainformat = new QRadioButton("output > gloss");
    m_arrowformat = new QRadioButton(QString("input %1 output").arg(arrow));
    m_squareinputformat = new QRadioButton("output [input]");
    m_squareglossformat = new QRadioButton("output [gloss]");
    m_arrowglossformat = new QRadioButton(QString("input %1 output [gloss]").arg(arrow));
    QVBoxLayout *fbox = new QVBoxLayout;
    fbox->addWidget(m_plainformat);
    fbox->addWidget(m_arrowformat);
    fbox->addWidget(m_squareinputformat);
    fbox->addWidget(m_squareglossformat);
    fbox->addWidget(m_arrowglossformat);
    m_plainformat->setChecked(true);
    m_formatgroup->setLayout(fbox);
    m_midlayout->addWidget(m_formatgroup);

    m_midlayout->addStretch();

    m_syllabify = new QLineEdit;
    m_midlayout->addWidget(m_syllabify);

    m_syllableseperatorlabel = new QLabel("Syllable seperator: ");
    m_syllableseperatorlayout->addWidget(m_syllableseperatorlabel);

    m_syllableseperator = new QLineEdit;
    m_syllableseperator->setMaxLength(1);
    // make width equal to 1em so it's clear the textbox only accepts 1 char
    // we multiply by 2 because we also need some space on the side
    m_syllableseperator->setFixedWidth(m_syllableseperator->fontMetrics().width('M') * 2);
    m_syllableseperator->setText("-");
    m_syllableseperator->setAlignment(Qt::AlignCenter);
    m_syllableseperatorlayout->addWidget(m_syllableseperator);

    m_syllableseperatorlayout->addStretch();
    m_syllableseperatorlayout->setSpacing(0);

    m_midlayout->addLayout(m_syllableseperatorlayout);
    m_layout->addLayout(m_midlayout);

    m_results = new QTextEdit;
    m_results->setReadOnly(true);
    m_layout->addWidget(m_results);

    m_categorieslist = new QMap<QChar, QList<QChar>>();

    connect(m_categories, &QPlainTextEdit::textChanged, this, &Window::UpdateCategories);
    connect(m_apply, &QPushButton::clicked, this, &Window::DoSoundChanges);

    fileMenu = menuBar()->addMenu("File");
    fileMenu->addAction("Open .esc file", this, &Window::OpenEsc);
    fileMenu->addAction("Open .lex file", this, &Window::OpenLex);
    fileMenu->addAction("Save .esc file", this, &Window::SaveEsc);
    fileMenu->addAction("Save .lex file", this, &Window::SaveLex);

    toolsMenu = menuBar()->addMenu("Tools");
    toolsMenu->addAction("Affixer", this, &Window::LaunchAffixer);

    helpMenu = menuBar()->addMenu("Help");
    helpMenu->addAction("About", this, &Window::LaunchAboutBox);
    helpMenu->addAction("About Qt", this, &Window::LaunchAboutQt);

    if (QCoreApplication::arguments().length() > 1)
    {
        RealOpenEsc(QCoreApplication::arguments().at(1));
    }
}

void Window::DoSoundChanges()
{
    QStringList result;
    QString syllabifyregexp(SoundChanges::PreProcessRegexp(m_syllabify->text(), *m_categorieslist));
    QString report;
    for (QString word : m_words->toPlainText().split('\n'))
    {
        QString gloss = "";
        bool hasGloss = false;

        if (word.split('>').length() > 1)
        {
            hasGloss = true;
            QStringList split = word.split('>');
            gloss = split.at(1);
            word = split.at(0);
        }

        QString changed = "";
        for (QString subword : word.split(' ', QString::SkipEmptyParts))
        {
            QString subchanged = ApplyRewrite(subword);

            for (QString change : ApplyRewrite(m_rules->toPlainText()).split('\n', QString::SkipEmptyParts))
            {
                QStringList splitchange = change.replace(QRegularExpression(R"(\*.*)"), "").split(' ', QString::SkipEmptyParts);
                if (splitchange.length() == 0) continue;
                QString _change;
                int prob = 100;
                if (splitchange.length() > 1)
                {
                    _change = splitchange.at(1);
                    switch (splitchange.at(0).at(0).toLatin1())
                    {
                    case 'x':
                        subchanged = SoundChanges::Syllabify(syllabifyregexp, subchanged, m_syllableseperator->text().at(0));
                    case '?':
                        bool ok;
                        int _prob = QString(splitchange.at(0).mid(1)).toInt(&ok);
                        if (ok)
                        {
                            prob = _prob;
                        }
                    }
                }
                else _change = splitchange.at(0);
                QString before = subchanged;
                subchanged = SoundChanges::ApplyChange(subchanged, _change, *m_categorieslist, prob);
                subchanged.remove(m_syllableseperator->text().at(0));
                if (subchanged != before)
                    report.append(QString("%1 changed %2 to %3
").arg(_change, before, subchanged));
            }

            if (m_doBackwards->isChecked()) subchanged = ApplyRewrite(subchanged, true);
            if (m_showChangedWords->isChecked() && subchanged != subword) subchanged = QString("").append(subchanged).append("");

            if (changed.length() == 0) changed =        subchanged;
            else                       changed += ' ' + subchanged;
        }
        changed = FormatOutput(word.trimmed(), changed.trimmed(), gloss.trimmed(), hasGloss);
        result.append(changed);
    }
    m_results->setHtml(result.join("
"));

    if (m_reportChanges->isChecked())
    {
        QMessageBox *msgBox = new QMessageBox(this);
        msgBox->setAttribute(Qt::WA_DeleteOnClose);
        msgBox->setText(report);
        msgBox->setWindowModality(Qt::NonModal);
        msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
        msgBox->show();
    }
}

void Window::UpdateCategories()
{
    bool first = true;
    for (QString line : ApplyRewrite(m_categories->toPlainText()).split('\n', QString::SkipEmptyParts))
    {
        if (!QRegularExpression("^.=.+$").match(line).hasMatch()) continue;
        QStringList parts = line.split("=");

        if (first) { m_categorieslist = new QMap<QChar, QList<QChar>>; first = false; }
        QList<QChar> phonemes;
        for (QChar c : parts.at(1))
        {
            if (m_categorieslist->contains(c)) phonemes.append(m_categorieslist->value(c));
            else phonemes.append(c);
        }
        m_categorieslist->insert(parts.at(0).at(0), phonemes);
    }

    QString regexp("");
    first = true;
    for (QChar key : m_categorieslist->keys())
    {
        if (!first) regexp.append("|");
        if (first) first = false;
        regexp.append(key);
    }
    m_highlighter->MakeHighlightingRules(regexp);
    m_highlighter->rehighlight();
}

QString Window::ApplyRewrite(QString str, bool backwards)
{
    QString rewritten = str;
    QString s = m_rewrites->toPlainText();
    for (QString line : m_rewrites->toPlainText().split('\n', QString::SkipEmptyParts))
    {
        QStringList parts = line.split('>');
        if (parts.length() != 2) continue;
        if (backwards) rewritten.replace(parts.at(1), parts.at(0));
        else           rewritten.replace(parts.at(0), parts.at(1));
    }
    return rewritten;
}

QString Window::FormatOutput(QString in, QString out, QString gloss, bool hasGloss)
{
    const QChar arrow(0x2192);
    if (m_plainformat->isChecked())
    {
        if (hasGloss) return QString("%1 > %2").arg(out, gloss);
        else         return out;
    }
    else if (m_arrowformat->isChecked())
    {
        return QString("%1 %2 %3").arg(in, arrow, out);
    }
    else if (m_squareinputformat->isChecked())
    {
        return QString("%1 [%2]").arg(out, in);
    }
    else if (m_squareglossformat->isChecked())
    {
        if (hasGloss) return QString("%1 [%2]").arg(out, gloss);
        else         return out;
    }
    else if (m_arrowglossformat->isChecked())
    {
        if (hasGloss) return QString("%1 %2 %3 [%4]").arg(in, arrow, out, gloss);
        else         return QString("%1 %2 %3").arg(in, arrow, out);
    }
    return out;
}

void Window::LaunchAffixer()
{
    AffixerDialog *affixer = new AffixerDialog;
    affixer->show();
    connect(affixer, &AffixerDialog::addText, this, &Window::AddFromAffixer);
}

void Window::AddFromAffixer(QStringList words, AffixerDialog::PlaceToAdd placeToAdd)
{
    QString textToAdd = words.join('\n');
    switch (placeToAdd)
    {
    case AffixerDialog::PlaceToAdd::AddToStart:
        m_words->setPlainText(textToAdd.append('\n').append(m_words->toPlainText()));
        break;
    case AffixerDialog::PlaceToAdd::AddToEnd:
        m_words->setPlainText(m_words->toPlainText().append('\n').append(textToAdd));
        break;
    case AffixerDialog::PlaceToAdd::AddAtCursor:
        m_words->insertPlainText(QString('\n').append(textToAdd).append('\n'));
        break;
    case AffixerDialog::PlaceToAdd::Overwrite:
        m_words->setPlainText(textToAdd);
        break;
    }
}

void Window::OpenEsc()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open .esc File", QString(), "exSCA Files (*.esc);;All files (*.*)");
    RealOpenEsc(fileName);
}

void Window::RealOpenEsc(QString fileName)
{
    QFile file(fileName);

    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::warning(this, "Could Not Open File", "The file could not be opened");
        return;
    }

    QTextStream in(&file);
    in.setCodec("UTF-8");

    QStringList cats, rules, rews;

    while (!in.atEnd())
    {
        QString line = in.readLine();
        if (line.contains('=')) cats.append(line);
        else if (line.contains('>') && !line.contains('/')) rews.append(line);
        else rules.append(line);
    }

    m_categories->setPlainText(cats.join('\n'));
    m_rules->setPlainText(rules.join('\n'));
    m_rewrites->setPlainText(rews.join('\n'));

    file.close();
}

void Window::OpenLex()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open .esc File", QString(), "exSCA word files (*.lex);;All files (*.*)");
    QFile file(fileName);

    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::warning(this, "Could Not Open File", "The file could not be opened");
        return;
    }

    QTextStream in(&file);
    in.setCodec("UTF-8");

    QStringList words;
    while (!in.atEnd()) words.append(in.readLine());

    m_words->setPlainText(words.join('\n'));

    file.close();
}

void Window::SaveEsc()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Open .esc File", QString(), "exSCA Files (*.esc);;All files (*.*)");
    QFile file(fileName);

    if (!file.open(QIODevice::WriteOnly))
    {
        QMessageBox::warning(this, "Could Not Open File", "The file could not be opened");
        return;
    }

    QTextStream out(&file);
    out.setCodec("UTF-8");
    out << m_categories->toPlainText().toUtf8() << endl;
    out << m_rewrites  ->toPlainText().toUtf8() << endl;
    out << m_rules     ->toPlainText().toUtf8() << endl;

    file.close();
}

void Window::SaveLex()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open .esc File", QString(), "exSCA word files (*.lex);;All files (*.*)");
    QFile file(fileName);

    if (!file.open(QIODevice::WriteOnly))
    {
        QMessageBox::warning(this, "Could Not Open File", "The file could not be opened");
        return;
    }

    QTextStream out(&file);
    out.setCodec("UTF-8");
    for (QString word : m_words->toPlainText().split('\n'))
    {
        out << word.toUtf8() << endl;
    }

    file.close();
}

void Window::LaunchAboutBox()
{
    QMessageBox::about(this, "About exSCA", "exSCA
Version 2.0.2
Copyright © Brad Neimann 2017");
}

void Window::LaunchAboutQt()
{
    QMessageBox::aboutQt(this, "About Qt");
}<commit_msg>Change Open/Save file dialogs<commit_after>#include <QtCore>
#include <QtWidgets>
#include <random>

#include "window.h"
#include "soundchanges.h"
#include "highlighter.h"
#include "affixerdialog.h"

Window::Window()
{
    QWidget *mainwidget = new QWidget;
    setCentralWidget(mainwidget);

    m_layout = new QHBoxLayout;
    m_leftlayout = new QVBoxLayout;
    m_midlayout = new QVBoxLayout;
    m_syllableseperatorlayout = new QHBoxLayout;
    mainwidget->setLayout(m_layout);

    QFont font("Courier", 10);
    font.setFixedPitch(true);

    m_categories = new QPlainTextEdit;
    m_categories->setFont(font);
    m_leftlayout->addWidget(m_categories, 2);

    m_rewrites = new QPlainTextEdit;
    m_leftlayout->addWidget(m_rewrites, 1);

    m_layout->addLayout(m_leftlayout);

    m_rules = new QPlainTextEdit;
    m_rules->setFont(font);
    m_highlighter = new Highlighter(m_rules->document());
    m_layout->addWidget(m_rules);

    m_words = new QPlainTextEdit;
    m_layout->addWidget(m_words);

    m_apply = new QPushButton("Apply");
    m_midlayout->addWidget(m_apply);

    m_showChangedWords = new QCheckBox("Show changed words");
    m_showChangedWords->setChecked(true);
    m_midlayout->addWidget(m_showChangedWords);

    m_reportChanges = new QCheckBox("Report which rules apply");
    m_midlayout->addWidget(m_reportChanges);

    m_doBackwards = new QCheckBox("Rewrite on output");
    m_midlayout->addWidget(m_doBackwards);

    const QChar arrow(0x2192);
    m_formatgroup = new QGroupBox("Output format");
    m_plainformat = new QRadioButton("output > gloss");
    m_arrowformat = new QRadioButton(QString("input %1 output").arg(arrow));
    m_squareinputformat = new QRadioButton("output [input]");
    m_squareglossformat = new QRadioButton("output [gloss]");
    m_arrowglossformat = new QRadioButton(QString("input %1 output [gloss]").arg(arrow));
    QVBoxLayout *fbox = new QVBoxLayout;
    fbox->addWidget(m_plainformat);
    fbox->addWidget(m_arrowformat);
    fbox->addWidget(m_squareinputformat);
    fbox->addWidget(m_squareglossformat);
    fbox->addWidget(m_arrowglossformat);
    m_plainformat->setChecked(true);
    m_formatgroup->setLayout(fbox);
    m_midlayout->addWidget(m_formatgroup);

    m_midlayout->addStretch();

    m_syllabify = new QLineEdit;
    m_midlayout->addWidget(m_syllabify);

    m_syllableseperatorlabel = new QLabel("Syllable seperator: ");
    m_syllableseperatorlayout->addWidget(m_syllableseperatorlabel);

    m_syllableseperator = new QLineEdit;
    m_syllableseperator->setMaxLength(1);
    // make width equal to 1em so it's clear the textbox only accepts 1 char
    // we multiply by 2 because we also need some space on the side
    m_syllableseperator->setFixedWidth(m_syllableseperator->fontMetrics().width('M') * 2);
    m_syllableseperator->setText("-");
    m_syllableseperator->setAlignment(Qt::AlignCenter);
    m_syllableseperatorlayout->addWidget(m_syllableseperator);

    m_syllableseperatorlayout->addStretch();
    m_syllableseperatorlayout->setSpacing(0);

    m_midlayout->addLayout(m_syllableseperatorlayout);
    m_layout->addLayout(m_midlayout);

    m_results = new QTextEdit;
    m_results->setReadOnly(true);
    m_layout->addWidget(m_results);

    m_categorieslist = new QMap<QChar, QList<QChar>>();

    connect(m_categories, &QPlainTextEdit::textChanged, this, &Window::UpdateCategories);
    connect(m_apply, &QPushButton::clicked, this, &Window::DoSoundChanges);

    fileMenu = menuBar()->addMenu("File");
    fileMenu->addAction("Open .esc file", this, &Window::OpenEsc);
    fileMenu->addAction("Open .lex file", this, &Window::OpenLex);
    fileMenu->addAction("Save .esc file", this, &Window::SaveEsc);
    fileMenu->addAction("Save .lex file", this, &Window::SaveLex);

    toolsMenu = menuBar()->addMenu("Tools");
    toolsMenu->addAction("Affixer", this, &Window::LaunchAffixer);

    helpMenu = menuBar()->addMenu("Help");
    helpMenu->addAction("About", this, &Window::LaunchAboutBox);
    helpMenu->addAction("About Qt", this, &Window::LaunchAboutQt);

    if (QCoreApplication::arguments().length() > 1)
    {
        RealOpenEsc(QCoreApplication::arguments().at(1));
    }
}

void Window::DoSoundChanges()
{
    QStringList result;
    QString syllabifyregexp(SoundChanges::PreProcessRegexp(m_syllabify->text(), *m_categorieslist));
    QString report;
    for (QString word : m_words->toPlainText().split('\n'))
    {
        QString gloss = "";
        bool hasGloss = false;

        if (word.split('>').length() > 1)
        {
            hasGloss = true;
            QStringList split = word.split('>');
            gloss = split.at(1);
            word = split.at(0);
        }

        QString changed = "";
        for (QString subword : word.split(' ', QString::SkipEmptyParts))
        {
            QString subchanged = ApplyRewrite(subword);

            for (QString change : ApplyRewrite(m_rules->toPlainText()).split('\n', QString::SkipEmptyParts))
            {
                QStringList splitchange = change.replace(QRegularExpression(R"(\*.*)"), "").split(' ', QString::SkipEmptyParts);
                if (splitchange.length() == 0) continue;
                QString _change;
                int prob = 100;
                if (splitchange.length() > 1)
                {
                    _change = splitchange.at(1);
                    switch (splitchange.at(0).at(0).toLatin1())
                    {
                    case 'x':
                        subchanged = SoundChanges::Syllabify(syllabifyregexp, subchanged, m_syllableseperator->text().at(0));
                    case '?':
                        bool ok;
                        int _prob = QString(splitchange.at(0).mid(1)).toInt(&ok);
                        if (ok)
                        {
                            prob = _prob;
                        }
                    }
                }
                else _change = splitchange.at(0);
                QString before = subchanged;
                subchanged = SoundChanges::ApplyChange(subchanged, _change, *m_categorieslist, prob);
                subchanged.remove(m_syllableseperator->text().at(0));
                if (subchanged != before)
                    report.append(QString("%1 changed %2 to %3
").arg(_change, before, subchanged));
            }

            if (m_doBackwards->isChecked()) subchanged = ApplyRewrite(subchanged, true);
            if (m_showChangedWords->isChecked() && subchanged != subword) subchanged = QString("").append(subchanged).append("");

            if (changed.length() == 0) changed =        subchanged;
            else                       changed += ' ' + subchanged;
        }
        changed = FormatOutput(word.trimmed(), changed.trimmed(), gloss.trimmed(), hasGloss);
        result.append(changed);
    }
    m_results->setHtml(result.join("
"));

    if (m_reportChanges->isChecked())
    {
        QMessageBox *msgBox = new QMessageBox(this);
        msgBox->setAttribute(Qt::WA_DeleteOnClose);
        msgBox->setText(report);
        msgBox->setWindowModality(Qt::NonModal);
        msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
        msgBox->show();
    }
}

void Window::UpdateCategories()
{
    bool first = true;
    for (QString line : ApplyRewrite(m_categories->toPlainText()).split('\n', QString::SkipEmptyParts))
    {
        if (!QRegularExpression("^.=.+$").match(line).hasMatch()) continue;
        QStringList parts = line.split("=");

        if (first) { m_categorieslist = new QMap<QChar, QList<QChar>>; first = false; }
        QList<QChar> phonemes;
        for (QChar c : parts.at(1))
        {
            if (m_categorieslist->contains(c)) phonemes.append(m_categorieslist->value(c));
            else phonemes.append(c);
        }
        m_categorieslist->insert(parts.at(0).at(0), phonemes);
    }

    QString regexp("");
    first = true;
    for (QChar key : m_categorieslist->keys())
    {
        if (!first) regexp.append("|");
        if (first) first = false;
        regexp.append(key);
    }
    m_highlighter->MakeHighlightingRules(regexp);
    m_highlighter->rehighlight();
}

QString Window::ApplyRewrite(QString str, bool backwards)
{
    QString rewritten = str;
    QString s = m_rewrites->toPlainText();
    for (QString line : m_rewrites->toPlainText().split('\n', QString::SkipEmptyParts))
    {
        QStringList parts = line.split('>');
        if (parts.length() != 2) continue;
        if (backwards) rewritten.replace(parts.at(1), parts.at(0));
        else           rewritten.replace(parts.at(0), parts.at(1));
    }
    return rewritten;
}

QString Window::FormatOutput(QString in, QString out, QString gloss, bool hasGloss)
{
    const QChar arrow(0x2192);
    if (m_plainformat->isChecked())
    {
        if (hasGloss) return QString("%1 > %2").arg(out, gloss);
        else         return out;
    }
    else if (m_arrowformat->isChecked())
    {
        return QString("%1 %2 %3").arg(in, arrow, out);
    }
    else if (m_squareinputformat->isChecked())
    {
        return QString("%1 [%2]").arg(out, in);
    }
    else if (m_squareglossformat->isChecked())
    {
        if (hasGloss) return QString("%1 [%2]").arg(out, gloss);
        else         return out;
    }
    else if (m_arrowglossformat->isChecked())
    {
        if (hasGloss) return QString("%1 %2 %3 [%4]").arg(in, arrow, out, gloss);
        else         return QString("%1 %2 %3").arg(in, arrow, out);
    }
    return out;
}

void Window::LaunchAffixer()
{
    AffixerDialog *affixer = new AffixerDialog;
    affixer->show();
    connect(affixer, &AffixerDialog::addText, this, &Window::AddFromAffixer);
}

void Window::AddFromAffixer(QStringList words, AffixerDialog::PlaceToAdd placeToAdd)
{
    QString textToAdd = words.join('\n');
    switch (placeToAdd)
    {
    case AffixerDialog::PlaceToAdd::AddToStart:
        m_words->setPlainText(textToAdd.append('\n').append(m_words->toPlainText()));
        break;
    case AffixerDialog::PlaceToAdd::AddToEnd:
        m_words->setPlainText(m_words->toPlainText().append('\n').append(textToAdd));
        break;
    case AffixerDialog::PlaceToAdd::AddAtCursor:
        m_words->insertPlainText(QString('\n').append(textToAdd).append('\n'));
        break;
    case AffixerDialog::PlaceToAdd::Overwrite:
        m_words->setPlainText(textToAdd);
        break;
    }
}

void Window::OpenEsc()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open .esc File", QString(), "exSCA Files (*.esc);;All files (*.*)");
    RealOpenEsc(fileName);
}

void Window::RealOpenEsc(QString fileName)
{
    QFile file(fileName);

    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::warning(this, "Could Not Open File", "The file could not be opened");
        return;
    }

    QTextStream in(&file);
    in.setCodec("UTF-8");

    QStringList cats, rules, rews;

    while (!in.atEnd())
    {
        QString line = in.readLine();
        if (line.contains('=')) cats.append(line);
        else if (line.contains('>') && !line.contains('/')) rews.append(line);
        else rules.append(line);
    }

    m_categories->setPlainText(cats.join('\n'));
    m_rules->setPlainText(rules.join('\n'));
    m_rewrites->setPlainText(rews.join('\n'));

    file.close();
}

void Window::OpenLex()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open .lex File", QString(), "exSCA word files (*.lex);;All files (*.*)");
    QFile file(fileName);

    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::warning(this, "Could Not Open File", "The file could not be opened");
        return;
    }

    QTextStream in(&file);
    in.setCodec("UTF-8");

    QStringList words;
    while (!in.atEnd()) words.append(in.readLine());

    m_words->setPlainText(words.join('\n'));

    file.close();
}

void Window::SaveEsc()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Save As .esc File", QString(), "exSCA Files (*.esc);;All files (*.*)");
    QFile file(fileName);

    if (!file.open(QIODevice::WriteOnly))
    {
        QMessageBox::warning(this, "Could Not Open File", "The file could not be opened");
        return;
    }

    QTextStream out(&file);
    out.setCodec("UTF-8");
    out << m_categories->toPlainText().toUtf8() << endl;
    out << m_rewrites  ->toPlainText().toUtf8() << endl;
    out << m_rules     ->toPlainText().toUtf8() << endl;

    file.close();
}

void Window::SaveLex()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Save As .lex File", QString(), "exSCA word files (*.lex);;All files (*.*)");
    QFile file(fileName);

    if (!file.open(QIODevice::WriteOnly))
    {
        QMessageBox::warning(this, "Could Not Open File", "The file could not be opened");
        return;
    }

    QTextStream out(&file);
    out.setCodec("UTF-8");
    for (QString word : m_words->toPlainText().split('\n'))
    {
        out << word.toUtf8() << endl;
    }

    file.close();
}

void Window::LaunchAboutBox()
{
    QMessageBox::about(this, "About exSCA", "exSCA
Version 2.0.2
Copyright © Brad Neimann 2017");
}

void Window::LaunchAboutQt()
{
    QMessageBox::aboutQt(this, "About Qt");
}<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: tabletree.hxx,v $
 *
 *  $Revision: 1.13 $
 *
 *  last change: $Author: kz $ $Date: 2005-01-21 17:19:20 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc..
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _DBAUI_TABLETREE_HXX_
#define _DBAUI_TABLETREE_HXX_

#ifndef _DBAUI_MARKTREE_HXX_
#include "marktree.hxx"
#endif

#ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUE_HPP_
#include <com/sun/star/beans/PropertyValue.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMEACCESS_HPP_
#include <com/sun/star/container/XNameAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATABASEMETADATA_HPP_
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
#include <com/sun/star/sdbc/XConnection.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDRIVER_HPP_
#include <com/sun/star/sdbc/XDriver.hpp>
#endif

//.........................................................................
namespace dbaui
{
//.........................................................................

//========================================================================
//= OTableTreeListBox
//========================================================================
class OTableTreeListBox : public OMarkableTreeListBox
{
protected:
    Image           m_aTableImage;
    Image           m_aViewImage;

    sal_Bool        m_bVirtualRoot; // should the first entry be visible

public:
    OTableTreeListBox( Window* pParent
                        ,const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB
                        ,sal_Bool _bHiContrast,WinBits nWinStyle = NULL,sal_Bool _bVirtualRoot=sal_True );
    OTableTreeListBox( Window* pParent
                        ,const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB
                        , const ResId& rResId,sal_Bool _bHiContrast,sal_Bool _bVirtualRoot=sal_True );

    typedef ::std::pair< ::rtl::OUString,sal_Bool>  TTableViewName;
    typedef ::std::vector< TTableViewName >         TNames;
    /** call when HiContrast change.
    */
    virtual void notifyHiContrastChanged();

    /** fill the table list with the tables belonging to the connection described by the parameters
        @param _xConnection
            the connection, which must support the service com.sun.star.sdb.Connection
        @throws
            <type scope="com::sun::star::sdbc">SQLException</type> if no connection could be created
    */
    void
            UpdateTableList(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _xConnection
            )   throw(::com::sun::star::sdbc::SQLException);

    /** fill the table list with the tables and views determined by the two given containers.
        The views sequence is used to determine which table is of type view.
        @param      _rxConnMetaData meta data describing the connection where you got the object names from. Must not be NULL.
                                    Used to split the full qualified names into it's parts.
        @param      _rTables        table/view sequence
        @param      _rViews         view sequence
    */
    void    UpdateTableList(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDatabaseMetaData >& _rxConnMetaData,
                const ::com::sun::star::uno::Sequence< ::rtl::OUString>& _rTables,
                const ::com::sun::star::uno::Sequence< ::rtl::OUString>& _rViews
            );

    /** to be used if a foreign instance added a table
    */
    SvLBoxEntry* addedTable(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConn,
                const ::rtl::OUString& _rName,
                const ::com::sun::star::uno::Any& _rObject
            );

    /** to be used if a foreign instance removed a table
    */
    void    removedTable(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConn,
                const ::rtl::OUString& _rName
            );

    SvLBoxEntry*    getEntryByQualifiedName(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConn,
                const ::rtl::OUString& _rName
            );

    SvLBoxEntry*    getAllObjectsEntry() const;

    /** does a wildcard check of the given entry

There are two different 'checked' states: If the user checks all children of an entry, this is different
        from checking the entry itself. The second is called 'wildcard' checking, 'cause in the resulting
        table filter it's represented by a wildcard.

    */
    void            checkWildcard(SvLBoxEntry* _pEntry);

    /** determine if the given entry is 'wildcard checked'
        @see checkWildcard
    */
    sal_Bool        isWildcardChecked(SvLBoxEntry* _pEntry) const;

protected:
    virtual void InitEntry(SvLBoxEntry* _pEntry, const XubString& _rString, const Image& _rCollapsedBitmap, const Image& _rExpandedBitmap);

    virtual void checkedButton_noBroadcast(SvLBoxEntry* _pEntry);

    void implEmphasize(SvLBoxEntry* _pEntry, sal_Bool _bChecked, sal_Bool _bUpdateDescendants = sal_True, sal_Bool _bUpdateAncestors = sal_True);

    SvLBoxEntry* implAddEntry(
            const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDatabaseMetaData >& _rxConnMetaData,
            const ::rtl::OUString& _rTableName,
            const Image& _rImage,
            SvLBoxEntry* _pParentEntry,
            sal_Int32 _nType,
            sal_Bool _bCheckName = sal_True
        );

    sal_Bool haveVirtualRoot() const { return m_bVirtualRoot; }

    /** fill the table list with the tables and views determined by the two given containers
        @param      _rxConnMetaData meta data describing the connection where you got the object names from. Must not be NULL.
                                    Used to split the full qualified names into it's parts.
        @param      _rTables        table/view sequence, the second argument is <TRUE/> if it is a table, otherwise it is a view.
    */
    void    UpdateTableList(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDatabaseMetaData >& _rxConnMetaData,
                const TNames& _rTables
            );

};

//.........................................................................
}   // namespace dbaui
//.........................................................................

#endif // _DBAUI_TABLETREE_HXX_

<commit_msg>INTEGRATION: CWS ooo19126 (1.13.142); FILE MERGED 2005/09/05 17:35:01 rt 1.13.142.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: tabletree.hxx,v $
 *
 *  $Revision: 1.14 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 16:04:35 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _DBAUI_TABLETREE_HXX_
#define _DBAUI_TABLETREE_HXX_

#ifndef _DBAUI_MARKTREE_HXX_
#include "marktree.hxx"
#endif

#ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUE_HPP_
#include <com/sun/star/beans/PropertyValue.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMEACCESS_HPP_
#include <com/sun/star/container/XNameAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATABASEMETADATA_HPP_
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
#include <com/sun/star/sdbc/XConnection.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDRIVER_HPP_
#include <com/sun/star/sdbc/XDriver.hpp>
#endif

//.........................................................................
namespace dbaui
{
//.........................................................................

//========================================================================
//= OTableTreeListBox
//========================================================================
class OTableTreeListBox : public OMarkableTreeListBox
{
protected:
    Image           m_aTableImage;
    Image           m_aViewImage;

    sal_Bool        m_bVirtualRoot; // should the first entry be visible

public:
    OTableTreeListBox( Window* pParent
                        ,const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB
                        ,sal_Bool _bHiContrast,WinBits nWinStyle = NULL,sal_Bool _bVirtualRoot=sal_True );
    OTableTreeListBox( Window* pParent
                        ,const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB
                        , const ResId& rResId,sal_Bool _bHiContrast,sal_Bool _bVirtualRoot=sal_True );

    typedef ::std::pair< ::rtl::OUString,sal_Bool>  TTableViewName;
    typedef ::std::vector< TTableViewName >         TNames;
    /** call when HiContrast change.
    */
    virtual void notifyHiContrastChanged();

    /** fill the table list with the tables belonging to the connection described by the parameters
        @param _xConnection
            the connection, which must support the service com.sun.star.sdb.Connection
        @throws
            <type scope="com::sun::star::sdbc">SQLException</type> if no connection could be created
    */
    void
            UpdateTableList(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _xConnection
            )   throw(::com::sun::star::sdbc::SQLException);

    /** fill the table list with the tables and views determined by the two given containers.
        The views sequence is used to determine which table is of type view.
        @param      _rxConnMetaData meta data describing the connection where you got the object names from. Must not be NULL.
                                    Used to split the full qualified names into it's parts.
        @param      _rTables        table/view sequence
        @param      _rViews         view sequence
    */
    void    UpdateTableList(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDatabaseMetaData >& _rxConnMetaData,
                const ::com::sun::star::uno::Sequence< ::rtl::OUString>& _rTables,
                const ::com::sun::star::uno::Sequence< ::rtl::OUString>& _rViews
            );

    /** to be used if a foreign instance added a table
    */
    SvLBoxEntry* addedTable(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConn,
                const ::rtl::OUString& _rName,
                const ::com::sun::star::uno::Any& _rObject
            );

    /** to be used if a foreign instance removed a table
    */
    void    removedTable(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConn,
                const ::rtl::OUString& _rName
            );

    SvLBoxEntry*    getEntryByQualifiedName(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConn,
                const ::rtl::OUString& _rName
            );

    SvLBoxEntry*    getAllObjectsEntry() const;

    /** does a wildcard check of the given entry

There are two different 'checked' states: If the user checks all children of an entry, this is different
        from checking the entry itself. The second is called 'wildcard' checking, 'cause in the resulting
        table filter it's represented by a wildcard.

    */
    void            checkWildcard(SvLBoxEntry* _pEntry);

    /** determine if the given entry is 'wildcard checked'
        @see checkWildcard
    */
    sal_Bool        isWildcardChecked(SvLBoxEntry* _pEntry) const;

protected:
    virtual void InitEntry(SvLBoxEntry* _pEntry, const XubString& _rString, const Image& _rCollapsedBitmap, const Image& _rExpandedBitmap);

    virtual void checkedButton_noBroadcast(SvLBoxEntry* _pEntry);

    void implEmphasize(SvLBoxEntry* _pEntry, sal_Bool _bChecked, sal_Bool _bUpdateDescendants = sal_True, sal_Bool _bUpdateAncestors = sal_True);

    SvLBoxEntry* implAddEntry(
            const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDatabaseMetaData >& _rxConnMetaData,
            const ::rtl::OUString& _rTableName,
            const Image& _rImage,
            SvLBoxEntry* _pParentEntry,
            sal_Int32 _nType,
            sal_Bool _bCheckName = sal_True
        );

    sal_Bool haveVirtualRoot() const { return m_bVirtualRoot; }

    /** fill the table list with the tables and views determined by the two given containers
        @param      _rxConnMetaData meta data describing the connection where you got the object names from. Must not be NULL.
                                    Used to split the full qualified names into it's parts.
        @param      _rTables        table/view sequence, the second argument is <TRUE/> if it is a table, otherwise it is a view.
    */
    void    UpdateTableList(
                const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XDatabaseMetaData >& _rxConnMetaData,
                const TNames& _rTables
            );

};

//.........................................................................
}   // namespace dbaui
//.........................................................................

#endif // _DBAUI_TABLETREE_HXX_

<|endoftext|>


<commit_before>#include "aboutdialog.h"
#include "ui_aboutdialog.h"
#include "clientmodel.h"

#include "version.h"

AboutDialog::AboutDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AboutDialog)
{
    ui->setupUi(this);
}

void AboutDialog::setModel(ClientModel *model)
{
    if(model)
    {
        ui->versionLabel->setText(model->formatFullVersion());
    }
}

AboutDialog::~AboutDialog()
{
    delete ui;
}

void AboutDialog::on_buttonBox_accepted()
{
    close();
}
<commit_msg>About dialog: add 32/64 bit info on i386 arches<commit_after>#include "aboutdialog.h"
#include "ui_aboutdialog.h"
#include "clientmodel.h"

#include "version.h"

AboutDialog::AboutDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AboutDialog)
{
    ui->setupUi(this);
}

void AboutDialog::setModel(ClientModel *model)
{
    if(model)
    {
	QString version = "";
	version += model->formatFullVersion();

	/* On x86 add a bit specifier to the version so that users can distinguish between
	 * 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambiguous.
	 */
#if defined(__x86_64__)
	version += " " + tr("(%1-bit)").arg(64);
#elif defined(__i386__)
	version += " " + tr("(%1-bit)").arg(32);
#endif

        ui->versionLabel->setText(version);
    }
}

AboutDialog::~AboutDialog()
{
    delete ui;
}

void AboutDialog::on_buttonBox_accepted()
{
    close();
}
<|endoftext|>


<commit_before>// Copyright (c) 2011-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qt/clientmodel.h>

#include <qt/bantablemodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/peertablemodel.h>
#include <qt/peertablesortproxy.h>

#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <net.h>
#include <netbase.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/time.h>
#include <validation.h>

#include <stdint.h>
#include <functional>

#include <QDebug>
#include <QThread>
#include <QTimer>

static int64_t nLastHeaderTipUpdateNotification = 0;
static int64_t nLastBlockTipUpdateNotification = 0;

ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
    QObject(parent),
    m_node(node),
    optionsModel(_optionsModel),
    peerTableModel(nullptr),
    banTableModel(nullptr),
    m_thread(new QThread(this))
{
    cachedBestHeaderHeight = -1;
    cachedBestHeaderTime = -1;

    peerTableModel = new PeerTableModel(m_node, this);
    m_peer_table_sort_proxy = new PeerTableSortProxy(this);
    m_peer_table_sort_proxy->setSourceModel(peerTableModel);

    banTableModel = new BanTableModel(m_node, this);

    QTimer* timer = new QTimer;
    timer->setInterval(MODEL_UPDATE_DELAY);
    connect(timer, &QTimer::timeout, [this] {
        // no locking required at this point
        // the following calls will acquire the required lock
        Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
        Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
    });
    connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
    connect(m_thread, &QThread::started, [timer] { timer->start(); });
    // move timer to thread so that polling doesn't disturb main event loop
    timer->moveToThread(m_thread);
    m_thread->start();
    QTimer::singleShot(0, timer, []() {
        util::ThreadRename("qt-clientmodl");
    });

    subscribeToCoreSignals();
}

ClientModel::~ClientModel()
{
    unsubscribeFromCoreSignals();

    m_thread->quit();
    m_thread->wait();
}

int ClientModel::getNumConnections(unsigned int flags) const
{
    ConnectionDirection connections = ConnectionDirection::None;

    if(flags == CONNECTIONS_IN)
        connections = ConnectionDirection::In;
    else if (flags == CONNECTIONS_OUT)
        connections = ConnectionDirection::Out;
    else if (flags == CONNECTIONS_ALL)
        connections = ConnectionDirection::Both;

    return m_node.getNodeCount(connections);
}

int ClientModel::getHeaderTipHeight() const
{
    if (cachedBestHeaderHeight == -1) {
        // make sure we initially populate the cache via a cs_main lock
        // otherwise we need to wait for a tip update
        int height;
        int64_t blockTime;
        if (m_node.getHeaderTip(height, blockTime)) {
            cachedBestHeaderHeight = height;
            cachedBestHeaderTime = blockTime;
        }
    }
    return cachedBestHeaderHeight;
}

int64_t ClientModel::getHeaderTipTime() const
{
    if (cachedBestHeaderTime == -1) {
        int height;
        int64_t blockTime;
        if (m_node.getHeaderTip(height, blockTime)) {
            cachedBestHeaderHeight = height;
            cachedBestHeaderTime = blockTime;
        }
    }
    return cachedBestHeaderTime;
}

int ClientModel::getNumBlocks() const
{
    if (m_cached_num_blocks == -1) {
        m_cached_num_blocks = m_node.getNumBlocks();
    }
    return m_cached_num_blocks;
}

uint256 ClientModel::getBestBlockHash()
{
    uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};

    if (!tip.IsNull()) {
        return tip;
    }

    // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
    // The following will lock `cs_main` (and release it), so we must not
    // own `m_cached_tip_mutex` here.
    tip = m_node.getBestBlockHash();

    LOCK(m_cached_tip_mutex);
    // We checked that `m_cached_tip_blocks` is not null above, but then we
    // released the mutex `m_cached_tip_mutex`, so it could have changed in the
    // meantime. Thus, check again.
    if (m_cached_tip_blocks.IsNull()) {
        m_cached_tip_blocks = tip;
    }
    return m_cached_tip_blocks;
}

void ClientModel::updateNumConnections(int numConnections)
{
    Q_EMIT numConnectionsChanged(numConnections);
}

void ClientModel::updateNetworkActive(bool networkActive)
{
    Q_EMIT networkActiveChanged(networkActive);
}

void ClientModel::updateAlert()
{
    Q_EMIT alertsChanged(getStatusBarWarnings());
}

enum BlockSource ClientModel::getBlockSource() const
{
    if (m_node.getReindex())
        return BlockSource::REINDEX;
    else if (m_node.getImporting())
        return BlockSource::DISK;
    else if (getNumConnections() > 0)
        return BlockSource::NETWORK;

    return BlockSource::NONE;
}

QString ClientModel::getStatusBarWarnings() const
{
    return QString::fromStdString(m_node.getWarnings().translated);
}

OptionsModel *ClientModel::getOptionsModel()
{
    return optionsModel;
}

PeerTableModel *ClientModel::getPeerTableModel()
{
    return peerTableModel;
}

PeerTableSortProxy* ClientModel::peerTableSortProxy()
{
    return m_peer_table_sort_proxy;
}

BanTableModel *ClientModel::getBanTableModel()
{
    return banTableModel;
}

QString ClientModel::formatFullVersion() const
{
    return QString::fromStdString(FormatFullVersion());
}

QString ClientModel::formatSubVersion() const
{
    return QString::fromStdString(strSubVersion);
}

bool ClientModel::isReleaseVersion() const
{
    return CLIENT_VERSION_IS_RELEASE;
}

QString ClientModel::formatClientStartupTime() const
{
    return QDateTime::fromSecsSinceEpoch(GetStartupTime()).toString();
}

QString ClientModel::dataDir() const
{
    return GUIUtil::PathToQString(gArgs.GetDataDirNet());
}

QString ClientModel::blocksDir() const
{
    return GUIUtil::PathToQString(gArgs.GetBlocksDirPath());
}

void ClientModel::updateBanlist()
{
    banTableModel->refresh();
}

// Handlers for core signals
static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
{
    // emits signal "showProgress"
    bool invoked = QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
                              Q_ARG(QString, QString::fromStdString(title)),
                              Q_ARG(int, nProgress));
    assert(invoked);
}

static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
{
    // Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
    bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
                              Q_ARG(int, newNumConnections));
    assert(invoked);
}

static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
{
    bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
                              Q_ARG(bool, networkActive));
    assert(invoked);
}

static void NotifyAlertChanged(ClientModel *clientmodel)
{
    qDebug() << "NotifyAlertChanged";
    bool invoked = QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
    assert(invoked);
}

static void BannedListChanged(ClientModel *clientmodel)
{
    qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
    bool invoked = QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
    assert(invoked);
}

static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
{
    if (fHeader) {
        // cache best headers time and height to reduce future cs_main locks
        clientmodel->cachedBestHeaderHeight = tip.block_height;
        clientmodel->cachedBestHeaderTime = tip.block_time;
    } else {
        clientmodel->m_cached_num_blocks = tip.block_height;
        WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
    }

    // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
    const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX;
    const int64_t now = throttle ? GetTimeMillis() : 0;
    int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
    if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) {
        return;
    }

    bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
        Q_ARG(int, tip.block_height),
        Q_ARG(QDateTime, QDateTime::fromSecsSinceEpoch(tip.block_time)),
        Q_ARG(double, verificationProgress),
        Q_ARG(bool, fHeader),
        Q_ARG(SynchronizationState, sync_state));
    assert(invoked);
    nLastUpdateNotification = now;
}

void ClientModel::subscribeToCoreSignals()
{
    // Connect signals to client
    m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
    m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(std::bind(NotifyNumConnectionsChanged, this, std::placeholders::_1));
    m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1));
    m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this));
    m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this));
    m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
    m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
}

void ClientModel::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    m_handler_show_progress->disconnect();
    m_handler_notify_num_connections_changed->disconnect();
    m_handler_notify_network_active_changed->disconnect();
    m_handler_notify_alert_changed->disconnect();
    m_handler_banned_list_changed->disconnect();
    m_handler_notify_block_tip->disconnect();
    m_handler_notify_header_tip->disconnect();
}

bool ClientModel::getProxyInfo(std::string& ip_port) const
{
    Proxy ipv4, ipv6;
    if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
      ip_port = ipv4.proxy.ToStringIPPort();
      return true;
    }
    return false;
}
<commit_msg>qt: Revamp ClientModel code to handle ShowProgress core signal<commit_after>// Copyright (c) 2011-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qt/clientmodel.h>

#include <qt/bantablemodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/peertablemodel.h>
#include <qt/peertablesortproxy.h>

#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <net.h>
#include <netbase.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/time.h>
#include <validation.h>

#include <stdint.h>
#include <functional>

#include <QDebug>
#include <QThread>
#include <QTimer>

static int64_t nLastHeaderTipUpdateNotification = 0;
static int64_t nLastBlockTipUpdateNotification = 0;

ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
    QObject(parent),
    m_node(node),
    optionsModel(_optionsModel),
    peerTableModel(nullptr),
    banTableModel(nullptr),
    m_thread(new QThread(this))
{
    cachedBestHeaderHeight = -1;
    cachedBestHeaderTime = -1;

    peerTableModel = new PeerTableModel(m_node, this);
    m_peer_table_sort_proxy = new PeerTableSortProxy(this);
    m_peer_table_sort_proxy->setSourceModel(peerTableModel);

    banTableModel = new BanTableModel(m_node, this);

    QTimer* timer = new QTimer;
    timer->setInterval(MODEL_UPDATE_DELAY);
    connect(timer, &QTimer::timeout, [this] {
        // no locking required at this point
        // the following calls will acquire the required lock
        Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
        Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
    });
    connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
    connect(m_thread, &QThread::started, [timer] { timer->start(); });
    // move timer to thread so that polling doesn't disturb main event loop
    timer->moveToThread(m_thread);
    m_thread->start();
    QTimer::singleShot(0, timer, []() {
        util::ThreadRename("qt-clientmodl");
    });

    subscribeToCoreSignals();
}

ClientModel::~ClientModel()
{
    unsubscribeFromCoreSignals();

    m_thread->quit();
    m_thread->wait();
}

int ClientModel::getNumConnections(unsigned int flags) const
{
    ConnectionDirection connections = ConnectionDirection::None;

    if(flags == CONNECTIONS_IN)
        connections = ConnectionDirection::In;
    else if (flags == CONNECTIONS_OUT)
        connections = ConnectionDirection::Out;
    else if (flags == CONNECTIONS_ALL)
        connections = ConnectionDirection::Both;

    return m_node.getNodeCount(connections);
}

int ClientModel::getHeaderTipHeight() const
{
    if (cachedBestHeaderHeight == -1) {
        // make sure we initially populate the cache via a cs_main lock
        // otherwise we need to wait for a tip update
        int height;
        int64_t blockTime;
        if (m_node.getHeaderTip(height, blockTime)) {
            cachedBestHeaderHeight = height;
            cachedBestHeaderTime = blockTime;
        }
    }
    return cachedBestHeaderHeight;
}

int64_t ClientModel::getHeaderTipTime() const
{
    if (cachedBestHeaderTime == -1) {
        int height;
        int64_t blockTime;
        if (m_node.getHeaderTip(height, blockTime)) {
            cachedBestHeaderHeight = height;
            cachedBestHeaderTime = blockTime;
        }
    }
    return cachedBestHeaderTime;
}

int ClientModel::getNumBlocks() const
{
    if (m_cached_num_blocks == -1) {
        m_cached_num_blocks = m_node.getNumBlocks();
    }
    return m_cached_num_blocks;
}

uint256 ClientModel::getBestBlockHash()
{
    uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};

    if (!tip.IsNull()) {
        return tip;
    }

    // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
    // The following will lock `cs_main` (and release it), so we must not
    // own `m_cached_tip_mutex` here.
    tip = m_node.getBestBlockHash();

    LOCK(m_cached_tip_mutex);
    // We checked that `m_cached_tip_blocks` is not null above, but then we
    // released the mutex `m_cached_tip_mutex`, so it could have changed in the
    // meantime. Thus, check again.
    if (m_cached_tip_blocks.IsNull()) {
        m_cached_tip_blocks = tip;
    }
    return m_cached_tip_blocks;
}

void ClientModel::updateNumConnections(int numConnections)
{
    Q_EMIT numConnectionsChanged(numConnections);
}

void ClientModel::updateNetworkActive(bool networkActive)
{
    Q_EMIT networkActiveChanged(networkActive);
}

void ClientModel::updateAlert()
{
    Q_EMIT alertsChanged(getStatusBarWarnings());
}

enum BlockSource ClientModel::getBlockSource() const
{
    if (m_node.getReindex())
        return BlockSource::REINDEX;
    else if (m_node.getImporting())
        return BlockSource::DISK;
    else if (getNumConnections() > 0)
        return BlockSource::NETWORK;

    return BlockSource::NONE;
}

QString ClientModel::getStatusBarWarnings() const
{
    return QString::fromStdString(m_node.getWarnings().translated);
}

OptionsModel *ClientModel::getOptionsModel()
{
    return optionsModel;
}

PeerTableModel *ClientModel::getPeerTableModel()
{
    return peerTableModel;
}

PeerTableSortProxy* ClientModel::peerTableSortProxy()
{
    return m_peer_table_sort_proxy;
}

BanTableModel *ClientModel::getBanTableModel()
{
    return banTableModel;
}

QString ClientModel::formatFullVersion() const
{
    return QString::fromStdString(FormatFullVersion());
}

QString ClientModel::formatSubVersion() const
{
    return QString::fromStdString(strSubVersion);
}

bool ClientModel::isReleaseVersion() const
{
    return CLIENT_VERSION_IS_RELEASE;
}

QString ClientModel::formatClientStartupTime() const
{
    return QDateTime::fromSecsSinceEpoch(GetStartupTime()).toString();
}

QString ClientModel::dataDir() const
{
    return GUIUtil::PathToQString(gArgs.GetDataDirNet());
}

QString ClientModel::blocksDir() const
{
    return GUIUtil::PathToQString(gArgs.GetBlocksDirPath());
}

void ClientModel::updateBanlist()
{
    banTableModel->refresh();
}

// Handlers for core signals
static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
{
    // Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
    bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
                              Q_ARG(int, newNumConnections));
    assert(invoked);
}

static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
{
    bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
                              Q_ARG(bool, networkActive));
    assert(invoked);
}

static void NotifyAlertChanged(ClientModel *clientmodel)
{
    qDebug() << "NotifyAlertChanged";
    bool invoked = QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
    assert(invoked);
}

static void BannedListChanged(ClientModel *clientmodel)
{
    qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
    bool invoked = QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
    assert(invoked);
}

static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
{
    if (fHeader) {
        // cache best headers time and height to reduce future cs_main locks
        clientmodel->cachedBestHeaderHeight = tip.block_height;
        clientmodel->cachedBestHeaderTime = tip.block_time;
    } else {
        clientmodel->m_cached_num_blocks = tip.block_height;
        WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
    }

    // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
    const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX;
    const int64_t now = throttle ? GetTimeMillis() : 0;
    int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
    if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) {
        return;
    }

    bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
        Q_ARG(int, tip.block_height),
        Q_ARG(QDateTime, QDateTime::fromSecsSinceEpoch(tip.block_time)),
        Q_ARG(double, verificationProgress),
        Q_ARG(bool, fHeader),
        Q_ARG(SynchronizationState, sync_state));
    assert(invoked);
    nLastUpdateNotification = now;
}

void ClientModel::subscribeToCoreSignals()
{
    // Connect signals to client
    m_handler_show_progress = m_node.handleShowProgress(
        [this](const std::string& title, int progress, [[maybe_unused]] bool resume_possible) {
            Q_EMIT showProgress(QString::fromStdString(title), progress);
        });
    m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(std::bind(NotifyNumConnectionsChanged, this, std::placeholders::_1));
    m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1));
    m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this));
    m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this));
    m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
    m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
}

void ClientModel::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    m_handler_show_progress->disconnect();
    m_handler_notify_num_connections_changed->disconnect();
    m_handler_notify_network_active_changed->disconnect();
    m_handler_notify_alert_changed->disconnect();
    m_handler_banned_list_changed->disconnect();
    m_handler_notify_block_tip->disconnect();
    m_handler_notify_header_tip->disconnect();
}

bool ClientModel::getProxyInfo(std::string& ip_port) const
{
    Proxy ipv4, ipv6;
    if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
      ip_port = ipv4.proxy.ToStringIPPort();
      return true;
    }
    return false;
}
<|endoftext|>


<commit_before>// Copyright (c) 2011-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2017 The PIVX developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "clientmodel.h"

#include "guiconstants.h"
#include "peertablemodel.h"

#include "alert.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
#include "main.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "net.h"
#include "ui_interface.h"
#include "util.h"

#include <stdint.h>

#include <QDateTime>
#include <QDebug>
#include <QTimer>

static const int64_t nClientStartupTime = GetTime();

ClientModel::ClientModel(OptionsModel* optionsModel, QObject* parent) : QObject(parent),
                                                                        optionsModel(optionsModel),
                                                                        peerTableModel(0),
                                                                        cachedNumBlocks(0),
                                                                        cachedMasternodeCountString(""),
                                                                        cachedReindexing(0), cachedImporting(0),
                                                                        numBlocksAtStartup(-1), pollTimer(0)
{
    peerTableModel = new PeerTableModel(this);
    pollTimer = new QTimer(this);
    connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
    pollTimer->start(MODEL_UPDATE_DELAY);

    pollMnTimer = new QTimer(this);
    connect(pollMnTimer, SIGNAL(timeout()), this, SLOT(updateMnTimer()));
    // no need to update as frequent as data for balances/txes/blocks
    pollMnTimer->start(MODEL_UPDATE_DELAY * 4);

    subscribeToCoreSignals();
}

ClientModel::~ClientModel()
{
    unsubscribeFromCoreSignals();
}

int ClientModel::getNumConnections(unsigned int flags) const
{
    LOCK(cs_vNodes);
    if (flags == CONNECTIONS_ALL) // Shortcut if we want total
        return vNodes.size();

    int nNum = 0;
    BOOST_FOREACH (CNode* pnode, vNodes)
        if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
            nNum++;

    return nNum;
}

QString ClientModel::getMasternodeCountString() const
{
    int ipv4 = 0, ipv6 = 0, onion = 0;
    mnodeman.CountNetworks(ActiveProtocol(), ipv4, ipv6, onion);
    return tr("Total: %1 (IPv4: %2 / IPv6: %3 / Tor: %4)").arg(QString::number((int)mnodeman.size())).arg(QString::number((int)ipv4)).arg(QString::number((int)ipv6)).arg(QString::number((int)onion));
}

int ClientModel::getNumBlocks() const
{
    LOCK(cs_main);
    return chainActive.Height();
}

int ClientModel::getNumBlocksAtStartup()
{
    if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks();
    return numBlocksAtStartup;
}

quint64 ClientModel::getTotalBytesRecv() const
{
    return CNode::GetTotalBytesRecv();
}

quint64 ClientModel::getTotalBytesSent() const
{
    return CNode::GetTotalBytesSent();
}

QDateTime ClientModel::getLastBlockDate() const
{
    LOCK(cs_main);
    if (chainActive.Tip())
        return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
    else
        return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network
}

double ClientModel::getVerificationProgress() const
{
    LOCK(cs_main);
    return Checkpoints::GuessVerificationProgress(chainActive.Tip());
}

void ClientModel::updateTimer()
{
    // Get required lock upfront. This avoids the GUI from getting stuck on
    // periodical polls if the core is holding the locks for a longer time -
    // for example, during a wallet rescan.
    TRY_LOCK(cs_main, lockMain);
    if (!lockMain)
        return;
    // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change.
    // Periodically check and update with a timer.
    int newNumBlocks = getNumBlocks();

    static int prevAttempt = -1;
    static int prevAssets = -1;

    // check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state
    if (cachedNumBlocks != newNumBlocks ||
        cachedReindexing != fReindex || cachedImporting != fImporting ||
        masternodeSync.RequestedMasternodeAttempt != prevAttempt || masternodeSync.RequestedMasternodeAssets != prevAssets) {
        cachedNumBlocks = newNumBlocks;
        cachedReindexing = fReindex;
        cachedImporting = fImporting;
        prevAttempt = masternodeSync.RequestedMasternodeAttempt;
        prevAssets = masternodeSync.RequestedMasternodeAssets;

        emit numBlocksChanged(newNumBlocks);
    }

    emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
}

void ClientModel::updateMnTimer()
{
    // Get required lock upfront. This avoids the GUI from getting stuck on
    // periodical polls if the core is holding the locks for a longer time -
    // for example, during a wallet rescan.
    TRY_LOCK(cs_main, lockMain);
    if (!lockMain)
        return;
    QString newMasternodeCountString = getMasternodeCountString();

    if (cachedMasternodeCountString != newMasternodeCountString) {
        cachedMasternodeCountString = newMasternodeCountString;

        emit strMasternodesChanged(cachedMasternodeCountString);
    }
}

void ClientModel::updateNumConnections(int numConnections)
{
    emit numConnectionsChanged(numConnections);
}

void ClientModel::updateAlert(const QString& hash, int status)
{
    // Show error message notification for new alert
    if (status == CT_NEW) {
        uint256 hash_256;
        hash_256.SetHex(hash.toStdString());
        CAlert alert = CAlert::getAlertByHash(hash_256);
        if (!alert.IsNull()) {
            emit message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR);
        }
    }

    emit alertsChanged(getStatusBarWarnings());
}

bool ClientModel::inInitialBlockDownload() const
{
    return IsInitialBlockDownload();
}

enum BlockSource ClientModel::getBlockSource() const
{
    if (fReindex)
        return BLOCK_SOURCE_REINDEX;
    else if (fImporting)
        return BLOCK_SOURCE_DISK;
    else if (getNumConnections() > 0)
        return BLOCK_SOURCE_NETWORK;

    return BLOCK_SOURCE_NONE;
}

QString ClientModel::getStatusBarWarnings() const
{
    return QString::fromStdString(GetWarnings("statusbar"));
}

OptionsModel* ClientModel::getOptionsModel()
{
    return optionsModel;
}

PeerTableModel* ClientModel::getPeerTableModel()
{
    return peerTableModel;
}

QString ClientModel::formatFullVersion() const
{
    return QString::fromStdString(FormatFullVersion());
}

QString ClientModel::formatBuildDate() const
{
    return QString::fromStdString(CLIENT_DATE);
}

bool ClientModel::isReleaseVersion() const
{
    return CLIENT_VERSION_IS_RELEASE;
}

QString ClientModel::clientName() const
{
    return QString::fromStdString(CLIENT_NAME);
}

QString ClientModel::formatClientStartupTime() const
{
    return QDateTime::fromTime_t(nClientStartupTime).toString();
}

// Handlers for core signals
static void ShowProgress(ClientModel* clientmodel, const std::string& title, int nProgress)
{
    // emits signal "showProgress"
    QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(title)),
        Q_ARG(int, nProgress));
}

static void NotifyNumConnectionsChanged(ClientModel* clientmodel, int newNumConnections)
{
    // Too noisy: qDebug() << "NotifyNumConnectionsChanged : " + QString::number(newNumConnections);
    QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
        Q_ARG(int, newNumConnections));
}

static void NotifyAlertChanged(ClientModel* clientmodel, const uint256& hash, ChangeType status)
{
    qDebug() << "NotifyAlertChanged : " + QString::fromStdString(hash.GetHex()) + " status=" + QString::number(status);
    QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(hash.GetHex())),
        Q_ARG(int, status));
}

void ClientModel::subscribeToCoreSignals()
{
    // Connect signals to client
    uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
    uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
    uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
}

void ClientModel::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
    uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
    uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
}
<commit_msg>[UI] Type 'Unknown' addet to Masternode count<commit_after>// Copyright (c) 2011-2014 The Bitcoin developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2017 The PIVX developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "clientmodel.h"

#include "guiconstants.h"
#include "peertablemodel.h"

#include "alert.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
#include "main.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "net.h"
#include "ui_interface.h"
#include "util.h"

#include <stdint.h>

#include <QDateTime>
#include <QDebug>
#include <QTimer>

static const int64_t nClientStartupTime = GetTime();

ClientModel::ClientModel(OptionsModel* optionsModel, QObject* parent) : QObject(parent),
                                                                        optionsModel(optionsModel),
                                                                        peerTableModel(0),
                                                                        cachedNumBlocks(0),
                                                                        cachedMasternodeCountString(""),
                                                                        cachedReindexing(0), cachedImporting(0),
                                                                        numBlocksAtStartup(-1), pollTimer(0)
{
    peerTableModel = new PeerTableModel(this);
    pollTimer = new QTimer(this);
    connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
    pollTimer->start(MODEL_UPDATE_DELAY);

    pollMnTimer = new QTimer(this);
    connect(pollMnTimer, SIGNAL(timeout()), this, SLOT(updateMnTimer()));
    // no need to update as frequent as data for balances/txes/blocks
    pollMnTimer->start(MODEL_UPDATE_DELAY * 4);

    subscribeToCoreSignals();
}

ClientModel::~ClientModel()
{
    unsubscribeFromCoreSignals();
}

int ClientModel::getNumConnections(unsigned int flags) const
{
    LOCK(cs_vNodes);
    if (flags == CONNECTIONS_ALL) // Shortcut if we want total
        return vNodes.size();

    int nNum = 0;
    BOOST_FOREACH (CNode* pnode, vNodes)
        if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
            nNum++;

    return nNum;
}

QString ClientModel::getMasternodeCountString() const
{
    int ipv4 = 0, ipv6 = 0, onion = 0;
    mnodeman.CountNetworks(ActiveProtocol(), ipv4, ipv6, onion);
    int nUnknown = mnodeman.size() - ipv4 - ipv6 - onion;
    if(nUnknown < 0) nUnknown = 0;
    return tr("Total: %1 (IPv4: %2 / IPv6: %3 / Tor: %4 / Unknown: %5)").arg(QString::number((int)mnodeman.size())).arg(QString::number((int)ipv4)).arg(QString::number((int)ipv6)).arg(QString::number((int)onion)).arg(QString::number((int)nUnknown));
}

int ClientModel::getNumBlocks() const
{
    LOCK(cs_main);
    return chainActive.Height();
}

int ClientModel::getNumBlocksAtStartup()
{
    if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks();
    return numBlocksAtStartup;
}

quint64 ClientModel::getTotalBytesRecv() const
{
    return CNode::GetTotalBytesRecv();
}

quint64 ClientModel::getTotalBytesSent() const
{
    return CNode::GetTotalBytesSent();
}

QDateTime ClientModel::getLastBlockDate() const
{
    LOCK(cs_main);
    if (chainActive.Tip())
        return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
    else
        return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network
}

double ClientModel::getVerificationProgress() const
{
    LOCK(cs_main);
    return Checkpoints::GuessVerificationProgress(chainActive.Tip());
}

void ClientModel::updateTimer()
{
    // Get required lock upfront. This avoids the GUI from getting stuck on
    // periodical polls if the core is holding the locks for a longer time -
    // for example, during a wallet rescan.
    TRY_LOCK(cs_main, lockMain);
    if (!lockMain)
        return;
    // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change.
    // Periodically check and update with a timer.
    int newNumBlocks = getNumBlocks();

    static int prevAttempt = -1;
    static int prevAssets = -1;

    // check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state
    if (cachedNumBlocks != newNumBlocks ||
        cachedReindexing != fReindex || cachedImporting != fImporting ||
        masternodeSync.RequestedMasternodeAttempt != prevAttempt || masternodeSync.RequestedMasternodeAssets != prevAssets) {
        cachedNumBlocks = newNumBlocks;
        cachedReindexing = fReindex;
        cachedImporting = fImporting;
        prevAttempt = masternodeSync.RequestedMasternodeAttempt;
        prevAssets = masternodeSync.RequestedMasternodeAssets;

        emit numBlocksChanged(newNumBlocks);
    }

    emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
}

void ClientModel::updateMnTimer()
{
    // Get required lock upfront. This avoids the GUI from getting stuck on
    // periodical polls if the core is holding the locks for a longer time -
    // for example, during a wallet rescan.
    TRY_LOCK(cs_main, lockMain);
    if (!lockMain)
        return;
    QString newMasternodeCountString = getMasternodeCountString();

    if (cachedMasternodeCountString != newMasternodeCountString) {
        cachedMasternodeCountString = newMasternodeCountString;

        emit strMasternodesChanged(cachedMasternodeCountString);
    }
}

void ClientModel::updateNumConnections(int numConnections)
{
    emit numConnectionsChanged(numConnections);
}

void ClientModel::updateAlert(const QString& hash, int status)
{
    // Show error message notification for new alert
    if (status == CT_NEW) {
        uint256 hash_256;
        hash_256.SetHex(hash.toStdString());
        CAlert alert = CAlert::getAlertByHash(hash_256);
        if (!alert.IsNull()) {
            emit message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR);
        }
    }

    emit alertsChanged(getStatusBarWarnings());
}

bool ClientModel::inInitialBlockDownload() const
{
    return IsInitialBlockDownload();
}

enum BlockSource ClientModel::getBlockSource() const
{
    if (fReindex)
        return BLOCK_SOURCE_REINDEX;
    else if (fImporting)
        return BLOCK_SOURCE_DISK;
    else if (getNumConnections() > 0)
        return BLOCK_SOURCE_NETWORK;

    return BLOCK_SOURCE_NONE;
}

QString ClientModel::getStatusBarWarnings() const
{
    return QString::fromStdString(GetWarnings("statusbar"));
}

OptionsModel* ClientModel::getOptionsModel()
{
    return optionsModel;
}

PeerTableModel* ClientModel::getPeerTableModel()
{
    return peerTableModel;
}

QString ClientModel::formatFullVersion() const
{
    return QString::fromStdString(FormatFullVersion());
}

QString ClientModel::formatBuildDate() const
{
    return QString::fromStdString(CLIENT_DATE);
}

bool ClientModel::isReleaseVersion() const
{
    return CLIENT_VERSION_IS_RELEASE;
}

QString ClientModel::clientName() const
{
    return QString::fromStdString(CLIENT_NAME);
}

QString ClientModel::formatClientStartupTime() const
{
    return QDateTime::fromTime_t(nClientStartupTime).toString();
}

// Handlers for core signals
static void ShowProgress(ClientModel* clientmodel, const std::string& title, int nProgress)
{
    // emits signal "showProgress"
    QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(title)),
        Q_ARG(int, nProgress));
}

static void NotifyNumConnectionsChanged(ClientModel* clientmodel, int newNumConnections)
{
    // Too noisy: qDebug() << "NotifyNumConnectionsChanged : " + QString::number(newNumConnections);
    QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
        Q_ARG(int, newNumConnections));
}

static void NotifyAlertChanged(ClientModel* clientmodel, const uint256& hash, ChangeType status)
{
    qDebug() << "NotifyAlertChanged : " + QString::fromStdString(hash.GetHex()) + " status=" + QString::number(status);
    QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(hash.GetHex())),
        Q_ARG(int, status));
}

void ClientModel::subscribeToCoreSignals()
{
    // Connect signals to client
    uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
    uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
    uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
}

void ClientModel::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
    uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
    uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
}
<|endoftext|>


<commit_before><commit_msg>Added Warning to TString constuctor<commit_after><|endoftext|>