Improved Mac experience; QDoubleSpinBox for BitcoinAmountField
Now it can't be told if this is was a Windows App before. All Mac design principles are fulfilled and some cosmetics have been applied to suit the native look and feel. The biggest change there is the proper use of the Dock icon which takes the role of the Tray icon on Mac. The QDoubleSpinBox improves entering of Bitcoin amounts, no two separate fields are required anymore. All functionality and validation effects have been retained; pressing the comma key will be internally translated to a period to keep it consistent throughout the application and eases entering in countries which use the comma as decimal separator. Additionally, Notificator now supports Growl, Mac's native notification system. This is provided via Apple Script in order to avoid linking to Growl on compile time. Other changes involve encapsulation of Toolbar and Menubar creation, loading of Qt's own translation and some clean up.
This commit is contained in:
parent
d934e7e3dd
commit
527137e3ee
18 changed files with 435 additions and 112 deletions
|
@ -221,6 +221,9 @@ windows:LIBS += -lws2_32 -lgdi32
|
||||||
windows:DEFINES += WIN32
|
windows:DEFINES += WIN32
|
||||||
windows:RC_FILE = src/qt/res/bitcoin-qt.rc
|
windows:RC_FILE = src/qt/res/bitcoin-qt.rc
|
||||||
|
|
||||||
|
macx:HEADERS += src/qt/macdockiconhandler.h
|
||||||
|
macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm
|
||||||
|
macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit
|
||||||
macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3
|
macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3
|
||||||
macx:ICON = src/qt/res/icons/bitcoin.icns
|
macx:ICON = src/qt/res/icons/bitcoin.icns
|
||||||
macx:TARGET = "Bitcoin Qt"
|
macx:TARGET = "Bitcoin Qt"
|
||||||
|
|
|
@ -18,6 +18,13 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
|
||||||
tab(tab)
|
tab(tab)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
#ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
|
||||||
|
ui->newAddressButton->setIcon(QIcon());
|
||||||
|
ui->copyToClipboard->setIcon(QIcon());
|
||||||
|
ui->deleteButton->setIcon(QIcon());
|
||||||
|
#endif
|
||||||
|
|
||||||
switch(mode)
|
switch(mode)
|
||||||
{
|
{
|
||||||
case ForSending:
|
case ForSending:
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
#include <QSplashScreen>
|
#include <QSplashScreen>
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
|
||||||
// Need a global reference for the notifications to find the GUI
|
// Need a global reference for the notifications to find the GUI
|
||||||
BitcoinGUI *guiref;
|
BitcoinGUI *guiref;
|
||||||
|
@ -119,11 +120,17 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
// Load language file for system locale
|
// Load language file for system locale
|
||||||
QString locale = QLocale::system().name();
|
QString locale = QLocale::system().name();
|
||||||
|
QTranslator qtTranslator;
|
||||||
|
qtTranslator.load(QLibraryInfo::location(QLibraryInfo::TranslationsPath) + "/qt_" + locale);
|
||||||
|
if (!qtTranslator.isEmpty())
|
||||||
|
app.installTranslator(&qtTranslator);
|
||||||
QTranslator translator;
|
QTranslator translator;
|
||||||
translator.load(":/translations/"+locale);
|
translator.load(":/translations/"+locale);
|
||||||
if (!translator.isEmpty())
|
if (!translator.isEmpty())
|
||||||
app.installTranslator(&translator);
|
app.installTranslator(&translator);
|
||||||
|
|
||||||
|
app.setApplicationName(QApplication::translate("main", "Bitcoin Qt"));
|
||||||
|
|
||||||
QSplashScreen splash(QPixmap(":/images/splash"), 0);
|
QSplashScreen splash(QPixmap(":/images/splash"), 0);
|
||||||
splash.show();
|
splash.show();
|
||||||
splash.setAutoFillBackground(true);
|
splash.setAutoFillBackground(true);
|
||||||
|
|
|
@ -1,33 +1,30 @@
|
||||||
#include "bitcoinamountfield.h"
|
#include "bitcoinamountfield.h"
|
||||||
#include "qvalidatedlineedit.h"
|
|
||||||
#include "qvaluecombobox.h"
|
#include "qvaluecombobox.h"
|
||||||
#include "bitcoinunits.h"
|
#include "bitcoinunits.h"
|
||||||
|
|
||||||
|
#include "guiconstants.h"
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QRegExpValidator>
|
#include <QRegExpValidator>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
#include <QDoubleSpinBox>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <qmath.h>
|
||||||
|
|
||||||
BitcoinAmountField::BitcoinAmountField(QWidget *parent):
|
BitcoinAmountField::BitcoinAmountField(QWidget *parent):
|
||||||
QWidget(parent), amount(0), decimals(0), currentUnit(-1)
|
QWidget(parent), amount(0), currentUnit(-1)
|
||||||
{
|
{
|
||||||
amount = new QValidatedLineEdit(this);
|
amount = new QDoubleSpinBox(this);
|
||||||
amount->setValidator(new QRegExpValidator(QRegExp("[0-9]*"), this));
|
amount->setLocale(QLocale::c());
|
||||||
amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
|
amount->setDecimals(8);
|
||||||
amount->installEventFilter(this);
|
amount->installEventFilter(this);
|
||||||
amount->setMaximumWidth(75);
|
amount->setMaximumWidth(170);
|
||||||
decimals = new QValidatedLineEdit(this);
|
|
||||||
decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this));
|
|
||||||
decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
|
|
||||||
decimals->setMaximumWidth(75);
|
|
||||||
|
|
||||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||||
layout->setSpacing(0);
|
|
||||||
layout->addWidget(amount);
|
layout->addWidget(amount);
|
||||||
layout->addWidget(new QLabel(QString("<b>.</b>")));
|
|
||||||
layout->addWidget(decimals);
|
|
||||||
unit = new QValueComboBox(this);
|
unit = new QValueComboBox(this);
|
||||||
unit->setModel(new BitcoinUnits(this));
|
unit->setModel(new BitcoinUnits(this));
|
||||||
layout->addWidget(unit);
|
layout->addWidget(unit);
|
||||||
|
@ -40,8 +37,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent):
|
||||||
setFocusProxy(amount);
|
setFocusProxy(amount);
|
||||||
|
|
||||||
// If one if the widgets changes, the combined content changes as well
|
// If one if the widgets changes, the combined content changes as well
|
||||||
connect(amount, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged()));
|
connect(amount, SIGNAL(valueChanged(QString)), this, SIGNAL(textChanged()));
|
||||||
connect(decimals, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged()));
|
|
||||||
connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
|
connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
|
||||||
|
|
||||||
// Set default based on configuration
|
// Set default based on configuration
|
||||||
|
@ -50,79 +46,72 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent):
|
||||||
|
|
||||||
void BitcoinAmountField::setText(const QString &text)
|
void BitcoinAmountField::setText(const QString &text)
|
||||||
{
|
{
|
||||||
const QStringList parts = text.split(QString("."));
|
if (text.isEmpty())
|
||||||
if(parts.size() == 2)
|
amount->clear();
|
||||||
{
|
|
||||||
amount->setText(parts[0]);
|
|
||||||
decimals->setText(parts[1]);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
amount->setValue(text.toDouble());
|
||||||
amount->setText(QString());
|
|
||||||
decimals->setText(QString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitcoinAmountField::clear()
|
void BitcoinAmountField::clear()
|
||||||
{
|
{
|
||||||
amount->clear();
|
amount->clear();
|
||||||
decimals->clear();
|
|
||||||
unit->setCurrentIndex(0);
|
unit->setCurrentIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BitcoinAmountField::validate()
|
bool BitcoinAmountField::validate()
|
||||||
{
|
{
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
if(decimals->text().isEmpty())
|
if (amount->value() == 0.0)
|
||||||
{
|
|
||||||
decimals->setValid(false);
|
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
if (valid && !BitcoinUnits::parse(currentUnit, text(), 0))
|
||||||
if(!BitcoinUnits::parse(currentUnit, text(), 0))
|
|
||||||
{
|
|
||||||
setValid(false);
|
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
|
||||||
|
setValid(valid);
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitcoinAmountField::setValid(bool valid)
|
void BitcoinAmountField::setValid(bool valid)
|
||||||
{
|
{
|
||||||
amount->setValid(valid);
|
if (valid)
|
||||||
decimals->setValid(valid);
|
amount->setStyleSheet("");
|
||||||
|
else
|
||||||
|
amount->setStyleSheet(STYLE_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BitcoinAmountField::text() const
|
QString BitcoinAmountField::text() const
|
||||||
{
|
{
|
||||||
if(decimals->text().isEmpty() && amount->text().isEmpty())
|
if (amount->text().isEmpty())
|
||||||
{
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
else
|
||||||
return amount->text() + QString(".") + decimals->text();
|
return amount->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intercept '.' and ',' keys, if pressed focus a specified widget
|
|
||||||
bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
|
bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
|
||||||
{
|
{
|
||||||
Q_UNUSED(object);
|
if (event->type() == QEvent::FocusIn)
|
||||||
if(event->type() == QEvent::KeyPress)
|
{
|
||||||
|
// Clear invalid flag on focus
|
||||||
|
setValid(true);
|
||||||
|
}
|
||||||
|
else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
|
||||||
{
|
{
|
||||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||||
if(keyEvent->key() == Qt::Key_Period || keyEvent->key() == Qt::Key_Comma)
|
if (keyEvent->key() == Qt::Key_Comma)
|
||||||
{
|
{
|
||||||
decimals->setFocus();
|
// Translate a comma into a period
|
||||||
decimals->selectAll();
|
QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
|
||||||
|
qApp->sendEvent(object, &periodKeyEvent);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return QWidget::eventFilter(object, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
|
QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
|
||||||
{
|
{
|
||||||
QWidget::setTabOrder(prev, amount);
|
QWidget::setTabOrder(prev, amount);
|
||||||
QWidget::setTabOrder(amount, decimals);
|
return amount;
|
||||||
return decimals;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 BitcoinAmountField::value(bool *valid_out) const
|
qint64 BitcoinAmountField::value(bool *valid_out) const
|
||||||
|
@ -156,8 +145,8 @@ void BitcoinAmountField::unitChanged(int idx)
|
||||||
currentUnit = newUnit;
|
currentUnit = newUnit;
|
||||||
|
|
||||||
// Set max length after retrieving the value, to prevent truncation
|
// Set max length after retrieving the value, to prevent truncation
|
||||||
amount->setMaxLength(BitcoinUnits::amountDigits(currentUnit));
|
amount->setDecimals(BitcoinUnits::decimals(currentUnit));
|
||||||
decimals->setMaxLength(BitcoinUnits::decimals(currentUnit));
|
amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals()));
|
||||||
|
|
||||||
if(valid)
|
if(valid)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QValidatedLineEdit;
|
class QDoubleSpinBox;
|
||||||
class QValueComboBox;
|
class QValueComboBox;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ QT_END_NAMESPACE
|
||||||
class BitcoinAmountField: public QWidget
|
class BitcoinAmountField: public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true);
|
Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true)
|
||||||
public:
|
public:
|
||||||
explicit BitcoinAmountField(QWidget *parent = 0);
|
explicit BitcoinAmountField(QWidget *parent = 0);
|
||||||
|
|
||||||
|
@ -38,12 +38,11 @@ signals:
|
||||||
void textChanged();
|
void textChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Intercept '.' and ',' keys, if pressed focus a specified widget
|
// Intercept focus-in event and ',' keypresses
|
||||||
bool eventFilter(QObject *object, QEvent *event);
|
bool eventFilter(QObject *object, QEvent *event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QValidatedLineEdit *amount;
|
QDoubleSpinBox *amount;
|
||||||
QValidatedLineEdit *decimals;
|
|
||||||
QValueComboBox *unit;
|
QValueComboBox *unit;
|
||||||
int currentUnit;
|
int currentUnit;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
#include "askpassphrasedialog.h"
|
#include "askpassphrasedialog.h"
|
||||||
#include "notificator.h"
|
#include "notificator.h"
|
||||||
|
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
#include "macdockiconhandler.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
|
@ -57,37 +61,26 @@ BitcoinGUI::BitcoinGUI(QWidget *parent):
|
||||||
{
|
{
|
||||||
resize(850, 550);
|
resize(850, 550);
|
||||||
setWindowTitle(tr("Bitcoin Wallet"));
|
setWindowTitle(tr("Bitcoin Wallet"));
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
setWindowIcon(QIcon(":icons/bitcoin"));
|
setWindowIcon(QIcon(":icons/bitcoin"));
|
||||||
|
#else
|
||||||
|
setUnifiedTitleAndToolBarOnMac(true);
|
||||||
|
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
|
||||||
|
#endif
|
||||||
// Accept D&D of URIs
|
// Accept D&D of URIs
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
|
|
||||||
|
// Create actions for the toolbar, menu bar and tray/dock icon
|
||||||
createActions();
|
createActions();
|
||||||
|
|
||||||
// Menus
|
// Create application menu bar
|
||||||
QMenu *file = menuBar()->addMenu(tr("&File"));
|
createMenuBar();
|
||||||
file->addAction(quitAction);
|
|
||||||
|
|
||||||
QMenu *settings = menuBar()->addMenu(tr("&Settings"));
|
// Create the toolbars
|
||||||
settings->addAction(encryptWalletAction);
|
createToolBars();
|
||||||
settings->addAction(changePassphraseAction);
|
|
||||||
settings->addSeparator();
|
|
||||||
settings->addAction(optionsAction);
|
|
||||||
|
|
||||||
QMenu *help = menuBar()->addMenu(tr("&Help"));
|
// Create the tray icon (or setup the dock icon)
|
||||||
help->addAction(aboutAction);
|
createTrayIcon();
|
||||||
|
|
||||||
// Toolbars
|
|
||||||
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
|
|
||||||
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
||||||
toolbar->addAction(overviewAction);
|
|
||||||
toolbar->addAction(sendCoinsAction);
|
|
||||||
toolbar->addAction(receiveCoinsAction);
|
|
||||||
toolbar->addAction(historyAction);
|
|
||||||
toolbar->addAction(addressBookAction);
|
|
||||||
|
|
||||||
QToolBar *toolbar2 = addToolBar(tr("Actions toolbar"));
|
|
||||||
toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
||||||
toolbar2->addAction(exportAction);
|
|
||||||
|
|
||||||
// Create tabs
|
// Create tabs
|
||||||
overviewPage = new OverviewPage();
|
overviewPage = new OverviewPage();
|
||||||
|
@ -146,8 +139,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent):
|
||||||
statusBar()->addWidget(progressBar);
|
statusBar()->addWidget(progressBar);
|
||||||
statusBar()->addPermanentWidget(frameBlocks);
|
statusBar()->addPermanentWidget(frameBlocks);
|
||||||
|
|
||||||
createTrayIcon();
|
|
||||||
|
|
||||||
syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this);
|
syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this);
|
||||||
|
|
||||||
// Clicking on a transaction on the overview page simply sends you to transaction history page
|
// Clicking on a transaction on the overview page simply sends you to transaction history page
|
||||||
|
@ -159,6 +150,13 @@ BitcoinGUI::BitcoinGUI(QWidget *parent):
|
||||||
gotoOverviewPage();
|
gotoOverviewPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BitcoinGUI::~BitcoinGUI()
|
||||||
|
{
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
delete appMenuBar;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void BitcoinGUI::createActions()
|
void BitcoinGUI::createActions()
|
||||||
{
|
{
|
||||||
QActionGroup *tabGroup = new QActionGroup(this);
|
QActionGroup *tabGroup = new QActionGroup(this);
|
||||||
|
@ -197,10 +195,13 @@ void BitcoinGUI::createActions()
|
||||||
quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this);
|
quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this);
|
||||||
quitAction->setToolTip(tr("Quit application"));
|
quitAction->setToolTip(tr("Quit application"));
|
||||||
quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
|
quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
|
||||||
aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this);
|
quitAction->setMenuRole(QAction::QuitRole);
|
||||||
|
aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About %1").arg(qApp->applicationName()), this);
|
||||||
aboutAction->setToolTip(tr("Show information about Bitcoin"));
|
aboutAction->setToolTip(tr("Show information about Bitcoin"));
|
||||||
|
aboutAction->setMenuRole(QAction::AboutQtRole);
|
||||||
optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
|
optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
|
||||||
optionsAction->setToolTip(tr("Modify configuration options for bitcoin"));
|
optionsAction->setToolTip(tr("Modify configuration options for bitcoin"));
|
||||||
|
optionsAction->setMenuRole(QAction::PreferencesRole);
|
||||||
openBitcoinAction = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this);
|
openBitcoinAction = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this);
|
||||||
openBitcoinAction->setToolTip(tr("Show the Bitcoin window"));
|
openBitcoinAction->setToolTip(tr("Show the Bitcoin window"));
|
||||||
exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this);
|
exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this);
|
||||||
|
@ -219,6 +220,45 @@ void BitcoinGUI::createActions()
|
||||||
connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase()));
|
connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BitcoinGUI::createMenuBar()
|
||||||
|
{
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
// Create a decoupled menu bar on Mac which stays even if the window is closed
|
||||||
|
appMenuBar = new QMenuBar();
|
||||||
|
#else
|
||||||
|
// Get the main window's menu bar on other platforms
|
||||||
|
appMenuBar = menuBar();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Configure the menus
|
||||||
|
QMenu *file = appMenuBar->addMenu(tr("&File"));
|
||||||
|
file->addAction(quitAction);
|
||||||
|
|
||||||
|
QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
|
||||||
|
settings->addAction(encryptWalletAction);
|
||||||
|
settings->addAction(changePassphraseAction);
|
||||||
|
settings->addSeparator();
|
||||||
|
settings->addAction(optionsAction);
|
||||||
|
|
||||||
|
QMenu *help = appMenuBar->addMenu(tr("&Help"));
|
||||||
|
help->addAction(aboutAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitcoinGUI::createToolBars()
|
||||||
|
{
|
||||||
|
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
|
||||||
|
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||||
|
toolbar->addAction(overviewAction);
|
||||||
|
toolbar->addAction(sendCoinsAction);
|
||||||
|
toolbar->addAction(receiveCoinsAction);
|
||||||
|
toolbar->addAction(historyAction);
|
||||||
|
toolbar->addAction(addressBookAction);
|
||||||
|
|
||||||
|
QToolBar *toolbar2 = addToolBar(tr("Actions toolbar"));
|
||||||
|
toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||||
|
toolbar2->addAction(exportAction);
|
||||||
|
}
|
||||||
|
|
||||||
void BitcoinGUI::setClientModel(ClientModel *clientModel)
|
void BitcoinGUI::setClientModel(ClientModel *clientModel)
|
||||||
{
|
{
|
||||||
this->clientModel = clientModel;
|
this->clientModel = clientModel;
|
||||||
|
@ -227,7 +267,11 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
|
||||||
{
|
{
|
||||||
QString title_testnet = windowTitle() + QString(" ") + tr("[testnet]");
|
QString title_testnet = windowTitle() + QString(" ") + tr("[testnet]");
|
||||||
setWindowTitle(title_testnet);
|
setWindowTitle(title_testnet);
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
setWindowIcon(QIcon(":icons/bitcoin_testnet"));
|
setWindowIcon(QIcon(":icons/bitcoin_testnet"));
|
||||||
|
#else
|
||||||
|
MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet"));
|
||||||
|
#endif
|
||||||
if(trayIcon)
|
if(trayIcon)
|
||||||
{
|
{
|
||||||
trayIcon->setToolTip(title_testnet);
|
trayIcon->setToolTip(title_testnet);
|
||||||
|
@ -274,23 +318,39 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel)
|
||||||
|
|
||||||
void BitcoinGUI::createTrayIcon()
|
void BitcoinGUI::createTrayIcon()
|
||||||
{
|
{
|
||||||
QMenu *trayIconMenu = new QMenu(this);
|
QMenu *trayIconMenu;
|
||||||
trayIconMenu->addAction(openBitcoinAction);
|
#ifndef Q_WS_MAC
|
||||||
trayIconMenu->addAction(optionsAction);
|
|
||||||
trayIconMenu->addSeparator();
|
|
||||||
trayIconMenu->addAction(quitAction);
|
|
||||||
|
|
||||||
trayIcon = new QSystemTrayIcon(this);
|
trayIcon = new QSystemTrayIcon(this);
|
||||||
|
trayIconMenu = new QMenu(this);
|
||||||
trayIcon->setContextMenu(trayIconMenu);
|
trayIcon->setContextMenu(trayIconMenu);
|
||||||
trayIcon->setToolTip("Bitcoin client");
|
trayIcon->setToolTip("Bitcoin client");
|
||||||
trayIcon->setIcon(QIcon(":/icons/toolbar"));
|
trayIcon->setIcon(QIcon(":/icons/toolbar"));
|
||||||
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||||
this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
|
this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
|
||||||
trayIcon->show();
|
trayIcon->show();
|
||||||
|
#else
|
||||||
|
// Note: On Mac, the dock icon is used to provide the tray's functionality.
|
||||||
|
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
|
||||||
|
connect(dockIconHandler, SIGNAL(dockIconClicked()), openBitcoinAction, SLOT(trigger()));
|
||||||
|
trayIconMenu = dockIconHandler->dockMenu();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Configuration of the tray icon (or dock icon) icon menu
|
||||||
|
trayIconMenu->addAction(openBitcoinAction);
|
||||||
|
trayIconMenu->addSeparator();
|
||||||
|
trayIconMenu->addAction(receiveCoinsAction);
|
||||||
|
trayIconMenu->addAction(sendCoinsAction);
|
||||||
|
trayIconMenu->addSeparator();
|
||||||
|
trayIconMenu->addAction(optionsAction);
|
||||||
|
#ifndef Q_WS_MAC // This is built-in on Mac
|
||||||
|
trayIconMenu->addSeparator();
|
||||||
|
trayIconMenu->addAction(quitAction);
|
||||||
|
#endif
|
||||||
|
|
||||||
notificator = new Notificator(tr("bitcoin-qt"), trayIcon);
|
notificator = new Notificator(tr("bitcoin-qt"), trayIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
|
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
|
||||||
{
|
{
|
||||||
if(reason == QSystemTrayIcon::Trigger)
|
if(reason == QSystemTrayIcon::Trigger)
|
||||||
|
@ -300,6 +360,7 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void BitcoinGUI::optionsClicked()
|
void BitcoinGUI::optionsClicked()
|
||||||
{
|
{
|
||||||
|
@ -403,9 +464,10 @@ void BitcoinGUI::error(const QString &title, const QString &message)
|
||||||
|
|
||||||
void BitcoinGUI::changeEvent(QEvent *e)
|
void BitcoinGUI::changeEvent(QEvent *e)
|
||||||
{
|
{
|
||||||
|
#ifndef Q_WS_MAC // Ignored on Mac
|
||||||
if (e->type() == QEvent::WindowStateChange)
|
if (e->type() == QEvent::WindowStateChange)
|
||||||
{
|
{
|
||||||
if(clientModel->getOptionsModel()->getMinimizeToTray())
|
if (clientModel->getOptionsModel()->getMinimizeToTray())
|
||||||
{
|
{
|
||||||
if (isMinimized())
|
if (isMinimized())
|
||||||
{
|
{
|
||||||
|
@ -419,16 +481,19 @@ void BitcoinGUI::changeEvent(QEvent *e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
QMainWindow::changeEvent(e);
|
QMainWindow::changeEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitcoinGUI::closeEvent(QCloseEvent *event)
|
void BitcoinGUI::closeEvent(QCloseEvent *event)
|
||||||
{
|
{
|
||||||
|
#ifndef Q_WS_MAC // Ignored on Mac
|
||||||
if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
|
if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
|
||||||
!clientModel->getOptionsModel()->getMinimizeOnClose())
|
!clientModel->getOptionsModel()->getMinimizeOnClose())
|
||||||
{
|
{
|
||||||
qApp->quit();
|
qApp->quit();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
QMainWindow::closeEvent(event);
|
QMainWindow::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,6 +545,7 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int
|
||||||
|
|
||||||
void BitcoinGUI::gotoOverviewPage()
|
void BitcoinGUI::gotoOverviewPage()
|
||||||
{
|
{
|
||||||
|
show();
|
||||||
overviewAction->setChecked(true);
|
overviewAction->setChecked(true);
|
||||||
centralWidget->setCurrentWidget(overviewPage);
|
centralWidget->setCurrentWidget(overviewPage);
|
||||||
|
|
||||||
|
@ -489,6 +555,7 @@ void BitcoinGUI::gotoOverviewPage()
|
||||||
|
|
||||||
void BitcoinGUI::gotoHistoryPage()
|
void BitcoinGUI::gotoHistoryPage()
|
||||||
{
|
{
|
||||||
|
show();
|
||||||
historyAction->setChecked(true);
|
historyAction->setChecked(true);
|
||||||
centralWidget->setCurrentWidget(transactionsPage);
|
centralWidget->setCurrentWidget(transactionsPage);
|
||||||
|
|
||||||
|
@ -499,6 +566,7 @@ void BitcoinGUI::gotoHistoryPage()
|
||||||
|
|
||||||
void BitcoinGUI::gotoAddressBookPage()
|
void BitcoinGUI::gotoAddressBookPage()
|
||||||
{
|
{
|
||||||
|
show();
|
||||||
addressBookAction->setChecked(true);
|
addressBookAction->setChecked(true);
|
||||||
centralWidget->setCurrentWidget(addressBookPage);
|
centralWidget->setCurrentWidget(addressBookPage);
|
||||||
|
|
||||||
|
@ -509,6 +577,7 @@ void BitcoinGUI::gotoAddressBookPage()
|
||||||
|
|
||||||
void BitcoinGUI::gotoReceiveCoinsPage()
|
void BitcoinGUI::gotoReceiveCoinsPage()
|
||||||
{
|
{
|
||||||
|
show();
|
||||||
receiveCoinsAction->setChecked(true);
|
receiveCoinsAction->setChecked(true);
|
||||||
centralWidget->setCurrentWidget(receiveCoinsPage);
|
centralWidget->setCurrentWidget(receiveCoinsPage);
|
||||||
|
|
||||||
|
@ -519,6 +588,7 @@ void BitcoinGUI::gotoReceiveCoinsPage()
|
||||||
|
|
||||||
void BitcoinGUI::gotoSendCoinsPage()
|
void BitcoinGUI::gotoSendCoinsPage()
|
||||||
{
|
{
|
||||||
|
show();
|
||||||
sendCoinsAction->setChecked(true);
|
sendCoinsAction->setChecked(true);
|
||||||
centralWidget->setCurrentWidget(sendCoinsPage);
|
centralWidget->setCurrentWidget(sendCoinsPage);
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ class BitcoinGUI : public QMainWindow
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit BitcoinGUI(QWidget *parent = 0);
|
explicit BitcoinGUI(QWidget *parent = 0);
|
||||||
|
~BitcoinGUI();
|
||||||
|
|
||||||
void setClientModel(ClientModel *clientModel);
|
void setClientModel(ClientModel *clientModel);
|
||||||
void setWalletModel(WalletModel *walletModel);
|
void setWalletModel(WalletModel *walletModel);
|
||||||
|
|
||||||
|
@ -64,6 +66,7 @@ private:
|
||||||
QLabel *progressBarLabel;
|
QLabel *progressBarLabel;
|
||||||
QProgressBar *progressBar;
|
QProgressBar *progressBar;
|
||||||
|
|
||||||
|
QMenuBar *appMenuBar;
|
||||||
QAction *overviewAction;
|
QAction *overviewAction;
|
||||||
QAction *historyAction;
|
QAction *historyAction;
|
||||||
QAction *quitAction;
|
QAction *quitAction;
|
||||||
|
@ -84,6 +87,8 @@ private:
|
||||||
QMovie *syncIconMovie;
|
QMovie *syncIconMovie;
|
||||||
|
|
||||||
void createActions();
|
void createActions();
|
||||||
|
void createMenuBar();
|
||||||
|
void createToolBars();
|
||||||
QWidget *createTabs();
|
QWidget *createTabs();
|
||||||
void createTrayIcon();
|
void createTrayIcon();
|
||||||
|
|
||||||
|
@ -110,7 +115,9 @@ private slots:
|
||||||
// Misc actions
|
// Misc actions
|
||||||
void optionsClicked();
|
void optionsClicked();
|
||||||
void aboutClicked();
|
void aboutClicked();
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
|
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||||
|
#endif
|
||||||
void incomingTransaction(const QModelIndex & parent, int start, int end);
|
void incomingTransaction(const QModelIndex & parent, int start, int end);
|
||||||
void encryptWallet(bool status);
|
void encryptWallet(bool status);
|
||||||
void changePassphrase();
|
void changePassphrase();
|
||||||
|
|
|
@ -58,9 +58,6 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="addButton">
|
<widget class="QPushButton" name="addButton">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="payToLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="addressBookButton">
|
<widget class="QToolButton" name="addressBookButton">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Choose adress from address book</string>
|
<string>Choose adress from address book</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -112,16 +112,10 @@
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Alt+A</string>
|
<string>Alt+A</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="autoDefault">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="flat">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="pasteButton">
|
<widget class="QToolButton" name="pasteButton">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Paste address from clipboard</string>
|
<string>Paste address from clipboard</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -135,13 +129,10 @@
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Alt+P</string>
|
<string>Alt+P</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="autoDefault">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="deleteButton">
|
<widget class="QToolButton" name="deleteButton">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Remove this recipient</string>
|
<string>Remove this recipient</string>
|
||||||
</property>
|
</property>
|
||||||
|
|
37
src/qt/macdockiconhandler.h
Normal file
37
src/qt/macdockiconhandler.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef MACDOCKICONHANDLER_H
|
||||||
|
#define MACDOCKICONHANDLER_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
class QMenu;
|
||||||
|
class QIcon;
|
||||||
|
class QWidget;
|
||||||
|
class objc_object;
|
||||||
|
|
||||||
|
class MacDockIconHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
~MacDockIconHandler();
|
||||||
|
|
||||||
|
QMenu *dockMenu();
|
||||||
|
void setIcon(const QIcon &icon);
|
||||||
|
|
||||||
|
static MacDockIconHandler *instance();
|
||||||
|
|
||||||
|
void handleDockIconClickEvent();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dockIconClicked();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
private:
|
||||||
|
MacDockIconHandler();
|
||||||
|
|
||||||
|
objc_object *m_dockIconClickEventHandler;
|
||||||
|
QWidget *m_dummyWidget;
|
||||||
|
QMenu *m_dockMenu;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MACDOCKICONCLICKHANDLER_H
|
99
src/qt/macdockiconhandler.mm
Normal file
99
src/qt/macdockiconhandler.mm
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
|
||||||
|
#include "macdockiconhandler.h"
|
||||||
|
|
||||||
|
#include <QtGui/QMenu>
|
||||||
|
#include <QtGui/QWidget>
|
||||||
|
|
||||||
|
extern void qt_mac_set_dock_menu(QMenu*);
|
||||||
|
|
||||||
|
#undef slots
|
||||||
|
#include <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface DockIconClickEventHandler : NSObject
|
||||||
|
{
|
||||||
|
MacDockIconHandler* dockIconHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DockIconClickEventHandler
|
||||||
|
|
||||||
|
- (id)initWithDockIconHandler:(MacDockIconHandler *)aDockIconHandler
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
dockIconHandler = aDockIconHandler;
|
||||||
|
|
||||||
|
[[NSAppleEventManager sharedAppleEventManager]
|
||||||
|
setEventHandler:self
|
||||||
|
andSelector:@selector(handleDockClickEvent:withReplyEvent:)
|
||||||
|
forEventClass:kCoreEventClass
|
||||||
|
andEventID:kAEReopenApplication];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleDockClickEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
|
||||||
|
{
|
||||||
|
Q_UNUSED(event)
|
||||||
|
Q_UNUSED(replyEvent)
|
||||||
|
|
||||||
|
if (dockIconHandler)
|
||||||
|
dockIconHandler->handleDockIconClickEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
MacDockIconHandler::MacDockIconHandler() : QObject()
|
||||||
|
{
|
||||||
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
this->m_dockIconClickEventHandler = [[DockIconClickEventHandler alloc] initWithDockIconHandler:this];
|
||||||
|
|
||||||
|
this->m_dummyWidget = new QWidget();
|
||||||
|
this->m_dockMenu = new QMenu(this->m_dummyWidget);
|
||||||
|
qt_mac_set_dock_menu(this->m_dockMenu);
|
||||||
|
[pool release];
|
||||||
|
}
|
||||||
|
|
||||||
|
MacDockIconHandler::~MacDockIconHandler()
|
||||||
|
{
|
||||||
|
[this->m_dockIconClickEventHandler release];
|
||||||
|
delete this->m_dummyWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu *MacDockIconHandler::dockMenu()
|
||||||
|
{
|
||||||
|
return this->m_dockMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacDockIconHandler::setIcon(const QIcon &icon)
|
||||||
|
{
|
||||||
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
NSImage *image;
|
||||||
|
if (icon.isNull())
|
||||||
|
image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
|
||||||
|
else {
|
||||||
|
QSize size = icon.actualSize(QSize(128, 128));
|
||||||
|
QPixmap pixmap = icon.pixmap(size);
|
||||||
|
CGImageRef cgImage = pixmap.toMacCGImageRef();
|
||||||
|
image = [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize];
|
||||||
|
CFRelease(cgImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NSApp setApplicationIconImage:image];
|
||||||
|
[image release];
|
||||||
|
[pool release];
|
||||||
|
}
|
||||||
|
|
||||||
|
MacDockIconHandler *MacDockIconHandler::instance()
|
||||||
|
{
|
||||||
|
static MacDockIconHandler *s_instance = NULL;
|
||||||
|
if (!s_instance)
|
||||||
|
s_instance = new MacDockIconHandler();
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacDockIconHandler::handleDockIconClickEvent()
|
||||||
|
{
|
||||||
|
emit this->dockIconClicked();
|
||||||
|
}
|
|
@ -8,12 +8,19 @@
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QSystemTrayIcon>
|
#include <QSystemTrayIcon>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QImageWriter>
|
||||||
|
|
||||||
#ifdef USE_DBUS
|
#ifdef USE_DBUS
|
||||||
#include <QtDBus/QtDBus>
|
#include <QtDBus/QtDBus>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
|
extern bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret);
|
||||||
|
#endif
|
||||||
|
|
||||||
// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
|
// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
|
||||||
const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
|
const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
|
||||||
|
|
||||||
|
@ -39,6 +46,19 @@ Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon,
|
||||||
mode = Freedesktop;
|
mode = Freedesktop;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
// Check if Growl is installed (based on Qt's tray icon implementation)
|
||||||
|
CFURLRef cfurl;
|
||||||
|
OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
|
||||||
|
if (status != kLSApplicationNotFoundErr) {
|
||||||
|
CFBundleRef bundle = CFBundleCreate(0, cfurl);
|
||||||
|
CFRelease(cfurl);
|
||||||
|
if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) {
|
||||||
|
mode = Growl;
|
||||||
|
}
|
||||||
|
CFRelease(bundle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Notificator::~Notificator()
|
Notificator::~Notificator()
|
||||||
|
@ -201,6 +221,54 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString &
|
||||||
trayIcon->showMessage(title, text, sicon, millisTimeout);
|
trayIcon->showMessage(title, text, sicon, millisTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Based on Qt's tray icon implementation
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon)
|
||||||
|
{
|
||||||
|
const QString script(
|
||||||
|
"tell application \"GrowlHelperApp\"\n"
|
||||||
|
" set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all)
|
||||||
|
" set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled)
|
||||||
|
" register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl
|
||||||
|
" notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification
|
||||||
|
"end tell"
|
||||||
|
);
|
||||||
|
|
||||||
|
QString notificationApp(QApplication::applicationName());
|
||||||
|
if (notificationApp.isEmpty())
|
||||||
|
notificationApp = "Application";
|
||||||
|
|
||||||
|
QPixmap notificationIconPixmap;
|
||||||
|
if (icon.isNull()) { // If no icon specified, set icon based on class
|
||||||
|
QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
|
||||||
|
switch (cls)
|
||||||
|
{
|
||||||
|
case Information: sicon = QStyle::SP_MessageBoxInformation; break;
|
||||||
|
case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
|
||||||
|
case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
|
||||||
|
}
|
||||||
|
notificationIconPixmap = QApplication::style()->standardPixmap(sicon);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QSize size = icon.actualSize(QSize(48, 48));
|
||||||
|
notificationIconPixmap = icon.pixmap(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString notificationIcon;
|
||||||
|
QTemporaryFile notificationIconFile;
|
||||||
|
if (!notificationIconPixmap.isNull() && notificationIconFile.open()) {
|
||||||
|
QImageWriter writer(¬ificationIconFile, "PNG");
|
||||||
|
if (writer.write(notificationIconPixmap.toImage()))
|
||||||
|
notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString quotedTitle(title), quotedText(text);
|
||||||
|
quotedTitle.replace("\\", "\\\\").replace("\"", "\\");
|
||||||
|
quotedText.replace("\\", "\\\\").replace("\"", "\\");
|
||||||
|
qt_mac_execute_apple_script(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon), 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
|
void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
|
||||||
{
|
{
|
||||||
switch(mode)
|
switch(mode)
|
||||||
|
@ -213,6 +281,11 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c
|
||||||
case QSystemTray:
|
case QSystemTray:
|
||||||
notifySystray(cls, title, text, icon, millisTimeout);
|
notifySystray(cls, title, text, icon, millisTimeout);
|
||||||
break;
|
break;
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
case Growl:
|
||||||
|
notifyGrowl(cls, title, text, icon);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
if(cls == Critical)
|
if(cls == Critical)
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,6 +48,7 @@ private:
|
||||||
None,
|
None,
|
||||||
Freedesktop, // Use DBus org.freedesktop.Notifications
|
Freedesktop, // Use DBus org.freedesktop.Notifications
|
||||||
QSystemTray, // Use QSystemTray::showMessage
|
QSystemTray, // Use QSystemTray::showMessage
|
||||||
|
Growl // Use the Growl notification system (Mac only)
|
||||||
};
|
};
|
||||||
QString programName;
|
QString programName;
|
||||||
Mode mode;
|
Mode mode;
|
||||||
|
@ -58,6 +59,9 @@ private:
|
||||||
void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
|
void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
|
||||||
#endif
|
#endif
|
||||||
void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
|
void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
void notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NOTIFICATOR_H
|
#endif // NOTIFICATOR_H
|
||||||
|
|
|
@ -30,9 +30,13 @@ public:
|
||||||
void setMapper(MonitoredDataMapper *mapper);
|
void setMapper(MonitoredDataMapper *mapper);
|
||||||
private:
|
private:
|
||||||
QCheckBox *bitcoin_at_startup;
|
QCheckBox *bitcoin_at_startup;
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
QCheckBox *minimize_to_tray;
|
QCheckBox *minimize_to_tray;
|
||||||
|
#endif
|
||||||
QCheckBox *map_port_upnp;
|
QCheckBox *map_port_upnp;
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
QCheckBox *minimize_on_close;
|
QCheckBox *minimize_on_close;
|
||||||
|
#endif
|
||||||
QCheckBox *connect_socks4;
|
QCheckBox *connect_socks4;
|
||||||
QLineEdit *proxy_ip;
|
QLineEdit *proxy_ip;
|
||||||
QLineEdit *proxy_port;
|
QLineEdit *proxy_port;
|
||||||
|
@ -167,17 +171,21 @@ MainOptionsPage::MainOptionsPage(QWidget *parent):
|
||||||
bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on"));
|
bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on"));
|
||||||
layout->addWidget(bitcoin_at_startup);
|
layout->addWidget(bitcoin_at_startup);
|
||||||
|
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar"));
|
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"));
|
minimize_to_tray->setToolTip(tr("Show only a tray icon after minimizing the window"));
|
||||||
layout->addWidget(minimize_to_tray);
|
layout->addWidget(minimize_to_tray);
|
||||||
|
#endif
|
||||||
|
|
||||||
map_port_upnp = new QCheckBox(tr("Map port using &UPnP"));
|
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."));
|
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);
|
layout->addWidget(map_port_upnp);
|
||||||
|
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
minimize_on_close = new QCheckBox(tr("M&inimize on close"));
|
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."));
|
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);
|
layout->addWidget(minimize_on_close);
|
||||||
|
#endif
|
||||||
|
|
||||||
connect_socks4 = new QCheckBox(tr("&Connect through SOCKS4 proxy:"));
|
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)"));
|
connect_socks4->setToolTip(tr("Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor)"));
|
||||||
|
@ -239,9 +247,13 @@ void MainOptionsPage::setMapper(MonitoredDataMapper *mapper)
|
||||||
{
|
{
|
||||||
// Map model to widgets
|
// Map model to widgets
|
||||||
mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup);
|
mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup);
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray);
|
mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray);
|
||||||
|
#endif
|
||||||
mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP);
|
mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP);
|
||||||
|
#ifndef Q_WS_MAC
|
||||||
mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose);
|
mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose);
|
||||||
|
#endif
|
||||||
mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4);
|
mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4);
|
||||||
mapper->addMapping(proxy_ip, OptionsModel::ProxyIP);
|
mapper->addMapping(proxy_ip, OptionsModel::ProxyIP);
|
||||||
mapper->addMapping(proxy_port, OptionsModel::ProxyPort);
|
mapper->addMapping(proxy_port, OptionsModel::ProxyPort);
|
||||||
|
|
|
@ -116,6 +116,7 @@ OverviewPage::OverviewPage(QWidget *parent) :
|
||||||
ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
|
ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
|
||||||
ui->listTransactions->setSelectionMode(QAbstractItemView::NoSelection);
|
ui->listTransactions->setSelectionMode(QAbstractItemView::NoSelection);
|
||||||
ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
|
ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
|
||||||
|
ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||||
|
|
||||||
connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SIGNAL(transactionClicked(QModelIndex)));
|
connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SIGNAL(transactionClicked(QModelIndex)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,12 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
#ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
|
||||||
|
ui->addButton->setIcon(QIcon());
|
||||||
|
ui->clearButton->setIcon(QIcon());
|
||||||
|
ui->sendButton->setIcon(QIcon());
|
||||||
|
#endif
|
||||||
|
|
||||||
addEntry();
|
addEntry();
|
||||||
|
|
||||||
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
|
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
|
||||||
|
|
|
@ -17,6 +17,10 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) :
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
ui->payToLayout->setSpacing(4);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if QT_VERSION >= 0x040700
|
#if QT_VERSION >= 0x040700
|
||||||
ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)"));
|
ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)"));
|
||||||
ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book"));
|
ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book"));
|
||||||
|
|
|
@ -38,13 +38,20 @@ TransactionView::TransactionView(QWidget *parent) :
|
||||||
|
|
||||||
QHBoxLayout *hlayout = new QHBoxLayout();
|
QHBoxLayout *hlayout = new QHBoxLayout();
|
||||||
hlayout->setContentsMargins(0,0,0,0);
|
hlayout->setContentsMargins(0,0,0,0);
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
hlayout->setSpacing(5);
|
||||||
|
hlayout->addSpacing(26);
|
||||||
|
#else
|
||||||
hlayout->setSpacing(0);
|
hlayout->setSpacing(0);
|
||||||
|
|
||||||
hlayout->addSpacing(23);
|
hlayout->addSpacing(23);
|
||||||
|
#endif
|
||||||
|
|
||||||
dateWidget = new QComboBox(this);
|
dateWidget = new QComboBox(this);
|
||||||
dateWidget->setMaximumWidth(120);
|
#ifdef Q_WS_MAC
|
||||||
dateWidget->setMinimumWidth(120);
|
dateWidget->setFixedWidth(121);
|
||||||
|
#else
|
||||||
|
dateWidget->setFixedWidth(120);
|
||||||
|
#endif
|
||||||
dateWidget->addItem(tr("All"), All);
|
dateWidget->addItem(tr("All"), All);
|
||||||
dateWidget->addItem(tr("Today"), Today);
|
dateWidget->addItem(tr("Today"), Today);
|
||||||
dateWidget->addItem(tr("This week"), ThisWeek);
|
dateWidget->addItem(tr("This week"), ThisWeek);
|
||||||
|
@ -55,8 +62,11 @@ TransactionView::TransactionView(QWidget *parent) :
|
||||||
hlayout->addWidget(dateWidget);
|
hlayout->addWidget(dateWidget);
|
||||||
|
|
||||||
typeWidget = new QComboBox(this);
|
typeWidget = new QComboBox(this);
|
||||||
typeWidget->setMaximumWidth(120);
|
#ifdef Q_WS_MAC
|
||||||
typeWidget->setMinimumWidth(120);
|
typeWidget->setFixedWidth(121);
|
||||||
|
#else
|
||||||
|
typeWidget->setFixedWidth(120);
|
||||||
|
#endif
|
||||||
|
|
||||||
typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES);
|
typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES);
|
||||||
typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) |
|
typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) |
|
||||||
|
@ -79,8 +89,11 @@ TransactionView::TransactionView(QWidget *parent) :
|
||||||
#if QT_VERSION >= 0x040700
|
#if QT_VERSION >= 0x040700
|
||||||
amountWidget->setPlaceholderText(tr("Min amount"));
|
amountWidget->setPlaceholderText(tr("Min amount"));
|
||||||
#endif
|
#endif
|
||||||
amountWidget->setMaximumWidth(100);
|
#ifdef Q_WS_MAC
|
||||||
amountWidget->setMinimumWidth(100);
|
amountWidget->setFixedWidth(97);
|
||||||
|
#else
|
||||||
|
amountWidget->setFixedWidth(100);
|
||||||
|
#endif
|
||||||
amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
|
amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
|
||||||
hlayout->addWidget(amountWidget);
|
hlayout->addWidget(amountWidget);
|
||||||
|
|
||||||
|
@ -96,7 +109,11 @@ TransactionView::TransactionView(QWidget *parent) :
|
||||||
vlayout->setSpacing(0);
|
vlayout->setSpacing(0);
|
||||||
int width = view->verticalScrollBar()->sizeHint().width();
|
int width = view->verticalScrollBar()->sizeHint().width();
|
||||||
// Cover scroll bar width with spacing
|
// Cover scroll bar width with spacing
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
hlayout->addSpacing(width+2);
|
||||||
|
#else
|
||||||
hlayout->addSpacing(width);
|
hlayout->addSpacing(width);
|
||||||
|
#endif
|
||||||
// Always show scroll bar
|
// Always show scroll bar
|
||||||
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||||
view->setTabKeyNavigation(false);
|
view->setTabKeyNavigation(false);
|
||||||
|
|
Loading…
Add table
Reference in a new issue