x-tools/Source/MainWindow.cpp
2024-03-31 00:46:00 +08:00

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();
}