<commit_before>#include "addressbookpage.h"
#include "ui_addressbookpage.h"

#include "addresstablemodel.h"
#include "optionsmodel.h"
#include "bitcoingui.h"
#include "editaddressdialog.h"
#include "csvmodelwriter.h"
#include "guiutil.h"
#include "qt/decoration.h"

#include <QSortFilterProxyModel>
#include <QClipboard>
#include <QMessageBox>
#include <QMenu>

#ifdef USE_QRCODE
#include "qrcodedialog.h"
#endif

AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget* parent)
             : QDialog(parent)
             , ui(new Ui::AddressBookPage)
             , model(nullptr)
             , optionsModel(nullptr)
             , mode(mode)
             , tab(tab)
{
    ui->setupUi(this);

    resize(GRC::ScaleSize(this, width(), height()));

#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
    ui->newAddressButton->setIcon(QIcon());
    ui->copyToClipboardButton->setIcon(QIcon());
    ui->deleteButton->setIcon(QIcon());
#endif

#ifndef USE_QRCODE
    ui->showQRCodeButton->setVisible(false);
#endif

    switch(mode)
    {
    case ForSending:
        connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept()));
        ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
        ui->tableView->setFocus();
        break;
    case ForEditing:
        ui->okayButtonBox->setVisible(false);
        break;
    }
    switch(tab)
    {
    case SendingTab:
        ui->explanationLabel->setVisible(false);
        ui->deleteButton->setVisible(true);
        ui->signMessageButton->setVisible(false);
        break;
    case ReceivingTab:
        ui->deleteButton->setVisible(false);
        ui->verifyMessageButton->setVisible(false);
        ui->signMessageButton->setVisible(true);
        break;
    }

    // Context menu actions
    QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
    QAction *copyAddressAction = new QAction(ui->copyToClipboardButton->text(), this);
    QAction *editAction = new QAction(tr("&Edit"), this);
    QAction *showQRCodeAction = new QAction(ui->showQRCodeButton->text(), this);
    QAction *signMessageAction = new QAction(ui->signMessageButton->text(), this);
    QAction *verifyMessageAction = new QAction(ui->verifyMessageButton->text(), this);
    deleteAction = new QAction(ui->deleteButton->text(), this);

    // Build context menu
    contextMenu = new QMenu(this);
    contextMenu->addAction(copyAddressAction);
    contextMenu->addAction(copyLabelAction);
    contextMenu->addAction(editAction);
    if(tab == SendingTab)
        contextMenu->addAction(deleteAction);
    contextMenu->addSeparator();
    contextMenu->addAction(showQRCodeAction);
    if(tab == ReceivingTab)
        contextMenu->addAction(signMessageAction);
    else if(tab == SendingTab)
        contextMenu->addAction(verifyMessageAction);

    // Connect signals for context menu actions
    connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyToClipboardButton_clicked()));
    connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction()));
    connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction()));
    connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteButton_clicked()));
    connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCodeButton_clicked()));
    connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessageButton_clicked()));
    connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessageButton_clicked()));

    connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));

    // Pass through accept action from button box
    connect(ui->okayButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
}

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

void AddressBookPage::setModel(AddressTableModel *model)
{
    this->model = model;
    if(!model)
        return;

    proxyModel = new QSortFilterProxyModel(this);
    proxyModel->setSourceModel(model);
    proxyModel->setDynamicSortFilter(true);
    proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
    proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);

    switch(tab)
    {
    case ReceivingTab:
        // Receive filter
        proxyModel->setFilterRole(AddressTableModel::TypeRole);
        proxyModel->setFilterFixedString(AddressTableModel::Receive);
        break;
    case SendingTab:
        // Send filter
        proxyModel->setFilterRole(AddressTableModel::TypeRole);
        proxyModel->setFilterFixedString(AddressTableModel::Send);
        break;
    }

    filterProxyModel = new QSortFilterProxyModel(this);
    filterProxyModel->setSourceModel(proxyModel);
    filterProxyModel->setDynamicSortFilter(true);
    filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
    filterProxyModel->setFilterKeyColumn(-1); // All columns

    ui->tableView->setModel(filterProxyModel);
    ui->tableView->sortByColumn(0, Qt::AscendingOrder);

    // Set column widths
    ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
    ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
    ui->tableView->horizontalHeader()->resizeSection(AddressTableModel::Address, 320);

    connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            this, SLOT(selectionChanged()));

    // Select row for newly created address
    connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
            this, SLOT(selectNewAddress(QModelIndex,int,int)));

    selectionChanged();
}

void AddressBookPage::setOptionsModel(OptionsModel *optionsModel)
{
    this->optionsModel = optionsModel;
}

void AddressBookPage::on_copyToClipboardButton_clicked()
{
    GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
}

void AddressBookPage::onCopyLabelAction()
{
    GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label);
}

void AddressBookPage::onEditAction()
{
    if(!ui->tableView->selectionModel())
        return;
    QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
    if(indexes.isEmpty())
        return;

    EditAddressDialog dlg(
            tab == SendingTab ?
            EditAddressDialog::EditSendingAddress :
            EditAddressDialog::EditReceivingAddress);
    dlg.setModel(model);
    QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
    dlg.loadRow(origIndex.row());
    dlg.exec();
}

void AddressBookPage::on_signMessageButton_clicked()
{
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
    QString addr;

    for (QModelIndex index : indexes) {
        QVariant address = index.data();
        addr = address.toString();
    }

    emit signMessage(addr);
}

void AddressBookPage::on_verifyMessageButton_clicked()
{
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
    QString addr;

    for (QModelIndex index : indexes) {
        QVariant address = index.data();
        addr = address.toString();
    }

    emit verifyMessage(addr);
}

void AddressBookPage::on_newAddressButton_clicked()
{
    if(!model)
        return;
    EditAddressDialog dlg(
            tab == SendingTab ?
            EditAddressDialog::NewSendingAddress :
            EditAddressDialog::NewReceivingAddress, this);
    dlg.setModel(model);
    if(dlg.exec())
    {
        newAddressToSelect = dlg.getAddress();
    }
}

void AddressBookPage::on_deleteButton_clicked()
{
    QTableView *table = ui->tableView;
    if(!table->selectionModel())
        return;
    QModelIndexList indexes = table->selectionModel()->selectedRows();
    if(!indexes.isEmpty())
    {
        table->model()->removeRow(indexes.at(0).row());
    }
}

void AddressBookPage::selectionChanged()
{
    // Set button states based on selected tab and selection
    QTableView *table = ui->tableView;
    if(!table->selectionModel())
        return;

    if(table->selectionModel()->hasSelection())
    {
        switch(tab)
        {
        case SendingTab:
            // In sending tab, allow deletion of selection
            ui->deleteButton->setEnabled(true);
            ui->deleteButton->setVisible(true);
            deleteAction->setEnabled(true);
            ui->signMessageButton->setEnabled(false);
            ui->signMessageButton->setVisible(false);
            ui->verifyMessageButton->setEnabled(true);
            ui->verifyMessageButton->setVisible(true);
            break;
        case ReceivingTab:
            // Deleting receiving addresses, however, is not allowed
            ui->deleteButton->setEnabled(false);
            ui->deleteButton->setVisible(false);
            deleteAction->setEnabled(false);
            ui->signMessageButton->setEnabled(true);
            ui->signMessageButton->setVisible(true);
            ui->verifyMessageButton->setEnabled(false);
            ui->verifyMessageButton->setVisible(false);
            break;
        }
        ui->copyToClipboardButton->setEnabled(true);
        ui->showQRCodeButton->setEnabled(true);
    }
    else
    {
        ui->deleteButton->setEnabled(false);
        ui->showQRCodeButton->setEnabled(false);
        ui->copyToClipboardButton->setEnabled(false);
        ui->signMessageButton->setEnabled(false);
        ui->verifyMessageButton->setEnabled(false);
    }
}

void AddressBookPage::done(int retval)
{
    QTableView *table = ui->tableView;
    if(!table->selectionModel() || !table->model())
        return;
    // When this is a tab/widget and not a model dialog, ignore "done"
    if(mode == ForEditing)
        return;

    // Figure out which address was selected, and return it
    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);

    for (QModelIndex index : indexes) {
        QVariant address = table->model()->data(index);
        returnValue = address.toString();
    }

    if(returnValue.isEmpty())
    {
        // If no address entry selected, return rejected
        retval = Rejected;
    }

    QDialog::done(retval);
}

void AddressBookPage::exportClicked()
{
    // CSV is currently the only supported format
    QString filename = GUIUtil::getSaveFileName(
            this,
            tr("Export Address Book Data"), QString(),
            tr("Comma separated file (*.csv)"));

    if (filename.isNull()) return;

    CSVModelWriter writer(filename);

    // name, column, role
    writer.setModel(proxyModel);
    writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
    writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);

    if(!writer.write())
    {
        QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
                              QMessageBox::Abort, QMessageBox::Abort);
    }
}

void AddressBookPage::changeFilter(const QString& needle)
{
    filterProxyModel->setFilterFixedString(needle);
}

void AddressBookPage::on_showQRCodeButton_clicked()
{
#ifdef USE_QRCODE
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);

    for (QModelIndex index : indexes) {
        QString address = index.data().toString(), label = index.sibling(index.row(), 0).data(Qt::EditRole).toString();

        QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this);
        if(optionsModel)
            dialog->setModel(optionsModel);
        dialog->setAttribute(Qt::WA_DeleteOnClose);
        dialog->show();
    }
