<commit_before>// Copyright (c) 2011-2015 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/walletview.h>

#include <qt/addressbookpage.h>
#include <qt/askpassphrasedialog.h>
#include <qt/bitcoingui.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/overviewpage.h>
#include <qt/receivecoinsdialog.h>
#include <qt/sendcoinsdialog.h>
#include <qt/signverifymessagedialog.h>
#include <qt/transactionrecord.h>
#include <qt/transactiontablemodel.h>
#include <qt/transactionview.h>
#include <qt/walletmodel.h>

#include <interfaces/node.h>
#include <ui_interface.h>

#include <QAction>
#include <QActionGroup>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QProgressDialog>
#include <QPushButton>
#include <QSettings>
#include <QVBoxLayout>

WalletView::WalletView(QWidget* parent) :
    QStackedWidget(parent),
    clientModel(nullptr),
    walletModel(nullptr)
{
    // Create tabs
    overviewPage = new OverviewPage();

    transactionsPage = new QWidget(this);
    QVBoxLayout *vbox = new QVBoxLayout();
    QHBoxLayout *hbox_buttons = new QHBoxLayout();
    transactionView = new TransactionView(this);
    vbox->addWidget(transactionView);
    QPushButton *exportButton = new QPushButton(tr("&Export"), this);
    exportButton->setToolTip(tr("Export the data in the current tab to a file"));
    hbox_buttons->addStretch();

    // Sum of selected transactions
    QLabel *transactionSumLabel = new QLabel(); // Label
    transactionSumLabel->setObjectName("transactionSumLabel"); // Label ID as CSS-reference
    transactionSumLabel->setText(tr("Selected amount:"));
    hbox_buttons->addWidget(transactionSumLabel);

    transactionSum = new QLabel(); // Amount
    transactionSum->setObjectName("transactionSum"); // Label ID as CSS-reference
    transactionSum->setMinimumSize(200, 8);
    transactionSum->setTextInteractionFlags(Qt::TextSelectableByMouse);

    GUIUtil::setFont({transactionSumLabel,
                      transactionSum,
                     }, GUIUtil::FontWeight::Bold, 14);
    GUIUtil::updateFonts();

    hbox_buttons->addWidget(transactionSum);

    hbox_buttons->addWidget(exportButton);
    vbox->addLayout(hbox_buttons);
    transactionsPage->setLayout(vbox);

    receiveCoinsPage = new ReceiveCoinsDialog();
    sendCoinsPage = new SendCoinsDialog();
    coinJoinCoinsPage = new SendCoinsDialog(true);

    usedSendingAddressesPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
    usedReceivingAddressesPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this);

    addWidget(overviewPage);
    addWidget(transactionsPage);
    addWidget(receiveCoinsPage);
    addWidget(sendCoinsPage);
    addWidget(coinJoinCoinsPage);

    QSettings settings;
    if (settings.value("fShowMasternodesTab").toBool()) {
        masternodeListPage = new MasternodeList();
        addWidget(masternodeListPage);
    }
    if (settings.value("fShowGovernanceTab").toBool()) {
        governanceListPage = new GovernanceList();
        addWidget(governanceListPage);
    }

    // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
    connect(overviewPage, &OverviewPage::transactionClicked, transactionView, static_cast<void (TransactionView::*)(const QModelIndex&)>(&TransactionView::focusTransaction));
    connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);

    // Highlight transaction after send
    connect(sendCoinsPage, &SendCoinsDialog::coinsSent, transactionView, static_cast<void (TransactionView::*)(const uint256&)>(&TransactionView::focusTransaction));
    connect(coinJoinCoinsPage, &SendCoinsDialog::coinsSent, transactionView, static_cast<void (TransactionView::*)(const uint256&)>(&TransactionView::focusTransaction));

    // Update wallet with sum of selected transactions
    connect(transactionView, &TransactionView::trxAmount, this, &WalletView::trxAmount);

    // Clicking on "Export" allows to export the transaction list
    connect(exportButton, &QPushButton::clicked, transactionView, &TransactionView::exportClicked);

    // Pass through messages from SendCoinsDialog
    connect(sendCoinsPage, &SendCoinsDialog::message, this, &WalletView::message);
    connect(coinJoinCoinsPage, &SendCoinsDialog::message, this, &WalletView::message);

    // Pass through messages from transactionView
    connect(transactionView, &TransactionView::message, this, &WalletView::message);

    GUIUtil::disableMacFocusRect(this);
}

WalletView::~WalletView()
{
}

void WalletView::setBitcoinGUI(BitcoinGUI *gui)
{
    if (gui)
    {
        // Clicking on a transaction on the overview page simply sends you to transaction history page
        connect(overviewPage, &OverviewPage::transactionClicked, gui, &BitcoinGUI::gotoHistoryPage);

        // Navigate to transaction history page after send
        connect(sendCoinsPage, &SendCoinsDialog::coinsSent, gui, &BitcoinGUI::gotoHistoryPage);
        connect(coinJoinCoinsPage, &SendCoinsDialog::coinsSent, gui, &BitcoinGUI::gotoHistoryPage);

        // Receive and report messages
        connect(this, &WalletView::message, [gui](const QString &title, const QString &message, unsigned int style) {
            gui->message(title, message, style);
        });

        // Pass through encryption status changed signals
        connect(this, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus);

        // Pass through transaction notifications
        connect(this, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction);

        // Connect HD enabled state signal
        connect(this, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
    }
}

void WalletView::setClientModel(ClientModel *_clientModel)
{
    this->clientModel = _clientModel;

    overviewPage->setClientModel(_clientModel);
    sendCoinsPage->setClientModel(_clientModel);
    coinJoinCoinsPage->setClientModel(_clientModel);
    QSettings settings;
    if (settings.value("fShowMasternodesTab").toBool()) {
        masternodeListPage->setClientModel(_clientModel);
    }
    if (settings.value("fShowGovernanceTab").toBool()) {
        governanceListPage->setClientModel(_clientModel);
    }
}

void WalletView::setWalletModel(WalletModel *_walletModel)
{
    this->walletModel = _walletModel;

    // Put transaction list in tabs
    transactionView->setModel(_walletModel);
    overviewPage->setWalletModel(_walletModel);
    QSettings settings;
    if (settings.value("fShowMasternodesTab").toBool()) {
        masternodeListPage->setWalletModel(_walletModel);
    }
    receiveCoinsPage->setModel(_walletModel);
    sendCoinsPage->setModel(_walletModel);
    coinJoinCoinsPage->setModel(_walletModel);
    usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);
    usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);

    if (_walletModel)
    {
        // Receive and pass through messages from wallet model
        connect(_walletModel, &WalletModel::message, this, &WalletView::message);

        // Handle changes in encryption status
        connect(_walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged);
        updateEncryptionStatus();

        // update HD status
        Q_EMIT hdEnabledStatusChanged();

        // Balloon pop-up for new transaction
        connect(_walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction);

        // Ask for passphrase if needed
        connect(_walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet);

        // Show progress dialog
        connect(_walletModel, &WalletModel::showProgress, this, &WalletView::showProgress);
    }
}

void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
{
    // Prevent balloon-spam when initial block download is in progress
    if (!walletModel || !clientModel || clientModel->node().isInitialBlockDownload())
        return;

    TransactionTableModel *ttm = walletModel->getTransactionTableModel();
    if (!ttm || ttm->processingQueuedTransactions())
        return;

    QModelIndex index = ttm->index(start, 0, parent);
    QSettings settings;
    if (!settings.value("fShowCoinJoinPopups").toBool()) {
        QVariant nType = ttm->data(index, TransactionTableModel::TypeRole);
        if (nType == TransactionRecord::CoinJoinMixing ||
            nType == TransactionRecord::CoinJoinCollateralPayment ||
            nType == TransactionRecord::CoinJoinMakeCollaterals ||
            nType == TransactionRecord::CoinJoinCreateDenominations) return;
    }

    QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString();
    qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
    QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
    QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
    QString label = GUIUtil::HtmlEscape(ttm->data(index, TransactionTableModel::LabelRole).toString());

    Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, GUIUtil::HtmlEscape(walletModel->getWalletName()));
}

void WalletView::gotoGovernancePage()
{
    QSettings settings;
    if (settings.value("fShowGovernanceTab").toBool()) {
        setCurrentWidget(governanceListPage);
    }
}

void WalletView::gotoOverviewPage()
{
    setCurrentWidget(overviewPage);
}

void WalletView::gotoHistoryPage()
{
    setCurrentWidget(transactionsPage);
}

void WalletView::gotoMasternodePage()
{
    QSettings settings;
    if (settings.value("fShowMasternodesTab").toBool()) {
        setCurrentWidget(masternodeListPage);
    }
}

void WalletView::gotoReceiveCoinsPage()
{
    setCurrentWidget(receiveCoinsPage);
}

void WalletView::gotoSendCoinsPage(QString addr)
{
    setCurrentWidget(sendCoinsPage);

    if (!addr.isEmpty()) {
        sendCoinsPage->setAddress(addr);
    }
}

void WalletView::gotoCoinJoinCoinsPage(QString addr)
{
    setCurrentWidget(coinJoinCoinsPage);

    if (!addr.isEmpty())
        coinJoinCoinsPage->setAddress(addr);
}

void WalletView::gotoSignMessageTab(QString addr)
{
    // calls show() in showTab_SM()
    SignVerifyMessageDialog* signVerifyMessageDialog = new SignVerifyMessageDialog(this);
    signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
    signVerifyMessageDialog->setModel(walletModel);
    signVerifyMessageDialog->showTab_SM(true);

    if (!addr.isEmpty())
        signVerifyMessageDialog->setAddress_SM(addr);
}

void WalletView::gotoVerifyMessageTab(QString addr)
{
    // calls show() in showTab_VM()
    SignVerifyMessageDialog* signVerifyMessageDialog = new SignVerifyMessageDialog(this);
    signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
    signVerifyMessageDialog->setModel(walletModel);
    signVerifyMessageDialog->showTab_VM(true);

    if (!addr.isEmpty())
        signVerifyMessageDialog->setAddress_VM(addr);
}

bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
{
    return sendCoinsPage->handlePaymentRequest(recipient);
}

void WalletView::showOutOfSyncWarning(bool fShow)
{
    overviewPage->showOutOfSyncWarning(fShow);
}

void WalletView::updateEncryptionStatus()
{
    Q_EMIT encryptionStatusChanged();
}

void WalletView::encryptWallet()
{
    if(!walletModel)
        return;
    AskPassphraseDialog dlg(AskPassphraseDialog::Encrypt, this);
    dlg.setModel(walletModel);
    dlg.exec();

    updateEncryptionStatus();
}

void WalletView::backupWallet()
{
    QString filename = GUIUtil::getSaveFileName(this,
        tr("Backup Wallet"), QString(),
        tr("Wallet Data (*.dat)"), nullptr);

    if (filename.isEmpty())
        return;

    if (!walletModel->wallet().backupWallet(filename.toLocal8Bit().data())) {
        Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename),
            CClientUIInterface::MSG_ERROR);
        }
    else {
        Q_EMIT message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename),
            CClientUIInterface::MSG_INFORMATION);
    }
}

