<commit_before>// Copyright 2016 Strength in Numbers Foundation

#include "giftcardpage.h"
#include "ui_giftcardpage.h"

#include "giftcardtablemodel.h"
#include "optionsmodel.h"
#include "bitcoingui.h"
#include "creategiftdialog.h"
#include "csvmodelwriter.h"
#include "guiutil.h"
#include "paperwallet.h"

#include <QSortFilterProxyModel>
#include <QClipboard>
#include <QMessageBox>
#include <QMenu>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QDesktopServices>     // dvd add for launching URL
#include <QUrl>                 // dvd add for launching URL

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

extern int VanityGen(int addrtype, char *prefix, char *pubKey, char *privKey);

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

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

#ifndef USE_QRCODE
    ui->showQRCode->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->buttonBox->setVisible(false);
        break;
    }

    ui->labelExplanation->setVisible(true);
    ui->deleteButton->setVisible(true);
    ui->signMessage->setVisible(false);

//dvd considering adding a "Give" option from the right click

    // Context menu actions
    QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
    QAction *copyAddressAction = new QAction(ui->copyToClipboard->text(), this);
    QAction *editAction = new QAction(tr("&Edit"), this);
    QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this);
//    QAction *signMessageAction = new QAction(ui->signMessage->text(), this);
    QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this);
    deleteAction = new QAction(ui->deleteButton->text(), this);

    // Build context menu
    contextMenu = new QMenu();
    contextMenu->addAction(copyAddressAction);
    contextMenu->addAction(copyLabelAction);
    contextMenu->addAction(editAction);
    contextMenu->addAction(deleteAction);
    contextMenu->addSeparator();
    contextMenu->addAction(showQRCodeAction);
    contextMenu->addAction(verifyMessageAction);

    // Connect signals for context menu actions
    connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyToClipboard_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_showQRCode_clicked()));
//    connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked()));
    connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked()));

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

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

    // Set default filepath to save to
    filePath = QDir::homePath();
    printf("homePath = %s\n", filePath.toStdString().c_str());
}

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

void GiftCardPage::setModel(GiftCardTableModel *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);

    proxyModel->setFilterRole(GiftCardTableModel::TypeRole);
    proxyModel->setFilterFixedString(GiftCardTableModel::Gift);

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

    // Set column widths
#if QT_VERSION < 0x050000
    ui->tableView->horizontalHeader()->resizeSection(
            GiftCardTableModel::Address, 333);
    ui->tableView->horizontalHeader()->setResizeMode(
            GiftCardTableModel::Label, QHeaderView::Stretch);
#else
    ui->tableView->horizontalHeader()->setSectionResizeMode(GiftCardTableModel::Label, QHeaderView::Stretch);
    ui->tableView->horizontalHeader()->setSectionResizeMode(GiftCardTableModel::Address, QHeaderView::ResizeToContents);
#endif

    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 GiftCardPage::setOptionsModel(OptionsModel *optionsModel)
{
    this->optionsModel = optionsModel;
}

void GiftCardPage::on_copyToClipboard_clicked()
{
    GUIUtil::copyEntryData(ui->tableView, GiftCardTableModel::Address);
}

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

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

    CreateGiftDialog dlg(CreateGiftDialog::EditGiftAddress);

    dlg.setModel(model);
    QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
    dlg.loadRow(origIndex.row());
    dlg.exec();

}

void GiftCardPage::on_signMessage_clicked()
{
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(GiftCardTableModel::Address);
    QString addr;

    foreach (QModelIndex index, indexes)
    {
        QVariant address = index.data();
        addr = address.toString();
    }

    emit signMessage(addr);
}

void GiftCardPage::on_verifyMessage_clicked()
{
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(GiftCardTableModel::Address);
    QString addr;

    foreach (QModelIndex index, indexes)
    {
        QVariant address = index.data();
        addr = address.toString();
    }

    emit verifyMessage(addr);
}

void GiftCardPage::on_newAddressButton_clicked()
{
    char    strPubKey[256],
            strPrivKey[256];

    if(!model)
        return;

    printf("GiftCardPage::on_newAddressButton(): CreateGiftDialog\n");
    CreateGiftDialog dlg(CreateGiftDialog::NewGiftAddress);
    dlg.setModel(model);
    if(dlg.exec())
    {
        printf("GiftCardPage::on_newAddressButton(): dlg.exec()\n");
        newAddressToSelect = dlg.getAddress();
        QStringList giftKeys = newAddressToSelect.split(":");

        strcpy(strPubKey, giftKeys.at(0).toStdString().c_str());
        strcpy(strPrivKey, giftKeys.at(1).toStdString().c_str());
        printf("strPubKey = %s\tstrPrivKey = %s\n", strPubKey, strPrivKey);

//        QString appDirPath = QCoreApplication::applicationDirPath();
//        printf("appDirPath = %s\n", appDirPath.toStdString().c_str());

        QString defaultFileName = filePath + QDir::separator() + giftKeys.at(0) + ".html";

        QString fileName = GUIUtil::getSaveFileName(
                    this, tr("Save Gift* Card"), defaultFileName, tr(".html"));

        if (!fileName.isNull()) {
            PaperWallet pWallet = PaperWallet::PaperWallet(fileName, giftKeys.at(0), giftKeys.at(1), "");
            if (pWallet.genWallet()) {
                QFile file(fileName);
                if (file.exists()) {
                    // save the user selected folder for easy next use
                    QFileInfo fileInfo(file.fileName());
                    filePath = fileInfo.absolutePath();

                    // launch browser to display/print
                    QString url = "file://" + fileName;
                    QDesktopServices::openUrl(QUrl(url));
                }
            }
        }
    }
}

void GiftCardPage::on_templateButton_clicked()
{
    QMessageBox msgBox;

    msgBox.setWindowTitle("Update Templates");
    msgBox.setStandardButtons(QMessageBox::Ok);
    msgBox.setDefaultButton(QMessageBox::Ok);

    PaperWallet pWallet = PaperWallet::PaperWallet("", "", "", "");

    if (pWallet.updateTemplates())
        msgBox.setText("Template Update Successful");
    else {
        msgBox.setText("Template Update Failed");
        msgBox.setInformativeText("Check your network connectivity and retry");
    }
    msgBox.exec();
}

void GiftCardPage::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 GiftCardPage::selectionChanged()
{
    // Set button states based on selected tab and selection
    QTableView *table = ui->tableView;
    if(!table->selectionModel())
        return;

    if(table->selectionModel()->hasSelection())
    {
        // In gift tab, allow deletion of selection
        ui->deleteButton->setEnabled(true);
        ui->deleteButton->setVisible(true);
        deleteAction->setEnabled(true);
        ui->signMessage->setEnabled(false);
        ui->signMessage->setVisible(false);
        ui->verifyMessage->setEnabled(true);
        ui->verifyMessage->setVisible(true);
        ui->copyToClipboard->setEnabled(true);
        ui->showQRCode->setEnabled(true);
    }
    else
    {
        ui->deleteButton->setEnabled(false);
        ui->showQRCode->setEnabled(false);
        ui->copyToClipboard->setEnabled(false);
        ui->signMessage->setEnabled(false);
        ui->verifyMessage->setEnabled(false);
    }
}

void GiftCardPage::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(GiftCardTableModel::Address);

    foreach (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 GiftCardPage::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", GiftCardTableModel::Label, Qt::EditRole);
    writer.addColumn("Address", GiftCardTableModel::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 GiftCardPage::on_showQRCode_clicked()
{
#ifdef USE_QRCODE
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(GiftCardTableModel::Address);

    foreach (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 GiftCardPage::contextualMenu(const QPoint &point)
{
    QModelIndex index = ui->tableView->indexAt(point);
    if(index.isValid())
    {
        contextMenu->exec(QCursor::pos());
    }
}

void GiftCardPage::selectNewAddress(const QModelIndex &parent, int begin, int end)
{
    QModelIndex idx = proxyModel->mapFromSource(model->index(begin, GiftCardTableModel::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>XBUILD is bitching about a "redundant" ::PaperWallet [-permissive] thing !?<commit_after>// Copyright 2016 Strength in Numbers Foundation

#include "giftcardpage.h"
#include "ui_giftcardpage.h"

#include "giftcardtablemodel.h"
#include "optionsmodel.h"
#include "bitcoingui.h"
#include "creategiftdialog.h"
#include "csvmodelwriter.h"
#include "guiutil.h"
#include "paperwallet.h"

#include <QSortFilterProxyModel>
#include <QClipboard>
#include <QMessageBox>
#include <QMenu>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QDesktopServices>     // dvd add for launching URL
#include <QUrl>                 // dvd add for launching URL

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

extern int VanityGen(int addrtype, char *prefix, char *pubKey, char *privKey);

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

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

#ifndef USE_QRCODE
    ui->showQRCode->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->buttonBox->setVisible(false);
        break;
    }

    ui->labelExplanation->setVisible(true);
    ui->deleteButton->setVisible(true);
    ui->signMessage->setVisible(false);

//dvd considering adding a "Give" option from the right click

    // Context menu actions
    QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
    QAction *copyAddressAction = new QAction(ui->copyToClipboard->text(), this);
    QAction *editAction = new QAction(tr("&Edit"), this);
    QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this);
//    QAction *signMessageAction = new QAction(ui->signMessage->text(), this);
    QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this);
    deleteAction = new QAction(ui->deleteButton->text(), this);

    // Build context menu
    contextMenu = new QMenu();
    contextMenu->addAction(copyAddressAction);
    contextMenu->addAction(copyLabelAction);
    contextMenu->addAction(editAction);
    contextMenu->addAction(deleteAction);
    contextMenu->addSeparator();
    contextMenu->addAction(showQRCodeAction);
    contextMenu->addAction(verifyMessageAction);

    // Connect signals for context menu actions
    connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyToClipboard_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_showQRCode_clicked()));
//    connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked()));
    connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked()));

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

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

    // Set default filepath to save to
    filePath = QDir::homePath();
    printf("homePath = %s\n", filePath.toStdString().c_str());
}

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