#endif
}

void AddressBookPage::contextualMenu(const QPoint &point)
{
    QModelIndex index = ui->tableView->indexAt(point);
    if(index.isValid())
    {
        contextMenu->exec(QCursor::pos());
    }
}

void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int end)
{
    QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
    if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
    {
        // Select row of newly created address, once
        ui->tableView->setFocus();
        ui->tableView->selectRow(idx.row());
        newAddressToSelect.clear();
    }
}
<commit_msg>Fix address book selected model record when editing<commit_after>#include "addressbookpage.h"
#include "ui_addressbookpage.h"

#include "addresstablemodel.h"
#include "optionsmodel.h"
#include "bitcoingui.h"
#include "editaddressdialog.h"
#include "csvmodelwriter.h"
#include "guiutil.h"
#include "qt/decoration.h"

#include <QSortFilterProxyModel>
#include <QClipboard>
#include <QMessageBox>
#include <QMenu>

#ifdef USE_QRCODE
#include "qrcodedialog.h"
#endif

AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget* parent)
             : QDialog(parent)
             , ui(new Ui::AddressBookPage)
             , model(nullptr)
             , optionsModel(nullptr)
             , mode(mode)
             , tab(tab)
{
    ui->setupUi(this);

    resize(GRC::ScaleSize(this, width(), height()));

#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
    ui->newAddressButton->setIcon(QIcon());
    ui->copyToClipboardButton->setIcon(QIcon());
    ui->deleteButton->setIcon(QIcon());
#endif

#ifndef USE_QRCODE
    ui->showQRCodeButton->setVisible(false);
#endif

    switch(mode)
    {
    case ForSending:
        connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept()));
        ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
        ui->tableView->setFocus();
        break;
    case ForEditing:
        ui->okayButtonBox->setVisible(false);
        break;
    }
    switch(tab)
    {
    case SendingTab:
        ui->explanationLabel->setVisible(false);
        ui->deleteButton->setVisible(true);
        ui->signMessageButton->setVisible(false);
        break;
    case ReceivingTab:
        ui->deleteButton->setVisible(false);
        ui->verifyMessageButton->setVisible(false);
        ui->signMessageButton->setVisible(true);
        break;
    }

    // Context menu actions
    QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
    QAction *copyAddressAction = new QAction(ui->copyToClipboardButton->text(), this);
    QAction *editAction = new QAction(tr("&Edit"), this);
    QAction *showQRCodeAction = new QAction(ui->showQRCodeButton->text(), this);
    QAction *signMessageAction = new QAction(ui->signMessageButton->text(), this);
    QAction *verifyMessageAction = new QAction(ui->verifyMessageButton->text(), this);
    deleteAction = new QAction(ui->deleteButton->text(), this);

    // Build context menu
    contextMenu = new QMenu(this);
    contextMenu->addAction(copyAddressAction);
    contextMenu->addAction(copyLabelAction);
    contextMenu->addAction(editAction);
    if(tab == SendingTab)
        contextMenu->addAction(deleteAction);
    contextMenu->addSeparator();
    contextMenu->addAction(showQRCodeAction);
    if(tab == ReceivingTab)
        contextMenu->addAction(signMessageAction);
    else if(tab == SendingTab)
        contextMenu->addAction(verifyMessageAction);

    // Connect signals for context menu actions
    connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyToClipboardButton_clicked()));
    connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction()));
    connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction()));
    connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteButton_clicked()));
    connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCodeButton_clicked()));
    connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessageButton_clicked()));
    connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessageButton_clicked()));

    connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));

    // Pass through accept action from button box
    connect(ui->okayButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
}

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

void AddressBookPage::setModel(AddressTableModel *model)
{
    this->model = model;
    if(!model)
        return;

    proxyModel = new QSortFilterProxyModel(this);
    proxyModel->setSourceModel(model);
    proxyModel->setDynamicSortFilter(true);
    proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
    proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);

    switch(tab)
    {
    case ReceivingTab:
        // Receive filter
        proxyModel->setFilterRole(AddressTableModel::TypeRole);
        proxyModel->setFilterFixedString(AddressTableModel::Receive);
        break;
    case SendingTab:
        // Send filter
        proxyModel->setFilterRole(AddressTableModel::TypeRole);
        proxyModel->setFilterFixedString(AddressTableModel::Send);
        break;
    }

    filterProxyModel = new QSortFilterProxyModel(this);
    filterProxyModel->setSourceModel(proxyModel);
    filterProxyModel->setDynamicSortFilter(true);
    filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
    filterProxyModel->setFilterKeyColumn(-1); // All columns

    ui->tableView->setModel(filterProxyModel);
    ui->tableView->sortByColumn(0, Qt::AscendingOrder);

    // Set column widths
    ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
    ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
    ui->tableView->horizontalHeader()->resizeSection(AddressTableModel::Address, 320);

    connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            this, SLOT(selectionChanged()));

    // Select row for newly created address
    connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
            this, SLOT(selectNewAddress(QModelIndex,int,int)));

    selectionChanged();
}

void AddressBookPage::setOptionsModel(OptionsModel *optionsModel)
{
    this->optionsModel = optionsModel;
}

void AddressBookPage::on_copyToClipboardButton_clicked()
{
    GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
}

void AddressBookPage::onCopyLabelAction()
{
    GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label);
}

void AddressBookPage::onEditAction()
{
    if(!ui->tableView->selectionModel())
        return;
    QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
    if(indexes.isEmpty())
        return;

    EditAddressDialog dlg(
            tab == SendingTab ?
            EditAddressDialog::EditSendingAddress :
            EditAddressDialog::EditReceivingAddress);

    QModelIndex origIndex = filterProxyModel->mapToSource(indexes.at(0));
    origIndex = proxyModel->mapToSource(origIndex);

    dlg.setModel(model);
    dlg.loadRow(origIndex.row());
    dlg.exec();
}

void AddressBookPage::on_signMessageButton_clicked()
{
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
    QString addr;

    for (QModelIndex index : indexes) {
        QVariant address = index.data();
        addr = address.toString();
    }

    emit signMessage(addr);
}

void AddressBookPage::on_verifyMessageButton_clicked()
{
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
    QString addr;

    for (QModelIndex index : indexes) {
        QVariant address = index.data();
        addr = address.toString();
    }

    emit verifyMessage(addr);
}

void AddressBookPage::on_newAddressButton_clicked()
{
    if(!model)
        return;
    EditAddressDialog dlg(
            tab == SendingTab ?
            EditAddressDialog::NewSendingAddress :
            EditAddressDialog::NewReceivingAddress, this);
    dlg.setModel(model);
    if(dlg.exec())
    {
        newAddressToSelect = dlg.getAddress();
    }
}

void AddressBookPage::on_deleteButton_clicked()
{
    QTableView *table = ui->tableView;
    if(!table->selectionModel())
        return;
    QModelIndexList indexes = table->selectionModel()->selectedRows();
    if(!indexes.isEmpty())
    {
        table->model()->removeRow(indexes.at(0).row());
    }
}

void AddressBookPage::selectionChanged()
{
    // Set button states based on selected tab and selection
    QTableView *table = ui->tableView;
    if(!table->selectionModel())
        return;

    if(table->selectionModel()->hasSelection())
    {
        switch(tab)
        {
        case SendingTab:
            // In sending tab, allow deletion of selection
            ui->deleteButton->setEnabled(true);
            ui->deleteButton->setVisible(true);
            deleteAction->setEnabled(true);
            ui->signMessageButton->setEnabled(false);
            ui->signMessageButton->setVisible(false);
            ui->verifyMessageButton->setEnabled(true);
            ui->verifyMessageButton->setVisible(true);
            break;
        case ReceivingTab:
            // Deleting receiving addresses, however, is not allowed
            ui->deleteButton->setEnabled(false);
            ui->deleteButton->setVisible(false);
            deleteAction->setEnabled(false);
            ui->signMessageButton->setEnabled(true);
            ui->signMessageButton->setVisible(true);
            ui->verifyMessageButton->setEnabled(false);
            ui->verifyMessageButton->setVisible(false);
            break;
        }
        ui->copyToClipboardButton->setEnabled(true);
        ui->showQRCodeButton->setEnabled(true);
    }
    else
    {
        ui->deleteButton->setEnabled(false);
        ui->showQRCodeButton->setEnabled(false);
        ui->copyToClipboardButton->setEnabled(false);
        ui->signMessageButton->setEnabled(false);
        ui->verifyMessageButton->setEnabled(false);
    }
}

void AddressBookPage::done(int retval)
{
    QTableView *table = ui->tableView;
    if(!table->selectionModel() || !table->model())
        return;
    // When this is a tab/widget and not a model dialog, ignore "done"
    if(mode == ForEditing)
        return;

    // Figure out which address was selected, and return it
    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);

    for (QModelIndex index : indexes) {
        QVariant address = table->model()->data(index);
        returnValue = address.toString();
    }

    if(returnValue.isEmpty())
    {
        // If no address entry selected, return rejected
        retval = Rejected;
    }

    QDialog::done(retval);
}

void AddressBookPage::exportClicked()
{
    // CSV is currently the only supported format
    QString filename = GUIUtil::getSaveFileName(
            this,
            tr("Export Address Book Data"), QString(),
            tr("Comma separated file (*.csv)"));

    if (filename.isNull()) return;

    CSVModelWriter writer(filename);

    // name, column, role
    writer.setModel(proxyModel);
    writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
    writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);

    if(!writer.write())
    {
        QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
                              QMessageBox::Abort, QMessageBox::Abort);
    }
}

