Remove UPNP support & do some cleanup.
[novacoin.git] / src / qt / optionsdialog.cpp
index 0eeb6f8..79e8260 100644 (file)
 #include "optionsdialog.h"
-#include "optionsmodel.h"
-#include "bitcoinamountfield.h"
-#include "monitoreddatamapper.h"
-#include "guiutil.h"
-#include "bitcoinunits.h"
-#include "qvaluecombobox.h"
+#include "ui_optionsdialog.h"
 
-#include <QHBoxLayout>
-#include <QVBoxLayout>
-#include <QPushButton>
-#include <QListWidget>
-#include <QStackedWidget>
+#include "bitcoinunits.h"
+#include "monitoreddatamapper.h"
+#include "netbase.h"
+#include "optionsmodel.h"
+#include "dialogwindowflags.h"
 
-#include <QCheckBox>
-#include <QLabel>
-#include <QLineEdit>
+#include <QDir>
 #include <QIntValidator>
-#include <QDoubleValidator>
+#include <QLocale>
+#include <QMessageBox>
+#include <QRegExp>
 #include <QRegExpValidator>
-#include <QDialogButtonBox>
-
-/* First page of options */
-class MainOptionsPage : public QWidget
+#include <QKeyEvent>
+
+OptionsDialog::OptionsDialog(QWidget *parent) :
+    QWidget(parent, DIALOGWINDOWHINTS),
+    ui(new Ui::OptionsDialog),
+    model(0),
+    mapper(0),
+    fRestartWarningDisplayed_Proxy(false),
+    fRestartWarningDisplayed_Tor(false),
+    fRestartWarningDisplayed_Lang(false),
+    fRestartWarningDisplayed_URL(false),
+    fProxyIpValid(true),
+    fTorIpValid(true)
 {
-public:
-    explicit MainOptionsPage(QWidget *parent=0);
-
-    void setMapper(MonitoredDataMapper *mapper);
-private:
-    QCheckBox *bitcoin_at_startup;
-    QCheckBox *minimize_to_tray;
-    QCheckBox *map_port_upnp;
-    QCheckBox *minimize_on_close;
-    QCheckBox *connect_socks4;
-    QLineEdit *proxy_ip;
-    QLineEdit *proxy_port;
-    BitcoinAmountField *fee_edit;
-
-signals:
+    ui->setupUi(this);
+
+    /* Network elements init */
+    ui->proxyIp->setEnabled(false);
+    ui->proxyPort->setEnabled(false);
+    ui->proxyPort->setValidator(new QIntValidator(1, 65535, this));
+
+    ui->torIp->setEnabled(false);
+    ui->torPort->setEnabled(false);
+    ui->torPort->setValidator(new QIntValidator(1, 65535, this));
+    ui->TorOnly->setEnabled(false);
+    ui->torName->setEnabled(false);
+
+    ui->socksVersion->setEnabled(false);
+    ui->socksVersion->addItem("5", 5);
+    ui->socksVersion->addItem("4", 4);
+    ui->socksVersion->setCurrentIndex(0);
+
+    connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool)));
+    connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool)));
+    connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->socksVersion, SLOT(setEnabled(bool)));
+    connect(ui->connectSocks, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning_Proxy()));
+
+    connect(ui->connectTor, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning_Tor()));
+    connect(ui->connectTor, SIGNAL(toggled(bool)), ui->torIp, SLOT(setEnabled(bool)));
+    connect(ui->connectTor, SIGNAL(toggled(bool)), ui->torPort, SLOT(setEnabled(bool)));
+    connect(ui->connectTor, SIGNAL(toggled(bool)), ui->TorOnly, SLOT(setEnabled(bool)));
+    connect(ui->connectTor, SIGNAL(toggled(bool)), ui->torName, SLOT(setEnabled(bool)));
+    connect(ui->TorOnly, SIGNAL(toggled(bool)), ui->connectSocks, SLOT(setDisabled(bool)));
+
+    ui->proxyIp->installEventFilter(this);
+    ui->torIp->installEventFilter(this);
+
+    /* Window elements init */
+#ifdef Q_OS_MAC
+    ui->tabWindow->setVisible(false);
+#endif
 