void WalletView::changePassphrase()
{
    AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
    dlg.setModel(walletModel);
    dlg.exec();
}

void WalletView::unlockWallet(bool fForMixingOnly)
{
    if(!walletModel)
        return;
    // Unlock wallet when requested by wallet model

    if (walletModel->getEncryptionStatus() == WalletModel::Locked || walletModel->getEncryptionStatus() == WalletModel::UnlockedForMixingOnly)
    {
        AskPassphraseDialog dlg(fForMixingOnly ? AskPassphraseDialog::UnlockMixing : AskPassphraseDialog::Unlock, this);
        dlg.setModel(walletModel);
        dlg.exec();
    }
}

void WalletView::lockWallet()
{
    if(!walletModel)
        return;

    walletModel->setWalletLocked(true);
}

void WalletView::usedSendingAddresses()
{
    if(!walletModel)
        return;

    GUIUtil::bringToFront(usedSendingAddressesPage);
}

void WalletView::usedReceivingAddresses()
{
    if(!walletModel)
        return;

    GUIUtil::bringToFront(usedReceivingAddressesPage);
}

void WalletView::showProgress(const QString &title, int nProgress)
{
    if (nProgress == 0) {
        progressDialog = new QProgressDialog(title, tr("Cancel"), 0, 100, this);
        GUIUtil::PolishProgressDialog(progressDialog);
        progressDialog->setWindowModality(Qt::ApplicationModal);
        progressDialog->setMinimumDuration(0);
        progressDialog->setAutoClose(false);
        progressDialog->setValue(0);
    } else if (nProgress == 100) {
        if (progressDialog) {
            progressDialog->close();
            progressDialog->deleteLater();
            progressDialog = nullptr;
        }
    } else if (progressDialog) {
        if (progressDialog->wasCanceled()) {
            getWalletModel()->wallet().abortRescan();
        } else {
            progressDialog->setValue(nProgress);
        }
    }
}

void WalletView::requestedSyncWarningInfo()
{
    Q_EMIT outOfSyncWarningClicked();
}

/** Update wallet with the sum of the selected transactions */
void WalletView::trxAmount(QString amount)
{
    transactionSum->setText(amount);
}
<commit_msg>fix(qt): fix crash when first enabling governance tab due to null-ptr deref (#4795)<commit_after>// Copyright (c) 2011-2015 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/walletview.h>

#include <qt/addressbookpage.h>
#include <qt/askpassphrasedialog.h>
#include <qt/bitcoingui.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/overviewpage.h>
#include <qt/receivecoinsdialog.h>
#include <qt/sendcoinsdialog.h>
#include <qt/signverifymessagedialog.h>
#include <qt/transactionrecord.h>
#include <qt/transactiontablemodel.h>
#include <qt/transactionview.h>
#include <qt/walletmodel.h>

#include <interfaces/node.h>
#include <ui_interface.h>

#include <QAction>
#include <QActionGroup>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QProgressDialog>
#include <QPushButton>
#include <QSettings>
#include <QVBoxLayout>

WalletView::WalletView(QWidget* parent) :
    QStackedWidget(parent),
    clientModel(nullptr),
    walletModel(nullptr)
{
    // Create tabs
    overviewPage = new OverviewPage();

    transactionsPage = new QWidget(this);
    QVBoxLayout *vbox = new QVBoxLayout();
    QHBoxLayout *hbox_buttons = new QHBoxLayout();
    transactionView = new TransactionView(this);
    vbox->addWidget(transactionView);
    QPushButton *exportButton = new QPushButton(tr("&Export"), this);
    exportButton->setToolTip(tr("Export the data in the current tab to a file"));
    hbox_buttons->addStretch();

    // Sum of selected transactions
    QLabel *transactionSumLabel = new QLabel(); // Label
    transactionSumLabel->setObjectName("transactionSumLabel"); // Label ID as CSS-reference
    transactionSumLabel->setText(tr("Selected amount:"));
    hbox_buttons->addWidget(transactionSumLabel);

    transactionSum = new QLabel(); // Amount
    transactionSum->setObjectName("transactionSum"); // Label ID as CSS-reference
    transactionSum->setMinimumSize(200, 8);
    transactionSum->setTextInteractionFlags(Qt::TextSelectableByMouse);

    GUIUtil::setFont({transactionSumLabel,
                      transactionSum,
                     }, GUIUtil::FontWeight::Bold, 14);
    GUIUtil::updateFonts();

    hbox_buttons->addWidget(transactionSum);

    hbox_buttons->addWidget(exportButton);
    vbox->addLayout(hbox_buttons);
    transactionsPage->setLayout(vbox);

    receiveCoinsPage = new ReceiveCoinsDialog();
    sendCoinsPage = new SendCoinsDialog();
    coinJoinCoinsPage = new SendCoinsDialog(true);

    usedSendingAddressesPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
    usedReceivingAddressesPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this);

    addWidget(overviewPage);
    addWidget(transactionsPage);
    addWidget(receiveCoinsPage);
    addWidget(sendCoinsPage);
    addWidget(coinJoinCoinsPage);

    QSettings settings;
    if (settings.value("fShowMasternodesTab").toBool()) {
        masternodeListPage = new MasternodeList();
        addWidget(masternodeListPage);
    }
    if (settings.value("fShowGovernanceTab").toBool()) {
        governanceListPage = new GovernanceList();
        addWidget(governanceListPage);
    }

    // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
    connect(overviewPage, &OverviewPage::transactionClicked, transactionView, static_cast<void (TransactionView::*)(const QModelIndex&)>(&TransactionView::focusTransaction));
    connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);

    // Highlight transaction after send
    connect(sendCoinsPage, &SendCoinsDialog::coinsSent, transactionView, static_cast<void (TransactionView::*)(const uint256&)>(&TransactionView::focusTransaction));
    connect(coinJoinCoinsPage, &SendCoinsDialog::coinsSent, transactionView, static_cast<void (TransactionView::*)(const uint256&)>(&TransactionView::focusTransaction));

    // Update wallet with sum of selected transactions
    connect(transactionView, &TransactionView::trxAmount, this, &WalletView::trxAmount);

    // Clicking on "Export" allows to export the transaction list
    connect(exportButton, &QPushButton::clicked, transactionView, &TransactionView::exportClicked);

    // Pass through messages from SendCoinsDialog
    connect(sendCoinsPage, &SendCoinsDialog::message, this, &WalletView::message);
    connect(coinJoinCoinsPage, &SendCoinsDialog::message, this, &WalletView::message);

    // Pass through messages from transactionView
    connect(transactionView, &TransactionView::message, this, &WalletView::message);

    GUIUtil::disableMacFocusRect(this);
}

WalletView::~WalletView()
{
}

void WalletView::setBitcoinGUI(BitcoinGUI *gui)
{
    if (gui)
    {
        // Clicking on a transaction on the overview page simply sends you to transaction history page
        connect(overviewPage, &OverviewPage::transactionClicked, gui, &BitcoinGUI::gotoHistoryPage);

        // Navigate to transaction history page after send
        connect(sendCoinsPage, &SendCoinsDialog::coinsSent, gui, &BitcoinGUI::gotoHistoryPage);
        connect(coinJoinCoinsPage, &SendCoinsDialog::coinsSent, gui, &BitcoinGUI::gotoHistoryPage);

        // Receive and report messages
        connect(this, &WalletView::message, [gui](const QString &title, const QString &message, unsigned int style) {
            gui->message(title, message, style);
        });

        // Pass through encryption status changed signals
        connect(this, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus);

        // Pass through transaction notifications
        connect(this, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction);

        // Connect HD enabled state signal
        connect(this, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
    }
}

void WalletView::setClientModel(ClientModel *_clientModel)
{
    this->clientModel = _clientModel;

    if (overviewPage != nullptr) {
        overviewPage->setClientModel(_clientModel);
    }
    if (sendCoinsPage != nullptr) {
        sendCoinsPage->setClientModel(_clientModel);
    }
    if (coinJoinCoinsPage != nullptr) {
        coinJoinCoinsPage->setClientModel(_clientModel);
    }
    QSettings settings;
    if (settings.value("fShowMasternodesTab").toBool() && masternodeListPage != nullptr) {
        masternodeListPage->setClientModel(_clientModel);
    }
    if (settings.value("fShowGovernanceTab").toBool() && governanceListPage != nullptr) {
        governanceListPage->setClientModel(_clientModel);
    }
}

void WalletView::setWalletModel(WalletModel *_walletModel)
{
    this->walletModel = _walletModel;

    // Put transaction list in tabs
    transactionView->setModel(_walletModel);
    overviewPage->setWalletModel(_walletModel);
    QSettings settings;
    if (settings.value("fShowMasternodesTab").toBool()) {
        masternodeListPage->setWalletModel(_walletModel);
    }
    receiveCoinsPage->setModel(_walletModel);
    sendCoinsPage->setModel(_walletModel);
    coinJoinCoinsPage->setModel(_walletModel);
    usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);
    usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);

    if (_walletModel)
    {
        // Receive and pass through messages from wallet model
        connect(_walletModel, &WalletModel::message, this, &WalletView::message);

        // Handle changes in encryption status
        connect(_walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged);
        updateEncryptionStatus();

        // update HD status
        Q_EMIT hdEnabledStatusChanged();

        // Balloon pop-up for new transaction
        connect(_walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction);

        // Ask for passphrase if needed
        connect(_walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet);

        // Show progress dialog
        connect(_walletModel, &WalletModel::showProgress, this, &WalletView::showProgress);
    }
}

void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
{
    // Prevent balloon-spam when initial block download is in progress
    if (!walletModel || !clientModel || clientModel->node().isInitialBlockDownload())
        return;

    TransactionTableModel *ttm = walletModel->getTransactionTableModel();
    if (!ttm || ttm->processingQueuedTransactions())
        return;

    QModelIndex index = ttm->index(start, 0, parent);
    QSettings settings;
    if (!settings.value("fShowCoinJoinPopups").toBool()) {
        QVariant nType = ttm->data(index, TransactionTableModel::TypeRole);
        if (nType == TransactionRecord::CoinJoinMixing ||
            nType == TransactionRecord::CoinJoinCollateralPayment ||
            nType == TransactionRecord::CoinJoinMakeCollaterals ||
            nType == TransactionRecord::CoinJoinCreateDenominations) return;
    }

    QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString();
    qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
    QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
    QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
    QString label = GUIUtil::HtmlEscape(ttm->data(index, TransactionTableModel::LabelRole).toString());

    Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, GUIUtil::HtmlEscape(walletModel->getWalletName()));
}

void WalletView::gotoGovernancePage()
{
    QSettings settings;
    if (settings.value("fShowGovernanceTab").toBool()) {
        setCurrentWidget(governanceListPage);
    }
}

void WalletView::gotoOverviewPage()
{
    setCurrentWidget(overviewPage);
}

void WalletView::gotoHistoryPage()
{
    setCurrentWidget(transactionsPage);
}

void WalletView::gotoMasternodePage()
{
    QSettings settings;
    if (settings.value("fShowMasternodesTab").toBool()) {
        setCurrentWidget(masternodeListPage);
    }
}