void AddressBookPage::changeFilter(const QString& needle)
{
    filterProxyModel->setFilterFixedString(needle);
}

void AddressBookPage::on_showQRCodeButton_clicked()
{
#ifdef USE_QRCODE
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);

    for (QModelIndex index : indexes) {
        QString address = index.data().toString(), label = index.sibling(index.row(), 0).data(Qt::EditRole).toString();

        QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this);
        if(optionsModel)
            dialog->setModel(optionsModel);
        dialog->setAttribute(Qt::WA_DeleteOnClose);
        dialog->show();
    }
#endif
}

void AddressBookPage::contextualMenu(const QPoint &point)
{
    QModelIndex index = ui->tableView->indexAt(point);
    if(index.isValid())
    {
        contextMenu->exec(QCursor::pos());
    }
}

void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int end)
{
    QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
    if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
    {
        // Select row of newly created address, once
        ui->tableView->setFocus();
        ui->tableView->selectRow(idx.row());
        newAddressToSelect.clear();
    }
}
<|endoftext|>


<commit_before>#include <string>

#include "transactiondesc.h"
#include "darksilkunits.h"
#include "guiutil.h"
#include "base58.h"
#include "main.h"
#include "paymentserver.h"
#include "transactionrecord.h"
#include "timedata.h"
#include "ui_interface.h"
#include "consensus/consensus.h"
#include "wallet/wallet.h"
#include "txdb-leveldb.h"

QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
{
    AssertLockHeld(cs_main);
    if (!IsFinalTx(wtx, nBestHeight + 1))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
            return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight);
        else
            return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime));
    }
    else
    {
        int nDepth = wtx.GetDepthInMainChain();
        if (nDepth < 0)
            return tr("conflicted");
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
            return tr("%1/offline").arg(nDepth);
        else if (nDepth < 10)
            return tr("%1/unconfirmed").arg(nDepth);
        else
            return tr("%1 confirmations").arg(nDepth);
    }
}

QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit)
{
    QString strHTML;

    LOCK2(cs_main, wallet->cs_wallet);
    strHTML.reserve(4000);
    strHTML += "";

    int64_t nTime = wtx.GetTxTime();
    CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
    CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
    CAmount nNet = nCredit - nDebit;

    strHTML += "" + tr("Status") + ": " + FormatTxStatus(wtx);
    int nRequests = wtx.GetRequestCount();
    if (nRequests != -1)
    {
        if (nRequests == 0)
            strHTML += tr(", has not been successfully broadcast yet");
        else if (nRequests > 0)
            strHTML += tr(", broadcast through %n node(s)", "", nRequests);
    }
    strHTML += "
";

    strHTML += "" + tr("Date") + ": " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "
";

    //
    // From
    //
    if (wtx.IsCoinBase() || wtx.IsCoinStake())
    {
        strHTML += "" + tr("Source") + ": " + tr("Generated") + "
";
    }
    else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty())
    {
        // Online transaction
        strHTML += "" + tr("From") + ": " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "
";
    }
    else
    {
        // Offline transaction
        if (nNet > 0)
        {
            // Credit
            if (CDarkSilkAddress(rec->address).IsValid())
            {
                CTxDestination address = CDarkSilkAddress(rec->address).Get();
                if (wallet->mapAddressBook.count(address))
                {
                    strHTML += "" + tr("From") + ": " + tr("unknown") + "
";
                    strHTML += "" + tr("To") + ": ";
                    strHTML += GUIUtil::HtmlEscape(rec->address);
                    if (!wallet->mapAddressBook[address].empty())
                        strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + ")";
                    else
                        strHTML += " (" + tr("own address") + ")";
                    strHTML += "
";
                }
            }
        }
    }

    //
    // To
    //
    if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty())
    {
        // Online transaction
        std::string strAddress = wtx.mapValue["to"];
        strHTML += "" + tr("To") + ": ";
        CTxDestination dest = CDarkSilkAddress(strAddress).Get();
        if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].empty())
            strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest]) + " ";
        strHTML += GUIUtil::HtmlEscape(strAddress) + "
";
    }

    //
    // Amount
    //
    if (wtx.IsCoinBase() && nCredit == 0)
    {
        //
        // Coinbase
        //
        CAmount nUnmatured = 0;
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
        strHTML += "" + tr("Credit") + ": ";
        if (wtx.IsInMainChain())
            strHTML += DarkSilkUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
        else
            strHTML += "(" + tr("not accepted") + ")";
        strHTML += "
";
    }
    else if (nNet > 0)
    {
        //
        // Credit
        //
        strHTML += "" + tr("Credit") + ": " + DarkSilkUnits::formatWithUnit(unit, nNet) + "
";
    }
    else
    {
        bool fAllFromMe = true;
        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
            fAllFromMe = fAllFromMe && wallet->IsMine(txin);

        bool fAllToMe = true;
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            fAllToMe = fAllToMe && wallet->IsMine(txout);

        if (fAllFromMe)
        {
            //
            // Debit
            //
            BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            {
                if (wallet->IsMine(txout))
                    continue;

                if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty())
                {
                    // Offline transaction
                    CTxDestination address;
                    if (ExtractDestination(txout.scriptPubKey, address))
                    {
                        strHTML += "" + tr("To") + ": ";
                        if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty())
                            strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + " ";
                        strHTML += GUIUtil::HtmlEscape(CDarkSilkAddress(address).ToString());
                        strHTML += "
";
                    }
                }

                strHTML += "" + tr("Debit") + ": " + DarkSilkUnits::formatWithUnit(unit, -txout.nValue) + "
";
            }

            if (fAllToMe)
            {
                // Payment to self
                CAmount nChange = wtx.GetChange();
                CAmount nValue = nCredit - nChange;
                strHTML += "" + tr("Debit") + ": " + DarkSilkUnits::formatWithUnit(unit, -nValue) + "
";
                strHTML += "" + tr("Credit") + ": " + DarkSilkUnits::formatWithUnit(unit, nValue) + "
";
            }
            CTransactionPoS txPoS;
            CTransaction tx = CTransaction(wtx);
            CAmount nTxFee = nDebit - txPoS.GetValueOut(tx);
            if (nTxFee > 0)
                strHTML += "" + tr("Transaction fee") + ": " + DarkSilkUnits::formatWithUnit(unit, -nTxFee) + "
";
        }
        else
        {
            //
            // Mixed debit transaction
            //
            BOOST_FOREACH(const CTxIn& txin, wtx.vin)
                if (wallet->IsMine(txin))
                    strHTML += "" + tr("Debit") + ": " + DarkSilkUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
";
            BOOST_FOREACH(const CTxOut& txout, wtx.vout)
                if (wallet->IsMine(txout))
                    strHTML += "" + tr("Credit") + ": " + DarkSilkUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
";
        }
    }

    strHTML += "" + tr("Net amount") + ": " + DarkSilkUnits::formatWithUnit(unit, nNet, true) + "
";

    //
    // Message
    //
    if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty())
        strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "
";
    if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty())
        strHTML += "
" + tr("Comment") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "
";

    strHTML += "" + tr("Transaction ID") + ": " + TransactionRecord::formatSubTxId(wtx.GetHash(), rec->idx) + "
";

    if (wtx.IsCoinBase() || wtx.IsCoinStake())
        strHTML += "
" + tr("DRKSLK from standard transactions confirm after 10 blocks and are spendable after just 1 block. Mined coins (PoW or PoS generated) must mature 100 blocks and be held for a minimum of 1 day before they can be staked or spent. When you generate a PoW or PoS block it is broadcast to the network to be added to the DarkSilk blockchain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another wallet/node generates a block within a few seconds of your wallet/node.") + "
";

    //
    // Debug view
    //
    if (fDebug)
    {
        strHTML += "

" + tr("Debug information") + "

";
        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
            if(wallet->IsMine(txin))
                strHTML += "" + tr("Debit") + ": " + DarkSilkUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
";
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            if(wallet->IsMine(txout))
                strHTML += "" + tr("Credit") + ": " + DarkSilkUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
";

        strHTML += "
" + tr("Transaction") + ":
";
        strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);

        CTxDB txdb("r"); // To fetch source txouts

        strHTML += "
" + tr("Inputs") + ":";
        strHTML += "
";

        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
        {
            COutPoint prevout = txin.prevout;

            CTransaction prev;
            if(txdb.ReadDiskTx(prevout.hash, prev))
            {
                if (prevout.n < prev.vout.size())
                {
                    strHTML += "
";
                    const CTxOut &vout = prev.vout[prevout.n];
                    CTxDestination address;
                    if (ExtractDestination(vout.scriptPubKey, address))
                    {
                        if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty())
                            strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + " ";
                        strHTML += QString::fromStdString(CDarkSilkAddress(address).ToString());
                    }
                    strHTML = strHTML + " " + tr("Amount") + "=" + DarkSilkUnits::formatWithUnit(unit, vout.nValue);
                    strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "
";
                    strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "
";
                }
            }
        }

        strHTML += "
";
    }

    strHTML += "";
    return strHTML;
}
<commit_msg>WatchOnly changes<commit_after>#include <string>

#include "transactiondesc.h"
#include "darksilkunits.h"
#include "guiutil.h"
#include "base58.h"
#include "main.h"
#include "paymentserver.h"
#include "transactionrecord.h"
#include "timedata.h"
#include "ui_interface.h"
#include "consensus/consensus.h"
#include "wallet/wallet.h"
#include "txdb-leveldb.h"

QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
{
    AssertLockHeld(cs_main);
    if (!IsFinalTx(wtx, nBestHeight + 1))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
            return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight);
        else
            return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime));
    }
    else
    {
        int nDepth = wtx.GetDepthInMainChain();
        if (nDepth < 0)
            return tr("conflicted");
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
            return tr("%1/offline").arg(nDepth);
        else if (nDepth < 10)
            return tr("%1/unconfirmed").arg(nDepth);
        else
            return tr("%1 confirmations").arg(nDepth);
    }
}

QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit)
{
    QString strHTML;

    LOCK2(cs_main, wallet->cs_wallet);
    strHTML.reserve(4000);
    strHTML += "";

    int64_t nTime = wtx.GetTxTime();
    CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
    CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
    CAmount nNet = nCredit - nDebit;

    strHTML += "" + tr("Status") + ": " + FormatTxStatus(wtx);
    int nRequests = wtx.GetRequestCount();
    if (nRequests != -1)
    {
        if (nRequests == 0)
            strHTML += tr(", has not been successfully broadcast yet");
        else if (nRequests > 0)
            strHTML += tr(", broadcast through %n node(s)", "", nRequests);
    }
    strHTML += "
";

    strHTML += "" + tr("Date") + ": " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "
";

    //
    // From
    //
    if (wtx.IsCoinBase() || wtx.IsCoinStake())
    {
        strHTML += "" + tr("Source") + ": " + tr("Generated") + "
";
    }
    else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty())
    {
        // Online transaction
        strHTML += "" + tr("From") + ": " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "
";
    }
    else
    {
        // Offline transaction
        if (nNet > 0)
        {
            // Credit
            if (CDarkSilkAddress(rec->address).IsValid())
            {
                CTxDestination address = CDarkSilkAddress(rec->address).Get();
                if (wallet->mapAddressBook.count(address))
                {
                    strHTML += "" + tr("From") + ": " + tr("unknown") + "
";
                    strHTML += "" + tr("To") + ": ";
                    strHTML += GUIUtil::HtmlEscape(rec->address);
                    QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only");
                    if (!wallet->mapAddressBook[address].empty())
                        strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + ")";
                    else
                        strHTML += " (" + addressOwned + ")";
                    strHTML += "
";
                }
            }
        }
    }

    //
    // To
    //
    if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty())
    {
        // Online transaction
        std::string strAddress = wtx.mapValue["to"];
        strHTML += "" + tr("To") + ": ";
        CTxDestination dest = CDarkSilkAddress(strAddress).Get();
        if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].empty())
            strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest]) + " ";
        strHTML += GUIUtil::HtmlEscape(strAddress) + "
";
    }

    //
    // Amount
    //
    if (wtx.IsCoinBase() && nCredit == 0)
    {
        //
        // Coinbase
        //
        CAmount nUnmatured = 0;
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
        strHTML += "" + tr("Credit") + ": ";
        if (wtx.IsInMainChain())
            strHTML += DarkSilkUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
        else
            strHTML += "(" + tr("not accepted") + ")";
        strHTML += "
";
    }
    else if (nNet > 0)
    {
        //
        // Credit
        //
        strHTML += "" + tr("Credit") + ": " + DarkSilkUnits::formatWithUnit(unit, nNet) + "
";
    }
    else
    {
        bool fAllFromMe = true;
        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
            fAllFromMe = fAllFromMe && wallet->IsMine(txin);

        bool fAllToMe = true;
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            fAllToMe = fAllToMe && wallet->IsMine(txout);

        if (fAllFromMe)
        {
            //
            // Debit
            //
            BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            {
                if (wallet->IsMine(txout))
                    continue;

                if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty())
                {
                    // Offline transaction
                    CTxDestination address;
                    if (ExtractDestination(txout.scriptPubKey, address))
                    {
                        strHTML += "" + tr("To") + ": ";
                        if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty())
                            strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + " ";
                        strHTML += GUIUtil::HtmlEscape(CDarkSilkAddress(address).ToString());
                        strHTML += "
";
                    }
                }

                strHTML += "" + tr("Debit") + ": " + DarkSilkUnits::formatWithUnit(unit, -txout.nValue) + "
";
            }

            if (fAllToMe)
            {
                // Payment to self
                CAmount nChange = wtx.GetChange();
                CAmount nValue = nCredit - nChange;
                strHTML += "" + tr("Debit") + ": " + DarkSilkUnits::formatWithUnit(unit, -nValue) + "
";
                strHTML += "" + tr("Credit") + ": " + DarkSilkUnits::formatWithUnit(unit, nValue) + "
";
            }
            CTransactionPoS txPoS;
            CTransaction tx = CTransaction(wtx);
            CAmount nTxFee = nDebit - txPoS.GetValueOut(tx);
            if (nTxFee > 0)
                strHTML += "" + tr("Transaction fee") + ": " + DarkSilkUnits::formatWithUnit(unit, -nTxFee) + "
";
        }
        else
        {
            //
            // Mixed debit transaction
            //
            BOOST_FOREACH(const CTxIn& txin, wtx.vin)
                if (wallet->IsMine(txin))
                    strHTML += "" + tr("Debit") + ": " + DarkSilkUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
";
            BOOST_FOREACH(const CTxOut& txout, wtx.vout)
                if (wallet->IsMine(txout))
                    strHTML += "" + tr("Credit") + ": " + DarkSilkUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
";
        }
    }

    strHTML += "" + tr("Net amount") + ": " + DarkSilkUnits::formatWithUnit(unit, nNet, true) + "
";

    //
    // Message
    //
    if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty())
        strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "
";
    if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty())
        strHTML += "
" + tr("Comment") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "
";

    strHTML += "" + tr("Transaction ID") + ": " + TransactionRecord::formatSubTxId(wtx.GetHash(), rec->idx) + "
";

    if (wtx.IsCoinBase() || wtx.IsCoinStake())
        strHTML += "
" + tr("DRKSLK from standard transactions confirm after 10 blocks and are spendable after just 1 block. Mined coins (PoW or PoS generated) must mature 100 blocks and be held for a minimum of 1 day before they can be staked or spent. When you generate a PoW or PoS block it is broadcast to the network to be added to the DarkSilk blockchain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another wallet/node generates a block within a few seconds of your wallet/node.") + "
";

    //
    // Debug view
    //
    if (fDebug)
    {
        strHTML += "

" + tr("Debug information") + "

";
        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
            if(wallet->IsMine(txin))
                strHTML += "" + tr("Debit") + ": " + DarkSilkUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
";
        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
            if(wallet->IsMine(txout))
                strHTML += "" + tr("Credit") + ": " + DarkSilkUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
";

        strHTML += "
" + tr("Transaction") + ":
";
        strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);

        CTxDB txdb("r"); // To fetch source txouts

        strHTML += "
" + tr("Inputs") + ":";
        strHTML += "
";

        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
        {
            COutPoint prevout = txin.prevout;

            CTransaction prev;
            if(txdb.ReadDiskTx(prevout.hash, prev))
            {
                if (prevout.n < prev.vout.size())
                {
                    strHTML += "
";
                    const CTxOut &vout = prev.vout[prevout.n];
                    CTxDestination address;
                    if (ExtractDestination(vout.scriptPubKey, address))
                    {
                        if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty())
                            strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + " ";
                        strHTML += QString::fromStdString(CDarkSilkAddress(address).ToString());
                    }
                    strHTML = strHTML + " " + tr("Amount") + "=" + DarkSilkUnits::formatWithUnit(unit, vout.nValue);
                    strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "
";
                    strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "
";
                }
            }
        }

        strHTML += "
";
    }

    strHTML += "";
    return strHTML;
}
<|endoftext|>


<commit_before>/******************************************************************
*
* Round for C++
*
* Copyright (C) Satoshi Konno 2014
*
* This is licensed under BSD-style license, see file COPYING.
*
******************************************************************/

#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>

#include <round/Round.h>
#include <round/core/LocalNode.h>
#include <round/core/SystemMethod.h>

////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////

Round::LocalNode::LocalNode() {
  init();
}

Round::LocalNode::~LocalNode() {
}

void Round::LocalNode::init() {
  setState(NodeStatus::STOP);
}

////////////////////////////////////////////////
// Configuration
////////////////////////////////////////////////

bool Round::LocalNode::loadConfigFromString(const std::string &string, Error *error) {
  if (this->nodeConfig.loadFromString(string, error) == false)
    return false;
  return true;
}

bool Round::LocalNode::loadConfigFromFile(const std::string &filename, Error *error) {
  if (this->nodeConfig.loadFromFile(filename, error) == false)
    return false;
  return true;
}

bool Round::LocalNode::isConfigValid(Error *error) {
  return this->nodeConfig.isValid(error);
}

bool Round::LocalNode::getClusterName(std::string *name, Error *error) {
  return this->nodeConfig.getCluster(name, error);
}

////////////////////////////////////////////////
// Thread
////////////////////////////////////////////////

bool Round::LocalNode::start(Error *error) {
  stop(error);

  bool areAllOperationSucess = true;

  setState(NodeStatus::ACTIVATING);

  nodeWorker.setObject(this);
  if (nodeWorker.start()) {
    if (!this->nodeGraph.addNode(this)) {
      areAllOperationSucess = false;
    }
  }
  else {
    areAllOperationSucess = false;
  }

  if (!areAllOperationSucess) {
    stop(error);
    return false;
  }

  setState(NodeStatus::ACTIVE);

  return true;
}