-public slots:
+    /* Display elements init */
+    QDir translations(":translations");
+    ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant(""));
+    foreach(const QString &langStr, translations.entryList())
+    {
+        QLocale locale(langStr);
+
+        /** check if the locale name consists of 2 parts (language_country) */
+        if(langStr.contains("_"))
+        {
+#if QT_VERSION >= 0x040800
+            /** display language strings as "native language - native country (locale name)", e.g. "Deutsch - Deutschland (de)" */
+            ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
+#else
+            /** display language strings as "language - country (locale name)", e.g. "German - Germany (de)" */
+            ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" - ") + QLocale::countryToString(locale.country()) + QString(" (") + langStr + QString(")"), QVariant(langStr));
+#endif
+        }
+        else
+        {
+#if QT_VERSION >= 0x040800
+            /** display language strings as "native language (locale name)", e.g. "Deutsch (de)" */
+            ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
+#else
+            /** display language strings as "language (locale name)", e.g. "German (de)" */
+            ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" (") + langStr + QString(")"), QVariant(langStr));
+#endif
+        }
+    }
 
-};
+#if QT_VERSION >= 0x040700
+    ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s");
+#endif
 
-class DisplayOptionsPage : public QWidget
-{
-public:
-    explicit DisplayOptionsPage(QWidget *parent=0);
 
-    void setMapper(MonitoredDataMapper *mapper);
-private:
-    QValueComboBox *unit;
-    QCheckBox *display_addresses;
-signals:
+    ui->unit->setModel(new BitcoinUnits(this));
 
-public slots:
+    /* Widget-to-option mapper */
+    mapper = new MonitoredDataMapper(this);
+    mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
+    mapper->setOrientation(Qt::Vertical);
 
-};
+    /* enable apply button when data modified */
+    connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApplyButton()));
+    /* disable apply button when new data loaded */
+    connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApplyButton()));
+    /* setup/change UI elements when proxy IP is invalid/valid */
+    connect(this, SIGNAL(proxyIpValid(QValidatedLineEdit *, bool)), this, SLOT(handleProxyIpValid(QValidatedLineEdit *, bool)));
+    /* setup/change UI elements when Tor IP is invalid/valid */
+    connect(this, SIGNAL(torIpValid(QValidatedLineEdit *, bool)), this, SLOT(handleTorIpValid(QValidatedLineEdit *, bool)));
+}
 
-OptionsDialog::OptionsDialog(QWidget *parent):
-    QDialog(parent), contents_widget(0), pages_widget(0),
-    model(0), main_page(0), display_page(0)
+OptionsDialog::~OptionsDialog()
 {
-    contents_widget = new QListWidget();
-    contents_widget->setMaximumWidth(128);
-
-    pages_widget = new QStackedWidget();
-    pages_widget->setMinimumWidth(300);
-
-    QListWidgetItem *item_main = new QListWidgetItem(tr("Main"));
-    contents_widget->addItem(item_main);
-    main_page = new MainOptionsPage(this);
-    pages_widget->addWidget(main_page);
+    delete ui;
+}
 
-    QListWidgetItem *item_display = new QListWidgetItem(tr("Display"));
-    contents_widget->addItem(item_display);
-    display_page = new DisplayOptionsPage(this);
-    pages_widget->addWidget(display_page);
+void OptionsDialog::setModel(OptionsModel *model)
+{
+    this->model = model;
 
-    contents_widget->setCurrentRow(0);
+    if(model)
+    {
+        connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
 
-    QHBoxLayout *main_layout = new QHBoxLayout();
-    main_layout->addWidget(contents_widget);
-    main_layout->addWidget(pages_widget, 1);
+        mapper->setModel(model);
+        setMapper();
+        mapper->toFirst();
+    }
 
-    QVBoxLayout *layout = new QVBoxLayout();
-    layout->addLayout(main_layout);
+    /* update the display unit, to not use the default ("BTC") */
+    updateDisplayUnit();
 
-    QDialogButtonBox *buttonbox = new QDialogButtonBox();
-    buttonbox->setStandardButtons(QDialogButtonBox::Apply|QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
-    apply_button = buttonbox->button(QDialogButtonBox::Apply);
-    layout->addWidget(buttonbox);
+    /* warn only when language selection changes by user action (placed here so init via mapper doesn't trigger this) */
+    connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning_Lang()));
+    connect(ui->thirdPartyTxUrls, SIGNAL(textChanged(const QString &)), this, SLOT(showRestartWarning_URL()));
 