void WalletView::gotoReceiveCoinsPage()
{
    setCurrentWidget(receiveCoinsPage);
}

void WalletView::gotoSendCoinsPage(QString addr)
{
    setCurrentWidget(sendCoinsPage);

    if (!addr.isEmpty()) {
        sendCoinsPage->setAddress(addr);
    }
}

void WalletView::gotoCoinJoinCoinsPage(QString addr)
{
    setCurrentWidget(coinJoinCoinsPage);

    if (!addr.isEmpty())
        coinJoinCoinsPage->setAddress(addr);
}

void WalletView::gotoSignMessageTab(QString addr)
{
    // calls show() in showTab_SM()
    SignVerifyMessageDialog* signVerifyMessageDialog = new SignVerifyMessageDialog(this);
    signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
    signVerifyMessageDialog->setModel(walletModel);
    signVerifyMessageDialog->showTab_SM(true);

    if (!addr.isEmpty())
        signVerifyMessageDialog->setAddress_SM(addr);
}

void WalletView::gotoVerifyMessageTab(QString addr)
{
    // calls show() in showTab_VM()
    SignVerifyMessageDialog* signVerifyMessageDialog = new SignVerifyMessageDialog(this);
    signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
    signVerifyMessageDialog->setModel(walletModel);
    signVerifyMessageDialog->showTab_VM(true);

    if (!addr.isEmpty())
        signVerifyMessageDialog->setAddress_VM(addr);
}

bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
{
    return sendCoinsPage->handlePaymentRequest(recipient);
}

void WalletView::showOutOfSyncWarning(bool fShow)
{
    overviewPage->showOutOfSyncWarning(fShow);
}

void WalletView::updateEncryptionStatus()
{
    Q_EMIT encryptionStatusChanged();
}

void WalletView::encryptWallet()
{
    if(!walletModel)
        return;
    AskPassphraseDialog dlg(AskPassphraseDialog::Encrypt, this);
    dlg.setModel(walletModel);
    dlg.exec();

    updateEncryptionStatus();
}

void WalletView::backupWallet()
{
    QString filename = GUIUtil::getSaveFileName(this,
        tr("Backup Wallet"), QString(),
        tr("Wallet Data (*.dat)"), nullptr);

    if (filename.isEmpty())
        return;

    if (!walletModel->wallet().backupWallet(filename.toLocal8Bit().data())) {
        Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename),
            CClientUIInterface::MSG_ERROR);
        }
    else {
        Q_EMIT message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename),
            CClientUIInterface::MSG_INFORMATION);
    }
}

void WalletView::changePassphrase()
{
    AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
    dlg.setModel(walletModel);
    dlg.exec();
}

void WalletView::unlockWallet(bool fForMixingOnly)
{
    if(!walletModel)
        return;
    // Unlock wallet when requested by wallet model

    if (walletModel->getEncryptionStatus() == WalletModel::Locked || walletModel->getEncryptionStatus() == WalletModel::UnlockedForMixingOnly)
    {
        AskPassphraseDialog dlg(fForMixingOnly ? AskPassphraseDialog::UnlockMixing : AskPassphraseDialog::Unlock, this);
        dlg.setModel(walletModel);
        dlg.exec();
    }
}

void WalletView::lockWallet()
{
    if(!walletModel)
        return;

    walletModel->setWalletLocked(true);
}

void WalletView::usedSendingAddresses()
{
    if(!walletModel)
        return;

    GUIUtil::bringToFront(usedSendingAddressesPage);
}

void WalletView::usedReceivingAddresses()
{
    if(!walletModel)
        return;

    GUIUtil::bringToFront(usedReceivingAddressesPage);
}

void WalletView::showProgress(const QString &title, int nProgress)
{
    if (nProgress == 0) {
        progressDialog = new QProgressDialog(title, tr("Cancel"), 0, 100, this);
        GUIUtil::PolishProgressDialog(progressDialog);
        progressDialog->setWindowModality(Qt::ApplicationModal);
        progressDialog->setMinimumDuration(0);
        progressDialog->setAutoClose(false);
        progressDialog->setValue(0);
    } else if (nProgress == 100) {
        if (progressDialog) {
            progressDialog->close();
            progressDialog->deleteLater();
            progressDialog = nullptr;
        }
    } else if (progressDialog) {
        if (progressDialog->wasCanceled()) {
            getWalletModel()->wallet().abortRescan();
        } else {
            progressDialog->setValue(nProgress);
        }
    }
}

void WalletView::requestedSyncWarningInfo()
{
    Q_EMIT outOfSyncWarningClicked();
}

/** Update wallet with the sum of the selected transactions */
void WalletView::trxAmount(QString amount)
{
    transactionSum->setText(amount);
}
<|endoftext|>


<commit_before>// Copyright (c) 2011-2015 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 "walletview.h"

#include "addressbookpage.h"
#include "zerocoinpage.h"
#include "sigmapage.h"
#include "askpassphrasedialog.h"
#include "bitcoingui.h"
#include "clientmodel.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "overviewpage.h"
#include "platformstyle.h"
#include "receivecoinsdialog.h"
#include "sendcoinsdialog.h"
#include "sendmpdialog.h"
#include "lookupspdialog.h"
#include "lookuptxdialog.h"
#include "lookupaddressdialog.h"
#include "metadexcanceldialog.h"
#include "metadexdialog.h"
#include "signverifymessagedialog.h"
#include "tradehistorydialog.h"
#include "transactiontablemodel.h"
#include "transactionview.h"
#include "txhistorydialog.h"
#include "walletmodel.h"

#include "ui_interface.h"

#include <exodus/exodus.h>

#include <QAction>
#include <QActionGroup>
#include <QDebug>
#include <QDialog>
#include <QFileDialog>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QProgressDialog>
#include <QPushButton>
#include <QTableView>
#include <QVBoxLayout>

WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent):
    QStackedWidget(parent),
    clientModel(0),
    walletModel(0),
    overviewPage(0),
    sendExodusView(0),
    sigmaPage(0),
    exodusTransactionsView(0),
    zcoinTransactionsView(0),
    platformStyle(platformStyle),
    transactionTabs(0),
    sendCoinsTabs(0)
{
    overviewPage = new OverviewPage(platformStyle);
    transactionsPage = new QWidget(this);
    exoAssetsPage = new ExoAssetsDialog();
    receiveCoinsPage = new ReceiveCoinsDialog(platformStyle);
    usedSendingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
    usedReceivingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this);
    zerocoinPage = new ZerocoinPage(platformStyle, ZerocoinPage::ForEditing, this);
    sigmaPage = new SigmaPage(platformStyle, this);
    sendCoinsPage = new QWidget(this);
    toolboxPage = new QWidget(this);
    znodeListPage = new ZnodeList(platformStyle);

    setupTransactionPage();
    setupSendCoinPage();
    setupToolboxPage();

    addWidget(overviewPage);
    addWidget(exoAssetsPage);
    addWidget(transactionsPage);
    addWidget(receiveCoinsPage);
    addWidget(sendCoinsPage);
    addWidget(zerocoinPage);
    addWidget(sigmaPage);
    addWidget(toolboxPage);
    addWidget(znodeListPage);

    // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
    connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(focusBitcoinHistoryTab(QModelIndex)));
    connect(overviewPage, SIGNAL(exodusTransactionClicked(uint256)), this, SLOT(focusExodusTransaction(uint256)));
}

WalletView::~WalletView()
{
}

void WalletView::setupTransactionPage()
{
    // Create Zcoin transactions list
    zcoinTransactionList = new TransactionView(platformStyle);

    connect(zcoinTransactionList, SIGNAL(doubleClicked(QModelIndex)), zcoinTransactionList, SLOT(showDetails()));
    connect(zcoinTransactionList, SIGNAL(message(QString, QString, unsigned int)), this, SIGNAL(message(QString, QString, unsigned int)));

    // Create export panel for Zcoin transactions
    auto exportButton = new QPushButton(tr("&Export"));

    exportButton->setToolTip(tr("Export the data in the current tab to a file"));

    if (platformStyle->getImagesOnButtons()) {
        exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
    }

    connect(exportButton, SIGNAL(clicked()), zcoinTransactionList, SLOT(exportClicked()));

    auto exportLayout = new QHBoxLayout();
    exportLayout->addStretch();
    exportLayout->addWidget(exportButton);

    // Compose transaction list and export panel together
    auto zcoinLayout = new QVBoxLayout();
    zcoinLayout->addWidget(zcoinTransactionList);
    zcoinLayout->addLayout(exportLayout);

    zcoinTransactionsView = new QWidget();
    zcoinTransactionsView->setLayout(zcoinLayout);

    // Create tabs for transaction categories
    if (isExodusEnabled()) {
        exodusTransactionsView = new TXHistoryDialog();

        transactionTabs = new QTabWidget();
        transactionTabs->addTab(zcoinTransactionsView, tr("Zcoin"));
        transactionTabs->addTab(exodusTransactionsView, tr("Exodus"));
    }

    // Set layout for transaction page
    auto pageLayout = new QVBoxLayout();

    if (transactionTabs) {
        pageLayout->addWidget(transactionTabs);
    } else {
        pageLayout->addWidget(zcoinTransactionsView);
    }

    transactionsPage->setLayout(pageLayout);
}

void WalletView::setupSendCoinPage()
{
    sendZcoinView = new SendCoinsDialog(platformStyle);

    connect(sendZcoinView, SIGNAL(message(QString, QString, unsigned int)), this, SIGNAL(message(QString, QString, unsigned int)));

    // Create tab for coin type
    if (isExodusEnabled()) {
        sendExodusView = new SendMPDialog(platformStyle);

        sendCoinsTabs = new QTabWidget();
        sendCoinsTabs->addTab(sendZcoinView, tr("Zcoin"));
        sendCoinsTabs->addTab(sendExodusView, tr("Exodus"));
    }

    // Set layout for send coin page
    auto pageLayout = new QVBoxLayout();

    if (sendCoinsTabs) {
        pageLayout->addWidget(sendCoinsTabs);
    } else {
        pageLayout->addWidget(sendZcoinView);
    }

    sendCoinsPage->setLayout(pageLayout);
}

void WalletView::setupToolboxPage()
{
    // Create tools widget
    auto lookupAddress = new LookupAddressDialog();
    auto lookupProperty = new LookupSPDialog();
    auto lookupTransaction = new LookupTXDialog();

    // Create tab for each tool
    auto tabs = new QTabWidget();

    tabs->addTab(lookupAddress, tr("Lookup Address"));
    tabs->addTab(lookupProperty, tr("Lookup Property"));
    tabs->addTab(lookupTransaction, tr("Lookup Transaction"));

    // Set layout for toolbox page
    auto pageLayout = new QVBoxLayout();
    pageLayout->addWidget(tabs);
    toolboxPage->setLayout(pageLayout);
}

void WalletView::setBitcoinGUI(BitcoinGUI *gui)
{
    if (gui)
    {
        // Clicking on a transaction on the overview page simply sends you to transaction history page
        connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui, SLOT(gotoBitcoinHistoryTab()));
        connect(overviewPage, SIGNAL(exodusTransactionClicked(uint256)), gui, SLOT(gotoExodusHistoryTab()));

        // Receive and report messages
        connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int)));

        // Pass through encryption status changed signals
        connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int)));

        // Pass through transaction notifications
        connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString,QString)));
    }
}