bool Round::LocalNode::stop(Error *error) {
  bool areAllOperationSucess = true;

  if (!nodeWorker.stop()) {
    areAllOperationSucess = false;
    return false;
  }

  NodeGraph *nodeGraph = getNodeGraph();
  if (!nodeGraph->clear()) {
    areAllOperationSucess = false;
  }

  if (areAllOperationSucess == true) {
    setState(NodeStatus::STOP);
  }

  return areAllOperationSucess;
}

bool Round::LocalNode::restart(Error *error) {
  if (stop(error) == false)
    return false;
  return start(error);
}

////////////////////////////////////////////////
// Notification
////////////////////////////////////////////////

bool Round::LocalNode::nodeAdded(Round::Node *notifyNode)  {
  Error error;

  std::string notifyNodeCluster;
  if (!notifyNode->getClusterName(&notifyNodeCluster, &error))
    return false;

  std::string thisNodeCluster;
  if (!getClusterName(&thisNodeCluster, &error))
    return false;

  if (thisNodeCluster.compare(notifyNodeCluster) != 0)
    return false;

  if (this->nodeGraph.hasNode(notifyNode))
    return true;

  if (!this->nodeGraph.addNode(notifyNode))
    return true;

  if (equals(notifyNode))
    return true;

  return true;
}

bool Round::LocalNode::nodeRemoved(Round::Node *notifyNode)  {
  if (!this->nodeGraph.hasNode(notifyNode))
    return true;

  if (equals(notifyNode)) {
    this->nodeGraph.removeNode(notifyNode);
    return true;
  }

  ssize_t notifyNodeIndex = this->nodeGraph.getNodeIndex(notifyNode);
  if (notifyNodeIndex < 0)
    return false;

  Node *failedNode = this->nodeGraph.getNode(notifyNodeIndex);
  if (!failedNode)
    return false;

  return true;
}

////////////////////////////////////////////////
// Message
////////////////////////////////////////////////

bool Round::LocalNode::postMessage(NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  // Set id and ts parameter

  clock_t localTs = getLocalClock();
  nodeReq->setId(localTs);
  nodeReq->setTimestamp(localTs);

  // Post RPC message

  return execMessage(nodeReq, nodeRes, error);
}

bool Round::LocalNode::pushMessage(const NodeRequest *nodeReq) {
  return this->nodeMsgMgr.pushMessage(nodeReq);
}

bool Round::LocalNode::waitMessage(const NodeRequest **nodeReq) {
  *nodeReq = NULL;
  const Message *nodeMsg = NULL;
  if (!this->nodeMsgMgr.waitMessage(&nodeMsg))
    return false;
  *nodeReq = dynamic_cast<const NodeRequest *>(nodeMsg);
  return (*nodeReq) ? true : false;
}

////////////////////////////////////////////////
// Execution
////////////////////////////////////////////////

bool Round::LocalNode::hasUserMethod(const std::string &method) {
  return this->scriptMgr.hasScript(method);
}

bool Round::LocalNode::setError(int rpcErrorCode, Error *err) {
  if (!err)
    return false;
  RPC::JSON::ErrorCodeToError(rpcErrorCode, err);
  return true;
}

bool Round::LocalNode::execMessage(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  if (!nodeReq || !nodeRes || !error)
    return false;

  // Check hash code

  if (nodeReq->hasHash()) {
    std::string hashCode;
    if (nodeReq->getHash(&hashCode)) {
      if (hashCode.length() != HashObject::GetHashCodeLength()) {
        setError(RPC::JSON::ErrorCodeBadHashCode, error);
        return false;
      }
      NodeGraph *nodeGraph = getNodeGraph();
      if (!nodeGraph->isHandleNode(this, hashCode)) {
        setError(RPC::JSON::ErrorCodeMovedPermanently, error);
        return false;
      }
    }
  }

  // Check dest

  bool isDestOne = true;
  if (nodeReq->hasDest()) {
    isDestOne = nodeReq->isDestOne();
    if (!isDestOne && !nodeReq->isDestValid()) {
      setError(RPC::JSON::ErrorCodeInvalidParams, error);
      return false;
    }
  }

  // Update local clock

  clock_t remoteTs;
  if (nodeRes->getTimestamp(&remoteTs)) {
    setRemoteClock(remoteTs);
  }
  else {
    incrementLocalClock();
  }

  // Exec Method

  return execMethod(nodeReq, nodeRes, error);
}

////////////////////////////////////////////////
// Exce Method
////////////////////////////////////////////////

bool Round::LocalNode::execMethod(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  // Set id and ts parameter

  size_t msgId;
  if (nodeReq->getId(&msgId)) {
    nodeRes->setId(msgId);
  }
  nodeRes->setTimestamp(getLocalClock());

  // Exec Message

  std::string name;
  if (!nodeReq->getMethod(&name) || (name.length() <= 0)) {
    setError(RPC::JSON::ErrorCodeMethodNotFound, error);
    return false;
  }

  if (isSetMethod(name)) {
    if (!setMethod(nodeReq, nodeRes, error)) {
      setError(RPC::JSON::ErrorCodeInvalidParams, error);
      return false;
    }
    return true;
  }

  if (hasUserMethod(name)) {
    std::string params;
    nodeReq->getParams(&params);
    std::string result;
    bool isSuccess = this->scriptMgr.run(name, params, &result, error);
    if (isSuccess) {
      nodeRes->setResult(result);
    }
    else {
      nodeRes->setError(error);
    }
    return isSuccess;
  }

  if (isSystemMethod(name)) {
    return execSystemMethod(nodeReq, nodeRes, error);
  }

  return false;
}

////////////////////////////////////////////////
// System Static Method
////////////////////////////////////////////////

bool Round::LocalNode::isSetMethod(const std::string &method) {
  return (SystemMethodRequest::SET_METHOD.compare(method) == 0) ? true : false;
}

bool Round::LocalNode::setMethod(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *err) {
  std::string params;
  nodeReq->getParams(&params);

  JSONParser jsonParser;
  if (!jsonParser.parse(params, err)) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  JSONObject *jsonObj = jsonParser.getRootObject();
  if (!jsonObj->isDictionary()) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  JSONDictionary *jsonDict = dynamic_cast<JSONDictionary *>(jsonObj);
  if (!jsonDict) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  std::string scriptMethod;
  if (!jsonDict->get(SystemMethodRequest::NAME, &scriptMethod) || (scriptMethod.length() <= 0)) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  // Couldn't override '_set_method'
  if (isSetMethod(scriptMethod)) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  std::string scriptLang;
  if (!jsonDict->get(SystemMethodRequest::LANGUAGE, &scriptLang) || (scriptLang.length() <= 0)) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  // This method is removed if the code parameter is null.
  std::string scriptCode;
  jsonDict->get(SystemMethodRequest::CODE, &scriptCode);

  // Encode
  int encodeType = Script::ENCODING_NONE;
  std::string encodeTypeStr;
  if (jsonDict->get(SystemMethodRequest::ENCODE, &encodeTypeStr)) {
    if (encodeTypeStr.compare(SystemMethodRequest::ENCODE_BASE64)) {
      encodeType = Script::ENCODING_BASE64;
    }
  }

  return this->scriptMgr.setScript(scriptMethod, scriptLang, scriptCode, encodeType, err);
}

////////////////////////////////////////////////
// System Dynamic Method
////////////////////////////////////////////////

bool Round::LocalNode::isSystemMethod(const std::string &method) {
  return (method.find(SystemMethodRequest::PREFIX) == 0) ? true : false;
}

bool Round::LocalNode::execSystemMethod(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  static std::map<std::string, int> systemMethods;
  enum {
      SystemGetNodeInfo,
      SystemGetClusterInfo,
      SystemGetNetworkInfo,
  };

  if (systemMethods.size() <= 0) {
    systemMethods[SystemMethodRequest::GET_NODE_INFO] = SystemGetNodeInfo;
    systemMethods[SystemMethodRequest::GET_CLUSTER_INFO] = SystemGetClusterInfo;
    systemMethods[SystemMethodRequest::GET_NETWORK_INFO] = SystemGetNetworkInfo;
  }

  std::string reqMethod;
  nodeReq->getMethod(&reqMethod);

  if (systemMethods.find(reqMethod) == systemMethods.end()) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeMethodNotFound, error);
    return false;
  }

  int systemMethodType = systemMethods[reqMethod];
  switch (systemMethodType) {
  case SystemGetNodeInfo:
    return _get_node_info(nodeReq, nodeRes, error);
  case SystemGetClusterInfo:
    return _get_cluster_info(nodeReq, nodeRes, error);
  case SystemGetNetworkInfo:
    return _get_network_info(nodeReq, nodeRes, error);
  }

  RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeMethodNotFound, error);

  return false;
}

bool Round::LocalNode::_get_node_info(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  SystemGetNodeInfoResponse sysRes(nodeRes);
  return sysRes.setNode(this);
}

bool Round::LocalNode::_get_cluster_info(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  SystemGetClusterInfoResponse sysRes(nodeRes);
  return sysRes.setCluster(this);
}

bool Round::LocalNode::_get_network_info(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  SystemGetNetworkInfoResponse sysRes(nodeRes);
  return sysRes.setClusters(this);
}
<commit_msg>* Updated LocalNode::execMessage().<commit_after>/******************************************************************
*
* Round for C++
*
* Copyright (C) Satoshi Konno 2014
*
* This is licensed under BSD-style license, see file COPYING.
*
******************************************************************/

