From ba4334e453ee7878f28fc7357b9a84d718533f70 Mon Sep 17 00:00:00 2001 From: Graham Higgins Date: Mon, 10 Oct 2016 10:52:48 +0100 Subject: [PATCH 1/3] Add Bittrex trading window --- src/Makefile.qt.include | 6 + src/qt/bitcoingui.cpp | 21 + src/qt/bitcoingui.h | 5 + src/qt/forms/tradingdialog.ui | 3230 +++++++++++++++++++++++++++++++++ src/qt/res/icons/trade.png | Bin 0 -> 4259 bytes src/qt/tradingdialog.cpp | 1094 +++++++++++ src/qt/tradingdialog.h | 99 + src/qt/walletframe.cpp | 7 + src/qt/walletframe.h | 3 + src/qt/walletview.cpp | 8 + src/qt/walletview.h | 4 + 11 files changed, 4477 insertions(+) create mode 100644 src/qt/forms/tradingdialog.ui create mode 100644 src/qt/res/icons/trade.png create mode 100644 src/qt/tradingdialog.cpp create mode 100644 src/qt/tradingdialog.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index f70c3dc22df8e..c1176831447a5 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -37,6 +37,7 @@ QT_FORMS_UI = \ qt/forms/sendcoinsdialog.ui \ qt/forms/sendcoinsentry.ui \ qt/forms/signverifymessagedialog.ui \ + qt/forms/tradingdialog.ui \ qt/forms/transactiondescdialog.ui QT_MOC_CPP = \ @@ -74,6 +75,7 @@ QT_MOC_CPP = \ qt/moc_sendcoinsentry.cpp \ qt/moc_signverifymessagedialog.cpp \ qt/moc_splashscreen.cpp \ + qt/moc_tradingdialog.cpp \ qt/moc_trafficgraphwidget.cpp \ qt/moc_transactiondesc.cpp \ qt/moc_transactiondescdialog.cpp \ @@ -143,6 +145,7 @@ BITCOIN_QT_H = \ qt/sendcoinsentry.h \ qt/signverifymessagedialog.h \ qt/splashscreen.h \ + qt/tradingdialog.h \ qt/trafficgraphwidget.h \ qt/transactiondesc.h \ qt/transactiondescdialog.h \ @@ -208,6 +211,7 @@ RES_ICONS = \ qt/res/icons/remove.png \ qt/res/icons/send.png \ qt/res/icons/synced.png \ + qt/res/icons/trade.png \ qt/res/icons/transaction0.png \ qt/res/icons/transaction2.png \ qt/res/icons/transaction_conflicted.png \ @@ -240,6 +244,7 @@ BITCOIN_QT_CPP = \ qt/qvaluecombobox.cpp \ qt/rpcconsole.cpp \ qt/splashscreen.cpp \ + qt/tradingdialog.cpp \ qt/trafficgraphwidget.cpp \ qt/utilitydialog.cpp \ qt/winshutdownmonitor.cpp @@ -263,6 +268,7 @@ BITCOIN_QT_CPP += \ qt/sendcoinsdialog.cpp \ qt/sendcoinsentry.cpp \ qt/signverifymessagedialog.cpp \ + qt/tradingdialog.cpp \ qt/transactiondesc.cpp \ qt/transactiondescdialog.cpp \ qt/transactionfilterproxy.cpp \ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 1879981b36c68..fe65d1de32a6c 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -22,6 +22,7 @@ #ifdef ENABLE_WALLET #include "walletframe.h" #include "walletmodel.h" +#include "tradingdialog.h" #endif // ENABLE_WALLET #ifdef Q_OS_MAC @@ -99,6 +100,7 @@ BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : trayIconMenu(0), notificator(0), rpcConsole(0), + tradingWindow(0), prevBlocks(0), spinnerFrame(0) { @@ -143,6 +145,7 @@ BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : { /** Create wallet frame*/ walletFrame = new WalletFrame(this); + tradingWindow = new tradingDialog(this); } else #endif // ENABLE_WALLET { @@ -238,6 +241,11 @@ BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : // prevents an open debug window from becoming stuck/unusable on client shutdown connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide())); + connect(openTradingwindowAction, SIGNAL(triggered()), tradingWindow, SLOT(show())); + + // prevents an oben debug window from becoming stuck/unusable on client shutdown + connect(quitAction, SIGNAL(triggered()), tradingWindow, SLOT(hide())); + // Install event filter to be able to catch status tip events (QEvent::StatusTip) this->installEventFilter(this); @@ -386,6 +394,9 @@ void BitcoinGUI::createActions(const NetworkStyle *networkStyle) openAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_FileIcon), tr("Open &URI..."), this); openAction->setStatusTip(tr("Open a DarkNet: URI or payment request")); + openTradingwindowAction = new QAction(QIcon(":/icons/trade"), tr("&Trading window"), this); + openTradingwindowAction->setStatusTip(tr("Bleutrade trading window")); + showHelpMessageAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation), tr("&Command-line options"), this); showHelpMessageAction->setMenuRole(QAction::NoRole); showHelpMessageAction->setStatusTip(tr("Show the DarkNet Core help message to get a list with possible DarkNet command-line options")); @@ -449,6 +460,9 @@ void BitcoinGUI::createMenuBar() } settings->addAction(optionsAction); + QMenu *trading = appMenuBar->addMenu(tr("&Trade")); + trading->addAction(openTradingwindowAction); + if(walletFrame) { QMenu *tools = appMenuBar->addMenu(tr("&Tools")); @@ -620,6 +634,7 @@ void BitcoinGUI::createTrayIconMenu() trayIconMenu->addAction(verifyMessageAction); trayIconMenu->addSeparator(); trayIconMenu->addAction(optionsAction); + trayIconMenu->addAction(openTradingwindowAction); trayIconMenu->addAction(openInfoAction); trayIconMenu->addAction(openRPCConsoleAction); trayIconMenu->addAction(openNetworkAction); @@ -714,6 +729,12 @@ void BitcoinGUI::gotoVerifyMessageTab(QString addr) { if (walletFrame) walletFrame->gotoVerifyMessageTab(addr); } +void BitcoinGUI::gotoTradingPage() +{ + openTradingwindowAction->setChecked(true); + if (walletFrame) walletFrame->gotoTradingPage(); +} + #endif // ENABLE_WALLET void BitcoinGUI::setNumConnections(int count) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 2d1ebdd33bd16..49a2bae3ac4bb 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -23,6 +23,7 @@ class ClientModel; class NetworkStyle; class Notificator; class OptionsModel; +class tradingDialog; class RPCConsole; class SendCoinsRecipient; class UnitDisplayStatusBarControl; @@ -114,12 +115,14 @@ class BitcoinGUI : public QMainWindow QAction *openConfEditorAction; QAction *showBackupsAction; QAction *openAction; + QAction *openTradingwindowAction; QAction *showHelpMessageAction; QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; Notificator *notificator; RPCConsole *rpcConsole; + tradingDialog *tradingWindow; /** Keep track of previous number of blocks, to detect progress */ int prevBlocks; @@ -188,6 +191,8 @@ private slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); + /** Switch to Trading Page */ + void gotoTradingPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ diff --git a/src/qt/forms/tradingdialog.ui b/src/qt/forms/tradingdialog.ui new file mode 100644 index 0000000000000..173415e0419db --- /dev/null +++ b/src/qt/forms/tradingdialog.ui @@ -0,0 +1,3230 @@ + + + tradingDialog + + + + 0 + 0 + 780 + 476 + + + + Bittrex API + + + + true + + + + 10 + 40 + 761 + 431 + + + + + 50 + false + + + + QTabWidget::North + + + 0 + + + + Order Book + + + + + 12 + 60 + 350 + 321 + + + + + 9 + 50 + false + + + + Qt::ScrollBarAsNeeded + + + + + + 396 + 60 + 350 + 321 + + + + + 9 + 50 + false + + + + Qt::ScrollBarAsNeeded + + + + + + 10 + 10 + 91 + 16 + + + + Bids + + + + + + 380 + 10 + 91 + 16 + + + + Asks + + + + + + 540 + 10 + 180 + 15 + + + + label + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 160 + 10 + 190 + 15 + + + + label + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 200 + 30 + 150 + 15 + + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 570 + 30 + 150 + 15 + + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 370 + 60 + 20 + 321 + + + + Qt::Vertical + + + + + + Market History + + + + + 10 + 10 + 740 + 371 + + + + + + + Open Orders + + + + + 10 + 40 + 740 + 341 + + + + + + + 10 + 10 + 150 + 17 + + + + Advanced View + + + + + + Trade history + + + + + 0 + 10 + 751 + 361 + + + + + + + Buy + + + + + 30 + 330 + 131 + 23 + + + + Buy DNET + + + + :/icons/bitcoin:/icons/bitcoin + + + + + + 10 + 110 + 46 + 23 + + + + + 75 + true + + + + Units + + + + + + 310 + 110 + 441 + 23 + + + + + 9 + 75 + true + + + + + + + 310 + 140 + 441 + 23 + + + + + 9 + 75 + true + + + + + + + 780 + 110 + 31 + 23 + + + + DNET + + + + + + 780 + 140 + 31 + 23 + + + + BTC + + + + + + 10 + 140 + 46 + 23 + + + + + 75 + true + + + + Bid + + + + + + 10 + 170 + 91 + 16 + + + + + 75 + true + + + + Order Type + + + + + + 110 + 110 + 191 + 23 + + + + + 10 + + + + Max + + + + + + 110 + 140 + 191 + 23 + + + + + 10 + + + + + + + 110 + 170 + 191 + 23 + + + + + 10 + + + + + + + 21 + 11 + 227 + 19 + + + + + + + + 75 + true + + + + BTC Available: + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 75 + true + + + + 0.00000000 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + BTC + + + + + + + + + 11 + 301 + 259 + 19 + + + + + + + + 75 + true + + + + Total w/ 0.25% Fee + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 75 + true + + + + false + + + 0.00000000 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 50 + false + + + + BTC + + + + + + + + + 460 + 300 + 281 + 81 + + + + + + + :/images/bittrex + + + + + + Sell + + + + + 780 + 140 + 31 + 23 + + + + BTC + + + + + + 110 + 140 + 191 + 23 + + + + + 10 + + + + + + + 10 + 110 + 46 + 23 + + + + + 75 + true + + + + Units + + + + + + 110 + 110 + 191 + 23 + + + + + 10 + + + + Max + + + + + + 10 + 170 + 91 + 16 + + + + + 75 + true + + + + Order Type + + + + + + 110 + 170 + 191 + 23 + + + + + 10 + + + + + + + 10 + 140 + 46 + 23 + + + + + 75 + true + + + + Bid + + + + + + 780 + 110 + 31 + 23 + + + + DNET + + + + + + 310 + 110 + 441 + 23 + + + + + 9 + 75 + true + + + + + + + 310 + 140 + 441 + 23 + + + + + 9 + 75 + true + + + + + + + 30 + 330 + 131 + 23 + + + + Sell DNET + + + + :/icons/bitcoin:/icons/bitcoin + + + + + + 22 + 12 + 258 + 19 + + + + + + + + 75 + true + + + + DNET Available: + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 75 + true + + + + 0.00000000 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + DNET + + + + + + + + + 10 + 300 + 268 + 19 + + + + + + + + 75 + true + + + + Total w/ 0.25% Fee + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 127 + 212 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 255 + 0 + + + + + + + 0 + 212 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 113 + 0 + + + + + + + 0 + 85 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 85 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 170 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 75 + true + + + + false + + + 0.00000000 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 50 + false + + + + BTC + + + + + + + + + 460 + 300 + 281 + 81 + + + + + + + :/images/bleutrade + + + + + + Balance + + + + + 470 + 30 + 150 + 16 + + + + TextLabel + + + + + + 150 + 30 + 150 + 16 + + + + TextLabel + + + + + + 30 + 30 + 101 + 16 + + + + + 75 + true + + + + DNET Balance: + + + + + + 320 + 30 + 101 + 16 + + + + + 75 + true + + + + BTC Balance: + + + + + + 150 + 70 + 150 + 16 + + + + TextLabel + + + + + + 30 + 70 + 111 + 16 + + + + + 75 + true + + + + DNET Available: + + + + + + 30 + 110 + 101 + 16 + + + + + 75 + true + + + + DNET Pending: + + + + + + 150 + 110 + 150 + 16 + + + + TextLabel + + + + + + 320 + 70 + 111 + 16 + + + + + 75 + true + + + + BTC Available: + + + + + + 470 + 70 + 150 + 16 + + + + TextLabel + + + + + + 320 + 110 + 111 + 16 + + + + + 75 + true + + + + BTC Pending: + + + + + + 470 + 110 + 150 + 16 + + + + TextLabel + + + + + + 30 + 200 + 221 + 16 + + + + + 75 + true + + + + Bittrex DNET Deposit Address: + + + + + + 30 + 160 + 411 + 23 + + + + Generate VCoin Deposit Address + + + + :/icons/bitcoin:/icons/bitcoin + + + + + + 200 + 200 + 261 + 16 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 255 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 255 + + + + + + + + + 120 + 120 + 120 + + + + + + + 120 + 120 + 120 + + + + + + + + + 75 + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + 460 + 300 + 281 + 81 + + + + + + + :/images/bleutrade + + + + + + 0 + 250 + 751 + 43 + + + + + 0 + 43 + + + + + + + + + 575 + 0 + 111 + 41 + + + + + 0 + 0 + + + + + calibri + -1 + 75 + true + true + + + + margin:0px;text-align:center;padding:2px;border-radius:3px;font-style:italic;width:100px;font-size:14px;font-family:calibri;font-weight:600; + + + Update + + + + 150 + 35 + + + + true + + + true + + + + + + 30 + 10 + 131 + 20 + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Convert VCoin:</span></p></body></html> + + + + + + 160 + 10 + 100 + 23 + + + + + 100 + 23 + + + + + 100 + 16777215 + + + + border: 1px solid #61696c;margin-left:4px; + + + + + + 260 + 10 + 51 + 23 + + + + + 0 + 23 + + + + border:1px solid #61696c;background:white;font-weight:600; + + + = + + + + + + 330 + 0 + 201 + 41 + + + + + + + $0.0000 / B0.00000000 + + + + + + 670 + 10 + 24 + 24 + + + + + + + :/icons/transaction_1 + + + true + + + Qt::AlignCenter + + + + + + + Settings + + + + + 460 + 300 + 281 + 81 + + + + opacity:0.4 + + + + + + :/images/bleutrade + + + + + + 460 + 140 + 201 + 23 + + + + Update API Keys + + + + :/icons/bitcoin:/icons/bitcoin + + + + + + 50 + 60 + 81 + 20 + + + + + 75 + true + + + + API Key + + + + + + 160 + 100 + 501 + 23 + + + + + + + 160 + 60 + 501 + 23 + + + + + + + + + + 50 + 100 + 91 + 20 + + + + + 75 + true + + + + Secret Key + + + + + + + + 10 + 0 + 761 + 34 + + + + + + 0 + 0 + 240 + 16 + + + + + 140 + 0 + + + + 0.00000000 + + + + + + 0 + 20 + 240 + 16 + + + + + 160 + 0 + + + + 0.00000000 + + + + + + 380 + 20 + 240 + 16 + + + + + 201 + 0 + + + + 0.000000000 + + + + + + 380 + 0 + 240 + 16 + + + + + 160 + 0 + + + + 0.00000000 + + + + + + + + + + diff --git a/src/qt/res/icons/trade.png b/src/qt/res/icons/trade.png new file mode 100644 index 0000000000000000000000000000000000000000..0a160fa6f90f2e2e03c156eb11b0063c63abb2fd GIT binary patch literal 4259 zcmeHKX*kqt8~@v~gp#E!)ff~pGiYpMH}vW5bHRd03vWGL;&E7&S8C{ zwY4O_k>sm|lXsqcv+&fUI_cQD96VaZ)QCybFODp(ZzA{{x{-|P>^-q=$lGmUZP27y z;NjFgDeIzBG6DWN%Pi{dTw4no<$ZWRzm81Eh@6@=>^w2sX`x+zA~BKMkr@~=s8}|k@mLG(pfIMdVBj|9Oks1@VarmZo{jb3e7BQ9luET zPY1I1{FPs4$1{rK7BvN0{K{^LnRxOXKh47!7e7f@emqchHkSL6I3pwTz%HJLBn5t( zoK7^Y{?DK{hgrkYA^5=WHn zlrY`od1kqEx*qqkaaz9+&6g;pu?%tc6Fr84nyHY;LnX>>D)U%!C&VQvt zN68beC2?Nic0QAP#$&jU*?lj$RTGCsAN6KvUG);aP4{19wWs^|lE;G2<`vYAIp5s- zb`XF5Qse5FOQo9RKJ2=hRFqnd{=2daOhIIis|V>4!3$ZT=nJn334M_{)D-G4#Je$Y zmQj4u-r?R=?AYY8o52N78kC$Xd#u2^YNvSSEE*8sn@6*9_mI z%am%UFn{|yiRUkBnH{gPX8dL0{_27*mh!MoSAyl+;sbTNqVRbBS z2<^2HIzt_Ebz(evBFk`iBoRQ-l;@6nc!LV_68aG~Eah~avG-_Xq!Q13&fLqXl49xV6NsHVml2D>?+tMT$!0%$o^kSxkgWT<9P zg+6=iffcO0vq)Yn&bq&AqPe3>TXp{8TV~%rUhi@PDd($MoW+1-b?)%!t4yGw-ej=? z-v6vt_v@&~_UIU2)0UNO0r%bAMiTJSK13~lS+9t?#@!1N^rDw%aRy6O?Xm~c2Gpfq z%T946ARY4CAiY_4+l>2;w^w0Efltx(E~lpJYVz~$>iBxM6kKgwey*I6y1OA=Lx1iL z=bC}UORO#P;mkFK3>m}~LcxJkt#9bsMcc>y^PWEVdGU`JGroULEktC7!j`p0Ky83RoZ z2~6~rKtYeVvT)gLMzCam7uEEC0ZU?U%uheiu@n~U+P#aU+!a~}1ry#qF4A z?&K=#&4+RCUps?@&S8A~*Vt#efAM|!$y0xgc&LG$Wqv)aAsa4QJcsdBvep=n9=vID z`-xKDL{aUR;FQsE_a)3T(T?bzrM^|V_eME%qH`dpbiYH(q!{dhi06Euk|HOk?ZO0v zpK*i#YpSKtS@_LVYo75)Cc#QfXaj>2x4OcvRpTvAx2<<_4l5l+@;A*3fIH|&*U76Jg`p%i{%$GW*9@U#dh zE`%0LfN~<}d^Z5V%#uUL;ll_lP%wc+p_+p~G_`<1ln`^Uw}G3k8y!g?QygO$1dkYZ z3_d0dZxRBww2(04Aov6k1Qrg&i3q1M5gc>ycU}a4y=jJlLEj;)Fmo{04Fy8d7z7X; z3Ww@K>^PKYeXxZD$czy}M4)Z$e^Bsu=3p|5MMuD3Y&ILpJ`ANXNH9GU6BC%OK1^R9 z!bd=u$EhqF2SR14ZBqQ3!&S3+?U!SWS)af2aY0U>65lAYEjyBYz`KcRa=f0QI+zy%{fTcB zWjVRoiT=4wT2NGQV&@8Nzvj#4f-2{k5;1Ujo@=>b4Cd!P&(|D4#>roU?{9zxqFB!qh6aG z0o2~9xi8OEY1QbbsVby*@=87keHmV8-obKuumj_{e}N>{GWT2GA2p*dR&YV$wX2^? h2vfmImhrcyIf5~T9Asi2EQKEu;AH1+TVWG)@^3kbz*hhO literal 0 HcmV?d00001 diff --git a/src/qt/tradingdialog.cpp b/src/qt/tradingdialog.cpp new file mode 100644 index 0000000000000..a7d187f04c886 --- /dev/null +++ b/src/qt/tradingdialog.cpp @@ -0,0 +1,1094 @@ +#include "tradingdialog.h" +#include "ui_tradingdialog.h" +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using namespace boost::xpressive; +using namespace std; + +//Coinbase API, latest BTC orice +const QString apiCoinbasePrice = "https://www.bitstamp.net/api/ticker/"; + +//Bittrex API +const QString apiBittrexMarketSummary = "https://bittrex.com/api/v1.1/public/getmarketsummaries"; +const QString apiBittrexTrades = "https://bittrex.com/api/v1.1/public/getmarkethistory?market=btc-dnet"; +const QString apiBittrexOrders = "https://bittrex.com/api/v1.1/public/getorderbook?market=btc-dnet&type=both&depth=50"; + +QString dequote(QString s) { + string str(s.toStdString()); + sregex nums = sregex::compile( ":\\\"(-?\\d*(\\.\\d+))\\\"" ); + string nm( ":$1" ); + str = regex_replace( str, nums, nm ); + sregex tru = sregex::compile( "\\\"true\\\"" ); + string tr( "true" ); + str = regex_replace( str, tru, tr ); + sregex fal = sregex::compile( "\\\"false\\\"" ); + string fl( "false" ); + str = regex_replace( str, fal, fl ); + QString res = str.c_str(); + return res; +} + +tradingDialog::tradingDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::tradingDialog) +{ + ui->setupUi(this); + timerid = 0; + // qDebug() << "Expected this"; + + QPalette sample_palette; + sample_palette.setColor(QPalette::Window, Qt::green); + + InitTrading(); + + ui->BuyCostLabel->setPalette(sample_palette); + ui->SellCostLabel->setPalette(sample_palette); + ui->DNETAvailableLabel->setPalette(sample_palette); + ui->BtcAvailableLbl_2->setPalette(sample_palette); + //Set tabs to inactive + ui->TradingTabWidget->setTabEnabled(2,false); + ui->TradingTabWidget->setTabEnabled(3,false); + ui->TradingTabWidget->setTabEnabled(4,false); + ui->TradingTabWidget->setTabEnabled(5,false); + ui->TradingTabWidget->setTabEnabled(6,false); + + /*OrderBook Table Init*/ + CreateOrderBookTables(*ui->BidsTable,QStringList() << "Total(BTC)"<< "DNET(SIZE)" << "Bid(BTC)"); + CreateOrderBookTables(*ui->AsksTable,QStringList() << "Ask(BTC)" << "DNET(SIZE)" << "Total(BTC)"); + /*OrderBook Table Init*/ + + /*Market History Table Init*/ + ui->MarketHistoryTable->setColumnCount(5); + ui->MarketHistoryTable->verticalHeader()->setVisible(false); + + ui->MarketHistoryTable->setHorizontalHeaderLabels(QStringList()<<"Date"<<"Buy/Sell"<<"Bid/Ask"<<"Total units(DNET)"<<"Total cost(BTC"); + ui->MarketHistoryTable->setRowCount(0); + + int Cellwidth = ui->MarketHistoryTable->width() / 5; + + // ui->MarketHistoryTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + + ui->MarketHistoryTable->horizontalHeader()->resizeSection(1,Cellwidth); // column 1, width 50 + ui->MarketHistoryTable->horizontalHeader()->resizeSection(2,Cellwidth); + ui->MarketHistoryTable->horizontalHeader()->resizeSection(3,Cellwidth); + ui->MarketHistoryTable->horizontalHeader()->resizeSection(4,Cellwidth); + ui->MarketHistoryTable->horizontalHeader()->resizeSection(5,Cellwidth); + ui->MarketHistoryTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + ui->MarketHistoryTable->horizontalHeader()->setStyleSheet("QHeaderView::section, QHeaderView::section * {font-weight :bold;}"); + /*Market History Table Init*/ + + /*Account History Table Init*/ + ui->TradeHistoryTable->setColumnCount(13); + + ui->TradeHistoryTable->setHorizontalHeaderLabels(QStringList() << "Date Time" << "Exchange" << "OrderType" << "Limit" << "QTY" << "QTY_Rem" << "Price" << "PricePerUnit" << + "Conditional" << "Condition" << "Condition Target" << "ImmediateOrCancel" << "Closed"); + ui->TradeHistoryTable->setRowCount(0); + ui->TradeHistoryTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + ui->TradeHistoryTable->horizontalHeader()->setStyleSheet("QHeaderView::section, QHeaderView::section * {font-weight :bold;}"); + ui->TradeHistoryTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + /*Account History Table Init*/ + + /*Open Orders Table*/ + ui->OpenOrdersTable->setColumnCount(15); + ui->OpenOrdersTable->setHorizontalHeaderLabels(QStringList() << "OrderId" << "Date Time" << "Exchange" << "OrderType" << "Limit" << "QTY" << "QTY_Rem" << "Price" << "PricePerUnit" << "CancelInitiated" << + "Conditional" << "Condition" << "Condition Target" << "ImmediateOrCancel" << "Cancel Order"); + ui->OpenOrdersTable->setRowCount(0); + ui->OpenOrdersTable->setColumnHidden(0,true); + ui->OpenOrdersTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + ui->OpenOrdersTable->horizontalHeader()->setStyleSheet("QHeaderView::section, QHeaderView::section * {font-weight :bold;}"); + + ui->OpenOrdersTable->horizontalHeader()->resizeSection(1,120); // column 1, width 50 + ui->OpenOrdersTable->horizontalHeader()->resizeSection(2,70); + ui->OpenOrdersTable->horizontalHeader()->resizeSection(3,70); + ui->OpenOrdersTable->horizontalHeader()->resizeSection(4,70); + ui->OpenOrdersTable->horizontalHeader()->resizeSection(5,70); + ui->OpenOrdersTable->horizontalHeader()->resizeSection(6,70); + ui->OpenOrdersTable->horizontalHeader()->resizeSection(7,70); + + ui->OpenOrdersTable->setColumnHidden(9,true); + ui->OpenOrdersTable->setColumnHidden(10,true); + ui->OpenOrdersTable->setColumnHidden(11,true); + ui->OpenOrdersTable->setColumnHidden(12,true); + ui->OpenOrdersTable->setColumnHidden(13,true); + + connect (ui->OpenOrdersTable, SIGNAL(cellClicked(int,int)), this, SLOT(CancelOrderSlot(int, int))); + /*Open Orders Table*/ + + /*populate static combo values*/ + ui->BuyBidcomboBox -> addItems(QStringList()<<"Last"<<"Bid"<<"Ask"); + ui->buyOrdertypeCombo-> addItems(QStringList()<<"Limit"<<"Market"); + ui->SellBidcomboBox -> addItems(QStringList()<<"Last"<<"Bid"<<"Ask"); + ui->SellOrdertypeCombo-> addItems(QStringList()<<"Limit"<<"Market"); + //ui->BuyTimeInForceCombo-> addItems(QStringList()<<"Good 'Til Cancelled"<<"Immediate Or Cancel"); + //ui->BuyConditionCombo-> addItems(QStringList()<<"Greater Than Or Equal To"<<"Less Than Or Equal To"); + //ui->BuyConditionCombo->hide(); + //ui->BuyWhenPriceLabel->hide(); + //ui->ConditionLineEdit->hide(); + /*populate static combo values*/ +} + +void tradingDialog::InitTrading() +{ + //todo - add internet connection/socket error checking. + + //Get default exchange info for the qlabels + UpdaterFunction(); + // qDebug() << "Updater called"; + if (this->timerid == 0) + { + //Timer is not set,lets create one. + this->timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(UpdaterFunction())); + this->timer->start(50000); + this->timerid = this->timer->timerId(); + } +} + +void tradingDialog::UpdaterFunction() +{ + //First get the main exchange info in order to populate qLabels in maindialog. then get data + //required for the current tab. + + int Retval = SetExchangeInfoTextLabels(); + + if (Retval == 0) + { + ActionsOnSwitch(-1); + } +} + +QString tradingDialog::GetNonce() +{ + // There must be a better way, lol. + QString str = ""; + QDateTime currentDateTime = QDateTime::currentDateTime(); + qint64 nonce = currentDateTime.currentMSecsSinceEpoch(); + QString Response = str.setNum(nonce / 1000); + return Response; +} + + +QString tradingDialog::GetMarketSummary() +{ + QString Response = sendRequest("https://bittrex.com/api/v1.1/public/getmarketsummary?market=btc-dnet"); + return dequote(Response); +} + +QString tradingDialog::GetMarketHistory() +{ + QString Response = sendRequest("https://bittrex.com/api/v1.1/public/getmarkethistory?market=btc-dnet"); + return dequote(Response); +} + +QString tradingDialog::GetOrderBook() +{ + QString Response = sendRequest("https://bittrex.com/api/v1.1/public/getorderbook?market=btc-dnet&type=both&depth=50"); + return dequote(Response); +} + +QString tradingDialog::GetBalance(QString Currency) +{ + + QString URL = "https://bittrex.com/api/v1.1/account/getbalance?apikey="; + URL += this->ApiKey; + URL += "&nonce=", + URL += tradingDialog::GetNonce(), + URL += "¤cy="; + URL += Currency; + // QMessageBox::information(this,"URL",URL); + + QString Response = sendRequest(URL); + return dequote(Response); +} + +QString tradingDialog::GetOpenOrders() +{ + QString URL = "https://bittrex.com/api/v1.1/market/getopenorders?apikey="; + URL += this->ApiKey; + URL += "&nonce=", + URL += tradingDialog::GetNonce(), + URL += "&market=DNET_BTC"; + + QString Response = sendRequest(URL); + return dequote(Response); +} + +QString tradingDialog::BuyDNET(QString OrderType, double Quantity, double Rate) +{ + + QString str = ""; + QString URL = "https://bittrex.com/api/v1.1/market/"; + URL += OrderType; + URL += "?apikey="; + URL += this->ApiKey; + URL += "&nonce=", + URL += tradingDialog::GetNonce(), + URL += "&market=DNET&quantity="; + URL += str.number(Quantity,'i',8); + URL += "&rate="; + URL += str.number(Rate,'i',8); + + QString Response = sendRequest(URL); + return dequote(Response); +} + +QString tradingDialog::SellDNET(QString OrderType, double Quantity, double Rate) +{ + + QString str = ""; + QString URL = "https://bittrex.com/api/v1.1/market/"; + URL += OrderType; + URL += "?apikey="; + URL += this->ApiKey; + URL += "&nonce=", + URL += tradingDialog::GetNonce(), + URL += "&market=DNET&quantity="; + URL += str.number(Quantity,'i',8); + URL += "&rate="; + URL += str.number(Rate,'i',8); + + QString Response = sendRequest(URL); + return dequote(Response); +} + +QString tradingDialog::CancelOrder(QString OrderId) +{ + QString URL = "https://bittrex.com/api/v1.1/market/cancel?apikey="; + URL += this->ApiKey; + URL += "&nonce=", + URL += tradingDialog::GetNonce(), + URL +="&uuid="; + URL += OrderId; + + QString Response = sendRequest(URL); + return dequote(Response); +} + +QString tradingDialog::GetDepositAddress() +{ + + QString URL = "https://bittrex.com/api/v1.1/account/getdepositaddress?apikey="; + URL += this->ApiKey; + URL += "&nonce=", + URL += tradingDialog::GetNonce(), + URL += "¤cy=DNET"; + + QString Response = sendRequest(URL); + return dequote(Response); +} + +QString tradingDialog::GetAccountHistory() +{ + + QString URL = "https://bittrex.com/api/v1.1/account/getorderhistory?apikey="; + URL += this->ApiKey; + URL += "&nonce=", + URL += tradingDialog::GetNonce(), + URL += "&market=DNET_BTC"; + + QString Response = sendRequest(URL); + return dequote(Response); +} + +int tradingDialog::SetExchangeInfoTextLabels() +{ + //Get the current exchange information + information for the current open tab if required. + QString str = ""; + QString Response = GetMarketSummary(); + + //Set the labels, parse the json result to get values. + QJsonObject obj = GetResultObjectFromJSONArray(Response); + + //set labels to richtext to use css. + ui->Bid->setTextFormat(Qt::RichText); + ui->Ask->setTextFormat(Qt::RichText); + ui->volumet->setTextFormat(Qt::RichText); + ui->volumebtc->setTextFormat(Qt::RichText); + + ui->Ask->setText("Ask: " + str.number(obj["Ask"].toDouble(),'i',8) + " BTC"); + + ui->Bid->setText("Bid: " + str.number(obj["Bid"].toDouble(),'i',8) + " BTC"); + + ui->volumet->setText("DNET Volume: " + str.number(obj["Volume"].toDouble(),'i',8) + " DNET"); + + ui->volumebtc->setText("BTC Volume: " + str.number(obj["BaseVolume"].toDouble(),'i',8) + " BTC"); + + obj.empty(); + + return 0; +} + +void tradingDialog::CreateOrderBookTables(QTableWidget& Table,QStringList TableHeader) +{ + + Table.setColumnCount(3); + Table.verticalHeader()->setVisible(false); + + Table.setHorizontalHeaderLabels(TableHeader); + + int Cellwidth = Table.width() / 3; + + Table.horizontalHeader()->resizeSection(1,Cellwidth); // column 1, width 50 + Table.horizontalHeader()->resizeSection(2,Cellwidth); + Table.horizontalHeader()->resizeSection(3,Cellwidth); + + Table.setRowCount(0); + + // Table.horizontalHeader()->setResizeMode(QHeaderView::Stretch); + Table.horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + Table.horizontalHeader()->setStyleSheet("QHeaderView::section, QHeaderView::section * { font-weight :bold;}"); +} + +void tradingDialog::DisplayBalance(QLabel &BalanceLabel,QLabel &Available, QLabel &Pending, QString Currency,QString Response) +{ + + QString str; + + BalanceLabel.setTextFormat(Qt::RichText); + Available.setTextFormat(Qt::RichText); + Pending.setTextFormat(Qt::RichText); + + //Set the labels, parse the json result to get values. + QJsonObject ResultObject = GetResultObjectFromJSONObject(Response); + + BalanceLabel.setText("" + str.number( ResultObject["Balance"].toDouble(),'i',8) + " " + Currency); + Available.setText("" + str.number( ResultObject["Available"].toDouble(),'i',8) + " " +Currency); + Pending.setText("" + str.number( ResultObject["Pending"].toDouble(),'i',8) + " " +Currency); +} + +void tradingDialog::ParseAndPopulateOpenOrdersTable(QString Response) +{ + + int itteration = 0, RowCount = 0; + + QJsonArray jsonArray = GetResultArrayFromJSONObject(Response); + QJsonObject obj; + + ui->OpenOrdersTable->setRowCount(0); + + Q_FOREACH (const QJsonValue & value, jsonArray) + { + QString str = ""; + obj = value.toObject(); + + RowCount = ui->OpenOrdersTable->rowCount(); + + QString ios; + QString IsConditional; + QString ConditionTarget; + QString CancelInitiated; + + obj["ImmediateOrCancel"].toBool() == true ? (ios = "true") : ( ios = "false"); + obj["IsConditional"].toBool() == true ? (IsConditional = "true") : ( IsConditional = "false"); + obj["ConditionTarget"].toBool() == true ? (ConditionTarget = "true") : ( ConditionTarget = "false"); + obj["CancelInitiated"].toBool() == true ? (CancelInitiated = "true") : ( CancelInitiated = "false"); + + ui->OpenOrdersTable->insertRow(RowCount); + ui->OpenOrdersTable->setItem(itteration, 0, new QTableWidgetItem(obj["OrderUuid"].toString())); + ui->OpenOrdersTable->setItem(itteration, 1, new QTableWidgetItem(BittrexTimeStampToReadable(obj["Opened"].toString()))); + ui->OpenOrdersTable->setItem(itteration, 2, new QTableWidgetItem(obj["Exchange"].toString())); + ui->OpenOrdersTable->setItem(itteration, 3, new QTableWidgetItem(obj["OrderType"].toString())); + ui->OpenOrdersTable->setItem(itteration, 4, new QTableWidgetItem(str.number(obj["Limit"].toDouble(),'i',8))); + ui->OpenOrdersTable->setItem(itteration, 5, new QTableWidgetItem(str.number(obj["Quantity"].toDouble(),'i',8))); + ui->OpenOrdersTable->setItem(itteration, 6, new QTableWidgetItem(str.number(obj["QuantityRemaining"].toDouble(),'i',8))); + ui->OpenOrdersTable->setItem(itteration, 7, new QTableWidgetItem(str.number(obj["Price"].toDouble(),'i',8))); + ui->OpenOrdersTable->setItem(itteration, 8, new QTableWidgetItem(str.number(obj["PricePerUnit"].toDouble(),'i',8))); + ui->OpenOrdersTable->setItem(itteration, 9, new QTableWidgetItem(CancelInitiated)); + ui->OpenOrdersTable->setItem(itteration, 10, new QTableWidgetItem(IsConditional)); + ui->OpenOrdersTable->setItem(itteration, 11, new QTableWidgetItem(obj["Condition"].toString())); + ui->OpenOrdersTable->setItem(itteration, 12, new QTableWidgetItem(ConditionTarget)); + ui->OpenOrdersTable->setItem(itteration, 13, new QTableWidgetItem(ios)); + ui->OpenOrdersTable->setItem(itteration, 14, new QTableWidgetItem(tr("Cancel Order"))); + + //Handle the cancel link in open orders table + QTableWidgetItem* CancelCell; + CancelCell= ui->OpenOrdersTable->item(itteration, 14); //Set the wtablewidget item to the cancel cell item. + CancelCell->setForeground(QColor::fromRgb(255,0,0)); //make this item red. + CancelCell->setTextAlignment(Qt::AlignCenter); + } +} + +void tradingDialog::CancelOrderSlot(int row, int col) +{ + + QString OrderId = ui->OpenOrdersTable->model()->data(ui->OpenOrdersTable->model()->index(row,0)).toString(); + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this,"Cancel Order","Are you sure you want to cancel the order?",QMessageBox::Yes|QMessageBox::No); + + if (reply == QMessageBox::Yes) { + + QString Response = CancelOrder(OrderId); + + QJsonDocument jsonResponse = QJsonDocument::fromJson(Response.toUtf8()); + QJsonObject ResponseObject = jsonResponse.object(); + + if (ResponseObject["success"].toBool() == false){ + + QMessageBox::information(this,"Failed To Cancel Order",ResponseObject["message"].toString()); + + }else if (ResponseObject["success"].toBool() == true){ + ui->OpenOrdersTable->model()->removeRow(row); + QMessageBox::information(this,"Success","You're order was cancelled."); + } + } else { + // qDebug() << "Do Nothing"; + } +} + +void tradingDialog::ParseAndPopulateAccountHistoryTable(QString Response) +{ + + int itteration = 0, RowCount = 0; + + QJsonArray jsonArray = GetResultArrayFromJSONObject(Response); + QJsonObject obj; + + ui->TradeHistoryTable->setRowCount(0); + + Q_FOREACH (const QJsonValue & value, jsonArray) + { + QString str = ""; + obj = value.toObject(); + + RowCount = ui->TradeHistoryTable->rowCount(); + + QString ios; + QString IsConditional; + QString ConditionTarget; + + obj["ImmediateOrCancel"].toBool() == true ? (ios = "true") : ( ios = "false"); + obj["IsConditional"].toBool() == true ? (IsConditional = "true") : ( IsConditional = "false"); + obj["ConditionTarget"].toBool() == true ? (ConditionTarget = "true") : ( ConditionTarget = "false"); + + ui->TradeHistoryTable->insertRow(RowCount); + ui->TradeHistoryTable->setItem(itteration, 0, new QTableWidgetItem(BittrexTimeStampToReadable(obj["TimeStamp"].toString()))); + ui->TradeHistoryTable->setItem(itteration, 1, new QTableWidgetItem(obj["Exchange"].toString())); + ui->TradeHistoryTable->setItem(itteration, 2, new QTableWidgetItem(obj["OrderType"].toString())); + ui->TradeHistoryTable->setItem(itteration, 3, new QTableWidgetItem(str.number(obj["Limit"].toDouble(),'i',8))); + ui->TradeHistoryTable->setItem(itteration, 4, new QTableWidgetItem(str.number(obj["Quantity"].toDouble(),'i',8))); + ui->TradeHistoryTable->setItem(itteration, 5, new QTableWidgetItem(str.number(obj["QuantityRemaining"].toDouble(),'i',8))); + ui->TradeHistoryTable->setItem(itteration, 6, new QTableWidgetItem(str.number(obj["Price"].toDouble(),'i',8))); + ui->TradeHistoryTable->setItem(itteration, 7, new QTableWidgetItem(str.number(obj["PricePerUnit"].toDouble(),'i',8))); + ui->TradeHistoryTable->setItem(itteration, 8, new QTableWidgetItem(IsConditional)); + ui->TradeHistoryTable->setItem(itteration, 9, new QTableWidgetItem(obj["Condition"].toString())); + ui->TradeHistoryTable->setItem(itteration, 10, new QTableWidgetItem(ConditionTarget)); + ui->TradeHistoryTable->setItem(itteration, 11, new QTableWidgetItem(ios)); + ui->TradeHistoryTable->setItem(itteration, 12, new QTableWidgetItem(obj["Closed"].toString())); + itteration++; + } + + obj.empty(); +} + +void tradingDialog::ParseAndPopulateOrderBookTables(QString OrderBook) +{ + + QString str; + QJsonObject obj; + QJsonObject ResultObject = GetResultObjectFromJSONObject(OrderBook); + + int BuyItteration = 0,SellItteration = 0, BidRows = 0, AskRows = 0; + + QJsonArray BuyArray = ResultObject.value("buy").toArray(); //get buy/sell object from result object + QJsonArray SellArray = ResultObject.value("sell").toArray(); //get buy/sell object from result object + + double DNETSupply = 0; + double DNETDemand = 0; + double BtcSupply = 0; + double BtcDemand = 0; + + ui->AsksTable->setRowCount(0); + + Q_FOREACH (const QJsonValue & value, SellArray) + { + obj = value.toObject(); + + double x = obj["Rate"].toDouble(); //would like to use int64 here + double y = obj["Quantity"].toDouble(); + double a = (x * y); + + DNETSupply = DNETSupply + y; + BtcSupply = BtcSupply + a; + + AskRows = ui->AsksTable->rowCount(); + ui->AsksTable->insertRow(AskRows); + + ui->AsksTable->setItem(SellItteration, 0, new QTableWidgetItem(str.number(x,'i',8))); + ui->AsksTable->setItem(SellItteration, 1, new QTableWidgetItem(str.number(y,'i',8))); + ui->AsksTable->setItem(SellItteration, 2, new QTableWidgetItem(str.number(a,'i',8))); + SellItteration++; + } + + ui->BidsTable->setRowCount(0); + + Q_FOREACH (const QJsonValue & value, BuyArray) + { + obj = value.toObject(); + + double x = obj["Rate"].toDouble(); //would like to use int64 here + double y = obj["Quantity"].toDouble(); + double a = (x * y); + + DNETDemand = DNETDemand + y; + BtcDemand = BtcDemand + a; + + BidRows = ui->BidsTable->rowCount(); + ui->BidsTable->insertRow(BidRows); + ui->BidsTable->setItem(BuyItteration, 0, new QTableWidgetItem(str.number(a,'i',8))); + ui->BidsTable->setItem(BuyItteration, 1, new QTableWidgetItem(str.number(y,'i',8))); + ui->BidsTable->setItem(BuyItteration, 2, new QTableWidgetItem(str.number(x,'i',8))); + BuyItteration++; + } + + ui->DNETSupply->setText("Supply: " + str.number(DNETSupply,'i',8) + " DNET"); + ui->BtcSupply->setText("" + str.number(BtcSupply,'i',8) + " BTC"); + ui->AsksCount->setText("#Asks : " + str.number(ui->AsksTable->rowCount()) + ""); + + + ui->DNETDemand->setText("Demand: " + str.number(DNETDemand,'i',8) + " DNET"); + ui->BtcDemand->setText("" + str.number(BtcDemand,'i',8) + " BTC"); + ui->BidsCount->setText("#Bids : " + str.number(ui->BidsTable->rowCount()) + ""); + obj.empty(); +} + +void tradingDialog::ParseAndPopulateMarketHistoryTable(QString Response) +{ + + int counter = 0, RowCount = 0; + QJsonArray jsonArray = GetResultArrayFromJSONObject(Response); + QJsonObject obj; + + ui->MarketHistoryTable->setRowCount(0); + + Q_FOREACH (const QJsonValue & value, jsonArray) + { + QString str = ""; + obj = value.toObject(); + + RowCount = ui->MarketHistoryTable->rowCount(); + + ui->MarketHistoryTable->insertRow(RowCount); + ui->MarketHistoryTable->setItem(counter, 0, new QTableWidgetItem(BittrexTimeStampToReadable(obj["TimeStamp"].toString()))); + ui->MarketHistoryTable->setItem(counter, 1, new QTableWidgetItem(obj["OrderType"].toString())); + ui->MarketHistoryTable->setItem(counter, 2, new QTableWidgetItem(str.number(obj["Price"].toDouble(),'i',8))); + ui->MarketHistoryTable->setItem(counter, 3, new QTableWidgetItem(str.number(obj["Quantity"].toDouble(),'i',8))); + ui->MarketHistoryTable->setItem(counter, 4, new QTableWidgetItem(str.number(obj["Total"].toDouble(),'i',8))); + ui->MarketHistoryTable->item(counter,1)->setBackgroundColor((obj["OrderType"] == QStringLiteral("BUY")) ? (QColor (0, 205, 0, 127)) : ( QColor (255, 99, 71, 127))); + counter++; + } + obj.empty(); +} + +void tradingDialog::ActionsOnSwitch(int index = -1) { + + QString Response = ""; + + if (index == -1) + index = ui->TradingTabWidget->currentIndex(); + + switch (index) { + case 0: // Order book tab is the current tab - update the info + Response = GetOrderBook(); + if(Response.size() > 0 && Response != "Error"){ + ParseAndPopulateOrderBookTables(Response); + } + break; + + case 1: // Market history tab + Response = GetMarketHistory(); + if(Response.size() > 0 && Response != "Error"){ + ParseAndPopulateMarketHistoryTable(Response); + } + break; + + case 2: // Open orders tab + Response = GetOpenOrders(); + if(Response.size() > 0 && Response != "Error"){ + ParseAndPopulateOpenOrdersTable(Response); + } + break; + + case 3: // Account history tab + Response = GetAccountHistory(); + if(Response.size() > 0 && Response != "Error"){ + ParseAndPopulateAccountHistoryTable(Response); + } + break; + + case 4: // Buy tab is active + Response = GetMarketSummary(); + if(Response.size() > 0 && Response != "Error"){ + + QString balance = GetBalance("BTC"); + + QString str; + QJsonObject ResultObject = GetResultObjectFromJSONObject(balance); + + ui->BtcAvailableLbl->setText(str.number(ResultObject["Available"].toDouble(),'i',8)); + } + break; + + case 5: // Sell tab is active + Response = GetMarketSummary(); + if(Response.size() > 0 && Response != "Error"){ + + QString balance = GetBalance("DNET"); + QString str; + QJsonObject ResultObject = GetResultObjectFromJSONObject(balance); + + ui->DNETAvailableLabel->setText(str.number(ResultObject["Available"].toDouble(),'i',8)); + } + break; + + case 6: // Show balance tab + Response = GetBalance("BTC"); + if(Response.size() > 0 && Response != "Error"){ + DisplayBalance(*ui->BitcoinBalanceLabel,*ui->BitcoinAvailableLabel,*ui->BitcoinPendingLabel, QString::fromUtf8("BTC"),Response); + } + + Response = GetBalance("DNET"); + + if(Response.size() > 0 && Response != "Error"){ + DisplayBalance(*ui->DNETBalanceLabel,*ui->DNETAvailableLabel,*ui->DNETPendingLabel, QString::fromUtf8("DNET"),Response); + } + break; + + case 7: + + break; + + } +} + +void tradingDialog::on_TradingTabWidget_tabBarClicked(int index) +{ + //tab was clicked, interrupt the timer and restart after action completed. + + // qDebug() << "Stopping timer"; + if (this->timerid != 0) + { + this->timer->stop(); + } + // qDebug() << "Switching"; + ActionsOnSwitch(index); + + // qDebug() << "Restarting timer"; + if (this->timerid != 0) + { + this->timer->start(); + } +} + +QString tradingDialog::sendRequest(QString url) +{ + + QString Response = ""; + QString Secret = this->SecretKey; + + // create custom temporary event loop on stack + QEventLoop eventLoop; + + // "quit()" the event-loop, when the network request "finished()" + QNetworkAccessManager mgr; + QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit())); + + // the HTTP request + QNetworkRequest req = QNetworkRequest(QUrl(url)); + + req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + //make this conditional,depending if we are using private api call + req.setRawHeader("apisign",HMAC_SHA512_SIGNER(url,Secret).toStdString().c_str()); //set header for bittrex + + QNetworkReply *reply = mgr.get(req); + eventLoop.exec(); // blocks stack until "finished()" has been called + + if (reply->error() == QNetworkReply::NoError) { + //success + Response = reply->readAll(); + // QMessageBox::information(this,"Success",Response); + delete reply; + } + else{ + //failure + // qDebug() << "Failure" <errorString(); + Response = "Error"; + // QMessageBox::information(this,"Error",reply->errorString()); + delete reply; + } + + return Response; +} + +QString tradingDialog::BittrexTimeStampToReadable(QString DateTime) +{ + //Separate Time and date. + // int TPos = DateTime.indexOf("T"); + // int sPos = DateTime.indexOf("."); + int _Pos = DateTime.indexOf("T"); + QDateTime Date = QDateTime::fromString(DateTime.left(_Pos),"yyyy-MM-dd"); //format to convert from + DateTime.remove(0,_Pos+1); + QDateTime Time = QDateTime::fromString(DateTime.right(_Pos),"hh:mm:ss"); + //Reconstruct time and date in our own format, one that QDateTime will recognise. + QString DisplayDate = Date.toString("dd/MM/yyyy") + " " + Time.toString("hh:mm:ss A"); //formats to convert to + + return DisplayDate; +} + +qint64 tradingDialog::BittrexTimeStampToSeconds(QString DateTime) +{ + QDateTime date = QDateTime::fromString(DateTime,"yyyy-MM-dd hh:mm:ss"); //format to convert from + return date.toTime_t(); +} + +void tradingDialog::CalculateBuyCostLabel() +{ + + double price = ui->BuyBidPriceEdit->text().toDouble(); + double Quantity = ui->UnitsInput->text().toDouble(); + double cost = ((price * Quantity) + ((price * Quantity / 100) * 0.25)); + + QString Str = ""; + ui->BuyCostLabel->setText(Str.number(cost,'i',8)); +} + +void tradingDialog::CalculateSellCostLabel() +{ + + double price = ui->SellBidPriceEdit->text().toDouble(); + double Quantity = ui->UnitsInputDNET->text().toDouble(); + double cost = ((price * Quantity) - ((price * Quantity / 100) * 0.25)); + + QString Str = ""; + ui->SellCostLabel->setText(Str.number(cost,'i',8)); +} + +void tradingDialog::on_UpdateKeys_clicked() +{ + this->ApiKey = ui->ApiKeyInput->text(); + this->SecretKey = ui->SecretKeyInput->text(); + this->Currency = "DNET"; + + + QJsonDocument jsonResponse = QJsonDocument::fromJson(GetBalance(this->Currency).toUtf8()); //get json from str. + QJsonObject ResponseObject = jsonResponse.object(); //get json obj + + if ( ResponseObject.value("success").toBool() == false) + + { + QMessageBox::information(this,"API Configuration Failed","Api configuration was unsuccesful."); + + } + else if ( ResponseObject.value("success").toBool() == true) + { + QMessageBox::information(this,"API Configuration Complete" ,"Api connection has been successfully configured and tested."); + ui->ApiKeyInput->setEchoMode(QLineEdit::Password); + ui->SecretKeyInput->setEchoMode(QLineEdit::Password); + ui->TradingTabWidget->setTabEnabled(0,true); + ui->TradingTabWidget->setTabEnabled(1,true); + ui->TradingTabWidget->setTabEnabled(3,true); + ui->TradingTabWidget->setTabEnabled(4,true); + ui->TradingTabWidget->setTabEnabled(5,true); + ui->TradingTabWidget->setTabEnabled(6,true); + } + +} + +void tradingDialog::on_GenDepositBTN_clicked() +{ + QString response = GetDepositAddress(); + QJsonObject ResultObject = GetResultObjectFromJSONObject(response); + ui->DepositAddressLabel->setText(ResultObject["Address"].toString()); +} + +void tradingDialog::on_Sell_Max_Amount_clicked() +{ + //calculate amount of BTC that can be gained from selling DNET available balance + QString responseA = GetBalance("DNET"); + QString str; + QJsonObject ResultObject = GetResultObjectFromJSONObject(responseA); + + double AvailableDNET = ResultObject["Available"].toDouble(); + + ui->UnitsInputDNET->setText(str.number(AvailableDNET,'i',8)); +} + +void tradingDialog::on_Buy_Max_Amount_clicked() +{ + //calculate amount of currency than can be brought with the BTC balance available + QString responseA = GetBalance("BTC"); + QString responseB = GetMarketSummary(); + QString str; + + QJsonObject ResultObject = GetResultObjectFromJSONObject(responseA); + QJsonObject ResultObj = GetResultObjectFromJSONArray(responseB); + + //Get the Bid ask or last value from combo + QString value = ui->BuyBidcomboBox->currentText(); + + double AvailableBTC = ResultObject["Available"].toDouble(); + double CurrentASK = ResultObj[value].toDouble(); + double Result = (AvailableBTC / CurrentASK); + double percentofnumber = (Result * 0.0025); + + Result = Result - percentofnumber; + ui->UnitsInput->setText(str.number(Result,'i',8)); +} + +void tradingDialog::on_buyOrdertypeCombo_activated(const QString &arg1) +{ + if(arg1 == "Conditional"){ + //ui->BuyWhenPriceLabel->show(); + //ui->BuyConditionCombo->show(); + //ui->ConditionLineEdit->show(); + //ui->Conditionlabel->show(); + }else if (arg1 == "Limit"){ + // ui->BuyWhenPriceLabel->hide(); + // ui->BuyConditionCombo->hide(); + // ui->ConditionLineEdit->hide(); + // ui->Conditionlabel->hide(); + } +} + +QJsonObject tradingDialog::GetResultObjectFromJSONObject(QString response) +{ + + QJsonDocument jsonResponse = QJsonDocument::fromJson(response.toUtf8()); //get json from str. + QJsonObject ResponseObject = jsonResponse.object(); //get json obj + QJsonObject ResultObject = ResponseObject.value(QString("result")).toObject(); //get result object + + return ResultObject; +} + +QJsonObject tradingDialog::GetResultObjectFromJSONArray(QString response) +{ + + QJsonDocument jsonResponsea = QJsonDocument::fromJson(response.toUtf8()); + QJsonObject jsonObjecta = jsonResponsea.object(); + QJsonArray jsonArraya = jsonObjecta["result"].toArray(); + QJsonObject obj; + + Q_FOREACH (const QJsonValue & value, jsonArraya) + { + obj = value.toObject(); + } + + return obj; +} + +QJsonArray tradingDialog::GetResultArrayFromJSONObject(QString response) +{ + + QJsonDocument jsonResponse = QJsonDocument::fromJson(response.toUtf8()); + QJsonObject jsonObject = jsonResponse.object(); + QJsonArray jsonArray = jsonObject["result"].toArray(); + + return jsonArray; +} + +QString tradingDialog::HMAC_SHA512_SIGNER(QString UrlToSign, QString Secret) +{ + + QString retval = ""; + + QByteArray byteArray = UrlToSign.toUtf8(); + const char* URL = byteArray.constData(); + + QByteArray byteArrayB = Secret.toUtf8(); + const char* Secretkey = byteArrayB.constData(); + + unsigned char* digest; + + // Using sha512 hash engine here. + digest = HMAC(EVP_sha512(), Secretkey, strlen( Secretkey), (unsigned char*) URL, strlen( URL), NULL, NULL); + + // Be careful of the length of string with the choosen hash engine. SHA1 produces a 20-byte hash value which rendered as 40 characters. + // Change the length accordingly with your choosen hash engine + char mdString[128]; + + for(int i = 0; i < 64; i++){ + sprintf(&mdString[i*2], "%02x", (unsigned int)digest[i]); + } + retval = mdString; + //qDebug() << "HMAC digest:"<< retval; + + return retval; +} + +void tradingDialog::on_SellBidcomboBox_currentIndexChanged(const QString &arg1) +{ + QString response = GetMarketSummary(); + QJsonObject ResultObject = GetResultObjectFromJSONArray(response); + QString Str; + + //Get the Bid ask or last value from combo + ui->SellBidPriceEdit->setText(Str.number(ResultObject[arg1].toDouble(),'i',8)); + + CalculateSellCostLabel(); //update cost +} + +void tradingDialog::on_BuyBidcomboBox_currentIndexChanged(const QString &arg1) +{ + QString response = GetMarketSummary(); + QJsonObject ResultObject = GetResultObjectFromJSONArray(response); + QString Str; + + //Get the Bid ask or last value from combo + ui->BuyBidPriceEdit->setText(Str.number(ResultObject[arg1].toDouble(),'i',8)); + + CalculateBuyCostLabel(); //update cost +} + +void tradingDialog::on_BuyDNET_clicked() +{ + double Rate; + double Quantity; + + Rate = ui->BuyBidPriceEdit->text().toDouble(); + Quantity = ui->UnitsInput->text().toDouble(); + + QString OrderType = ui->buyOrdertypeCombo->currentText(); + QString Order; + + if(OrderType == "Limit"){Order = "buylimit";}else if (OrderType == "Market"){ Order = "buymarket";} + + QString Msg = "Are you sure you want to buy "; + Msg += ui->UnitsInput->text(); + Msg += "DNET @ "; + Msg += ui->BuyBidPriceEdit->text(); + Msg += " BTC Each"; + + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this,"Buy Order",Msg,QMessageBox::Yes|QMessageBox::No); + + if (reply == QMessageBox::Yes) { + + QString Response = BuyDNET(Order,Quantity,Rate); + + QJsonDocument jsonResponse = QJsonDocument::fromJson(Response.toUtf8()); //get json from str. + QJsonObject ResponseObject = jsonResponse.object(); //get json obj + + if (ResponseObject["success"].toBool() == false){ + + QMessageBox::information(this,"Buy Order Failed",ResponseObject["message"].toString()); + + }else if (ResponseObject["success"].toBool() == true){ + QMessageBox::information(this,"Buy Order Initiated","You Placed an order"); + } + }else{ + + //do nothing + } +} + +void tradingDialog::on_SellDNETBTN_clicked() +{ + double Rate; + double Quantity; + + Rate = ui->SellBidPriceEdit->text().toDouble(); + Quantity = ui->UnitsInputDNET->text().toDouble(); + + QString OrderType = ui->SellOrdertypeCombo->currentText(); + QString Order; + + if(OrderType == "Limit"){Order = "selllimit";}else if (OrderType == "Market"){ Order = "sellmarket";} + + QString Msg = "Are you sure you want to Sell "; + Msg += ui->UnitsInputDNET->text(); + Msg += " DNET @ "; + Msg += ui->SellBidPriceEdit->text(); + Msg += " BTC Each"; + + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this,"Sell Order",Msg,QMessageBox::Yes|QMessageBox::No); + + if (reply == QMessageBox::Yes) { + + QString Response = SellDNET(Order,Quantity,Rate); + QJsonDocument jsonResponse = QJsonDocument::fromJson(Response.toUtf8()); //get json from str. + QJsonObject ResponseObject = jsonResponse.object(); //get json obj + + if (ResponseObject["success"].toBool() == false){ + + QMessageBox::information(this,"Sell Order Failed",ResponseObject["message"].toString()); + + }else if (ResponseObject["success"].toBool() == true){ + QMessageBox::information(this,"Sell Order Initiated","You Placed an order"); + } +}else{ + + //do nothing + } +} + +void tradingDialog::on_AdvancedView_stateChanged(int arg1) +{ + //Show or hide columns in OpenOrders Table depending on checkbox state + if(arg1 == 2){ + ui->OpenOrdersTable->setColumnHidden(9,false); + ui->OpenOrdersTable->setColumnHidden(10,false); + ui->OpenOrdersTable->setColumnHidden(11,false); + ui->OpenOrdersTable->setColumnHidden(12,false); + ui->OpenOrdersTable->setColumnHidden(13,false); + }else if(arg1 == 0) { + ui->OpenOrdersTable->setColumnHidden(9,true); + ui->OpenOrdersTable->setColumnHidden(10,true); + ui->OpenOrdersTable->setColumnHidden(11,true); + ui->OpenOrdersTable->setColumnHidden(12,true); + ui->OpenOrdersTable->setColumnHidden(13,true); + + } +} + +void tradingDialog::on_UnitsInputDNET_textChanged(const QString &arg1) +{ + CalculateSellCostLabel(); //update cost +} + +void tradingDialog::on_UnitsInput_textChanged(const QString &arg1) +{ + CalculateBuyCostLabel(); //update cost +} + +void tradingDialog::on_BuyBidPriceEdit_textChanged(const QString &arg1) +{ + CalculateBuyCostLabel(); //update cost +} + +void tradingDialog::on_SellBidPriceEdit_textChanged(const QString &arg1) +{ + CalculateSellCostLabel(); +} + +void tradingDialog::setModel(WalletModel *model) +{ + this->model = model; +} + +tradingDialog::~tradingDialog() +{ + delete ui; +} diff --git a/src/qt/tradingdialog.h b/src/qt/tradingdialog.h new file mode 100644 index 0000000000000..a552b6039c5b4 --- /dev/null +++ b/src/qt/tradingdialog.h @@ -0,0 +1,99 @@ + +#ifndef TRADINGDIALOG_H +#define TRADINGDIALOG_H + +#include +#include +#include +#include "ui_tradingdialog.h" + +#include +#include +#include "walletmodel.h" + +namespace Ui { +class tradingDialog; +} + +extern double _dDnetPriceLast; +extern double _dBtcPriceCurrent; +extern double _dBtcPriceLast; + + +class tradingDialog : public QDialog +{ + Q_OBJECT + +public: + explicit tradingDialog(QWidget *parent = 0); + ~tradingDialog(); + + void setModel(WalletModel *model); + +private Q_SLOTS: + + void InitTrading(); + void on_TradingTabWidget_tabBarClicked(int index); + void ParseAndPopulateOrderBookTables(QString Response); + void ParseAndPopulateMarketHistoryTable(QString Response); + void ParseAndPopulateAccountHistoryTable(QString Response); + void ParseAndPopulateOpenOrdersTable(QString Response); + void UpdaterFunction(); + void CreateOrderBookTables(QTableWidget& Table,QStringList TableHeader); + void CalculateBuyCostLabel(); + void CalculateSellCostLabel(); + void DisplayBalance(QLabel &BalanceLabel,QLabel &Available, QLabel &Pending, QString Currency,QString Response); + void ActionsOnSwitch(int index); + void CancelOrderSlot(int row, int col); + void on_UpdateKeys_clicked(); + void on_GenDepositBTN_clicked(); + void on_Buy_Max_Amount_clicked(); + void on_buyOrdertypeCombo_activated(const QString &arg1); + void on_BuyBidcomboBox_currentIndexChanged(const QString &arg1); + void on_UnitsInput_textChanged(const QString &arg1); + void on_BuyBidPriceEdit_textChanged(const QString &arg1); + void on_BuyDNET_clicked(); + void on_SellDNETBTN_clicked(); + void on_SellBidcomboBox_currentIndexChanged(const QString &arg1); + void on_Sell_Max_Amount_clicked(); + void on_UnitsInputDNET_textChanged(const QString &arg1); + void on_SellBidPriceEdit_textChanged(const QString &arg1); + void on_AdvancedView_stateChanged(int arg1); + + int SetExchangeInfoTextLabels(); + QString BittrexTimeStampToReadable(QString DateTime); + qint64 BittrexTimeStampToSeconds(QString DateTime); + QString CancelOrder(QString Orderid); + QString BuyDNET(QString OrderType, double Quantity, double Rate); + QString SellDNET(QString OrderType, double Quantity, double Rate); + QString GetMarketHistory(); + QString GetMarketSummary(); + QString GetOrderBook(); + QString GetOpenOrders(); + QString GetAccountHistory(); + QString GetBalance(QString Currency); + QString GetDepositAddress(); + QString GetNonce(); + QString HMAC_SHA512_SIGNER(QString UrlToSign,QString Secretkey); + QString sendRequest(QString url); + QJsonObject GetResultObjectFromJSONObject(QString response); + QJsonObject GetResultObjectFromJSONArray(QString response); + QJsonArray GetResultArrayFromJSONObject(QString response); + +public Q_SLOTS: + + +private: + Ui::tradingDialog *ui; + //Socket *socket; + int timerid; + QTimer *timer; + QString ApiKey; + QString SecretKey; + QString Currency; + WalletModel *model; + + +}; + +#endif // TRADINGDIALOG_H \ No newline at end of file diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index d2b1faaf67e9b..a9615687d809d 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -119,6 +119,13 @@ void WalletFrame::gotoHistoryPage() i.value()->gotoHistoryPage(); } +void WalletFrame::gotoTradingPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoTradingPage(); +} + void WalletFrame::gotoReceiveCoinsPage() { QMap::const_iterator i; diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 57b377b9efefc..9205878cf4524 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -13,6 +13,7 @@ class ClientModel; class SendCoinsRecipient; class WalletModel; class WalletView; +class TradingDialog; QT_BEGIN_NAMESPACE class QStackedWidget; @@ -56,6 +57,8 @@ public slots: void gotoReceiveCoinsPage(); /** Switch to send coins page */ void gotoSendCoinsPage(QString addr = ""); + /** Switch to trading page */ + void gotoTradingPage(); /** Show Sign/Verify Message dialog and switch to sign message tab */ void gotoSignMessageTab(QString addr = ""); diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 652385958b37b..5dacaa27b926e 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -17,6 +17,7 @@ #include "transactiontablemodel.h" #include "transactionview.h" #include "walletmodel.h" +#include "tradingdialog.h" #include "ui_interface.h" @@ -36,6 +37,7 @@ WalletView::WalletView(QWidget *parent): { // Create tabs overviewPage = new OverviewPage(); + tradingPage = new tradingDialog(this); transactionsPage = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); @@ -72,6 +74,7 @@ WalletView::WalletView(QWidget *parent): addWidget(transactionsPage); addWidget(receiveCoinsPage); addWidget(sendCoinsPage); + addWidget(tradingPage); // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); @@ -180,6 +183,11 @@ void WalletView::gotoHistoryPage() setCurrentWidget(transactionsPage); } +void WalletView::gotoTradingPage() +{ + setCurrentWidget(tradingPage); +} + void WalletView::gotoReceiveCoinsPage() { setCurrentWidget(receiveCoinsPage); diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 691875396bc03..57304e6556351 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -17,6 +17,7 @@ class SendCoinsDialog; class SendCoinsRecipient; class TransactionView; class WalletModel; +class tradingDialog; QT_BEGIN_NAMESPACE class QLabel; @@ -61,6 +62,7 @@ class WalletView : public QStackedWidget QWidget *transactionsPage; ReceiveCoinsDialog *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; + tradingDialog *tradingPage; TransactionView *transactionView; @@ -72,6 +74,8 @@ public slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); + /** Switch to trading page */ + void gotoTradingPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ From 6041b880cde8b4725435be3a1b894f2947e207df Mon Sep 17 00:00:00 2001 From: Graham Higgins Date: Mon, 10 Oct 2016 11:08:02 +0100 Subject: [PATCH 2/3] finalise rebrand --- src/qt/bitcoingui.cpp | 2 +- src/qt/forms/tradingdialog.ui | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index fe65d1de32a6c..b4ac8e19b18e7 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -395,7 +395,7 @@ void BitcoinGUI::createActions(const NetworkStyle *networkStyle) openAction->setStatusTip(tr("Open a DarkNet: URI or payment request")); openTradingwindowAction = new QAction(QIcon(":/icons/trade"), tr("&Trading window"), this); - openTradingwindowAction->setStatusTip(tr("Bleutrade trading window")); + openTradingwindowAction->setStatusTip(tr("Bittrex trading window")); showHelpMessageAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation), tr("&Command-line options"), this); showHelpMessageAction->setMenuRole(QAction::NoRole); diff --git a/src/qt/forms/tradingdialog.ui b/src/qt/forms/tradingdialog.ui index 173415e0419db..47183b6ee48f7 100644 --- a/src/qt/forms/tradingdialog.ui +++ b/src/qt/forms/tradingdialog.ui @@ -2527,7 +2527,7 @@ - :/images/bleutrade + :/images/bittrex @@ -2865,7 +2865,7 @@ - :/images/bleutrade + :/images/bittrex @@ -3036,7 +3036,7 @@ Settings - + 460 @@ -3052,7 +3052,7 @@ - :/images/bleutrade + :/images/bittrex From c2e858ad6929d1ae7bbfbf8d383fc02ca8a86500 Mon Sep 17 00:00:00 2001 From: Graham Higgins Date: Mon, 10 Oct 2016 13:28:22 +0100 Subject: [PATCH 3/3] add exchange logo, adjust visuals --- src/qt/forms/tradingdialog.ui | 7 +++---- src/qt/res/icons/bittrex.png | Bin 0 -> 86406 bytes src/qt/tradingdialog.cpp | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 src/qt/res/icons/bittrex.png diff --git a/src/qt/forms/tradingdialog.ui b/src/qt/forms/tradingdialog.ui index 47183b6ee48f7..02c78e8ded398 100644 --- a/src/qt/forms/tradingdialog.ui +++ b/src/qt/forms/tradingdialog.ui @@ -247,7 +247,7 @@ Buy - + 30 @@ -2756,7 +2756,7 @@ - Generate VCoin Deposit Address + Generate Darknet Deposit Address @@ -2970,7 +2970,7 @@ border: 1px solid #61696c;margin-left:4px; - + 260 @@ -3224,7 +3224,6 @@ - diff --git a/src/qt/res/icons/bittrex.png b/src/qt/res/icons/bittrex.png new file mode 100644 index 0000000000000000000000000000000000000000..00a3c347dccf30d38dc88dbf10e49ecbfdea8eea GIT binary patch literal 86406 zcmdSCby!q;*FMg`3=BgJjYjwt2tjmQ9cktV`q84Fz&&++bPc`KcwmTekkNYZ?a8#1g4>#f zGq)~BQmx+)4X&g@eRUvFI?%Xznn9C0YrBQ(Xv|9NExq65*x4G$DrVxtTBv#GhNFBP z9mcB1-p;mEQrBm_%`WZm;;ue^+NI_(OY#A0+T;bL3l?tu*1U=aQPa9Zb8~zR#d*Z> zez{Zr!&{oI-^;LHc^vn9&I6?dzL_f{#=~gmO|BXVd_DDC_jr_k20|VCml{&d1}^UV z!E-@m?7TwXQiH$1?Mmx?|GV@ZFlXx$@RKh^w_3kQ?fYAD`fx7b`wAX*I)d&7^pQ~D z>%Odv6j;Sy|3~`1OIr4VISZ*l46;P52uw^<*NI83h`>hE#wKHLk04`9)sSG(lab9T zlbFC>NVw%+6n}wagkyis1^j%1sE}#v8v4kn{aK{-7;wTI=ddV4`t?UUKF4nmQS{%u z)p|!G9B$kRP8WSTx)K`C1VlKurecSjN=y;sAm&`*zde608KK@U5oU2{+>-bhHkD0e zq=&z5m#3WKfZoc1AL@Gf-q)XvZEgW$(=5QN<`$DKXVR zkl>^qG{4!7bdo{bFLy1rH`Q%KdaDAh5fMeIg$)z&r(t@jOGkM$87FPMZYcylOQ*88 z)!VY*u8Lf+X0yPjO+80qsdgCK_m;NzT&Hp!xQdYPezSS45l zp7_F2f)Tgac^GF|xuLX|^zz?EuYZ9Yi*LQ#&0x2FE&QsIkLU6ETvb!6^AzthcB30I zuO?|8_aCI79U$zT=v4dbc!sreW{@$A#K)| za{CZ7Ims24TRL+&*{AF|8O{NSS>?t6qVWC))u1J=9PP}b0%K9W-bx#7Ej%rOFnz}d zBDVHSJ+Ra_XTaI5LnpO-h^!Rm_#v!Q@u0|SNAXc|Q_4Ce2|n@lN(qiKD%Ch1_3;`8 zE^)8VQ7k-B%y{a{^LPZy$OFTk{JU8RXG=!hC(%Fg0o5W4Iet z+47T9T%e$gXzcTUHsm;%jsDorEBP=3Id3YG?XZ+nu=Hz^e;L0urdQR^KA_qczvAe> z&T?)k;v7uuBG}xs=eirqIx`8+a|U6)Gj}gGIe#6oLhR*;tiUKf1@@>0pQA(LS$P}!0pOF5 z-bsYCh8`xp{W_43Z_pPGd%%C&Q(hMjKF&t}WCQMJH&W+Y>&bovpKvN8e@P!yHmbh< z$`7JSzhQbtb$!QRb0bJec3pJX%*p@RF*xL{82%EF*i?PL6D}w_`RpjA`<&p}Vh1_7 z!`PPmlViB0BIxktD2E(N7I!lNddOh?t0grP{TNYihH2wjs;|l$8+JYLphYmK%p_ ztFzG;y_Ifz#Pk(g@ji4pIqIhnS$w!;}|4NqjUGS+*P!q*6VH06JPf>cmZOGQfvAcL0?Td5L9+od+C-wP;=7Dr+a`n!Wx* z*Ex$n4b%7ueV`~pd9A2bWj4<>Z>>85eoJmFT|xsppNtJaPk;WQ;nRMow2b>3n!%4o zt(YSjv%S6?Bz@?cCS_Wgot^ypq~x-P6rQO%4|F%G^7j|sfIWJRKbJ_`qZ@>3h}4@3%y8P+WABW4K(FpQ!ggc0F!9ht2Q;%xG>_xqvj-YOcDE04^td3gSL= zr;DQ5O9@-GCc#uwJe`s0_~c{g41>P_EkYZGrCeb=h(DeESvpcy3GKAukGRv_yzMD; zT!jF?p>W6T9KH{yHV_FbnJ-Im(IfOoFCgTSKGbZ zXcus}CJu(V3MgL*_L`K06MF8?rzkDHdd?}q0GD3{I$Ml>_lWj|`0~jjLPo7@;0-?^ zOx45>XZ5@Hz;cei1n)EQLsrWq<;O8s-Z(i4{QNW6%Hv=q`-y^9HUggy> zCW@Z0#3+nBDPCdNW%t)M1B&wxGZI+d)G{4=mqEVEX=1PNP-2BEdkTv{hqs6+tU)K% zia$dfQMJYRZohKCf<4S4-u{diJhVX_Z2!z+1OlX~& zW&1jH9Itv@J-*5osj7GbCex&c>z&@{^;I5#iSCp-Ohk~37dW3q&VOOH>VgeCmf;a2 z@WOUkL3TJ!ROL$nQSJR%>_Bx_7)C{6;D4GNLHx8Tg*kL=-5kSBR|wohcID#Tm!qE( zQfF@zp_b_Ipi|`a|B7pGgvY7!6 zZ8FHTlGK7JDjHOxPy>dCjadO$@!Y}^NGCzSNVJYwxO#eN>i9l?-BgEH7musj?8k7p zpXI~$^g9=|pg@e)3knXbJE$sL{0D_d(>~Km9wQ2#o zepJ>b4hxqC;Ujhg63c|su>A$CQ06K?F0~f4RM+(y5{l%zAMu-)SiZ#=2{cSHhca6 zgzoR~2(7_*PkA1EOZd5nf#NWeh3KJic@ca|Y$yCl8W=>us*><*FJ5&O#*V0)UfC$v zyXKz4Jt+DB*^&*xxY8%iyK;J%O-M=>u#EMmzl~gm;);O2q`LSL$=`qT_I!^WF~Kt4 z`BztOVg)qp3?y>N@E>OV1g#_D*+F2I(jq5tVcHWM_|5?X{0W>x>DWB*3ke7TjXka? z0+8N6N-rZVY3p5#%}Y(f%beYyF2OK_JkS9ADkGzNLmurfy@mn$@Eg_tpB?Nc7Y;Wc zqSxON*c`x()jlfyA7=3_7iFY1U>3H&>Jo%o$bQ!eH9c0}OQX;}-^Bwsp@x#Ef5;4v zY$9=8$q{@UZ@c^^h~n4o8)5W-YEP05ixE9txC6|W z-`OJYLlX__J|AQeFAhIV1$exhV!N`)zI!x}hj`ny*4g&-GQoB^x|MMEf&SIS`cS=v z_<^}k=yg>@w5JnJeuj8))9jH*}&_tu<1PK_lrUy)M2#xa{!= zKMj-z6AG6|w(C2x&J);0VxiIrulYIy9|XF+|FYK@!lOOq7|i6D)knwF1#}@#yAQ}c zcB9&O^bs#9@018#-4U@{wCB=zKe>~+hesawnbgm64hy+Ve@0seU}g%u*@_Th2cpT( z39Lg8NVh+p$2(-Vyi+4sw(^`n?y}YCESz@xq4X5)5+sA}p^wfYYo(td1x6xYy%Zl! zJ(EtvCC(}jsI*__+0S3^9sN3Tfga~ePi%5;;uQH1UJe090%GsNI-~91WvT10vNXz8 zl6pcJGQO!t zV@4k3L$v?K2kY4JvouUd0YD_)@cn>oYT$zvxd**pGMv+i%7`I{8*3HcPp4Y=;&Ln* zGnRJrY5v+~h(B$!6lNIdoNOY1iO`)KX{?%D89qUCe3exy>W0bKq$9Cl8-9ct({tmBAI?cv6-L042WyGEA?G9=df)t z{Q-<}YzgzQTM&#Q`3XS8@BIm&Ef!T=K+AHcTjwZnHu*o2lj{+SiDnBgXm_KZ;Avf;HAunqxR8H=!~B&uU9V13d>yIN zBlIGBcWy+nk*Q#Vly+$Bu_Ib;gLF+kQFx%dN%ec=B*1{3JpuB7Eq)4P+(8SBXfw{S z8Uu&m!g#wY8PHBG+2a_4cZ-0TH5(Q5iz9e2X4xnGFn=l4p9&MBq63oj?3K;Kwu+Ml zc+g1k43}HMAlUr`wl94hqAE*B4ij6XWmiy~OcB$PKzV3*ji- zrlR$o0Ww#GJ!5lM$td(<8;&3eNsz5%Xl|I^| zIOs6#PC)CNDrU$?Z7{+ys2z}9YhH{)=zkTsM3M3Yb|V^S!=gS6Jn$zk!kj#}7CYlL@r=2t6Cu@Az;HMSY_@*zFe^{K!s*B+EYfgqM3YFh|@tw1R;jK9^ zRs~KA2T{g`LJ^^!kx(siXbg}$c6t?+NHs22x-p?};JYJw>%9LT#3WMlUaSGR#r(JA`UbkQ(hAgmF0lxq{l$=D(_pg~r0ID7e7yoDMO$iF7~( ziJ@At^O>I7pu<5L>O^BnEGk4K&{$7xu#q}gJk(1YsD2PVhCF^j{}hbSbu_!^S2Emd z?KEs__jDMVjt^ZfGQw^)FfOOdo6N#b6$CLiQeiA@)(1sJWS{2WN_1mZVVl-5f?Ns> zKgMp_AeoOKwPA)htE8a~-$A=i_1M`sP{x4Agkap)U?rhYdjmlRU=wE&F2rcod=O5e z+04?`+}`XZAucRM$rZjB3q=6uBqDYq0y!hWZA9UiuT_8?IIU}PvGN3I{GpfXRPjvp zg@n*c?R|SJQi1Jeok2e_&OfpUAA^w*a3Dxdcos42Y3jb zh$xuJ9D{g0BH~z(Gc6?oK^mz=X-_K=8b0p^^yU@1My^mOrPhAI&$1-w1xpHn3PaL& z0UwOx+n$a<@`VfSCYU|6@F~C$GJLKJpQN3XF++mNiOB9nmVwP#W|xNMM7`FYqe%KT zdGms;Jy8{-Z??nj6*E28uBGO*WL|CR`q@iH#(Ij8g(NX_bsQopYD+|!Ng9i63uO^! zQYQj|B}n1oq;Qq>%K(}pv6DG?pEv~EUVK4#7Qn-f2Tx_r+SQiGHyEKwa~n|YajhG4Qe;PWv)5PA` zODqBHdx^c9l-Bdkz#v!1irtv{HgQiu8F3CkxM>l@5uAO&zHv0DEt&a-k+)(yM<&rb zUv+n`&WNpdq850XSij__>*`Xdk7ME3oSeDaqP(Wnm03m`7 zOJMvXX#4pt&G{V13lX!j{%$Ka>R=tO%fypNg34NS>A1M@@%1qZU*(T615Fwog~&t+ z#t4#K5+9hXn-g6alSfPE`ex6fB)*_Vh9^)v?Ib&oXy_Z)3)^gA)@Q}I6r4n2LG+^fIqZzU!-!zlGd6~#;tftTJ!jtDQb35{W!^i7nGUv}Iqa&3! zf1dfYJ;`z~{p}rEC*c92HZEx*2?r8*L@Y!CL28tM2}%tpWr+U#l@_ck@z|BCDqRBD z{FE^+leOGMcPk(U?@ET17G_h6USecH)k5%uyc}RN{nh1e!GODT#~?Ya%S2`5vQdW0 z6Ll-SLF;^I(Olox)e$K`M8P{BQOgodv08A^`SLp=aEe5twoob+N+4ULZN!%`WXc3o zkpC+AumlUPhUNsRBIOQbl%hIX)Ed9GC*{6haWlZ&4E>yeHH-?_RSTUes(x_o8esLb zQq_Qn%0`QUOc~^76T$LAdc3~_A&qPW`VF7mkUz2Jzd482^-((L{cW17{3{(b@&^PS zKR_A_sS4%lZ~|Os7X{=nD_x5`Db|NK)#>2bu!A7z>pR=Kf=+YnBG*&wHefZ*w?sIK z6!27x)vALZy1Tt0y@vrS1<;`Jc^HLy{o?lXB?9Haa+SU<{oo}enshl*_ z7Bh|i0_xKj=8u84JF7>SnMK3vh>F7oh>R*h9W3mpRPBh~0r2hw&-sdbU95mC%Me&L zL>4i4C;x1z0AnL+4?hvq_|f%A@c58`ZUN@5QPf?dhdy45wk?aXUfQ5b`P8=L$D+K5 z-;kHKZeR7E{V5c*ehm%plh?ILbKlBAb&Tu;HgVgY_4dE5;c`%Wf|%bb5OjB}MZHTG zR0Y_HB{~nq{j~S8O1Lvs9g?$5?jqP*- zxnWe=>PAIQ<&dE^nm$bwp?C7=BlYju#d&_=;@9@1br!Z~l>#F9NCa*RCE`-x^*aiz z3NS=Ojr9bKPdKOvo?s4@LSV@qB+yvb&CLM$*=(yfO;?QoZr;JH3*2Wu{3r(c|1!AU~)ACLE`NVTV z1wWw~`xH@8G^j;Ig*kOs2F7M(Cn(SF(J28bz#!aBV%*a) zfqd%zFd<}#k%7Z8ka;ayqVI_(AuAh8sKtz}Db(^+t1X|H_IBRDNn#C`u~r^9$trPd zD7h?mt8=S!xJ*p)&A=Jr?f1v$fy#Z((1?srKWuT`d-On`fQmugu$Bc42-O_jw^!M&1Y#=UhWhK_2<2niVPbgs;WFrKN3-R!Msz8^czLQQ6Nl-=v6WyRSt($RD^>y2e=c?w`dr;94qvh zQ}QYhcP+nuByiki{1Tb}Q_uSXpO%~`O&nN6467K7lpkL8YB^GxA*f~eL~}c+RsDtd z!%GE>Hi~`pCG{8UVd=f8N|64DfO+!Lh+9AE(+bK3Qna+M?Nu=3z$ASJw?y!6|h|7q3&-R(>>8oPTzIX}kFvt4}?ItSQ14=!R z-h8_hTrD@)|`a!&`|MOnD6C>UnXzMc`Z5;fgmFrt}m;mfB^k*<|4ZJmaBza0_| zUSvy1ObnlUyhh_U9VhAeh7%F#%Ev;1q;3oCCW3Xmq@1)`o1odP~&|{;Z@9CA3 zmCk(X5OGkXH6|JSnM82-*h*xT$EcHz%Q3Z?;o#4zjxTK_T~^zA+rCpTMvu9g)}E0g z6%bEIFR_A^a@2|Lww&o-%!B~Ldg-&!hk=amg1Za&Cm+IgkTb8Q^7X}&aDlr zh%%6tA2mH~n0?sJH%W$4E2~wpcV9`Uc7fs6-^Zu-nbP?LvrWOpr^UP^b6J zR7J6rEHCt!_J`3A`LH`e&&*4#u)sLPxKgA4Wv9C-3PE&Yv$yUA$UO-9H+N=5+CCpSNDDlKrxtz=!QJ$q0O z{K`O_Gg4l~6w1{c%CeyjbSohlK+0|TDQ(<}pNZm=q47X~^?X|_1B+&_sW7Df>RgF! zCFOKc&#PLc*%@u-JxUDMl?v$wot2I!}w^3uL<)D zI&8BReqgvF?W7K)yg4b5PCVVYW-4k$x?(cKhsW^>U!pR>^~A?cvjTvm4*6t=8C*7@1|YzS2zCZPxV=LbKKx{M*!Z zxyW#+XJDYze|;@)^6R!?bHR~QFb7FEqlzL}PMnncRv(LM&@?^M+k(l|FT-%ZmB}c9^EFT}ggq7z`#l1QC_#~v) zMJJDV8mYJr?nk~ReUBsYW9_d?F1Wb6_cwg_ab%mMq_+QvksvZDq7*vK*%cw-psLdR zUyFg^37U^%VhKG(As$Q=F04+U&)%w#Nu&pbvZ!IJ0m8K@$Dcx?5q@}ANJ2shY;n(2 zIZr%UGU5D5*C>m=rM7R>L-)vpLSL7J1{l#Ofi5LoY<_@bw6M&)V^Fnx`^14mhcud2 z*Cn@_7tl}{_DBSP%zm}|Bzi==#NdPi!$|$sfzWt?v~QFTuCgJRVG-|c2-HyA!BSR1 zsdUuUhF_z2b)pqb9Sa5yI9YDmuMdS3%63a(6cyTKkG}9J_`lFJ41Rfu=c4!SpIgAOM@cW&%mbgsfV_EW(53@;TG> zt|bXBNc5{k23^p~1epM{NW_P@#TlBri=T8A!*c*xrw7wqq`N9;U$Up@Yy?F&6;@m} z07&93^Uf0N(Y8kunaq!?$EG# zwDF(gAb<~G>*-JLKYR-66tH0yNnHi2#AdE;jAJD7V)Sc0|d{owx$c$5-oHtaDy%Jysl}II8X>#X`lX|!H&XfjkX%Y%x z#kmK?x_wYQge!o_b{ylu-jn=R9rx1rH6r3H30^M)zMf3De08U_X~Ne)k&4VG?0GnO zGZ?{?00OtYqA5A++FURY9>g(gwu7f?-AtkPp6U)#B3KeZOJ z5N|X{(n4LynMoNNg&-0}iydxo^oLX|`$Oh9S4J5F6l1|YR*O-TlS6XDK1-sF+aG%x zCdy@w1p0}2NHxvNquRAl;{vs}iHFrLFlWMB6<8>wi7^?#7`W=wI+LE!AF2qUukJAJ z3e!+u`>smQ)hAh@Pb#iKF?LHqqZQdg2-ADcd#@u zCh?@$YmQOrrzx4MfM|x*Dkz>X1dz97AoP?%%~N3BaY6f7Wyc2%S>K zK=m}SJ5xBYU*rb1i^3Ir4OIfcJua|m5G0QnHjQNL55r6xPhqPJ!e zsxLg!os0>I_ddhMP?IDU7Kivm-k}N)gW^3*?+t;prw7h;Qe&r{-+F#*i_sE{)T|QQ zE=M%Y!SNiyXIEJDDJtCM$GLs3nk+>+-_r-{s%f5N8UOYSwTNMkgDWzEkLCuPz4Ft;Y*`zeq_U8yq>qI5+xgE0;!kCXd3g_u7?`0tf2=z zmMqcl-HduLAcqzb1Ct7=x(YFt+fTmJyUqH-NZuzFV@2alPtT#^_8Rot9<>C=@MF2% zL~()cZyIzc;qqacEixw5!~t1Z@!D?52X34Q2Imw_63uliHK7_iKNu zVNF~1kwCQWE;HAE-$yOQcv8eh4m@rBC)9EV>rP(QA7IpC4@zh^)%A3stG)(51voUS zmPiC?LJ`K89LiceR{8hTvsMREOBs zqCK(>`g6#ED;DM;47P@Y$W_1?|BuVL$^M)t6LLO37O-o1gF|lMi6tt-RoFM*;Xu93 zu9XP;j#ZRg$9ouqIr6+zc5&2MI5&WaT2Ix7E?gMw56~j8KB`)d8+&|9Cocfgpnx}Z zg$*Nbgc`OyXy)^DWV6XwkGJ1OpwPub2{*`gX6%_-=RV z&a4x@&Cg4yRjxU7@<>>>!jDfRyy1GZ zdQeATb3lsqEHn4t_Xd`rN%PX1X$&GeLKgj`KWeI_Y5yVg*L(@BmM?dFmA1eCqV~wcl4ythaOQh-!A{ z^}kpc8ClA55Y|g?QBS%~M6a4kQnl+&-ao+TWcr7Nvv|_ZvFEijM0gy)xWoAxY#-_f zq^nLZ#o+t!q+fD}7a1(Q>Q2$FC#=Q!^8;VXfWdJ?5yK!#FUD^lW6ccxBnlA7mljP2 zRX7Q6zG39|8-l2Kuj^#yz{0L&rCDBEH0+q)3bD)&8DZ3FV4UjH()HI%74zx!hN|NQ z_i5b0*U7~>)#cz6z<{Ct70?;alWxUk)Msw9C~* zTS}-*LIX0N{hJ1S3N&EcE|QtS*3d-<67d6Yl8)Dan;6@;wIu>jM<_4G4i2k%kp~eU zr+L@6)TxX%*l8IV>@D@%`2;ERRnU={ZJ9lUh($<}^G5QMIkjkp;sz4i_ZXG2KG#`7 zuu>t{{S?C*TrflG)4mRj9}z0Dg5B=pD8}jfr7?|zy}*k)jdJ|_BdqG{6*qsMZeX3I zh);K(9{}k~vf|`*Cvmjb3=AQFx&t%%_iKxfN&y>rOr?cy)D&2|u-#I$lIxAT8p6kc z23GZE4PFILMbPV({^!fd18G#@$kTLsP-E)0@aMq&`oGV(7}iVSVzItlbWs7t081J$KRfg8RXbveAm}rxuyx_!nl9Vo(L=Gk>6*X z<{D*frae$vDm|^w$9N>s62FQcWT|6h@B37oiHR z45(!&@2KJ}wt4o~?00iYHhgu1eoi$WqJ3JLDYxk1Dx4Fu9RnZ-m>(it$zmZ{SThfR z?=c|qX4U;yI0$&(Jh;m#z9M_sAF(gMBggT&=1mev??c^Gl0endYnm5x3f*ZACl${Z zNwOU~NnuvhgLkQ6$%}yc3S+9ozyc)vWG|Iug|9 zqG%L#pJG_;Lj*-d^3ZK5_MHo>X|ROLSr^bec79)6Wc^sep}^`$E!HR8N}M(px4**n zgATwuPYL>OzppZ9u(dOK|_qdzjNa zeUQaHqq{ZP*O=2JQxjt>;k|k0tZ;ct*jX&js5n26JGH3ey)TxN1cB}r!k*?w=93B} zL8(07OYX7g8qgsmSfzTI|LUtsOZ7~4bp^u_NygXSm#Y;MRHh4j?&&`I-U2&;@AlsV zE$GnA@vexgResafd)=PcylZl_jiBY84o4g!rff`%AYgsAQmr?lepibXAJ6bU{{m^8 z7i)?@F91W7;OPr!;l!e&11M-Xw7R}I8a@jK?n%HwfaRz>UVHDTsbHG$g9%2gEciSd zevcr?LhbkKzcvmCSQ?m|bbX0y9D_ki*!AnMZ*thiSuv7-Uj!_3qRR18y!=bRauF8L zcIoVmiCB5FQdkjY&5XpV#b=yaV5!%Un;&e!&n%2954>LjgBr36Q zXz6q#dPYX_|Im3Bww1SIvbR3bv3`OK9>@6S7IlpL;SQwBMM7%yFh6en{mCi&k)P*3`G0@(zpPRLu=&KejqRNSJ?)`bdTUVmhfM_d zrnTzj`SS^v>qB;~`YkVAISGIt|j7SazKRlKpP9VlWaw+2)8K9$a~*_?HNQSuQgy5sf1A9>M;@E_a*@YWmz zlRKCTk&f98Vfrcs=5$W8ly${c_#19`k_}qHSMPruPs^~MY@vC+DAu^9pK$p)O~~ILa)c6VAlJLMC4(#Oa zao<<%6Nopo&kyca<9Danht_K3+lTHTSCR&8sXrYRAB#DD{< zhH7x64>N#Ow%=hDGb?QA<5f;^M`0m=*uU2(wb*lNqzwvelCiS_g^V#?vv>Tl9`<35 z>1vwl>0;FY1LKD=wAzaOFD`xaQM7V&!;hB0BMrET7+AFp^}aql=VGQX|MgmS(7Edo zftOGDrb7#bkv?S7vEs-`mWZl!s&HV7kQQn2RnBGM8^wJwe`wVC0=QLyZKb=>Jsya; zbZG!iC|1ZD-+#GtXG(5$Tc*l$^O2E75Q;?J3_dD$Y+Jz^Ct5a$vf6 zKBA7D;I0f#^wICK&W`?GLweFrIyL9}6q-%tl~jF-R&9Rh-P35&^~_AWPoQU2ocMOk zhdNQr%@p@U+!A*<>==F)1@4Ap8uCfPWis~Qlxv0}-kyfZKVHxwQ+c{(n@MSTW8u!? z@2ZjT4fWcTmoUW=VWr@(RUVpxN)ZNPA>bA(RnLQ4`8}lOG}Eu{lMUZg;+#2&?MHf! z8xvaS3>gWSUH!mhI!$o8NZW{Cm7lKcYLi)PeQrGI|VcTSPO4HB$1j>;0i zS(amHg~UlF2I7BSOy_@A%%VRfSNeJO6~)(f@s=;1wDz%HpXgMYKdsqfDR#ehQY~+? zwU@ad2)@r~a_$+;D?MWh4}NB0A14@)2;7pxjf?#HV=rv>)l+hRJ;VP8^$h-J^(TXY{0;yFnGnzZ}l>smngK6Qo zG0*0Wv0QeEG>V?UvC3MzLSh|H3)~LI`f271#kQ0z{RXMiTT4>uCyby2{|?8J5S}Ix z)4dAp_5hAG^jf^_LOm;xGK3Y&L5x^}e=nBpC1NidQ&sHm{4WR|Qc zjl&;3;>@tMLQyeaEFS_15J<(IlUNV>iWFM$7MOVi2SN42Z0yHwk0x$l^daN|)frkn>w_`!Jd^CRvn-hk`Oxc_cG zV#BR#265J|Ym=6WmO6O;e|=WOi2?njZrW3eR)-v0 zp%SFd!iT`XEo>q3@|WH{<8PQq?5ovi_XmL%=K4fbk>t>*rbikm361#@4sikAA%(fd?Q>%bWJ& z_w3izF4PsM-}i>mtAp#v+oCh|sN0;}pJ5IIaif*Z{>m;-89p*&0I^lMnsB0JhtGF^ufAs==YaVirHS1dvU?k^%u^nGJK+DgCQW zTh||XPYiS2bQrVD8!ri&ID6rfB(fHB>H%BYDo$SD#&a8B@`2s;S9;CMv_?@C*)uP{ z`Bk=>pFJj+hS8wzsA!~HmGi1q4sH#lV_G}WE1?QJ(uUo=$5~_Nv^LWm)D_K}kf_k} zQ>np4N!Jx=6-)R(K84<5KX(TEJV519+^qyZ;Hk$;n1$L4;8w!pj3TY!zEVGjh2sWF zZ%0RSi3eLRgzH^FTMI-#8s>Y`9X<+%@NKxsTj?v z07Fc3pwvv&&V%=vB3fJD;^qo#B|G`o$MVo5J8R0vp6?7n!doxs!5wQLZIPMcWKQry zz}r25BjZ0pTsEIp!*+~~#-dhGU#lnu5G2XEm%tMkKF`?i>=~&bxm`o7ETM}X-~0eW z{L1xyKD-jQ6RF`V+}-fF$NakC`8|Uj*vDZ1va8Y0*&9@j2c*a3pq1w~L+pnhmz{Zh zrug)&mO|40#INj}vV(VPx=+9B^a+w1^hV~bsyTO6< zBt7v2apD)%$FqeIqF!KP@~}v=_WL(<2lafBG&ho$)5^ZzDO(H7Q#?5{(eJ+cHQ|oF z&C#-vxqdanH#4W;&k-l`9r1`D@j1xl+m)JPA$yi3NJZ=IKz=)Lvq?Pl%TNfZg3 zw?%Ad{kNUk1#Ys{bk9lug!rnuBTig;sbmtdmqHO-lyTy5388}hCj`zOY=+6dUhO}p zad6IGAgW9J+~Eb`PYpU=Aw(LzR;dr?m6dZ5sRuewYr2R!t!z`630*NBLOOg?94Q$5 z6j|;3-hXn`wk>&EBDjLT{ween=wNxOomeQN=#?Y4LP=i|wE$0*3apEks=6h%7m?iE z8WxijFv#6-o~>)@9@}dgmGrRTNQiA+g*2$0!j*+KpFV6hP z>7LN>W?^>rb`fWypG1NssNw37oV2{jKyL*I@#vFSIcj~#wJF!0Pz2idfaZDb+^8(e z<1c9PT@S>+W*Xk`3pYB=nsp$pF81vSrcw*mn~x)!U)MLO)zp!@blzL)X{n1nyy?7Q zXY3P0+eIv8Bk;tINXVAeMH@tw4+;mKmo%L}X%zMDV7xAwRo!;Rbp}4eZHcu{BcJDY zj{ICy5VdTcyli#IxsU~;9w)$#+L2Rq)&+BErZh_WkFFuPe_SSdLH_o#OZ?)*E#UPK z*ZF+ViXt>@bTj+>b`omio#n!+y^RB1_RcWPdb@APWq3w+CzIukr?o6uLJ?Ig86>oA zvcPMN(dwD%nktOb<*8Cs4pD4p1J6C+N?Ze+)hU(-$$m^1Y9<*a7A{&{MpAjlw6LMU{3=e(N^bX_M2H!AM?t~B83KTMm$X#^5>!gc3GBNP1^qI!p;cD#%<1BC4_;u&(ivB0y z{wXf|G;w9=1h%-$%!I5oM8tN=`j&uh==B1BJUQ2~C`rMki{r0ldaJ>pA9S+;M zi2tL7Mq(&zl=D>SoNCv=`k3j~Rg_CQ%J+?-q`_mcI`8R%3YyJapjM#~TJabB6F z&<-bCO0HOzhvaHpdR%}(Sh3K#L0Z{*<-Xzro8*o_W`6b7(!{nJ&o}hkQ)EW>6Ex0J zcmxY0`6aJBPw262n-+=3l2};v@~rud=lcBeJ~@`uFEm&OIo_;Yu9iYzOvP!FY=ui4|Gb@@fp*Rh4G$-!!JheRe!Oc3 z&HLQXkI)$|J7Xi(PISu~2Re7g$bPO4g5FLev4{Rmf>$uSiiJ4>$`yScV8?4+&{EYW zX(`2^yOoE}8!{yta3B$^>;{RYrAnY!%L!r2tVL4PRIzv+_k)_*34yc z)e=M8kFN&KdU*MvqJsRXGkPbyH0~x!7$>kqNTiE|18wIWclYI5&uy~2;*Z@3Iv)+9 zsmOEfRT_f=lT84Lm6Ada@VmYvS^4U4&7dT{eQBU+-8!Vu#*r>4cqL}*)5p2fKEMk) z*PoOMKDcBrb-7xl0e(Dw3T##|taJPgk;Opr!x9Uj*M4(cQQ9~j3kd%l z#Bn*jivRpr`G_98C&5|r<`L$afL^Y*mz~Xg^M#*mlK=BsmsNXh-oa4!P^aRWj9f#L zYNJ9-@8oZFT<^4il1vZmdq-;%nd}@Y=#Yi5?#){E@n754=lfMiNOWm2XXR zgl3Red22^5WP;hghhr-Ooql|wLV4xr&a1hQu#CoaFTD^Z^Y-*rhc-vxVV(FY+E@ga zJN=(CxTr~Nr17D+Gh@Yk+Ar1$aH6&7N)WBgw-aJFu?$YEI~Nds+MB-T%E_s;2ya@g z)z0N7g+HhdCqZ2An*=-|vZ$5qNg_U|LIMWfcqkdI&xe&CWy-j7_fv=nz~EZ56V7X> zjHjDt1fD$`^YXWCjJ+h$p%GifGBLo#MJdoHfK}?oRjHvXFC8j(0kY)9h1!)NB!x zSjwBybbYOfC3`kyui~+9CoGN2Wi>C6g%$ZKA_+ESa+B-c-7jBh7=RWhu_nEm`z>Yl zFNp0P0QLuZm#=ZuUA;PlK1n^7$D>@s%slzqPp)^9;-c7*o{6?Mi67_Ae3jQ3eyw>s zd?%aqAwLXSS`9q9BNa-D$OnumO*M#jdA+5>)Yc-b6E&4_JxfGFAOv_H5QZN0ZR|?Z zx~Br9S2jL6^tpMntR6!Ii#qEGtXI4K2d@?!&%pH*%k2|qQ^g^(sgbwX#y;=~uVkUsNDG3oy#*Cq)sv>bDrA>NCsA5!8up&O%+dethw*NBPo{ z2bzCr^GkQys*^4CHL(a%cUxj-aZxgYvLE?_vTrg8s7GT-uj9?0vioxrl>OQC!kt~G zZh<@euu(2YWD{b)Ef>JfB9nqbkL-JI)y_Q4o1&T9l#Zc!mXbFZ5x-a)MC*JwAra@- z02acNyR&`Z|Q7X)S-=j2)aUvcW)OQ>b#wI`(-sD*YoxKpdcX|W~-chrpi4*f|2!J#YY zdo|MFMzM4)f>~|9%ieVPT)RY>JXH9JTNIYxZ|9io6Ye~mTiLDfMKO-;aaat^4^A1$ zc;J+_z$x2ofIC}N0G~Z1$Mtlc-}-^aSr}JpxDkRc^p*TLT z4aEcc?f&U2SnMW#`Z{EIZI95C34|`OpU`b^Bj>r2R89y$FHp>jf-NP#2-ZWIp=d#tVT#t91ZcMO{UDI9H>0mjbtX{8Q@r z7v5AhQ~4rFfqamZ$~^Gg7)%Lg!fah8uh=`9eA;L+TuSa!dJ$h1*u{9J$`~+W&Uc?? zLTMwcp5p9?tPo|x;%%*5&Y9DrkwZ73t?i~NNJc7Ui_y$AF5)VxG`wmm=u-t2IF}u#l z+n(68gz}zh3d|Iuetu2th0_G9=nF^Sj4W-4ca``lihhG+@{K*};Nt_bj+O#wG#Cm~ znxATjL6TaNzEBxf71kCQpMEOYu*ehm^AN?Rtk0#i47T{pfSZ~f#_+`c$#NL)Jm4fczlA(GL?O<2^@ zx^CjAp1xvU`FVwQ!<{EIugT@8*)r7%>BhO-ZPgQiU+(23?8K!)JAn&D0>4op#g8Aa z{Pu-Hx|cG=Y=jAJb(Hz`LVisePR2=Vl8MT?18v9j%!n{H z1t0|wDJ{uAj3!tFB!F89Je!W=DNBpR?lp@&Ui~pOT_(Hi4NPs5O;LZ}jTTlpTV%j#Vhq2I)fN>Jlz&rWp_%$of!AA|*SZ$}Y^3#gI zPOS^BCu(Qi$N@dLctmhX;tpSWsRb9-$!8tJ&gy%pQJYUHN-Elwyak)$;#{%W#spt%eeu|G=%oMe z;_>-dcdIwiHzbT^sJ=Bp{uIsOAH|3GOZ3$VXIFjWD*M*?%N0}EuVUIIfs0m!$0 z>!rhPp$}BfUm@+TQ%V({cm?#g+Wz$1-j^OWe)(b_9sClK)33{9f3et1P3ym@p?5fU zes(ssetU4HZoE49b39MKW+s0h)1l{x4yQuaSPkHzs#q2{5}zT--#7aH4CQ>@%}@at zpvGglJwaFA()XGP(PCG``cyZbQ|}jWxWXxcQ-J6EP8r`z22jo=J^h(zv5<-SEsngP z*gl2+|Hs~YMn#z|ZJ=bD+(>E!$tXD^QL-XIG9rRR34#(uf+G^02vS@ zBSA7Mii(nhf=ZGc`tG-z24==NXU@IfcWzjBxn{ZiF{|Ib>#3)ns@l8ueF?+6&yg&* zVKkF)23vDVTJ0o!QK*2cCIAWmYfTpPs6r`I<<=GcJw+Eq`ILE97U}L|_3uX%PgF#hys#stitn9lXoBAX}8^Wf~#2CbHn&3PJc4>oyGakJUA zzFIJv@_dwa?!oyRp#Dpc?Bdk-M-qq}Vb@~U0&&CPoVmiwjw8jkPBU$}+m};7AiJWl ztrXgeyw~o1zBj(Nh4~Q+TdZePcd%C~Ph=_#d>ts=akjD_3g)q~Rf5}sVPz#}fEX4g z_$xCApp{|*F4z*T;@F$RY2U`uvfjIdb$`q zAS$_>qHMhWBdzSy>a97qIedQqh0&uxyG!Wvs@q*l{&1oU7UJ&PEECho{*GNJ4iOFh zajyB4m4FR%4pf2xgZB{DcieEPft_(F^epER9xy^}6MUy~#=~tX=wz%tD4dC9&(K3# zR{Qw7qQ2{j6@D?i(T(h>_1-dL z3rey^(yhMcGcj7t5h7v;B|Xl^_RboFWahC&%#Ly#$Frp##{td@;~;`J1G*)Pnpd$R zcMRI_OOZhHEsNg6?mp={?Ojj#G%wv=#gfDf2xWeXm?mCq{EH9YlFV6GInDAz=v?aa zx%?0Q^c~b#{T<}jNA6ErM^@B#-qwAcjh_|5B$DyR`p;1Rks zQTva4a5-gNp;8Dp%qoPph}zgT%W`#aw%S}KZ{;|y5_3|UfTy(uTrdDA0L3#jPV>4D zeK^a@LYL4kbRy^Mom_YSv|T=~9Cc6dJi#N=dhg0;x8)h7j+lGng^(gk;1CA84aL;hR_vcbncL8jrsNyz@I23yNQ71VaeVLkEInT<5DdT*-heB5{asX-6^kKZgA#MTPB4DXME97iinGzWKP z_QGokh>fl*JwC2FzNU{ZOo&;HMzo#oS+j*@q~z|V{R!EP=gu*3|%_~M3DFLuN%C@vm0UDUfAw2 z!JYc_pd?21Hp8^uQoq5ske|ucHk1zP7+TJWDUPQ~w8FNPM=oE-`PUmR zi&_buTl}EjGas67ureoD=5UQjJM4X`;Ft#+k+Us46hV9n9R|@JCUmkjh^V008xse= zx^ypMNfU9!gCnJ*He0L2bx|5A$-t)8N+$rf~E~d`ZyNMH!l0TPhD;z zMrF6HNoAYaakl%s3MO4wxxq$6GpPi6eNmuL2#YEO{sgoZ0~~1LKf1o07=YzlEay`U zbNN2SM9G@3{Ae*De>(v9{&hkq9_a{=zrXROf1tXnSNiBJQ57UydXVK8uzc5B;`0;E z?_HM#6!IO{V#V$oid%fF+vcGs%C3J9TgTJ#p97&sb_;T>V=oKq5a{3;Hp(>0~^p8?p;*6}=Qg zy%E~*VFia4el1N5Jbl-zR?DUGaeyTNEf zbC=*PW8V&1C_}XQrc?Ubm;~UkC2`=TyeB zFH-ac=;I555RvXOAUd|>+;~7-mDB6D4YXI#ul#du6H0emv%*k9|Yaa$NkL4BPV9mp1Jt2AQ`2sS(Cz78qL2 zqQk?fut0k0JLJ2(8-z!iw#R9Qqt&TOJqDOk)6>-khPK>ywpE86MqkodF&`BQCc69t z*2;FA3Vf1TGeU?YIH+=KfM0WY}DQRZzhioueds%$aG&DQIOyZ zCNi%hXtg${Y_>utMiC(F|JT2&h+v&W%=YFqZ_hW}Znt$Y$OlV^Y&}8p2>QWS zz#$JJ38jEqt#o_nXbV-rk+fX*t;3|H*8Q#G`1DVZVH4(5nz108^%+muS9`j32{V@= z#&`Rsmbq~r6OlN6mNk61h>h*EdN0u}f1pF&4WOB!dI&HB*XFVMCDwI^>@`{8`lqLo z?a&=dsBa;v50ct(AelwDwivAwenqDg;qOJ9#?x@g1{77W`C83_)@~$PXMT9h@}$>s z5X~8+uie!NxRQvDl>hd-0&i<9Oc-8~?R5z$%fV|RyTd2m!o2=|v`lG2phbloVR8Cn z=9!(D!JVLdgSMeG<_FTC)yR~hByhANI0!)ujufzu_QDdI5z3(kmcv|o5g_RjbA5!$ zWkmTxS5WwDz4HI?C(lYDQ>*8;m}}N>-s3@6x4>8qwjdBFCkXAXRlL*|eW3XAWn1jJ zW|sdq>5}~BI?dpK>!kgZ$)?Z!sUjcJIYru6(FS~Z6Spvxhzbs8`2K>P55Z*ZDQkex6PWGmA*zzD*XiP)8p zIHBZ^lmy@;UCrj6EzF(5Y18@1luL%lWmtLVR2A(-(~nJhUZ|3+{L)G!XyxeD=T_`R$?G5LJqTT;uW=Wze@#h@7D)W|qgSD+xPF>O zz}%N$+@sQ;5ft5pLP}G?b$56vaB4Sp>q7`mN4=TZI~bL8QRzFkxP(gp9maPQ$Oe$1 z6YU^_vvne?U&tc>M|xuOA(h!~qMvz-5mK4e<^SGyK=l_=A{Xjg@2^$QZF1!+Y~j>6 zvNqe5$drg$HJCO8Nk@b!a@Lgas1{u|1ZQEu0XL#X)#OHdhAeriqb2iC@we_-ZaQyG zp?(gF6h5z2P+Wh9?DpgMc4sh@zl-B|Q!}X8g5soTl>2{eS?kCTp-f+1_AEAlId8Rd zwZ_l#iW2g~Zk|8R)>-fZR#p}yh-^7QeoTfjA~DDz%C`FL<)@LE_R?vlgYQc|cqU(_ zm-<0m2#H%I>o8d|;bXVg8Bb#Nuzbey^^h=HG0sgzAcj z9>2-(P9!Ui*96}CVvU`HNK^r2ceHBfj{U^5CGhI_*2Fj_O|1JqiN_8+OIG!{n1h`B zJH5%FqeJVGcPHt8n#%sp_syRJkMHW;H2udZsl!W75ze^Z(;sJiudeU8JWj5ULM}| zvS)HY%sl%E)KY?6@?Bb5c#kNk8}sDEJLE846ul@~&{Rk?_yjS!7Ap6J?v1x%N3N=6 zs1|Xq5xy{v8If({8km05CY#IFcM131al$J`&>sdQw5*O78;r2o=E z?JMc~o_y%|P`5eTb-YKU-}S5HsW2{Yfs?#EY#Fwi%5-{4?7d3jFKu4h=!F|nR9p~k zS|IlB<9{%x(6qZg37iUo3i8&F1u$VD&Pzo(Z0RPSqB8Ub1n4ABpHl}BRsewCeLK`A{ts_K$K4u1;~?GkXpY6WYeb~ zCe%a`*P{7JXy&%~UU`sh2pl<2cTIsBmrnLoSHU~2t~p=H7v(|My3h8ke71C`aF56h z)G8L`;Ht9a)l|kEWi})&0(#PyC|#Lel|Ga&!>Q!^P3l<1%1{f@+D%c)6aM5vj9~^8 zmq{)&%9FJci?Xp0QokdaW|%z2OK42Wmm`ytBU6B%5-i)t{FLD3=wR4GGC2I6B zV*C|+Jpr5;gie)4jV$rHBL z7X*L`7x0v*7kFSyVJ`N6^{DFUw_$a;pT{(tnlD(gV?F98!@ft=7F(k|$^$S4dD$&- z@?|qWQ2j`d{Ts$0M0-@^HQ-U+tfPIowr=fgNzPWC;EzBmcqL&0@L(fS^+QgOfnFX2 zIVaLs}=_37n2wuNsw|tc!*-jojX(wiQ*5ic+q@ z(NmKrZ6eCO!^l}+XwT3CZ`LtB(F^jSRjVASSC`Sc4G0j#ESU-32{6DVV@Cl$ z6e zhB18iq!^Cn^Npo_J|E&|#4d)_w&K;rg5msMqwQVDfjH%u4$%TEl0^YRTOk^4S%fq} zkpF~vH4&n2FVYx+Km`t{(LN<#gyxt@N|(RPmY+Xmfn4tVLWlZRLACI+EFZyGOHhh? z{1HcM8y0HgxOjhS8j3yBq}^i8IBiLjhr`RIH&|L?Aes5t$M7LcS9PLX+%hNPYC8ZNENv zdT2xpumHg#LEYOCsi8$pg{y7CulRSq2gG@d+_SW?8S^ONz6=7bf(4udq}w>g%|1av zAQJuBZzT8q=IEZ^Omprku{0EN^XiH>q@i z6kMr{=#%>0%v>Y2a<)^gQ}9dKRY&909diPsFKXiV&I^myuk!85p?_+a$IQrmn34V( zW}KSJA9A242d3hWjTl66uua`T`tYa38GR+p_E|J_30y_mIQsA_%QPoDM=dXeD1fCT zE72{;gCnr-NIrKVlrzPu`?wA$pwFJ%`xx7I|EdERGsa*{X3+bedW@OO0lUy*0_mUa zqMj7;R9P83Ke*`i0Q z^Q)EzEE;p=P$abtGHt>;?}%kqq#l}^vo=RaHWT^eDKz^;SrS3c4~|6We6dHXfK!Rm zJ&iQSxiuM?ibbAHYhr1+N0=H*%Wc#9K{LQabXR8AZz9TW9aY^{sBJ~CYaG$iCnbeV zr-JcGpnxRaNB)$4S7uvO-5#ep^JZ6us*k=_=6TzY+?d>0W`zgGx8aE@(WxAnQ#H-c zf!=cw0vs4UdcD{4yQG<~2dcP_-(|aAq3ND3@{U#orETZ#Le6RHfrYHGfugVMZnR`N zv!4hxeahk|2~e$WxgB;xu3qi;Paz6gk!Qn?WZw!R&2+Rkn&qBaJ@dB&`iTJH*N6%nZj+w>qTgyMdsA>=FZeSWQ3H|Jyyq=fByJp_vFlGbeo~;bx*uC) zSZ(%g?ead2x!xQ-uT*W%@38r~E%k~7s$Tsq) z%#SssVz-viwv4735oEQ8Q4XTYup&e+xKxUOBpVZ2vi%8!4`EmkqCy<22m_KSACt-{ z(CjBa)#-b7QwRI=C%buPc<8qnd>%`5pfu7qc4I7J6uO{VhA=WUcXV5xfa%tw=>1v- zR}_1Q6*kBj;wU++DN60+kz_vfqPU>N1&M^*J5hS)%r#CYFvs*ANh&kK0au^xpd?u0 zPJHpg;TV26$I3!`|ZvKjOd`u?FYYMhq)m;PbrxU~h#E z*q~H0F1|10>Kh zgaF#GdFdAz{oCca%{uJU;r3qD0oki>&vYhYBLJI25P+(~k$m!>W_-Eay+;b}7LAA* ziA&si1=MOX{bB7hu_B%*?D9q(3U;j*!y4j14Sf?jUOm473xA`pnJ{4$_9F-Qpmc+B zR&XbqtFkjia`v?Cre*J)C)qg+{!^VxHS5o)URf5x&ql&Qdd@dyT4P04U~`!SyHU5$ z^61u4_l(eEmo<|_j$7Ed^wMIBDzS`MTKTk*wl67AZ?TeM*z-TO-X|nkt=CSW5HMTZg`++=B@Anz3Pj2{nFw@SnVNRO3nUlgQml+kmsn;B` zsG>3w1@9e3fwUQjkyDTuaj08T-3{W_mup%-W$|K*Vn@W)Iz`dN%ugHBSa#{!+QG7m z#r0MFy1KrJF)Y1I2lnCpTYmY3k)fJ%!2B?fDIlvqJpkPN>S&!$!cos zv#0b+aW|O>(TjqP{&7_Gt0`$ac_w{ib!BV#726*bGL}l-MmG|juDXd}4N=e|6b9?V zLY(^Z z1mdIM6E5u<+XzRQ2(;!hLkaAckdT5C@K9~ zo@~dt&(Dkg2TbDe()Oj<{C7k7%FUE@OG}jED>5C0RlJniuPkmX{X#@cN`<7Y#K;4y zkrq}Xu^l&_S}|9}u4J5dj~N#ou*hrPsvUrm;AhFwN9X*~6OVBe5Sf>d=rDeHW~8E( zC8u}tfFcnPlt@iyww`LhF5mvpiKpc)A8j3fmIYH|NQQH5`8 zL}wo16!Ab5NZyXuO?Z%~_{AEU{@EJb$vg99YETqgolJpQ7H*4G43*znud}V@oG1P} zC`nGOSYV;_X@U0D;^Cj`pafAl`34Q8B&QP>cZQrHiDah5oCutvorYhJqSA^cL#)rW zFf3W#JLhImNT*(RpTsFh_?V2Th7Z~n)hmjvjq0)btY~Sg^;c<|e=muvy%F|V zSw@S{4JsF>tJ1$n6EC4(NJFkZtXV+@^45+_cnZ6HxV5VEL%Ez1O1btA6*pUee3t~&;D>W=dfGjQE9uJVFMCD-yceJ z#)K)k>A9C;>_rQn{$A41KLHab29?xn-;pt)am@D=WTV0)o0CNXVOb}&actnW=%8?v zgH}iVulk&RtAyn6(p%!8mk*CtyvF$SXCJBw7Ngm}#Zv2^uw)UckPXE6Gk;k$I~hEV z>>9ql%TBh7Px57aZ3v5S87_wy5^V_H?n{`)D)97HM^K^DA|pd9I4e-y1`V-SugyA^a-hx3ceXG>t_R*36XXfbS1g^jLnZq9?0p<2VJ87vOmly;iBt}6QWyI;n6}XGRvb!k~+DuK0QnZ z)_~@vAd-f^^3tt8@lp*@fCA_Uxval|-3P6%T`WPVIfh$rCLa4cERl8F=-FM$ZA4?~ z!B^lAtfM2GZF@@ElITyl-kw8%S_3#53OvG>$d^1nd-|qHT~z(nwq?GFzc=q#i)#&& z7(RLpGsHAO^@K)^WT51#uKb=YgW)d73l4i;FXrBDv;Oc&aP+0v@Rz;!u&En)I2bA{ z=AEZXtDlp_l53`}`(iVx5x&rSg13~(bMXB>JH6@F}DUj^&4nX7z zO7VZAM!cZMoTXnHz7&6H>nO0$YPs&Ku=8W!4S!wW*!7yl2iF@3^p4;$DfLku#rbQD z1H^PeYd6My+Tp?w($b;1=qJaqhGxXPR3K%%F8|I!VYWxntDf5-_=#=hBHqc?zE_|mtHCLSl zCE4Z8qQ29K8SN$9*&?ALKl`q_Sg z6!b^997g&NMgG`i__Xmcw z3&yk!zgu#hhr&e9mm;^Ne2(}%&2ga@t2vf5{$6tkO5DP=`)U+g*#7vpiceJ7vjAPp zsSixPee@0sjf^t@vAY9=J<&kMU)MA;n4mraXjUC@1a-({rR0WZb)8r0y#!acXOWu1 zRjA*Ntx45yXOB!<5STfQ5W#A{1IMqDx%3Q7=#LowwGC~T?hXHRY}JcN_#1bPj@dk3Vi1o-T z7x%oD=0Diy80R1C^H{CAoNJo3c3h(_01D(NtFY_gw-?bH?U6jkslidVaa000b^fKG&dOROMd8lMQ%DzM_V^;6v}3_lJWjSxKQ zg5uaGUArGT#jtcq;EK7O1`rhV?XxmC)%}$SS9G^%HDxE>TbIlh+ z04$(ReO~+1%1Mk&jG8HOdvlCa4NGJ7bAA!d+#kyg+V1?2>sXlEiFeBG)x9%K;;hkQ zndvl^5du>z!-`+x{#;P41eM))DroWRC?Z*PJB=8sz<3u9MpBe;SZ>o z(06OEB)Emr;^mnI2b#K}+cn~Dp!Gd`VWLHtbz~wENn;ykv7|e+f|HF}a&7t&%?2g4 z!^Ev20Cge?R!U(YyG7@NDIl}mN8OcOR){TlLMj!&p|Yo1HUoo4&)>MFF;zLv*_LK$ zK7O}v@eTc21Vdoze7i$(yEyTwwY16I)I=tS(wJCCRZ_K#elImKOY^ll2BPPXy3E?k zfn6EHjV=g6y#dgT|8iYcOC?Ev06rKz>|us{O30td`52L687Q|f%12T2@o@#K-^bniOhq81+Q(!Dvn`P zC1=sYrZd@5aI6wQ)md6qp1pe^L1t(t*C7r0gS=cYIlH3a@Y$aG{wp8mK!G}DUc3{V zJ1QM1qJ;7;dqGSwmk^3ZOK7^N1$6OS@gI^F$Zn#v%?Isvm6vdG4&ps<@<6H4E(a{o zK0e=VOYYm+fYkhNgBDuC8_N9`f8Ls#)F$XJ+RcH-f=!5M<*7uV-gwO+?wAV@oiB$4 ztAjoeJ2Mth!=mN*_0?^>7v+b(Cv+{WP@U-9@^mP;Qo8j! z;ky!#qmyO5f!Xq&fILk9LLSb!pxc1S?d_(5GeNEFx?)eGdL7ViFp`I$N_iRNGBH#( z4TzoOs6SMyx32QcCWU~5S4koG62$s?#5!P!lx`zpYY&NvYXXz*p>_ljwqH(q9@AO4 zX*o!ohx`6DaULInbxV+q#fMv|5yr;GG8?u8s<;@hqanh29RY?%)Vz!$ua#zq6*>?o^5QSDXv{i06DxCUNnDL%nX6j7U?j09KvidKJimW`j(63ntY8VnRL5 zcLXX{Uk5oSYRfp{RHTSCT`6~rAfS5yPLYTZ1XK@PqYj=#7X+K8cocQOj7}Zpb!x&S z%<&P>4ewN9z9^F+-O_2{$*YST8DN~D`f41fDB4+E^BrGAgxneiE%P!ASQ3vGdY7|A zN4gl4WY8{%k1ke2C>4ea7At4#sv7~ly2EwA1zq-0Dl?TWr+OuNGGDMH;fNlR+mSIQhnZ`^NY-Skwy3*DZOWkNlGE+OiCjjQUS_u)s}gy*V5YYD$MW+M@3jE zH&Ef4;7lMCQ=i|M=fvw&)P{POtEf_9VZGST%fZw2^{9Z;d|x#OuBDKfhmRAf z4_0%eQL-CFIgsN^a}<`wur!sCQ?VLt+>s^d)aSKc5{pZxky? zs-sMs0bX*%A=beqn+YO6e?;0VBBf@=Nn!oYfM>gQ7TJfnz%>|Zus8sAO_3>td4fX( zca0EERRh-(VPRuyq947RaQ|D~^QKowPr>j$k)l^KhP9J%8t@naWKUyX7RLmkXxXq0cl0KW5NN>14$%7w0C3!xe=-HNN`+0_& zxX`o@S3fpoE*s`d+{p`sGhjNBd4{b~y3ES3chW}T89s4^>GNNh&fmeOWIb`~i1d8e zkxpLs_G5f&=g2StxZLrbY8gwxQ;#v}=yKm=%DQl}7CF@NvGr%H>v854Zj1@haK$*V z#!VPY6z;eWNTFNH zwltVT`E>TqkDDkaW{$N;ed@H0^QQ=$P;Y>i83vX~ppC;5MGS*EkTBflvRi#i7h5-d zo;26m=-!8XzfRV~M_#!I5&E|k^A9#@4wy1JHQIB}ly7sz#yy{e*X5R{xvzbZy*AQ7 zb4?(>IWMSS#8wa!uioA@rRw&}>FBsIF7R&3RCbMCh_-&QSfMqSYMVDj!I|MuGF-9Q zM@EFj#vi=gGtR4)%GE?e@NN*FsF0BJa@^CCj0ScuWgBowopKd}bf{j_Zt&8|XI;n~ z-*7IfS-;&o*;ZVak$FGGUVe!oFmbAo;MxiN8hFzozpA0;T5RZH}ak%!zTj+d4hSQiPIao39ngNjG6WdEG;5S8pyKbdbP5=>9SZ$ zXae#AL$ZCAz9WPqc`b=7qQbaA{l^!MZ$YUCr_zJ>91=Qeny`4U_r`jM>o(_qXT^%^ z%bRQO=T4BCT%fcj0LR!VoAJ?1pASa^w{HcVZqm(>oD$Q|7_Qh5BvLA3NTt;0q>}Hzqnp2vg0e zMHO0mLlON1K^z#cYQG!gyFmZj`3) z_5)KzjnC>ZJCq*pecfnj@8p!fUWi;7+%gTw?6mi`UZnQHJu1M_st&o(IR4G3ws^N> zH&Ij9{BN~IUB4k0R2GAGiGn(zT!R&~IK7HqISw5?v4J)@VlATt0C3VmdD z`)5S)9E?6p|ER%&+N2BomJ&!t(W#aPOXLrowrye*Q5c(6BIi)Zu_8AMi+<%NXE$eT z7yD^r@2i&n*;jqc5uqGsYPRvw<(*5`uXJ<}qj+@_A0Y zW5C+*PF(yRtG4zYjZ%xbC!SODP-#0h+iur}!zjs_{Nn{(1Q^Dll=i=RM9yu5L@wEI z%s|O#zLXxLL7ZMainKqWXE)jGPVc-V@OkO%g9q0VEH|c+FVz*6?d*|1o%+L4%^Brc zX+T69iv;&-+Tvk*yG8eh-LgJl!w&~-Sk~1db1E;*7PVP_YP+B4c3P09;7^Lj-MIhI zPp%Jj^!MwOe$e``9f9mV@0qCO4Usd=DX?8^R5oy70%|ZFlH)R%5IXxksTR1e$1GVp zafJRY`|O$S$UQmXGwyI82DSByF-#+F+sAItp$;z_G8x(9^^}9NfyaseAwU1{ld0X`drl_>B!rX&^z*c?I15?{>Sz?%>X};3@tUj*qU+mPr87zrPB^d;SZkXY$T70^Z}P=#=yBnGV z5drFraBXBUtFuUTNswk#3V+DLZ&pOjY+1mHKIAt_PFAKqerCAxf;#G^WzG6(LDw8d>W#)QA8YEENCI&-q6L327Txx=wAO*|a(=n%h-WOh z_CgOuw#I(wxo+99t9em7e=(xu-_WT7nobjU>2xqP{dr&QJhCb=uU@|8n>k_o?Z&m! z=fG&wKo+%zwIDVd#32B9dx##Y`I)Ph4`WUEhGCx`UO4P$8#_ZOyY%3VYSfPn@Z8-k zmTsMnP-Ioe-{?1Wv=m=NOYtAROooOGPvaC99k#~0kqz9vb)=zVll)fxTp76I)?3U` zL2L3F;PNuim0b|cdz%2_lAOSQQE;kEclm*E!XC{>^m6UHVy*Bl&AV33DlQEiJ6?Go z;r4_utK?PZnr@s_X&S!n`_hav?$jlgkG<7oQ;iP>O2{$p`v=k7Rs&3Yy( z>6QDmXJNel=it`z@bR?FZ1v2RPUD8P(|S1L>=rn?080&7zSc4H88DY0#d-rs(7qYg zevRH2yrbeI9Mt1q$a&`Y^pJB}X+YJ_JV^dnO;xtF#>g|B`jO}_I-V7+T#{0ZJ*NQTN!sWvA0x$b9gICW*7uvwGzvsZyEU_6qI zNE-(XMF$e*kKfXUv6lKSj=(b(^JxU^Hq1=;9c6?wa{-Ky(|ztYxW1ZlE^v!H$y57E z=wG6696Ab5>_*{h4;~1q6<02>QL(PerYJf{Ir($(1j}-n5RBVGt=R=!Xi|62p$Arg zlf1pdIiFdmD$HZl>QA0_Ck9QBS$=F<242R|S}K|&Z)@og5VIf&d$Jemwy04a{9}l~&H_7uU z9HNEOuqm^Ek0EXi0temEjC0^ywpAB{F;HpXUTZ*DsLlAI$2)Hmq-Dg8O zAF8XHejD6jUvIEQ=jbEU32%jx@SvIAvx=Dqc1G9%gs`GJ=bfI(cl-mqm|1-t@)nVl z{AzSN<+B@urR<`Y{li}67^+e7sJJk88-FzwrA8{8Q1DJjhTZ$S2N9FJn`yE5r0!cu z_ZSS!07d9!3F;w-JF&zf823*%<_MTZ^0Yg!!`bC|q}< zu65oMPz)sMka9$&Q|M@BEUmAPf%Aw;G(g&a3??DEw9#Q z>_M2PGcG&WBew90o2L1n63OUok{Zv z#FbPy`ZCxDd=V4_FxNIkxFYfoJ;-v|b1E$bkr*e5O(QCK?WZ5Z4H}^LiZ%|ItA1$` zi{mMEQ-y0pe3_IPZS!*z(7dPq$LwrZP5@0P zSaNkv4$O?9MS3nuNYzdoenQQgkOaH!@{i zEUjpGUF!-WQ>0)g(N<3+>-Y)Q4VrgnWz82KIlw0t3kb7 zP?%aCu7LbqZtCTg0c`Tf#L8j%In3ZC#%SD-kDcc2?a`}WqWgo<4*$t$(j|;V^xePs zsIQ!H{^7&hFKxFzWDAd9Ft8rTOBrbfc%}uPg#bsc@gOm=fTJt|p1%T8C&Lv_I$Xk& zoP@^pgvR;}hqkS+_5Pw*ZLz?f@b(N(IK+*`sEcSTCM|v=Uq)Ne0b|9mKUDL?-wl=DD58B{-EdHcuhJNn1%^97Pa z>-&`YIfZL~*gj(=ecH;v@j+-JC=%LzfD_Eurx(&UMN3C}QM&jRe@Fbk3<)$7!M#vz>^ zEupi%L*t76AaQtWAtou=1Zr)YZTb3%x{|9gaf(|e@;|4CM}GPdd&me?ZyEUas<+^a zMll2}z{f*izoo#ma9UlCA)g&5ksEmQ{K`!5LZB-cNZazt%k(!;-fC`oH%ot8{t(g61%@NYHv0=UCYMW9Ar5CjAF z994nRs0$V06?;hQ$#R{Z0}Tn#FeoxnWA>X~!Xn}VRl*||72b=`qhp_s9Rgzc^8F+S zhl1f{&8abYbToVOLV#6{;x<|VNrUi#`;ITC0MAKTFg>r}jB@TP$N4wq{A-JuuJ%qd zo8=!^-R{1caPFj-LDsh-ONbk1>xs?7hJgpmhYUh}Z=5uC#uwlgM_Qo2A& zI=nT-H?R}R@B7XBiGx#zjCy{`>+cU9RU9{!DQho$!?d-nsZv(eJ}hxwKJaP#a9~+W znmuvC8Aq?i)f@@@aQLF{d;pwE4V*lJ=psXpkH|=q@KHz9TI z1h_iNSD*@=?Pi+4e>S0Hb1}-h{zsb<-L#kzxFQ?EXO>jxDcsH6!wZ|vV3RhktICR} z5I|T*mweDNb#_VUBz#nU{KJjqCq2u~sw5O764Vgd z5Xpu^4aetT1Kl~6s2~`6t;|U`ac=|cv2lRA+h8VNgc^==m>`G0chmj$|G;Xjy1P~r zG^ju5BnQ|si4!zKrV(I{su8&MTOOp6{7QE-!$Jn%iG4Oy)(zhc^co3`D|ig2qj0{{ z8jxkV{U9k&amDEg2IHDXNY-$9;AsIpfAo`H!_eo+S^lnltiy<)*o#*=S@!8OynU)WS7IhpR~P=3Sd&vgRFLvYb68ayVi zJaYAV5Y)#ZZUvv$0qU#-YB&IFcs5FeYD)4-N(6dJh(owQuhyLeA)W|eQ57-?n^mq6 z=?yLRcYIN!8cnAE%L|z#0drCA&LBMK`*U4zNM|7Y<>YiE_LrVaD*R4ENS@(Bg&+s3 z*rI`3pC*9Aso+XVXpODP;eTgdkRu2Oq$1$Maq#e@x%ncRTi~;_@Q5%J>8MJgX=;b<7&!?i0le-cPQF@8d?msvbMb0Z zxKNp<=ya(Ay9zvmFY(D+M!n29>8mf6NH7Ay3&n7H55}9N2ViX3vI}EM`In2OZ}i$l z%PDkj+>0-OTV+CB)U2Ewa^6^WFY>}gE@SXeCT)@Sm28+Q5abf&R$;7^z|o+Md(R=C zIe)b&e*Os9R?Nl`{c7X9h>WH=DL>Eqs|-WcCH>DR!i2Y!Nh(l2IuIzJ9EE5*rRiI7 zdi#e7s9!Kb^$UEerw53h3nKc<`dKP6T8svh8XYKIUsB@-Jx8b`-X$npa%zN4XU~Jz zH|8$@i_S&QpMag?<_m9b!I@3U4nP>i?CFly zjdJsZ2Xte&S~%F%Ii2+EGg_kOdTZL7ib zfSqG)dK`LH7}3J>tb>x5!dM?g@3CDex1D{=^t%#98FfcO9U@3u2pTA^E$wabwCR&< zY@W8e&NwlzohXstKh7jRppOw@3}cmks0IQGaKEQCcJK7N*1wtZaH5H9hPhdpql!JiC{xV#vX1aqs8cUX^G9&X2}@$1A6A zf21X&H!EE1AbVUHQu0xIVienur+o39OPpF~+^x$Y5O!eVo zv;#3v{GVG`?tz8XrSGv`(B#IYf(dkGSpT#UtVAa*T^@Kz1=>r@CH^%$9f!$w1+CZc zHrK-=P^t*dT}-J$WoRgqr7vXlm)zS#oX%yhf{8S!3jP1V-;ya7BPNA05E1(0QrGeS z*On+_sb+O_+Ez`^_-IxT+f_rYWEpr?53f6hdj2!y;9+UsTX8qEL(d8tpKc?#X}Bx%wcAamh0 zOx&k~=m_Jx2B*!*OLRWm5S!(h3Rn?HqDChgnDF!6jBA$*0~qtVhzs7D+she|0lcmz=J`Sy_I`_zqAnY7ZXsM_DwIEI%+y_21IrAOlW2vb(; zKiQ7WeTKMiG~LPk@!4st(<-PxeGRcbXn1 zZWkFAZP=}-$^PHi3erDpMZ*8T6&Ph(*Zcppt=P84I|QoXNsE_%+o&B7Wne(Jxx52n z|1(t3guOi*2Pu|c(1j7EF5zWRv}8y5ee+Tix)*8?FztZJ2En@!*X5_-go$r_;$$8zx2+&n zwoD0dJrXZ|c*&iDBSSR1%)FA+b#CIlc-G1IZ(Zk}e#5CEWP@9|;$4$!c+U$TB_e6L za{PwNS;-5Z^Zb^@+FD&GKjJAHIS;MAs%IXpPEcW*(Shk1z!fzZZ{?4i-pP7h^4jIK zsgqt3(Zj&75h;RD{Z@eiHo|(z-`UB{@1DiKa5zgnTeO12a`$(B4Z@Tc~{h9Eb`)q*; zB8PPwf7?PbHXt6QCx#5m{@dTRXt{EFilUXVw%RhDT$kOgu=Re36cUUgD6~m|SfFjB$SP$%uZP z!>obw2%F<0y;2~@uB4&7#bqs#6~bBiDEuyk^~j;}!(U#{%ky2;==AiNG$0Pnk%)LM zC)54)!sW7nIpa?e7l*7eUR7@rw$Ibzr8sHx`7FITVB2SVzen~rzo)S^5%$W5G`9IE z{mtWH!CL(uukuEHjlLu#PtSMcQQ|3IBKPw4NPkoP-hOlFVI@U-O{4TZ?^WvmOTXtx zN^!$ZkoF1;Kc!&uuGWjCfhHq~gpHJ>CZp6{s4cQV1T$9wCWJGegNiGAE~4Jlenq^qdfn3A1jYHHwis`}gg z+}?Ru8H?G^rKpQOq`_K}N9k%7F74URXI2o4m(7p^c(II|zi={_!s{3T5ATgtUBs;i zeevhssl191al3I|Xb&&Ir4BAn9r(p82A^_qrz7&=l}5G(jZE1`*;I;BCw>?HJ?h!e zU8eP}PLDIFTH*K6aXI7eLYko-T-(dAVPo2ldfqBQ2?;)^uvfp@POUSMuC?z^;pFA6 z0C)zj8r;#{dCNK?i`mbf1N+%~2+nS-?eUS}e6yMBn_Kczc2y5hL!1Zj^6&T4(4d4T z_6_45;o|w>ozJXeRX2S@7m|iDBe`A}!pw_A3rB8cb01jO114Y0y41h5sP`>MGvq1D zB<%R@yMaaxvdK{*Jfiu$NW`#6(8y(kQjnD>+5-g{0NXh{mk_u)ezkR~lYeVfu_|_& z4>-D^pEpawo}&jQ?>YLA-yN3;QQ=O>?Nyf20~|L?UH6dZ_uL<0INaO|J0$mB(H=hJ z91QzE+Pl-Jrp`1D;D8AvXn=!A1r?G34}n3#CQGY^MNvSJMXM+xh!GT9s8}5+HWUy# z8byIZrC^BEQe_Fm;b0{YS=13GVX*^akwun4WeJFcMM&n}3ki&8Jg1$RbLPW5`I?)1 z-}m>v&;Nb$CFwp+yw|tWlt>~b%{-AW;{J)qY|-ChqtEBiza%y8Me;2F&B&(3Je##u zo-4t^J>#MU>Eq37^KI-72L=zMaC=c(q$EVHijtVw zy>fmD!_kshh;zZDt)FoS4O7_C=M!#xvfj*Z(?J|Ei4(Y{(RFus|ZWci;n0Wr{pCnbw^9|F-vm5i9eyS zKtH)9lxE-}Kh6H-N;|L4Z3?KkSO2hIPiKtJBdiEv_=@}Nc%&)p^#C7SaSFyw&>fHN&Azi+0rY0oS9MDpw+O`!J^Tt}N z{R1Nvd(I#(m5eL<0Xy*nP9HCM_1xiFX*A<}j%Gy9MjdR=4L9!4FwwF_o0x-!CW<}R z-gR@~kmyJrdf+PWDVXQXj;%+}C0r1j$!K{u#O zCAxTrW1VcnwoKImgUe~A-|Y(w8mK+^2hx1ZuIRf^tE7bg(S57)p11qvb0_`(k73Ew zrN+wg#BJ}&uNbmHPxcCS=?9t|MT^9UZR0epUuel5vE1}*bz0*soj5;9iuT%B(9(qJ zq8i^P!IR|YWBZnQYrlKi_#vkaZ+u}9sCj4KW-8$S{iome6P9(Ft;x)Md~9(Gv@MG? ze)pvP;bfpcV<5SFR3AC(ecZ$nnmbnd)K$-B-BlKAZ}-`!SetQ{9`Y+TV18D3<-2p| zs&((sia+pAUCz#@d|tgsGvYZKJbA3n;lrs|>HC=7ole6cU!JVZ8R?NYAYp8b7;>eWJdFoY>7LtZ*UdUa~^|>F53dV^vd}3qh1>mDIA@sf2(N zP)bhJt-hj?MS9S+5t~>1`{tFx@Xy%GoL~R>=ttK7nMWM*?Z@VqaOE+HVe=tLsd)qM zRfJ{ziwZ!?KPkmu1VJcBu$a{9R7V^ma;u z><#U>opA=k|KmB~^^DW9=|;!tb{&@#1RJ}o$JQj^ld%t&GrF{@lDa21>Uic2x4B71 z(Whof%u%l4Rf8OthMNdV$nOypOH~SPgTUF9h@4~S=sex4{XTIJUO&Q8ru>LZ;Vv5q za6+!_sL+qU9TV^RA+x(upT&>2F_FVUah2hcd(9z1egwI% zf1|~!0NH0tKeFV;;dGBUEo6WVF=uXdml>{CjAir}rX@B;`jzf7rVO_=4jZJ6%`{WW z@3_$YbNQqa@^atiwYAtKD4q;25C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X d009sH0T2KI5C8!X009sH0T2KI5cppT`~w_?u6qCg literal 0 HcmV?d00001 diff --git a/src/qt/tradingdialog.cpp b/src/qt/tradingdialog.cpp index a7d187f04c886..e392a15674e09 100644 --- a/src/qt/tradingdialog.cpp +++ b/src/qt/tradingdialog.cpp @@ -425,7 +425,7 @@ void tradingDialog::ParseAndPopulateOpenOrdersTable(QString Response) //Handle the cancel link in open orders table QTableWidgetItem* CancelCell; CancelCell= ui->OpenOrdersTable->item(itteration, 14); //Set the wtablewidget item to the cancel cell item. - CancelCell->setForeground(QColor::fromRgb(255,0,0)); //make this item red. + CancelCell->setForeground(QColor::fromRgb(255,0,0,127)); //make this item red. CancelCell->setTextAlignment(Qt::AlignCenter); } } @@ -595,7 +595,7 @@ void tradingDialog::ParseAndPopulateMarketHistoryTable(QString Response) ui->MarketHistoryTable->setItem(counter, 2, new QTableWidgetItem(str.number(obj["Price"].toDouble(),'i',8))); ui->MarketHistoryTable->setItem(counter, 3, new QTableWidgetItem(str.number(obj["Quantity"].toDouble(),'i',8))); ui->MarketHistoryTable->setItem(counter, 4, new QTableWidgetItem(str.number(obj["Total"].toDouble(),'i',8))); - ui->MarketHistoryTable->item(counter,1)->setBackgroundColor((obj["OrderType"] == QStringLiteral("BUY")) ? (QColor (0, 205, 0, 127)) : ( QColor (255, 99, 71, 127))); + ui->MarketHistoryTable->item(counter,1)->setBackgroundColor((obj["OrderType"] == QStringLiteral("BUY")) ? (QColor(0,205,0,127)) : (QColor(255,99,71,127))); counter++; } obj.empty();