void WalletView::setClientModel(ClientModel *clientModel)
{
    this->clientModel = clientModel;

    overviewPage->setClientModel(clientModel);
    sendZcoinView->setClientModel(clientModel);
    znodeListPage->setClientModel(clientModel);
    exoAssetsPage->setClientModel(clientModel);
    sigmaPage->setClientModel(clientModel);

    if (exodusTransactionsView) {
        exodusTransactionsView->setClientModel(clientModel);
    }

    if (sendExodusView) {
        sendExodusView->setClientModel(clientModel);
    }
}

void WalletView::setWalletModel(WalletModel *walletModel)
{
    this->walletModel = walletModel;

    // Put transaction list in tabs
    zcoinTransactionList->setModel(walletModel);
    overviewPage->setWalletModel(walletModel);
    receiveCoinsPage->setModel(walletModel);
    zerocoinPage->setModel(walletModel->getAddressTableModel());
    sigmaPage->setWalletModel(walletModel);
    usedReceivingAddressesPage->setModel(walletModel->getAddressTableModel());
    usedSendingAddressesPage->setModel(walletModel->getAddressTableModel());
    znodeListPage->setWalletModel(walletModel);
    sendZcoinView->setModel(walletModel);
    exoAssetsPage->setWalletModel(walletModel);

    if (exodusTransactionsView) {
        exodusTransactionsView->setWalletModel(walletModel);
    }

    if (sendExodusView) {
        sendExodusView->setWalletModel(walletModel);
    }

    if (walletModel)
    {
        // Receive and pass through messages from wallet model
        connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int)));

        // Handle changes in encryption status
        connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int)));
        updateEncryptionStatus();

        // Balloon pop-up for new transaction
        connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
                this, SLOT(processNewTransaction(QModelIndex,int,int)));

        // Ask for passphrase if needed
        connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet()));

        // Show progress dialog
        connect(walletModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int)));
    }
}

void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
{
    // Prevent balloon-spam when initial block download is in progress
    if (!walletModel || !clientModel || clientModel->inInitialBlockDownload())
        return;

    TransactionTableModel *ttm = walletModel->getTransactionTableModel();
    if (!ttm || ttm->processingQueuedTransactions())
        return;

    QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString();
    qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
    QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
    QModelIndex index = ttm->index(start, 0, parent);
    QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
    QString label = ttm->data(index, TransactionTableModel::LabelRole).toString();

    Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label);
}

void WalletView::gotoOverviewPage()
{
    setCurrentWidget(overviewPage);
}

void WalletView::gotoExoAssetsPage()
{
    setCurrentWidget(exoAssetsPage);
}

void WalletView::gotoHistoryPage()
{
    setCurrentWidget(transactionsPage);
}

void WalletView::gotoExodusHistoryTab()
{
    if (!transactionTabs) {
        return;
    }

    setCurrentWidget(transactionsPage);
    transactionTabs->setCurrentIndex(1);
}

void WalletView::gotoBitcoinHistoryTab()
{
    setCurrentWidget(transactionsPage);

    if (transactionTabs) {
        transactionTabs->setCurrentIndex(0);
    }
}

void WalletView::focusExodusTransaction(const uint256& txid)
{
    if (!exodusTransactionsView) {
        return;
    }

    gotoExodusHistoryTab();
    exodusTransactionsView->focusTransaction(txid);
}

void WalletView::focusBitcoinHistoryTab(const QModelIndex &idx)
{
    gotoBitcoinHistoryTab();
    zcoinTransactionList->focusTransaction(idx);
}

void WalletView::gotoZnodePage()
{
    setCurrentWidget(znodeListPage);
}

void WalletView::gotoReceiveCoinsPage()
{
    setCurrentWidget(receiveCoinsPage);
}

void WalletView::gotoZerocoinPage()
{
    setCurrentWidget(zerocoinPage);
}

void WalletView::gotoSigmaPage()
{
    setCurrentWidget(sigmaPage);
}

void WalletView::gotoToolboxPage()
{
    setCurrentWidget(toolboxPage);
}

void WalletView::gotoSendCoinsPage(QString addr)
{
    setCurrentWidget(sendCoinsPage);

    if (!addr.isEmpty()){
        sendZcoinView->setAddress(addr);
    }
}

void WalletView::gotoSignMessageTab(QString addr)
{
    // calls show() in showTab_SM()
    SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this);
    signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
    signVerifyMessageDialog->setModel(walletModel);
    signVerifyMessageDialog->showTab_SM(true);

    if (!addr.isEmpty())
        signVerifyMessageDialog->setAddress_SM(addr);
}

void WalletView::gotoVerifyMessageTab(QString addr)
{
    // calls show() in showTab_VM()
    SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this);
    signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
    signVerifyMessageDialog->setModel(walletModel);
    signVerifyMessageDialog->showTab_VM(true);

    if (!addr.isEmpty())
        signVerifyMessageDialog->setAddress_VM(addr);
}

bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
{
    if (sendCoinsTabs) {
        sendCoinsTabs->setCurrentIndex(0);
    }

    return sendZcoinView->handlePaymentRequest(recipient);
}

void WalletView::showOutOfSyncWarning(bool fShow)
{
    overviewPage->showOutOfSyncWarning(fShow);
}

void WalletView::updateEncryptionStatus()
{
    Q_EMIT encryptionStatusChanged(walletModel->getEncryptionStatus());
}

void WalletView::encryptWallet(bool status)
{
    if(!walletModel)
        return;
    AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt : AskPassphraseDialog::Decrypt, this);
    dlg.setModel(walletModel);
    dlg.exec();

    updateEncryptionStatus();
}

void WalletView::backupWallet()
{
    QString filename = GUIUtil::getSaveFileName(this,
        tr("Backup Wallet"), QString(),
        tr("Wallet Data (*.dat)"), NULL);

    if (filename.isEmpty())
        return;

    if (!walletModel->backupWallet(filename)) {
        Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename),
            CClientUIInterface::MSG_ERROR);
        }
    else {
        Q_EMIT message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename),
            CClientUIInterface::MSG_INFORMATION);
    }
}

void WalletView::changePassphrase()
{
    AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
    dlg.setModel(walletModel);
    dlg.exec();
}

void WalletView::unlockWallet()
{
    if(!walletModel)
        return;
    // Unlock wallet when requested by wallet model
    if (walletModel->getEncryptionStatus() == WalletModel::Locked)
    {
        AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
        dlg.setModel(walletModel);
        dlg.exec();
    }
}

void WalletView::usedSendingAddresses()
{
    if(!walletModel)
        return;

    usedSendingAddressesPage->show();
    usedSendingAddressesPage->raise();
    usedSendingAddressesPage->activateWindow();
}

void WalletView::usedReceivingAddresses()
{
    if(!walletModel)
        return;

    usedReceivingAddressesPage->show();
    usedReceivingAddressesPage->raise();
    usedReceivingAddressesPage->activateWindow();
}

void WalletView::showProgress(const QString &title, int nProgress)
{
    if (nProgress == 0)
    {
        progressDialog = new QProgressDialog(title, "", 0, 100);
        progressDialog->setWindowModality(Qt::ApplicationModal);
        progressDialog->setMinimumDuration(0);
        progressDialog->setCancelButton(0);
        progressDialog->setAutoClose(false);
        progressDialog->setValue(0);
    }
    else if (nProgress == 100)
    {
        if (progressDialog)
        {
            progressDialog->close();
            progressDialog->deleteLater();
        }
    }
    else if (progressDialog)
        progressDialog->setValue(nProgress);
}
<commit_msg>Fix sigma page don't show error spend message<commit_after>// Copyright (c) 2011-2015 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 "walletview.h"

#include "addressbookpage.h"
#include "zerocoinpage.h"
#include "sigmapage.h"
#include "askpassphrasedialog.h"
#include "bitcoingui.h"
#include "clientmodel.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "overviewpage.h"
#include "platformstyle.h"
#include "receivecoinsdialog.h"
#include "sendcoinsdialog.h"
#include "sendmpdialog.h"
#include "lookupspdialog.h"
#include "lookuptxdialog.h"
#include "lookupaddressdialog.h"
#include "metadexcanceldialog.h"
#include "metadexdialog.h"
#include "signverifymessagedialog.h"
#include "tradehistorydialog.h"
#include "transactiontablemodel.h"
#include "transactionview.h"
#include "txhistorydialog.h"
#include "walletmodel.h"

#include "ui_interface.h"

#include <exodus/exodus.h>

#include <QAction>
#include <QActionGroup>
#include <QDebug>
#include <QDialog>
#include <QFileDialog>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QProgressDialog>
#include <QPushButton>
#include <QTableView>
#include <QVBoxLayout>

WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent):
    QStackedWidget(parent),
    clientModel(0),
    walletModel(0),
    overviewPage(0),
    sendExodusView(0),
    sigmaPage(0),
    exodusTransactionsView(0),
    zcoinTransactionsView(0),
    platformStyle(platformStyle),
    transactionTabs(0),
    sendCoinsTabs(0)
{
    overviewPage = new OverviewPage(platformStyle);
    transactionsPage = new QWidget(this);
    exoAssetsPage = new ExoAssetsDialog();
    receiveCoinsPage = new ReceiveCoinsDialog(platformStyle);
    usedSendingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
    usedReceivingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this);
    zerocoinPage = new ZerocoinPage(platformStyle, ZerocoinPage::ForEditing, this);
    sigmaPage = new SigmaPage(platformStyle, this);
    sendCoinsPage = new QWidget(this);
    toolboxPage = new QWidget(this);
    znodeListPage = new ZnodeList(platformStyle);

    setupTransactionPage();
    setupSendCoinPage();
    setupToolboxPage();

    addWidget(overviewPage);
    addWidget(exoAssetsPage);
    addWidget(transactionsPage);
    addWidget(receiveCoinsPage);
    addWidget(sendCoinsPage);
    addWidget(zerocoinPage);
    addWidget(sigmaPage);
    addWidget(toolboxPage);
    addWidget(znodeListPage);

    // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
    connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(focusBitcoinHistoryTab(QModelIndex)));
    connect(overviewPage, SIGNAL(exodusTransactionClicked(uint256)), this, SLOT(focusExodusTransaction(uint256)));

    // Subscribe message from sigma tab.
    connect(sigmaPage, SIGNAL(message(QString, QString, unsigned int)), this, SIGNAL(message(QString, QString, unsigned int)));
}

WalletView::~WalletView()
{
}