#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>

#include <round/Round.h>
#include <round/core/LocalNode.h>
#include <round/core/SystemMethod.h>

////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////

Round::LocalNode::LocalNode() {
  init();
}

Round::LocalNode::~LocalNode() {
}

void Round::LocalNode::init() {
  setState(NodeStatus::STOP);
}

////////////////////////////////////////////////
// Configuration
////////////////////////////////////////////////

bool Round::LocalNode::loadConfigFromString(const std::string &string, Error *error) {
  if (this->nodeConfig.loadFromString(string, error) == false)
    return false;
  return true;
}

bool Round::LocalNode::loadConfigFromFile(const std::string &filename, Error *error) {
  if (this->nodeConfig.loadFromFile(filename, error) == false)
    return false;
  return true;
}

bool Round::LocalNode::isConfigValid(Error *error) {
  return this->nodeConfig.isValid(error);
}

bool Round::LocalNode::getClusterName(std::string *name, Error *error) {
  return this->nodeConfig.getCluster(name, error);
}

////////////////////////////////////////////////
// Thread
////////////////////////////////////////////////

bool Round::LocalNode::start(Error *error) {
  stop(error);

  bool areAllOperationSucess = true;

  setState(NodeStatus::ACTIVATING);

  nodeWorker.setObject(this);
  if (nodeWorker.start()) {
    if (!this->nodeGraph.addNode(this)) {
      areAllOperationSucess = false;
    }
  }
  else {
    areAllOperationSucess = false;
  }

  if (!areAllOperationSucess) {
    stop(error);
    return false;
  }

  setState(NodeStatus::ACTIVE);

  return true;
}

bool Round::LocalNode::stop(Error *error) {
  bool areAllOperationSucess = true;

  if (!nodeWorker.stop()) {
    areAllOperationSucess = false;
    return false;
  }

  NodeGraph *nodeGraph = getNodeGraph();
  if (!nodeGraph->clear()) {
    areAllOperationSucess = false;
  }

  if (areAllOperationSucess == true) {
    setState(NodeStatus::STOP);
  }

  return areAllOperationSucess;
}

bool Round::LocalNode::restart(Error *error) {
  if (stop(error) == false)
    return false;
  return start(error);
}

////////////////////////////////////////////////
// Notification
////////////////////////////////////////////////

bool Round::LocalNode::nodeAdded(Round::Node *notifyNode)  {
  Error error;

  std::string notifyNodeCluster;
  if (!notifyNode->getClusterName(&notifyNodeCluster, &error))
    return false;

  std::string thisNodeCluster;
  if (!getClusterName(&thisNodeCluster, &error))
    return false;

  if (thisNodeCluster.compare(notifyNodeCluster) != 0)
    return false;

  if (this->nodeGraph.hasNode(notifyNode))
    return true;

  if (!this->nodeGraph.addNode(notifyNode))
    return true;

  if (equals(notifyNode))
    return true;

  return true;
}

bool Round::LocalNode::nodeRemoved(Round::Node *notifyNode)  {
  if (!this->nodeGraph.hasNode(notifyNode))
    return true;

  if (equals(notifyNode)) {
    this->nodeGraph.removeNode(notifyNode);
    return true;
  }

  ssize_t notifyNodeIndex = this->nodeGraph.getNodeIndex(notifyNode);
  if (notifyNodeIndex < 0)
    return false;

  Node *failedNode = this->nodeGraph.getNode(notifyNodeIndex);
  if (!failedNode)
    return false;

  return true;
}

////////////////////////////////////////////////
// Message
////////////////////////////////////////////////

bool Round::LocalNode::postMessage(NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  // Set id and ts parameter

  clock_t localTs = getLocalClock();
  nodeReq->setId(localTs);
  nodeReq->setTimestamp(localTs);

  // Post RPC message

  return execMessage(nodeReq, nodeRes, error);
}

bool Round::LocalNode::pushMessage(const NodeRequest *nodeReq) {
  return this->nodeMsgMgr.pushMessage(nodeReq);
}

bool Round::LocalNode::waitMessage(const NodeRequest **nodeReq) {
  *nodeReq = NULL;
  const Message *nodeMsg = NULL;
  if (!this->nodeMsgMgr.waitMessage(&nodeMsg))
    return false;
  *nodeReq = dynamic_cast<const NodeRequest *>(nodeMsg);
  return (*nodeReq) ? true : false;
}

////////////////////////////////////////////////
// Execution
////////////////////////////////////////////////

bool Round::LocalNode::hasUserMethod(const std::string &method) {
  return this->scriptMgr.hasScript(method);
}

bool Round::LocalNode::setError(int rpcErrorCode, Error *err) {
  if (!err)
    return false;
  RPC::JSON::ErrorCodeToError(rpcErrorCode, err);
  return true;
}

bool Round::LocalNode::execMessage(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  if (!nodeReq || !nodeRes || !error)
    return false;

  // Check hash code

  if (nodeReq->hasHash()) {
    std::string hashCode;
    if (nodeReq->getHash(&hashCode)) {
      if (hashCode.length() != HashObject::GetHashCodeLength()) {
        setError(RPC::JSON::ErrorCodeBadHashCode, error);
        return false;
      }
      NodeGraph *nodeGraph = getNodeGraph();
      if (!nodeGraph->isHandleNode(this, hashCode)) {
        setError(RPC::JSON::ErrorCodeMovedPermanently, error);
        return false;
      }
    }
  }

  // Check dest

  bool isDestOne = true;
  if (nodeReq->hasDest()) {
    isDestOne = nodeReq->isDestOne();
    if (!isDestOne && !nodeReq->isDestValid()) {
      setError(RPC::JSON::ErrorCodeInvalidParams, error);
      return false;
    }
  }

  // Update local clock

  clock_t remoteTs;
  if (nodeRes->getTimestamp(&remoteTs)) {
    setRemoteClock(remoteTs);
  }
  else {
    incrementLocalClock();
  }

  // Exec Method (One node)

  if (isDestOne) {
    return execMethod(nodeReq, nodeRes, error);
  }

  // Exec Method (Multi node)

  JSONArray *batchArray = new JSONArray();
  nodeRes->setResult(batchArray);

  Error thisNodeError;
  NodeResponse *thisNodeRes = new NodeResponse();
  execMethod(nodeReq, thisNodeRes, &thisNodeError);
  batchArray->add(thisNodeRes);

  NodeList otherNodes;
  for (NodeList::iterator node = otherNodes.begin(); node != otherNodes.end(); node++) {
    Error otherNodeError;
    NodeResponse *otherNodeRes = new NodeResponse();
    (*node)->postMessage(nodeReq, otherNodeRes, &otherNodeError);
    batchArray->add(thisNodeRes);
  }

  return true;
}

////////////////////////////////////////////////
// Exce Method
////////////////////////////////////////////////

bool Round::LocalNode::execMethod(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  // Set id and ts parameter

  size_t msgId;
  if (nodeReq->getId(&msgId)) {
    nodeRes->setId(msgId);
  }
  nodeRes->setTimestamp(getLocalClock());

  // Exec Message

  std::string name;
  if (!nodeReq->getMethod(&name) || (name.length() <= 0)) {
    setError(RPC::JSON::ErrorCodeMethodNotFound, error);
    return false;
  }

  if (isSetMethod(name)) {
    if (!setMethod(nodeReq, nodeRes, error)) {
      setError(RPC::JSON::ErrorCodeInvalidParams, error);
      return false;
    }
    return true;
  }

  if (hasUserMethod(name)) {
    std::string params;
    nodeReq->getParams(&params);
    std::string result;
    bool isSuccess = this->scriptMgr.run(name, params, &result, error);
    if (isSuccess) {
      nodeRes->setResult(result);
    }
    else {
      nodeRes->setError(error);
    }
    return isSuccess;
  }

  if (isSystemMethod(name)) {
    return execSystemMethod(nodeReq, nodeRes, error);
  }

  return false;
}

////////////////////////////////////////////////
// System Static Method
////////////////////////////////////////////////

bool Round::LocalNode::isSetMethod(const std::string &method) {
  return (SystemMethodRequest::SET_METHOD.compare(method) == 0) ? true : false;
}

bool Round::LocalNode::setMethod(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *err) {
  std::string params;
  nodeReq->getParams(&params);

  JSONParser jsonParser;
  if (!jsonParser.parse(params, err)) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  JSONObject *jsonObj = jsonParser.getRootObject();
  if (!jsonObj->isDictionary()) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  JSONDictionary *jsonDict = dynamic_cast<JSONDictionary *>(jsonObj);
  if (!jsonDict) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  std::string scriptMethod;
  if (!jsonDict->get(SystemMethodRequest::NAME, &scriptMethod) || (scriptMethod.length() <= 0)) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  // Couldn't override '_set_method'
  if (isSetMethod(scriptMethod)) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  std::string scriptLang;
  if (!jsonDict->get(SystemMethodRequest::LANGUAGE, &scriptLang) || (scriptLang.length() <= 0)) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeInvalidParams, err);
    return false;
  }

  // This method is removed if the code parameter is null.
  std::string scriptCode;
  jsonDict->get(SystemMethodRequest::CODE, &scriptCode);

  // Encode
  int encodeType = Script::ENCODING_NONE;
  std::string encodeTypeStr;
  if (jsonDict->get(SystemMethodRequest::ENCODE, &encodeTypeStr)) {
    if (encodeTypeStr.compare(SystemMethodRequest::ENCODE_BASE64)) {
      encodeType = Script::ENCODING_BASE64;
    }
  }

  return this->scriptMgr.setScript(scriptMethod, scriptLang, scriptCode, encodeType, err);
}

