aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bitcoin-qt.pro3
-rw-r--r--src/qt/addressbookpage.cpp7
-rw-r--r--src/qt/bitcoin.cpp7
-rw-r--r--src/qt/bitcoinamountfield.cpp91
-rw-r--r--src/qt/bitcoinamountfield.h9
-rw-r--r--src/qt/bitcoingui.cpp136
-rw-r--r--src/qt/bitcoingui.h7
-rw-r--r--src/qt/forms/sendcoinsdialog.ui3
-rw-r--r--src/qt/forms/sendcoinsentry.ui17
-rw-r--r--src/qt/macdockiconhandler.h37
-rw-r--r--src/qt/macdockiconhandler.mm99
-rw-r--r--src/qt/notificator.cpp73
-rw-r--r--src/qt/notificator.h4
-rw-r--r--src/qt/optionsdialog.cpp12
-rw-r--r--src/qt/overviewpage.cpp1
-rw-r--r--src/qt/sendcoinsdialog.cpp6
-rw-r--r--src/qt/sendcoinsentry.cpp4
-rw-r--r--src/qt/transactionview.cpp31
18 files changed, 435 insertions, 112 deletions
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro
index 1f65d0574f..5b9d748e24 100644
--- a/bitcoin-qt.pro
+++ b/bitcoin-qt.pro
@@ -221,6 +221,9 @@ windows:LIBS += -lws2_32 -lgdi32
windows:DEFINES += WIN32
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:ICON = src/qt/res/icons/bitcoin.icns
macx:TARGET = "Bitcoin Qt"
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index ee64cc2c80..6be59a082f 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -18,6 +18,13 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
tab(tab)
{
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)
{
case ForSending:
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 91881c349a..c8e332419c 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -16,6 +16,7 @@
#include <QLocale>
#include <QTranslator>
#include <QSplashScreen>
+#include <QLibraryInfo>
// Need a global reference for the notifications to find the GUI
BitcoinGUI *guiref;
@@ -119,11 +120,17 @@ int main(int argc, char *argv[])
// Load language file for system locale
QString locale = QLocale::system().name();
+ QTranslator qtTranslator;
+ qtTranslator.load(QLibraryInfo::location(QLibraryInfo::TranslationsPath) + "/qt_" + locale);
+ if (!qtTranslator.isEmpty())
+ app.installTranslator(&qtTranslator);
QTranslator translator;
translator.load(":/translations/"+locale);
if (!translator.isEmpty())
app.installTranslator(&translator);
+ app.setApplicationName(QApplication::translate("main", "Bitcoin Qt"));
+
QSplashScreen splash(QPixmap(":/images/splash"), 0);
splash.show();
splash.setAutoFillBackground(true);
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index f1edc62bbe..19cd5655cc 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -1,33 +1,30 @@
#include "bitcoinamountfield.h"
-#include "qvalidatedlineedit.h"
#include "qvaluecombobox.h"
#include "bitcoinunits.h"
+#include "guiconstants.h"
+
#include <QLabel>
#include <QLineEdit>
#include <QRegExpValidator>
#include <QHBoxLayout>
#include <QKeyEvent>
+#include <QDoubleSpinBox>
#include <QComboBox>
+#include <QApplication>
+#include <qmath.h>
BitcoinAmountField::BitcoinAmountField(QWidget *parent):
- QWidget(parent), amount(0), decimals(0), currentUnit(-1)
+ QWidget(parent), amount(0), currentUnit(-1)
{
- amount = new QValidatedLineEdit(this);
- amount->setValidator(new QRegExpValidator(QRegExp("[0-9]*"), this));
- amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ amount = new QDoubleSpinBox(this);
+ amount->setLocale(QLocale::c());
+ amount->setDecimals(8);
amount->installEventFilter(this);
- amount->setMaximumWidth(75);
- decimals = new QValidatedLineEdit(this);
- decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this));
- decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
- decimals->setMaximumWidth(75);
+ amount->setMaximumWidth(170);
QHBoxLayout *layout = new QHBoxLayout(this);
- layout->setSpacing(0);
layout->addWidget(amount);
- layout->addWidget(new QLabel(QString("<b>.</b>")));
- layout->addWidget(decimals);
unit = new QValueComboBox(this);
unit->setModel(new BitcoinUnits(this));
layout->addWidget(unit);
@@ -40,8 +37,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent):
setFocusProxy(amount);
// If one if the widgets changes, the combined content changes as well
- connect(amount, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged()));
- connect(decimals, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged()));
+ connect(amount, SIGNAL(valueChanged(QString)), this, SIGNAL(textChanged()));
connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
// Set default based on configuration
@@ -50,79 +46,72 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent):
void BitcoinAmountField::setText(const QString &text)
{
- const QStringList parts = text.split(QString("."));
- if(parts.size() == 2)
- {
- amount->setText(parts[0]);
- decimals->setText(parts[1]);
- }
+ if (text.isEmpty())
+ amount->clear();
else
- {
- amount->setText(QString());
- decimals->setText(QString());
- }
+ amount->setValue(text.toDouble());
}
void BitcoinAmountField::clear()
{
amount->clear();
- decimals->clear();
unit->setCurrentIndex(0);
}
bool BitcoinAmountField::validate()
{
bool valid = true;
- if(decimals->text().isEmpty())
- {
- decimals->setValid(false);
+ if (amount->value() == 0.0)
valid = false;
- }
- if(!BitcoinUnits::parse(currentUnit, text(), 0))
- {
- setValid(false);
+ if (valid && !BitcoinUnits::parse(currentUnit, text(), 0))
valid = false;
- }
+
+ setValid(valid);
return valid;
}
void BitcoinAmountField::setValid(bool valid)
{
- amount->setValid(valid);
- decimals->setValid(valid);
+ if (valid)
+ amount->setStyleSheet("");
+ else
+ amount->setStyleSheet(STYLE_INVALID);
}
QString BitcoinAmountField::text() const
{
- if(decimals->text().isEmpty() && amount->text().isEmpty())
- {
+ if (amount->text().isEmpty())
return QString();
- }
- return amount->text() + QString(".") + decimals->text();
+ else
+ return amount->text();
}
-// Intercept '.' and ',' keys, if pressed focus a specified widget
bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
- Q_UNUSED(object);
- if(event->type() == QEvent::KeyPress)
+ if (event->type() == QEvent::FocusIn)
+ {
+ // Clear invalid flag on focus
+ setValid(true);
+ }
+ else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
{
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();
- decimals->selectAll();
+ // Translate a comma into a period
+ 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::setTabOrder(prev, amount);
- QWidget::setTabOrder(amount, decimals);
- return decimals;
+ return amount;
}
qint64 BitcoinAmountField::value(bool *valid_out) const
@@ -156,8 +145,8 @@ void BitcoinAmountField::unitChanged(int idx)
currentUnit = newUnit;
// Set max length after retrieving the value, to prevent truncation
- amount->setMaxLength(BitcoinUnits::amountDigits(currentUnit));
- decimals->setMaxLength(BitcoinUnits::decimals(currentUnit));
+ amount->setDecimals(BitcoinUnits::decimals(currentUnit));
+ amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals()));
if(valid)
{
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index cc92159fed..8457a418c2 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -4,7 +4,7 @@
#include <QWidget>
QT_BEGIN_NAMESPACE
-class QValidatedLineEdit;
+class QDoubleSpinBox;
class QValueComboBox;
QT_END_NAMESPACE
@@ -13,7 +13,7 @@ QT_END_NAMESPACE
class BitcoinAmountField: public QWidget
{
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:
explicit BitcoinAmountField(QWidget *parent = 0);
@@ -38,12 +38,11 @@ signals:
void textChanged();
protected:
- // Intercept '.' and ',' keys, if pressed focus a specified widget
+ // Intercept focus-in event and ',' keypresses
bool eventFilter(QObject *object, QEvent *event);
private:
- QValidatedLineEdit *amount;
- QValidatedLineEdit *decimals;
+ QDoubleSpinBox *amount;
QValueComboBox *unit;
int currentUnit;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 25261c780f..beffa85c5d 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -22,6 +22,10 @@
#include "askpassphrasedialog.h"
#include "notificator.h"
+#ifdef Q_WS_MAC
+#include "macdockiconhandler.h"
+#endif
+
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
@@ -57,37 +61,26 @@ BitcoinGUI::BitcoinGUI(QWidget *parent):
{
resize(850, 550);
setWindowTitle(tr("Bitcoin Wallet"));
+#ifndef Q_WS_MAC
setWindowIcon(QIcon(":icons/bitcoin"));
+#else
+ setUnifiedTitleAndToolBarOnMac(true);
+ QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
+#endif
// Accept D&D of URIs
setAcceptDrops(true);
+ // Create actions for the toolbar, menu bar and tray/dock icon
createActions();
- // Menus
- QMenu *file = menuBar()->addMenu(tr("&File"));
- file->addAction(quitAction);
-
- QMenu *settings = menuBar()->addMenu(tr("&Settings"));
- settings->addAction(encryptWalletAction);
- settings->addAction(changePassphraseAction);
- settings->addSeparator();
- settings->addAction(optionsAction);
+ // Create application menu bar
+ createMenuBar();
- QMenu *help = menuBar()->addMenu(tr("&Help"));
- help->addAction(aboutAction);
-
- // 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);
+ // Create the toolbars
+ createToolBars();
- QToolBar *toolbar2 = addToolBar(tr("Actions toolbar"));
- toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
- toolbar2->addAction(exportAction);
+ // Create the tray icon (or setup the dock icon)
+ createTrayIcon();
// Create tabs
overviewPage = new OverviewPage();
@@ -146,8 +139,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent):
statusBar()->addWidget(progressBar);
statusBar()->addPermanentWidget(frameBlocks);
- createTrayIcon();
-
syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this);
// Clicking on a transaction on the overview page simply sends you to transaction history page
@@ -159,6 +150,13 @@ BitcoinGUI::BitcoinGUI(QWidget *parent):
gotoOverviewPage();
}
+BitcoinGUI::~BitcoinGUI()
+{
+#ifdef Q_WS_MAC
+ delete appMenuBar;
+#endif
+}
+
void BitcoinGUI::createActions()
{
QActionGroup *tabGroup = new QActionGroup(this);
@@ -197,10 +195,13 @@ void BitcoinGUI::createActions()
quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this);
quitAction->setToolTip(tr("Quit application"));
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->setMenuRole(QAction::AboutQtRole);
optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
optionsAction->setToolTip(tr("Modify configuration options for bitcoin"));
+ optionsAction->setMenuRole(QAction::PreferencesRole);
openBitcoinAction = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this);
openBitcoinAction->setToolTip(tr("Show the Bitcoin window"));
exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this);
@@ -219,6 +220,45 @@ void BitcoinGUI::createActions()
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)
{
this->clientModel = clientModel;
@@ -227,7 +267,11 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
{
QString title_testnet = windowTitle() + QString(" ") + tr("[testnet]");
setWindowTitle(title_testnet);
+#ifndef Q_WS_MAC
setWindowIcon(QIcon(":icons/bitcoin_testnet"));
+#else
+ MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet"));
+#endif
if(trayIcon)
{
trayIcon->setToolTip(title_testnet);
@@ -274,23 +318,39 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel)
void BitcoinGUI::createTrayIcon()
{
- QMenu *trayIconMenu = new QMenu(this);
- trayIconMenu->addAction(openBitcoinAction);
- trayIconMenu->addAction(optionsAction);
- trayIconMenu->addSeparator();
- trayIconMenu->addAction(quitAction);
-
+ QMenu *trayIconMenu;
+#ifndef Q_WS_MAC
trayIcon = new QSystemTrayIcon(this);
+ trayIconMenu = new QMenu(this);
trayIcon->setContextMenu(trayIconMenu);
trayIcon->setToolTip("Bitcoin client");
trayIcon->setIcon(QIcon(":/icons/toolbar"));
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
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);
}
+#ifndef Q_WS_MAC
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
if(reason == QSystemTrayIcon::Trigger)
@@ -300,6 +360,7 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
}
}
+#endif
void BitcoinGUI::optionsClicked()
{
@@ -403,9 +464,10 @@ void BitcoinGUI::error(const QString &title, const QString &message)
void BitcoinGUI::changeEvent(QEvent *e)
{
+#ifndef Q_WS_MAC // Ignored on Mac
if (e->type() == QEvent::WindowStateChange)
{
- if(clientModel->getOptionsModel()->getMinimizeToTray())
+ if (clientModel->getOptionsModel()->getMinimizeToTray())
{
if (isMinimized())
{
@@ -419,16 +481,19 @@ void BitcoinGUI::changeEvent(QEvent *e)
}
}
}
+#endif
QMainWindow::changeEvent(e);
}
void BitcoinGUI::closeEvent(QCloseEvent *event)
{
+#ifndef Q_WS_MAC // Ignored on Mac
if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
!clientModel->getOptionsModel()->getMinimizeOnClose())
{
qApp->quit();
}
+#endif
QMainWindow::closeEvent(event);
}
@@ -480,6 +545,7 @@ void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int
void BitcoinGUI::gotoOverviewPage()
{
+ show();
overviewAction->setChecked(true);
centralWidget->setCurrentWidget(overviewPage);
@@ -489,6 +555,7 @@ void BitcoinGUI::gotoOverviewPage()
void BitcoinGUI::gotoHistoryPage()
{
+ show();
historyAction->setChecked(true);
centralWidget->setCurrentWidget(transactionsPage);
@@ -499,6 +566,7 @@ void BitcoinGUI::gotoHistoryPage()
void BitcoinGUI::gotoAddressBookPage()
{
+ show();
addressBookAction->setChecked(true);
centralWidget->setCurrentWidget(addressBookPage);
@@ -509,6 +577,7 @@ void BitcoinGUI::gotoAddressBookPage()
void BitcoinGUI::gotoReceiveCoinsPage()
{
+ show();
receiveCoinsAction->setChecked(true);
centralWidget->setCurrentWidget(receiveCoinsPage);
@@ -519,6 +588,7 @@ void BitcoinGUI::gotoReceiveCoinsPage()
void BitcoinGUI::gotoSendCoinsPage()
{
+ show();
sendCoinsAction->setChecked(true);
centralWidget->setCurrentWidget(sendCoinsPage);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 59661350c3..a912192196 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -29,6 +29,8 @@ class BitcoinGUI : public QMainWindow
Q_OBJECT
public:
explicit BitcoinGUI(QWidget *parent = 0);
+ ~BitcoinGUI();
+
void setClientModel(ClientModel *clientModel);
void setWalletModel(WalletModel *walletModel);
@@ -64,6 +66,7 @@ private:
QLabel *progressBarLabel;
QProgressBar *progressBar;
+ QMenuBar *appMenuBar;
QAction *overviewAction;
QAction *historyAction;
QAction *quitAction;
@@ -84,6 +87,8 @@ private:
QMovie *syncIconMovie;
void createActions();
+ void createMenuBar();
+ void createToolBars();
QWidget *createTabs();
void createTrayIcon();
@@ -110,7 +115,9 @@ private slots:
// Misc actions
void optionsClicked();
void aboutClicked();
+#ifndef Q_WS_MAC
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
+#endif
void incomingTransaction(const QModelIndex & parent, int start, int end);
void encryptWallet(bool status);
void changePassphrase();
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index f9dd02fef5..e5e19e1015 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -58,9 +58,6 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
- <property name="spacing">
- <number>6</number>
- </property>
<item>
<widget class="QPushButton" name="addButton">
<property name="toolTip">
diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui
index 13593c2c1e..0297d17f15 100644
--- a/src/qt/forms/sendcoinsentry.ui
+++ b/src/qt/forms/sendcoinsentry.ui
@@ -83,7 +83,7 @@
</widget>
</item>
<item row="3" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <layout class="QHBoxLayout" name="payToLayout">
<property name="spacing">
<number>0</number>
</property>
@@ -98,7 +98,7 @@
</widget>
</item>
<item>
- <widget class="QPushButton" name="addressBookButton">
+ <widget class="QToolButton" name="addressBookButton">
<property name="toolTip">
<string>Choose adress from address book</string>
</property>
@@ -112,16 +112,10 @@
<property name="shortcut">
<string>Alt+A</string>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="pasteButton">
+ <widget class="QToolButton" name="pasteButton">
<property name="toolTip">
<string>Paste address from clipboard</string>
</property>
@@ -135,13 +129,10 @@
<property name="shortcut">
<string>Alt+P</string>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="deleteButton">
+ <widget class="QToolButton" name="deleteButton">
<property name="toolTip">
<string>Remove this recipient</string>
</property>
diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h
new file mode 100644
index 0000000000..cc78c7a787
--- /dev/null
+++ b/src/qt/macdockiconhandler.h
@@ -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
diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm
new file mode 100644
index 0000000000..df56e6949d
--- /dev/null
+++ b/src/qt/macdockiconhandler.mm
@@ -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();
+}
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index cf0c0a3901..a2314caa47 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -8,12 +8,19 @@
#include <QByteArray>
#include <QSystemTrayIcon>
#include <QMessageBox>
+#include <QTemporaryFile>
+#include <QImageWriter>
#ifdef USE_DBUS
#include <QtDBus/QtDBus>
#include <stdint.h>
#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
const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
@@ -39,6 +46,19 @@ Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon,
mode = Freedesktop;
}
#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()
@@ -201,6 +221,54 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString &
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(&notificationIconFile, "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)
{
switch(mode)
@@ -213,6 +281,11 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c
case QSystemTray:
notifySystray(cls, title, text, icon, millisTimeout);
break;
+#ifdef Q_WS_MAC
+ case Growl:
+ notifyGrowl(cls, title, text, icon);
+ break;
+#endif
default:
if(cls == Critical)
{
diff --git a/src/qt/notificator.h b/src/qt/notificator.h
index 4217f7e06f..ed69ae5c61 100644
--- a/src/qt/notificator.h
+++ b/src/qt/notificator.h
@@ -48,6 +48,7 @@ private:
None,
Freedesktop, // Use DBus org.freedesktop.Notifications
QSystemTray, // Use QSystemTray::showMessage
+ Growl // Use the Growl notification system (Mac only)
};
QString programName;
Mode mode;
@@ -58,6 +59,9 @@ private:
void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
#endif
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
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 7267e3d103..ea3164e3d7 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -30,9 +30,13 @@ public:
void setMapper(MonitoredDataMapper *mapper);
private:
QCheckBox *bitcoin_at_startup;
+#ifndef Q_WS_MAC
QCheckBox *minimize_to_tray;
+#endif
QCheckBox *map_port_upnp;
+#ifndef Q_WS_MAC
QCheckBox *minimize_on_close;
+#endif
QCheckBox *connect_socks4;
QLineEdit *proxy_ip;
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"));
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->setToolTip(tr("Show only a tray icon after minimizing the window"));
layout->addWidget(minimize_to_tray);
+#endif
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);
+#ifndef Q_WS_MAC
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);
+#endif
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)"));
@@ -239,9 +247,13 @@ void MainOptionsPage::setMapper(MonitoredDataMapper *mapper)
{
// Map model to widgets
mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup);
+#ifndef Q_WS_MAC
mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray);
+#endif
mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP);
+#ifndef Q_WS_MAC
mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose);
+#endif
mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4);
mapper->addMapping(proxy_ip, OptionsModel::ProxyIP);
mapper->addMapping(proxy_port, OptionsModel::ProxyPort);
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index f84a79fe30..6dedde0272 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -116,6 +116,7 @@ OverviewPage::OverviewPage(QWidget *parent) :
ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
ui->listTransactions->setSelectionMode(QAbstractItemView::NoSelection);
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)));
}
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 58eb5c21f7..719cc51880 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -19,6 +19,12 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
{
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();
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index fccef232bb..1802095b3a 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -17,6 +17,10 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) :
{
ui->setupUi(this);
+#ifdef Q_WS_MAC
+ ui->payToLayout->setSpacing(4);
+#endif
+
#if QT_VERSION >= 0x040700
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"));
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index b2777b7b20..92dda5784c 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -38,13 +38,20 @@ TransactionView::TransactionView(QWidget *parent) :
QHBoxLayout *hlayout = new QHBoxLayout();
hlayout->setContentsMargins(0,0,0,0);
+#ifdef Q_WS_MAC
+ hlayout->setSpacing(5);
+ hlayout->addSpacing(26);
+#else
hlayout->setSpacing(0);
-
hlayout->addSpacing(23);
+#endif
dateWidget = new QComboBox(this);
- dateWidget->setMaximumWidth(120);
- dateWidget->setMinimumWidth(120);
+#ifdef Q_WS_MAC
+ dateWidget->setFixedWidth(121);
+#else
+ dateWidget->setFixedWidth(120);
+#endif
dateWidget->addItem(tr("All"), All);
dateWidget->addItem(tr("Today"), Today);
dateWidget->addItem(tr("This week"), ThisWeek);
@@ -55,8 +62,11 @@ TransactionView::TransactionView(QWidget *parent) :
hlayout->addWidget(dateWidget);
typeWidget = new QComboBox(this);
- typeWidget->setMaximumWidth(120);
- typeWidget->setMinimumWidth(120);
+#ifdef Q_WS_MAC
+ typeWidget->setFixedWidth(121);
+#else
+ typeWidget->setFixedWidth(120);
+#endif
typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES);
typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) |
@@ -79,8 +89,11 @@ TransactionView::TransactionView(QWidget *parent) :
#if QT_VERSION >= 0x040700
amountWidget->setPlaceholderText(tr("Min amount"));
#endif
- amountWidget->setMaximumWidth(100);
- amountWidget->setMinimumWidth(100);
+#ifdef Q_WS_MAC
+ amountWidget->setFixedWidth(97);
+#else
+ amountWidget->setFixedWidth(100);
+#endif
amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
hlayout->addWidget(amountWidget);
@@ -96,7 +109,11 @@ TransactionView::TransactionView(QWidget *parent) :
vlayout->setSpacing(0);
int width = view->verticalScrollBar()->sizeHint().width();
// Cover scroll bar width with spacing
+#ifdef Q_WS_MAC
+ hlayout->addSpacing(width+2);
+#else
hlayout->addSpacing(width);
+#endif
// Always show scroll bar
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
view->setTabKeyNavigation(false);