void WalletView::setupTransactionPage()
{
    // Create Zcoin transactions list
    zcoinTransactionList = new TransactionView(platformStyle);

    connect(zcoinTransactionList, SIGNAL(doubleClicked(QModelIndex)), zcoinTransactionList, SLOT(showDetails()));
    connect(zcoinTransactionList, SIGNAL(message(QString, QString, unsigned int)), this, SIGNAL(message(QString, QString, unsigned int)));

    // Create export panel for Zcoin transactions
    auto exportButton = new QPushButton(tr("&Export"));

    exportButton->setToolTip(tr("Export the data in the current tab to a file"));

    if (platformStyle->getImagesOnButtons()) {
        exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
    }

    connect(exportButton, SIGNAL(clicked()), zcoinTransactionList, SLOT(exportClicked()));

    auto exportLayout = new QHBoxLayout();
    exportLayout->addStretch();
    exportLayout->addWidget(exportButton);

    // Compose transaction list and export panel together
    auto zcoinLayout = new QVBoxLayout();
    zcoinLayout->addWidget(zcoinTransactionList);
    zcoinLayout->addLayout(exportLayout);

    zcoinTransactionsView = new QWidget();
    zcoinTransactionsView->setLayout(zcoinLayout);

    // Create tabs for transaction categories
    if (isExodusEnabled()) {
        exodusTransactionsView = new TXHistoryDialog();

        transactionTabs = new QTabWidget();
        transactionTabs->addTab(zcoinTransactionsView, tr("Zcoin"));
        transactionTabs->addTab(exodusTransactionsView, tr("Exodus"));
    }

    // Set layout for transaction page
    auto pageLayout = new QVBoxLayout();

    if (transactionTabs) {
        pageLayout->addWidget(transactionTabs);
    } else {
        pageLayout->addWidget(zcoinTransactionsView);
    }

    transactionsPage->setLayout(pageLayout);
}

void WalletView::setupSendCoinPage()
{
    sendZcoinView = new SendCoinsDialog(platformStyle);

    connect(sendZcoinView, SIGNAL(message(QString, QString, unsigned int)), this, SIGNAL(message(QString, QString, unsigned int)));

    // Create tab for coin type
    if (isExodusEnabled()) {
        sendExodusView = new SendMPDialog(platformStyle);

        sendCoinsTabs = new QTabWidget();
        sendCoinsTabs->addTab(sendZcoinView, tr("Zcoin"));
        sendCoinsTabs->addTab(sendExodusView, tr("Exodus"));
    }

    // Set layout for send coin page
    auto pageLayout = new QVBoxLayout();

    if (sendCoinsTabs) {
        pageLayout->addWidget(sendCoinsTabs);
    } else {
        pageLayout->addWidget(sendZcoinView);
    }

    sendCoinsPage->setLayout(pageLayout);
}

void WalletView::setupToolboxPage()
{
    // Create tools widget
    auto lookupAddress = new LookupAddressDialog();
    auto lookupProperty = new LookupSPDialog();
    auto lookupTransaction = new LookupTXDialog();

    // Create tab for each tool
    auto tabs = new QTabWidget();

    tabs->addTab(lookupAddress, tr("Lookup Address"));
    tabs->addTab(lookupProperty, tr("Lookup Property"));
    tabs->addTab(lookupTransaction, tr("Lookup Transaction"));

    // Set layout for toolbox page
    auto pageLayout = new QVBoxLayout();
    pageLayout->addWidget(tabs);
    toolboxPage->setLayout(pageLayout);
}

void WalletView::setBitcoinGUI(BitcoinGUI *gui)
{
    if (gui)
    {
        // Clicking on a transaction on the overview page simply sends you to transaction history page
        connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui, SLOT(gotoBitcoinHistoryTab()));
        connect(overviewPage, SIGNAL(exodusTransactionClicked(uint256)), gui, SLOT(gotoExodusHistoryTab()));

        // Receive and report messages
        connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int)));

        // Pass through encryption status changed signals
        connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int)));

        // Pass through transaction notifications
        connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString,QString)));
    }
}

void WalletView::setClientModel(ClientModel *clientModel)
{
    this->clientModel = clientModel;

    overviewPage->setClientModel(clientModel);
    sendZcoinView->setClientModel(clientModel);
    znodeListPage->setClientModel(clientModel);
    exoAssetsPage->setClientModel(clientModel);
    sigmaPage->setClientModel(clientModel);

    if (exodusTransactionsView) {
        exodusTransactionsView->setClientModel(clientModel);
    }

    if (sendExodusView) {
        sendExodusView->setClientModel(clientModel);
    }
}

void WalletView::setWalletModel(WalletModel *walletModel)
{
    this->walletModel = walletModel;

    // Put transaction list in tabs
    zcoinTransactionList->setModel(walletModel);
    overviewPage->setWalletModel(walletModel);
    receiveCoinsPage->setModel(walletModel);
    zerocoinPage->setModel(walletModel->getAddressTableModel());
    sigmaPage->setWalletModel(walletModel);
    usedReceivingAddressesPage->setModel(walletModel->getAddressTableModel());
    usedSendingAddressesPage->setModel(walletModel->getAddressTableModel());
    znodeListPage->setWalletModel(walletModel);
    sendZcoinView->setModel(walletModel);
    exoAssetsPage->setWalletModel(walletModel);

    if (exodusTransactionsView) {
        exodusTransactionsView->setWalletModel(walletModel);
    }

    if (sendExodusView) {
        sendExodusView->setWalletModel(walletModel);
    }

    if (walletModel)
    {
        // Receive and pass through messages from wallet model
        connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int)));

        // Handle changes in encryption status
        connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int)));
        updateEncryptionStatus();

        // Balloon pop-up for new transaction
        connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
                this, SLOT(processNewTransaction(QModelIndex,int,int)));

        // Ask for passphrase if needed
        connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet()));

        // Show progress dialog
        connect(walletModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int)));
    }
}

void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
{
    // Prevent balloon-spam when initial block download is in progress
    if (!walletModel || !clientModel || clientModel->inInitialBlockDownload())
        return;

    TransactionTableModel *ttm = walletModel->getTransactionTableModel();
    if (!ttm || ttm->processingQueuedTransactions())
        return;

    QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString();
    qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
    QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
    QModelIndex index = ttm->index(start, 0, parent);
    QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
    QString label = ttm->data(index, TransactionTableModel::LabelRole).toString();

    Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label);
}

void WalletView::gotoOverviewPage()
{
    setCurrentWidget(overviewPage);
}

void WalletView::gotoExoAssetsPage()
{
    setCurrentWidget(exoAssetsPage);
}

void WalletView::gotoHistoryPage()
{
    setCurrentWidget(transactionsPage);
}

void WalletView::gotoExodusHistoryTab()
{
    if (!transactionTabs) {
        return;
    }

    setCurrentWidget(transactionsPage);
    transactionTabs->setCurrentIndex(1);
}

void WalletView::gotoBitcoinHistoryTab()
{
    setCurrentWidget(transactionsPage);

    if (transactionTabs) {
        transactionTabs->setCurrentIndex(0);
    }
}

void WalletView::focusExodusTransaction(const uint256& txid)
{
    if (!exodusTransactionsView) {
        return;
    }

    gotoExodusHistoryTab();
    exodusTransactionsView->focusTransaction(txid);
}

void WalletView::focusBitcoinHistoryTab(const QModelIndex &idx)
{
    gotoBitcoinHistoryTab();
    zcoinTransactionList->focusTransaction(idx);
}

void WalletView::gotoZnodePage()
{
    setCurrentWidget(znodeListPage);
}

void WalletView::gotoReceiveCoinsPage()
{
    setCurrentWidget(receiveCoinsPage);
}

void WalletView::gotoZerocoinPage()
{
    setCurrentWidget(zerocoinPage);
}

void WalletView::gotoSigmaPage()
{
    setCurrentWidget(sigmaPage);
}

void WalletView::gotoToolboxPage()
{
    setCurrentWidget(toolboxPage);
}

void WalletView::gotoSendCoinsPage(QString addr)
{
    setCurrentWidget(sendCoinsPage);

    if (!addr.isEmpty()){
        sendZcoinView->setAddress(addr);
    }
}

void WalletView::gotoSignMessageTab(QString addr)
{
    // calls show() in showTab_SM()
    SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this);
    signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
    signVerifyMessageDialog->setModel(walletModel);
    signVerifyMessageDialog->showTab_SM(true);

    if (!addr.isEmpty())
        signVerifyMessageDialog->setAddress_SM(addr);
}

void WalletView::gotoVerifyMessageTab(QString addr)
{
    // calls show() in showTab_VM()
    SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this);
    signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
    signVerifyMessageDialog->setModel(walletModel);
    signVerifyMessageDialog->showTab_VM(true);

    if (!addr.isEmpty())
        signVerifyMessageDialog->setAddress_VM(addr);
}

bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
{
    if (sendCoinsTabs) {
        sendCoinsTabs->setCurrentIndex(0);
    }

    return sendZcoinView->handlePaymentRequest(recipient);
}

void WalletView::showOutOfSyncWarning(bool fShow)
{
    overviewPage->showOutOfSyncWarning(fShow);
}

void WalletView::updateEncryptionStatus()
{
    Q_EMIT encryptionStatusChanged(walletModel->getEncryptionStatus());
}

void WalletView::encryptWallet(bool status)
{
    if(!walletModel)
        return;
    AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt : AskPassphraseDialog::Decrypt, this);
    dlg.setModel(walletModel);
    dlg.exec();

    updateEncryptionStatus();
}

void WalletView::backupWallet()
{
    QString filename = GUIUtil::getSaveFileName(this,
        tr("Backup Wallet"), QString(),
        tr("Wallet Data (*.dat)"), NULL);

    if (filename.isEmpty())
        return;

    if (!walletModel->backupWallet(filename)) {
        Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename),
            CClientUIInterface::MSG_ERROR);
        }
    else {
        Q_EMIT message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename),
            CClientUIInterface::MSG_INFORMATION);
    }
}

void WalletView::changePassphrase()
{
    AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
    dlg.setModel(walletModel);
    dlg.exec();
}

void WalletView::unlockWallet()
{
    if(!walletModel)
        return;
    // Unlock wallet when requested by wallet model
    if (walletModel->getEncryptionStatus() == WalletModel::Locked)
    {
        AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
        dlg.setModel(walletModel);
        dlg.exec();
    }
}

void WalletView::usedSendingAddresses()
{
    if(!walletModel)
        return;

    usedSendingAddressesPage->show();
    usedSendingAddressesPage->raise();
    usedSendingAddressesPage->activateWindow();
}

void WalletView::usedReceivingAddresses()
{
    if(!walletModel)
        return;

    usedReceivingAddressesPage->show();
    usedReceivingAddressesPage->raise();
    usedReceivingAddressesPage->activateWindow();
}

void WalletView::showProgress(const QString &title, int nProgress)
{
    if (nProgress == 0)
    {
        progressDialog = new QProgressDialog(title, "", 0, 100);
        progressDialog->setWindowModality(Qt::ApplicationModal);
        progressDialog->setMinimumDuration(0);
        progressDialog->setCancelButton(0);
        progressDialog->setAutoClose(false);
        progressDialog->setValue(0);
    }
    else if (nProgress == 100)
    {
        if (progressDialog)
        {
            progressDialog->close();
            progressDialog->deleteLater();
        }
    }
    else if (progressDialog)
        progressDialog->setValue(nProgress);
}
<|endoftext|>


<commit_before>#include <math.h>
#include "quickalign.h"
#include "logger.h"