////////////////////////////////////////////////
// System Dynamic Method
////////////////////////////////////////////////

bool Round::LocalNode::isSystemMethod(const std::string &method) {
  return (method.find(SystemMethodRequest::PREFIX) == 0) ? true : false;
}

bool Round::LocalNode::execSystemMethod(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  static std::map<std::string, int> systemMethods;
  enum {
      SystemGetNodeInfo,
      SystemGetClusterInfo,
      SystemGetNetworkInfo,
  };

  if (systemMethods.size() <= 0) {
    systemMethods[SystemMethodRequest::GET_NODE_INFO] = SystemGetNodeInfo;
    systemMethods[SystemMethodRequest::GET_CLUSTER_INFO] = SystemGetClusterInfo;
    systemMethods[SystemMethodRequest::GET_NETWORK_INFO] = SystemGetNetworkInfo;
  }

  std::string reqMethod;
  nodeReq->getMethod(&reqMethod);

  if (systemMethods.find(reqMethod) == systemMethods.end()) {
    RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeMethodNotFound, error);
    return false;
  }

  int systemMethodType = systemMethods[reqMethod];
  switch (systemMethodType) {
  case SystemGetNodeInfo:
    return _get_node_info(nodeReq, nodeRes, error);
  case SystemGetClusterInfo:
    return _get_cluster_info(nodeReq, nodeRes, error);
  case SystemGetNetworkInfo:
    return _get_network_info(nodeReq, nodeRes, error);
  }

  RPC::JSON::ErrorCodeToError(RPC::JSON::ErrorCodeMethodNotFound, error);

  return false;
}

bool Round::LocalNode::_get_node_info(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  SystemGetNodeInfoResponse sysRes(nodeRes);
  return sysRes.setNode(this);
}

bool Round::LocalNode::_get_cluster_info(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  SystemGetClusterInfoResponse sysRes(nodeRes);
  return sysRes.setCluster(this);
}

bool Round::LocalNode::_get_network_info(const NodeRequest *nodeReq, NodeResponse *nodeRes, Error *error) {
  SystemGetNetworkInfoResponse sysRes(nodeRes);
  return sysRes.setClusters(this);
}
<|endoftext|>


<commit_before>// @(#)root/cintex:$Id$
// Author: Pere Mato 2005

// Copyright CERN, CH-1211 Geneva 23, 2004-2005, All rights reserved.
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.

#include "Reflex/Callback.h"
#include "Reflex/Type.h"
#include "Reflex/Member.h"
#include "Reflex/Builder/ReflexBuilder.h"

#include "Cintex/Cintex.h"
#include "CINTClassBuilder.h"
#include "CINTFunctionBuilder.h"
#include "CINTVariableBuilder.h"
#include "CINTTypedefBuilder.h"
#include "CINTEnumBuilder.h"
#include "ROOTClassEnhancer.h"
#include "CINTSourceFile.h"
#include <iostream>

#include "TROOT.h"

using namespace ROOT::Reflex;
using namespace ROOT::Cintex;
using namespace std;

//Build the Cintex dictionary on Reflex and convert it to CINT
namespace {
   struct Cintex_dict_t {
   public:
      Cintex_dict_t() {
         //--Reflex class builder
         //NamespaceBuilder( "ROOT::Cintex" );
         Type t_void = TypeBuilder(Literal("void"));
         Type t_int  = TypeBuilder(Literal("int"));
         Type t_bool = TypeBuilder(Literal("bool"));
         Type t_double = TypeBuilder(Literal("double"));
         Type t_double32 = TypedefTypeBuilder(Literal("Double32_t"), t_double);
         ClassBuilderT< Cintex >("Cintex", PUBLIC)
            .AddFunctionMember(FunctionTypeBuilder(t_void), "Enable", Enable, 0, 0, PUBLIC | STATIC)
            .AddFunctionMember(FunctionTypeBuilder(t_void, t_int), "SetDebug", SetDebug, 0, 0, PUBLIC | STATIC)
            .AddFunctionMember(FunctionTypeBuilder(t_int), "Debug", Debug, 0, 0, PUBLIC | STATIC)
            .AddFunctionMember(FunctionTypeBuilder(t_bool), "PropagateClassTypedefs", PropagateClassTypedefs, 0, 0, PUBLIC | STATIC)
            .AddFunctionMember(FunctionTypeBuilder(t_void, t_bool), "SetPropagateClassTypedefs", SetPropagateClassTypedefs, 0, 0, PUBLIC | STATIC);

         //--CINT class builder
         Type t = Type::ByName("Cintex");

         // Before loading in CINT, let's make sure that CINT (and ROOT) are initialized
         ROOT::GetROOT();
         ROOT::Cintex::CINTClassBuilder::Get(t).Setup();
      }
      static void Enable(void*, void*, const std::vector<void*>&, void*) {
         Cintex::Enable();
      }
      static void SetDebug(void*, void*, const std::vector<void*>& arg, void*) {
         Cintex::SetDebug(*(bool*)arg[0]);
      }
      static void Debug(void*, void* ret, const std::vector<void*>&, void*) {
         if (ret) *(int*)ret = Cintex::Debug();
         else Cintex::Debug();
      }

      static void PropagateClassTypedefs(void*, void* ret, const std::vector<void*>&, void*) {
         if (ret) *(bool*)ret = Cintex::PropagateClassTypedefs();
         else Cintex::PropagateClassTypedefs();
      }

      static void SetPropagateClassTypedefs(void*, void*, const std::vector<void*>& arg, void*) {
         Cintex::SetPropagateClassTypedefs(*(bool*)arg[0]);
      }

   };
   static Cintex_dict_t s_dict;

   static const char* btypes[] = { "bool", "char", "unsigned char", "short", "unsigned short", "int", "unsigned int",
     "long", "unsigned long", "float", "double", "string" };

   void Declare_additional_CINT_typedefs() {
      // as the function name says
      std::string name;
      std::string value;
      int autoload = G__set_class_autoloading(0); // To avoid recursive loads
      for ( size_t i = 0; i < sizeof(btypes)/sizeof(char*); i ++ ) {
         //--- vector ---
         name = std::string("vector<") + btypes[i];
         value = name;
         name += ">";
         value += std::string(",allocator<") + btypes[i] + "> >";
         CINTTypedefBuilder::Set(name.c_str(), value.c_str());
      }
      // Now that genreflex always translates basic_string<char> to string
      // we need a "typedef" (the wrong way!) for backward compatibility:
      CINTTypedefBuilder::Set("basic_string<char>", "string");
      G__set_class_autoloading(autoload);
   }
}

namespace ROOT {
   namespace Cintex {

      Cintex& Cintex::Instance() {
         static Cintex s_instance;
         return s_instance;
      }

      Cintex::Cintex() {
         fCallback = new Callback();
         fRootcreator = 0;
         fDbglevel = 0;
         fPropagateClassTypedefs = true;
         fPropagateClassEnums = true;
         fEnabled = false;
      }

      Cintex::~Cintex() {
         if( fCallback ) UninstallClassCallback( fCallback );
         delete fCallback;
      }

      void Cintex::Enable() {
         if ( Instance().fEnabled ) return;
         //---Declare some extra typdefs to please CINT
         Declare_additional_CINT_typedefs();
         //---Install the callback to fothcoming classes ----//
         InstallClassCallback( Instance().fCallback );
         //---Convert to CINT all existing classes ---//
         for( size_t i = 0; i < Type::TypeSize(); i++ ) {

            ( * Instance().fCallback)( Type::TypeAt(i) );
         }
         //---Convert to CINT all existing free functions and variables
         for ( size_t n = 0; n < Scope::ScopeSize(); n++ ) {
            Scope ns = Scope::ScopeAt(n);
            if ( ns.IsNamespace() ) {
               for( size_t m = 0; m < ns.MemberSize(); m++ ) {
                  ( * Instance().fCallback)( ns.MemberAt(m) );
               }
            }
         }
         Instance().fEnabled = true;
      }

      void Cintex::SetROOTCreator(ROOTCreator_t c) {
         Instance().fRootcreator = c;
      }

      ROOTCreator_t Cintex::GetROOTCreator() {
         return Instance().fRootcreator;
      }

      int Cintex::Debug() {
         return Instance().fDbglevel;
      }

      void Cintex::SetDebug(int l) {
         Instance().fDbglevel = l;
      }

      bool Cintex::PropagateClassTypedefs() {
         return Instance().fPropagateClassTypedefs;
      }

      void Cintex::SetPropagateClassTypedefs(bool val) {
         Instance().fPropagateClassTypedefs = val;
      }

      bool Cintex::PropagateClassEnums() {
         return Instance().fPropagateClassEnums;
      }

      void Cintex::SetPropagateClassEnums(bool val) {
         Instance().fPropagateClassEnums = val;
      }

      void Cintex::Default_CreateClass(const char* name, TGenericClassInfo* gci) {
         // Create a TClass object from the Reflex data; forward to ROOTClassEnhancer.
         ROOTClassEnhancer::Default_CreateClass(Reflex::Type::ByName(name), gci);
      }

      void Callback::operator () ( const Type& t ) {
         ArtificialSourceFile asf;
         int autoload = G__set_class_autoloading(0); // To avoid recursive loads
         if ( t.IsClass() || t.IsStruct() ) {
            ROOTClassEnhancer enhancer(t);
            enhancer.Setup();
            CINTClassBuilder::Get(t).Setup();
            enhancer.CreateInfo();
         }
         else if ( t.IsTypedef() ) {
            CINTTypedefBuilder::Setup(t);
         }
         else if ( t.IsEnum() ) {
            CINTEnumBuilder::Setup(t);
         }
         G__set_class_autoloading(autoload);
      }