void GiftCardPage::setModel(GiftCardTableModel *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);

    proxyModel->setFilterRole(GiftCardTableModel::TypeRole);
    proxyModel->setFilterFixedString(GiftCardTableModel::Gift);

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

    // Set column widths
#if QT_VERSION < 0x050000
    ui->tableView->horizontalHeader()->resizeSection(
            GiftCardTableModel::Address, 333);
    ui->tableView->horizontalHeader()->setResizeMode(
            GiftCardTableModel::Label, QHeaderView::Stretch);
#else
    ui->tableView->horizontalHeader()->setSectionResizeMode(GiftCardTableModel::Label, QHeaderView::Stretch);
    ui->tableView->horizontalHeader()->setSectionResizeMode(GiftCardTableModel::Address, QHeaderView::ResizeToContents);
#endif

    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 GiftCardPage::setOptionsModel(OptionsModel *optionsModel)
{
    this->optionsModel = optionsModel;
}

void GiftCardPage::on_copyToClipboard_clicked()
{
    GUIUtil::copyEntryData(ui->tableView, GiftCardTableModel::Address);
}

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

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

    CreateGiftDialog dlg(CreateGiftDialog::EditGiftAddress);

    dlg.setModel(model);
    QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
    dlg.loadRow(origIndex.row());
    dlg.exec();

}

void GiftCardPage::on_signMessage_clicked()
{
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(GiftCardTableModel::Address);
    QString addr;

    foreach (QModelIndex index, indexes)
    {
        QVariant address = index.data();
        addr = address.toString();
    }

    emit signMessage(addr);
}

void GiftCardPage::on_verifyMessage_clicked()
{
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(GiftCardTableModel::Address);
    QString addr;

    foreach (QModelIndex index, indexes)
    {
        QVariant address = index.data();
        addr = address.toString();
    }

    emit verifyMessage(addr);
}

void GiftCardPage::on_newAddressButton_clicked()
{
    char    strPubKey[256],
            strPrivKey[256];

    if(!model)
        return;

    printf("GiftCardPage::on_newAddressButton(): CreateGiftDialog\n");
    CreateGiftDialog dlg(CreateGiftDialog::NewGiftAddress);
    dlg.setModel(model);
    if(dlg.exec())
    {
        printf("GiftCardPage::on_newAddressButton(): dlg.exec()\n");
        newAddressToSelect = dlg.getAddress();
        QStringList giftKeys = newAddressToSelect.split(":");

        strcpy(strPubKey, giftKeys.at(0).toStdString().c_str());
        strcpy(strPrivKey, giftKeys.at(1).toStdString().c_str());
        printf("strPubKey = %s\tstrPrivKey = %s\n", strPubKey, strPrivKey);

//        QString appDirPath = QCoreApplication::applicationDirPath();
//        printf("appDirPath = %s\n", appDirPath.toStdString().c_str());

        QString defaultFileName = filePath + QDir::separator() + giftKeys.at(0) + ".html";

        QString fileName = GUIUtil::getSaveFileName(
                    this, tr("Save Gift* Card"), defaultFileName, tr(".html"));

        if (!fileName.isNull()) {
            PaperWallet pWallet = PaperWallet::PaperWallet(fileName, giftKeys.at(0), giftKeys.at(1), "");
            if (pWallet.genWallet()) {
                QFile file(fileName);
                if (file.exists()) {
                    // save the user selected folder for easy next use
                    QFileInfo fileInfo(file.fileName());
                    filePath = fileInfo.absolutePath();

                    // launch browser to display/print
                    QString url = "file://" + fileName;
                    QDesktopServices::openUrl(QUrl(url));
                }
            }
        }
    }
}

void GiftCardPage::on_templateButton_clicked()
{
    QMessageBox msgBox;

    msgBox.setWindowTitle("Update Templates");
    msgBox.setStandardButtons(QMessageBox::Ok);
    msgBox.setDefaultButton(QMessageBox::Ok);

    PaperWallet pWallet = PaperWallet("", "", "", "");

    if (pWallet.updateTemplates())
        msgBox.setText("Template Update Successful");
    else {
        msgBox.setText("Template Update Failed");
        msgBox.setInformativeText("Check your network connectivity and retry");
    }
    msgBox.exec();
}

void GiftCardPage::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 GiftCardPage::selectionChanged()
{
    // Set button states based on selected tab and selection
    QTableView *table = ui->tableView;
    if(!table->selectionModel())
        return;

    if(table->selectionModel()->hasSelection())
    {
        // In gift tab, allow deletion of selection
        ui->deleteButton->setEnabled(true);
        ui->deleteButton->setVisible(true);
        deleteAction->setEnabled(true);
        ui->signMessage->setEnabled(false);
        ui->signMessage->setVisible(false);
        ui->verifyMessage->setEnabled(true);
        ui->verifyMessage->setVisible(true);
        ui->copyToClipboard->setEnabled(true);
        ui->showQRCode->setEnabled(true);
    }
    else
    {
        ui->deleteButton->setEnabled(false);
        ui->showQRCode->setEnabled(false);
        ui->copyToClipboard->setEnabled(false);
        ui->signMessage->setEnabled(false);
        ui->verifyMessage->setEnabled(false);
    }
}

void GiftCardPage::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(GiftCardTableModel::Address);

    foreach (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 GiftCardPage::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", GiftCardTableModel::Label, Qt::EditRole);
    writer.addColumn("Address", GiftCardTableModel::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 GiftCardPage::on_showQRCode_clicked()
{
#ifdef USE_QRCODE
    QTableView *table = ui->tableView;
    QModelIndexList indexes = table->selectionModel()->selectedRows(GiftCardTableModel::Address);

    foreach (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 GiftCardPage::contextualMenu(const QPoint &point)
{
    QModelIndex index = ui->tableView->indexAt(point);
    if(index.isValid())
    {
        contextMenu->exec(QCursor::pos());
    }
}

void GiftCardPage::selectNewAddress(const QModelIndex &parent, int begin, int end)
{
    QModelIndex idx = proxyModel->mapFromSource(model->index(begin, GiftCardTableModel::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 "overviewpage.h"
#include "ui_overviewpage.h"

#include "walletmodel.h"
#include "bitcoinunits.h"
#include "optionsmodel.h"
#include "transactiontablemodel.h"
#include "transactionfilterproxy.h"
#include "guiutil.h"
#include "guiconstants.h"

#include <QAbstractItemDelegate>
#include <QPainter>

#define DECORATION_SIZE 64
#define NUM_ITEMS 3

class TxViewDelegate : public QAbstractItemDelegate
{
    Q_OBJECT
public:
    TxViewDelegate(): QAbstractItemDelegate(), unit(BitcoinUnits::BTC)
    {

    }

    inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
                      const QModelIndex &index ) const
    {
        painter->save();

        QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
        QRect mainRect = option.rect;
        QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE));
        int xspace = DECORATION_SIZE + 8;
        int ypad = 6;
        int halfheight = (mainRect.height() - 2*ypad)/2;
        QRect amountRect(mainRect.left() + xspace, mainRect.top()+ypad, mainRect.width() - xspace, halfheight);
        QRect addressRect(mainRect.left() + xspace, mainRect.top()+ypad+halfheight, mainRect.width() - xspace, halfheight);
        icon.paint(painter, decorationRect);

        QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime();
        QString address = index.data(Qt::DisplayRole).toString();
        qint64 amount = index.data(TransactionTableModel::AmountRole).toLongLong();
        bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool();
        QVariant value = index.data(Qt::ForegroundRole);
        QColor foreground = option.palette.color(QPalette::Text);
        if(qVariantCanConvert<QColor>(value))
        {
            foreground = qvariant_cast<QColor>(value);
        }

        painter->setPen(foreground);
        painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address);

        if(amount < 0)
        {
            foreground = COLOR_NEGATIVE;
        }
        else if(!confirmed)
        {
            foreground = COLOR_UNCONFIRMED;
        }
        else
        {
            foreground = option.palette.color(QPalette::Text);
        }
        painter->setPen(foreground);
        QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true);
        if(!confirmed)
        {
            amountText = QString("[") + amountText + QString("]");
        }
        painter->drawText(amountRect, Qt::AlignRight|Qt::AlignVCenter, amountText);

        painter->setPen(option.palette.color(QPalette::Text));
        painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::dateTimeStr(date));

        painter->restore();
    }

    inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        return QSize(DECORATION_SIZE, DECORATION_SIZE);
    }

    int unit;

};
#include "overviewpage.moc"

OverviewPage::OverviewPage(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::OverviewPage),
    currentBalance(-1),
    currentStake(0),
    currentUnconfirmedBalance(-1),
    currentImmatureBalance(-1),
    txdelegate(new TxViewDelegate()),
    filter(0)
{
    isUpdateRunning = false;
    updateConcluderTimer = new QTimer(this);
    updateConcluderTimeout = 3000;
    updateCheckTimer = new QTimer(this);
    updateCheckTimerTimeout = 15*60*1000; //check for updates every 15 minutes

    ui->setupUi(this);

    setupUpdateControls();

    // Recent transactions
    ui->listTransactions->setItemDelegate(txdelegate);
    ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
    ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
    ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false);

    connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex)));

    // init "out of sync" warning labels
    ui->labelWalletStatus->setText("(" + tr("out of sync") + ")");
    ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")");

    // start with displaying the "out of sync" warnings
    showOutOfSyncWarning(true);

    updateCheckTimer->start(updateCheckTimerTimeout);
    checkForNeblioUpdates();
}

void OverviewPage::handleTransactionClicked(const QModelIndex &index)
{
    if(filter)
        emit transactionClicked(filter->mapToSource(index));
}

void OverviewPage::updateCheckAnimation_frameChanged(int frameNumber) {
    if(frameNumber == (bottom_bar_updater_check_movie->frameCount()-1)) {
        bottom_bar_updater_check_movie->stop();
    }
}