-    setLayout(layout);
-    setWindowTitle(tr("Options"));
+    /* disable apply button after settings are loaded as there is nothing to save */
+    disableApplyButton();
+}
 
-    /* Widget-to-option mapper */
-    mapper = new MonitoredDataMapper(this);
-    mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
-    mapper->setOrientation(Qt::Vertical);
-    /* enable apply button when data modified */
-    connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApply()));
-    /* disable apply button when new data loaded */
-    connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply()));
+void OptionsDialog::setMapper()
+{
+    /* Main */
+    mapper->addMapping(ui->transactionFee, OptionsModel::Fee);
+    mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup);
+    mapper->addMapping(ui->detachDatabases, OptionsModel::DetachDatabases);
+
+    /* Network */
+    mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
+    mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
+    mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort);
+    mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion);
+
+    mapper->addMapping(ui->connectTor, OptionsModel::TorUse);
+    mapper->addMapping(ui->torIp, OptionsModel::TorIP);
+    mapper->addMapping(ui->torPort, OptionsModel::TorPort);
+    mapper->addMapping(ui->TorOnly, OptionsModel::TorOnly);
+    mapper->addMapping(ui->torName, OptionsModel::TorName);
+
+
+    /* Window */
+#ifndef Q_OS_MAC
+    mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
+    mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose);
+#endif
 
-    /* Event bindings */
-    connect(contents_widget, SIGNAL(currentRowChanged(int)), this, SLOT(changePage(int)));
-    connect(buttonbox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked()));
-    connect(buttonbox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked()));
-    connect(buttonbox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyClicked()));
+    /* Display */
+    mapper->addMapping(ui->lang, OptionsModel::Language);
+    mapper->addMapping(ui->unit, OptionsModel::DisplayUnit);
+    mapper->addMapping(ui->displayAddresses, OptionsModel::DisplayAddresses);
+    mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
+    mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls);
 }
 
-void OptionsDialog::setModel(OptionsModel *model)
+void OptionsDialog::enableApplyButton()
 {
-    this->model = model;
-
-    mapper->setModel(model);
-    main_page->setMapper(mapper);
-    display_page->setMapper(mapper);
+    ui->applyButton->setEnabled(true);
+}
 
-    mapper->toFirst();
+void OptionsDialog::disableApplyButton()
+{
+    ui->applyButton->setEnabled(false);
 }
 
-void OptionsDialog::changePage(int index)
+void OptionsDialog::enableSaveButtons()
 {
-    pages_widget->setCurrentIndex(index);
+    /* prevent enabling of the save buttons when data modified, if there is an invalid proxy address present */
+    if(fProxyIpValid && fTorIpValid)
+        setSaveButtonState(true);
 }
 
-void OptionsDialog::okClicked()
+void OptionsDialog::disableSaveButtons()
 {
-    mapper->submit();
-    accept();
+    setSaveButtonState(false);
 }
 
-void OptionsDialog::cancelClicked()
+void OptionsDialog::setSaveButtonState(bool fState)
 {
-    reject();
+    ui->applyButton->setEnabled(fState);
+    ui->okButton->setEnabled(fState);
 }
 
-void OptionsDialog::applyClicked()
+void OptionsDialog::on_okButton_clicked()
 {
     mapper->submit();
-    apply_button->setEnabled(false);
+//    accept();
+    close();
 }
 
-void OptionsDialog::enableApply()
+void OptionsDialog::on_cancelButton_clicked()
 {
-    apply_button->setEnabled(true);
+//    reject();
+    close();
 }
 
-void OptionsDialog::disableApply()
+void OptionsDialog::on_applyButton_clicked()
 {
-    apply_button->setEnabled(false);
+    mapper->submit();
+    disableApplyButton();
 }
 