      void Callback::operator () ( const Member& m ) {
         ArtificialSourceFile asf;
         int autoload = G__set_class_autoloading(0); // To avoid recursive loads
         if ( m.IsFunctionMember() ) {
            if( Cintex::Debug() ) cout << "Cintex: Building function " << m.Name(SCOPED|QUALIFIED) << endl;
            CINTFunctionBuilder(m).Setup();
         }
         else if ( m.IsDataMember() ) {
            if( Cintex::Debug() ) cout << "Cintex: Building variable " << m.Name(SCOPED|QUALIFIED) << endl;
            CINTVariableBuilder(m).Setup();
         }
         G__set_class_autoloading(autoload);
      }
   }
}
<commit_msg>Need to use CINT mutex in Cintex::Callback::operator()<commit_after>// @(#)root/cintex:$Id$
// Author: Pere Mato 2005

// Copyright CERN, CH-1211 Geneva 23, 2004-2005, All rights reserved.
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.

#include "Reflex/Callback.h"
#include "Reflex/Type.h"
#include "Reflex/Member.h"
#include "Reflex/Builder/ReflexBuilder.h"

#include "Cintex/Cintex.h"
#include "CINTClassBuilder.h"
#include "CINTFunctionBuilder.h"
#include "CINTVariableBuilder.h"
#include "CINTTypedefBuilder.h"
#include "CINTEnumBuilder.h"
#include "ROOTClassEnhancer.h"
#include "CINTSourceFile.h"
#include <iostream>

#include "TROOT.h"
#include "TInterpreter.h" //gCINTMutex
#include "TVirtualMutex.h"

using namespace ROOT::Reflex;
using namespace ROOT::Cintex;
using namespace std;

//Build the Cintex dictionary on Reflex and convert it to CINT
namespace {
   struct Cintex_dict_t {
   public:
      Cintex_dict_t() {
         //--Reflex class builder
         //NamespaceBuilder( "ROOT::Cintex" );
         Type t_void = TypeBuilder(Literal("void"));
         Type t_int  = TypeBuilder(Literal("int"));
         Type t_bool = TypeBuilder(Literal("bool"));
         Type t_double = TypeBuilder(Literal("double"));
         Type t_double32 = TypedefTypeBuilder(Literal("Double32_t"), t_double);
         ClassBuilderT< Cintex >("Cintex", PUBLIC)
            .AddFunctionMember(FunctionTypeBuilder(t_void), "Enable", Enable, 0, 0, PUBLIC | STATIC)
            .AddFunctionMember(FunctionTypeBuilder(t_void, t_int), "SetDebug", SetDebug, 0, 0, PUBLIC | STATIC)
            .AddFunctionMember(FunctionTypeBuilder(t_int), "Debug", Debug, 0, 0, PUBLIC | STATIC)
            .AddFunctionMember(FunctionTypeBuilder(t_bool), "PropagateClassTypedefs", PropagateClassTypedefs, 0, 0, PUBLIC | STATIC)
            .AddFunctionMember(FunctionTypeBuilder(t_void, t_bool), "SetPropagateClassTypedefs", SetPropagateClassTypedefs, 0, 0, PUBLIC | STATIC);

         //--CINT class builder
         Type t = Type::ByName("Cintex");

         // Before loading in CINT, let's make sure that CINT (and ROOT) are initialized
         ROOT::GetROOT();
         ROOT::Cintex::CINTClassBuilder::Get(t).Setup();
      }
      static void Enable(void*, void*, const std::vector<void*>&, void*) {
         Cintex::Enable();
      }
      static void SetDebug(void*, void*, const std::vector<void*>& arg, void*) {
         Cintex::SetDebug(*(bool*)arg[0]);
      }
      static void Debug(void*, void* ret, const std::vector<void*>&, void*) {
         if (ret) *(int*)ret = Cintex::Debug();
         else Cintex::Debug();
      }

      static void PropagateClassTypedefs(void*, void* ret, const std::vector<void*>&, void*) {
         if (ret) *(bool*)ret = Cintex::PropagateClassTypedefs();
         else Cintex::PropagateClassTypedefs();
      }

      static void SetPropagateClassTypedefs(void*, void*, const std::vector<void*>& arg, void*) {
         Cintex::SetPropagateClassTypedefs(*(bool*)arg[0]);
      }

   };
   static Cintex_dict_t s_dict;

   static const char* btypes[] = { "bool", "char", "unsigned char", "short", "unsigned short", "int", "unsigned int",
     "long", "unsigned long", "float", "double", "string" };

   void Declare_additional_CINT_typedefs() {
      // as the function name says
      std::string name;
      std::string value;
      int autoload = G__set_class_autoloading(0); // To avoid recursive loads
      for ( size_t i = 0; i < sizeof(btypes)/sizeof(char*); i ++ ) {
         //--- vector ---
         name = std::string("vector<") + btypes[i];
         value = name;
         name += ">";
         value += std::string(",allocator<") + btypes[i] + "> >";
         CINTTypedefBuilder::Set(name.c_str(), value.c_str());
      }
      // Now that genreflex always translates basic_string<char> to string
      // we need a "typedef" (the wrong way!) for backward compatibility:
      CINTTypedefBuilder::Set("basic_string<char>", "string");
      G__set_class_autoloading(autoload);
   }
}

namespace ROOT {
   namespace Cintex {

      Cintex& Cintex::Instance() {
         static Cintex s_instance;
         return s_instance;
      }

      Cintex::Cintex() {
         fCallback = new Callback();
         fRootcreator = 0;
         fDbglevel = 0;
         fPropagateClassTypedefs = true;
         fPropagateClassEnums = true;
         fEnabled = false;
      }

      Cintex::~Cintex() {
         if( fCallback ) UninstallClassCallback( fCallback );
         delete fCallback;
      }

      void Cintex::Enable() {
         if ( Instance().fEnabled ) return;
         //---Declare some extra typdefs to please CINT
         Declare_additional_CINT_typedefs();
         //---Install the callback to fothcoming classes ----//
         InstallClassCallback( Instance().fCallback );
         //---Convert to CINT all existing classes ---//
         for( size_t i = 0; i < Type::TypeSize(); i++ ) {

            ( * Instance().fCallback)( Type::TypeAt(i) );
         }
         //---Convert to CINT all existing free functions and variables
         for ( size_t n = 0; n < Scope::ScopeSize(); n++ ) {
            Scope ns = Scope::ScopeAt(n);
            if ( ns.IsNamespace() ) {
               for( size_t m = 0; m < ns.MemberSize(); m++ ) {
                  ( * Instance().fCallback)( ns.MemberAt(m) );
               }
            }
         }
         Instance().fEnabled = true;
      }

      void Cintex::SetROOTCreator(ROOTCreator_t c) {
         Instance().fRootcreator = c;
      }

      ROOTCreator_t Cintex::GetROOTCreator() {
         return Instance().fRootcreator;
      }

      int Cintex::Debug() {
         return Instance().fDbglevel;
      }

      void Cintex::SetDebug(int l) {
         Instance().fDbglevel = l;
      }

      bool Cintex::PropagateClassTypedefs() {
         return Instance().fPropagateClassTypedefs;
      }

      void Cintex::SetPropagateClassTypedefs(bool val) {
         Instance().fPropagateClassTypedefs = val;
      }

      bool Cintex::PropagateClassEnums() {
         return Instance().fPropagateClassEnums;
      }

      void Cintex::SetPropagateClassEnums(bool val) {
         Instance().fPropagateClassEnums = val;
      }

      void Cintex::Default_CreateClass(const char* name, TGenericClassInfo* gci) {
         // Create a TClass object from the Reflex data; forward to ROOTClassEnhancer.
         ROOTClassEnhancer::Default_CreateClass(Reflex::Type::ByName(name), gci);
      }

      void Callback::operator () ( const Type& t ) {
         R__LOCKGUARD2(gCINTMutex);
         ArtificialSourceFile asf;
         int autoload = G__set_class_autoloading(0); // To avoid recursive loads
         if ( t.IsClass() || t.IsStruct() ) {
            ROOTClassEnhancer enhancer(t);
            enhancer.Setup();
            CINTClassBuilder::Get(t).Setup();
            enhancer.CreateInfo();
         }
         else if ( t.IsTypedef() ) {
            CINTTypedefBuilder::Setup(t);
         }
         else if ( t.IsEnum() ) {
            CINTEnumBuilder::Setup(t);
         }
         G__set_class_autoloading(autoload);
      }

      void Callback::operator () ( const Member& m ) {
         R__LOCKGUARD2(gCINTMutex);
         ArtificialSourceFile asf;
         int autoload = G__set_class_autoloading(0); // To avoid recursive loads
         if ( m.IsFunctionMember() ) {
            if( Cintex::Debug() ) cout << "Cintex: Building function " << m.Name(SCOPED|QUALIFIED) << endl;
            CINTFunctionBuilder(m).Setup();
         }
         else if ( m.IsDataMember() ) {
            if( Cintex::Debug() ) cout << "Cintex: Building variable " << m.Name(SCOPED|QUALIFIED) << endl;
            CINTVariableBuilder(m).Setup();
         }
         G__set_class_autoloading(autoload);
      }
   }
}
<|endoftext|>