void OverviewPage::checkForNeblioUpdates()
{
    if(!isUpdateRunning) {
        printf("Checking for updates...\n");
        ui->bottom_bar_updater_label->setMovie(bottom_bar_updater_spinner_movie);
        bottom_bar_updater_spinner_movie->start();
        latestVersion.clear();
        updateAvailablePromise = boost::promise<bool>();
        updateAvailableFuture = updateAvailablePromise.get_future();
        boost::thread updaterThread(boost::bind(&NeblioUpdater::checkIfUpdateIsAvailable,
                                    &neblioUpdater,
                                    boost::ref(updateAvailablePromise),
                                    boost::ref(latestVersion)
                                    ));
        updaterThread.detach();
        updateConcluderTimer->start(updateConcluderTimeout);
        isUpdateRunning = true;
    }
}

void OverviewPage::finishCheckForNeblioUpdates()
{
    if(isUpdateRunning) {
        printf("Concluding update check...\n");
        try {
            bool updateAvailable = updateAvailableFuture.get();
            if(updateAvailable) {
                ui->bottom_bar_updater_label->setMovie(bottom_bar_updater_no_update_movie);
                bottom_bar_updater_no_update_movie->start();
                ui->bottom_bar_updater_label->setToolTip("An update exists. Please visit nebl.io and download it.");
            } else {
                ui->bottom_bar_updater_label->setMovie(bottom_bar_updater_check_movie);
                bottom_bar_updater_check_movie->start();
                ui->bottom_bar_updater_label->setToolTip("Your Neblio client is up-to-date.");
            }
        } catch (std::exception& ex) {
            ui->bottom_bar_updater_label->setMovie(bottom_bar_updater_error_movie);
            bottom_bar_updater_error_movie->start();
            ui->bottom_bar_updater_label->setToolTip(QString("Unable to retrieve update information: ") + QString(ex.what()));
        }
        updateConcluderTimer->stop();
        printf("Done with updates check.\n");
        isUpdateRunning = false;
    }
}

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

void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
{
    int unit = model->getOptionsModel()->getDisplayUnit();
    currentBalance = balance;
    currentStake = stake;
    currentUnconfirmedBalance = unconfirmedBalance;
    currentImmatureBalance = immatureBalance;
    ui->spendable_value_label->setText(BitcoinUnits::formatWithUnit(unit, balance));
    ui->stake_value_label->setText(BitcoinUnits::formatWithUnit(unit, stake));
    ui->unconfirmed_value_label->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance));
    ui->immature_value_label->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance));
    ui->total_value_label->setText(BitcoinUnits::formatWithUnit(unit, balance + stake + unconfirmedBalance + immatureBalance));

    // only show immature (newly mined) balance if it's non-zero, so as not to complicate things
    // for the non-mining users
    bool showImmature = immatureBalance != 0;
    ui->immature_value_label->setVisible(showImmature);
    ui->immature_title_label->setVisible(showImmature);
}

void OverviewPage::setupUpdateControls()
{
    // Updater animations
    QSize updaterIconSize(this->height()/ui->bottom_bar_downscale_factor,
                          this->height()/ui->bottom_bar_downscale_factor);

    bottom_bar_updater_check_movie = new QMovie(":images/update-animated-check", QByteArray(), ui->bottom_bar_widget);
    bottom_bar_updater_check_movie->setScaledSize(updaterIconSize);
    bottom_bar_updater_check_movie->start();

    bottom_bar_updater_no_update_movie = new QMovie(":images/update-no-update", QByteArray(), ui->bottom_bar_widget);
    bottom_bar_updater_no_update_movie->setScaledSize(updaterIconSize);
    bottom_bar_updater_no_update_movie->start();

    bottom_bar_updater_error_movie = new QMovie(":images/update-error", QByteArray(), ui->bottom_bar_widget);
    bottom_bar_updater_error_movie->setScaledSize(updaterIconSize);
    bottom_bar_updater_error_movie->start();

    bottom_bar_updater_spinner_movie = new QMovie(":images/update-spinner", QByteArray(), ui->bottom_bar_widget);
    bottom_bar_updater_spinner_movie->setScaledSize(updaterIconSize);
    bottom_bar_updater_spinner_movie->start();

    connect(bottom_bar_updater_check_movie, &QMovie::frameChanged,
            this, &OverviewPage::updateCheckAnimation_frameChanged);

    connect(ui->bottom_bar_updater_label, &ClickableLabel::clicked,
            this, &OverviewPage::checkForNeblioUpdates);

    connect(updateConcluderTimer, &QTimer::timeout,
            this, &OverviewPage::finishCheckForNeblioUpdates);

    connect(updateCheckTimer, &QTimer::timeout,
            this, &OverviewPage::checkForNeblioUpdates);
}

void OverviewPage::setModel(WalletModel *model)
{
    this->model = model;
    if(model && model->getOptionsModel())
    {
        // Set up transaction list
        filter = new TransactionFilterProxy();
        filter->setSourceModel(model->getTransactionTableModel());
        filter->setLimit(NUM_ITEMS);
        filter->setDynamicSortFilter(true);
        filter->setSortRole(Qt::EditRole);
        filter->setShowInactive(false);
        filter->sort(TransactionTableModel::Status, Qt::DescendingOrder);

        ui->listTransactions->setModel(filter);
        ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);

        // Keep up to date with wallet
        setBalance(model->getBalance(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance());
        connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64)));

        connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
    }

    // update the display unit, to not use the default ("BTC")
    updateDisplayUnit();
}

void OverviewPage::updateDisplayUnit()
{
    if(model && model->getOptionsModel())
    {
        if(currentBalance != -1)
            setBalance(currentBalance, model->getStake(), currentUnconfirmedBalance, currentImmatureBalance);

        // Update txdelegate->unit with the current unit
        txdelegate->unit = model->getOptionsModel()->getDisplayUnit();

        ui->listTransactions->update();
    }
}

void OverviewPage::showOutOfSyncWarning(bool fShow)
{
    ui->labelWalletStatus->setVisible(fShow);
    ui->labelTransactionsStatus->setVisible(fShow);
}
<commit_msg>Renamed some resource files in the source and a tooltip change while updates are running.<commit_after>#include "overviewpage.h"
#include "ui_overviewpage.h"

#include "walletmodel.h"
#include "bitcoinunits.h"
#include "optionsmodel.h"
#include "transactiontablemodel.h"
#include "transactionfilterproxy.h"
#include "guiutil.h"
#include "guiconstants.h"

#include <QAbstractItemDelegate>
#include <QPainter>

#define DECORATION_SIZE 64
#define NUM_ITEMS 3

class TxViewDelegate : public QAbstractItemDelegate
{
    Q_OBJECT
public:
    TxViewDelegate(): QAbstractItemDelegate(), unit(BitcoinUnits::BTC)
    {

    }

    inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
                      const QModelIndex &index ) const
    {
        painter->save();

        QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
        QRect mainRect = option.rect;
        QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE));
        int xspace = DECORATION_SIZE + 8;
        int ypad = 6;
        int halfheight = (mainRect.height() - 2*ypad)/2;
        QRect amountRect(mainRect.left() + xspace, mainRect.top()+ypad, mainRect.width() - xspace, halfheight);
        QRect addressRect(mainRect.left() + xspace, mainRect.top()+ypad+halfheight, mainRect.width() - xspace, halfheight);
        icon.paint(painter, decorationRect);

        QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime();
        QString address = index.data(Qt::DisplayRole).toString();
        qint64 amount = index.data(TransactionTableModel::AmountRole).toLongLong();
        bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool();
        QVariant value = index.data(Qt::ForegroundRole);
        QColor foreground = option.palette.color(QPalette::Text);
        if(qVariantCanConvert<QColor>(value))
        {
            foreground = qvariant_cast<QColor>(value);
        }

        painter->setPen(foreground);
        painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address);

        if(amount < 0)
        {
            foreground = COLOR_NEGATIVE;
        }
        else if(!confirmed)
        {
            foreground = COLOR_UNCONFIRMED;
        }
        else
        {
            foreground = option.palette.color(QPalette::Text);
        }
        painter->setPen(foreground);
        QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true);
        if(!confirmed)
        {
            amountText = QString("[") + amountText + QString("]");
        }
        painter->drawText(amountRect, Qt::AlignRight|Qt::AlignVCenter, amountText);

        painter->setPen(option.palette.color(QPalette::Text));
        painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::dateTimeStr(date));

        painter->restore();
    }

    inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        return QSize(DECORATION_SIZE, DECORATION_SIZE);
    }

    int unit;

};
#include "overviewpage.moc"

OverviewPage::OverviewPage(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::OverviewPage),
    currentBalance(-1),
    currentStake(0),
    currentUnconfirmedBalance(-1),
    currentImmatureBalance(-1),
    txdelegate(new TxViewDelegate()),
    filter(0)
{
    isUpdateRunning = false;
    updateConcluderTimer = new QTimer(this);
    updateConcluderTimeout = 3000;
    updateCheckTimer = new QTimer(this);
    updateCheckTimerTimeout = 15*60*1000; //check for updates every 15 minutes

    ui->setupUi(this);

    setupUpdateControls();

    // Recent transactions
    ui->listTransactions->setItemDelegate(txdelegate);
    ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
    ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
    ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false);

    connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex)));

    // init "out of sync" warning labels
    ui->labelWalletStatus->setText("(" + tr("out of sync") + ")");
    ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")");

    // start with displaying the "out of sync" warnings
    showOutOfSyncWarning(true);

    updateCheckTimer->start(updateCheckTimerTimeout);
    checkForNeblioUpdates();
}

void OverviewPage::handleTransactionClicked(const QModelIndex &index)
{
    if(filter)
        emit transactionClicked(filter->mapToSource(index));
}

void OverviewPage::updateCheckAnimation_frameChanged(int frameNumber) {
    if(frameNumber == (bottom_bar_updater_check_movie->frameCount()-1)) {
        bottom_bar_updater_check_movie->stop();
    }
}