LogProb QuickAlignMatrix::dummy = -numeric_limits<double>::infinity();

QuickAlignMatrix::QuickAlignMatrix (const DiagonalEnvelope& env, const RateModel& model, double time)
  : penv (&env),
    px (env.px),
    py (env.py),
    xLen (px->length()),
    yLen (py->length()),
    xTok (env.px->tokens (model.alphabet)),
    yTok (env.py->tokens (model.alphabet)),
    cell (env.totalStorageSize * 3, -numeric_limits<double>::infinity()),
    start (-numeric_limits<double>::infinity()),
    end (-numeric_limits<double>::infinity()),
    result (-numeric_limits<double>::infinity()),
    model (model),
    time (time)
{
  // compute scores
  ProbModel pm (model, time);
  LogProbModel lpm (pm);
  submat = lpm.logSubProb;
  for (AlphTok i = 0; i < model.alphabetSize(); ++i)
    for (AlphTok j = 0; j < model.alphabetSize(); ++j)
      submat[i][j] -= lpm.logInsProb[j];

  const double gapProb = pm.ins + (1 - pm.ins) * pm.del;
  const double noGapProb = 1 - gapProb;
  const double gapExt = 1 / ( (pm.ins/gapProb) / pm.insExt + (1 - pm.ins/gapProb) / pm.delExt );
  const double noGapExt = 1 - gapExt;

  m2i = log (gapProb);
  m2d = log (noGapProb * gapProb);
  m2m = log (noGapProb * noGapProb);
  m2e = 0;

  i2i = log (gapExt);
  i2d = log (noGapExt * gapProb);
  i2m = log (noGapExt * noGapProb);
  i2e = i2m;

  d2d = log (gapExt);
  d2m = log (noGapExt);
  d2e = d2m;

  // fill
  const FastSeq& x (*px);
  const FastSeq& y (*py);

  ProgressLog (plog, 4);
  plog.initProgress ("Viterbi algorithm (%s vs %s)", x.name.c_str(), y.name.c_str());

  start = 0;
  for (SeqIdx j = 1; j <= yLen; ++j) {

    plog.logProgress (j / (double) yLen, "base %d/%d", j, yLen);

    for (DiagonalEnvelope::iterator pi = env.begin(j);
	 !pi.finished();
	 ++pi) {

      const SeqIdx i = *pi;

      mat(i,j) = max (max (mat(i-1,j-1) + m2m,
			   del(i-1,j-1) + d2m),
		      ins(i-1,j-1) + i2m);

      if (j == 1 && i == 1)
	mat(i,j) = max (mat(i,j),
			start);

      mat(i,j) += matchEmitScore(i,j);

      ins(i,j) = max (ins(i,j-1) + i2i,
		      mat(i,j-1) + m2i);

      del(i,j) = max (del(i-1,j) + d2d,
		      mat(i-1,j) + m2d);

      if (j == yLen && i == xLen)
	end = max (max (max (end,
			     mat(i,j) + m2e),
			ins(i,j) + i2e),
		   del(i,j) + d2e);
    }
  }

  result = end;

  LogThisAt(4, "Viterbi score: " << result << endl);
}

LogProb QuickAlignMatrix::cellScore (SeqIdx i, SeqIdx j, State state) const {
  LogProb cs = numeric_limits<double>::quiet_NaN();
  switch (state) {
  case Match:
    cs = mat(i,j);
    break;
  case Insert:
    cs = ins(i,j);
    break;
  case Delete:
    cs = del(i,j);
    break;
  default:
    break;
  }
  return cs;
}

const char* QuickAlignMatrix::stateToString (State state) {
  const char* s = "Unknown";
  switch (state) {
  case Start:
    s = "Start";
    break;
  case Match:
    s = "Match";
    break;
  case Insert:
    s = "Insert";
    break;
  case Delete:
    s = "Delete";
    break;
  default:
    break;
  }
  return s;
}

void QuickAlignMatrix::updateMax (double& currentMax, State& currentMaxIdx, double candidateMax, State candidateMaxIdx) {
  if (candidateMax > currentMax) {
    currentMax = candidateMax;
    currentMaxIdx = candidateMaxIdx;
  }
}

AlignPath QuickAlignMatrix::alignment() const {
  Require (resultIsFinite(), "Can't do Viterbi traceback if final score is -infinity");
  AlignPath path;
  SeqIdx i = xLen, j = yLen;
  State state = Match;
  while (state != Start) {
    LogThisAt(9, "Traceback: i=" << i << " j=" << j << " state=" << stateToString(state) << " score=" << cellScore(i,j,state) << endl);
    LogProb srcSc = -numeric_limits<double>::infinity();
    LogProb emitSc = 0;
    switch (state) {
    case Match:
      emitSc = matchEmitScore(i,j);
      --i;
      --j;
      path[0].insert (path[0].begin(), true);
      path[1].insert (path[1].begin(), true);
      updateMax (srcSc, state, mat(i,j) + m2m + emitSc, Match);
      updateMax (srcSc, state, ins(i,j) + i2m + emitSc, Insert);
      updateMax (srcSc, state, del(i,j) + d2m + emitSc, Delete);
      if (j == 0 && i == 0)
	updateMax (srcSc, state, emitSc, Start);
      Assert (srcSc == mat(i+1,j+1), "Traceback error");
      break;

    case Insert:
      --j;
      path[0].insert (path[0].begin(), false);
      path[1].insert (path[1].begin(), true);
      updateMax (srcSc, state, mat(i,j) + m2i, Match);
      updateMax (srcSc, state, ins(i,j) + i2i, Insert);
      Assert (srcSc == ins(i,j+1), "Traceback error");
      break;

    case Delete:
      --i;
      path[0].insert (path[0].begin(), true);
      path[1].insert (path[1].begin(), false);
      updateMax (srcSc, state, mat(i,j) + m2d, Match);
      updateMax (srcSc, state, ins(i,j) + i2d, Insert);
      updateMax (srcSc, state, del(i,j) + d2d, Delete);
      Assert (srcSc == del(i+1,j), "Traceback error");
      break;

    default:
      Abort ("Traceback error");
      break;
    }
  }
  return path;
}
<commit_msg>fixed missing I->D transition in QuickAlignMatrix<commit_after>#include <math.h>
#include "quickalign.h"
#include "logger.h"

LogProb QuickAlignMatrix::dummy = -numeric_limits<double>::infinity();

QuickAlignMatrix::QuickAlignMatrix (const DiagonalEnvelope& env, const RateModel& model, double time)
  : penv (&env),
    px (env.px),
    py (env.py),
    xLen (px->length()),
    yLen (py->length()),
    xTok (env.px->tokens (model.alphabet)),
    yTok (env.py->tokens (model.alphabet)),
    cell (env.totalStorageSize * 3, -numeric_limits<double>::infinity()),
    start (-numeric_limits<double>::infinity()),
    end (-numeric_limits<double>::infinity()),
    result (-numeric_limits<double>::infinity()),
    model (model),
    time (time)
{
  // compute scores
  ProbModel pm (model, time);
  LogProbModel lpm (pm);
  submat = lpm.logSubProb;
  for (AlphTok i = 0; i < model.alphabetSize(); ++i)
    for (AlphTok j = 0; j < model.alphabetSize(); ++j)
      submat[i][j] -= lpm.logInsProb[j];

  const double gapProb = pm.ins + (1 - pm.ins) * pm.del;
  const double noGapProb = 1 - gapProb;
  const double gapExt = 1 / ( (pm.ins/gapProb) / pm.insExt + (1 - pm.ins/gapProb) / pm.delExt );
  const double noGapExt = 1 - gapExt;

  m2i = log (gapProb);
  m2d = log (noGapProb * gapProb);
  m2m = log (noGapProb * noGapProb);
  m2e = 0;

  i2i = log (gapExt);
  i2d = log (noGapExt * gapProb);
  i2m = log (noGapExt * noGapProb);
  i2e = i2m;

  d2d = log (gapExt);
  d2m = log (noGapExt);
  d2e = d2m;

  // fill
  const FastSeq& x (*px);
  const FastSeq& y (*py);

  ProgressLog (plog, 4);
  plog.initProgress ("Viterbi algorithm (%s vs %s)", x.name.c_str(), y.name.c_str());

  start = 0;
  for (SeqIdx j = 1; j <= yLen; ++j) {

    plog.logProgress (j / (double) yLen, "base %d/%d", j, yLen);

    for (DiagonalEnvelope::iterator pi = env.begin(j);
	 !pi.finished();
	 ++pi) {

      const SeqIdx i = *pi;

      mat(i,j) = max (max (mat(i-1,j-1) + m2m,
			   del(i-1,j-1) + d2m),
		      ins(i-1,j-1) + i2m);

      if (j == 1 && i == 1)
	mat(i,j) = max (mat(i,j),
			start);

      mat(i,j) += matchEmitScore(i,j);

      ins(i,j) = max (ins(i,j-1) + i2i,
		      mat(i,j-1) + m2i);

      del(i,j) = max (max (ins(i-1,j) + i2d,
			   del(i-1,j) + d2d),
		      mat(i-1,j) + m2d);

      if (j == yLen && i == xLen)
	end = max (max (max (end,
			     mat(i,j) + m2e),
			ins(i,j) + i2e),
		   del(i,j) + d2e);
    }
  }

  result = end;

  LogThisAt(4, "Viterbi score: " << result << endl);
}

LogProb QuickAlignMatrix::cellScore (SeqIdx i, SeqIdx j, State state) const {
  LogProb cs = numeric_limits<double>::quiet_NaN();
  switch (state) {
  case Match:
    cs = mat(i,j);
    break;
  case Insert:
    cs = ins(i,j);
    break;
  case Delete:
    cs = del(i,j);
    break;
  default:
    break;
  }
  return cs;
}

const char* QuickAlignMatrix::stateToString (State state) {
  const char* s = "Unknown";
  switch (state) {
  case Start:
    s = "Start";
    break;
  case Match:
    s = "Match";
    break;
  case Insert:
    s = "Insert";
    break;
  case Delete:
    s = "Delete";
    break;
  default:
    break;
  }
  return s;
}

void QuickAlignMatrix::updateMax (double& currentMax, State& currentMaxIdx, double candidateMax, State candidateMaxIdx) {
  if (candidateMax > currentMax) {
    currentMax = candidateMax;
    currentMaxIdx = candidateMaxIdx;
  }
}

