1 //  This file is part of Qt Bitcoin Trader
2 //      https://github.com/JulyIGHOR/QtBitcoinTrader
3 //  Copyright (C) 2013-2021 July Ighor <julyighor@gmail.com>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  In addition, as a special exception, the copyright holders give
11 //  permission to link the code of portions of this program with the
12 //  OpenSSL library under certain conditions as described in each
13 //  individual source file, and distribute linked combinations including
14 //  the two.
15 //
16 //  You must obey the GNU General Public License in all respects for all
17 //  of the code used other than OpenSSL. If you modify file(s) with this
18 //  exception, you may extend this exception to your version of the
19 //  file(s), but you are not obligated to do so. If you do not wish to do
20 //  so, delete this exception statement from your version. If you delete
21 //  this exception statement from all source files in the program, then
22 //  also delete it here.
23 //
24 //  This program is distributed in the hope that it will be useful,
25 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
26 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27 //  GNU General Public License for more details.
28 //
29 //  You should have received a copy of the GNU General Public License
30 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
31 
32 #include "julyspinboxpicker.h"
33 #include <QTableWidget>
34 #include <QTimeLine>
35 #include <QScrollBar>
36 #include <QDir>
37 #include <QMessageBox>
38 #include <QSettings>
39 #include "main.h"
40 #include "timesync.h"
41 #include "julylightchanges.h"
42 #include "julyspinboxfix.h"
43 #include "julyscrolluponidle.h"
44 #include <QFileInfo>
45 #include <QClipboard>
46 #include <QProcess>
47 #include <QFile>
48 #include <QSysInfo>
49 #include <QProcess>
50 #include <QDesktopWidget>
51 #include <QDesktopServices>
52 #include <QUrl>
53 #include <QDockWidget>
54 #include "script/addscriptwindow.h"
55 #include "aboutdialog.h"
56 #include "exchange/exchange.h"
57 #include "exchange/exchange_bitstamp.h"
58 #include "exchange/exchange_bitfinex.h"
59 #include "exchange/exchange_indacoin.h"
60 #include "exchange/exchange_yobit.h"
61 #include "exchange/exchange_binance.h"
62 #include "exchange/exchange_bittrex.h"
63 #include "exchange/exchange_hitbtc.h"
64 #include "exchange/exchange_poloniex.h"
65 #include <QSystemTrayIcon>
66 #include <QtCore/qmath.h>
67 #include "script/addrulegroup.h"
68 #include "percentpicker.h"
69 #include "logobutton.h"
70 #include "script/scriptwidget.h"
71 #include "thisfeatureunderdevelopment.h"
72 #include "orderstablecancelbutton.h"
73 #include "script/rulescriptparser.h"
74 #include "julymath.h"
75 #include "platform/sound.h"
76 #include "dock/dock_host.h"
77 #include "config/config_manager.h"
78 #include "config/config_manager_dialog.h"
79 #include "utils/utils.h"
80 #include "settings/settingsdialog.h"
81 #include "indicatorengine.h"
82 #include "charts/chartsmodel.h"
83 #include "charts/chartsview.h"
84 #include "news/newsview.h"
85 #include "menu/networkmenu.h"
86 #include "menu/currencymenu.h"
87 #include "utils/currencysignloader.h"
88 #include "utils/traderspinbox.h"
89 #include "iniengine.h"
90 #include <QScreen>
91 
92 #ifdef Q_OS_WIN
93     #ifdef SAPI_ENABLED
94         #include <windows.h>
95         #include <sapi.h>
96     #endif
97 
98     #include "windows.h"
99 #endif
100 
101 #include <QStyleFactory>
102 
103 #ifdef Q_OS_MAC
104     #include <ApplicationServices/ApplicationServices.h>
105 #endif
106 
107 static const int ContentMargin = 4;
108 
QtBitcoinTrader()109 QtBitcoinTrader::QtBitcoinTrader() :
110     QMainWindow(),
111     feeCalculator(nullptr),
112     meridianPrice(0.0),
113     availableAmount(0.0),
114     exchangeId(-1),
115     floatFee(0.0),
116     floatFeeDec(1.0),
117     floatFeeInc(1.0),
118     ordersModel(nullptr),
119     currencyChangedDate(0),
120     iniSettings(new QSettings(baseValues.iniFileName, QSettings::IniFormat, this)),
121     isValidSoftLag(false),
122     confirmOpenOrder(false),
123     lastRuleExecutedTime(QTime(1, 0, 0, 0)),
124     secondTimer(new QTimer),
125 
126     windowWidget(this),
127     lastCopyTable(nullptr),
128     swapedDepth(false),
129     depthAsksModel(nullptr),
130     depthBidsModel(nullptr),
131     tradesModel(nullptr),
132     historyModel(nullptr),
133     isDataPending(false),
134     waitingDepthLag(false),
135     trayMenu(nullptr),
136     trayIcon(nullptr),
137     checkForUpdates(true),
138     lastLoadedCurrency(-1),
139     constructorFinished(false),
140     appDir(QApplication::applicationDirPath() + "/"),
141 
142     showingMessage(false),
143     balanceNotLoaded(true),
144     marketPricesNotLoaded(true),
145     sellLockBtcToSell(false),
146     sellLockPricePerCoin(false),
147     sellLockAmountToReceive(false),
148     buyLockTotalBtc(false),
149     buyLockTotalBtcSelf(false),
150     buyLockPricePerCoin(false),
151     profitSellThanBuyUnlocked(true),
152     profitBuyThanSellUnlocked(true),
153     profitBuyThanSellChangedUnlocked(true),
154     profitSellThanBuyChangedUnlocked(true),
155     debugViewer(nullptr),
156     tradesPrecentLast(0.0),
157     currentPopupDialogs(0),
158     networkMenu(nullptr),
159     currencyMenu(nullptr),
160     currencySignLoader(new CurrencySignLoader),
161     lockedDocks(false),
162     actionExit(nullptr),
163     actionUpdate(nullptr),
164     actionSendBugReport(nullptr),
165     actionAbout(nullptr),
166     actionAboutQt(nullptr),
167     actionLockDocks(nullptr),
168     actionConfigManager(nullptr),
169     actionSettings(nullptr),
170     actionDebug(nullptr),
171     actionUninstall(nullptr),
172     menuFile(nullptr),
173     menuView(nullptr),
174     menuConfig(nullptr),
175     menuHelp(nullptr),
176     configDialog(nullptr),
177     dockHost(new DockHost(this)),
178     dockLogo(nullptr),
179     dockDepth(nullptr),
180     buyTotalSpend(new TraderSpinBox(this)),
181     buyPricePerCoin(new TraderSpinBox(this)),
182     buyTotalBtc(new TraderSpinBox(this)),
183     profitLossSpinBox(new TraderSpinBox(this)),
184     profitLossSpinBoxPrec(new TraderSpinBox(this)),
185     sellTotalBtc(new TraderSpinBox(this)),
186     sellPricePerCoin(new TraderSpinBox(this)),
187     sellAmountToReceive(new TraderSpinBox(this)),
188     sellThanBuySpinBox(new TraderSpinBox(this)),
189     sellThanBuySpinBoxPrec(new TraderSpinBox(this))
190 {
191     historyForceUpdate.start();
192     speedTestTime.start();
193     lastMessageTime.start();
194     depthLagTime.restart();
195     softLagTime.restart();
196 
197     ui.setupUi(this);
198     setupWidgets();
199     fixAllCurrencyLabels(this);
200     setSpinValue(ui.accountFee, 0.0);
201     ui.accountLoginLabel->setStyleSheet("background: " + baseValues.appTheme.white.name());
202     ui.noOpenedOrdersLabel->setStyleSheet("font-size:27px; border: 1px solid gray; background: " +
203                                           baseValues.appTheme.white.name() + "; color: " + baseValues.appTheme.gray.name());
204     ui.rulesNoMessage->setStyleSheet("font-size:27px; border: 1px solid gray; background: " +
205                                      baseValues.appTheme.white.name() + "; color: " + baseValues.appTheme.gray.name());
206     ui.ordersTableFrame->setVisible(false);
207 
208     currentlyAddingOrders = false;
209     ordersModel = new OrdersModel;
210     ordersSortModel = new QSortFilterProxyModel;
211     ordersSortModel->setSortRole(Qt::EditRole);
212     ordersSortModel->setFilterRole(Qt::WhatsThisRole);
213     ordersSortModel->setDynamicSortFilter(true);
214     ordersSortModel->setSourceModel(ordersModel);
215     ui.ordersTable->setModel(ordersSortModel);
216     setColumnResizeMode(ui.ordersTable, 0, QHeaderView::ResizeToContents);
217     setColumnResizeMode(ui.ordersTable, 1, QHeaderView::Stretch);
218     setColumnResizeMode(ui.ordersTable, 2, QHeaderView::ResizeToContents);
219     setColumnResizeMode(ui.ordersTable, 3, QHeaderView::ResizeToContents);
220     setColumnResizeMode(ui.ordersTable, 4, QHeaderView::ResizeToContents);
221     setColumnResizeMode(ui.ordersTable, 5, QHeaderView::ResizeToContents);
222     setColumnResizeMode(ui.ordersTable, 6, QHeaderView::ResizeToContents);
223     setColumnResizeMode(ui.ordersTable, 7, QHeaderView::ResizeToContents);
224     ui.ordersTable->setItemDelegateForColumn(7, new OrdersTableCancelButton(ui.ordersTable));
225 
226     ui.ordersTable->setSortingEnabled(true);
227     ui.ordersTable->sortByColumn(0, Qt::AscendingOrder);
228 
229     connect(ordersModel, &OrdersModel::ordersIsAvailable,    this, &QtBitcoinTrader::ordersIsAvailable);
230     connect(ordersModel, &OrdersModel::cancelOrder,          this, &QtBitcoinTrader::cancelOrder);
231     connect(ordersModel, &OrdersModel::volumeAmountChanged,  this, &QtBitcoinTrader::volumeAmountChanged);
232     connect(ui.ordersTable->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this,
233             SLOT(checkValidOrdersButtons()));
234 
235     tradesModel = new TradesModel;
236     ui.tableTrades->setModel(tradesModel);
237     setColumnResizeMode(ui.tableTrades, 0, QHeaderView::Stretch);
238     setColumnResizeMode(ui.tableTrades, 1, QHeaderView::ResizeToContents);
239     setColumnResizeMode(ui.tableTrades, 2, QHeaderView::ResizeToContents);
240     setColumnResizeMode(ui.tableTrades, 3, QHeaderView::ResizeToContents);
241     setColumnResizeMode(ui.tableTrades, 4, QHeaderView::ResizeToContents);
242     setColumnResizeMode(ui.tableTrades, 5, QHeaderView::ResizeToContents);
243     setColumnResizeMode(ui.tableTrades, 6, QHeaderView::ResizeToContents);
244     setColumnResizeMode(ui.tableTrades, 7, QHeaderView::Stretch);
245     connect(tradesModel, SIGNAL(trades10MinVolumeChanged(double)), this, SLOT(setLastTrades10MinVolume(double)));
246     connect(tradesModel, SIGNAL(precentBidsChanged(double)), this, SLOT(precentBidsChanged(double)));
247 
248     historyModel = new HistoryModel;
249     ui.tableHistory->setModel(historyModel);
250     setColumnResizeMode(ui.tableHistory, 0, QHeaderView::Stretch);
251     setColumnResizeMode(ui.tableHistory, 1, QHeaderView::ResizeToContents);
252     setColumnResizeMode(ui.tableHistory, 2, QHeaderView::ResizeToContents);
253     setColumnResizeMode(ui.tableHistory, 3, QHeaderView::ResizeToContents);
254     setColumnResizeMode(ui.tableHistory, 4, QHeaderView::ResizeToContents);
255     setColumnResizeMode(ui.tableHistory, 5, QHeaderView::ResizeToContents);
256     setColumnResizeMode(ui.tableHistory, 6, QHeaderView::Stretch);
257     connect(historyModel, SIGNAL(accLastSellChanged(const QString&, double)), this, SLOT(accLastSellChanged(const QString&, double)));
258     connect(historyModel, SIGNAL(accLastBuyChanged(const QString&, double)), this, SLOT(accLastBuyChanged(const QString&, double)));
259 
260 
261     depthAsksModel = new DepthModel(ui.comboBoxGroupByPrice, true);
262     ui.depthAsksTable->setModel(depthAsksModel);
263     depthBidsModel = new DepthModel(ui.comboBoxGroupByPrice, false);
264     ui.depthBidsTable->setModel(depthBidsModel);
265 
266     new JulyScrollUpOnIdle(ui.depthAsksTable->verticalScrollBar());
267     new JulyScrollUpOnIdle(ui.depthBidsTable->verticalScrollBar());
268 
269     setColumnResizeMode(ui.depthAsksTable, 0, QHeaderView::Stretch);
270     setColumnResizeMode(ui.depthAsksTable, 1, QHeaderView::ResizeToContents);
271     setColumnResizeMode(ui.depthAsksTable, 2, QHeaderView::ResizeToContents);
272     setColumnResizeMode(ui.depthAsksTable, 3, QHeaderView::ResizeToContents);
273     setColumnResizeMode(ui.depthAsksTable, 4, QHeaderView::ResizeToContents);
274     ui.depthAsksTable->horizontalHeader()->setMinimumSectionSize(0);
275 
276     setColumnResizeMode(ui.depthBidsTable, 0, QHeaderView::ResizeToContents);
277     setColumnResizeMode(ui.depthBidsTable, 1, QHeaderView::ResizeToContents);
278     setColumnResizeMode(ui.depthBidsTable, 2, QHeaderView::ResizeToContents);
279     setColumnResizeMode(ui.depthBidsTable, 3, QHeaderView::ResizeToContents);
280     setColumnResizeMode(ui.depthBidsTable, 4, QHeaderView::Stretch);
281     ui.depthBidsTable->horizontalHeader()->setMinimumSectionSize(0);
282 
283     closeToTray = iniSettings->value("UI/CloseToTray", false).toBool();
284 #ifdef Q_OS_MAC
285     closeToTray = false;
286 #endif
287 
288     confirmOpenOrder = iniSettings->value("UI/ConfirmOpenOrder", true).toBool();
289     iniSettings->setValue("UI/ConfirmOpenOrder", confirmOpenOrder);
290 
291     checkValidOrdersButtons();
292 
293     new JulyLightChanges(ui.accountFee);
294     new JulyLightChanges(ui.marketVolume);
295     new JulyLightChanges(ui.marketBid);
296     new JulyLightChanges(ui.marketAsk);
297     new JulyLightChanges(ui.marketHigh);
298     new JulyLightChanges(ui.marketLow);
299     new JulyLightChanges(ui.marketLast);
300     new JulyLightChanges(ui.accountBTC);
301     new JulyLightChanges(ui.accountUSD);
302     new JulyLightChanges(ui.ordersLastSellPrice);
303     new JulyLightChanges(ui.ordersLastBuyPrice);
304     new JulyLightChanges(ui.tradesVolume5m);
305 
306     new JulyLightChanges(ui.ruleTotalToBuyValue);
307     new JulyLightChanges(ui.ruleAmountToReceiveValue);
308     new JulyLightChanges(ui.ruleTotalToBuyBSValue);
309     new JulyLightChanges(ui.ruleAmountToReceiveBSValue);
310     new JulyLightChanges(ui.tradesBidsPrecent);
311 
312     baseValues.forceDotInSpinBoxes = iniSettings->value("UI/ForceDotInDouble", true).toBool();
313     iniSettings->setValue("UI/ForceDotInDouble", baseValues.forceDotInSpinBoxes);
314 
315     ui.totalToSpendLayout->addWidget(new JulySpinBoxPicker(buyTotalSpend));
316     ui.pricePerCoinLayout->addWidget(new JulySpinBoxPicker(buyPricePerCoin));
317     ui.totalBtcToBuyLayout->addWidget(new JulySpinBoxPicker(buyTotalBtc));
318 
319     ui.totalToSellLayout->addWidget(new JulySpinBoxPicker(sellTotalBtc));
320     ui.pricePerCoinSellLayout->addWidget(new JulySpinBoxPicker(sellPricePerCoin));
321     ui.amountToReceiveLayout->addWidget(new JulySpinBoxPicker(sellAmountToReceive));
322 
323     ui.gssoProfitLayout->addWidget(new JulySpinBoxPicker(profitLossSpinBox));
324     ui.gssoProfitPercLayout->addWidget(new JulySpinBoxPicker(profitLossSpinBoxPrec));
325 
326     ui.gssboProfitLayout->addWidget(new JulySpinBoxPicker(sellThanBuySpinBox));
327     ui.gsboProfitPercLayout->addWidget(new JulySpinBoxPicker(sellThanBuySpinBoxPrec));
328 
329     for (QDoubleSpinBox* spinBox : findChildren<QDoubleSpinBox*>())
330     {
331         new JulySpinBoxFix(spinBox);
332 
333         QString scriptName = spinBox->whatsThis();
334 
335         if (scriptName.isEmpty())
336             continue;
337 
338         indicatorsMap[scriptName] = spinBox;
339     }
340 
341     double iniFileVersion = iniSettings->value("Profile/Version", 1.0).toDouble();
342 
343     if (iniFileVersion < baseValues.appVerReal)
344         iniSettings->setValue("Profile/Version", baseValues.appVerReal);
345 
346     QSettings settingsMain(appDataDir + "/QtBitcoinTrader.cfg", QSettings::IniFormat);
347     checkForUpdates = settingsMain.value("CheckForUpdates", true).toBool();
348 
349     int defTextHeight = baseValues.fontMetrics_->boundingRect("0123456789").height();
350     defaultHeightForRow = settingsMain.value("RowHeight", defTextHeight * 1.6).toInt();
351 
352     if (defaultHeightForRow < defTextHeight)
353         defaultHeightForRow = defTextHeight;
354 
355     settingsMain.setValue("RowHeight", defaultHeightForRow);
356 
357     exchangeId = iniSettings->value("Profile/ExchangeId", 0).toInt();
358 
359     if (exchangeId == 11)
360     {
361         ui.depthComboBoxLimitRows->blockSignals(true);
362         ui.depthComboBoxLimitRows->clear();
363         ui.depthComboBoxLimitRows->addItems({"1000", "500", "100", "50", "20", "10", "5"});
364         ui.depthComboBoxLimitRows->blockSignals(false);
365     }
366 
367     baseValues.depthCountLimit = iniSettings->value("UI/DepthCountLimit", 100).toInt();
368 
369     if (baseValues.depthCountLimit < 0)
370         baseValues.depthCountLimit = 100;
371 
372     baseValues.depthCountLimitStr = QByteArray::number(baseValues.depthCountLimit);
373     int currentDepthComboBoxLimitIndex = 0;
374 
375     for (int n = 0; n < ui.depthComboBoxLimitRows->count(); n++)
376     {
377         int currentValueDouble = ui.depthComboBoxLimitRows->itemText(n).toInt();
378 
379         if (currentValueDouble == baseValues.depthCountLimit)
380             currentDepthComboBoxLimitIndex = n;
381 
382         ui.depthComboBoxLimitRows->setItemData(n, currentValueDouble, Qt::UserRole);
383     }
384 
385     ui.depthComboBoxLimitRows->setCurrentIndex(currentDepthComboBoxLimitIndex);
386 
387     baseValues.apiDownCount = iniSettings->value("Network/ApiDownCounterMax", 5).toInt();
388 
389     if (baseValues.apiDownCount < 0)
390         baseValues.apiDownCount = 5;
391 
392     iniSettings->setValue("Network/ApiDownCounterMax", baseValues.apiDownCount);
393 
394     baseValues.httpRetryCount = iniSettings->value("Network/HttpRetryCount", 8).toInt();
395 
396     if (baseValues.httpRetryCount < 1 || baseValues.httpRetryCount > 50)
397         baseValues.httpRetryCount = 8;
398 
399     iniSettings->setValue("Network/HttpRetryCount", baseValues.httpRetryCount);
400 
401     baseValues.uiUpdateInterval = iniSettings->value("UI/UiUpdateInterval", 100).toInt();
402 
403     if (baseValues.uiUpdateInterval < 1)
404         baseValues.uiUpdateInterval = 100;
405 
406     baseValues.customUserAgent = iniSettings->value("Network/UserAgent", "").toString();
407     baseValues.customCookies = iniSettings->value("Network/Cookies", "").toString();
408 
409     baseValues.gzipEnabled = iniSettings->value("Network/GZIPEnabled", true).toBool();
410     iniSettings->setValue("Network/GZIPEnabled", baseValues.gzipEnabled);
411 
412     feeCalculatorSingleInstance = iniSettings->value("UI/FeeCalcSingleInstance", true).toBool();
413     iniSettings->setValue("UI/FeeCalcSingleInstance", feeCalculatorSingleInstance);
414 
415     ui.depthAutoResize->setChecked(iniSettings->value("UI/DepthAutoResizeColumns", true).toBool());
416 
417     if (!ui.depthAutoResize->isChecked())
418     {
419         ui.depthAsksTable->setColumnWidth(1, iniSettings->value("UI/DepthColumnAsksSizeWidth",
420                                           ui.depthAsksTable->columnWidth(1)).toInt());
421         ui.depthAsksTable->setColumnWidth(2, iniSettings->value("UI/DepthColumnAsksVolumeWidth",
422                                           ui.depthAsksTable->columnWidth(2)).toInt());
423         ui.depthAsksTable->setColumnWidth(3, iniSettings->value("UI/DepthColumnAsksPriceWidth",
424                                           ui.depthAsksTable->columnWidth(3)).toInt());
425 
426         ui.depthBidsTable->setColumnWidth(0, iniSettings->value("UI/DepthColumnBidsPriceWidth",
427                                           ui.depthBidsTable->columnWidth(0)).toInt());
428         ui.depthBidsTable->setColumnWidth(1, iniSettings->value("UI/DepthColumnBidsVolumeWidth",
429                                           ui.depthBidsTable->columnWidth(1)).toInt());
430         ui.depthBidsTable->setColumnWidth(2, iniSettings->value("UI/DepthColumnBidsSizeWidth",
431                                           ui.depthBidsTable->columnWidth(2)).toInt());
432     }
433 
434     ui.rulesTabs->setVisible(false);
435 
436     iniSettings->setValue("Network/HttpRetryCount", baseValues.httpRetryCount);
437     iniSettings->setValue("UI/UiUpdateInterval", baseValues.uiUpdateInterval);
438     iniSettings->setValue("UI/DepthAutoResizeColumns", ui.depthAutoResize->isChecked());
439 
440     profileName = iniSettings->value("Profile/Name", "Default Profile").toString();
441     windowTitleP = profileName + " - " + windowTitle() + " v" + baseValues.appVerStr;
442 
443 #ifdef QTBUILDTARGETWIN32
444     windowTitleP += " (32bit)";
445 #endif
446 
447 #ifdef QTBUILDTARGETWIN64
448     windowTitleP += " (64bit)";
449 #endif
450 
451     if (debugLevel)
452         windowTitleP.append(" [DEBUG MODE]");
453 
454     //else if (baseValues.appVerIsBeta)
455     //    windowTitleP.append(" [BETA]");
456 
457     windowWidget->setWindowTitle(windowTitleP);
458 
459     fixTableViews(this);
460 
461     int defaultMinWidth = qMax(1024, minimumSizeHint().width());
462     int defaultMinHeight = qMax(720, minimumSizeHint().height());
463 
464     baseValues.highResolutionDisplay = false;
465     int screenCount = QApplication::screens().count();
466 
467     QRect currScrRect;
468 
469     for (int n = 0; n < screenCount; n++)
470     {
471         currScrRect = QApplication::screens().at(n)->availableGeometry();
472 
473         if (currScrRect.width() > defaultMinWidth && currScrRect.height() > defaultMinHeight)
474         {
475             baseValues.highResolutionDisplay = true;
476             break;
477         }
478     }
479 
480     setSpinValue(ui.accountBTC, 0.0);
481     setSpinValue(ui.accountUSD, 0.0);
482     setSpinValue(ui.marketBid, 0.0);
483     setSpinValue(ui.marketAsk, 0.0);
484     setSpinValue(ui.marketHigh, 0.0);
485     setSpinValue(ui.marketLow, 0.0);
486     setSpinValue(ui.marketLast, 0.0);
487     setSpinValue(ui.marketVolume, 0.0);
488 
489     if (iniSettings->value("UI/SwapDepth", false).toBool())
490         on_swapDepth_clicked();
491 
492     ui.depthLag->setValue(0.0);
493 
494     copyTableValuesMenu.addAction("Copy selected Rows", this, SLOT(copySelectedRow()));
495     copyTableValuesMenu.addSeparator();
496     copyTableValuesMenu.addAction("Copy Date", this, SLOT(copyDate()));
497     copyTableValuesMenu.addAction("Copy Amount", this, SLOT(copyAmount()));
498     copyTableValuesMenu.addAction("Copy Price", this, SLOT(copyPrice()));
499     copyTableValuesMenu.addAction("Copy Total", this, SLOT(copyTotal()));
500     copyTableValuesMenu.addSeparator();
501     copyTableValuesMenu.addAction("Repeat Buy and Sell order", this, SLOT(repeatBuySellOrder()));
502     copyTableValuesMenu.addAction("Repeat Buy order", this, SLOT(repeatBuyOrder()));
503     copyTableValuesMenu.addAction("Repeat Sell order", this, SLOT(repeatSellOrder()));
504     copyTableValuesMenu.addSeparator();
505     copyTableValuesMenu.addAction("Cancel Order", this, SLOT(on_ordersCancelSelected_clicked()));
506     copyTableValuesMenu.addAction("Cancel All Orders", this, SLOT(on_ordersCancelAllButton_clicked()));
507 
508     createActions();
509     createMenu();
510 
511     networkMenu = new NetworkMenu(ui.networkMenuTool);
512 
513     currencyMenu = new CurrencyMenu(ui.currencyMenuTool);
514     connect(currencyMenu, &CurrencyMenu::currencyMenuChanged, this, &QtBitcoinTrader::currencyMenuChanged);
515 
516     reloadLanguage();
517 
518     volumeAmountChanged(0.0, 0.0);
519 
520     connect(&julyTranslator, SIGNAL(languageChanged()), this, SLOT(languageChanged()));
521 
522     if (checkForUpdates)
523         QProcess::startDetached(QApplication::applicationFilePath(), QStringList("/checkupdate"));
524 
525     connect(networkMenu, &NetworkMenu::trafficTotalToZero_clicked, this, &QtBitcoinTrader::trafficTotalToZero_clicked);
526     iniSettings->sync();
527 
528     chartsView = new ChartsView;
529     connect(tradesModel, &TradesModel::addChartsTrades, chartsView->chartsModel.data(), &ChartsModel::addLastTrades);
530     connect(this, &QtBitcoinTrader::clearCharts, chartsView->chartsModel.data(), &ChartsModel::clearCharts);
531     connect(this, &QtBitcoinTrader::addBound, chartsView->chartsModel.data(), &ChartsModel::addBound);
532     ui.chartsLayout->addWidget(chartsView);
533 
534     newsView = new NewsView();
535     ui.newsLayout->addWidget(newsView);
536 
537     //ChatWindow *chatWindow=new ChatWindow();
538     //ui.chatLayout->addWidget(chatWindow);
539 
540     setContentsMargins(ContentMargin, ContentMargin, ContentMargin, ContentMargin);
541     initDocks();
542     moveWidgetsToDocks();
543     ui.menubar->setFixedHeight(ui.menubar->height() + 2);
544 
545     connect(::config, &ConfigManager::onChanged, this, &QtBitcoinTrader::onConfigChanged);
546     connect(::config, &ConfigManager::onError, this, &QtBitcoinTrader::onConfigError);
547     initConfigMenu();
548 
549     if (iniSettings->value("UI/OptimizeInterface", false).toBool())
550         recursiveUpdateLayouts(this);
551 
552     secondTimer->setSingleShot(true);
553     connect(secondTimer.data(), &QTimer::timeout, this, &QtBitcoinTrader::secondSlot);
554     secondSlot();
555 }
556 
~QtBitcoinTrader()557 QtBitcoinTrader::~QtBitcoinTrader()
558 {
559     if (currentExchangeThread && currentExchangeThread->isRunning())
560     {
561         currentExchangeThread->quit();
562         currentExchangeThread->wait();
563         delete currentExchange;
564         currentExchange = nullptr;
565         currentExchangeThread.reset();
566     }
567 }
568 
fixTableViews(QWidget * wid)569 void QtBitcoinTrader::fixTableViews(QWidget* wid)
570 {
571     for (QTableView* tables : wid->findChildren<QTableView*>())
572     {
573         QFont tableFont = tables->font();
574         tableFont.setFixedPitch(true);
575         tables->setFont(tableFont);
576         tables->setMinimumWidth(200);
577         tables->setMinimumHeight(190);
578         tables->verticalHeader()->setDefaultSectionSize(defaultHeightForRow);
579     }
580 }
581 
getIndicatorValue(const QString & name)582 double QtBitcoinTrader::getIndicatorValue(const QString& name)
583 {
584     QDoubleSpinBox* spin = indicatorsMap.value(name, nullptr);
585 
586     if (spin == nullptr)
587         return 0.0;
588 
589     return spin->value();
590 }
591 
setColumnResizeMode(QTableView * table,int column,QHeaderView::ResizeMode mode)592 void QtBitcoinTrader::setColumnResizeMode(QTableView* table, int column, QHeaderView::ResizeMode mode)
593 {
594     table->horizontalHeader()->setSectionResizeMode(column, mode);
595 }
596 
setColumnResizeMode(QTableView * table,QHeaderView::ResizeMode mode)597 void QtBitcoinTrader::setColumnResizeMode(QTableView* table, QHeaderView::ResizeMode mode)
598 {
599     table->horizontalHeader()->setSectionResizeMode(mode);
600 }
601 
setupClass()602 void QtBitcoinTrader::setupClass()
603 {
604     switch (exchangeId)
605     {
606     case 0:
607         QCoreApplication::quit();
608         return;//Secret Excange
609 
610     case 2:
611         currentExchange = new Exchange_Bitstamp(baseValues.restSign, baseValues.restKey);
612         break;//Bitstamp
613 
614     case 4:
615         currentExchange = new Exchange_Bitfinex(baseValues.restSign, baseValues.restKey);
616         break;//Bitfinex
617 
618     case 6:
619         currentExchange = new Exchange_Indacoin(baseValues.restSign, baseValues.restKey);
620         break;//Indacoin
621 
622     case 10:
623         currentExchange = new Exchange_YObit(baseValues.restSign, baseValues.restKey);
624         break;//YObit
625 
626     case 11:
627         currentExchange = new Exchange_Binance(baseValues.restSign, baseValues.restKey);
628         break;//Binance
629 
630     case 12:
631         currentExchange = new Exchange_Bittrex(baseValues.restSign, baseValues.restKey);
632         break;//Bittrex
633 
634     case 13:
635         currentExchange = new Exchange_HitBTC(baseValues.restSign, baseValues.restKey);
636         break;//HitBTC
637 
638     case 14:
639         currentExchange = new Exchange_Poloniex(baseValues.restSign, baseValues.restKey);
640         break;//Poloniex
641 
642     default:
643         return;
644     }
645 
646     baseValues.minimumRequestInterval = currentExchange->minimumRequestIntervalAllowed;
647     baseValues.httpRequestInterval = iniSettings->value("Network/HttpRequestsInterval", baseValues.minimumRequestInterval).toInt();
648     baseValues.minimumRequestTimeout = currentExchange->minimumRequestTimeoutAllowed;
649     baseValues.httpRequestTimeout = iniSettings->value("Network/HttpRequestsTimeout", baseValues.minimumRequestTimeout).toInt();
650 
651     if (baseValues.httpRequestInterval < 50)
652         baseValues.httpRequestInterval = 50;
653 
654     if (baseValues.httpRequestTimeout < 100)
655         baseValues.httpRequestTimeout = 100;
656 
657     iniSettings->setValue("Network/HttpRequestsInterval", baseValues.httpRequestInterval);
658     iniSettings->setValue("Network/HttpRequestsTimeout", baseValues.httpRequestTimeout);
659 
660     currentExchangeThread.reset(new QThread);
661     currentExchangeThread->setObjectName("Exchange Thread");
662     currentExchange->moveToThread(currentExchangeThread.data());
663     connect(currentExchangeThread.data(), &QThread::started, currentExchange, &Exchange::run);
664 
665     baseValues.restSign.clear();
666 
667     currentExchange->setupApi(this, false);
668 
669     if (currentExchange->domain.isEmpty())
670         setCurrencyPairsList();
671 
672     setApiDown(false);
673 
674     if (!currentExchange->exchangeTickerSupportsHiLowPrices)
675         for (int n = 0; n < ui.highLowLayout->count(); n++)
676         {
677             QWidgetItem* curWid = dynamic_cast<QWidgetItem*>(ui.highLowLayout->itemAt(n));
678 
679             if (curWid)
680                 curWid->widget()->setVisible(false);
681         }
682 
683     if (!currentExchange->supportsExchangeFee)
684     {
685         ui.accountFee->setButtonSymbols(QAbstractSpinBox::UpDownArrows);
686         ui.accountFee->setReadOnly(false);
687 
688         setSpinValue(ui.accountFee, iniSettings->value("Profile/CustomFee", 0.35).toDouble());
689         ui.feeSpinboxLayout->addWidget(new JulySpinBoxPicker(ui.accountFee));
690     }
691 
692     if (!currentExchange->supportsExchangeVolume)
693     {
694         ui.marketVolumeLabel->setVisible(false);
695         ui.btcLabel4->setVisible(false);
696         ui.marketVolume->setVisible(false);
697     }
698 
699     if (currentExchange->clearOpenOrdersOnCurrencyChanged || currentExchange->exchangeDisplayOnlyCurrentPairOpenOrders)
700     {
701         ui.ordersFilterCheckBox->setVisible(false);
702 
703         if (currentExchange->exchangeDisplayOnlyCurrentPairOpenOrders)
704             ui.ordersFilterCheckBox->setChecked(true);
705 
706         ui.filterOrdersCurrency->setVisible(false);
707         ui.centerOrdersTotalSpacer->setVisible(true);
708     }
709     else
710         ui.centerOrdersTotalSpacer->setVisible(false);
711 
712     if (!currentExchange->supportsLoginIndicator)
713     {
714         ui.loginVolumeBack->setVisible(false);
715         QSize sz = ui.widgetAccount->maximumSize();
716         ui.widgetAccount->setMaximumSize(QSize(200, sz.height()));
717     }
718     else if (currentExchange->supportsLoginIndicator && !currentExchange->supportsAccountVolume)
719     {
720         ui.labelAccountVolume->setVisible(false);
721         ui.btcLabelAccountVolume->setVisible(false);
722         ui.accountVolume->setVisible(false);
723     }
724 
725     ordersModel->checkDuplicatedOID = currentExchange->checkDuplicatedOID;
726 
727     {
728         QEventLoop waitStarted;
729         connect(currentExchange, &Exchange::started, &waitStarted, &QEventLoop::quit);
730         currentExchangeThread->start();
731         waitStarted.exec();
732     }
733 
734     if (!ui.widgetStaysOnTop->isChecked())
735         on_widgetStaysOnTop_toggled(ui.widgetStaysOnTop->isChecked());
736 
737     ui.widgetStaysOnTop->setChecked(iniSettings->value("UI/WindowOnTop", false).toBool());
738     languageChanged();
739     reloadScripts();
740     IndicatorEngine::global();
741     int nextTheme = iniSettings->value("UI/NightMode", 0).toInt();
742 
743     if (nextTheme == 1)
744         on_buttonNight_clicked();
745     else if (nextTheme == 2)
746     {
747         baseValues.currentTheme = 1;
748         on_buttonNight_clicked();
749     }
750     else
751         ui.widgetLogo->setStyleSheet("background:white");
752 
753     ::config->load("");
754 
755     if (isHidden())
756         show();
757 
758     ui.buyPercentage->setMaximumWidth(ui.buyPercentage->height());
759     ui.sellPercentage->setMaximumWidth(ui.sellPercentage->height());
760     fixDepthBidsTable();
761 
762     ui.comboBoxGroupByPrice->blockSignals(true);
763     ui.comboBoxGroupByPrice->addItem(julyTr("DONT_GROUP", "None"), double(0));
764     ui.comboBoxGroupByPrice->blockSignals(false);
765     baseValues.groupPriceValue = iniSettings->value("UI/DepthGroupByPrice", 0.0).toDouble();
766 
767     if (baseValues.groupPriceValue < 0.0)
768         baseValues.groupPriceValue = 0.0;
769     else if (baseValues.groupPriceValue > 0.0)
770     {
771         ui.comboBoxGroupByPrice->addItem(QString::number(baseValues.groupPriceValue), baseValues.groupPriceValue);
772         ui.comboBoxGroupByPrice->setCurrentIndex(1);
773     }
774 }
775 
addRuleByHolder(RuleHolder & holder,bool isEnabled,QString titleName)776 void QtBitcoinTrader::addRuleByHolder(RuleHolder& holder, bool isEnabled, QString titleName)
777 {
778     int findNameTab = -1;
779 
780     for (int n = 0; n < ui.rulesTabs->count(); n++)
781     {
782         QWidget* currentWidget = ui.rulesTabs->widget(n);
783 
784         if (!titleName.isEmpty() && currentWidget->windowTitle() == titleName)
785             findNameTab = n;
786 
787         if (findNameTab > -1)
788             break;
789     }
790 
791     if (findNameTab > -1)
792     {
793         QWidget* currentWidget = ui.rulesTabs->widget(findNameTab);
794 
795         if (currentWidget->property("GroupType").toString() == QLatin1String("Rule"))
796             (static_cast<RuleWidget*>(currentWidget))->addRuleByHolder(holder, isEnabled);
797         else if (currentWidget->property("GroupType").toString() == QLatin1String("Script"))
798             (static_cast<ScriptWidget*>(currentWidget))->replaceScript(RuleScriptParser::holderToScript(holder, false));
799     }
800     else
801     {
802         QString nameTemplate(baseValues.scriptFolder + "Script_%1.JLS");
803         int ruleN = 1;
804 
805         while (QFile::exists(nameTemplate.arg(ruleN)))
806             ruleN++;
807 
808         ScriptWidget* newRule = new ScriptWidget(titleName, nameTemplate.arg(ruleN));
809         findNameTab = ui.rulesTabs->count();
810         ui.rulesTabs->insertTab(findNameTab, newRule, newRule->windowTitle());
811         newRule->replaceScript(RuleScriptParser::holderToScript(holder, false));
812     }
813 
814     if (findNameTab > -1 && findNameTab < ui.rulesTabs->count())
815         ui.rulesTabs->setCurrentIndex(findNameTab);
816 }
817 
getRuleGroupsNames()818 QStringList QtBitcoinTrader::getRuleGroupsNames()
819 {
820     QStringList rezult;
821 
822     for (RuleWidget* ruleWidget : ui.tabRules->findChildren<RuleWidget*>())
823         if (ruleWidget)
824             rezult << ruleWidget->windowTitle();
825 
826     return rezult;
827 }
828 
getScriptGroupsNames()829 QStringList QtBitcoinTrader::getScriptGroupsNames()
830 {
831     QStringList rezult;
832 
833     for (ScriptWidget* scriptWidget : ui.tabRules->findChildren<ScriptWidget*>())
834         if (scriptWidget)
835             rezult << scriptWidget->windowTitle();
836 
837     return rezult;
838 }
839 
getIsGroupRunning(const QString & name)840 bool QtBitcoinTrader::getIsGroupRunning(const QString& name)
841 {
842     for (RuleWidget* ruleWidget : ui.tabRules->findChildren<RuleWidget*>())
843         if (ruleWidget && (ruleWidget->windowTitle().compare(name, Qt::CaseInsensitive) == 0))
844             return ruleWidget->haveWorkingRules();
845 
846     for (ScriptWidget* scriptWidget : ui.tabRules->findChildren<ScriptWidget*>())
847         if (scriptWidget && (scriptWidget->windowTitle().compare(name, Qt::CaseInsensitive) == 0))
848             return scriptWidget->isRunning();
849 
850     return false;
851 }
852 
setGroupState(const QString & name,bool enabled)853 void QtBitcoinTrader::setGroupState(const QString& name, bool enabled)
854 {
855     for (RuleWidget* ruleWidget : ui.tabRules->findChildren<RuleWidget*>())
856         if (ruleWidget && (ruleWidget->windowTitle().compare(name, Qt::CaseInsensitive) == 0))
857         {
858             if (enabled)
859                 ruleWidget->ruleEnableAll();
860             else
861                 ruleWidget->ruleDisableAll();
862         }
863 
864     for (ScriptWidget* scriptWidget : ui.tabRules->findChildren<ScriptWidget*>())
865         if (scriptWidget && (scriptWidget->windowTitle().compare(name, Qt::CaseInsensitive) == 0))
866             scriptWidget->setRunning(enabled);
867 }
868 
clearPendingGroup(const QString & name)869 void QtBitcoinTrader::clearPendingGroup(const QString& name)
870 {
871     for (int n = pendingGroupStates.size() - 1; n >= 0; n--)
872         if (pendingGroupStates.at(n).name == name)
873             pendingGroupStates.removeAt(n);
874 }
875 
setGroupRunning(const QString & name,bool enabled)876 void QtBitcoinTrader::setGroupRunning(const QString& name, bool enabled)
877 {
878     if (enabled)
879         pendingGroupStates << GroupStateItem(name, enabled);
880     else
881     {
882         clearPendingGroup(name);
883         setGroupState(name, enabled);
884     }
885 }
886 
on_buyPercentage_clicked()887 void QtBitcoinTrader::on_buyPercentage_clicked()
888 {
889     PercentPicker* percentPicker = new PercentPicker(buyTotalBtc, getAvailableUSDtoBTC(buyPricePerCoin->value()));
890     QPoint execPos = ui.buyAmountPickerBack->mapToGlobal(ui.buyPercentage->geometry().center());
891     execPos.setX(execPos.x() - percentPicker->width() / 2);
892     execPos.setY(execPos.y() - percentPicker->width());
893     percentPicker->exec(execPos);
894 }
895 
on_sellPercentage_clicked()896 void QtBitcoinTrader::on_sellPercentage_clicked()
897 {
898     PercentPicker* percentPicker = new PercentPicker(sellTotalBtc, getAvailableBTC());
899     QPoint execPos = ui.sellAmountPickerBack->mapToGlobal(ui.sellPercentage->geometry().center());
900     execPos.setX(execPos.x() - percentPicker->width() / 2);
901     execPos.setY(execPos.y() - percentPicker->width());
902     percentPicker->exec(execPos);
903 }
904 
ordersFilterChanged()905 void QtBitcoinTrader::ordersFilterChanged()
906 {
907     QString filterSymbol;
908     QString currAStr;
909     QString currBStr;
910     QPixmap currPixmap;
911 
912     if (ui.ordersFilterCheckBox->isChecked())
913     {
914         filterSymbol = ui.filterOrdersCurrency->currentText();
915         int posSplitter = filterSymbol.indexOf('/');
916 
917         if (posSplitter == -1)
918         {
919             currAStr = filterSymbol.left(3);
920             currBStr = filterSymbol.right(3);
921         }
922         else
923         {
924             currAStr = filterSymbol.left(posSplitter);
925             currBStr = filterSymbol.right(filterSymbol.size() - posSplitter - 1);
926         }
927 
928         if (filterSymbol.size())
929             if (baseValues.currentPair.symbol.indexOf("/") == -1)
930                 filterSymbol.replace("/", "");
931     }
932     else
933     {
934         currAStr = baseValues.currentPair.currAStr.toUpper();
935         currBStr = baseValues.currentPair.currBStr.toUpper();
936     }
937 
938     currencySignLoader->getCurrencySign(currAStr, currPixmap);
939     ui.currALabel->setPixmap(currPixmap);
940     ui.currALabel->setToolTip(currAStr);
941 
942     currencySignLoader->getCurrencySign(currBStr, currPixmap);
943     ui.currBLabel->setPixmap(currPixmap);
944     ui.currBLabel->setToolTip(currBStr);
945 
946     ordersSortModel->setFilterWildcard(filterSymbol);
947     ordersModel->filterSymbolChanged(filterSymbol);
948 }
949 
tableCopyContextMenuRequested(QPoint point)950 void QtBitcoinTrader::tableCopyContextMenuRequested(QPoint point)
951 {
952     QTableView* table = dynamic_cast<QTableView*>(sender());
953 
954     if (table == nullptr)
955         return;
956 
957     int selectedCount = table->selectionModel()->selectedRows().size();
958 
959     if (selectedCount == 0)
960         return;
961 
962     lastCopyTable = table;
963     bool isOpenOrders = table == ui.ordersTable;
964     bool isDateAvailable = table != ui.depthAsksTable && table != ui.depthBidsTable;
965 
966     copyTableValuesMenu.actions().at(2)->setVisible(isDateAvailable);
967 
968     copyTableValuesMenu.actions().at(6)->setEnabled(selectedCount == 1);
969     copyTableValuesMenu.actions().at(7)->setEnabled(selectedCount == 1);
970     copyTableValuesMenu.actions().at(8)->setEnabled(selectedCount == 1);
971     copyTableValuesMenu.actions().at(9)->setEnabled(selectedCount == 1);
972 
973     copyTableValuesMenu.actions().at(10)->setVisible(isOpenOrders);
974     copyTableValuesMenu.actions().at(11)->setVisible(isOpenOrders);
975     copyTableValuesMenu.actions().at(12)->setVisible(isOpenOrders);
976 
977     copyTableValuesMenu.exec(lastCopyTable->viewport()->mapToGlobal(point));
978 }
979 
getOpenOrdersCount(int all)980 int QtBitcoinTrader::getOpenOrdersCount(int all)//-1: asks, 0 all, 1: bids
981 {
982     if (all == 0)
983         return ordersModel->rowCount();
984 
985     if (all == -1)
986         return ordersModel->getAsksCount();
987 
988     return ordersModel->rowCount() - ordersModel->getAsksCount();
989 }
990 
991 
repeatSelectedOrderByType(int type,bool availableOnly)992 void QtBitcoinTrader::repeatSelectedOrderByType(int type, bool availableOnly)
993 {
994     if (lastCopyTable == nullptr || lastCopyTable->selectionModel()->selectedRows().size() != 1)
995         return;
996 
997     int row = lastCopyTable->selectionModel()->selectedRows().first().row();
998 
999     if (lastCopyTable == ui.tableTrades)
1000         repeatOrderFromTrades(type, row);
1001     else if (lastCopyTable == ui.tableHistory)
1002         repeatOrderFromValues(type, historyModel->getRowPrice(row), historyModel->getRowVolume(row), availableOnly);
1003 
1004     if (lastCopyTable == ui.ordersTable)
1005     {
1006         row = ordersSortModel->mapToSource(ordersSortModel->index(row, 0)).row();
1007         repeatOrderFromValues(type, ordersModel->getRowPrice(row), ordersModel->getRowVolume(row)*floatFeeDec, availableOnly);
1008     }
1009 
1010     if (lastCopyTable == ui.depthAsksTable)
1011         depthSelectOrder(swapedDepth ? depthBidsModel->index(row, 3) : depthAsksModel->index(row, 3), true, type);
1012 
1013     if (lastCopyTable == ui.depthBidsTable)
1014         depthSelectOrder(!swapedDepth ? depthBidsModel->index(row, 1) : depthAsksModel->index(row, 1), false, type);
1015 }
1016 
repeatBuySellOrder()1017 void QtBitcoinTrader::repeatBuySellOrder()
1018 {
1019     repeatSelectedOrderByType(0);
1020 }
1021 
repeatBuyOrder()1022 void QtBitcoinTrader::repeatBuyOrder()
1023 {
1024     repeatSelectedOrderByType(1);
1025 }
1026 
repeatSellOrder()1027 void QtBitcoinTrader::repeatSellOrder()
1028 {
1029     repeatSelectedOrderByType(-1);
1030 }
1031 
copyDate()1032 void QtBitcoinTrader::copyDate()
1033 {
1034     if (lastCopyTable == nullptr)
1035         return;
1036 
1037     if (lastCopyTable == ui.tableHistory)
1038         copyInfoFromTable(ui.tableHistory, historyModel, 1);
1039     else if (lastCopyTable == ui.tableTrades)
1040         copyInfoFromTable(ui.tableTrades, tradesModel, 1);
1041     else if (lastCopyTable == ui.ordersTable)
1042         copyInfoFromTable(ui.ordersTable, ordersSortModel, 1);
1043 }
1044 
copyAmount()1045 void QtBitcoinTrader::copyAmount()
1046 {
1047     if (lastCopyTable == nullptr)
1048         return;
1049 
1050     if (lastCopyTable == ui.tableHistory)
1051         copyInfoFromTable(ui.tableHistory, historyModel, 2);
1052     else if (lastCopyTable == ui.tableTrades)
1053         copyInfoFromTable(ui.tableTrades, tradesModel, 2);
1054     else
1055 
1056         if (lastCopyTable == ui.depthAsksTable)
1057             copyInfoFromTable(ui.depthAsksTable, swapedDepth ? depthBidsModel : depthAsksModel, 3);
1058         else if (lastCopyTable == ui.depthBidsTable)
1059             copyInfoFromTable(ui.depthBidsTable, swapedDepth ? depthAsksModel : depthBidsModel, 1);
1060         else if (lastCopyTable == ui.ordersTable)
1061             copyInfoFromTable(ui.ordersTable, ordersSortModel, 4);
1062 }
1063 
copyPrice()1064 void QtBitcoinTrader::copyPrice()
1065 {
1066     if (lastCopyTable == nullptr)
1067         return;
1068 
1069     if (lastCopyTable == ui.tableHistory)
1070         copyInfoFromTable(ui.tableHistory, historyModel, 4);
1071     else if (lastCopyTable == ui.tableTrades)
1072         copyInfoFromTable(ui.tableTrades, tradesModel, 5);
1073     else
1074 
1075         if (lastCopyTable == ui.depthAsksTable)
1076             copyInfoFromTable(ui.depthAsksTable, swapedDepth ? depthBidsModel : depthAsksModel, 4);
1077         else if (lastCopyTable == ui.depthBidsTable)
1078             copyInfoFromTable(ui.depthBidsTable, swapedDepth ? depthAsksModel : depthBidsModel, 0);
1079         else if (lastCopyTable == ui.ordersTable)
1080             copyInfoFromTable(ui.ordersTable, ordersSortModel, 5);
1081 }
1082 
copyTotal()1083 void QtBitcoinTrader::copyTotal()
1084 {
1085     if (lastCopyTable == nullptr)
1086         return;
1087 
1088     if (lastCopyTable == ui.tableHistory)
1089         copyInfoFromTable(ui.tableHistory, historyModel, 5);
1090     else if (lastCopyTable == ui.tableTrades)
1091         copyInfoFromTable(ui.tableTrades, tradesModel, 6);
1092     else
1093 
1094         if (lastCopyTable == ui.depthAsksTable)
1095             copyInfoFromTable(ui.depthAsksTable, swapedDepth ? depthBidsModel : depthAsksModel, 1);
1096         else if (lastCopyTable == ui.depthBidsTable)
1097             copyInfoFromTable(ui.depthBidsTable, swapedDepth ? depthAsksModel : depthBidsModel, 3);
1098         else if (lastCopyTable == ui.ordersTable)
1099             copyInfoFromTable(ui.ordersTable, ordersSortModel, 6);
1100 }
1101 
copyInfoFromTable(QTableView * table,QAbstractItemModel * model,int i)1102 void QtBitcoinTrader::copyInfoFromTable(QTableView* table, QAbstractItemModel* model, int i)
1103 {
1104     QModelIndexList selectedRows = table->selectionModel()->selectedRows();
1105 
1106     if (selectedRows.size() == 0)
1107         return;
1108 
1109     QStringList copyData;
1110 
1111     for (int n = 0; n < selectedRows.size(); n++)
1112     {
1113         bool getToolTip = false;
1114 
1115         if ((table == ui.tableHistory && i == 1) ||
1116             (table == ui.tableTrades && i == 1))
1117             getToolTip = true;
1118 
1119         copyData << model->index(selectedRows.at(n).row(), i).data((getToolTip ? Qt::ToolTipRole : Qt::DisplayRole)).toString();
1120     }
1121 
1122     QApplication::clipboard()->setText(copyData.join("\n"));
1123 }
1124 
copySelectedRow()1125 void QtBitcoinTrader::copySelectedRow()
1126 {
1127     QStringList listToCopy;
1128 
1129     if (lastCopyTable == nullptr)
1130         return;
1131 
1132     QModelIndexList selectedRows = lastCopyTable->selectionModel()->selectedRows();
1133 
1134     if (selectedRows.size() == 0)
1135         return;
1136 
1137     for (int n = 0; n < selectedRows.size(); n++)
1138     {
1139         QString currentText = selectedRows.at(n).data(Qt::StatusTipRole).toString();
1140 
1141         if (!currentText.isEmpty())
1142             listToCopy << currentText;
1143     }
1144 
1145     if (listToCopy.isEmpty())
1146         return;
1147 
1148     QApplication::clipboard()->setText(listToCopy.join("\n"));
1149 }
1150 
on_buttonAddRuleGroup_clicked()1151 void QtBitcoinTrader::on_buttonAddRuleGroup_clicked()
1152 {
1153     AddRuleGroup ruleGroup;
1154 
1155     if (ui.widgetStaysOnTop->isChecked())
1156         ruleGroup.setWindowFlags(Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint);
1157 
1158     if (ruleGroup.exec() == QDialog::Rejected || ruleGroup.fileName.isEmpty() || !QFile::exists(ruleGroup.fileName))
1159     {
1160         return;
1161     }
1162 
1163     RuleWidget* newRule = new RuleWidget(ruleGroup.fileName);
1164     ui.rulesTabs->insertTab(ui.rulesTabs->count(), newRule, newRule->windowTitle());
1165 
1166     QStringList rulesListLoad = ruleGroup.groupsList;
1167 
1168     if (rulesListLoad.size())
1169     {
1170         ui.rulesTabs->setVisible(true);
1171         ui.rulesNoMessage->setVisible(false);
1172         return;
1173     }
1174 
1175     ui.rulesTabs->setCurrentIndex(ui.rulesTabs->count() - 1);
1176 
1177     ui.rulesTabs->setVisible(true);
1178     ui.rulesNoMessage->setVisible(false);
1179 }
1180 
on_buttonAddScript_clicked()1181 void QtBitcoinTrader::on_buttonAddScript_clicked()
1182 {
1183     AddScriptWindow scriptWindow;
1184 
1185     if (ui.widgetStaysOnTop->isChecked())
1186         scriptWindow.setWindowFlags(Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint);
1187 
1188     if (scriptWindow.exec() == QDialog::Rejected || scriptWindow.scriptName.isEmpty())
1189         return;
1190 
1191     ScriptWidget* newScript = new ScriptWidget(scriptWindow.scriptName, "", scriptWindow.copyFromExistingScript);
1192     ui.rulesTabs->insertTab(ui.rulesTabs->count(), newScript, newScript->windowTitle());
1193 
1194     ui.rulesTabs->setCurrentIndex(ui.rulesTabs->count() - 1);
1195 
1196     ui.rulesTabs->setVisible(true);
1197     ui.rulesNoMessage->setVisible(false);
1198 }
1199 
reloadScripts()1200 void QtBitcoinTrader::reloadScripts()
1201 {
1202     for (const QString& currentFilePath : QDir(baseValues.scriptFolder).entryList(QStringList() << "*.JLS" << "*.JLR"))
1203     {
1204         QString currentFile = baseValues.scriptFolder + currentFilePath;
1205 
1206         QString suffix = QFileInfo(currentFile).suffix().toUpper();
1207 
1208         if (suffix == QLatin1String("JLS"))
1209         {
1210             QSettings loadScript(currentFile, QSettings::IniFormat);
1211             QString currentName = loadScript.value("JLScript/Name").toString();
1212 
1213             if (currentName != "")
1214             {
1215                 ScriptWidget* scriptWidget = new ScriptWidget(currentName, currentFile);
1216                 ui.rulesTabs->insertTab(ui.rulesTabs->count(), scriptWidget, scriptWidget->windowTitle());
1217             }
1218             else
1219                 QFile::remove(currentFile);
1220         }
1221         else if (suffix == QLatin1String("JLR"))
1222         {
1223             QSettings loadScript(currentFile, QSettings::IniFormat);
1224             QString currentName = loadScript.value("JLRuleGroup/Name", "").toString();
1225 
1226             if (currentName != "")
1227             {
1228                 RuleWidget* newRule = new RuleWidget(currentFile);
1229                 ui.rulesTabs->insertTab(ui.rulesTabs->count(), newRule, newRule->windowTitle());
1230             }
1231             else
1232                 QFile::remove(currentFile);
1233         }
1234     }
1235 
1236     ui.rulesTabs->setVisible(ui.rulesTabs->count());
1237     ui.rulesNoMessage->setVisible(ui.rulesTabs->count() == 0);
1238 }
1239 
keyPressEvent(QKeyEvent * event)1240 void QtBitcoinTrader::keyPressEvent(QKeyEvent* event)
1241 {
1242     event->accept();
1243 
1244     if (ui.ordersTable->hasFocus() && (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace))
1245     {
1246         on_ordersCancelSelected_clicked();
1247         return;
1248     }
1249 
1250     if (event->modifiers()&Qt::ControlModifier)
1251     {
1252         if (event->key() == Qt::Key_B)
1253             buyBitcoinsButton();
1254         else if (event->key() == Qt::Key_S)
1255             sellBitcoinButton();
1256         else
1257 #ifndef Q_OS_MAC
1258             if (event->key() == Qt::Key_H)
1259                 buttonMinimizeToTray();
1260             else
1261 #endif
1262                 if (event->key() == Qt::Key_N)
1263                     buttonNewWindow();
1264                 else if (event->key() == Qt::Key_T)
1265                     ui.widgetStaysOnTop->setChecked(!ui.widgetStaysOnTop->isChecked());
1266 
1267         return;
1268     }
1269 
1270     int modifiersPressed = 0;
1271 
1272     if (event->modifiers()&Qt::ControlModifier)
1273         modifiersPressed++;
1274 
1275     if (event->modifiers()&Qt::ShiftModifier)
1276         modifiersPressed++;
1277 
1278     if (event->modifiers()&Qt::AltModifier)
1279         modifiersPressed++;
1280 
1281     if (event->modifiers()&Qt::MetaModifier)
1282         modifiersPressed++;
1283 
1284     if (event->key() == Qt::Key_T && modifiersPressed > 1)
1285     {
1286         (new TranslationAbout(windowWidget))->showWindow();
1287         return;
1288     }
1289 
1290     if (event->key() == Qt::Key_D && modifiersPressed > 1)
1291     {
1292         onActionDebug();
1293         return;
1294     }
1295 }
1296 
precentBidsChanged(double val)1297 void QtBitcoinTrader::precentBidsChanged(double val)
1298 {
1299     ui.tradesBidsPrecent->setValue(val);
1300 
1301     static bool lastPrecentGrowing = false;
1302     bool precentGrowing = tradesPrecentLast < val;
1303 
1304     if (lastPrecentGrowing != precentGrowing)
1305     {
1306         lastPrecentGrowing = precentGrowing;
1307         ui.tradesLabelDirection->setText(precentGrowing ? upArrowNoUtfStr : downArrowNoUtfStr);
1308     }
1309 
1310     tradesPrecentLast = val;
1311 }
1312 
on_swapDepth_clicked()1313 void QtBitcoinTrader::on_swapDepth_clicked()
1314 {
1315     swapedDepth = !swapedDepth;
1316 
1317     if (swapedDepth)
1318     {
1319         depthBidsModel->setAsk(true);
1320         ui.depthAsksTable->setModel(depthBidsModel);
1321         depthAsksModel->setAsk(false);
1322         ui.depthBidsTable->setModel(depthAsksModel);
1323     }
1324     else
1325     {
1326         depthAsksModel->setAsk(true);
1327         ui.depthAsksTable->setModel(depthAsksModel);
1328         depthBidsModel->setAsk(false);
1329         ui.depthBidsTable->setModel(depthBidsModel);
1330     }
1331 
1332     QString tempText = ui.asksLabel->text();
1333     QString tempId = ui.asksLabel->accessibleName();
1334     QString tempStyle = ui.asksLabel->styleSheet();
1335 
1336     ui.asksLabel->setText(ui.bidsLabel->text());
1337     ui.asksLabel->setAccessibleName(ui.bidsLabel->accessibleName());
1338     ui.asksLabel->setStyleSheet(ui.bidsLabel->styleSheet());
1339 
1340     ui.bidsLabel->setText(tempText);
1341     ui.bidsLabel->setAccessibleName(tempId);
1342     ui.bidsLabel->setStyleSheet(tempStyle);
1343 
1344     iniSettings->setValue("UI/SwapDepth", swapedDepth);
1345     iniSettings->sync();
1346 }
1347 
anyDataReceived()1348 void QtBitcoinTrader::anyDataReceived()
1349 {
1350     softLagTime.restart();
1351     setSoftLagValue(0);
1352 }
1353 
getFeeForUSDDec(double usd)1354 double QtBitcoinTrader::getFeeForUSDDec(double usd)
1355 {
1356     double result = JulyMath::cutDoubleDecimalsCopy(usd, baseValues.currentPair.currBDecimals, false);
1357     double calcFee = JulyMath::cutDoubleDecimalsCopy(result, baseValues.currentPair.priceDecimals, true) * floatFee;
1358     calcFee = JulyMath::cutDoubleDecimalsCopy(calcFee, baseValues.currentPair.priceDecimals, true);
1359     result = result - calcFee;
1360     return result;
1361 }
1362 
addPopupDialog(int val)1363 void QtBitcoinTrader::addPopupDialog(int val)
1364 {
1365     currentPopupDialogs += val;
1366     ui.buttonNewWindow->setEnabled(currentPopupDialogs == 0);
1367 }
1368 
buttonMinimizeToTray()1369 void QtBitcoinTrader::buttonMinimizeToTray()
1370 {
1371     if (trayIcon == nullptr)
1372     {
1373         trayIcon = new QSystemTrayIcon(QIcon(":/Resources/QtBitcoinTrader.png"), this);
1374         trayIcon->setToolTip(windowTitle());
1375         connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this,
1376                 SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
1377         trayMenu = new QMenu(this);
1378         trayIcon->setContextMenu(trayMenu);
1379         trayMenu->addAction(QIcon(":/Resources/exit.png"), "Exit");
1380         trayMenu->actions().last()->setWhatsThis("EXIT");
1381         connect(trayMenu->actions().first(), SIGNAL(triggered(bool)), this, SLOT(exitApp()));
1382     }
1383 
1384     trayIcon->show();
1385     trayIcon->showMessage(windowTitleP, windowTitle());
1386     windowWidget->hide();
1387     dockHost->setFloatingVisible(false);
1388 }
1389 
trayActivated(QSystemTrayIcon::ActivationReason reazon)1390 void QtBitcoinTrader::trayActivated(QSystemTrayIcon::ActivationReason reazon)
1391 {
1392     if (trayIcon == nullptr)
1393         return;
1394 
1395     if (reazon == QSystemTrayIcon::Context)
1396     {
1397         QList<QAction*> tList = trayMenu->actions();
1398 
1399         for (int n = 0; n < tList.size(); n++)
1400             tList.at(n)->setText(julyTr(tList.at(n)->whatsThis(), tList.at(n)->text()));
1401 
1402         trayMenu->exec();
1403         return;
1404     }
1405 
1406     windowWidget->show();
1407     dockHost->setFloatingVisible(true);
1408 
1409     trayIcon->hide();
1410     delete trayMenu;
1411     trayMenu = nullptr;
1412     delete trayIcon;
1413     trayIcon = nullptr;
1414 }
1415 
checkUpdate()1416 void QtBitcoinTrader::checkUpdate()
1417 {
1418     QProcess::startDetached(QApplication::applicationFilePath(), QStringList("/checkupdatemessage"));
1419 }
1420 
startApplication(const QString & name,QStringList params)1421 void QtBitcoinTrader::startApplication(const QString& name, QStringList params)
1422 {
1423 #ifdef Q_OS_MAC
1424 
1425     if (QFileInfo(name).fileName().contains(QLatin1Char('.')))
1426         params.prepend(name);
1427 
1428     QProcess::startDetached(QLatin1String("open"), params);
1429 #else
1430     QProcess::startDetached(name, params);
1431 #endif
1432 }
1433 
sayText(const QString & text)1434 void QtBitcoinTrader::sayText(const QString& text)
1435 {
1436     if (ttsEngine.isNull())
1437         ttsEngine.reset(new QTextToSpeech(this));
1438 
1439     if (ttsEngine->availableEngines().isEmpty())
1440         startApplication(QLatin1String("say"), QStringList() << text);
1441     else
1442         ttsEngine->say(text);
1443 }
1444 
setLastTrades10MinVolume(double val)1445 void QtBitcoinTrader::setLastTrades10MinVolume(double val)
1446 {
1447     ui.tradesVolume5m->setValue(val);
1448 }
1449 
availableAmountChanged(const QString & symbol,double val)1450 void QtBitcoinTrader::availableAmountChanged(const QString& symbol, double val)
1451 {
1452     if (baseValues.currentPair.symbolSecond() == symbol)
1453         availableAmount = val;
1454 }
1455 
addLastTrades(const QString & symbol,QList<TradesItem> * newItems)1456 void QtBitcoinTrader::addLastTrades(const QString& symbol, QList<TradesItem>* newItems)
1457 {
1458     if (secondTimer.isNull())
1459     {
1460         delete newItems;
1461         return;
1462     }
1463 
1464     if (baseValues.currentPair.symbol != symbol)
1465     {
1466         delete newItems;
1467         return;
1468     }
1469 
1470     int newRowsCount = newItems->size();
1471     tradesModel->addNewTrades(newItems);
1472 
1473     tradesModel->updateTotalBTC();
1474 
1475     if (ui.tradesAutoScrollCheck->isChecked() && ui.tabLastTrades->isVisible())
1476     {
1477         setTradesScrollBarValue(ui.tableTrades->verticalScrollBar()->value() + defaultHeightForRow * newRowsCount);
1478         tabTradesScrollUp();
1479     }
1480 
1481     ui.tableTrades->resizeColumnToContents(1);
1482     ui.tableTrades->resizeColumnToContents(2);
1483     ui.tableTrades->resizeColumnToContents(3);
1484     ui.tableTrades->resizeColumnToContents(4);
1485     ui.tableTrades->resizeColumnToContents(5);
1486     ui.tableTrades->resizeColumnToContents(6);
1487 }
1488 
clearTimeOutedTrades()1489 void QtBitcoinTrader::clearTimeOutedTrades()
1490 {
1491     if (secondTimer.isNull())
1492         return;
1493 
1494     if (tradesModel->rowCount() == 0)
1495         return;
1496 
1497     int lastSliderValue = ui.tableTrades->verticalScrollBar()->value();
1498     tradesModel->removeDataOlderThen(TimeSync::getTimeT() - 600);
1499     ui.tableTrades->verticalScrollBar()->setValue(qMin(lastSliderValue, ui.tableTrades->verticalScrollBar()->maximum()));
1500 }
1501 
depthRequested()1502 void QtBitcoinTrader::depthRequested()
1503 {
1504     depthLagTime.restart();
1505     waitingDepthLag = true;
1506 }
1507 
depthRequestReceived()1508 void QtBitcoinTrader::depthRequestReceived()
1509 {
1510     waitingDepthLag = false;
1511 }
1512 
secondSlot()1513 void QtBitcoinTrader::secondSlot()
1514 {
1515     while (pendingGroupStates.size() && pendingGroupStates.first().elapsed.elapsed() >= 100)
1516     {
1517         setGroupState(pendingGroupStates.first().name, pendingGroupStates.first().enabled);
1518         pendingGroupStates.removeFirst();
1519     }
1520 
1521     static int execCount = 0;
1522 
1523     if (execCount == 0 || execCount == 2 || execCount == 4)
1524     {
1525         clearTimeOutedTrades();
1526         setSoftLagValue(softLagTime.elapsed());
1527     }
1528     else if (execCount == 1 || execCount == 3 || execCount == 5)
1529     {
1530         int currentElapsed = depthLagTime.elapsed();
1531 
1532         if (ui.tabDepth->isVisible())
1533             ui.depthLag->setValue(currentElapsed / 1000.0);
1534     }
1535 
1536     if (historyForceUpdate.elapsed() >= 15000)
1537     {
1538         historyForceUpdate.restart();
1539         emit getHistory(true);
1540     }
1541 
1542     if (speedTestTime.elapsed() >= 500)
1543     {
1544         speedTestTime.restart();
1545 
1546         static QList<double> halfSecondsList;
1547         halfSecondsList << static_cast<double>(baseValues.trafficSpeed / 512.0);
1548         baseValues.trafficTotal += baseValues.trafficSpeed;
1549         updateTrafficTotalValue();
1550 
1551         while (halfSecondsList.size() > 10)
1552             halfSecondsList.removeFirst();
1553 
1554         static double avSpeed;
1555         avSpeed = 0.0;
1556 
1557         for (int n = 0; n < halfSecondsList.size(); n++)
1558             avSpeed += halfSecondsList.at(n);
1559 
1560         if (halfSecondsList.size())
1561             avSpeed = avSpeed * 2.0 / halfSecondsList.size();
1562 
1563         ui.trafficSpeed->setValue(avSpeed);
1564         baseValues.trafficSpeed = 0;
1565     }
1566 
1567     if (++execCount > 5)
1568         execCount = 0;
1569 
1570     secondTimer->start(baseValues.uiUpdateInterval);
1571 }
1572 
depthVisibilityChanged(bool visible)1573 void QtBitcoinTrader::depthVisibilityChanged(bool visible)
1574 {
1575     if (currentExchange)
1576         currentExchange->depthEnabledFlag = visible || depthAsksModel->rowCount() == 0 || depthBidsModel->rowCount() == 0;
1577 }
1578 
trafficTotalToZero_clicked()1579 void QtBitcoinTrader::trafficTotalToZero_clicked()
1580 {
1581     baseValues.trafficTotal = 0;
1582     updateTrafficTotalValue();
1583 }
1584 
updateTrafficTotalValue()1585 void QtBitcoinTrader::updateTrafficTotalValue()
1586 {
1587     static int trafficTotalTypeLast = -1;
1588 
1589     if (baseValues.trafficTotal > 1073741824) //Gb
1590         baseValues.trafficTotalType = 2;
1591     else if (baseValues.trafficTotal > 1048576) //Mb
1592         baseValues.trafficTotalType = 1;
1593     else if (baseValues.trafficTotal > 1024) //Kb
1594         baseValues.trafficTotalType = 0;
1595 
1596     if (trafficTotalTypeLast != baseValues.trafficTotalType)
1597     {
1598         trafficTotalTypeLast = baseValues.trafficTotalType;
1599 
1600         switch (trafficTotalTypeLast)
1601         {
1602         case 0:
1603             networkMenu->setSuffix(" Kb");
1604             break;
1605 
1606         case 1:
1607             networkMenu->setSuffix(" Mb");
1608             break;
1609 
1610         case 2:
1611             networkMenu->setSuffix(" Gb");
1612             break;
1613 
1614         default:
1615             break;
1616         }
1617     }
1618 
1619     static int totalValueLast = -1;
1620     int totalValue = 0;
1621 
1622     switch (trafficTotalTypeLast)
1623     {
1624     case 0:
1625         totalValue = static_cast<int>(baseValues.trafficTotal / 1024);
1626         break;
1627 
1628     case 1:
1629         totalValue = static_cast<int>(baseValues.trafficTotal / 1048576);
1630         break;
1631 
1632     case 2:
1633         totalValue = static_cast<int>(baseValues.trafficTotal / 1073741824);
1634         break;
1635 
1636     default:
1637         break;
1638     }
1639 
1640     if (totalValueLast != totalValue)
1641     {
1642         totalValueLast = totalValue;
1643 
1644         if (networkMenu->getNetworkTotalMaximum() < totalValue)
1645             networkMenu->setNetworkTotalMaximum(totalValue * 10);
1646 
1647         networkMenu->setNetworkTotal(totalValue);
1648     }
1649 }
1650 
tabTradesScrollUp()1651 void QtBitcoinTrader::tabTradesScrollUp()
1652 {
1653     if (secondTimer.isNull())
1654         return;
1655 
1656     static QTimeLine timeLine(1, this);
1657 
1658     if (timeLine.duration() == 1)
1659     {
1660         connect(&timeLine, SIGNAL(frameChanged(int)), this, SLOT(setTradesScrollBarValue(int)));
1661         timeLine.setDuration(500);
1662         timeLine.setEasingCurve(QEasingCurve::OutCirc);
1663         timeLine.setLoopCount(1);
1664     }
1665 
1666     timeLine.stop();
1667     int currentScrollPos = ui.tableTrades->verticalScrollBar()->value();
1668 
1669     if (currentScrollPos == 0)
1670         return;
1671 
1672     timeLine.setFrameRange(currentScrollPos, 0);
1673     timeLine.start();
1674 }
1675 
tabTradesIndexChanged(int)1676 void QtBitcoinTrader::tabTradesIndexChanged(int)
1677 {
1678     if (ui.tabLastTrades->isVisible() && ui.tradesAutoScrollCheck->isChecked())
1679         tabTradesScrollUp();
1680 }
1681 
setTradesScrollBarValue(int val)1682 void QtBitcoinTrader::setTradesScrollBarValue(int val)
1683 {
1684     if (secondTimer.isNull())
1685         return;
1686 
1687     if (val > ui.tableTrades->verticalScrollBar()->maximum())
1688         tabTradesScrollUp();
1689     else
1690         ui.tableTrades->verticalScrollBar()->setValue(val);
1691 
1692     //ui.tableTrades->verticalScrollBar()->setValue(qMin(val,ui.tableTrades->verticalScrollBar()->maximum()));
1693 }
1694 
fixAllCurrencyLabels(QWidget * par)1695 void QtBitcoinTrader::fixAllCurrencyLabels(QWidget* par)
1696 {
1697     for (QLabel* label : par->findChildren<QLabel*>())
1698         if (label->accessibleDescription() == "USD_ICON" || label->accessibleDescription() == "BTC_ICON")
1699         {
1700             label->setMaximumSize(20, 20);
1701             label->setScaledContents(true);
1702         }
1703 }
1704 
fillAllUsdLabels(QWidget * par,QString curName)1705 void QtBitcoinTrader::fillAllUsdLabels(QWidget* par, QString curName)
1706 {
1707     curName = curName.toUpper();
1708     QPixmap btcPixmap;
1709     currencySignLoader->getCurrencySign(curName, btcPixmap);
1710 
1711     for (QLabel* labels : par->findChildren<QLabel*>())
1712         if (labels->accessibleDescription() == "USD_ICON")
1713         {
1714             labels->setPixmap(btcPixmap);
1715             labels->setToolTip(curName);
1716         }
1717 }
fillAllBtcLabels(QWidget * par,QString curName)1718 void QtBitcoinTrader::fillAllBtcLabels(QWidget* par, QString curName)
1719 {
1720     curName = curName.toUpper();
1721     QPixmap btcPixmap;
1722     currencySignLoader->getCurrencySign(curName, btcPixmap);
1723 
1724     for (QLabel* labels : par->findChildren<QLabel*>())
1725     {
1726         if (labels->accessibleDescription() == "BTC_ICON")
1727         {
1728             labels->setPixmap(btcPixmap);
1729             labels->setToolTip(curName);
1730         }
1731         else if (labels->accessibleDescription() == "DONATE_BTC_ICON")
1732         {
1733             if (labels->toolTip() != "BTC")
1734             {
1735                 QPixmap btcPixmapBTC;
1736                 currencySignLoader->getCurrencySign("BTC", btcPixmapBTC);
1737                 labels->setPixmap(btcPixmapBTC);
1738                 labels->setToolTip("BTC");
1739             }
1740         }
1741     }
1742 }
1743 
makeRitchValue(QString * text)1744 void QtBitcoinTrader::makeRitchValue(QString* text)
1745 {
1746     int lastSymbol = text->length() - 1;
1747 
1748     if (lastSymbol == -1)
1749         return;
1750 
1751     while (lastSymbol > -1 && text->at(lastSymbol) == '0')
1752         lastSymbol--;
1753 
1754     if (lastSymbol > -1 && text->at(lastSymbol) == '.')
1755         lastSymbol--;
1756 
1757     if (lastSymbol < -1)
1758         return;
1759 
1760     QString buff = text->left(lastSymbol + 1);
1761     text->remove(0, lastSymbol + 1);
1762     text->prepend("<font color=gray>");
1763     text->append("</font>");
1764     text->prepend(buff);
1765 }
1766 
reloadLanguage(const QString & preferedLangFile)1767 void QtBitcoinTrader::reloadLanguage(const QString& preferedLangFile)
1768 {
1769     constructorFinished = false;
1770 
1771     QString preferedLangFilePath = (preferedLangFile.isEmpty() || !QFile::exists(preferedLangFile)) ? julyTranslator.lastFile() : preferedLangFile;
1772 
1773     julyTranslator.loadFromFile(preferedLangFilePath);
1774     constructorFinished = true;
1775 
1776     languageChanged();
1777 }
1778 
fixAllChildButtonsAndLabels(QWidget * par)1779 void QtBitcoinTrader::fixAllChildButtonsAndLabels(QWidget* par)
1780 {
1781     for (QPushButton* pushButtons : par->findChildren<QPushButton*>())
1782         if (!pushButtons->text().isEmpty())
1783             pushButtons->setMinimumWidth(qMin(pushButtons->maximumWidth(), textFontWidth(pushButtons->text()) + 10));
1784 
1785     for (QToolButton* toolButtons : par->findChildren<QToolButton*>())
1786         if (!toolButtons->text().isEmpty())
1787             toolButtons->setMinimumWidth(toolButtons->minimumSizeHint().width());
1788 
1789     for (QCheckBox* checkBoxes : par->findChildren<QCheckBox*>())
1790         checkBoxes->setMinimumWidth(qMin(checkBoxes->maximumWidth(), textFontWidth(checkBoxes->text()) + 20));
1791 
1792     for (QLabel* labels : par->findChildren<QLabel*>())
1793         if (labels->text().length() && labels->text().at(0) != '<' && labels->accessibleDescription() != "IGNORED")
1794             labels->setMinimumWidth(qMin(labels->maximumWidth(), textFontWidth(labels->text())));
1795 
1796     fixDecimals(this);
1797 
1798     for (QWidget* widget : par->findChildren<QWidget*>())
1799     {
1800         if (widget->accessibleName() == "LOGOBUTTON")
1801         {
1802             QLayout* layout = widget->layout();
1803 
1804             if (layout == nullptr)
1805             {
1806                 layout = new QGridLayout();
1807                 layout->setContentsMargins(0, 0, 0, 0);
1808                 layout->setSpacing(0);
1809                 widget->setLayout(layout);
1810                 LogoButton* logoButton = new LogoButton(false);
1811                 connect(this, SIGNAL(themeChanged()), logoButton, SLOT(themeChanged()));
1812                 layout->addWidget(logoButton);
1813             }
1814         }
1815     }
1816 
1817     if (par == this)
1818         return;
1819 
1820     QSize minSizeHint = par->minimumSizeHint();
1821 
1822     if (isValidSize(&minSizeHint))
1823     {
1824         par->setMinimumSize(par->minimumSizeHint());
1825 
1826         if (par->width() < par->minimumSizeHint().width())
1827             par->resize(par->minimumSizeHint().width(), par->height());
1828     }
1829 }
1830 
fixDecimals(QWidget * par)1831 void QtBitcoinTrader::fixDecimals(QWidget* par)
1832 {
1833     for (QDoubleSpinBox* spinBox : par->findChildren<QDoubleSpinBox*>())
1834     {
1835         if (!spinBox->whatsThis().isEmpty())
1836             continue;
1837 
1838         if (spinBox->accessibleName().startsWith("BTC"))
1839         {
1840             if (spinBox->accessibleName().endsWith("BALANCE"))
1841                 spinBox->setDecimals(baseValues.currentPair.currABalanceDecimals);
1842             else
1843                 spinBox->setDecimals(baseValues.currentPair.currADecimals);
1844 
1845             if (spinBox->accessibleDescription() != "CAN_BE_ZERO")
1846                 spinBox->setMinimum(baseValues.currentPair.tradeVolumeMin);
1847         }
1848         else if (spinBox->accessibleName().startsWith("USD"))
1849         {
1850             if (spinBox->accessibleName().endsWith("BALANCE"))
1851                 spinBox->setDecimals(baseValues.currentPair.currBBalanceDecimals);
1852             else
1853                 spinBox->setDecimals(baseValues.currentPair.currBDecimals);
1854         }
1855         else if (spinBox->accessibleName() == "PRICE")
1856         {
1857             spinBox->setDecimals(baseValues.currentPair.priceDecimals);
1858 
1859             if (spinBox->accessibleDescription() != "CAN_BE_ZERO")
1860                 spinBox->setMinimum(baseValues.currentPair.tradePriceMin);
1861         }
1862     }
1863 }
1864 
loginChanged(const QString & text)1865 void QtBitcoinTrader::loginChanged(const QString& text)
1866 {
1867     const QString& profileNameText = text.compare(QLatin1String(" ")) ? text : profileName;
1868     ui.accountLoginLabel->setText(profileNameText);
1869     ui.accountLoginLabel->setMinimumWidth(textFontWidth(profileNameText) + 20);
1870 }
1871 
setCurrencyPairsList()1872 void QtBitcoinTrader::setCurrencyPairsList()
1873 {
1874     baseValues.currencyPairMap.clear();
1875     QString savedCurrency = iniSettings->value("Profile/Currency", "BTC/USD").toString();
1876     int indexCurrency = 0;
1877     QStringList currencyItems;
1878     QStringList filterItems;
1879 
1880     for (int n = 0; n < IniEngine::getPairsCount(); n++)
1881     {
1882         CurrencyPairItem pairItem = IniEngine::getPairs()->at(n);
1883 
1884         if (pairItem.currRequestSecond.isEmpty())
1885             baseValues.currencyPairMap[pairItem.symbol] = pairItem;
1886         else
1887         {
1888             baseValues.currencyPairMap[pairItem.symbol + pairItem.currRequestSecond.toUpper()] = pairItem;
1889 
1890             if (pairItem.currRequestSecond == "exchange")
1891             {
1892                 baseValues.currencyPairMap[pairItem.symbol] = pairItem;
1893                 filterItems << pairItem.currAStr + "/" + pairItem.currBStr;
1894             }
1895         }
1896 
1897         if (pairItem.name == savedCurrency)
1898             indexCurrency = n;
1899 
1900         currencyItems << pairItem.name;
1901     }
1902 
1903     currencyMenu->setPairs(currencyItems);
1904     currencyMenu->setCurrentIndex(indexCurrency);
1905 
1906     ui.filterOrdersCurrency->clear();
1907     ui.filterOrdersCurrency->insertItems(0, filterItems.isEmpty() ? currencyItems : filterItems);
1908     ui.filterOrdersCurrency->setCurrentIndex(0);
1909 }
1910 
currencyMenuChanged(int val)1911 void QtBitcoinTrader::currencyMenuChanged(int val)
1912 {
1913     if (!constructorFinished || val < 0 || IniEngine::getPairsCount() != currencyMenu->count())
1914         return;
1915 
1916     /*bool fastChange = ui.currencyComboBox->itemText(val).left(5) ==
1917                       ui.currencyComboBox->itemText(lastLoadedCurrency).left(5);
1918     if (val == lastLoadedCurrency)
1919         return;
1920     lastLoadedCurrency = val;*/
1921 
1922     CurrencyPairItem nextCurrencyPair = IniEngine::getPairs()->at(val);
1923 
1924     bool currencyAChanged = nextCurrencyPair.currAStr != baseValues.currentPair.currAStr;
1925     bool currencyBChanged = nextCurrencyPair.currBStr != baseValues.currentPair.currBStr;
1926 
1927     /*if (fastChange)
1928     {
1929         baseValues.currentPair = nextCurrencyPair;
1930 
1931         setSpinValue(ui.accountBTC, 0.0);
1932         setSpinValue(ui.accountUSD, 0.0);
1933 
1934         for (RuleWidget* currentGroup: ui.tabRules->findChildren<RuleWidget*>())
1935             currentGroup->currencyChanged();
1936 
1937         for (ScriptWidget* currentGroup: ui.tabRules->findChildren<ScriptWidget*>())
1938             currentGroup->currencyChanged();
1939 
1940         return;
1941     }*/
1942 
1943     fillAllUsdLabels(this, nextCurrencyPair.currBStr);
1944     fillAllBtcLabels(this, nextCurrencyPair.currAStr);
1945 
1946     // TODO: ?? fillAll Usd/Btc for float
1947 
1948     iniSettings->setValue("Profile/Currency", ui.currencyMenuTool->text());
1949 
1950     if (currencyAChanged)
1951         setSpinValue(ui.accountBTC, 0.0);
1952 
1953     if (currencyBChanged)
1954         setSpinValue(ui.accountUSD, 0.0);
1955 
1956     buyTotalSpend->setValue(0.0);
1957     sellTotalBtc->setValue(0.0);
1958     ui.tradesVolume5m->setValue(0.0);
1959     setSpinValue(ui.ruleAmountToReceiveValue, 0.0);
1960     setSpinValue(ui.ruleTotalToBuyValue, 0.0);
1961     setSpinValue(ui.ruleAmountToReceiveBSValue, 0.0);
1962     setSpinValue(ui.ruleTotalToBuyBSValue, 0.0);
1963 
1964     precentBidsChanged(0.0);
1965     tradesModel->clear();
1966     tradesPrecentLast = 0.0;
1967 
1968     QString buyGroupboxText = julyTr("GROUPBOX_BUY", "Buy %1");
1969     bool buyGroupboxCase = false;
1970 
1971     if (buyGroupboxText.length() > 2)
1972         buyGroupboxCase = buyGroupboxText.at(2).isUpper();
1973 
1974     if (buyGroupboxCase)
1975         buyGroupboxText = buyGroupboxText.arg(nextCurrencyPair.currAName.toUpper());
1976     else
1977         buyGroupboxText = buyGroupboxText.arg(nextCurrencyPair.currAName);
1978 
1979     ui.widgetBuy->parentWidget()->setWindowTitle(buyGroupboxText);
1980 
1981     QString sellGroupboxText = julyTr("GROUPBOX_SELL", "Sell %1");
1982     bool sellGroupboxCase = true;
1983 
1984     if (sellGroupboxText.length() > 2)
1985         sellGroupboxCase = sellGroupboxText.at(2).isUpper();
1986 
1987     if (sellGroupboxCase)
1988         sellGroupboxText = sellGroupboxText.arg(nextCurrencyPair.currAName.toUpper());
1989     else
1990         sellGroupboxText = sellGroupboxText.arg(nextCurrencyPair.currAName);
1991 
1992     ui.widgetSell->parentWidget()->setWindowTitle(sellGroupboxText);
1993 
1994     static int firstLoad = 0;
1995 
1996     if (++firstLoad > 1)
1997     {
1998         firstLoad = 3;
1999         emit clearValues();
2000     }
2001 
2002     if (ui.comboBoxGroupByPrice->count() > 1)
2003     {
2004         ui.comboBoxGroupByPrice->blockSignals(true);
2005         ui.comboBoxGroupByPrice->clear();
2006         ui.comboBoxGroupByPrice->blockSignals(false);
2007         ui.comboBoxGroupByPrice->addItem(julyTr("DONT_GROUP", "None"), double(0));
2008     }
2009 
2010     marketPricesNotLoaded = true;
2011     balanceNotLoaded = true;
2012     fixDecimals(this);
2013 
2014     iniSettings->sync();
2015 
2016     baseValues.currentPair = nextCurrencyPair;
2017     depthAsksModel->fixTitleWidths();
2018     depthBidsModel->fixTitleWidths();
2019 
2020     setSpinValue(ui.ordersLastBuyPrice, 0.0);
2021     setSpinValue(ui.ordersLastSellPrice, 0.0);
2022 
2023     if (currentExchange->clearHistoryOnCurrencyChanged)
2024         historyModel->clear();
2025     else
2026         historyModel->loadLastPrice();
2027 
2028     calcOrdersTotalValues();
2029 
2030     ui.filterOrdersCurrency->setCurrentIndex(val);
2031 
2032     currencyChangedDate = TimeSync::getTimeT();
2033 
2034     fixDecimals(this);
2035 
2036     emit getHistory(true);
2037     emit clearCharts();
2038     chartsView->clearCharts();
2039 
2040     setSpinValue(ui.marketHigh, IndicatorEngine::getValue(baseValues.exchangeName +
2041                  '_' + baseValues.currentPair.symbol + "_High"));
2042     setSpinValue(ui.marketLow, IndicatorEngine::getValue(baseValues.exchangeName +
2043                  '_' + baseValues.currentPair.symbol + "_Low"));
2044     setSpinValue(ui.marketLast, IndicatorEngine::getValue(baseValues.exchangeName +
2045                  '_' + baseValues.currentPair.symbol + "_Last"));
2046     setSpinValue(ui.marketVolume, IndicatorEngine::getValue(baseValues.exchangeName +
2047                  '_' + baseValues.currentPair.symbol + "_Volume"));
2048     setSpinValue(ui.marketAsk, IndicatorEngine::getValue(baseValues.exchangeName +
2049                  '_' + baseValues.currentPair.symbol + "_Buy"));
2050     setSpinValue(ui.marketBid, IndicatorEngine::getValue(baseValues.exchangeName +
2051                  '_' + baseValues.currentPair.symbol + "_Sell"));
2052 
2053     if (qFuzzyIsNull(ui.marketAsk->value()))
2054         buyPricePerCoin->setValue(100.0);
2055     else
2056         buyPricePerCoin->setValue(ui.marketAsk->value());
2057 
2058     if (qFuzzyIsNull(ui.marketBid->value()))
2059         sellPricePerCoin->setValue(200.0);
2060     else
2061         sellPricePerCoin->setValue(ui.marketBid->value());
2062 }
2063 
clearDepth()2064 void QtBitcoinTrader::clearDepth()
2065 {
2066     depthAsksModel->clear();
2067     depthBidsModel->clear();
2068     emit reloadDepth();
2069 }
2070 
volumeAmountChanged(double volumeTotal,double amountTotal)2071 void QtBitcoinTrader::volumeAmountChanged(double volumeTotal, double amountTotal)
2072 {
2073     setSpinValue(ui.ordersTotalBTC, volumeTotal);
2074     setSpinValue(ui.ordersTotalUSD, amountTotal);
2075 }
2076 
calcOrdersTotalValues()2077 void QtBitcoinTrader::calcOrdersTotalValues()
2078 {
2079     checkValidBuyButtons();
2080     checkValidSellButtons();
2081 }
2082 
profitSellThanBuyCalc()2083 void QtBitcoinTrader::profitSellThanBuyCalc()
2084 {
2085     if (!profitSellThanBuyUnlocked)
2086         return;
2087 
2088     profitSellThanBuyUnlocked = false;
2089     double calcValue = 0.0;
2090 
2091     if (sellTotalBtc->value() != 0.0 && ui.buyTotalBtcResult->value() != 0.0)
2092         calcValue = ui.buyTotalBtcResult->value() - sellTotalBtc->value();
2093 
2094     sellThanBuySpinBox->setValue(calcValue);
2095     profitSellThanBuyUnlocked = true;
2096 }
2097 
profitBuyThanSellCalc()2098 void QtBitcoinTrader::profitBuyThanSellCalc()
2099 {
2100     if (!profitBuyThanSellUnlocked)
2101         return;
2102 
2103     profitBuyThanSellUnlocked = false;
2104     double calcValue = 0.0;
2105 
2106     if (buyTotalSpend->value() != 0.0 && sellAmountToReceive->value() != 0.0)
2107         calcValue = sellAmountToReceive->value() - buyTotalSpend->value();
2108 
2109     profitLossSpinBox->setValue(calcValue);
2110     profitBuyThanSellUnlocked = true;
2111 }
2112 
profitLossSpinBoxPrec_valueChanged(double val)2113 void QtBitcoinTrader::profitLossSpinBoxPrec_valueChanged(double val)
2114 {
2115     if (profitBuyThanSellChangedUnlocked)
2116     {
2117         profitBuyThanSellChangedUnlocked = false;
2118         profitLossSpinBox->setValue(val == 0.0 ? 0.0 : buyTotalSpend->value()*val / 100.0);
2119         profitBuyThanSellChangedUnlocked = true;
2120     }
2121 }
2122 
profitLossSpinBox_valueChanged(double val)2123 void QtBitcoinTrader::profitLossSpinBox_valueChanged(double val)
2124 {
2125     QString styleChanged;
2126 
2127     if (val < -0.009)
2128         styleChanged = "QDoubleSpinBox {background: " + baseValues.appTheme.lightRed.name() + ";}";
2129     else if (val > 0.009)
2130         styleChanged = "QDoubleSpinBox {background: " + baseValues.appTheme.lightGreen.name() + ";}";
2131 
2132     if (profitBuyThanSellChangedUnlocked)
2133     {
2134         profitBuyThanSellChangedUnlocked = false;
2135         profitLossSpinBoxPrec->setValue(buyTotalSpend->value() == 0.0 ? 0.0 : val * 100.0 / buyTotalSpend->value());
2136         profitBuyThanSellChangedUnlocked = true;
2137     }
2138 
2139     profitLossSpinBox->setStyleSheet(styleChanged);
2140     profitLossSpinBoxPrec->setStyleSheet(styleChanged);
2141 
2142     ui.buttonBuyThenSellApply->setEnabled(true);
2143 }
2144 
sellThanBuySpinBoxPrec_valueChanged(double val)2145 void QtBitcoinTrader::sellThanBuySpinBoxPrec_valueChanged(double val)
2146 {
2147     if (profitBuyThanSellChangedUnlocked)
2148     {
2149         profitBuyThanSellChangedUnlocked = false;
2150         sellThanBuySpinBox->setValue(val == 0.0 ? 0.0 : sellTotalBtc->value()*val / 100.0);
2151         profitBuyThanSellChangedUnlocked = true;
2152     }
2153 }
2154 
sellThanBuySpinBox_valueChanged(double val)2155 void QtBitcoinTrader::sellThanBuySpinBox_valueChanged(double val)
2156 {
2157     QString styleChanged;
2158 
2159     if (val < -0.009)
2160         styleChanged = "QDoubleSpinBox {background: " + baseValues.appTheme.lightRed.name() + ";}";
2161     else if (val > 0.009)
2162         styleChanged = "QDoubleSpinBox {background: " + baseValues.appTheme.lightGreen.name() + ";}";
2163 
2164     if (profitBuyThanSellChangedUnlocked)
2165     {
2166         profitBuyThanSellChangedUnlocked = false;
2167         sellThanBuySpinBoxPrec->setValue(sellTotalBtc->value() == 0.0 ? 0.0 : val * 100.0 / sellTotalBtc->value());
2168         profitBuyThanSellChangedUnlocked = true;
2169     }
2170 
2171     sellThanBuySpinBox->setStyleSheet(styleChanged);
2172     sellThanBuySpinBoxPrec->setStyleSheet(styleChanged);
2173 
2174     ui.buttonSellThenBuyApply->setEnabled(true);
2175 }
2176 
on_zeroSellThanBuyProfit_clicked()2177 void QtBitcoinTrader::on_zeroSellThanBuyProfit_clicked()
2178 {
2179     sellThanBuySpinBox->setValue(0.0);
2180 }
2181 
on_zeroBuyThanSellProfit_clicked()2182 void QtBitcoinTrader::on_zeroBuyThanSellProfit_clicked()
2183 {
2184     profitLossSpinBox->setValue(0.0);
2185 }
2186 
profitSellThanBuy()2187 void QtBitcoinTrader::profitSellThanBuy()
2188 {
2189     profitSellThanBuyUnlocked = false;
2190     buyTotalSpend->setValue(sellAmountToReceive->value());
2191     buyPricePerCoin->setValue(buyTotalSpend->value() / ((sellTotalBtc->value() + sellThanBuySpinBox->value()) /
2192                               floatFeeDec) - baseValues.currentPair.priceMin);
2193     profitSellThanBuyUnlocked = true;
2194     profitBuyThanSellCalc();
2195     profitSellThanBuyCalc();
2196     ui.buttonSellThenBuyApply->setEnabled(false);
2197 }
2198 
profitBuyThanSell()2199 void QtBitcoinTrader::profitBuyThanSell()
2200 {
2201     profitBuyThanSellUnlocked = false;
2202     sellTotalBtc->setValue(ui.buyTotalBtcResult->value());
2203     sellPricePerCoin->setValue((buyTotalSpend->value() + profitLossSpinBox->value()) /
2204                                (sellTotalBtc->value()*floatFeeDec) + baseValues.currentPair.priceMin);
2205     profitBuyThanSellUnlocked = true;
2206     profitBuyThanSellCalc();
2207     profitSellThanBuyCalc();
2208     ui.buttonBuyThenSellApply->setEnabled(false);
2209 }
2210 
setApiDown(bool)2211 void QtBitcoinTrader::setApiDown(bool)
2212 {
2213     //ui.exchangeLagBack->setVisible(on);
2214 }
2215 
clearData(QString data)2216 QString QtBitcoinTrader::clearData(QString data)
2217 {
2218     while (data.size() && (data.at(0) == '{' || data.at(0) == '[' || data.at(0) == '\"'))
2219         data.remove(0, 1);
2220 
2221     while (data.size() && (data.at(data.length() - 1) == '}' || data.at(data.length() - 1) == ']' ||
2222                            data.at(data.length() - 1) == '\"'))
2223         data.remove(data.length() - 1, 1);
2224 
2225     return data;
2226 }
2227 
on_accountFee_valueChanged(double val)2228 void QtBitcoinTrader::on_accountFee_valueChanged(double val)
2229 {
2230     floatFee = val / 100.0;
2231     floatFeeDec = 1.0 - floatFee;
2232     floatFeeInc = 1.0 + floatFee;
2233 
2234     if (currentExchange && !currentExchange->supportsExchangeFee)
2235         iniSettings->setValue("Profile/CustomFee", ui.accountFee->value());
2236 
2237     bool notZeroFee = floatFee > 0.0;
2238     ui.calcButton->setVisible(notZeroFee);
2239     ui.label_6->setVisible(notZeroFee);
2240     ui.label_10->setVisible(notZeroFee);
2241     ui.label_28->setVisible(notZeroFee);
2242     ui.label_29->setVisible(notZeroFee);
2243     ui.buyNextInSellPrice->setVisible(notZeroFee);
2244     ui.buyNextMinBuyStep->setVisible(notZeroFee);
2245     ui.sellNextMaxBuyPrice->setVisible(notZeroFee);
2246     ui.sellNextMaxBuyStep->setVisible(notZeroFee);
2247     ui.usdLabel9->setVisible(notZeroFee);
2248     ui.usdLabel10->setVisible(notZeroFee);
2249     ui.usdLabel13->setVisible(notZeroFee);
2250     ui.usdLabel14->setVisible(notZeroFee);
2251     ui.buyTotalBtcResult->setVisible(notZeroFee);
2252     ui.btcLabel6->setVisible(notZeroFee);
2253     ui.label_62->setVisible(notZeroFee);
2254 }
2255 
getMidData(const QString & a,QString b,QByteArray * data)2256 QByteArray QtBitcoinTrader::getMidData(const QString& a, QString b, QByteArray* data)
2257 {
2258     QByteArray rez;
2259 
2260     if (b.isEmpty())
2261         b = "\",";
2262 
2263     int startPos = data->indexOf(a, 0);
2264 
2265     if (startPos > -1)
2266     {
2267         int endPos = data->indexOf(b, startPos + a.length());
2268 
2269         if (endPos > -1)
2270             rez = data->mid(startPos + a.length(), endPos - startPos - a.length());
2271     }
2272 
2273     return rez;
2274 }
2275 
adjustDockMinSize(QWidget * widget)2276 static void adjustDockMinSize(QWidget* widget)
2277 {
2278     QDockWidget* dock = static_cast<QDockWidget*>(widget->parentWidget());
2279     int minHint = widget->minimumSizeHint().width();
2280     int textWidth = textFontWidth(dock->windowTitle());
2281     static const int TitleExtra = 50; // left padding + undock/close buttons
2282     int minWidth = qMax(minHint, textWidth + TitleExtra);
2283     widget->setMinimumWidth(minWidth);
2284     widget->setMaximumWidth(minWidth + 100);
2285 }
2286 
updateLogTable()2287 void QtBitcoinTrader::updateLogTable()
2288 {
2289     emit getHistory(false);
2290 }
2291 
balanceChanged(double)2292 void QtBitcoinTrader::balanceChanged(double)
2293 {
2294     calcOrdersTotalValues();
2295     emit getHistory(true);
2296 }
2297 
ordersIsAvailable()2298 void QtBitcoinTrader::ordersIsAvailable()
2299 {
2300     if (ui.ordersTableFrame->isVisible())
2301         return;
2302 
2303     ui.noOpenedOrdersLabel->setVisible(false);
2304     ui.ordersTableFrame->setVisible(true);
2305 }
2306 
ordersIsEmpty()2307 void QtBitcoinTrader::ordersIsEmpty()
2308 {
2309     if (ordersModel->rowCount())
2310     {
2311         if (debugLevel)
2312             logThread->writeLog("Order table cleared");
2313 
2314         ordersModel->clear();
2315         setSpinValue(ui.ordersTotalBTC, 0.0);
2316         setSpinValue(ui.ordersTotalUSD, 0.0);
2317         ui.ordersTableFrame->setVisible(false);
2318         ui.noOpenedOrdersLabel->setVisible(true);
2319     }
2320 
2321     //calcOrdersTotalValues();
2322 }
2323 
orderCanceled(const QString & symbol,QByteArray oid)2324 void QtBitcoinTrader::orderCanceled(const QString& symbol, QByteArray oid)
2325 {
2326     if (debugLevel)
2327         logThread->writeLog("Removed order: " + oid + " " + symbol.toLatin1());
2328 
2329     ordersModel->setOrderCanceled(oid);
2330 }
2331 
orderBookChanged(const QString & symbol,QList<OrderItem> * orders)2332 void QtBitcoinTrader::orderBookChanged(const QString& symbol, QList<OrderItem>* orders)
2333 {
2334     if (symbol != baseValues.currentPair.symbol)
2335     {
2336         delete orders;
2337         return;
2338     }
2339 
2340     currentlyAddingOrders = true;
2341     ordersModel->orderBookChanged(orders);
2342 
2343     calcOrdersTotalValues();
2344     checkValidOrdersButtons();
2345 
2346     depthAsksModel->reloadVisibleItems();
2347     depthBidsModel->reloadVisibleItems();
2348     currentlyAddingOrders = false;
2349 
2350     ui.tableHistory->resizeColumnToContents(0);
2351     ui.tableHistory->resizeColumnToContents(2);
2352     ui.tableHistory->resizeColumnToContents(3);
2353     ui.tableHistory->resizeColumnToContents(4);
2354     ui.tableHistory->resizeColumnToContents(5);
2355     ui.tableHistory->resizeColumnToContents(6);
2356 }
2357 
showErrorMessage(const QString & message)2358 void QtBitcoinTrader::showErrorMessage(const QString& message)
2359 {
2360     if (!showingMessage && lastMessageTime.elapsed() > 10000)
2361     {
2362         showingMessage = true;
2363 
2364         if (debugLevel)
2365             logThread->writeLog(baseValues.exchangeName.toLatin1() + " Error: " + message.toUtf8(), 2);
2366 
2367         lastMessageTime.restart();
2368 
2369         if (message.startsWith("I:>"))
2370             identificationRequired(message.right(message.size() - 3));
2371         else
2372             QMessageBox::warning(windowWidget, julyTr("AUTH_ERROR", "%1 Error").arg(baseValues.exchangeName), message);
2373 
2374         showingMessage = false;
2375     }
2376 }
2377 
identificationRequired(QString message)2378 void QtBitcoinTrader::identificationRequired(QString message)
2379 {
2380     if (!message.isEmpty())
2381         message.prepend("<br><br>");
2382 
2383     message.prepend(julyTr("TRUNAUTHORIZED",
2384                            "Identification required to access private API.<br>Please enter valid API key and Secret."));
2385 
2386     QMessageBox::warning(windowWidget, julyTr("AUTH_ERROR", "%1 Error").arg(baseValues.exchangeName), message);
2387 }
2388 
historyChanged(QList<HistoryItem> * historyItems)2389 void QtBitcoinTrader::historyChanged(QList<HistoryItem>* historyItems)
2390 {
2391     historyModel->historyChanged(historyItems);
2392     ui.tableHistory->resizeColumnToContents(1);
2393     ui.tableHistory->resizeColumnToContents(2);
2394     ui.tableHistory->resizeColumnToContents(3);
2395     ui.tableHistory->resizeColumnToContents(4);
2396     ui.tableHistory->resizeColumnToContents(5);
2397 
2398     if (debugLevel)
2399         logThread->writeLog("Log Table Updated");
2400 }
2401 
accLastSellChanged(const QString & priceCurrency,double val)2402 void QtBitcoinTrader::accLastSellChanged(const QString& priceCurrency, double val)
2403 {
2404     setSpinValue(ui.ordersLastSellPrice, val);
2405 
2406     if (ui.usdLabelLastSell->toolTip() != priceCurrency)
2407     {
2408         QPixmap btcPixmap;
2409         currencySignLoader->getCurrencySign(priceCurrency.toUpper(), btcPixmap);
2410         ui.usdLabelLastSell->setPixmap(btcPixmap);
2411         ui.usdLabelLastSell->setToolTip(priceCurrency);
2412     }
2413 }
2414 
accLastBuyChanged(const QString & priceCurrency,double val)2415 void QtBitcoinTrader::accLastBuyChanged(const QString& priceCurrency, double val)
2416 {
2417     setSpinValue(ui.ordersLastBuyPrice, val);
2418 
2419     if (ui.usdLabelLastBuy->toolTip() != priceCurrency)
2420     {
2421         QPixmap btcPixmap;
2422         currencySignLoader->getCurrencySign(priceCurrency.toUpper(), btcPixmap);
2423         ui.usdLabelLastBuy->setPixmap(btcPixmap);
2424         ui.usdLabelLastBuy->setToolTip(priceCurrency);
2425     }
2426 }
2427 
translateUnicodeStr(QString * str)2428 void QtBitcoinTrader::translateUnicodeStr(QString* str)
2429 {
2430     const QRegExp rx("(\\\\u[0-9a-fA-F]{4})");
2431     int pos = 0;
2432 
2433     while ((pos = rx.indexIn(*str, pos)) != -1)
2434         str->replace(pos++, 6, QChar(rx.cap(1).right(4).toUShort(nullptr, 16)));
2435 }
2436 
cancelOrder(const QString & symbol,const QByteArray & oid)2437 void QtBitcoinTrader::cancelOrder(const QString& symbol, const QByteArray& oid)
2438 {
2439     emit cancelOrderByOid(symbol, oid);
2440 }
2441 
on_ordersCancelBidsButton_clicked()2442 void QtBitcoinTrader::on_ordersCancelBidsButton_clicked()
2443 {
2444     QString cancelSymbol;
2445 
2446     if (ui.ordersFilterCheckBox->isChecked())
2447         cancelSymbol = IniEngine::getPairSymbol(ui.filterOrdersCurrency->currentIndex());
2448 
2449     ordersModel->ordersCancelBids(cancelSymbol);
2450 }
2451 
on_ordersCancelAsksButton_clicked()2452 void QtBitcoinTrader::on_ordersCancelAsksButton_clicked()
2453 {
2454     QString cancelSymbol;
2455 
2456     if (ui.ordersFilterCheckBox->isChecked())
2457         cancelSymbol = IniEngine::getPairSymbol(ui.filterOrdersCurrency->currentIndex());
2458 
2459     ordersModel->ordersCancelAsks(cancelSymbol);
2460 }
2461 
on_ordersCancelAllButton_clicked()2462 void QtBitcoinTrader::on_ordersCancelAllButton_clicked()
2463 {
2464     QString cancelSymbol;
2465 
2466     if (ui.ordersFilterCheckBox->isChecked())
2467         cancelSymbol = IniEngine::getPairSymbol(ui.filterOrdersCurrency->currentIndex());
2468 
2469     ordersModel->ordersCancelAll(cancelSymbol);
2470 }
2471 
2472 
cancelPairOrders(const QString & symbol)2473 void QtBitcoinTrader::cancelPairOrders(const QString& symbol)
2474 {
2475     ordersModel->ordersCancelAll(symbol);
2476 }
2477 
cancelAskOrders(const QString & symbol)2478 void QtBitcoinTrader::cancelAskOrders(const QString& symbol)
2479 {
2480     ordersModel->ordersCancelAsks(symbol);
2481 }
2482 
cancelBidOrders(const QString & symbol)2483 void QtBitcoinTrader::cancelBidOrders(const QString& symbol)
2484 {
2485     ordersModel->ordersCancelBids(symbol);
2486 }
2487 
cancelAllCurrentPairOrders()2488 void QtBitcoinTrader::cancelAllCurrentPairOrders()
2489 {
2490     cancelPairOrders(baseValues.currentPair.symbol);
2491 }
2492 
on_ordersCancelSelected_clicked()2493 void QtBitcoinTrader::on_ordersCancelSelected_clicked()
2494 {
2495     QModelIndexList selectedRows = ui.ordersTable->selectionModel()->selectedRows();
2496 
2497     if (selectedRows.size() == 0)
2498         return;
2499 
2500     for (int n = 0; n < selectedRows.size(); n++)
2501     {
2502         QByteArray oid = selectedRows.at(n).data(Qt::UserRole).toByteArray();
2503 
2504         if (!oid.isEmpty())
2505             cancelOrder(selectedRows.at(n).data(Qt::AccessibleTextRole).toString(), oid);
2506     }
2507 }
2508 
cancelOrderByXButton()2509 void QtBitcoinTrader::cancelOrderByXButton()
2510 {
2511     QPushButton* buttonCancel = dynamic_cast<QPushButton*>(sender());
2512 
2513     if (!buttonCancel)
2514         return;
2515 
2516     QByteArray order = buttonCancel->property("OrderId").toByteArray();
2517 
2518     if (!order.isEmpty())
2519         cancelOrder(buttonCancel->property("Symbol").toString(), order);
2520 }
2521 
on_buttonNight_clicked()2522 void QtBitcoinTrader::on_buttonNight_clicked()
2523 {
2524     baseValues.currentTheme++;
2525 
2526     if (baseValues.currentTheme > 2)
2527         baseValues.currentTheme = 0;
2528 
2529     if (baseValues.currentTheme == 1)
2530     {
2531         baseValues.appTheme = baseValues.appThemeDark;
2532 #if QT_VERSION < 0x050000
2533         qApp->setStyle(new QPlastiqueStyle);
2534 #endif
2535     }
2536     else if (baseValues.currentTheme == 2)
2537     {
2538         baseValues.appTheme = baseValues.appThemeGray;
2539         qApp->setStyle(baseValues.osStyle);
2540     }
2541     else if (baseValues.currentTheme == 0)
2542     {
2543         baseValues.appTheme = baseValues.appThemeLight;
2544         qApp->setStyle(baseValues.osStyle);
2545     }
2546 
2547     qApp->setPalette(baseValues.appTheme.palette);
2548     qApp->setStyleSheet(baseValues.appTheme.styleSheet);
2549 
2550     ui.accountLoginLabel->setStyleSheet("color: " + baseValues.appTheme.black.name() + "; background: " +
2551                                         baseValues.appTheme.white.name());
2552     ui.noOpenedOrdersLabel->setStyleSheet("font-size:27px; border: 1px solid gray; background: " +
2553                                           baseValues.appTheme.white.name() + "; color: " + baseValues.appTheme.gray.name());
2554     ui.rulesNoMessage->setStyleSheet("font-size:27px; border: 1px solid gray; background: " +
2555                                      baseValues.appTheme.white.name() + "; color: " + baseValues.appTheme.gray.name());
2556 
2557     ui.buyBitcoinsButton->setStyleSheet("QPushButton {font-size:21px; color: " + baseValues.appTheme.blue.name() +
2558                                         "} QPushButton::disabled {color: " + baseValues.appTheme.gray.name() + "}");
2559     ui.sellBitcoinsButton->setStyleSheet("QPushButton {font-size:21px; color: " + baseValues.appTheme.red.name() +
2560                                          "} QPushButton::disabled {color: " + baseValues.appTheme.gray.name() + "}");
2561 
2562     profitLossSpinBox_valueChanged(profitLossSpinBox->value());
2563     sellThanBuySpinBox_valueChanged(sellThanBuySpinBox->value());
2564     buyTotalSpend_valueChanged(buyTotalSpend->value());
2565     sellTotalBtc_valueChanged(sellTotalBtc->value());
2566 
2567     for (RuleWidget* currentGroup : ui.tabRules->findChildren<RuleWidget*>())
2568         currentGroup->updateStyleSheets();
2569 
2570     if (baseValues.currentTheme == 2)
2571         ui.buttonNight->setIcon(QIcon("://Resources/Day.png"));
2572     else if (baseValues.currentTheme == 0)
2573         ui.buttonNight->setIcon(QIcon("://Resources/Night.png"));
2574     else
2575         ui.buttonNight->setIcon(QIcon("://Resources/Gray.png"));
2576 
2577     if (swapedDepth)
2578     {
2579         ui.asksLabel->setStyleSheet("color: " + baseValues.appTheme.blue.name());
2580         ui.bidsLabel->setStyleSheet("color: " + baseValues.appTheme.red.name());
2581     }
2582     else
2583     {
2584         ui.asksLabel->setStyleSheet("color: " + baseValues.appTheme.red.name());
2585         ui.bidsLabel->setStyleSheet("color: " + baseValues.appTheme.blue.name());
2586     }
2587 
2588     iniSettings->setValue("UI/NightMode", baseValues.currentTheme);
2589 
2590     emit themeChanged();
2591 
2592     chartsView->setStyleSheet("background: " + baseValues.appTheme.white.name());
2593     chartsView->refreshCharts();
2594 
2595     if (baseValues.currentTheme == 1)
2596         ui.widgetLogo->setStyleSheet("background:black");
2597     else
2598         ui.widgetLogo->setStyleSheet("background:white");
2599 }
2600 
on_calcButton_clicked()2601 void QtBitcoinTrader::on_calcButton_clicked()
2602 {
2603     if (feeCalculatorSingleInstance && feeCalculator)
2604         feeCalculator->activateWindow();
2605     else
2606         feeCalculator = new FeeCalculator;
2607 }
2608 
checkValidSellButtons()2609 void QtBitcoinTrader::checkValidSellButtons()
2610 {
2611     ui.widgetSellThenBuy->setEnabled(sellTotalBtc->value() >= baseValues.currentPair.tradeVolumeMin &&
2612                                      sellAmountToReceive->value() >= baseValues.currentPair.tradeTotalMin);
2613     ui.sellBitcoinsButton->setEnabled(ui.widgetSellThenBuy->isEnabled() &&
2614                                       /*ui.sellTotalBtc->value()<=getAvailableBTC()&&*/sellTotalBtc->value() > 0.0);
2615 }
2616 
on_sellPricePerCoinAsMarketLastPrice_clicked()2617 void QtBitcoinTrader::on_sellPricePerCoinAsMarketLastPrice_clicked()
2618 {
2619     sellPricePerCoin->setValue(ui.marketLast->value());
2620 }
2621 
on_sellTotalBtcAllIn_clicked()2622 void QtBitcoinTrader::on_sellTotalBtcAllIn_clicked()
2623 {
2624     sellTotalBtc->setValue(getAvailableBTC());
2625 }
2626 
on_sellTotalBtcHalfIn_clicked()2627 void QtBitcoinTrader::on_sellTotalBtcHalfIn_clicked()
2628 {
2629     sellTotalBtc->setValue(getAvailableBTC() / 2.0);
2630 }
2631 
setDataPending(bool on)2632 void QtBitcoinTrader::setDataPending(bool on)
2633 {
2634     isDataPending = on;
2635 }
2636 
setSoftLagValue(int mseconds)2637 void QtBitcoinTrader::setSoftLagValue(int mseconds)
2638 {
2639     if (secondTimer.isNull())
2640         return;
2641 
2642     if (!isDataPending && mseconds < baseValues.httpRequestTimeout)
2643         mseconds = 0;
2644 
2645     static int lastSoftLag = -1;
2646 
2647     if (lastSoftLag == mseconds)
2648         return;
2649 
2650     ui.lastUpdate->setValue(mseconds / 1000.0);
2651 
2652     static bool lastSoftLagValid = true;
2653     isValidSoftLag = mseconds <= baseValues.httpRequestTimeout + baseValues.httpRequestInterval + 200;
2654 
2655     if (isValidSoftLag != lastSoftLagValid)
2656     {
2657         lastSoftLagValid = isValidSoftLag;
2658 
2659         if (!isValidSoftLag)
2660             ui.lastUpdate->setStyleSheet("QDoubleSpinBox {background: " + baseValues.appTheme.lightRed.name() + ";}");
2661         else
2662             ui.lastUpdate->setStyleSheet("");
2663 
2664         calcOrdersTotalValues();
2665         //ui.ordersControls->setEnabled(isValidSoftLag);
2666         //ui.buyButtonBack->setEnabled(isValidSoftLag);
2667         //ui.sellButtonBack->setEnabled(isValidSoftLag);
2668         QString toolTip;
2669 
2670         if (!isValidSoftLag)
2671             toolTip = julyTr("TOOLTIP_API_LAG_TO_HIGH", "API Lag is to High");
2672 
2673         ui.ordersControls->setToolTip(toolTip);
2674         ui.buyButtonBack->setToolTip(toolTip);
2675         ui.sellButtonBack->setToolTip(toolTip);
2676     }
2677 }
2678 
sellTotalBtc_valueChanged(double val)2679 void QtBitcoinTrader::sellTotalBtc_valueChanged(double val)
2680 {
2681     if (val == 0.0)
2682         sellTotalBtc->setStyleSheet("QDoubleSpinBox {background: " + baseValues.appTheme.lightRed.name() + ";}");
2683     else
2684         sellTotalBtc->setStyleSheet("");
2685 
2686     profitBuyThanSellCalc();
2687     profitSellThanBuyCalc();
2688 
2689     if (sellLockBtcToSell)
2690         return;
2691 
2692     sellLockBtcToSell = true;
2693 
2694     sellLockAmountToReceive = true;
2695     sellAmountToReceive->setValue(sellPricePerCoin->value()*val * floatFeeDec);
2696 
2697     sellLockAmountToReceive = false;
2698 
2699     checkValidSellButtons();
2700     sellLockBtcToSell = false;
2701 }
2702 
sellPricePerCoin_valueChanged(double)2703 void QtBitcoinTrader::sellPricePerCoin_valueChanged(double)
2704 {
2705     if (!sellLockPricePerCoin)
2706     {
2707         sellLockPricePerCoin = true;
2708         sellTotalBtc_valueChanged(sellTotalBtc->value());
2709         sellLockPricePerCoin = false;
2710     }
2711 
2712     ui.sellNextMaxBuyPrice->setValue(sellPricePerCoin->value()*floatFeeDec * floatFeeDec -
2713                                      baseValues.currentPair.priceMin);
2714     ui.sellNextMaxBuyStep->setValue(sellPricePerCoin->value() - ui.sellNextMaxBuyPrice->value());
2715     checkValidSellButtons();
2716     ui.buttonSellThenBuyApply->setEnabled(true);
2717     profitBuyThanSellCalc();
2718     profitSellThanBuyCalc();
2719 }
2720 
sellAmountToReceive_valueChanged(double val)2721 void QtBitcoinTrader::sellAmountToReceive_valueChanged(double val)
2722 {
2723     profitBuyThanSellCalc();
2724     profitSellThanBuyCalc();
2725 
2726     if (sellLockAmountToReceive)
2727         return;
2728 
2729     sellLockAmountToReceive = true;
2730 
2731     sellLockBtcToSell = true;
2732     sellLockPricePerCoin = true;
2733 
2734     sellTotalBtc->setValue(val / sellPricePerCoin->value() / floatFeeDec);
2735 
2736     sellLockPricePerCoin = false;
2737     sellLockBtcToSell = false;
2738 
2739     sellLockAmountToReceive = false;
2740     checkValidSellButtons();
2741 }
2742 
sellBitcoinButton()2743 void QtBitcoinTrader::sellBitcoinButton()
2744 {
2745     checkValidSellButtons();
2746 
2747     if (ui.sellBitcoinsButton->isEnabled() == false)
2748         return;
2749 
2750     double sellTotalBtcV = sellTotalBtc->value();
2751     double sellPricePerCoinV = sellPricePerCoin->value();
2752 
2753     if (confirmOpenOrder)
2754     {
2755         QMessageBox msgBox(windowWidget);
2756         msgBox.setIcon(QMessageBox::Question);
2757         msgBox.setWindowTitle(julyTr("MESSAGE_CONFIRM_SELL_TRANSACTION", "Please confirm transaction"));
2758         msgBox.setText(julyTr("MESSAGE_CONFIRM_SELL_TRANSACTION_TEXT",
2759                               "Are you sure to sell %1 at %2 ?<br><br>Note: If total orders amount of your Bitcoins exceeds your balance, %3 will remove this order immediately.").arg(
2760                            baseValues.currentPair.currASign + " " + JulyMath::textFromDouble(sellTotalBtcV,
2761                                    baseValues.currentPair.currADecimals)).arg(baseValues.currentPair.currBSign + " " + JulyMath::textFromDouble(sellPricePerCoinV,
2762                                            baseValues.currentPair.priceDecimals)).arg(baseValues.exchangeName));
2763         msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
2764         msgBox.setDefaultButton(QMessageBox::Yes);
2765         msgBox.setButtonText(QMessageBox::Yes, julyTr("YES", "Yes"));
2766         msgBox.setButtonText(QMessageBox::No, julyTr("NO", "No"));
2767 
2768         if (msgBox.exec() != QMessageBox::Yes)
2769             return;
2770     }
2771 
2772     apiSellSend(baseValues.currentPair.symbolSecond(), sellTotalBtcV, sellPricePerCoinV);
2773 }
2774 
buyTotalSpend_valueChanged(double val)2775 void QtBitcoinTrader::buyTotalSpend_valueChanged(double val)
2776 {
2777     if (val == 0.0)
2778         buyTotalSpend->setStyleSheet("QDoubleSpinBox {background: " + baseValues.appTheme.lightRed.name() + ";}");
2779     else
2780         buyTotalSpend->setStyleSheet("");
2781 
2782     profitBuyThanSellCalc();
2783     profitSellThanBuyCalc();
2784 
2785     buyLockTotalBtc = true;
2786 
2787     if (!buyLockTotalBtcSelf)
2788         buyTotalBtc->setValue(val / buyPricePerCoin->value());
2789 
2790     buyLockTotalBtc = false;
2791 
2792     double valueForResult = val / buyPricePerCoin->value();
2793     valueForResult *= floatFeeDec;
2794     valueForResult = JulyMath::cutDoubleDecimalsCopy(valueForResult, baseValues.currentPair.currADecimals, false);
2795     setSpinValue(ui.buyTotalBtcResult, valueForResult);
2796 
2797     checkValidBuyButtons();
2798 }
2799 
sendIndicatorEvent(const QString & symbol,const QString & name,double val)2800 void QtBitcoinTrader::sendIndicatorEvent(const QString& symbol, const QString& name, double val)
2801 {
2802     emit indicatorEventSignal(symbol, name, val);
2803 }
2804 
buyTotalBtc_valueChanged(double)2805 void QtBitcoinTrader::buyTotalBtc_valueChanged(double)
2806 {
2807     if (buyLockTotalBtc)
2808     {
2809         checkValidBuyButtons();
2810         return;
2811     }
2812 
2813     buyLockTotalBtc = true;
2814     buyLockTotalBtcSelf = true;
2815 
2816     buyTotalSpend->setValue(buyTotalBtc->value() * buyPricePerCoin->value());
2817     buyLockTotalBtcSelf = false;
2818     buyLockTotalBtc = false;
2819     checkValidBuyButtons();
2820 }
2821 
buyPricePerCoin_valueChanged(double)2822 void QtBitcoinTrader::buyPricePerCoin_valueChanged(double)
2823 {
2824     if (!buyLockPricePerCoin)
2825     {
2826         buyLockPricePerCoin = true;
2827         buyTotalSpend_valueChanged(buyTotalSpend->value());
2828         buyLockPricePerCoin = false;
2829     }
2830 
2831     ui.buyNextInSellPrice->setValue(buyPricePerCoin->value() * floatFeeInc * floatFeeInc +
2832                                     baseValues.currentPair.priceMin);
2833     ui.buyNextMinBuyStep->setValue(ui.buyNextInSellPrice->value() - buyPricePerCoin->value());
2834     checkValidBuyButtons();
2835     ui.buttonBuyThenSellApply->setEnabled(true);
2836     profitBuyThanSellCalc();
2837     profitSellThanBuyCalc();
2838 }
2839 
checkValidBuyButtons()2840 void QtBitcoinTrader::checkValidBuyButtons()
2841 {
2842     ui.widgetBuyThenSell->setEnabled(buyTotalBtc->value() >= baseValues.currentPair.tradeVolumeMin &&
2843                                      buyTotalSpend->value() >= baseValues.currentPair.tradeTotalMin);
2844     ui.buyBitcoinsButton->setEnabled(ui.widgetBuyThenSell->isEnabled() &&
2845                                      /*ui.buyTotalSpend->value()<=getAvailableUSD()&&*/buyTotalSpend->value() > 0.0);
2846 }
2847 
checkValidOrdersButtons()2848 void QtBitcoinTrader::checkValidOrdersButtons()
2849 {
2850     ui.ordersCancelAllButton->setEnabled(ordersModel->rowCount());
2851     ui.ordersCancelSelected->setEnabled(ui.ordersTable->selectionModel()->selectedRows().size());
2852 }
2853 
on_buyTotalBtcAllIn_clicked()2854 void QtBitcoinTrader::on_buyTotalBtcAllIn_clicked()
2855 {
2856     buyTotalBtc->setValue(getAvailableUSDtoBTC(buyPricePerCoin->value()));
2857 }
2858 
on_buyTotalBtcHalfIn_clicked()2859 void QtBitcoinTrader::on_buyTotalBtcHalfIn_clicked()
2860 {
2861     buyTotalBtc->setValue(getAvailableUSDtoBTC(buyPricePerCoin->value()) / 2.0);
2862 }
2863 
on_sellPriceAsMarketBid_clicked()2864 void QtBitcoinTrader::on_sellPriceAsMarketBid_clicked()
2865 {
2866     sellPricePerCoin->setValue(ui.marketBid->value());
2867 }
2868 
on_buyPriceAsMarketBid_clicked()2869 void QtBitcoinTrader::on_buyPriceAsMarketBid_clicked()
2870 {
2871     buyPricePerCoin->setValue(ui.marketBid->value());
2872 }
2873 
on_sellPriceAsMarketAsk_clicked()2874 void QtBitcoinTrader::on_sellPriceAsMarketAsk_clicked()
2875 {
2876     sellPricePerCoin->setValue(ui.marketAsk->value());
2877 }
2878 
on_buyPriceAsMarketAsk_clicked()2879 void QtBitcoinTrader::on_buyPriceAsMarketAsk_clicked()
2880 {
2881     buyPricePerCoin->setValue(ui.marketAsk->value());
2882 }
2883 
on_buyPriceAsMarketLastPrice_clicked()2884 void QtBitcoinTrader::on_buyPriceAsMarketLastPrice_clicked()
2885 {
2886     buyPricePerCoin->setValue(ui.marketLast->value());
2887 }
2888 
hasWorkingRules()2889 bool QtBitcoinTrader::hasWorkingRules()
2890 {
2891     for (RuleWidget* group : ui.tabRules->findChildren<RuleWidget*>())
2892     {
2893         if (group)
2894         {
2895             if (group->haveWorkingRules())
2896                 return true;
2897         }
2898     }
2899 
2900     for (ScriptWidget* group : ui.tabRules->findChildren<ScriptWidget*>())
2901     {
2902         if (group)
2903         {
2904             if (group->isRunning())
2905                 return true;
2906         }
2907     }
2908 
2909     return false;
2910 }
2911 
buyBitcoinsButton()2912 void QtBitcoinTrader::buyBitcoinsButton()
2913 {
2914     checkValidBuyButtons();
2915 
2916     if (ui.buyBitcoinsButton->isEnabled() == false)
2917         return;
2918 
2919     double btcToBuy = 0.0;
2920     double priceToBuy = buyPricePerCoin->value();
2921 
2922     if (currentExchange->buySellAmountExcludedFee && floatFee != 0.0)
2923         btcToBuy = ui.buyTotalBtcResult->value();
2924     else
2925         btcToBuy = buyTotalBtc->value();
2926 
2927     //double amountWithoutFee=getAvailableUSD()/priceToBuy;
2928     //amountWithoutFee=JulyMath::cutDoubleDecimalsCopy(amountWithoutFee,baseValues.currentPair.currADecimals,false);
2929 
2930     if (confirmOpenOrder)
2931     {
2932         QMessageBox msgBox(windowWidget);
2933         msgBox.setIcon(QMessageBox::Question);
2934         msgBox.setWindowTitle(julyTr("MESSAGE_CONFIRM_BUY_TRANSACTION", "Please confirm new order"));
2935         msgBox.setText(julyTr("MESSAGE_CONFIRM_BUY_TRANSACTION_TEXT",
2936                               "Are you sure to buy %1 at %2 ?<br><br>Note: If total orders amount of your funds exceeds your balance, %3 will remove this order immediately.").arg(
2937                            baseValues.currentPair.currASign + " " + JulyMath::textFromDouble(btcToBuy,
2938                                    baseValues.currentPair.currADecimals)).arg(baseValues.currentPair.currBSign + " " + JulyMath::textFromDouble(priceToBuy,
2939                                            baseValues.currentPair.priceDecimals)).arg(baseValues.exchangeName));
2940         msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
2941         msgBox.setDefaultButton(QMessageBox::Yes);
2942         msgBox.setButtonText(QMessageBox::Yes, julyTr("YES", "Yes"));
2943         msgBox.setButtonText(QMessageBox::No, julyTr("NO", "No"));
2944 
2945         if (msgBox.exec() != QMessageBox::Yes)
2946             return;
2947     }
2948 
2949     apiBuySend(baseValues.currentPair.symbolSecond(), btcToBuy, priceToBuy);
2950 }
2951 
playWav(const QString & wav,bool noBlink)2952 void QtBitcoinTrader::playWav(const QString& wav, bool noBlink)
2953 {
2954     Platform::playSound(wav);
2955 
2956     if (!noBlink)
2957         blinkWindow();
2958 }
2959 
beep(bool noBlink)2960 void QtBitcoinTrader::beep(bool noBlink)
2961 {
2962     QString fileName = appDataDir + "/Sound/Beep.wav";
2963 
2964     if (!QFile::exists(fileName))
2965     {
2966         QDir().mkpath(QFileInfo(fileName).dir().path());
2967         QFile::copy(":/Resources/Sound/800.wav", fileName);
2968     }
2969 
2970     if (!QFile::exists(fileName))
2971         QApplication::beep();
2972     else
2973         playWav(fileName, noBlink);
2974 
2975     if (!noBlink)
2976         blinkWindow();
2977 }
2978 
blinkWindow()2979 void QtBitcoinTrader::blinkWindow()
2980 {
2981 #ifdef  Q_OS_WIN
2982 
2983     if (!isActiveWindow())
2984     {
2985         FLASHWINFO flashInfo;
2986         flashInfo.cbSize = sizeof(FLASHWINFO);
2987         flashInfo.hwnd = reinterpret_cast<HWND>(windowWidget->winId());
2988         flashInfo.dwFlags = FLASHW_ALL;
2989         flashInfo.uCount = 10;
2990         flashInfo.dwTimeout = 400;
2991         ::FlashWindowEx(&flashInfo);
2992     }
2993 
2994 #endif//ToDo: make this feature works on Mac OS X
2995 }
2996 
ruleTotalToBuyValueChanged()2997 void QtBitcoinTrader::ruleTotalToBuyValueChanged()
2998 {
2999     if (ui.marketLast->value() == 0.0)
3000         return;
3001 
3002     double newValue = ui.accountUSD->value() / ui.marketLast->value() * floatFeeDec;
3003 
3004     if (!qFuzzyCompare(newValue, ui.ruleTotalToBuyValue->value()))
3005     {
3006         setSpinValueP(ui.ruleTotalToBuyValue, newValue);
3007     }
3008 }
3009 
ruleAmountToReceiveValueChanged()3010 void QtBitcoinTrader::ruleAmountToReceiveValueChanged()
3011 {
3012     if (ui.marketLast->value() == 0.0)
3013         return;
3014 
3015     double newValue = ui.accountBTC->value() * ui.marketLast->value() * floatFeeDec;
3016 
3017     if (!qFuzzyCompare(newValue, ui.ruleAmountToReceiveValue->value()))
3018     {
3019         setSpinValueP(ui.ruleAmountToReceiveValue, newValue);
3020     }
3021 }
3022 
ruleTotalToBuyBSValueChanged()3023 void QtBitcoinTrader::ruleTotalToBuyBSValueChanged()
3024 {
3025     if (ui.marketBid->value() == 0.0)
3026         return;
3027 
3028     double newValue = ui.accountUSD->value() / ui.marketBid->value() * floatFeeDec;
3029 
3030     if (!qFuzzyCompare(newValue, ui.ruleTotalToBuyValue->value()))
3031     {
3032         setSpinValueP(ui.ruleTotalToBuyBSValue, newValue);
3033     }
3034 }
3035 
ruleAmountToReceiveBSValueChanged()3036 void QtBitcoinTrader::ruleAmountToReceiveBSValueChanged()
3037 {
3038     if (ui.marketAsk->value() == 0.0)
3039         return;
3040 
3041     double newValue = ui.accountBTC->value() * ui.marketAsk->value() * floatFeeDec;
3042 
3043     if (!qFuzzyCompare(newValue, ui.ruleAmountToReceiveBSValue->value()))
3044     {
3045         setSpinValueP(ui.ruleAmountToReceiveBSValue, newValue);
3046     }
3047 }
3048 
on_accountUSD_valueChanged(double)3049 void QtBitcoinTrader::on_accountUSD_valueChanged(double)
3050 {
3051     ruleTotalToBuyValueChanged();
3052     ruleTotalToBuyBSValueChanged();
3053 }
3054 
on_accountBTC_valueChanged(double)3055 void QtBitcoinTrader::on_accountBTC_valueChanged(double)
3056 {
3057     ruleAmountToReceiveValueChanged();
3058     ruleAmountToReceiveBSValueChanged();
3059 }
3060 
on_marketBid_valueChanged(double val)3061 void QtBitcoinTrader::on_marketBid_valueChanged(double val)
3062 {
3063     ruleTotalToBuyBSValueChanged();
3064     meridianPrice = (val + ui.marketAsk->value()) / 2;
3065 
3066     if (val > 0.000000001)
3067     {
3068         emit addBound(val, false);
3069 
3070         double ask = ui.marketAsk->value();
3071 
3072         if (ask > 0.000000001)
3073         {
3074             val = (val + ask) / 2;
3075 
3076             static double lastValue = val;
3077             static int priceDirection = 0;
3078             int lastPriceDirection = priceDirection;
3079 
3080             if (lastValue < val)
3081                 priceDirection = 1;
3082             else if (lastValue > val)
3083                 priceDirection = -1;
3084             else
3085                 priceDirection = lastPriceDirection;
3086 
3087             lastValue = val;
3088 
3089             static QString directionChar("-");
3090 
3091             switch (priceDirection)
3092             {
3093             case -1:
3094                 directionChar = downArrowNoUtfStr;
3095                 break;
3096 
3097             case 1:
3098                 directionChar = upArrowNoUtfStr;
3099                 break;
3100 
3101             default:
3102                 break;
3103             }
3104 
3105             static QString titleText;
3106             titleText = baseValues.currentPair.currBSign + " " + JulyMath::textFromDouble(val) + " " + directionChar + " " + windowTitleP;
3107 
3108             if (windowWidget->isVisible())
3109                 windowWidget->setWindowTitle(titleText);
3110 
3111             if (trayIcon && trayIcon->isVisible())
3112                 trayIcon->setToolTip(titleText);
3113         }
3114     }
3115 }
3116 
on_marketAsk_valueChanged(double val)3117 void QtBitcoinTrader::on_marketAsk_valueChanged(double val)
3118 {
3119     ruleAmountToReceiveBSValueChanged();
3120     meridianPrice = (val + ui.marketBid->value()) / 2;
3121 
3122     if (val > 0.000000001)
3123     {
3124         emit addBound(val, true);
3125 
3126         double bid = ui.marketBid->value();
3127 
3128         if (bid > 0.000000001)
3129         {
3130             val = (val + bid) / 2;
3131 
3132             static double lastValue = val;
3133             static int priceDirection = 0;
3134             int lastPriceDirection = priceDirection;
3135 
3136             if (lastValue < val)
3137                 priceDirection = 1;
3138             else if (lastValue > val)
3139                 priceDirection = -1;
3140             else
3141                 priceDirection = lastPriceDirection;
3142 
3143             lastValue = val;
3144 
3145             static QString directionChar("-");
3146 
3147             switch (priceDirection)
3148             {
3149             case -1:
3150                 directionChar = downArrowNoUtfStr;
3151                 break;
3152 
3153             case 1:
3154                 directionChar = upArrowNoUtfStr;
3155                 break;
3156 
3157             default:
3158                 break;
3159             }
3160 
3161             static QString titleText;
3162             titleText = baseValues.currentPair.currBSign + " " + JulyMath::textFromDouble(val) + " " + directionChar + " " + windowTitleP;
3163 
3164             if (windowWidget->isVisible())
3165                 windowWidget->setWindowTitle(titleText);
3166 
3167             if (trayIcon && trayIcon->isVisible())
3168                 trayIcon->setToolTip(titleText);
3169         }
3170     }
3171 }
3172 
on_marketLast_valueChanged(double)3173 void QtBitcoinTrader::on_marketLast_valueChanged(double)
3174 {
3175     ruleTotalToBuyValueChanged();
3176     ruleAmountToReceiveValueChanged();
3177 }
3178 
historyDoubleClicked(QModelIndex index)3179 void QtBitcoinTrader::historyDoubleClicked(QModelIndex index)
3180 {
3181     repeatOrderFromValues(0, historyModel->getRowPrice(index.row()), historyModel->getRowVolume(index.row()), false);
3182 }
3183 
repeatOrderFromValues(int type,double itemPrice,double itemVolume,bool availableOnly)3184 void QtBitcoinTrader::repeatOrderFromValues(int type, double itemPrice, double itemVolume, bool availableOnly)
3185 {
3186     if (itemPrice == 0.0)
3187         return;
3188 
3189     if (QApplication::keyboardModifiers() != Qt::NoModifier)
3190         availableOnly = !availableOnly;
3191 
3192     if (type == 1 || type == 0)
3193     {
3194         buyPricePerCoin->setValue(itemPrice);
3195 
3196         if (availableOnly)
3197             itemVolume = qMin(itemVolume, getAvailableUSD() / itemPrice);
3198 
3199         buyTotalBtc->setValue(itemVolume);
3200     }
3201 
3202     if (type == -1 || type == 0)
3203     {
3204         sellPricePerCoin->setValue(itemPrice);
3205 
3206         if (availableOnly)
3207             itemVolume = qMin(getAvailableBTC(), itemVolume);
3208 
3209         sellTotalBtc->setValue(itemVolume);
3210     }
3211 }
3212 
repeatOrderFromTrades(int type,int row)3213 void QtBitcoinTrader::repeatOrderFromTrades(int type, int row)
3214 {
3215     repeatOrderFromValues(type, tradesModel->getRowPrice(row), tradesModel->getRowVolume(row));
3216 }
3217 
tradesDoubleClicked(QModelIndex index)3218 void QtBitcoinTrader::tradesDoubleClicked(QModelIndex index)
3219 {
3220     repeatOrderFromTrades(0, index.row());
3221 }
3222 
depthSelectOrder(QModelIndex index,bool isSell,int type)3223 void QtBitcoinTrader::depthSelectOrder(QModelIndex index, bool isSell, int type)
3224 {
3225     double itemPrice = 0.0;
3226     double itemVolume = 0.0;
3227     int row = index.row();
3228     int col = index.column();
3229 
3230     if (swapedDepth)
3231         isSell = !isSell;
3232 
3233     if (isSell)
3234     {
3235         if (!swapedDepth)
3236             col = depthAsksModel->columnCount() - col - 1;
3237 
3238         if (row < 0 || depthAsksModel->rowCount() <= row)
3239             return;
3240 
3241         itemPrice = depthAsksModel->rowPrice(row);
3242 
3243         if (col == 0 || col == 1)
3244             itemVolume = depthAsksModel->rowVolume(row);
3245         else
3246             itemVolume = depthAsksModel->rowSize(row);
3247     }
3248     else
3249     {
3250         if (swapedDepth)
3251             col = depthBidsModel->columnCount() - col - 1;
3252 
3253         if (row < 0 || depthBidsModel->rowCount() <= row)
3254             return;
3255 
3256         itemPrice = depthBidsModel->rowPrice(row);
3257 
3258         if (col == 0 || col == 1)
3259             itemVolume = depthBidsModel->rowVolume(row);
3260         else
3261             itemVolume = depthBidsModel->rowSize(row);
3262     }
3263 
3264     repeatOrderFromValues(type, itemPrice, itemVolume * floatFeeDec);
3265 }
3266 
depthSelectSellOrder(QModelIndex index)3267 void QtBitcoinTrader::depthSelectSellOrder(QModelIndex index)
3268 {
3269     depthSelectOrder(index, true);
3270 }
3271 
depthSelectBuyOrder(QModelIndex index)3272 void QtBitcoinTrader::depthSelectBuyOrder(QModelIndex index)
3273 {
3274     depthSelectOrder(index, false);
3275 }
3276 
translateTab(QWidget * tab)3277 void QtBitcoinTrader::translateTab(QWidget* tab)
3278 {
3279     QDockWidget* dock = static_cast<QDockWidget*>(tab->parentWidget());
3280 
3281     if (dock)
3282     {
3283         QString key = tab->accessibleName();
3284         QString defaultValue = dock->windowTitle();
3285         QString s = julyTr(key, defaultValue);
3286 
3287         if (dock->isFloating())
3288         {
3289             s += " [" + profileName + "]";
3290         }
3291 
3292         tab->parentWidget()->setWindowTitle(s);
3293 
3294         if (dock->isFloating())
3295         {
3296             julyTranslator.translateUi(tab);
3297             fixAllChildButtonsAndLabels(tab);
3298         }
3299     }
3300 }
3301 
lockLogo(bool lock)3302 void QtBitcoinTrader::lockLogo(bool lock)
3303 {
3304     if (lock)
3305     {
3306         dockLogo->setFeatures(QDockWidget::NoDockWidgetFeatures);
3307     }
3308     else
3309     {
3310         dockLogo->setFeatures(QDockWidget::DockWidgetMovable);
3311     }
3312 
3313     dockLogo->setAllowedAreas(Qt::AllDockWidgetAreas);
3314 }
3315 
initConfigMenu()3316 void QtBitcoinTrader::initConfigMenu()
3317 {
3318     menuConfig->clear();
3319     menuConfig->addAction(actionConfigManager);
3320     menuConfig->addSeparator();
3321 
3322     for (const QString& name : ::config->getConfigNames())
3323     {
3324         QAction* action = menuConfig->addAction(name);
3325         connect(action, &QAction::triggered, this, &QtBitcoinTrader::onMenuConfigTriggered);
3326     }
3327 }
3328 
languageChanged()3329 void QtBitcoinTrader::languageChanged()
3330 {
3331     if (!constructorFinished)
3332         return;
3333 
3334     julyTranslator.translateUi(this);
3335     julyTranslator.translateUi(networkMenu);
3336     baseValues.dateTimeFormat = julyTr("DATETIME_FORMAT", baseValues.dateTimeFormat);
3337     baseValues.timeFormat = julyTr("TIME_FORMAT", baseValues.timeFormat);
3338     QStringList ordersLabels;
3339     ordersLabels << julyTr("ORDERS_COUNTER", "#") << julyTr("ORDERS_DATE", "Date") << julyTr("ORDERS_TYPE",
3340                  "Type") << julyTr("ORDERS_STATUS", "Status") << julyTr("ORDERS_AMOUNT", "Amount") << julyTr("ORDERS_PRICE",
3341                          "Price") << julyTr("ORDERS_TOTAL", "Total");
3342     ordersModel->setHorizontalHeaderLabels(ordersLabels);
3343 
3344     QStringList tradesLabels;
3345     tradesLabels << "" << julyTr("ORDERS_DATE", "Date") << julyTr("ORDERS_AMOUNT", "Amount") << julyTr("ORDERS_TYPE",
3346                  "Type") << julyTr("ORDERS_PRICE", "Price") << julyTr("ORDERS_TOTAL", "Total") << "";
3347     historyModel->setHorizontalHeaderLabels(tradesLabels);
3348     tradesLabels.insert(4, upArrowStr + downArrowStr);
3349     tradesModel->setHorizontalHeaderLabels(tradesLabels);
3350 
3351     QStringList depthHeaderLabels;
3352     depthHeaderLabels << julyTr("ORDERS_PRICE", "Price") << julyTr("ORDERS_AMOUNT",
3353                       "Amount") << upArrowStr + downArrowStr << julyTr("ORDERS_TOTAL", "Total") << "";
3354     depthBidsModel->setHorizontalHeaderLabels(depthHeaderLabels);
3355     depthAsksModel->setHorizontalHeaderLabels(depthHeaderLabels);
3356 
3357     translateTab(ui.tabOrdersLog);
3358     translateTab(ui.tabRules);
3359     translateTab(ui.tabLastTrades);
3360     translateTab(ui.tabDepth);
3361     translateTab(ui.tabCharts);
3362     translateTab(ui.tabNews);
3363     //translateTab(ui.tabChat);
3364 
3365     ui.widgetAccount->parentWidget()->setWindowTitle(julyTr("ACCOUNT_GROUPBOX", "%1 Account").arg(baseValues.exchangeName));
3366 
3367     QString curCurrencyName = IniEngine::getCurrencyInfo(baseValues.currentPair.currAStr).name;
3368     ui.widgetBuy->parentWidget()->setWindowTitle(julyTr("GROUPBOX_BUY", "Buy %1").arg(curCurrencyName));
3369     ui.widgetSell->parentWidget()->setWindowTitle(julyTr("GROUPBOX_SELL", "Sell %1").arg(curCurrencyName));
3370 
3371     for (QToolButton* toolButton : findChildren<QToolButton*>())
3372         if (toolButton->accessibleDescription() == "TOGGLE_SOUND")
3373             toolButton->setToolTip(julyTr("TOGGLE_SOUND", "Toggle sound notification on value change"));
3374 
3375     if (ui.comboBoxGroupByPrice->count())
3376     {
3377         ui.comboBoxGroupByPrice->setItemText(0, julyTr("DONT_GROUP", "None"));
3378         fixWidthComboBoxGroupByPrice();
3379     }
3380 
3381     copyTableValuesMenu.actions().at(0)->setText(julyTr("COPY_ROW", "Copy selected Rows"));
3382 
3383     copyTableValuesMenu.actions().at(2)->setText(julyTr("COPY_DATE", "Copy Date"));
3384     copyTableValuesMenu.actions().at(3)->setText(julyTr("COPY_AMOUNT", "Copy Amount"));
3385     copyTableValuesMenu.actions().at(4)->setText(julyTr("COPY_PRICE", "Copy Price"));
3386     copyTableValuesMenu.actions().at(5)->setText(julyTr("COPY_TOTAL", "Copy Total"));
3387 
3388     copyTableValuesMenu.actions().at(7)->setText(julyTr("REPEAT_BUYSELL_ORDER", "Repeat Buy and Sell order"));
3389     copyTableValuesMenu.actions().at(8)->setText(julyTr("REPEAT_BUY_ORDER", "Repeat Buy order"));
3390     copyTableValuesMenu.actions().at(9)->setText(julyTr("REPEAT_SELL_ORDER", "Repeat Sell order"));
3391 
3392     copyTableValuesMenu.actions().at(11)->setText(julyTr("CANCEL_ORDER", "Cancel selected Orders"));
3393     copyTableValuesMenu.actions().at(12)->setText(julyTranslator.translateCheckBox("TR00075", "Cancel All Orders"));
3394 
3395     ui.tradesBidsPrecent->setToolTip(julyTr("10_MIN_BIDS_VOLUME", "(10 min Bids Volume)/(10 min Asks Volume)*100"));
3396 
3397     for (RuleWidget* currentGroup : ui.tabRules->findChildren<RuleWidget*>())
3398         if (currentGroup)
3399             currentGroup->languageChanged();
3400 
3401     for (ScriptWidget* currentGroup : ui.tabRules->findChildren<ScriptWidget*>())
3402         if (currentGroup)
3403             currentGroup->languageChanged();
3404 
3405     actionLockDocks->setText(julyTr("LOCK_DOCKS", "&Lock Docks"));
3406     actionExit->setText(julyTr("EXIT", "E&xit"));
3407     actionUpdate->setText(julyTr("UPDATE", "Check for &updates"));
3408     actionSendBugReport->setText(julyTr("SEND_BUG_REPORT", "&Send bug report"));
3409     actionAbout->setText(julyTr("ABOUT", "&About Qt Bitcoin Trader"));
3410     actionAboutQt->setText(julyTr("ABOUT_QT", "About &Qt"));
3411 #ifndef Q_OS_MAC
3412 
3413     if (!baseValues_->portableMode && actionUninstall)
3414         actionUninstall->setText(julyTr("UNINSTALL", "&Uninstall"));
3415 
3416 #endif
3417     actionConfigManager->setText(julyTr("CONFIG_MANAGER", "&Save..."));
3418     actionSettings->setText(julyTr("CONFIG_SETTINGS", "Se&ttings"));
3419     actionDebug->setText(julyTr("CONFIG_DEBUG", "&Debug"));
3420     menuFile->setTitle("&QtBitcoinTrader");
3421     menuView->setTitle(julyTr("MENU_VIEW", "&View"));
3422     menuConfig->setTitle(julyTr("MENU_CONFIG", "&Interface"));
3423     menuHelp->setTitle(julyTr("MENU_HELP", "&Help"));
3424 
3425     if (configDialog)
3426         configDialog->setWindowTitle(julyTr("CONFIG_MANAGER_TITLE", "Config Manager"));
3427 
3428     ::config->translateDefaultNames();
3429 
3430     if (configDialog)
3431         ::config->onChanged();
3432 
3433     adjustDockMinSize(ui.widgetTotalAtLast);
3434     adjustDockMinSize(ui.widgetTotalAtBuySell);
3435 
3436     fixAllChildButtonsAndLabels(this);
3437 }
3438 
buttonNewWindow()3439 void QtBitcoinTrader::buttonNewWindow()
3440 {
3441     QProcess::startDetached(QApplication::applicationFilePath(), QStringList());
3442 }
3443 
on_rulesTabs_tabCloseRequested(int tab)3444 void QtBitcoinTrader::on_rulesTabs_tabCloseRequested(int tab)
3445 {
3446     ScriptWidget* currentScript = dynamic_cast<ScriptWidget*>(ui.rulesTabs->widget(tab));
3447     RuleWidget* currentGroup = dynamic_cast<RuleWidget*>(ui.rulesTabs->widget(tab));
3448 
3449     if (currentGroup == nullptr && currentScript == nullptr)
3450         return;
3451 
3452     if (currentScript || currentGroup->haveAnyRules())
3453     {
3454         QMessageBox msgBox(windowWidget);
3455         msgBox.setIcon(QMessageBox::Question);
3456         msgBox.setWindowTitle(julyTr("RULES_CONFIRM_DELETION", "Please confirm rules group deletion"));
3457         msgBox.setText(julyTr("RULES_ARE_YOU_SURE_TO_DELETE_GROUP",
3458                               "Are you sure to delete rules group %1?").arg(ui.rulesTabs->widget(tab)->windowTitle()));
3459         msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
3460         msgBox.setDefaultButton(QMessageBox::Yes);
3461         msgBox.setButtonText(QMessageBox::Yes, julyTr("YES", "Yes"));
3462         msgBox.setButtonText(QMessageBox::No, julyTr("NO", "No"));
3463 
3464         if (msgBox.exec() != QMessageBox::Yes)
3465             return;
3466     }
3467 
3468     //bool removed = false;
3469     //Q_UNUSED(removed);
3470 
3471     if (currentGroup)
3472     {
3473         //removed = currentGroup->removeGroup();
3474         currentGroup->removeGroup();
3475     }
3476     else if (currentScript)
3477     {
3478         //removed = currentScript->removeGroup();
3479         currentScript->removeGroup();
3480     }
3481 
3482     delete ui.rulesTabs->widget(tab);
3483 
3484     ui.rulesTabs->setVisible(ui.rulesTabs->count());
3485     ui.rulesNoMessage->setVisible(!ui.rulesTabs->isVisible());
3486 }
3487 
on_widgetStaysOnTop_toggled(bool on)3488 void QtBitcoinTrader::on_widgetStaysOnTop_toggled(bool on)
3489 {
3490     bool visible = isVisible();
3491 
3492     if (on)
3493         windowWidget->setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);
3494     else
3495         windowWidget->setWindowFlags(Qt::Window);
3496 
3497     dockHost->setStaysOnTop(on);
3498 
3499     if (visible)
3500         windowWidget->show();
3501 }
3502 
depthFirstOrder(const QString & symbol,double price,double volume,bool isAsk)3503 void QtBitcoinTrader::depthFirstOrder(const QString& symbol, double price, double volume, bool isAsk)
3504 {
3505     if (symbol != baseValues.currentPair.symbol)
3506         return;
3507 
3508     waitingDepthLag = false;
3509 
3510     if (price == 0.0 || ui.comboBoxGroupByPrice->currentIndex() == 0)
3511         return;
3512 
3513     if (isAsk)
3514         depthAsksModel->depthFirstOrder(price, volume);
3515     else
3516         depthBidsModel->depthFirstOrder(price, volume);
3517 }
3518 
fixDepthBidsTable()3519 void QtBitcoinTrader::fixDepthBidsTable()
3520 {
3521     ui.depthAsksTable->resizeColumnToContents(1);
3522     ui.depthAsksTable->resizeColumnToContents(3);
3523     ui.depthAsksTable->resizeColumnToContents(4);
3524 
3525     ui.depthBidsTable->resizeColumnToContents(0);
3526     ui.depthBidsTable->resizeColumnToContents(1);
3527     ui.depthBidsTable->resizeColumnToContents(3);
3528 
3529     int asksWidth = ui.depthAsksTable->columnWidth(1) + ui.depthAsksTable->columnWidth(2) +
3530                     ui.depthAsksTable->columnWidth(3) + ui.depthAsksTable->columnWidth(4) + 16;
3531     int bidsWidth = ui.depthBidsTable->columnWidth(0) + ui.depthBidsTable->columnWidth(1) +
3532                     ui.depthBidsTable->columnWidth(2) + ui.depthBidsTable->columnWidth(3) + 16;
3533 
3534     ui.depthAsksTable->setMinimumWidth(asksWidth);
3535     ui.depthAsksTable->horizontalScrollBar()->setValue(ui.depthAsksTable->horizontalScrollBar()->maximum());
3536     ui.depthBidsTable->setMinimumWidth(bidsWidth);
3537     ui.depthBidsTable->horizontalScrollBar()->setValue(0);
3538 
3539     ui.tabDepth->setMinimumWidth(qMax(ui.gridLayout_31->minimumSize().width(), asksWidth + bidsWidth + 24));
3540 }
3541 
depthSubmitOrders(const QString & symbol,QList<DepthItem> * asks,QList<DepthItem> * bids)3542 void QtBitcoinTrader::depthSubmitOrders(const QString& symbol, QList<DepthItem>* asks, QList<DepthItem>* bids)
3543 {
3544     if (symbol != baseValues.currentPair.symbol)
3545         return;
3546 
3547     waitingDepthLag = false;
3548     int currentAsksScroll = ui.depthAsksTable->verticalScrollBar()->value();
3549     int currentBidsScroll = ui.depthBidsTable->verticalScrollBar()->value();
3550     depthAsksModel->depthUpdateOrders(asks);
3551     depthBidsModel->depthUpdateOrders(bids);
3552     depthVisibilityChanged(dockDepth->isVisible());
3553     ui.depthAsksTable->verticalScrollBar()->setValue(qMin(currentAsksScroll,
3554             ui.depthAsksTable->verticalScrollBar()->maximum()));
3555     ui.depthBidsTable->verticalScrollBar()->setValue(qMin(currentBidsScroll,
3556             ui.depthBidsTable->verticalScrollBar()->maximum()));
3557 
3558     fixDepthBidsTable();
3559 }
3560 
saveAppState()3561 void QtBitcoinTrader::saveAppState()
3562 {
3563     for (RuleWidget* currentGroup : ui.tabRules->findChildren<RuleWidget*>())
3564         if (currentGroup)
3565             currentGroup->saveRulesData();
3566 
3567     for (ScriptWidget* currentGroup : ui.tabRules->findChildren<ScriptWidget*>())
3568         if (currentGroup)
3569             currentGroup->saveScriptToFile();
3570 
3571     if (trayIcon)
3572         trayIcon->hide();
3573 
3574     //saveRulesData();////
3575 
3576     iniSettings->setValue("UI/ConfirmOpenOrder", confirmOpenOrder);
3577 
3578     iniSettings->setValue("UI/CloseToTray", closeToTray);
3579 
3580     iniSettings->setValue("UI/WindowOnTop", ui.widgetStaysOnTop->isChecked());
3581 
3582     iniSettings->setValue("UI/DepthAutoResizeColumns", ui.depthAutoResize->isChecked());
3583 
3584     if (!ui.depthAutoResize->isChecked())
3585     {
3586         iniSettings->setValue("UI/DepthColumnAsksSizeWidth", ui.depthAsksTable->columnWidth(1));
3587         iniSettings->setValue("UI/DepthColumnAsksVolumeWidth", ui.depthAsksTable->columnWidth(2));
3588         iniSettings->setValue("UI/DepthColumnAsksPriceWidth", ui.depthAsksTable->columnWidth(3));
3589 
3590         iniSettings->setValue("UI/DepthColumnBidsPriceWidth", ui.depthBidsTable->columnWidth(0));
3591         iniSettings->setValue("UI/DepthColumnBidsVolumeWidth", ui.depthBidsTable->columnWidth(1));
3592         iniSettings->setValue("UI/DepthColumnBidsSizeWidth", ui.depthBidsTable->columnWidth(2));
3593     }
3594 
3595     iniSettings->setValue("UI/FeeCalcSingleInstance", feeCalculatorSingleInstance);
3596 
3597     iniSettings->setValue("UI/LockedDocks", lockedDocks);
3598 
3599     iniSettings->sync();
3600 }
3601 
on_depthComboBoxLimitRows_currentIndexChanged(int val)3602 void QtBitcoinTrader::on_depthComboBoxLimitRows_currentIndexChanged(int val)
3603 {
3604     baseValues.depthCountLimit = ui.depthComboBoxLimitRows->itemData(val, Qt::UserRole).toInt();
3605     baseValues.depthCountLimitStr = QByteArray::number(baseValues.depthCountLimit);
3606     iniSettings->setValue("UI/DepthCountLimit", baseValues.depthCountLimit);
3607     iniSettings->sync();
3608     clearDepth();
3609 }
3610 
on_comboBoxGroupByPrice_currentIndexChanged(int val)3611 void QtBitcoinTrader::on_comboBoxGroupByPrice_currentIndexChanged(int val)
3612 {
3613     baseValues.groupPriceValue = ui.comboBoxGroupByPrice->itemData(val, Qt::UserRole).toDouble();
3614     iniSettings->setValue("UI/DepthGroupByPrice", baseValues.groupPriceValue);
3615     iniSettings->sync();
3616     clearDepth();
3617 }
3618 
fixWidthComboBoxGroupByPrice()3619 void QtBitcoinTrader::fixWidthComboBoxGroupByPrice()
3620 {
3621     if (ui.comboBoxGroupByPrice->count() == 0)
3622         return;
3623 
3624     int width = textFontWidth(ui.comboBoxGroupByPrice->itemText(0));
3625 
3626     if (ui.comboBoxGroupByPrice->count() > 1)
3627         width = qMax(textFontWidth(ui.comboBoxGroupByPrice->itemText(ui.comboBoxGroupByPrice->count() - 1)), width);
3628 
3629     if (ui.comboBoxGroupByPrice->count() > 2)
3630         width = qMax(textFontWidth(ui.comboBoxGroupByPrice->itemText(1)), width);
3631 
3632     int bonus = static_cast<int>(ui.comboBoxGroupByPrice->height() * 1.1);
3633     ui.comboBoxGroupByPrice->setMinimumWidth(width + bonus);
3634 }
3635 
on_depthAutoResize_toggled(bool on)3636 void QtBitcoinTrader::on_depthAutoResize_toggled(bool on)
3637 {
3638     if (on)
3639     {
3640         ui.depthAsksTable->horizontalHeader()->showSection(0);
3641         ui.depthBidsTable->horizontalHeader()->showSection(4);
3642 
3643         setColumnResizeMode(ui.depthAsksTable, 0, QHeaderView::Stretch);
3644         setColumnResizeMode(ui.depthAsksTable, 1, QHeaderView::ResizeToContents);
3645         setColumnResizeMode(ui.depthAsksTable, 2, QHeaderView::ResizeToContents);
3646         setColumnResizeMode(ui.depthAsksTable, 3, QHeaderView::ResizeToContents);
3647         setColumnResizeMode(ui.depthAsksTable, 4, QHeaderView::ResizeToContents);
3648 
3649         setColumnResizeMode(ui.depthBidsTable, 0, QHeaderView::ResizeToContents);
3650         setColumnResizeMode(ui.depthBidsTable, 1, QHeaderView::ResizeToContents);
3651         setColumnResizeMode(ui.depthBidsTable, 2, QHeaderView::ResizeToContents);
3652         setColumnResizeMode(ui.depthBidsTable, 3, QHeaderView::ResizeToContents);
3653         setColumnResizeMode(ui.depthBidsTable, 4, QHeaderView::Stretch);
3654 
3655         QCoreApplication::processEvents();
3656         ui.depthAsksTable->horizontalScrollBar()->setValue(ui.depthAsksTable->horizontalScrollBar()->maximum());
3657         ui.depthBidsTable->horizontalScrollBar()->setValue(0);
3658     }
3659     else
3660     {
3661         setColumnResizeMode(ui.depthAsksTable, QHeaderView::Interactive);
3662         setColumnResizeMode(ui.depthBidsTable, QHeaderView::Interactive);
3663         ui.depthAsksTable->horizontalHeader()->hideSection(0);
3664         ui.depthBidsTable->horizontalHeader()->hideSection(4);
3665     }
3666 }
3667 
getAvailableBTC()3668 double QtBitcoinTrader::getAvailableBTC()
3669 {
3670     if (currentExchange->balanceDisplayAvailableAmount)
3671         return JulyMath::cutDoubleDecimalsCopy(ui.accountBTC->value(), baseValues.currentPair.currADecimals, false);
3672 
3673     return JulyMath::cutDoubleDecimalsCopy(ui.accountBTC->value() - ui.ordersTotalBTC->value(), baseValues.currentPair.currADecimals, false);
3674 }
3675 
getAvailableUSD()3676 double QtBitcoinTrader::getAvailableUSD()
3677 {
3678     if (floatFee == 0.0)
3679         return ui.accountUSD->value();
3680 
3681     double amountToReturn = 0.0;
3682 
3683     if (currentExchange->balanceDisplayAvailableAmount)
3684         amountToReturn = ui.accountUSD->value();
3685     else
3686         amountToReturn = ui.accountUSD->value() - ui.ordersTotalUSD->value();
3687 
3688     amountToReturn = JulyMath::cutDoubleDecimalsCopy(amountToReturn, baseValues.currentPair.currBDecimals, false);
3689 
3690     if (currentExchange->exchangeSupportsAvailableAmount)
3691         amountToReturn = qMin(availableAmount, amountToReturn);
3692 
3693     currentExchange->filterAvailableUSDAmountValue(&amountToReturn);
3694 
3695     return amountToReturn;
3696 }
3697 
getAvailableUSDtoBTC(double priceToBuy)3698 double QtBitcoinTrader::getAvailableUSDtoBTC(double priceToBuy)
3699 {
3700     double avUSD = getAvailableUSD();
3701     double decValue = 0.0L;
3702 
3703     if (floatFee > 0.0)
3704     {
3705         switch (currentExchange->calculatingFeeMode)
3706         {
3707         case 1:
3708             decValue = qPow(0.1, qMax(baseValues.currentPair.currADecimals, 1));
3709             break;
3710 
3711         case 2:
3712             decValue = 2.0 * qPow(0.1, qMax(baseValues.currentPair.currADecimals, 1));
3713             break;
3714 
3715         case 3:
3716             {
3717                 double zeros = qPow(10, baseValues.currentPair.currBDecimals);
3718                 avUSD = ceil(avUSD / (floatFee + 1.0) * zeros) / zeros;
3719                 break;
3720             }
3721 
3722         default:
3723             break;
3724         }
3725     }
3726 
3727     return JulyMath::cutDoubleDecimalsCopy(avUSD / priceToBuy - decValue, baseValues.currentPair.currADecimals, false);
3728 }
3729 
apiSellSend(const QString & symbol,double btc,double price)3730 void QtBitcoinTrader::apiSellSend(const QString& symbol, double btc, double price)
3731 {
3732     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3733     {
3734         emit apiSell(symbol, btc, price);
3735     }
3736 }
3737 
apiBuySend(const QString & symbol,double btc,double price)3738 void QtBitcoinTrader::apiBuySend(const QString& symbol, double btc, double price)
3739 {
3740     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3741     {
3742         emit apiBuy(symbol, btc, price);
3743     }
3744 }
3745 
accFeeChanged(const QString & symbol,double val)3746 void QtBitcoinTrader::accFeeChanged(const QString& symbol, double val)
3747 {
3748     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3749         setSpinValue(ui.accountFee, val);
3750 }
3751 
accBtcBalanceChanged(const QString & symbol,double val)3752 void QtBitcoinTrader::accBtcBalanceChanged(const QString& symbol, double val)
3753 {
3754     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3755         setSpinValue(ui.accountBTC, val);
3756 }
3757 
accUsdBalanceChanged(const QString & symbol,double val)3758 void QtBitcoinTrader::accUsdBalanceChanged(const QString& symbol, double val)
3759 {
3760     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3761         setSpinValue(ui.accountUSD, val);
3762 }
3763 
indicatorHighChanged(const QString & symbol,double val)3764 void QtBitcoinTrader::indicatorHighChanged(const QString& symbol, double val)
3765 {
3766     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3767         setSpinValue(ui.marketHigh, val);
3768 }
3769 
indicatorLowChanged(const QString & symbol,double val)3770 void QtBitcoinTrader::indicatorLowChanged(const QString& symbol, double val)
3771 {
3772     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3773         setSpinValue(ui.marketLow, val);
3774 }
3775 
indicatorBuyChanged(const QString & symbol,double val)3776 void QtBitcoinTrader::indicatorBuyChanged(const QString& symbol, double val)
3777 {
3778     if (secondTimer.isNull())
3779         return;
3780 
3781     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3782     {
3783         if (val == 0.0)
3784             val = ui.marketLast->value();
3785 
3786         if (val == 0.0)
3787             val = ui.marketBid->value();
3788 
3789         if (ui.marketAsk->value() == 0.0 && val > 0.0)
3790             buyPricePerCoin->setValue(val);
3791 
3792         setSpinValue(ui.marketAsk, val);
3793     }
3794 }
3795 
indicatorLastChanged(const QString & symbol,double val)3796 void QtBitcoinTrader::indicatorLastChanged(const QString& symbol, double val)
3797 {
3798     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3799         setSpinValue(ui.marketLast, val);
3800 }
3801 
indicatorSellChanged(const QString & symbol,double val)3802 void QtBitcoinTrader::indicatorSellChanged(const QString& symbol, double val)
3803 {
3804     if (secondTimer.isNull())
3805         return;
3806 
3807     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3808     {
3809         if (val == 0.0)
3810             val = ui.marketLast->value();
3811 
3812         if (val == 0.0)
3813             val = ui.marketAsk->value();
3814 
3815         if (ui.marketBid->value() == 0.0 && val > 0.0)
3816             sellPricePerCoin->setValue(val);
3817 
3818         setSpinValue(ui.marketBid, val);
3819     }
3820 }
3821 
indicatorVolumeChanged(const QString & symbol,double val)3822 void QtBitcoinTrader::indicatorVolumeChanged(const QString& symbol, double val)
3823 {
3824     if (baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3825         setSpinValue(ui.marketVolume, val);
3826 }
3827 
setRuleTabRunning(const QString & name,bool on)3828 void QtBitcoinTrader::setRuleTabRunning(const QString& name, bool on)
3829 {
3830     if (secondTimer.isNull())
3831         return;
3832 
3833     for (int n = 0; n < ui.rulesTabs->count(); n++)
3834         if (ui.rulesTabs->tabText(n) == name)
3835         {
3836             static QIcon playIcon(":/Resources/Play.png");
3837             ui.rulesTabs->setTabIcon(n, on ? playIcon : QIcon());
3838         }
3839 }
3840 
setSpinValueP(QDoubleSpinBox * spin,double & val)3841 void QtBitcoinTrader::setSpinValueP(QDoubleSpinBox* spin, double& val)
3842 {
3843     if (spin == nullptr || secondTimer.isNull())
3844         return;
3845 
3846     spin->blockSignals(true);
3847     int needChangeDecimals = 0;
3848 
3849     if (val < 0.00000001)
3850         spin->setDecimals(1);
3851     else
3852     {
3853         QByteArray valueStr = JulyMath::byteArrayFromDouble(val);
3854         int dotPos = valueStr.indexOf('.');
3855 
3856         if (dotPos == -1)
3857             spin->setDecimals(1);
3858         else
3859         {
3860             int newDecimals = valueStr.size() - dotPos - 1;
3861 
3862             if (spin->decimals() > newDecimals)
3863                 needChangeDecimals = newDecimals;
3864             else
3865                 spin->setDecimals(newDecimals);
3866         }
3867     }
3868 
3869     spin->blockSignals(false);
3870 
3871     spin->setMaximum(val);
3872     spin->setValue(val);
3873 
3874     if (needChangeDecimals)
3875     {
3876         spin->blockSignals(true);
3877         spin->setDecimals(needChangeDecimals);
3878         spin->blockSignals(false);
3879     }
3880 }
3881 
setSpinValue(QDoubleSpinBox * spin,double val)3882 void QtBitcoinTrader::setSpinValue(QDoubleSpinBox* spin, double val)
3883 {
3884     setSpinValueP(spin, val);
3885 }
3886 
getVolumeByPrice(const QString & symbol,double price,bool isAsk)3887 double QtBitcoinTrader::getVolumeByPrice(const QString& symbol, double price, bool isAsk)
3888 {
3889     if (!baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3890         return 0.0;
3891 
3892     return (isAsk ? depthAsksModel : depthBidsModel)->getVolumeByPrice(price, isAsk);
3893 }
3894 
getPriceByVolume(const QString & symbol,double size,bool isAsk)3895 double QtBitcoinTrader::getPriceByVolume(const QString& symbol, double size, bool isAsk)
3896 {
3897     if (!baseValues.currentPair.symbolSecond().startsWith(symbol, Qt::CaseInsensitive))
3898         return 0.0;
3899 
3900     return (isAsk ? depthAsksModel : depthBidsModel)->getPriceByVolume(size);
3901 }
3902 
on_helpButton_clicked()3903 void QtBitcoinTrader::on_helpButton_clicked()
3904 {
3905     QString helpType = "JLRule";
3906 
3907     if (ui.rulesTabs->count())
3908     {
3909         if (qobject_cast<ScriptWidget*>(ui.rulesTabs->currentWidget()))
3910             helpType = "JLScript";
3911     }
3912 
3913     QDesktopServices::openUrl(QUrl("https://qbtapi.centrabit.com/?Object=Help&Method="
3914                                    + helpType + "&Locale=" + QLocale().name()));
3915 }
3916 
initDocks()3917 void QtBitcoinTrader::initDocks()
3918 {
3919     setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
3920     setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
3921 }
3922 
createActions()3923 void QtBitcoinTrader::createActions()
3924 {
3925     actionLockDocks = new QAction("&Lock Docks", this);
3926     actionLockDocks->setCheckable(true);
3927     connect(actionLockDocks, SIGNAL(triggered(bool)), this, SLOT(onActionLockDocks(bool)));
3928 
3929     actionExit = new QAction("E&xit", this);
3930     actionExit->setShortcut(QKeySequence::Quit);
3931     connect(actionExit, SIGNAL(triggered()), this, SLOT(exitApp()));
3932 
3933     actionUpdate = new QAction("Check for &updates...", this);
3934     connect(actionUpdate, SIGNAL(triggered()), this, SLOT(checkUpdate()));
3935 
3936     actionSendBugReport = new QAction("&Send bug report", this);
3937     connect(actionSendBugReport, SIGNAL(triggered()), this, SLOT(onActionSendBugReport()));
3938 
3939     actionAbout = new QAction("&About", this);
3940     connect(actionAbout, SIGNAL(triggered()), this, SLOT(onActionAbout()));
3941 
3942     actionAboutQt = new QAction("About &Qt", this);
3943     connect(actionAboutQt, SIGNAL(triggered()), this, SLOT(onActionAboutQt()));
3944 
3945     actionConfigManager = new QAction("&Save...", this);
3946     connect(actionConfigManager, &QAction::triggered, this, &QtBitcoinTrader::onActionConfigManager);
3947 
3948     actionSettings = new QAction("Se&ttings", this);
3949     connect(actionSettings, &QAction::triggered, this, &QtBitcoinTrader::onActionSettings);
3950 
3951     actionDebug = new QAction("&Debug", this);
3952     connect(actionDebug, &QAction::triggered, this, &QtBitcoinTrader::onActionDebug);
3953 
3954 #ifndef Q_OS_MAC
3955 
3956     if (!baseValues_->portableMode)
3957     {
3958         actionUninstall = new QAction("&Uninstall", this);
3959         connect(actionUninstall, &QAction::triggered, this, &QtBitcoinTrader::uninstall);
3960     }
3961 
3962 #endif
3963 }
3964 
createMenu()3965 void QtBitcoinTrader::createMenu()
3966 {
3967     menuFile = menuBar()->addMenu("&QtBitcoinTrader");
3968     menuFile->addSeparator();
3969 
3970     menuFile->addSeparator();
3971     menuFile->addAction(actionSettings);
3972     menuFile->addAction(actionDebug);
3973     menuFile->addSeparator();
3974     menuFile->addAction(actionExit);
3975 #ifdef Q_OS_MAC
3976     actionSettings->setMenuRole(QAction::ApplicationSpecificRole);
3977     actionDebug->setMenuRole(QAction::ApplicationSpecificRole);
3978 #endif
3979     actionExit->setMenuRole(QAction::QuitRole);
3980 
3981     menuView = menuBar()->addMenu("&View");
3982     menuView->addAction(actionLockDocks);
3983     menuView->addSeparator();
3984 
3985     menuConfig = menuBar()->addMenu("&Config");
3986 
3987     menuHelp = menuBar()->addMenu("&Help");
3988     menuHelp->addAction(actionUpdate);
3989     menuHelp->addAction(actionSendBugReport);
3990     menuHelp->addSeparator();
3991     menuHelp->addAction(actionAbout);
3992     menuHelp->addAction(actionAboutQt);
3993 
3994 #ifndef Q_OS_MAC
3995 
3996     if (actionUninstall && !baseValues_->portableMode)
3997     {
3998         menuHelp->addSeparator();
3999         menuHelp->addAction(actionUninstall);
4000     }
4001 
4002 #endif
4003 
4004     ui.menubar->setStyleSheet("font-size:12px");
4005 }
4006 
uninstall()4007 void QtBitcoinTrader::uninstall()
4008 {
4009     QMessageBox msgBox(windowWidget);
4010     msgBox.setIcon(QMessageBox::Question);
4011     msgBox.setWindowTitle("Qt Bitcoin Trader");
4012     msgBox.setText(julyTr("CONFIRM_UNINSTALL",
4013                           "Are you sure to uninstall Application?<br>All configs, scripts, rules will be deleted"));
4014     msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
4015     msgBox.setDefaultButton(QMessageBox::Yes);
4016     msgBox.setButtonText(QMessageBox::Yes, julyTr("YES", "Yes"));
4017     msgBox.setButtonText(QMessageBox::No, julyTr("NO", "No"));
4018 
4019     if (msgBox.exec() != QMessageBox::Yes)
4020         return;
4021 
4022     QString tmpDir = QStandardPaths::standardLocations(QStandardPaths::TempLocation).first();
4023 
4024     QString fileName = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
4025     QFile::Permissions selfPerms = QFile(QCoreApplication::applicationFilePath()).permissions();
4026 
4027     if (QFile::exists(tmpDir + "/TMP_" + fileName))
4028     {
4029         QFile::remove(tmpDir + "/TMP_" + fileName);
4030 
4031         if (QFile::exists(tmpDir + "/TMP_" + fileName))
4032             QFile::rename(tmpDir + "/TMP_" + fileName, tmpDir + "/TMP_" + QString::number(QDateTime::currentSecsSinceEpoch()) + "_" + fileName);
4033     }
4034 
4035     QFile::copy(QCoreApplication::applicationFilePath(), tmpDir + "/TMP_" + fileName);
4036     QFile(tmpDir + "/TMP_" + fileName).setPermissions(selfPerms);
4037     QProcess proc;
4038 
4039     if (!proc.startDetached(tmpDir + "/TMP_" + fileName, QStringList() << "/uninstall"))
4040     {
4041         QMessageBox::warning(this, "Qt Bitcoin Trader", julyTr("UNINSTALL_ERROR",
4042                              "Can't uninstall app, you can delete files manually") + "\n" + appDataDir + "\n" + tmpDir + "/TMP_" + fileName + "\n" + proc.errorString());
4043         QFile::remove(tmpDir + "/TMP_" + fileName);
4044         return;
4045     }
4046 
4047     QCoreApplication::quit();
4048 }
4049 
createDock(QWidget * widget,const QString & title)4050 QDockWidget* QtBitcoinTrader::createDock(QWidget* widget, const QString& title)
4051 {
4052     widget->setProperty("IsDockable", true);
4053     QDockWidget* dock = dockHost->createDock(this, widget, title);
4054 
4055     if (widget != ui.widgetLogo)
4056     {
4057         menuView->addAction(dock->toggleViewAction());
4058     }
4059 
4060     return dock;
4061 }
4062 
moveWidgetsToDocks()4063 void QtBitcoinTrader::moveWidgetsToDocks()
4064 {
4065     // Top
4066     Qt::DockWidgetArea area = Qt::TopDockWidgetArea;
4067     addDockWidget(area, createDock(ui.widgetAccount, "Exchange Account"));
4068     addDockWidget(area, createDock(ui.widgetBalance, "Balance"));
4069     addDockWidget(area, createDock(ui.widgetTotalAtLast, "Total at Last Price"));
4070     addDockWidget(area, createDock(ui.widgetTotalAtBuySell, "Total at Buy/Sell Price"));
4071 
4072     QWidget* titleNULL = new QWidget();
4073     QDockWidget* dockNULL = new QDockWidget();
4074     dockNULL->setObjectName("dockNULL");
4075     dockNULL->setTitleBarWidget(titleNULL);
4076     dockNULL->setMinimumSize(0, 1);
4077     addDockWidget(area, dockNULL);
4078 
4079     addDockWidget(area, createDock(ui.widgetMarket, "Market"));
4080     addDockWidget(area, createDock(ui.widgetNetwork, "Network"));
4081 
4082     // Bottom
4083     QDockWidget* dockBuy = createDock(ui.widgetBuy, "Buy Bitcoin");
4084     QDockWidget* dockBuySell = createDock(ui.widgetBuyThenSell, "Generate subsequent sell order");
4085     QDockWidget* dockSell = createDock(ui.widgetSell, "Sell Bitcoin");
4086     QDockWidget* dockSellBuy = createDock(ui.widgetSellThenBuy, "Generate subsequent buy order");
4087     QDockWidget* dockGeneral = createDock(ui.widgetSellBuy, "General");
4088     dockLogo = createDock(ui.widgetLogo, "Qt Trader Exchange"); //Powered By
4089     dockLogo->setMinimumSize(170, 70);
4090     ui.widgetBuyThenSell->setFixedHeight(ui.widgetBuyThenSell->minimumSizeHint().height());
4091     ui.widgetSellThenBuy->setFixedHeight(ui.widgetSellThenBuy->minimumSizeHint().height());
4092 
4093     QWidget* titleBottomNULL = new QWidget();
4094     QDockWidget* dockHSpacer = new QDockWidget();
4095     dockHSpacer->setObjectName("dockHSpacer");
4096     dockHSpacer->setMinimumSize(0, 1);
4097     dockHSpacer->setTitleBarWidget(titleBottomNULL);
4098 
4099     addDockWidget(Qt::BottomDockWidgetArea, dockBuy);
4100     splitDockWidget(dockBuy, dockSell, Qt::Horizontal);
4101     splitDockWidget(dockSell, dockHSpacer, Qt::Horizontal);
4102     splitDockWidget(dockHSpacer, dockGeneral, Qt::Horizontal);
4103     splitDockWidget(dockBuy, dockBuySell, Qt::Vertical);
4104     splitDockWidget(dockSell, dockSellBuy, Qt::Vertical);
4105     splitDockWidget(dockGeneral, dockLogo, Qt::Vertical);
4106 
4107     // Left
4108     QDockWidget* dockGroupOrders = createDock(ui.groupOrders, "Your Open Orders");
4109     addDockWidget(Qt::LeftDockWidgetArea, dockGroupOrders);
4110 
4111     // lock Logo
4112     lockLogo(false);
4113 
4114     // tabs
4115     QDockWidget* dockOrdersLog = createDock(ui.tabOrdersLog, "Orders Log");
4116     QDockWidget* dockRules = createDock(ui.tabRules, "Rules");
4117     dockDepth = createDock(ui.tabDepth, "Order Book");
4118     QDockWidget* dockLastTrades = createDock(ui.tabLastTrades, "Trades");
4119     QDockWidget* dockCharts = createDock(ui.tabCharts, "Charts");
4120     QDockWidget* dockNews = createDock(ui.tabNews, "News");
4121     //QDockWidget* dockChat = createDock(ui.tabChat, "Chat");
4122     splitDockWidget(dockGroupOrders, dockOrdersLog, Qt::Horizontal);
4123     tabifyDockWidget(dockOrdersLog, dockRules);
4124     tabifyDockWidget(dockOrdersLog, dockDepth);
4125     tabifyDockWidget(dockOrdersLog, dockLastTrades);
4126     tabifyDockWidget(dockOrdersLog, dockCharts);
4127     tabifyDockWidget(dockOrdersLog, dockNews);
4128     //tabifyDockWidget(dockOrdersLog, dockChat);
4129     delete ui.tabWidget;
4130 
4131     // Central
4132     QDockWidget* centralDockNULL = new QDockWidget(this);
4133     centralDockNULL->setFixedWidth(0);
4134     setCentralWidget(centralDockNULL);
4135     //centralWidget()->deleteLater();
4136 
4137     connect(dockDepth,  SIGNAL(visibilityChanged(bool)), this,       SLOT(depthVisibilityChanged(bool)));
4138     connect(dockCharts, SIGNAL(visibilityChanged(bool)), chartsView, SLOT(visibilityChanged(bool)));
4139     connect(dockNews,   SIGNAL(visibilityChanged(bool)), newsView,   SLOT(visibilityChanged(bool)));
4140 
4141     lockedDocks = iniSettings->value("UI/LockedDocks", false).toBool();
4142 
4143     if (lockedDocks)
4144         actionLockDocks->setChecked(true);
4145 
4146     onActionLockDocks(lockedDocks);
4147 }
4148 
onActionAbout()4149 void QtBitcoinTrader::onActionAbout()
4150 {
4151     (new TranslationAbout(windowWidget))->showWindow();
4152 }
4153 
onActionSendBugReport()4154 void QtBitcoinTrader::onActionSendBugReport()
4155 {
4156     QDesktopServices::openUrl(QUrl("https://github.com/JulyIGHOR/QtBitcoinTrader/issues"));
4157 }
4158 
onActionAboutQt()4159 void QtBitcoinTrader::onActionAboutQt()
4160 {
4161     QApplication::aboutQt();
4162 }
4163 
onActionLockDocks(bool checked)4164 void QtBitcoinTrader::onActionLockDocks(bool checked)
4165 {
4166     lockedDocks = checked;
4167     dockHost->lockDocks(checked);
4168     lockLogo(checked);
4169 }
4170 
onActionConfigManager()4171 void QtBitcoinTrader::onActionConfigManager()
4172 {
4173     if (!configDialog)
4174     {
4175         configDialog = new ConfigManagerDialog(this);
4176     }
4177 
4178     julyTranslator.translateUi(configDialog);
4179     configDialog->setWindowTitle(julyTr("CONFIG_MANAGER_TITLE", "Config Manager"));
4180     configDialog->setWindowFlags(configDialog->windowFlags() & ~Qt::WindowContextHelpButtonHint);
4181     configDialog->show();
4182 }
4183 
onActionSettings()4184 void QtBitcoinTrader::onActionSettings()
4185 {
4186     SettingsDialog settingsDialog;
4187 
4188     if (currentPopupDialogs != 0)
4189         settingsDialog.disableTranslateButton();
4190 
4191     settingsDialog.exec();
4192 }
4193 
onActionDebug()4194 void QtBitcoinTrader::onActionDebug()
4195 {
4196     if (debugLevel == 0)
4197         debugViewer = new DebugViewer;
4198     else
4199     {
4200         if (debugViewer == nullptr)
4201             debugViewer = new DebugViewer;
4202 
4203         debugViewer->setWindowState(Qt::WindowActive);
4204         debugViewer->activateWindow();
4205     }
4206 }
4207 
onMenuConfigTriggered()4208 void QtBitcoinTrader::onMenuConfigTriggered()
4209 {
4210     QAction* action = static_cast<QAction*>(sender());
4211     QString name = action->text();
4212 
4213     ::config->load(name);
4214 }
4215 
onConfigChanged()4216 void QtBitcoinTrader::onConfigChanged()
4217 {
4218     initConfigMenu();
4219 }
4220 
onConfigError(const QString &)4221 void QtBitcoinTrader::onConfigError(const QString&)
4222 {
4223 }
4224 
changeEvent(QEvent * event)4225 void QtBitcoinTrader::changeEvent(QEvent* event)
4226 {
4227     if (event->type() == QEvent::WindowStateChange)
4228     {
4229         QWindowStateChangeEvent* stateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);
4230 
4231         if (stateChangeEvent)
4232         {
4233             if (stateChangeEvent->oldState() == Qt::WindowMaximized && windowState() == Qt::WindowNoState)
4234             {
4235                 adjustWidgetGeometry(this);
4236             }
4237         }
4238     }
4239 }
4240 
executeConfirmExitDialog()4241 bool QtBitcoinTrader::executeConfirmExitDialog()
4242 {
4243     QMessageBox msgBox(windowWidget);
4244     msgBox.setIcon(QMessageBox::Question);
4245     msgBox.setWindowTitle("Qt Bitcoin Trader");
4246     msgBox.setText(julyTr("CONFIRM_EXIT",
4247                           "Are you sure to close Application?<br>Active rules works only while application is running."));
4248     msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
4249     msgBox.setDefaultButton(QMessageBox::Yes);
4250     msgBox.setButtonText(QMessageBox::Yes, julyTr("YES", "Yes"));
4251     msgBox.setButtonText(QMessageBox::No, julyTr("NO", "No"));
4252     return msgBox.exec() == QMessageBox::Yes;
4253 }
4254 
closeEvent(QCloseEvent * event)4255 void QtBitcoinTrader::closeEvent(QCloseEvent* event)
4256 {
4257     event->ignore();
4258 
4259     if (closeToTray)
4260     {
4261         buttonMinimizeToTray();
4262     }
4263     else if (confirmExitApp())
4264     {
4265         exitApp();
4266     }
4267 }
4268 
confirmExitApp()4269 bool QtBitcoinTrader::confirmExitApp()
4270 {
4271     if (hasWorkingRules())
4272     {
4273         return executeConfirmExitDialog();
4274     }
4275     else
4276     {
4277         return true;
4278     }
4279 }
4280 
exitApp()4281 void QtBitcoinTrader::exitApp()
4282 {
4283     secondTimer.reset();
4284 
4285     saveAppState();
4286     ::config->save("", false);
4287     dockHost->hideFloatingWindow();
4288     hide();
4289 
4290     if (configDialog)
4291         configDialog->deleteLater();
4292 
4293     QCoreApplication::quit();
4294 }
4295 
setupWidgets()4296 void QtBitcoinTrader::setupWidgets()
4297 {
4298     buyTotalSpend->setAccessibleName("USD");
4299     buyTotalSpend->setAccessibleDescription("CAN_BE_ZERO");
4300     buyTotalSpend->setDecimals(5);
4301     buyTotalSpend->setMaximum(999999999.0);
4302     ui.totalToSpendLayout->addWidget(buyTotalSpend);
4303 
4304     buyPricePerCoin->setAccessibleName("PRICE");
4305     buyPricePerCoin->setDecimals(5);
4306     buyPricePerCoin->setMinimum(0.01);
4307     buyPricePerCoin->setMaximum(999999999.0);
4308     buyPricePerCoin->setValue(100.0);
4309     ui.pricePerCoinLayout->addWidget(buyPricePerCoin);
4310 
4311     buyTotalBtc->setAccessibleName("BTC");
4312     buyTotalBtc->setAccessibleDescription("CAN_BE_ZERO");
4313     buyTotalBtc->setDecimals(8);
4314     buyTotalBtc->setMaximum(999999999.0);
4315     ui.totalBtcToBuyLayout->addWidget(buyTotalBtc);
4316 
4317     profitLossSpinBox->setAccessibleName("USD");
4318     profitLossSpinBox->setAccessibleDescription("CAN_BE_ZERO");
4319     profitLossSpinBox->setDecimals(5);
4320     profitLossSpinBox->setMinimum(-999999999.0);
4321     profitLossSpinBox->setMaximum(999999999.0);
4322     ui.profitLossSpinBoxLayout->addWidget(profitLossSpinBox);
4323 
4324     profitLossSpinBoxPrec->setDecimals(3);
4325     profitLossSpinBoxPrec->setMinimum(-1000.0);
4326     profitLossSpinBoxPrec->setMaximum(1000.0);
4327     ui.profitLossSpinBoxPrecLayout->addWidget(profitLossSpinBoxPrec);
4328 
4329     sellTotalBtc->setAccessibleName("BTC");
4330     sellTotalBtc->setAccessibleDescription("CAN_BE_ZERO");
4331     sellTotalBtc->setDecimals(8);
4332     sellTotalBtc->setMaximum(999999999.0);
4333     ui.totalToSellLayout->addWidget(sellTotalBtc);
4334 
4335     sellPricePerCoin->setAccessibleName("PRICE");
4336     sellPricePerCoin->setDecimals(5);
4337     sellPricePerCoin->setMinimum(0.01);
4338     sellPricePerCoin->setMaximum(999999999.0);
4339     sellPricePerCoin->setValue(200.0);
4340     ui.pricePerCoinSellLayout->addWidget(sellPricePerCoin);
4341 
4342     sellAmountToReceive->setAccessibleName("USD");
4343     sellAmountToReceive->setAccessibleDescription("CAN_BE_ZERO");
4344     sellAmountToReceive->setDecimals(5);
4345     sellAmountToReceive->setMaximum(999999999.0);
4346     ui.amountToReceiveLayout->addWidget(sellAmountToReceive);
4347 
4348     sellThanBuySpinBox->setAccessibleName("BTC");
4349     sellThanBuySpinBox->setAccessibleDescription("CAN_BE_ZERO");
4350     sellThanBuySpinBox->setDecimals(8);
4351     sellThanBuySpinBox->setMinimum(-999999999.0);
4352     sellThanBuySpinBox->setMaximum(999999999.0);
4353     ui.sellThanBuySpinBoxLayout->addWidget(sellThanBuySpinBox);
4354 
4355     sellThanBuySpinBoxPrec->setDecimals(3);
4356     sellThanBuySpinBoxPrec->setMinimum(-9999.0);
4357     sellThanBuySpinBoxPrec->setMaximum(9999.0);
4358     ui.sellThanBuySpinBoxPrecLayout->addWidget(sellThanBuySpinBoxPrec);
4359 
4360     connect(buyTotalSpend,          QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::buyTotalSpend_valueChanged);
4361     connect(buyPricePerCoin,        QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::buyPricePerCoin_valueChanged);
4362     connect(buyTotalBtc,            QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::buyTotalBtc_valueChanged);
4363     connect(profitLossSpinBox,      QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::profitLossSpinBox_valueChanged);
4364     connect(profitLossSpinBoxPrec,  QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::profitLossSpinBoxPrec_valueChanged);
4365     connect(sellTotalBtc,           QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::sellTotalBtc_valueChanged);
4366     connect(sellPricePerCoin,       QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::sellPricePerCoin_valueChanged);
4367     connect(sellAmountToReceive,    QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::sellAmountToReceive_valueChanged);
4368     connect(sellThanBuySpinBox,     QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::sellThanBuySpinBox_valueChanged);
4369     connect(sellThanBuySpinBoxPrec, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &QtBitcoinTrader::sellThanBuySpinBoxPrec_valueChanged);
4370 }
4371