-MainOptionsPage::MainOptionsPage(QWidget *parent):
-        QWidget(parent)
+void OptionsDialog::showRestartWarning_Proxy()
 {
-    QVBoxLayout *layout = new QVBoxLayout();
-
-    bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup"));
-    bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on"));
-    layout->addWidget(bitcoin_at_startup);
-
-    minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar"));
-    minimize_to_tray->setToolTip(tr("Show only a tray icon after minimizing the window"));
-    layout->addWidget(minimize_to_tray);
-
-    map_port_upnp = new QCheckBox(tr("Map port using &UPnP"));
-    map_port_upnp->setToolTip(tr("Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled."));
-    layout->addWidget(map_port_upnp);
-
-    minimize_on_close = new QCheckBox(tr("M&inimize on close"));
-    minimize_on_close->setToolTip(tr("Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu."));
-    layout->addWidget(minimize_on_close);
-
-    connect_socks4 = new QCheckBox(tr("&Connect through SOCKS4 proxy:"));
-    connect_socks4->setToolTip(tr("Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor)"));
-    layout->addWidget(connect_socks4);
-
-    QHBoxLayout *proxy_hbox = new QHBoxLayout();
-    proxy_hbox->addSpacing(18);
-    QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: "));
-    proxy_hbox->addWidget(proxy_ip_label);
-    proxy_ip = new QLineEdit();
-    proxy_ip->setMaximumWidth(140);
-    proxy_ip->setEnabled(false);
-    proxy_ip->setValidator(new QRegExpValidator(QRegExp("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"), this));
-    proxy_ip->setToolTip(tr("IP address of the proxy (e.g. 127.0.0.1)"));
-    proxy_ip_label->setBuddy(proxy_ip);
-    proxy_hbox->addWidget(proxy_ip);
-    QLabel *proxy_port_label = new QLabel(tr("&Port: "));
-    proxy_hbox->addWidget(proxy_port_label);
-    proxy_port = new QLineEdit();
-    proxy_port->setMaximumWidth(55);
-    proxy_port->setValidator(new QIntValidator(0, 65535, this));
-    proxy_port->setEnabled(false);
-    proxy_port->setToolTip(tr("Port of the proxy (e.g. 1234)"));
-    proxy_port_label->setBuddy(proxy_port);
-    proxy_hbox->addWidget(proxy_port);
-    proxy_hbox->addStretch(1);
-
-    layout->addLayout(proxy_hbox);
-    QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly.  Most transactions are 1KB.  Fee 0.01 recommended."));
-    fee_help->setWordWrap(true);
-    layout->addWidget(fee_help);
-
-    QHBoxLayout *fee_hbox = new QHBoxLayout();
-    fee_hbox->addSpacing(18);
-    QLabel *fee_label = new QLabel(tr("Pay transaction &fee"));
-    fee_hbox->addWidget(fee_label);
-    fee_edit = new BitcoinAmountField();
-    fee_edit->setToolTip(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended."));
-
-    fee_label->setBuddy(fee_edit);
-    fee_hbox->addWidget(fee_edit);
-    fee_hbox->addStretch(1);
-
-    layout->addLayout(fee_hbox);
-
-    layout->addStretch(1); // Extra space at bottom
-
-    setLayout(layout);
-
-    connect(connect_socks4, SIGNAL(toggled(bool)), proxy_ip, SLOT(setEnabled(bool)));
-    connect(connect_socks4, SIGNAL(toggled(bool)), proxy_port, SLOT(setEnabled(bool)));
-
-#ifndef USE_UPNP
-    map_port_upnp->setDisabled(true);
-#endif
+    if(!fRestartWarningDisplayed_Proxy)
+    {
+        QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting NovaCoin."), QMessageBox::Ok);
+        fRestartWarningDisplayed_Proxy = true;
+    }
 }
 
-void MainOptionsPage::setMapper(MonitoredDataMapper *mapper)
+void OptionsDialog::showRestartWarning_Tor()
 {
-    // Map model to widgets
-    mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup);
-    mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray);
-    mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP);
-    mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose);
-    mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4);
-    mapper->addMapping(proxy_ip, OptionsModel::ProxyIP);
-    mapper->addMapping(proxy_port, OptionsModel::ProxyPort);
-    mapper->addMapping(fee_edit, OptionsModel::Fee);
+    if(!fRestartWarningDisplayed_Proxy)
+    {
+        QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting NovaCoin."), QMessageBox::Ok);
+        fRestartWarningDisplayed_Tor = true;
+    }
 }
 