void OverviewPage::checkForNeblioUpdates()
{
    if(!isUpdateRunning) {
        printf("Checking for updates...\n");
        ui->bottom_bar_updater_label->setToolTip("Checking for updates...");
        ui->bottom_bar_updater_label->setMovie(bottom_bar_updater_spinner_movie);
        bottom_bar_updater_spinner_movie->start();
        latestVersion.clear();
        updateAvailablePromise = boost::promise<bool>();
        updateAvailableFuture = updateAvailablePromise.get_future();
        boost::thread updaterThread(boost::bind(&NeblioUpdater::checkIfUpdateIsAvailable,
                                    &neblioUpdater,
                                    boost::ref(updateAvailablePromise),
                                    boost::ref(latestVersion)
                                    ));
        updaterThread.detach();
        updateConcluderTimer->start(updateConcluderTimeout);
        isUpdateRunning = true;
    }
}

void OverviewPage::finishCheckForNeblioUpdates()
{
    if(isUpdateRunning) {
        printf("Concluding update check...\n");
        try {
            bool updateAvailable = updateAvailableFuture.get();
            if(updateAvailable) {
                ui->bottom_bar_updater_label->setMovie(bottom_bar_updater_no_update_movie);
                bottom_bar_updater_no_update_movie->start();
                ui->bottom_bar_updater_label->setToolTip("An update exists. Please visit nebl.io and download it.");
            } else {
                ui->bottom_bar_updater_label->setMovie(bottom_bar_updater_check_movie);
                bottom_bar_updater_check_movie->start();
                ui->bottom_bar_updater_label->setToolTip("Your Neblio client is up-to-date.");
            }
        } catch (std::exception& ex) {
            ui->bottom_bar_updater_label->setMovie(bottom_bar_updater_error_movie);
            bottom_bar_updater_error_movie->start();
            ui->bottom_bar_updater_label->setToolTip(QString("Unable to retrieve update information: ") + QString(ex.what()));
        }
        updateConcluderTimer->stop();
        printf("Done with updates check.\n");
        isUpdateRunning = false;
    }
}

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

void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
{
    int unit = model->getOptionsModel()->getDisplayUnit();
    currentBalance = balance;
    currentStake = stake;
    currentUnconfirmedBalance = unconfirmedBalance;
    currentImmatureBalance = immatureBalance;
    ui->spendable_value_label->setText(BitcoinUnits::formatWithUnit(unit, balance));
    ui->stake_value_label->setText(BitcoinUnits::formatWithUnit(unit, stake));
    ui->unconfirmed_value_label->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance));
    ui->immature_value_label->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance));
    ui->total_value_label->setText(BitcoinUnits::formatWithUnit(unit, balance + stake + unconfirmedBalance + immatureBalance));

    // only show immature (newly mined) balance if it's non-zero, so as not to complicate things
    // for the non-mining users
    bool showImmature = immatureBalance != 0;
    ui->immature_value_label->setVisible(showImmature);
    ui->immature_title_label->setVisible(showImmature);
}

void OverviewPage::setupUpdateControls()
{
    // Updater animations
    QSize updaterIconSize(this->height()/ui->bottom_bar_downscale_factor,
                          this->height()/ui->bottom_bar_downscale_factor);

    bottom_bar_updater_check_movie = new QMovie(":images/update-animated-check", QByteArray(), ui->bottom_bar_widget);
    bottom_bar_updater_check_movie->setScaledSize(updaterIconSize);
    bottom_bar_updater_check_movie->start();

    bottom_bar_updater_no_update_movie = new QMovie(":images/update-update-available", QByteArray(), ui->bottom_bar_widget);
    bottom_bar_updater_no_update_movie->setScaledSize(updaterIconSize);
    bottom_bar_updater_no_update_movie->start();

    bottom_bar_updater_error_movie = new QMovie(":images/update-error", QByteArray(), ui->bottom_bar_widget);
    bottom_bar_updater_error_movie->setScaledSize(updaterIconSize);
    bottom_bar_updater_error_movie->start();

    bottom_bar_updater_spinner_movie = new QMovie(":images/update-spinner", QByteArray(), ui->bottom_bar_widget);
    bottom_bar_updater_spinner_movie->setScaledSize(updaterIconSize);
    bottom_bar_updater_spinner_movie->start();

    connect(bottom_bar_updater_check_movie, &QMovie::frameChanged,
            this, &OverviewPage::updateCheckAnimation_frameChanged);

    connect(ui->bottom_bar_updater_label, &ClickableLabel::clicked,
            this, &OverviewPage::checkForNeblioUpdates);

    connect(updateConcluderTimer, &QTimer::timeout,
            this, &OverviewPage::finishCheckForNeblioUpdates);

    connect(updateCheckTimer, &QTimer::timeout,
            this, &OverviewPage::checkForNeblioUpdates);
}

void OverviewPage::setModel(WalletModel *model)
{
    this->model = model;
    if(model && model->getOptionsModel())
    {
        // Set up transaction list
        filter = new TransactionFilterProxy();
        filter->setSourceModel(model->getTransactionTableModel());
        filter->setLimit(NUM_ITEMS);
        filter->setDynamicSortFilter(true);
        filter->setSortRole(Qt::EditRole);
        filter->setShowInactive(false);
        filter->sort(TransactionTableModel::Status, Qt::DescendingOrder);

        ui->listTransactions->setModel(filter);
        ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);

        // Keep up to date with wallet
        setBalance(model->getBalance(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance());
        connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64)));

        connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
    }

    // update the display unit, to not use the default ("BTC")
    updateDisplayUnit();
}

void OverviewPage::updateDisplayUnit()
{
    if(model && model->getOptionsModel())
    {
        if(currentBalance != -1)
            setBalance(currentBalance, model->getStake(), currentUnconfirmedBalance, currentImmatureBalance);

        // Update txdelegate->unit with the current unit
        txdelegate->unit = model->getOptionsModel()->getDisplayUnit();

        ui->listTransactions->update();
    }
}

void OverviewPage::showOutOfSyncWarning(bool fShow)
{
    ui->labelWalletStatus->setVisible(fShow);
    ui->labelTransactionsStatus->setVisible(fShow);
}
<|endoftext|>


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

#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif

#include <qt/splashscreen.h>

#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <interfaces/wallet.h>
#include <qt/guiutil.h>
#include <qt/networkstyle.h>
#include <ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
#include <version.h>

#include <QApplication>
#include <QCloseEvent>
#include <QPainter>
#include <QRadialGradient>
#include <QScreen>

SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
    QWidget(nullptr, f), curAlignment(0), m_node(node)
{
    // set reference point, paddings
    int paddingRight            = 50;
    int paddingTop              = 50;
    int titleVersionVSpace      = 17;
    int titleCopyrightVSpace    = 40;

    float fontFactor            = 1.0;
    float devicePixelRatio      = 1.0;
    devicePixelRatio = static_cast<QGuiApplication*>(QCoreApplication::instance())->devicePixelRatio();

    // define text to place
    QString titleText       = PACKAGE_NAME;
    QString versionText     = QString("Version %1").arg(QString::fromStdString(FormatFullVersion()));
    QString copyrightText   = QString::fromUtf8(CopyrightHolders(strprintf("\xc2\xA9 %u-%u ", 2009, COPYRIGHT_YEAR)).c_str());
    QString titleAddText    = networkStyle->getTitleAddText();

    QString font            = QApplication::font().toString();

    // create a bitmap according to device pixelratio
    QSize splashSize(480*devicePixelRatio,320*devicePixelRatio);
    pixmap = QPixmap(splashSize);

    // change to HiDPI if it makes sense
    pixmap.setDevicePixelRatio(devicePixelRatio);

    QPainter pixPaint(&pixmap);
    pixPaint.setPen(QColor(100,100,100));

    // draw a slightly radial gradient
    QRadialGradient gradient(QPoint(0,0), splashSize.width()/devicePixelRatio);
    gradient.setColorAt(0, Qt::white);
    gradient.setColorAt(1, QColor(247,247,247));
    QRect rGradient(QPoint(0,0), splashSize);
    pixPaint.fillRect(rGradient, gradient);

    // draw the bitcoin icon, expected size of PNG: 1024x1024
    QRect rectIcon(QPoint(-150,-122), QSize(430,430));

    const QSize requiredSize(1024,1024);
    QPixmap icon(networkStyle->getAppIcon().pixmap(requiredSize));

    pixPaint.drawPixmap(rectIcon, icon);

    // check font size and drawing with
    pixPaint.setFont(QFont(font, 33*fontFactor));
    QFontMetrics fm = pixPaint.fontMetrics();
    int titleTextWidth = GUIUtil::TextWidth(fm, titleText);
    if (titleTextWidth > 176) {
        fontFactor = fontFactor * 176 / titleTextWidth;
    }

    pixPaint.setFont(QFont(font, 33*fontFactor));
    fm = pixPaint.fontMetrics();
    titleTextWidth  = GUIUtil::TextWidth(fm, titleText);
    pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight,paddingTop,titleText);

    pixPaint.setFont(QFont(font, 15*fontFactor));

    // if the version string is too long, reduce size
    fm = pixPaint.fontMetrics();
    int versionTextWidth  = GUIUtil::TextWidth(fm, versionText);
    if(versionTextWidth > titleTextWidth+paddingRight-10) {
        pixPaint.setFont(QFont(font, 10*fontFactor));
        titleVersionVSpace -= 5;
    }
    pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight+2,paddingTop+titleVersionVSpace,versionText);

    // draw copyright stuff
    {
        pixPaint.setFont(QFont(font, 10*fontFactor));
        const int x = pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight;
        const int y = paddingTop+titleCopyrightVSpace;
        QRect copyrightRect(x, y, pixmap.width() - x - paddingRight, pixmap.height() - y);
        pixPaint.drawText(copyrightRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, copyrightText);
    }

    // draw additional text if special network
    if(!titleAddText.isEmpty()) {
        QFont boldFont = QFont(font, 10*fontFactor);
        boldFont.setWeight(QFont::Bold);
        pixPaint.setFont(boldFont);
        fm = pixPaint.fontMetrics();
        int titleAddTextWidth  = GUIUtil::TextWidth(fm, titleAddText);
        pixPaint.drawText(pixmap.width()/devicePixelRatio-titleAddTextWidth-10,15,titleAddText);
    }

    pixPaint.end();

    // Set window title
    setWindowTitle(titleText + " " + titleAddText);

    // Resize window and move to center of desktop, disallow resizing
    QRect r(QPoint(), QSize(pixmap.size().width()/devicePixelRatio,pixmap.size().height()/devicePixelRatio));
    resize(r.size());
    setFixedSize(r.size());
    move(QGuiApplication::primaryScreen()->geometry().center() - r.center());

    subscribeToCoreSignals();
    installEventFilter(this);
}

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

bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
    if (ev->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
        if(keyEvent->text()[0] == 'q') {
            m_node.startShutdown();
        }
    }
    return QObject::eventFilter(obj, ev);
}

void SplashScreen::finish()
{
    /* If the window is minimized, hide() will be ignored. */
    /* Make sure we de-minimize the splashscreen window before hiding */
    if (isMinimized())
        showNormal();
    hide();
    deleteLater(); // No more need for this
}

static void InitMessage(SplashScreen *splash, const std::string &message)
{
    bool invoked = QMetaObject::invokeMethod(splash, "showMessage",
        Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(message)),
        Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter),
        Q_ARG(QColor, QColor(55,55,55)));
    assert(invoked);
}

static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible)
{
    InitMessage(splash, title + std::string("\n") +
            (resume_possible ? _("(press q to shutdown and continue later)").translated
                                : _("press q to shutdown").translated) +
            strprintf("\n%d", nProgress) + "%");
}
#ifdef ENABLE_WALLET
void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet)
{
    m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
    m_connected_wallets.emplace_back(std::move(wallet));
}
#endif

void SplashScreen::subscribeToCoreSignals()
{
    // Connect signals to client
    m_handler_init_message = m_node.handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
    m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
#ifdef ENABLE_WALLET
    m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
#endif
}

void SplashScreen::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    m_handler_init_message->disconnect();
    m_handler_show_progress->disconnect();
    for (const auto& handler : m_connected_wallet_handlers) {
        handler->disconnect();
    }
    m_connected_wallet_handlers.clear();
    m_connected_wallets.clear();
}

void SplashScreen::showMessage(const QString &message, int alignment, const QColor &color)
{
    curMessage = message;
    curAlignment = alignment;
    curColor = color;
    update();
}

void SplashScreen::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(0, 0, pixmap);
    QRect r = rect().adjusted(5, 5, -5, -5);
    painter.setPen(curColor);
    painter.drawText(r, curAlignment, curMessage);
}

void SplashScreen::closeEvent(QCloseEvent *event)
{
    m_node.startShutdown(); // allows an "emergency" shutdown during startup
    event->ignore();
}
<commit_msg>Port splash screen<commit_after>// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif

#include <qt/splashscreen.h>

#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <interfaces/wallet.h>
#include <qt/guiutil.h>
#include <qt/networkstyle.h>
#include <ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
#include <version.h>

#include <QApplication>
#include <QCloseEvent>
#include <QPainter>
#include <QRadialGradient>
#include <QScreen>

SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
    QWidget(nullptr, f), curAlignment(0), m_node(node)
{
    // set sizes
    int versionTextHeight       = 30;
    int statusHeight            = 30;
    int titleAddTextHeight      = 20;
    float fontFactor            = 1.0;
    float devicePixelRatio      = 1.0;
    devicePixelRatio = static_cast<QGuiApplication*>(QCoreApplication::instance())->devicePixelRatio();

    // define text to place
    QString titleText       = PACKAGE_NAME;
    QString versionText     = QString("Version %1").arg(QString::fromStdString(FormatFullVersion()));
    QString copyrightText   = QString::fromUtf8(CopyrightHolders(strprintf("\xc2\xA9 %u ", COPYRIGHT_YEAR)).c_str());
    QString titleAddText    = networkStyle->getTitleAddText();

    QString font            = QApplication::font().toString();

    // create a bitmap according to device pixelratio
    QSize splashSize(480,320);
    pixmap = QPixmap(480*devicePixelRatio,320*devicePixelRatio);

    // change to HiDPI if it makes sense
    pixmap.setDevicePixelRatio(devicePixelRatio);

    QPainter pixPaint(&pixmap);
    pixPaint.setPen(QColor("#ffffff"));

    QRect mainRect(QPoint(0,0), splashSize);
    pixPaint.fillRect(mainRect, QColor("#030509"));

    // draw background
    QRect rectBg(QPoint(-50, -50), QSize(splashSize.width() + 50, splashSize.height() + 50));
    QPixmap bg(":/styles/app-icons/splash_bg");
    pixPaint.drawPixmap(rectBg, bg);

    pixPaint.setFont(QFont(font, 32*fontFactor, QFont::Bold));
    QRect rectTitle(QPoint(0,0), QSize(splashSize.width(), (splashSize.height() / 2)));
    pixPaint.drawText(rectTitle, Qt::AlignHCenter | Qt::AlignBottom, titleText);

    QPoint versionPoint(rectTitle.bottomLeft());

    // draw additional text if special network
    if(!titleAddText.isEmpty())
    {
        QRect titleAddRect(rectTitle.bottomLeft(), QSize(rectTitle.width(), titleAddTextHeight));
        versionPoint = titleAddRect.bottomLeft();
        pixPaint.setFont(QFont(font, 8*fontFactor, QFont::Bold));
        pixPaint.drawText(titleAddRect, Qt::AlignHCenter | Qt::AlignVCenter, titleAddText);
    }

    pixPaint.setFont(QFont(font, 11*fontFactor));
    QRect versionRect(versionPoint, QSize(rectTitle.width(), versionTextHeight));
    pixPaint.drawText(versionRect, Qt::AlignHCenter | Qt::AlignTop, versionText);

    // draw copyright stuff
    QFont statusFont = QApplication::font();
    statusFont.setPointSizeF(statusFont.pointSizeF() * 0.9);
    pixPaint.setFont(statusFont);
    QRect statusRect(mainRect.left(), mainRect.height() - statusHeight, mainRect.width(), statusHeight);
    QColor statusColor(255, 255, 255);
    statusColor.setAlphaF(0.1);
    pixPaint.fillRect(statusRect, statusColor);
    pixPaint.drawText(statusRect.adjusted(10, 0, -10, 0), Qt::AlignLeft | Qt::AlignVCenter, copyrightText);

    pixPaint.end();

    // Set window title
    setWindowTitle(titleText + " " + titleAddText);

    // Resize window and move to center of desktop, disallow resizing
    QRect r(QPoint(), QSize(pixmap.size().width()/devicePixelRatio,pixmap.size().height()/devicePixelRatio));
    resize(r.size());
    setFixedSize(r.size());
    move(QGuiApplication::primaryScreen()->geometry().center() - r.center());

    subscribeToCoreSignals();
    installEventFilter(this);
}

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

bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
    if (ev->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
        if(keyEvent->text()[0] == 'q') {
            m_node.startShutdown();
        }
    }
    return QObject::eventFilter(obj, ev);
}

void SplashScreen::finish()
{
    /* If the window is minimized, hide() will be ignored. */
    /* Make sure we de-minimize the splashscreen window before hiding */
    if (isMinimized())
        showNormal();
    hide();
    deleteLater(); // No more need for this
}

static void InitMessage(SplashScreen *splash, const std::string &message)
{
    bool invoked = QMetaObject::invokeMethod(splash, "showMessage",
        Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(message)),
        Q_ARG(int, Qt::AlignBottom|Qt::AlignRight),
        Q_ARG(QColor, QColor("#ffffff")));
    assert(invoked);
}

static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible)
{
    InitMessage(splash, title + std::string("\n") +
            (resume_possible ? _("(press q to shutdown and continue later)").translated
                                : _("press q to shutdown").translated) +
            strprintf("\n%d", nProgress) + "%");
}
#ifdef ENABLE_WALLET
void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet)
{
    m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
    m_connected_wallets.emplace_back(std::move(wallet));
}
#endif

void SplashScreen::subscribeToCoreSignals()
{
    // Connect signals to client
    m_handler_init_message = m_node.handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
    m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
#ifdef ENABLE_WALLET
    m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
#endif
}

void SplashScreen::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    m_handler_init_message->disconnect();
    m_handler_show_progress->disconnect();
    for (const auto& handler : m_connected_wallet_handlers) {
        handler->disconnect();
    }
    m_connected_wallet_handlers.clear();
    m_connected_wallets.clear();
}

void SplashScreen::showMessage(const QString &message, int alignment, const QColor &color)
{
    curMessage = message;
    curAlignment = alignment;
    curColor = color;
    update();
}

void SplashScreen::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(0, 0, pixmap);
    QRect r = rect().adjusted(10, 10, -10, -10);
    painter.setPen(curColor);
    QFont font = QApplication::font();
    font.setPointSizeF(font.pointSizeF() * 0.9);
    painter.setFont(font);
    painter.drawText(r, curAlignment, curMessage);
}

void SplashScreen::closeEvent(QCloseEvent *event)
{
    m_node.startShutdown(); // allows an "emergency" shutdown during startup
    event->ignore();
}
<|endoftext|>


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

#include "splashscreen.h"

#include "clientversion.h"
#include "ui_interface.h"
#include "util.h"

#include <QApplication>
#include <QPainter>

SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet) :
    QSplashScreen(pixmap, f)
{
    setAutoFillBackground(true);

    // set reference point, paddings
    int paddingRight            = 50;
    int paddingTop              = 50;
    int titleVersionVSpace      = 17;
    int titleCopyrightVSpace    = 40;

    float fontFactor            = 1.0;

    // define text to place
    QString titleText       = tr("Dogecoin Core");
    QString versionText     = QString("Version %1").arg(QString::fromStdString(FormatFullVersion()));
    QString copyrightText   = QChar(0xA9)+QString(" 2009-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The Bitcoin Core developers"));
    QString testnetAddText  = QString(tr("[testnet]")); // define text to place as single text object

    QString font            = "Comic Sans MS";

    // load the bitmap for writing some text over it
    QPixmap newPixmap;
    if(isTestNet) {
        newPixmap     = QPixmap(":/images/splash_testnet");
    }
    else {
        newPixmap     = QPixmap(":/images/splash");
    }

    QPainter pixPaint(&newPixmap);
    pixPaint.setPen(QColor(100,100,100));

    // check font size and drawing with
    pixPaint.setFont(QFont(font, 33*fontFactor));
    QFontMetrics fm = pixPaint.fontMetrics();
    int titleTextWidth  = fm.width(titleText);
    if(titleTextWidth > 160) {
        // strange font rendering, Arial probably not found
        fontFactor = 0.75;
    }

    pixPaint.setFont(QFont(font, 33*fontFactor));
    fm = pixPaint.fontMetrics();
    titleTextWidth  = fm.width(titleText);
    pixPaint.drawText(newPixmap.width()-titleTextWidth-paddingRight,paddingTop,titleText);

    pixPaint.setFont(QFont(font, 15*fontFactor));

    // if the version string is to long, reduce size
    fm = pixPaint.fontMetrics();
    int versionTextWidth  = fm.width(versionText);
    if(versionTextWidth > titleTextWidth+paddingRight-10) {
        pixPaint.setFont(QFont(font, 10*fontFactor));
        titleVersionVSpace -= 5;
    }
    pixPaint.drawText(newPixmap.width()-titleTextWidth-paddingRight+2,paddingTop+titleVersionVSpace,versionText);

    // draw copyright stuff
    pixPaint.setFont(QFont(font, 10*fontFactor));
    pixPaint.drawText(newPixmap.width()-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,copyrightText);

    // draw testnet string if testnet is on
    if(isTestNet) {
        QFont boldFont = QFont(font, 10*fontFactor);
        boldFont.setWeight(QFont::Bold);
        pixPaint.setFont(boldFont);
        fm = pixPaint.fontMetrics();
        int testnetAddTextWidth  = fm.width(testnetAddText);
        pixPaint.drawText(newPixmap.width()-testnetAddTextWidth-10,15,testnetAddText);
    }

    pixPaint.end();

    this->setPixmap(newPixmap);

    subscribeToCoreSignals();
}

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

void SplashScreen::slotFinish(QWidget *mainWin)
{
    finish(mainWin);
}

static void InitMessage(SplashScreen *splash, const std::string &message)
{
    QMetaObject::invokeMethod(splash, "showMessage",
        Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(message)),
        Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter),
        Q_ARG(QColor, QColor(55,55,55)));
}

void SplashScreen::subscribeToCoreSignals()
{
    // Connect signals to client
    uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1));
}

void SplashScreen::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1));
}
<commit_msg>Add Dogecoin devs to splashscreen copyright.<commit_after>// Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "splashscreen.h"

#include "clientversion.h"
#include "ui_interface.h"
#include "util.h"

#include <QApplication>
#include <QPainter>
#include <QRectF>

SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet) :
    QSplashScreen(pixmap, f)
{
    setAutoFillBackground(true);

    // set reference point, paddings
    int paddingRight            = 50;
    int paddingTop              = 50;
    int titleVersionVSpace      = 17;
    int titleCopyrightVSpace    = 40;

    float fontFactor            = 1.0;

    // define text to place
    QString titleText       = tr("Dogecoin Core");
    QString versionText     = QString("Version %1").arg(QString::fromStdString(FormatFullVersion()));
    QString copyrightText   = QChar(0xA9)+QString(" 2009-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The Bitcoin Core developers\n")) + QChar(0xA9)+QString(" 2013-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The Dogecoin Core developers"));
    QString testnetAddText  = QString(tr("[testnet]")); // define text to place as single text object

    QString font            = "Comic Sans MS";

    // load the bitmap for writing some text over it
    QPixmap newPixmap;
    if(isTestNet) {
        newPixmap     = QPixmap(":/images/splash_testnet");
    }
    else {
        newPixmap     = QPixmap(":/images/splash");
    }

    QPainter pixPaint(&newPixmap);
    pixPaint.setPen(QColor(100,100,100));

    // check font size and drawing with
    pixPaint.setFont(QFont(font, 33*fontFactor));
    QFontMetrics fm = pixPaint.fontMetrics();
    int titleTextWidth  = fm.width(titleText);
    if(titleTextWidth > 160) {
        // strange font rendering, Arial probably not found
        fontFactor = 0.75;
    }

    pixPaint.setFont(QFont(font, 33*fontFactor));
    fm = pixPaint.fontMetrics();
    titleTextWidth  = fm.width(titleText);
    pixPaint.drawText(newPixmap.width()-titleTextWidth-paddingRight,paddingTop,titleText);

    pixPaint.setFont(QFont(font, 15*fontFactor));

    // if the version string is to long, reduce size
    fm = pixPaint.fontMetrics();
    int versionTextWidth  = fm.width(versionText);
    if(versionTextWidth > titleTextWidth+paddingRight-10) {
        pixPaint.setFont(QFont(font, 10*fontFactor));
        titleVersionVSpace -= 5;
    }
    pixPaint.drawText(newPixmap.width()-titleTextWidth-paddingRight+2,paddingTop+titleVersionVSpace,versionText);

    // draw copyright stuff
    pixPaint.setFont(QFont(font, 10*fontFactor));
    QRect copyrightRect = QRect(newPixmap.width()-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,fm.width(copyrightText),fm.height()*2);
    pixPaint.drawText(copyrightRect,Qt::TextWordWrap,copyrightText);

    // draw testnet string if testnet is on
    if(isTestNet) {
        QFont boldFont = QFont(font, 10*fontFactor);
        boldFont.setWeight(QFont::Bold);
        pixPaint.setFont(boldFont);
        fm = pixPaint.fontMetrics();
        int testnetAddTextWidth  = fm.width(testnetAddText);
        pixPaint.drawText(newPixmap.width()-testnetAddTextWidth-10,15,testnetAddText);
    }

    pixPaint.end();

    this->setPixmap(newPixmap);

    subscribeToCoreSignals();
}

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

void SplashScreen::slotFinish(QWidget *mainWin)
{
    finish(mainWin);
}

static void InitMessage(SplashScreen *splash, const std::string &message)
{
    QMetaObject::invokeMethod(splash, "showMessage",
        Qt::QueuedConnection,
        Q_ARG(QString, QString::fromStdString(message)),
        Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter),
        Q_ARG(QColor, QColor(55,55,55)));
}

void SplashScreen::subscribeToCoreSignals()
{
    // Connect signals to client
    uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1));
}

void SplashScreen::unsubscribeFromCoreSignals()
{
    // Disconnect signals from client
    uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1));
}
<|endoftext|>


<commit_before>#include "centsa/request_process.h"
#include "centsa/ui.h"
#include "centsa/ping.h"

#include "rapidjson/document.h"

#include <iostream>
#include <vector>

std::string mainPage(int &code, const char *data)
{
	code = 200;
	return ui_html();
}

std::string pingServer(int &code, const char *data)
{
	code = 204;
	ping::alive();
	return std::string("");
}

