mirror of
https://github.com/x-tools-author/x-tools.git
synced 2025-09-15 15:28:40 +08:00
526 lines
17 KiB
C++
526 lines
17 KiB
C++
/***************************************************************************************************
|
|
* Copyright 2018-2024 x-tools-author(x-tools@outlook.com). All rights reserved.
|
|
*
|
|
* The file is encoded using "utf8 with bom", it is a part of xTools project.
|
|
*
|
|
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
|
|
* code directory.
|
|
**************************************************************************************************/
|
|
#include "MainWindow.h"
|
|
|
|
#include <QAction>
|
|
#include <QButtonGroup>
|
|
#include <QClipboard>
|
|
#include <QCloseEvent>
|
|
#include <QDesktopServices>
|
|
#include <QFile>
|
|
#include <QFileDialog>
|
|
#include <QImage>
|
|
#include <QJsonParseError>
|
|
#include <QLocale>
|
|
#include <QMenuBar>
|
|
#include <QMessageBox>
|
|
#include <QPainter>
|
|
#include <QPixmap>
|
|
#include <QProcess>
|
|
#include <QScrollBar>
|
|
#include <QSizePolicy>
|
|
#include <QStatusBar>
|
|
#include <QStyleFactory>
|
|
#include <QSysInfo>
|
|
#include <QTextBrowser>
|
|
#include <QToolBar>
|
|
#include <QToolButton>
|
|
#include <QVariant>
|
|
|
|
#include "xToolsApplication.h"
|
|
#include "xToolsAssistantFactory.h"
|
|
#include "xToolsSettings.h"
|
|
#include "xToolsToolBoxUi.h"
|
|
#ifdef X_TOOLS_IMPORT_MODULE_CANBUS_STUDIO
|
|
#include "xToolsCanBusStudioUi.h"
|
|
#endif
|
|
#ifdef X_TOOLS_IMPORT_MODULE_MODBUS_STUDIO
|
|
#include "xToolsModbusStudioUi.h"
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include "SystemTrayIcon.h"
|
|
#endif
|
|
|
|
MainWindow::MainWindow(QWidget* parent)
|
|
#ifdef X_TOOLS_IMPORT_MODULE_PRIVATE
|
|
: xToolsPrivateMainWindow(parent)
|
|
#else
|
|
: xToolsMainWindow(parent)
|
|
#endif
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
// Setup system tray icon.
|
|
auto systemTrayIcon = new SystemTrayIcon(this);
|
|
QObject::connect(systemTrayIcon, &SystemTrayIcon::invokeExit, this, &MainWindow::close);
|
|
QObject::connect(systemTrayIcon, &SystemTrayIcon::invokeShowMainWindow, this, &MainWindow::show);
|
|
#endif
|
|
|
|
auto* stackedWidget = new QStackedWidget();
|
|
setCentralWidget(stackedWidget);
|
|
|
|
#if 0
|
|
#ifdef Q_OS_ANDROID
|
|
setWindowFlags(Qt::FramelessWindowHint);
|
|
QScrollArea* scrollArea = new QScrollArea(this);
|
|
scrollArea->setWidgetResizable(true);
|
|
setCentralWidget(scrollArea);
|
|
scrollArea->setWidget(mTabWidget);
|
|
#endif
|
|
#endif
|
|
|
|
QString title = QString("xTools");
|
|
title.append(QString(" "));
|
|
title.append(QString("v"));
|
|
title.append(qApp->applicationVersion());
|
|
setWindowTitle(title);
|
|
setWindowIcon(QIcon(":/Resources/Images/Logo.png"));
|
|
|
|
initMenuBar();
|
|
initNav();
|
|
#if 0
|
|
initStatusBar();
|
|
#endif
|
|
}
|
|
|
|
MainWindow::~MainWindow() = default;
|
|
|
|
void MainWindow::initMenuBar()
|
|
{
|
|
initFileMenu();
|
|
initToolMenu();
|
|
initOptionMenu();
|
|
initLanguageMenu();
|
|
initLinksMenu();
|
|
initHelpMenu();
|
|
}
|
|
|
|
#ifdef Q_OS_WIN
|
|
void MainWindow::closeEvent(QCloseEvent* event)
|
|
{
|
|
auto key = m_settingsKey.exitToSystemTray;
|
|
bool ignore = xToolsSettings::instance()->value(key).toBool();
|
|
if (ignore) {
|
|
this->hide();
|
|
event->ignore();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void MainWindow::initFileMenu()
|
|
{
|
|
// Tool box
|
|
auto* windowMenu = new QMenu(tr("New Window"), this);
|
|
m_fileMenu->addMenu(windowMenu);
|
|
QList<int> toolTypeList = xToolsToolBoxUi::supportedCommunicationTools();
|
|
for (auto& toolType : toolTypeList) {
|
|
const QString name = xToolsToolBoxUi::communicationToolName(toolType);
|
|
auto* action = new QAction(name, this);
|
|
windowMenu->addAction(action);
|
|
connect(action, &QAction::triggered, this, [=]() {
|
|
auto* w = new xToolsToolBoxUi();
|
|
w->setContentsMargins(9, 9, 9, 9);
|
|
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
|
w->initialize(toolType);
|
|
w->show();
|
|
});
|
|
}
|
|
|
|
// Other tools
|
|
#ifdef X_TOOLS_IMPORT_MODULE_MODBUS_STUDIO
|
|
auto* modbusAction = new QAction("Modbus Studio", this);
|
|
connect(modbusAction, &QAction::triggered, this, [=]() {
|
|
auto* w = new xToolsModbusStudioUi();
|
|
w->setContentsMargins(9, 9, 9, 9);
|
|
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
|
w->resize(1024, 480);
|
|
w->show();
|
|
});
|
|
windowMenu->addAction(modbusAction);
|
|
#endif
|
|
|
|
#ifdef X_TOOLS_IMPORT_MODULE_CANBUS_STUDIO
|
|
auto* canBusAction = new QAction("CANBus Studio", this);
|
|
connect(canBusAction, &QAction::triggered, this, [=]() {
|
|
auto* w = new xToolsCanBusStudioUi();
|
|
w->setContentsMargins(9, 9, 9, 9);
|
|
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
|
w->resize(1024, 480);
|
|
w->show();
|
|
});
|
|
windowMenu->addAction(canBusAction);
|
|
#endif
|
|
|
|
m_fileMenu->addSeparator();
|
|
m_fileMenu->addAction(m_exitAction);
|
|
}
|
|
|
|
void MainWindow::initToolMenu()
|
|
{
|
|
auto* toolMenu = new QMenu(tr("&Tools"));
|
|
menuBar()->insertMenu(m_languageMenu->menuAction(), toolMenu);
|
|
|
|
for (auto& type : SAKAssistantsFactory::instance()->supportedAssistants()) {
|
|
QString name = SAKAssistantsFactory::instance()->assistantName(type);
|
|
auto* action = new QAction(name, this);
|
|
QWidget* assistant = SAKAssistantsFactory::instance()->newAssistant(type);
|
|
|
|
Q_ASSERT_X(assistant, __FUNCTION__, "A null assistant widget!");
|
|
|
|
assistant->hide();
|
|
toolMenu->addAction(action);
|
|
connect(action, &QAction::triggered, this, [=]() {
|
|
if (assistant->isHidden()) {
|
|
assistant->show();
|
|
} else {
|
|
assistant->activateWindow();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void MainWindow::initOptionMenu()
|
|
{
|
|
auto* mainWindowMenu = new QMenu(tr("Main Window"), this);
|
|
auto* action = new QAction(tr("Exit to System Tray"), this);
|
|
action->setCheckable(true);
|
|
mainWindowMenu->addAction(action);
|
|
m_optionMenu->addSeparator();
|
|
m_optionMenu->addMenu(mainWindowMenu);
|
|
|
|
QVariant v = xToolsSettings::instance()->value(m_settingsKey.exitToSystemTray);
|
|
if (!v.isNull()) {
|
|
bool isExitToSystemTray = v.toBool();
|
|
action->setChecked(isExitToSystemTray);
|
|
}
|
|
|
|
connect(action, &QAction::triggered, this, [=]() {
|
|
bool keep = action->isChecked();
|
|
xToolsSettings::instance()->setValue(m_settingsKey.exitToSystemTray, keep);
|
|
});
|
|
}
|
|
|
|
void MainWindow::initLanguageMenu() {}
|
|
|
|
void MainWindow::initHelpMenu()
|
|
{
|
|
m_helpMenu->addSeparator();
|
|
m_helpMenu->addAction(/*QIcon(":/Resources/Icons/GitHub.svg"),*/
|
|
tr("Get Sources from Github"),
|
|
this,
|
|
[]() { QDesktopServices::openUrl(QUrl(X_TOOLS_GITHUB_REPOSITORY_URL)); });
|
|
m_helpMenu->addAction(/*QIcon(":/Resources/Icons/Gitee.svg"),*/
|
|
tr("Get Sources from Gitee"),
|
|
this,
|
|
[]() { QDesktopServices::openUrl(QUrl(X_TOOLS_GITEE_REPOSITORY_URL)); });
|
|
m_helpMenu->addSeparator();
|
|
#if 0
|
|
m_helpMenu->addAction(tr("About xTools"), this, &MainWindow::aboutSoftware);
|
|
#endif
|
|
#ifndef X_TOOLS_BUILD_FOR_STORE
|
|
#ifdef Q_OS_WIN
|
|
m_helpMenu->addAction(QIcon(":/Resources/Icons/IconBuy.svg"),
|
|
tr("Buy from Microsoft App Store"),
|
|
this,
|
|
[]() {
|
|
QUrl url("https://www.microsoft.com/store/apps/9P29H1NDNKBB");
|
|
QDesktopServices::openUrl(url);
|
|
});
|
|
#endif
|
|
#endif
|
|
m_helpMenu->addSeparator();
|
|
m_helpMenu->addAction(tr("Release History"),
|
|
this,
|
|
&MainWindow::showHistory);
|
|
m_helpMenu->addAction(/*QIcon(":/Resources/Icons/IconQQ.svg"),*/
|
|
tr("Join in QQ Group"),
|
|
this,
|
|
&MainWindow::showQrCode);
|
|
}
|
|
|
|
void MainWindow::initLinksMenu()
|
|
{
|
|
auto* linksMenu = new QMenu(tr("Links"), this);
|
|
menuBar()->insertMenu(m_helpMenu->menuAction(), linksMenu);
|
|
|
|
struct Link
|
|
{
|
|
QString name;
|
|
QString url;
|
|
QString iconPath;
|
|
};
|
|
QList<Link> linkList;
|
|
linkList << Link{tr("Qt Official Download"),
|
|
QString("http://download.qt.io/official_releases/qt"),
|
|
QString(":/resources/images/Qt.png")}
|
|
<< Link{tr("Qt Official Blog"),
|
|
QString("https://www.qt.io/blog"),
|
|
QString(":/resources/images/Qt.png")}
|
|
<< Link{tr("Qt Official Release"),
|
|
QString("https://wiki.qt.io/Qt_5.15_Release"),
|
|
QString(":/resources/images/Qt.png")}
|
|
<< Link{QString(""),
|
|
QString(""),
|
|
QString("")}
|
|
<< Link{tr("Download xTools from Github"),
|
|
QString("%1/releases").arg(X_TOOLS_GITHUB_REPOSITORY_URL),
|
|
QString(":/Resources/Icons/GitHub.svg")}
|
|
<< Link{tr("Download xTools from Gitee"),
|
|
QString("%1/releases").arg(X_TOOLS_GITEE_REPOSITORY_URL),
|
|
QString(":/Resources/Icons/Gitee.svg")}
|
|
<< Link{QString(""),
|
|
QString(""),
|
|
QString("")}
|
|
<< Link{tr("Office Web Site"),
|
|
QString("https://qsaker.gitee.io/qsak/"),
|
|
QString(":/Resources/Images/I18n.png")};
|
|
|
|
for (auto& var : linkList) {
|
|
if (var.url.isEmpty()) {
|
|
linksMenu->addSeparator();
|
|
continue;
|
|
}
|
|
|
|
auto* action = new QAction(var.name, this);
|
|
action->setObjectName(var.url);
|
|
linksMenu->addAction(action);
|
|
|
|
connect(action, &QAction::triggered, this, [=]() {
|
|
QDesktopServices::openUrl(QUrl(sender()->objectName()));
|
|
});
|
|
}
|
|
}
|
|
|
|
void MainWindow::initNav()
|
|
{
|
|
auto* tb = new QToolBar(this);
|
|
addToolBar(Qt::LeftToolBarArea, tb);
|
|
tb->setFloatable(false);
|
|
tb->setMovable(false);
|
|
tb->setOrientation(Qt::Vertical);
|
|
tb->setAllowedAreas(Qt::LeftToolBarArea);
|
|
|
|
static QButtonGroup btGroup;
|
|
QList<int> types = xToolsToolBoxUi::supportedCommunicationTools();
|
|
for (int i = 0; i < types.count(); i++) {
|
|
int type = types.at(i);
|
|
auto* toolBoxUi = new xToolsToolBoxUi(this);
|
|
toolBoxUi->initialize(type);
|
|
|
|
auto icon = xToolsApplication::cookedIcon(toolBoxUi->windowIcon());
|
|
initNav({&btGroup, icon, toolBoxUi->windowTitle(), toolBoxUi, tb});
|
|
}
|
|
|
|
tb->addSeparator();
|
|
initNavStudio(&btGroup, tb);
|
|
auto* lb = new QLabel(" ");
|
|
tb->addWidget(lb);
|
|
lb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
|
intNavControlButton(&btGroup, tb);
|
|
}
|
|
|
|
void MainWindow::initNavStudio(QButtonGroup* buttonGroup, QToolBar* toolBar)
|
|
{
|
|
#ifdef X_TOOLS_IMPORT_MODULE_MODBUS_STUDIO
|
|
auto* modbus = new xToolsModbusStudioUi(this);
|
|
auto icon = xToolsApplication::cookedIcon(QIcon(":/Resources/Icons/IconModbus.svg"));
|
|
initNav({buttonGroup, icon, "Modbus Studio", modbus, toolBar});
|
|
#endif
|
|
#ifdef X_TOOLS_IMPORT_MODULE_CANBUS_STUDIO
|
|
auto* canBus = new xToolsCanBusStudioUi(this);
|
|
icon = xToolsApplication::cookedIcon(QIcon(":/Resources/Icons/IconCanBus.svg"));
|
|
initNav({buttonGroup, icon, "CANBus Studio", canBus, toolBar});
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::initNav(const NavContext& ctx)
|
|
{
|
|
const QString key = m_settingsKey.isTextBesideIcon;
|
|
bool isTextBesideIcon = xToolsSettings::instance()->value(key).toBool();
|
|
auto style = isTextBesideIcon ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonIconOnly;
|
|
auto* bt = new QToolButton();
|
|
bt->setAutoRaise(true);
|
|
bt->setIcon(ctx.icon);
|
|
bt->setCheckable(true);
|
|
bt->setToolButtonStyle(style);
|
|
bt->setToolTip(ctx.name);
|
|
bt->setText(" " + ctx.name);
|
|
bt->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
|
#if 0
|
|
bt->setIconSize(QSize(32, 32));
|
|
#endif
|
|
ctx.bg->addButton(bt);
|
|
ctx.tb->addWidget(bt);
|
|
|
|
if (ctx.page->layout()) {
|
|
ctx.page->layout()->setContentsMargins(4, 4, 4, 4);
|
|
}
|
|
auto stackedWidget = qobject_cast<QStackedWidget*>(centralWidget());
|
|
stackedWidget->addWidget(ctx.page);
|
|
|
|
auto pageCount = ctx.bg->buttons().count();
|
|
QObject::connect(bt, &QToolButton::clicked, bt, [=]() {
|
|
stackedWidget->setCurrentIndex(int(pageCount) - 1);
|
|
xToolsSettings::instance()->setValue(m_settingsKey.pageIndex, pageCount - 1);
|
|
});
|
|
|
|
if (xToolsSettings::instance()->value(m_settingsKey.pageIndex).toInt() == (pageCount - 1)) {
|
|
bt->setChecked(true);
|
|
stackedWidget->setCurrentIndex(int(pageCount) - 1);
|
|
}
|
|
}
|
|
|
|
void MainWindow::intNavControlButton(QButtonGroup* buttonGroup, QToolBar* toolBar)
|
|
{
|
|
toolBar->addSeparator();
|
|
const QString key = m_settingsKey.isTextBesideIcon;
|
|
bool isTextBesideIcon = xToolsSettings::instance()->value(key).toBool();
|
|
auto style = isTextBesideIcon ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonIconOnly;
|
|
auto* tbt = new QToolButton(this);
|
|
const QString path = ":/Resources/Icons/IconListWithIcon.svg";
|
|
tbt->setIcon(xToolsApplication::cookedIcon(QIcon(path)));
|
|
tbt->setText(" " + tr("Show Icon Only"));
|
|
tbt->setToolTip(tr("Click to show(hide) nav text"));
|
|
tbt->setAutoRaise(true);
|
|
tbt->setToolButtonStyle(style);
|
|
tbt->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
|
toolBar->addWidget(tbt);
|
|
connect(tbt, &QToolButton::clicked, tbt, [=]() {
|
|
const QString key = m_settingsKey.isTextBesideIcon;
|
|
bool checked = xToolsSettings::instance()->value(key).toBool();
|
|
checked = !checked;
|
|
auto bts = buttonGroup->buttons();
|
|
auto style = checked ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonIconOnly;
|
|
tbt->setToolButtonStyle(style);
|
|
for (auto& bt : bts) {
|
|
auto cookedBt = qobject_cast<QToolButton*>(bt);
|
|
|
|
cookedBt->setToolButtonStyle(style);
|
|
}
|
|
xToolsSettings::instance()->setValue(key, checked);
|
|
});
|
|
}
|
|
|
|
void MainWindow::initStatusBar()
|
|
{
|
|
statusBar()->showMessage("Hello world", 10 * 1000);
|
|
}
|
|
|
|
void MainWindow::aboutSoftware()
|
|
{
|
|
struct Info
|
|
{
|
|
QString name;
|
|
QString value;
|
|
bool valueIsUrl;
|
|
};
|
|
|
|
QString format = QLocale::system().dateFormat();
|
|
format = format + " " + QLocale::system().timeFormat();
|
|
QString dateTimeString = xToolsApplication::buildDateTime(format);
|
|
QList<Info> infoList;
|
|
infoList << Info{tr("Version"), QString(qApp->applicationVersion()), false}
|
|
#ifndef SAK_RELEASE_FOR_APP_STORE
|
|
<< Info{tr("Edition"), X_TOOLS_EDITION, false}
|
|
#endif
|
|
<< Info{tr("Author"), QString(X_TOOLS_AUTHOR), false}
|
|
<< Info{tr("Email"), QString(X_TOOLS_AUTHOR_EMAIL), false}
|
|
<< Info{tr("QQ"), QString("QQ:2869470394"), false}
|
|
<< Info{tr("QQ Group"), QString("QQ:952218522"), false}
|
|
<< Info{tr("Build Time"), dateTimeString, false}
|
|
#ifndef SAK_RELEASE_FOR_APP_STORE
|
|
<< Info{tr("Gitee Url"),
|
|
QString("<a href=%1>%1</a>").arg(X_TOOLS_GITEE_REPOSITORY_URL),
|
|
true}
|
|
<< Info{tr("GitHub Url"),
|
|
QString("<a href=%1>%1</a>").arg(X_TOOLS_GITHUB_REPOSITORY_URL),
|
|
true}
|
|
#endif
|
|
<< Info{tr("Copyright"),
|
|
tr("Copyright 2018-%1 x-tools-author(x-tools@outlook.com)."
|
|
" All rights reserved.")
|
|
.arg(xToolsApplication::buildDateTime("yyyy")),
|
|
false};
|
|
|
|
QDialog dialog(this);
|
|
dialog.setWindowTitle(tr("About xTools"));
|
|
|
|
auto* gridLayout = new QGridLayout(&dialog);
|
|
int i = 0;
|
|
for (auto& var : infoList) {
|
|
auto* nameLabel = new QLabel(QString("<font color=green>%1</font>").arg(var.name),
|
|
&dialog);
|
|
auto* valueLabel = new QLabel(var.value, &dialog);
|
|
gridLayout->addWidget(nameLabel, i, 0, 1, 1);
|
|
gridLayout->addWidget(valueLabel, i, 1, 1, 1);
|
|
i += 1;
|
|
|
|
if (var.valueIsUrl) {
|
|
connect(valueLabel, &QLabel::linkActivated, [](const QString &url) {
|
|
QDesktopServices::openUrl(QUrl(url));
|
|
});
|
|
}
|
|
}
|
|
dialog.setLayout(gridLayout);
|
|
dialog.setModal(true);
|
|
dialog.show();
|
|
dialog.exec();
|
|
}
|
|
|
|
void MainWindow::showHistory()
|
|
{
|
|
QDialog dialog;
|
|
dialog.setModal(true);
|
|
dialog.setWindowTitle(tr("Release History"));
|
|
dialog.resize(600, 400);
|
|
|
|
auto* textBrowser = new QTextBrowser(&dialog);
|
|
QFile file(":/Resources/Files/History.txt");
|
|
if (file.open(QFile::ReadOnly)) {
|
|
QByteArray data = file.readAll();
|
|
textBrowser->setText(QString::fromUtf8(data));
|
|
}
|
|
|
|
auto* layout = new QHBoxLayout(&dialog);
|
|
layout->addWidget(textBrowser);
|
|
dialog.setLayout(layout);
|
|
dialog.show();
|
|
dialog.exec();
|
|
}
|
|
|
|
void MainWindow::showQrCode()
|
|
{
|
|
QDialog dialog;
|
|
dialog.setWindowTitle(tr("QR Code"));
|
|
|
|
struct QrCodeInfo
|
|
{
|
|
QString title;
|
|
QString qrCode;
|
|
};
|
|
QList<QrCodeInfo> qrCodeInfoList;
|
|
|
|
qrCodeInfoList << QrCodeInfo{tr("User QQ Group"), QString(":/Resources/Images/QSAKQQ.jpg")}
|
|
<< QrCodeInfo{tr("Qt QQ Group"), QString(":/Resources/Images/QtQQ.jpg")};
|
|
|
|
auto* tabWidget = new QTabWidget(&dialog);
|
|
for (auto& var : qrCodeInfoList) {
|
|
auto* label = new QLabel(tabWidget);
|
|
label->setPixmap(QPixmap::fromImage(QImage(var.qrCode)));
|
|
tabWidget->addTab(label, var.title);
|
|
}
|
|
|
|
auto* layout = new QHBoxLayout(&dialog);
|
|
layout->addWidget(tabWidget);
|
|
dialog.setLayout(layout);
|
|
dialog.setModal(true);
|
|
dialog.show();
|
|
dialog.exec();
|
|
}
|