-DisplayOptionsPage::DisplayOptionsPage(QWidget *parent):
-        QWidget(parent)
+void OptionsDialog::showRestartWarning_Lang()
 {
-    QVBoxLayout *layout = new QVBoxLayout();
-
-    QHBoxLayout *unit_hbox = new QHBoxLayout();
-    unit_hbox->addSpacing(18);
-    QLabel *unit_label = new QLabel(tr("&Unit to show amounts in: "));
-    unit_hbox->addWidget(unit_label);
-    unit = new QValueComboBox(this);
-    unit->setModel(new BitcoinUnits(this));
-    unit->setToolTip(tr("Choose the default subdivision unit to show in the interface, and when sending coins"));
+    if(!fRestartWarningDisplayed_Lang)
+    {
+        QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting NovaCoin."), QMessageBox::Ok);
+        fRestartWarningDisplayed_Lang = true;
+    }
+}
 
-    unit_label->setBuddy(unit);
-    unit_hbox->addWidget(unit);
+void OptionsDialog::showRestartWarning_URL()
+{
+    if(!fRestartWarningDisplayed_URL)
+    {
+        QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting NovaCoin."), QMessageBox::Ok);
+        fRestartWarningDisplayed_URL = true;
+    }
+}
 
-    layout->addLayout(unit_hbox);
 
-    display_addresses = new QCheckBox(tr("Display addresses in transaction list"), this);
-    layout->addWidget(display_addresses);
+void OptionsDialog::updateDisplayUnit()
+{
+    if(model)
+    {
+        /* Update transactionFee with the current unit */
+        ui->transactionFee->setDisplayUnit(model->getDisplayUnit());
+    }
+}
 
-    layout->addStretch();
+void OptionsDialog::handleProxyIpValid(QValidatedLineEdit *object, bool fState)
+{
+    // this is used in a check before re-enabling the save buttons
+    fProxyIpValid = fState;
+
+    if(fProxyIpValid)
+    {
+        enableSaveButtons();
+        ui->statusLabel->clear();
+    }
+    else
+    {
+        disableSaveButtons();
+        object->setValid(fProxyIpValid);
+        ui->statusLabel->setStyleSheet("QLabel { color: red; }");
+        ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
+    }
+}
 
-    setLayout(layout);
+void OptionsDialog::handleTorIpValid(QValidatedLineEdit *object, bool fState)
+{
+    // this is used in a check before re-enabling the save buttons
+    fTorIpValid = fState;
+
+    if(fTorIpValid)
+    {
+        enableSaveButtons();
+        ui->statusLabel->clear();
+    }
+    else
+    {
+        disableSaveButtons();
+        object->setValid(fTorIpValid);
+        ui->statusLabel->setStyleSheet("QLabel { color: red; }");
+        ui->statusLabel->setText(tr("The supplied tor address is invalid."));
+    }
 }
 
-void DisplayOptionsPage::setMapper(MonitoredDataMapper *mapper)
+bool OptionsDialog::eventFilter(QObject *object, QEvent *event)
 {
-    mapper->addMapping(unit, OptionsModel::DisplayUnit);
-    mapper->addMapping(display_addresses, OptionsModel::DisplayAddresses);
+    if(event->type() == QEvent::FocusOut)
+    {
+        if(object == ui->proxyIp)
+        {
+            CService addr;
+            /* Check proxyIp for a valid IPv4/IPv6 address and emit the proxyIpValid signal */
+            emit proxyIpValid(ui->proxyIp, LookupNumeric(ui->proxyIp->text().toStdString().c_str(), addr));
+        }
+
+        if(object == ui->torIp)
+        {
+            CService addr;
+            /* Check proxyIp for a valid IPv4/IPv6 address and emit the torIpValid signal */
+            emit torIpValid(ui->torIp, LookupNumeric(ui->torIp->text().toStdString().c_str(), addr));
+        }
+    }
+    return QWidget::eventFilter(object, event);
 }
+
+void OptionsDialog::keyPressEvent(QKeyEvent *event)
+{
+#ifdef ANDROID
+    if(windowType() != Qt::Widget && event->key() == Qt::Key_Back)
+    {
+        close();
+    }
+#else
+    if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape)
+    {
+        close();
+    }
+#endif
+}
\ No newline at end of file