std::string addAccount(int &code, const char *data)
{
	try
	{
		long id = dao::addAccount(data);
		std::cout << "Created new account: " << id << "\n";
		code = 200;
		return std::to_string(id);
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string setSetting(int &code, const char *data)
{
	try
	{
		rapidjson::Document setting;
		rapidjson::ParseResult result = setting.Parse(data);
		if (result)
		{
			const char *key = setting["setting"].GetString();
			const char *value = setting["value"].GetString();
			dao::setSetting(key, value);
			std::cout << "Set " << key << " to " << value << "\n";
			code = 204;
			return std::string("");
		}

		code = 400;
		return std::string("JSON error ") + data;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string saveTrans(int &code, const char *data)
{
	try
	{
		rapidjson::Document trans;
		rapidjson::ParseResult result = trans.Parse(data);
		if (result)
		{
			dao::transaction t;

			t.amount = trans["amount"].GetDouble();
			t.comment = trans["comment"].GetString();
			t.accountId = trans["account"].GetInt64();
			t.typeId = trans["type"].GetInt64();
			t.expenseId = trans["expense"].GetInt64();
			t.date = trans["date"].GetInt64();

			long id = dao::saveTransaction(t);
			code = 200;
			return std::to_string(id);
		}

		code = 400;
		return std::string("JSON error ") + data;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string addType(int &code, const char *data)
{
	try
	{
		long id = dao::addType(data);
		std::cout << "Created new type: " << id << "\n";
		code = 200;
		return std::to_string(id);
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getTrans(int &code, const char *data)
{
	try
	{
		rapidjson::Document req;
		rapidjson::ParseResult result = req.Parse(data);
		if (result)
		{
			std::vector<dao::transaction> trans = dao::getTransactions(req["limit"].GetInt64(), req["offset"].GetInt64());
			std::string resp("[");
			for (dao::transaction t : trans)
			{
				resp += "{";
				resp += "\"id\":";
				resp += std::to_string(t.id);
				resp += ",\"amount\":";
				resp += std::to_string(t.amount);
				resp += ",\"comment\":\"";
				resp += t.comment;
				resp += "\",\"account\":";
				resp += std::to_string(t.accountId);
				resp += ",\"type\":";
				resp += std::to_string(t.typeId);
				resp += ",\"date\":";
				resp += std::to_string(t.date);
				resp += ",\"expense\":";
				resp += std::to_string(t.expenseId);
				resp += "},";
			}
			if (trans.size() > 0)
				resp.pop_back();
			resp += "]";
			code = 200;
			return resp;
		}
		code = 400;
		return std::string("JSON error ") + data;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getAccounts(int &code, const char *data)
{
	try
	{
		std::vector<dao::account> accounts = dao::getAccounts();
		std::string resp("[");
		for (dao::account account : accounts)
		{
			resp += "{\"id\":";
			resp += std::to_string(account.id);
			resp += ",\"name\":\"";
			resp += account.name;
			resp += "\"},";
		}
		if (accounts.size() > 0)
			resp.pop_back();
		resp += "]";
		code = 200;
		return resp;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getTypes(int &code, const char *data)
{
	try
	{
		std::vector<dao::type> types = dao::getTypes();
		std::string resp("[");
		for (dao::type type : types)
		{
			resp += "{\"id\":";
			resp += std::to_string(type.id);
			resp += ",\"name\":\"";
			resp += type.name;
			resp += "\"},";
		}
		if (types.size() > 0)
			resp.pop_back();
		resp += "]";
		code = 200;
		return resp;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getExpenses(int &code, const char *data)
{
	try
	{
		std::vector<dao::expense> expenses = dao::getExpensesLite();
		std::string resp("[");
		for (dao::expense expense : expenses)
		{
			resp += "{\"id\":";
			resp += std::to_string(expense.id);
			resp += ",\"name\":\"";
			resp += expense.name;
			resp += "\"},";
		}
		if (expenses.size() > 0)
			resp.pop_back();
		resp += "]";
		code = 200;
		return resp;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

void bindUris()
{
	uriBindings["/"] = mainPage;
	uriBindings["/ping"] = pingServer;
	uriBindings["/addAccount"] = addAccount;
	uriBindings["/setSetting"] = setSetting;
	uriBindings["/saveTrans"] = saveTrans;
	uriBindings["/addType"] = addType;
	uriBindings["/getTrans.json"] = getTrans;
	uriBindings["/getAccounts.json"] = getAccounts;
	uriBindings["/getTypes.json"] = getTypes;
	uriBindings["/getExpenses.json"] = getExpenses;
}
<commit_msg>Added getSetting REST service<commit_after>#include "centsa/request_process.h"
#include "centsa/ui.h"
#include "centsa/ping.h"

#include "rapidjson/document.h"

#include <iostream>
#include <vector>

std::string mainPage(int &code, const char *data)
{
	code = 200;
	return ui_html();
}

std::string pingServer(int &code, const char *data)
{
	code = 204;
	ping::alive();
	return std::string("");
}

std::string addAccount(int &code, const char *data)
{
	try
	{
		long id = dao::addAccount(data);
		std::cout << "Created new account: " << id << "\n";
		code = 200;
		return std::to_string(id);
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string setSetting(int &code, const char *data)
{
	try
	{
		rapidjson::Document setting;
		rapidjson::ParseResult result = setting.Parse(data);
		if (result)
		{
			const char *key = setting["setting"].GetString();
			const char *value = setting["value"].GetString();
			dao::setSetting(key, value);
			std::cout << "Set " << key << " to " << value << "\n";
			code = 204;
			return std::string("");
		}

		code = 400;
		return std::string("JSON error ") + data;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string saveTrans(int &code, const char *data)
{
	try
	{
		rapidjson::Document trans;
		rapidjson::ParseResult result = trans.Parse(data);
		if (result)
		{
			dao::transaction t;

			t.amount = trans["amount"].GetDouble();
			t.comment = trans["comment"].GetString();
			t.accountId = trans["account"].GetInt64();
			t.typeId = trans["type"].GetInt64();
			t.expenseId = trans["expense"].GetInt64();
			t.date = trans["date"].GetInt64();

			long id = dao::saveTransaction(t);
			code = 200;
			return std::to_string(id);
		}

		code = 400;
		return std::string("JSON error ") + data;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string addType(int &code, const char *data)
{
	try
	{
		long id = dao::addType(data);
		std::cout << "Created new type: " << id << "\n";
		code = 200;
		return std::to_string(id);
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getTrans(int &code, const char *data)
{
	try
	{
		rapidjson::Document req;
		rapidjson::ParseResult result = req.Parse(data);
		if (result)
		{
			std::vector<dao::transaction> trans = dao::getTransactions(req["limit"].GetInt64(), req["offset"].GetInt64());
			std::string resp("[");
			for (dao::transaction t : trans)
			{
				resp += "{";
				resp += "\"id\":";
				resp += std::to_string(t.id);
				resp += ",\"amount\":";
				resp += std::to_string(t.amount);
				resp += ",\"comment\":\"";
				resp += t.comment;
				resp += "\",\"account\":";
				resp += std::to_string(t.accountId);
				resp += ",\"type\":";
				resp += std::to_string(t.typeId);
				resp += ",\"date\":";
				resp += std::to_string(t.date);
				resp += ",\"expense\":";
				resp += std::to_string(t.expenseId);
				resp += "},";
			}
			if (trans.size() > 0)
				resp.pop_back();
			resp += "]";
			code = 200;
			return resp;
		}
		code = 400;
		return std::string("JSON error ") + data;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getAccounts(int &code, const char *data)
{
	try
	{
		std::vector<dao::account> accounts = dao::getAccounts();
		std::string resp("[");
		for (dao::account account : accounts)
		{
			resp += "{\"id\":";
			resp += std::to_string(account.id);
			resp += ",\"name\":\"";
			resp += account.name;
			resp += "\"},";
		}
		if (accounts.size() > 0)
			resp.pop_back();
		resp += "]";
		code = 200;
		return resp;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getTypes(int &code, const char *data)
{
	try
	{
		std::vector<dao::type> types = dao::getTypes();
		std::string resp("[");
		for (dao::type type : types)
		{
			resp += "{\"id\":";
			resp += std::to_string(type.id);
			resp += ",\"name\":\"";
			resp += type.name;
			resp += "\"},";
		}
		if (types.size() > 0)
			resp.pop_back();
		resp += "]";
		code = 200;
		return resp;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getExpenses(int &code, const char *data)
{
	try
	{
		std::vector<dao::expense> expenses = dao::getExpensesLite();
		std::string resp("[");
		for (dao::expense expense : expenses)
		{
			resp += "{\"id\":";
			resp += std::to_string(expense.id);
			resp += ",\"name\":\"";
			resp += expense.name;
			resp += "\"},";
		}
		if (expenses.size() > 0)
			resp.pop_back();
		resp += "]";
		code = 200;
		return resp;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

std::string getSetting(int &code, const char *data)
{
	try
	{
		std::string resp = dao::getSetting(data);
		code = 200;
		return resp;
	}
	catch (const char *err)
	{
		std::cerr << err << "\n";
		return std::string(err);
	}
}

void bindUris()
{
	uriBindings["/"] = mainPage;
	uriBindings["/ping"] = pingServer;
	uriBindings["/addAccount"] = addAccount;
	uriBindings["/setSetting"] = setSetting;
	uriBindings["/saveTrans"] = saveTrans;
	uriBindings["/addType"] = addType;
	uriBindings["/getTrans.json"] = getTrans;
	uriBindings["/getAccounts.json"] = getAccounts;
	uriBindings["/getTypes.json"] = getTypes;
	uriBindings["/getExpenses.json"] = getExpenses;
	uriBindings["/getSetting"] = getSetting;
}
<|endoftext|>


<commit_before>/*
 # Copyright (c) 2011, Georgia Tech Research Corporation
 # 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 Georgia Tech Research Corporation 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 GEORGIA TECH RESEARCH CORPORATION ''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 GEORGIA TECH 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.
 #

 ## author Advait Jain (Healthcare Robotics Lab, Georgia Tech.)
 ## author Chih-Hung Aaron King (Healthcare Robotics Lab, Georgia Tech.)
 */

//== This application listens for a rigid body named 'Tracker' on a remote machine
//== and publishes & tf it's position and orientation through ROS.

#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <geometry_msgs/TransformStamped.h>
#include <stdio.h>
#include <math.h>

#include <vrpn_Connection.h>
#include <vrpn_Tracker.h>

#include <LinearMath/btQuaternion.h>

void VRPN_CALLBACK track_target(void *, const vrpn_TRACKERCB t);

class TargetState {
 public:
  geometry_msgs::TransformStamped target;
};

TargetState *target_state;
std::string frame_id;
std::string corrdinate_system_string;

enum CoordinateSystem {
  vicon,
  optitrack
} corrdinate_system;

// set to true in the VRPN callback function.
bool fresh_data = false;
vrpn_TRACKERCB prev_vrpn_data;

class Rigid_Body {
 private:
  ros::Publisher target_pub;
  tf::TransformBroadcaster br;
  vrpn_Connection *connection;
  vrpn_Tracker_Remote *tracker;

 public:
  Rigid_Body(ros::NodeHandle& nh, std::string server_ip, int port) {
    target_pub = nh.advertise<geometry_msgs::TransformStamped>("pose", 100);
    std::string connec_nm = server_ip + ":" + boost::lexical_cast<std::string>(port);
    connection = vrpn_get_connection_by_name(connec_nm.c_str());
    std::string target_name = nh.getNamespace().substr(1);
    tracker = new vrpn_Tracker_Remote(target_name.c_str(), connection);
    this->tracker->register_change_handler(NULL, track_target);
  }

  void publish_target_state(TargetState *target_state) {
    br.sendTransform(target_state->target);
    target_pub.publish(target_state->target);
  }

  void step_vrpn() {
    this->tracker->mainloop();
    this->connection->mainloop();
  }
};

//== Tracker Position/Orientation Callback ==--
void VRPN_CALLBACK track_target(void *, const vrpn_TRACKERCB t) {
  btQuaternion q_orig(t.quat[0], t.quat[1], t.quat[2], t.quat[3]);
  btQuaternion q_fix(0.70710678, 0., 0., 0.70710678);

  btQuaternion q_rot;
  btVector3 pos;
  switch (corrdinate_system) {
    case optitrack: {
      // optitrak <-- funky <-- object
      // the q_fix.inverse() esures that when optitrak_funky says 0 0 0
      // for roll pitch yaw, there is still a rotation that aligns the
      // object frame with the /optitrak frame (and not /optitrak_funky)
      q_rot = q_fix * q_orig * q_fix.inverse();
      pos = btVector3(t.pos[0], -t.pos[2], t.pos[1]);
      break;
    }
    case vicon: {
      q_rot = q_orig;
      pos = btVector3(t.pos[0], t.pos[1], t.pos[2]);
      break;
    }
    default: {
      ROS_FATAL("Coordinate system not defined!");
      break;
    }
  }

  // verifying that each callback indeed gives fresh data.
  if (prev_vrpn_data.quat[0] == t.quat[0] and prev_vrpn_data.quat[1] == t.quat[1]
      and prev_vrpn_data.quat[2] == t.quat[2] and prev_vrpn_data.quat[3] == t.quat[3]
      and prev_vrpn_data.pos[0] == t.pos[0] and prev_vrpn_data.pos[1] == t.pos[1]
      and prev_vrpn_data.pos[2] == t.pos[2])
    ROS_WARN("Repeated Values");

  prev_vrpn_data = t;

  const int kMicroSecToNanoSec = 1000;
  ros::Time timestamp = ros::Time(t.msg_time.tv_sec, t.msg_time.tv_usec * kMicroSecToNanoSec);

  target_state->target.transform.translation.x = pos.x();
  target_state->target.transform.translation.y = pos.y();
  target_state->target.transform.translation.z = pos.z();

  target_state->target.transform.rotation.x = q_rot.x();
  target_state->target.transform.rotation.y = q_rot.y();
  target_state->target.transform.rotation.z = q_rot.z();
  target_state->target.transform.rotation.w = q_rot.w();

  target_state->target.header.frame_id = corrdinate_system_string;
  target_state->target.child_frame_id = frame_id;
  target_state->target.header.stamp = timestamp;

  fresh_data = true;
}

int main(int argc, char* argv[]) {
  ros::init(argc, argv, "ros_vrpn_client");
  ros::NodeHandle nh("~");

  target_state = new TargetState;
  //frame_id = nh.getNamespace().substr(1);
  frame_id = nh.getNamespace();

  std::string vrpn_server_ip;
  int vrpn_port;
  std::string tracked_object_name;

  nh.param<std::string>("vrpn_server_ip", vrpn_server_ip, std::string());
  nh.param<int>("vrpn_port", vrpn_port, 3883);
  nh.param<std::string>("vrpn_coordinate_system", corrdinate_system_string, "vicon");

  std::cout << "vrpn_server_ip:" << vrpn_server_ip << std::endl;
  std::cout << "vrpn_port:" << vrpn_port << std::endl;
  std::cout << "vrpn_coordinate_system:" << corrdinate_system_string << std::endl;

  if (corrdinate_system_string == std::string("vicon")) {

  } else if (corrdinate_system_string == std::string("optitrack")) {

  } else {
    ROS_FATAL("ROS param vrpn_coordinate_system should be either 'vicon' or 'optitrack'!");
  }

  Rigid_Body tool(nh, vrpn_server_ip, vrpn_port);

  ros::Rate loop_rate(1000);  //TODO(gohlp): fix this

  while (ros::ok()) {
    tool.step_vrpn();
    //vrpn_SleepMsecs(10);
    if (fresh_data == true) {  // only publish when receive data over VRPN.
      tool.publish_target_state(target_state);
      fresh_data = false;
    }
    //ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}

<commit_msg>added timezone adjustments and warning if delays are too big<commit_after>/*
 # Copyright (c) 2011, Georgia Tech Research Corporation
 # 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 Georgia Tech Research Corporation 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 GEORGIA TECH RESEARCH CORPORATION ''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 GEORGIA TECH 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.
 #

 ## author Advait Jain (Healthcare Robotics Lab, Georgia Tech.)
 ## author Chih-Hung Aaron King (Healthcare Robotics Lab, Georgia Tech.)
 */

//== This application listens for a rigid body named 'Tracker' on a remote machine
//== and publishes & tf it's position and orientation through ROS.

#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <geometry_msgs/TransformStamped.h>
#include <stdio.h>
#include <math.h>

#include <vrpn_Connection.h>
#include <vrpn_Tracker.h>

#include <LinearMath/btQuaternion.h>

void VRPN_CALLBACK track_target(void *, const vrpn_TRACKERCB t);

class TargetState {
 public:
  geometry_msgs::TransformStamped target;
};

TargetState *target_state;
std::string frame_id;
std::string corrdinate_system_string;

enum CoordinateSystem {
  vicon,
  optitrack
} corrdinate_system;

// set to true in the VRPN callback function.
bool fresh_data = false;
vrpn_TRACKERCB prev_vrpn_data;

class Rigid_Body {
 private:
  ros::Publisher target_pub;
  tf::TransformBroadcaster br;
  vrpn_Connection *connection;
  vrpn_Tracker_Remote *tracker;

 public:
  Rigid_Body(ros::NodeHandle& nh, std::string server_ip, int port) {
    target_pub = nh.advertise<geometry_msgs::TransformStamped>("pose", 100);
    std::string connec_nm = server_ip + ":" + boost::lexical_cast<std::string>(port);
    connection = vrpn_get_connection_by_name(connec_nm.c_str());
    std::string target_name = nh.getNamespace().substr(1);
    tracker = new vrpn_Tracker_Remote(target_name.c_str(), connection);
    this->tracker->register_change_handler(NULL, track_target);
  }

  void publish_target_state(TargetState *target_state) {
    br.sendTransform(target_state->target);
    target_pub.publish(target_state->target);
  }

  void step_vrpn() {
    this->tracker->mainloop();
    this->connection->mainloop();
  }
};

//== Tracker Position/Orientation Callback ==--
void VRPN_CALLBACK track_target(void *, const vrpn_TRACKERCB t) {
  btQuaternion q_orig(t.quat[0], t.quat[1], t.quat[2], t.quat[3]);
  btQuaternion q_fix(0.70710678, 0., 0., 0.70710678);

  btQuaternion q_rot;
  btVector3 pos;
  switch (corrdinate_system) {
    case optitrack: {
      // optitrak <-- funky <-- object
      // the q_fix.inverse() esures that when optitrak_funky says 0 0 0
      // for roll pitch yaw, there is still a rotation that aligns the
      // object frame with the /optitrak frame (and not /optitrak_funky)
      q_rot = q_fix * q_orig * q_fix.inverse();
      pos = btVector3(t.pos[0], -t.pos[2], t.pos[1]);
      break;
    }
    case vicon: {
      q_rot = q_orig;
      pos = btVector3(t.pos[0], t.pos[1], t.pos[2]);
      break;
    }
    default: {
      ROS_FATAL("Coordinate system not defined!");
      break;
    }
  }

  // verifying that each callback indeed gives fresh data.
  if (prev_vrpn_data.quat[0] == t.quat[0] and prev_vrpn_data.quat[1] == t.quat[1]
      and prev_vrpn_data.quat[2] == t.quat[2] and prev_vrpn_data.quat[3] == t.quat[3]
      and prev_vrpn_data.pos[0] == t.pos[0] and prev_vrpn_data.pos[1] == t.pos[1]
      and prev_vrpn_data.pos[2] == t.pos[2])
    ROS_WARN("Repeated Values");

  prev_vrpn_data = t;

  const uint32_t kMicroSecToNanoSec = 1000;

  // Somehow the vrpn msgs are in a different time zone.
  ros::Time timestamp_local = ros::Time::now();
  int32_t timediff_sec = std::round(double(timestamp_local.sec - t.msg_time.tv_sec) / 3600) * 3600;

  uint32_t timestamp_nsec = t.msg_time.tv_usec * kMicroSecToNanoSec;
  ros::Time timestamp = ros::Time(t.msg_time.tv_sec + timediff_sec, timestamp_nsec);

  ros::Duration time_diff = ros::Time::now() - timestamp;
  if(std::abs(time_diff.toSec()) > 0.1) {
    ROS_WARN_STREAM_THROTTLE(1, "Time delay: " << time_diff);
  }

  target_state->target.transform.translation.x = pos.x();
  target_state->target.transform.translation.y = pos.y();
  target_state->target.transform.translation.z = pos.z();

  target_state->target.transform.rotation.x = q_rot.x();
  target_state->target.transform.rotation.y = q_rot.y();
  target_state->target.transform.rotation.z = q_rot.z();
  target_state->target.transform.rotation.w = q_rot.w();

  target_state->target.header.frame_id = corrdinate_system_string;
  target_state->target.child_frame_id = frame_id;
  target_state->target.header.stamp = timestamp;

  fresh_data = true;
}

int main(int argc, char* argv[]) {
  ros::init(argc, argv, "ros_vrpn_client");
  ros::NodeHandle nh("~");

  target_state = new TargetState;
  //frame_id = nh.getNamespace().substr(1);
  frame_id = nh.getNamespace();

  std::string vrpn_server_ip;
  int vrpn_port;
  std::string tracked_object_name;

  nh.param<std::string>("vrpn_server_ip", vrpn_server_ip, std::string());
  nh.param<int>("vrpn_port", vrpn_port, 3883);
  nh.param<std::string>("vrpn_coordinate_system", corrdinate_system_string, "vicon");

  std::cout << "vrpn_server_ip:" << vrpn_server_ip << std::endl;
  std::cout << "vrpn_port:" << vrpn_port << std::endl;
  std::cout << "vrpn_coordinate_system:" << corrdinate_system_string << std::endl;

  if (corrdinate_system_string == std::string("vicon")) {

  } else if (corrdinate_system_string == std::string("optitrack")) {

  } else {
    ROS_FATAL("ROS param vrpn_coordinate_system should be either 'vicon' or 'optitrack'!");
  }

  Rigid_Body tool(nh, vrpn_server_ip, vrpn_port);

  ros::Rate loop_rate(1000);  //TODO(gohlp): fix this

  while (ros::ok()) {
    tool.step_vrpn();
    //vrpn_SleepMsecs(10);
    if (fresh_data == true) {  // only publish when receive data over VRPN.
      tool.publish_target_state(target_state);
      fresh_data = false;
    }
    //ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}

<|endoftext|>