AlignPath QuickAlignMatrix::alignment() const {
  Require (resultIsFinite(), "Can't do Viterbi traceback if final score is -infinity");
  AlignPath path;
  SeqIdx i = xLen, j = yLen;
  State state = Match;
  while (state != Start) {
    LogThisAt(9, "Traceback: i=" << i << " j=" << j << " state=" << stateToString(state) << " score=" << cellScore(i,j,state) << endl);
    LogProb srcSc = -numeric_limits<double>::infinity();
    LogProb emitSc = 0;
    switch (state) {
    case Match:
      emitSc = matchEmitScore(i,j);
      --i;
      --j;
      path[0].insert (path[0].begin(), true);
      path[1].insert (path[1].begin(), true);
      updateMax (srcSc, state, mat(i,j) + m2m + emitSc, Match);
      updateMax (srcSc, state, ins(i,j) + i2m + emitSc, Insert);
      updateMax (srcSc, state, del(i,j) + d2m + emitSc, Delete);
      if (j == 0 && i == 0)
	updateMax (srcSc, state, emitSc, Start);
      Assert (srcSc == mat(i+1,j+1), "Traceback error");
      break;

    case Insert:
      --j;
      path[0].insert (path[0].begin(), false);
      path[1].insert (path[1].begin(), true);
      updateMax (srcSc, state, mat(i,j) + m2i, Match);
      updateMax (srcSc, state, ins(i,j) + i2i, Insert);
      Assert (srcSc == ins(i,j+1), "Traceback error");
      break;

    case Delete:
      --i;
      path[0].insert (path[0].begin(), true);
      path[1].insert (path[1].begin(), false);
      updateMax (srcSc, state, mat(i,j) + m2d, Match);
      updateMax (srcSc, state, ins(i,j) + i2d, Insert);
      updateMax (srcSc, state, del(i,j) + d2d, Delete);
      Assert (srcSc == del(i+1,j), "Traceback error");
      break;

    default:
      Abort ("Traceback error");
      break;
    }
  }
  return path;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2009, Piotr Korzuszek
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#define BOX_WIDTH 200

#include "Level.h"

#include <assert.h>

#include "common.h"
#include "race/Checkpoint.h"

Level::Level() :
	m_blocks(NULL),
	m_loaded(false),
	m_width(0),
	m_height(0)
{
	loadFromFile("resources/level.txt");
}

Level::~Level() {
	if (m_blocks != NULL) {
		unload();
	}
}

#ifdef CLIENT
void Level::draw(CL_GraphicContext &p_gc) {

	// draw tiles
	for (int x = 0; x < 10; ++x) {
		for (int y = 0; y < 10; ++y) {
			p_gc.push_translate(x * BOX_WIDTH, y * BOX_WIDTH);

			m_blocks[m_width * y + x].draw(p_gc);

			p_gc.pop_modelview();
		}
	}

	// draw tyre stripes
	m_tyreStripes.draw(p_gc);

	// draw cars
	for (std::vector<Car*>::iterator itor = m_cars.begin(); itor != m_cars.end(); ++itor) {
		(*itor)->draw(p_gc);
	}

	// draw bounds
	for (std::vector<Bound>::iterator itor = m_bounds.begin(); itor != m_bounds.end(); ++itor) {
		(*itor).draw(p_gc);
	}

#ifdef DRAW_COLLISION_OUTLINE
	// draw collision outlines
	foreach (Car *car, m_cars) {
		car->calculateCurrentCollisionOutline().draw(0, 0, CL_Colorf::red, p_gc);
	}
#endif // DRAW_COLLISION_OUTLINE

}

void Level::load(CL_GraphicContext &p_gc) {
	for (int x = 0; x < m_width; ++x) {
		for (int y = 0; y < m_height; ++y) {
			m_blocks[m_width * y + x].load(p_gc);
		}
	}
}

#endif // CLIENT

CL_String8 Level::readLine(CL_File& p_file) {
	CL_String8 line = p_file.read_string_text("", "\n", false);
	p_file.seek(1, CL_File::seek_cur);

	return line;
}

void Level::loadFromFile(const CL_String& p_filename)
{
	try {
		CL_File file(p_filename, CL_File::open_existing);
		CL_String8 line;

		line = readLine(file);

		std::vector<CL_TempString> parts = CL_StringHelp::split_text(line, " ", true);

		m_width = CL_StringHelp::text_to_int(parts[0]);
		m_height = CL_StringHelp::text_to_int(parts[2]);

		m_blocks = new Block[m_width * m_height];

		for (int i = 0; i < m_height; ++i) {
			line = readLine(file);

			parts = CL_StringHelp::split_text(line, " ", true);

			for (int j = 0; j < m_width; ++j) {
				m_blocks[m_width * i + j] = Block(decodeBlock(parts[j]), BOX_WIDTH);
			}

		}

		// read bounds num
		line = readLine(file);
		const int boundsCount = CL_StringHelp::text_to_int(line);

		// read all bounds
		float x1, y1, x2, y2;

		for (int i = 0; i < boundsCount; ++i) {
			line = readLine(file);
			parts = CL_StringHelp::split_text(line, " ", true);

			x1 = CL_StringHelp::text_to_float(parts[0]) * BOX_WIDTH;
			y1 = CL_StringHelp::text_to_float(parts[1]) * BOX_WIDTH;
			x2 = CL_StringHelp::text_to_float(parts[2]) * BOX_WIDTH;
			y2 = CL_StringHelp::text_to_float(parts[3]) * BOX_WIDTH;

			Bound bound(CL_LineSegment2f(CL_Vec2f(x1, y1), CL_Vec2f(x2, y2)));
			m_bounds.push_back(bound);
		}

		// read start positions num
		line = readLine(file);
		const int startPositionsCount = CL_StringHelp::text_to_int(line);

		// read start positions
		float x,y;

		for (int i = 0; i < startPositionsCount; ++i) {
			line = readLine(file);
			parts = CL_StringHelp::split_text(line, " ", true);

			x = CL_StringHelp::text_to_float(parts[0]) * BOX_WIDTH;
			y = CL_StringHelp::text_to_float(parts[1]) * BOX_WIDTH;

			m_startPositions[i + 1] = CL_Pointf(x, y);
		}

		file.close();

	} catch (CL_Exception e) {
		CL_Console::write_line(e.message);
	}

	m_loaded = true;

}

void Level::unload()
{
	delete[] m_blocks;
}

Block::BlockType Level::decodeBlock(const CL_String8& p_str) {

	const char c = p_str[0];

	switch (c) {
		case '0':
			return Block::BT_NONE;
		case '|':
			return Block::BT_STREET_VERT;
		case '-':
			return Block::BT_STREET_HORIZ;
		case '1':
			return Block::BT_TURN_BOTTOM_RIGHT;
		case '2':
			return Block::BT_TURN_BOTTOM_LEFT;
		case '3':
			return Block::BT_TURN_TOP_LEFT;
		case '4':
			return Block::BT_TURN_TOP_RIGHT;
		case '5':
			return Block::BT_START_LINE;
		default:
			assert(0 && "unknown char");
	}

	return Block::BT_NONE;

}

float Level::getResistance(float p_x, float p_y) {
	if (p_x < 0 || p_y < 0 || p_x >= BOX_WIDTH * m_width || p_y >= BOX_WIDTH * m_height) {
		return 0.0f;
	}

	int blockX = (int) floor(p_x / BOX_WIDTH);
	int blockY = (int) floor(p_y / BOX_WIDTH);

	int localX = (int) (p_x - blockX * BOX_WIDTH);
	int localY = (int) (p_y - blockY * BOX_WIDTH);

	return m_blocks[blockY * m_width + blockX].getResistance(localX, localY);
}

void Level::addCar(Car *p_car) {

	assert(m_loaded && "Level is not loaded");

	p_car->m_level = this;

	// fill car checkpoints
	for (int x = 0; x < m_width; ++x) {
		for (int y = 0; y < m_height; ++y) {

			const Block &block = getBlock(x, y);

			if (block.getType() != Block::BT_NONE) {

				const CL_Rectf rect(x * BOX_WIDTH, y * BOX_WIDTH, (x + 1) * BOX_WIDTH, (y + 1) * BOX_WIDTH);
				const Checkpoint checkpoint(rect);

				p_car->m_checkpoints.push_back(checkpoint);

			}

			// if this is a start/finish line, then add finish line checkpoint
			if (block.getType() == Block::BT_START_LINE) {
				const CL_Rectf rect(x * BOX_WIDTH, (y - 1) * BOX_WIDTH, (x + 1) * BOX_WIDTH, y * BOX_WIDTH);
				p_car->m_lapCheckpoint = Checkpoint(rect);
			}
		}
	}

	m_cars.push_back(p_car);
	m_carsDriftPoints[p_car] = new CL_Pointf[4];
}

void Level::removeCar(Car *p_car) {
	for (
		std::vector<Car*>::iterator itor = m_cars.begin();
		itor != m_cars.end();
		++itor
	) {
		if (*itor == p_car) {
			m_cars.erase(itor);
			break;
		}
	}

	p_car->m_level = NULL;

	delete[] m_carsDriftPoints[p_car];
	m_carsDriftPoints.erase(m_carsDriftPoints.find(p_car));
}

CL_Pointf Level::getStartPosition(int p_num) const {

	std::map<int, CL_Pointf>::const_iterator startPositionItor = m_startPositions.find(p_num);

	if (startPositionItor != m_startPositions.end()) {
		return startPositionItor->second;
	} else {
		return CL_Pointf(200, 200);
	}

}

void Level::update(unsigned p_timeElapsed)
{
#ifdef CLIENT

	// check collisions
	checkCollistions();

#ifndef NO_TYRE_STRIPES
	foreach (Car* car, m_cars) {

		CL_Pointf* lastDriftPoints = m_carsDriftPoints[car];

		const CL_Pointf &carPosition = car->getPosition();

		if (car->isDrifting()) {

			static const float tyreRadius = 10.0f;
			CL_Angle carAngle(car->getRotationRad(), cl_radians);

			CL_Vec2f v;
			float rad;

			for (int i = 0; i < 4; ++i) {

				carAngle += CL_Angle(i == 0 ? 45 : 90, cl_degrees);

				rad = carAngle.to_radians();

				v.x = cos(rad);
				v.y = sin(rad);

				v.normalize();

				v *= tyreRadius;

				CL_Pointf stripePointEnd(carPosition);
				stripePointEnd += v;

				// when last drift point is valid, then add the tire stripe
				// if not, only update the drift point
				if (lastDriftPoints[i].x != 0.0f && lastDriftPoints[i].y != 0.0f) {

					CL_Pointf stripePointStart(lastDriftPoints[i]);

					m_tyreStripes.add(stripePointStart, stripePointEnd, car);
				}

				lastDriftPoints[i].x = stripePointEnd.x;
				lastDriftPoints[i].y = stripePointEnd.y;

			}

		} else {
			// nullify all tires drift positions when no drift is done
			for (int i = 0; i < 4; ++i) {
				lastDriftPoints[i].x = 0;
				lastDriftPoints[i].y = 0;
			}
		}
	}
#endif // !NO_TYRE_STRIPES
#endif // CLIENT
}

#ifdef CLIENT
void Level::checkCollistions()
{
	CL_CollisionOutline coll1, coll2;

	foreach (Car *c1, m_cars) {
		coll1 = c1->calculateCurrentCollisionOutline();

		// check car collisions
		// TODO: later :-)
//		foreach (Car *c2, m_cars) {
//
//			if (c1 == c2) {
//				continue;
//			}
//
//			coll2 = c2->calculateCurrentCollisionOutline();
//
//			if (coll1.collide(coll2)) {
//				cl_log_event("debug", "collision");
//			}
//		}

		// check bounds collisions
		foreach (const Bound &bound, m_bounds) {
			if (coll1.collide(bound.getCollisionOutline())) {
				c1->performBoundCollision(bound);
			}
		}
	}

}
#endif // CLIENT
<commit_msg>* Little xml reading in Level<commit_after>/*
 * Copyright (c) 2009, Piotr Korzuszek
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#define BOX_WIDTH 200

#include "Level.h"

#include <assert.h>

#include "common.h"
#include "race/Checkpoint.h"

Level::Level() :
	m_blocks(NULL),
	m_loaded(false),
	m_width(0),
	m_height(0)
{
	loadFromFile("resources/level.txt");
}

Level::~Level() {
	if (m_blocks != NULL) {
		unload();
	}
}

#ifdef CLIENT
void Level::draw(CL_GraphicContext &p_gc) {

	// draw tiles
	for (int x = 0; x < 10; ++x) {
		for (int y = 0; y < 10; ++y) {
			p_gc.push_translate(x * BOX_WIDTH, y * BOX_WIDTH);

			m_blocks[m_width * y + x].draw(p_gc);

			p_gc.pop_modelview();
		}
	}

	// draw tyre stripes
	m_tyreStripes.draw(p_gc);

	// draw cars
	for (std::vector<Car*>::iterator itor = m_cars.begin(); itor != m_cars.end(); ++itor) {
		(*itor)->draw(p_gc);
	}

	// draw bounds
	for (std::vector<Bound>::iterator itor = m_bounds.begin(); itor != m_bounds.end(); ++itor) {
		(*itor).draw(p_gc);
	}

#ifdef DRAW_COLLISION_OUTLINE
	// draw collision outlines
	foreach (Car *car, m_cars) {
		car->calculateCurrentCollisionOutline().draw(0, 0, CL_Colorf::red, p_gc);
	}
#endif // DRAW_COLLISION_OUTLINE

}

void Level::load(CL_GraphicContext &p_gc) {
	for (int x = 0; x < m_width; ++x) {
		for (int y = 0; y < m_height; ++y) {
			m_blocks[m_width * y + x].load(p_gc);
		}
	}
}

#endif // CLIENT

CL_String8 Level::readLine(CL_File& p_file) {
	CL_String8 line = p_file.read_string_text("", "\n", false);
	p_file.seek(1, CL_File::seek_cur);

	return line;
}

void Level::loadFromFile(const CL_String& p_filename)
{
	try {

		CL_File file(p_filename, CL_File::open_existing);

		CL_DomDocument document(file);
		const CL_DomElement root = document.get_document_element();
		const CL_DomNode meta = document.named_item("meta");

		// read required meta values
		const CL_DomNode metaName = meta.named_item("name");
		const CL_DomNode metaSize = meta.named_item("size");

		if (metaName.is_null() || metaSize.is_null()) {
			cl_log_event("error", "Missing meta data in level file");
			return;
		}

		// read level size
		const CL_DomNode metaSizeWidth = metaSize.named_item("width");
		const CL_DomNode metaSizeHeigth = metaSize.named_item("height");

		if (metaSizeWidth.is_null() || metaSizeHeigth.is_null()) {
			cl_log_event("error", "Missing meta size in level file");
			return;
		}

		// assign these meta values
		m_width = CL_StringHelp::local8_to_int(metaSizeWidth.get_);

//		CL_String8 line;
//
//		line = readLine(file);
//
//		std::vector<CL_TempString> parts = CL_StringHelp::split_text(line, " ", true);
//
//		m_width = CL_StringHelp::text_to_int(parts[0]);
//		m_height = CL_StringHelp::text_to_int(parts[2]);
//
//		m_blocks = new Block[m_width * m_height];
//
//
//		for (int i = 0; i < m_height; ++i) {
//			line = readLine(file);
//
//			parts = CL_StringHelp::split_text(line, " ", true);
//
//			for (int j = 0; j < m_width; ++j) {
//				m_blocks[m_width * i + j] = Block(decodeBlock(parts[j]), BOX_WIDTH);
//			}
//
//		}
//
//		// read bounds num
//		line = readLine(file);
//		const int boundsCount = CL_StringHelp::text_to_int(line);
//
//		// read all bounds
//		float x1, y1, x2, y2;
//
//		for (int i = 0; i < boundsCount; ++i) {
//			line = readLine(file);
//			parts = CL_StringHelp::split_text(line, " ", true);
//
//			x1 = CL_StringHelp::text_to_float(parts[0]) * BOX_WIDTH;
//			y1 = CL_StringHelp::text_to_float(parts[1]) * BOX_WIDTH;
//			x2 = CL_StringHelp::text_to_float(parts[2]) * BOX_WIDTH;
//			y2 = CL_StringHelp::text_to_float(parts[3]) * BOX_WIDTH;
//
//			Bound bound(CL_LineSegment2f(CL_Vec2f(x1, y1), CL_Vec2f(x2, y2)));
//			m_bounds.push_back(bound);
//		}
//
//		// read start positions num
//		line = readLine(file);
//		const int startPositionsCount = CL_StringHelp::text_to_int(line);
//
//		// read start positions
//		float x,y;
//
//		for (int i = 0; i < startPositionsCount; ++i) {
//			line = readLine(file);
//			parts = CL_StringHelp::split_text(line, " ", true);
//
//			x = CL_StringHelp::text_to_float(parts[0]) * BOX_WIDTH;
//			y = CL_StringHelp::text_to_float(parts[1]) * BOX_WIDTH;
//
//			m_startPositions[i + 1] = CL_Pointf(x, y);
//		}

		file.close();

	} catch (CL_Exception e) {
		CL_Console::write_line(e.message);
	}

	m_loaded = true;

}

void Level::unload()
{
	delete[] m_blocks;
}

Block::BlockType Level::decodeBlock(const CL_String8& p_str) {

	const char c = p_str[0];

	switch (c) {
		case '0':
			return Block::BT_NONE;
		case '|':
			return Block::BT_STREET_VERT;
		case '-':
			return Block::BT_STREET_HORIZ;
		case '1':
			return Block::BT_TURN_BOTTOM_RIGHT;
		case '2':
			return Block::BT_TURN_BOTTOM_LEFT;
		case '3':
			return Block::BT_TURN_TOP_LEFT;
		case '4':
			return Block::BT_TURN_TOP_RIGHT;
		case '5':
			return Block::BT_START_LINE;
		default:
			assert(0 && "unknown char");
	}

	return Block::BT_NONE;

}

float Level::getResistance(float p_x, float p_y) {
	if (p_x < 0 || p_y < 0 || p_x >= BOX_WIDTH * m_width || p_y >= BOX_WIDTH * m_height) {
		return 0.0f;
	}

	int blockX = (int) floor(p_x / BOX_WIDTH);
	int blockY = (int) floor(p_y / BOX_WIDTH);

	int localX = (int) (p_x - blockX * BOX_WIDTH);
	int localY = (int) (p_y - blockY * BOX_WIDTH);

	return m_blocks[blockY * m_width + blockX].getResistance(localX, localY);
}

void Level::addCar(Car *p_car) {

	assert(m_loaded && "Level is not loaded");

	p_car->m_level = this;

	// fill car checkpoints
	for (int x = 0; x < m_width; ++x) {
		for (int y = 0; y < m_height; ++y) {

			const Block &block = getBlock(x, y);

			if (block.getType() != Block::BT_NONE) {

				const CL_Rectf rect(x * BOX_WIDTH, y * BOX_WIDTH, (x + 1) * BOX_WIDTH, (y + 1) * BOX_WIDTH);
				const Checkpoint checkpoint(rect);

				p_car->m_checkpoints.push_back(checkpoint);

			}

			// if this is a start/finish line, then add finish line checkpoint
			if (block.getType() == Block::BT_START_LINE) {
				const CL_Rectf rect(x * BOX_WIDTH, (y - 1) * BOX_WIDTH, (x + 1) * BOX_WIDTH, y * BOX_WIDTH);
				p_car->m_lapCheckpoint = Checkpoint(rect);
			}
		}
	}

	m_cars.push_back(p_car);
	m_carsDriftPoints[p_car] = new CL_Pointf[4];
}

void Level::removeCar(Car *p_car) {
	for (
		std::vector<Car*>::iterator itor = m_cars.begin();
		itor != m_cars.end();
		++itor
	) {
		if (*itor == p_car) {
			m_cars.erase(itor);
			break;
		}
	}

	p_car->m_level = NULL;

	delete[] m_carsDriftPoints[p_car];
	m_carsDriftPoints.erase(m_carsDriftPoints.find(p_car));
}

CL_Pointf Level::getStartPosition(int p_num) const {

	std::map<int, CL_Pointf>::const_iterator startPositionItor = m_startPositions.find(p_num);

	if (startPositionItor != m_startPositions.end()) {
		return startPositionItor->second;
	} else {
		return CL_Pointf(200, 200);
	}

}

void Level::update(unsigned p_timeElapsed)
{
#ifdef CLIENT

	// check collisions
	checkCollistions();

#ifndef NO_TYRE_STRIPES
	foreach (Car* car, m_cars) {

		CL_Pointf* lastDriftPoints = m_carsDriftPoints[car];

		const CL_Pointf &carPosition = car->getPosition();

		if (car->isDrifting()) {

			static const float tyreRadius = 10.0f;
			CL_Angle carAngle(car->getRotationRad(), cl_radians);

			CL_Vec2f v;
			float rad;

			for (int i = 0; i < 4; ++i) {

				carAngle += CL_Angle(i == 0 ? 45 : 90, cl_degrees);

				rad = carAngle.to_radians();

				v.x = cos(rad);
				v.y = sin(rad);

				v.normalize();

				v *= tyreRadius;

				CL_Pointf stripePointEnd(carPosition);
				stripePointEnd += v;

				// when last drift point is valid, then add the tire stripe
				// if not, only update the drift point
				if (lastDriftPoints[i].x != 0.0f && lastDriftPoints[i].y != 0.0f) {

					CL_Pointf stripePointStart(lastDriftPoints[i]);

					m_tyreStripes.add(stripePointStart, stripePointEnd, car);
				}

				lastDriftPoints[i].x = stripePointEnd.x;
				lastDriftPoints[i].y = stripePointEnd.y;

			}

		} else {
			// nullify all tires drift positions when no drift is done
			for (int i = 0; i < 4; ++i) {
				lastDriftPoints[i].x = 0;
				lastDriftPoints[i].y = 0;
			}
		}
	}
#endif // !NO_TYRE_STRIPES
#endif // CLIENT
}

#ifdef CLIENT
void Level::checkCollistions()
{
	CL_CollisionOutline coll1, coll2;

	foreach (Car *c1, m_cars) {
		coll1 = c1->calculateCurrentCollisionOutline();

		// check car collisions
		// TODO: later :-)
//		foreach (Car *c2, m_cars) {
//
//			if (c1 == c2) {
//				continue;
//			}
//
//			coll2 = c2->calculateCurrentCollisionOutline();
//
//			if (coll1.collide(coll2)) {
//				cl_log_event("debug", "collision");
//			}
//		}

		// check bounds collisions
		foreach (const Bound &bound, m_bounds) {
			if (coll1.collide(bound.getCollisionOutline())) {
				c1->performBoundCollision(bound);
			}
		}
	}

}
#endif // CLIENT
<|endoftext|>