syncthingtray/tray/application/main.cpp

284 lines
12 KiB
C++

#include "./singleinstance.h"
#include "../gui/trayicon.h"
#include "../gui/traywidget.h"
#include <syncthingwidgets/misc/syncthinglauncher.h>
#include <syncthingwidgets/settings/settings.h>
#include <syncthingconnector/syncthingprocess.h>
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
#include <syncthingconnector/syncthingservice.h>
#endif
#include "resources/config.h"
#include "resources/qtconfig.h"
#include <c++utilities/application/argumentparser.h>
#include <c++utilities/application/commandlineutils.h>
#include <c++utilities/misc/parseerror.h>
#include <qtutilities/resources/importplugin.h>
#include <qtutilities/resources/qtconfigarguments.h>
#include <qtutilities/resources/resources.h>
#include <qtutilities/settingsdialog/qtsettings.h>
#include <QApplication>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QSettings>
#include <QStringBuilder>
#include <QSvgRenderer>
#include <QPainter>
#include <QPixmap>
#include <iostream>
#include <thread>
using namespace std;
using namespace CppUtilities;
using namespace QtGui;
using namespace Data;
ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
void handleSystemdServiceError(const QString &context, const QString &name, const QString &message)
{
auto *const msgBox = new QMessageBox;
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setIcon(QMessageBox::Critical);
msgBox->setText(QCoreApplication::translate("main", "Unable to ") + context);
msgBox->setInformativeText(name % QStringLiteral(":\n") % message);
msgBox->show();
}
#endif
int initSyncthingTray(bool windowed, bool waitForTray, const Argument &connectionConfigArg)
{
// get settings
auto &settings = Settings::values();
static const auto defaultConnection = std::vector<const char *>({ "" });
std::cerr << "present: " << connectionConfigArg.isPresent() << "\n";
if (connectionConfigArg.isPresent()) {
std::cerr << "size: " << connectionConfigArg.values().size() << "\n";
}
const auto &connectionConfigurations
= connectionConfigArg.isPresent() && !connectionConfigArg.values().empty() ? connectionConfigArg.values() : defaultConnection;
// handle "windowed" case
if (windowed) {
// launch Syncthing if configured
settings.launcher.autostart();
// show a window for each connection
for (const auto *const connectionConfig : connectionConfigurations) {
auto *const trayWidget = new TrayWidget();
trayWidget->setAttribute(Qt::WA_DeleteOnClose);
trayWidget->show();
trayWidget->applySettings(QString::fromLocal8Bit(connectionConfig));
}
return 0;
}
#ifndef QT_NO_SYSTEMTRAYICON
// check whether system tray is available
if (!QSystemTrayIcon::isSystemTrayAvailable() && !waitForTray) {
QMessageBox::critical(nullptr, QApplication::applicationName(),
QApplication::translate("main",
"The system tray is (currently) not available. You could open the tray menu as a regular window using the --windowed flag, though."
"It is also possible to start Syncthing Tray with --wait to wait until the system tray becomes available instead of showing this "
"message."));
return -1;
}
// launch Syncthing if configured
std::cerr << ("before autostart\n");
settings.launcher.autostart();
// show a tray icon for each connection
TrayWidget *widget;
std::cerr << ("before conn loop\n");
for (const auto *const connectionConfig : connectionConfigurations) {
std::cerr << ("conn loop: ") << connectionConfig << "\n";
auto *const trayIcon = new TrayIcon(QString::fromLocal8Bit(connectionConfig), QApplication::instance());
trayIcon->show();
widget = &trayIcon->trayMenu().widget();
}
// show "first launch" message box
if (!settings.firstLaunch) {
return 0;
}
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Information);
msgBox.setText(QCoreApplication::translate("main", "You must configure how to connect to Syncthing when using Syncthing Tray the first time."));
msgBox.setInformativeText(QCoreApplication::translate(
"main", "Note that the settings dialog allows importing URL, credentials and API-key from the local Syncthing configuration."));
msgBox.exec();
widget->showSettingsDialog();
return 0;
#else
// show error if system tray is not supported by Qt
QMessageBox::critical(nullptr, QApplication::applicationName(),
QApplication::translate("main",
"The Qt libraries have not been built with tray icon support. You could open the tray menu as a regular "
"window using the -w flag, though."));
return -2;
#endif
}
void trigger(bool tray, bool webUi)
{
if (TrayWidget::instances().empty() || !(tray || webUi)) {
return;
}
auto *const trayWidget = TrayWidget::instances().front();
if (webUi) {
trayWidget->showWebUi();
}
if (tray) {
trayWidget->showUsingPositioningSettings();
}
}
void shutdownSyncthingTray()
{
Settings::save();
Settings::Launcher::terminate();
}
int runApplication(int argc, const char *const *argv)
{
std::cerr << ("run application\n");
// setup argument parser
SET_APPLICATION_INFO;
CMD_UTILS_CONVERT_ARGS_TO_UTF8;
ArgumentParser parser;
// Qt configuration arguments
QT_CONFIG_ARGUMENTS qtConfigArgs;
Argument windowedArg("windowed", 'w', "opens the tray menu as a regular window");
windowedArg.setCombinable(true);
Argument showWebUiArg("webui", '\0', "instantly shows the web UI - meant for creating shortcut to web UI");
showWebUiArg.setCombinable(true);
Argument triggerArg("trigger", '\0', "instantly shows the left-click tray menu - meant for creating a shortcut");
triggerArg.setCombinable(true);
Argument waitForTrayArg("wait", '\0',
"wait until the system tray becomes available instead of showing an error message if the system tray is not available on start-up");
waitForTrayArg.setCombinable(true);
ConfigValueArgument connectionArg("connection", '\0', "specifies one or more connection configurations to be used", { "config name" });
connectionArg.setRequiredValueCount(Argument::varValueCount);
ConfigValueArgument configPathArg("config-dir-path", '\0', "specifies the path to the configuration directory", { "path" });
configPathArg.setEnvironmentVariable(PROJECT_VARNAME_UPPER "_CONFIG_DIR");
ConfigValueArgument newInstanceArg("new-instance", '\0', "disable the usual single-process behavior");
Argument &widgetsGuiArg = qtConfigArgs.qtWidgetsGuiArg();
widgetsGuiArg.addSubArgument(&windowedArg);
widgetsGuiArg.addSubArgument(&showWebUiArg);
widgetsGuiArg.addSubArgument(&triggerArg);
widgetsGuiArg.addSubArgument(&waitForTrayArg);
widgetsGuiArg.addSubArgument(&connectionArg);
widgetsGuiArg.addSubArgument(&configPathArg);
widgetsGuiArg.addSubArgument(&newInstanceArg);
parser.setMainArguments({ &qtConfigArgs.qtWidgetsGuiArg(), &parser.noColorArg(), &parser.helpArg() });
std::cerr << ("before parsing args\n");
parser.parseArgs(argc, argv);
if (!qtConfigArgs.qtWidgetsGuiArg().isPresent()) {
return 0;
}
// handle override for config dir
std::cerr << ("before overriding config dir\n");
if (const char *const configPathDir = configPathArg.firstValue()) {
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, QString::fromLocal8Bit(configPathDir));
}
// check whether runApplication() has been called for the first time
static auto firstRun = true;
if (firstRun) {
firstRun = false;
// do first-time initializations
std::cerr << ("before first-time init\n");
SET_QT_APPLICATION_INFO;
QApplication application(argc, const_cast<char **>(argv));
QGuiApplication::setQuitOnLastWindowClosed(false);
const auto data = loadFontAwesomeIcon(QStringLiteral("hashtag"), QColor());
const auto i = renderSvgImage(data, QSize(64, 64), 0);
const auto data3 = loadFontAwesomeIcon(QStringLiteral("hashtag"), QColor());
const auto i3 = renderSvgImage(data, QSize(64, 64), 0);
std::cerr << "before sleep\n";
std::this_thread::sleep_for(2000ms);
std::cerr << "after sleep\n";
//const auto data2 = loadFontAwesomeIcon(QStringLiteral("globe"), QColor(10, 10, 10));
//const auto i2 = renderSvgImage(data2, QSize(64, 64), 0);
//static auto &m = IconManager::instance();
//const auto fas = FontAwesomeIcons(QColor(10, 10, 10), QSize(64, 64), 8);
std::cerr << ("before single instance\n");
SingleInstance singleInstance(argc, argv, newInstanceArg.isPresent());
networkAccessManager().setParent(&singleInstance);
QObject::connect(&singleInstance, &SingleInstance::newInstance, &runApplication);
std::cerr << ("before settings\n");
Settings::restore();
Settings::values().qt.apply();
qtConfigArgs.applySettings(true);
LOAD_QT_TRANSLATIONS;
std::cerr << ("before launcher\n");
SyncthingLauncher launcher;
SyncthingLauncher::setMainInstance(&launcher);
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
SyncthingService service;
SyncthingService::setMainInstance(&service);
Settings::values().systemd.setupService(service);
QObject::connect(&service, &SyncthingService::errorOccurred, &handleSystemdServiceError);
#endif
// init Syncthing Tray and immediately shutdown on failure
std::cerr << ("before init tray\n");
if (const auto res = initSyncthingTray(windowedArg.isPresent(), waitForTrayArg.isPresent(), connectionArg)) {
std::cerr << ("before shutdown\n");
shutdownSyncthingTray();
return res;
}
// trigger UI and enter event loop
std::cerr << ("before first run trigger\n");
QObject::connect(&application, &QCoreApplication::aboutToQuit, &shutdownSyncthingTray);
trigger(triggerArg.isPresent(), showWebUiArg.isPresent());
return application.exec();
}
// trigger actions if --webui or --trigger is present but don't create a new tray icon
std::cerr << ("before triggering\n");
if (!TrayWidget::instances().empty() && (showWebUiArg.isPresent() || triggerArg.isPresent())) {
trigger(triggerArg.isPresent(), showWebUiArg.isPresent());
return 0;
}
// create new/additional tray icon
std::cerr << ("before init additional\n");
const auto res = initSyncthingTray(windowedArg.isPresent(), waitForTrayArg.isPresent(), connectionArg);
if (!res) {
trigger(triggerArg.isPresent(), showWebUiArg.isPresent());
}
return res;
}
int main(int argc, char *argv[])
{
const auto application = QApplication(argc, const_cast<char **>(argv));
const auto data2 = QByteArray("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 496 512\"><path fill=\"#000000\" d=\"M336.5 160C322 70.7 287.8 8 248 8s-74 62.7-88.5 152h177zM152 256c0 22.2 1.2 43.5 3.3 64h185.3c2.1-20.5 3.3-41.8 3.3-64s-1.2-43.5-3.3-64H155. 3c-2.1 20.5-3.3 41.8-3.3 64zm324.7-96c-28.6-67.9-86.5-120.4-158-141.6 24.4 33.8 41.2 84.7 50 141.6h108zM177.2 18.4C105.8 39.6 47.8 92.1 19.3 160h108c8.7-56.9 25.5-107.8 49.9-141.6zM487.4 192H372.7c2.1 21 3.3 42.5 3.3 64s-1.2 43-3.3 64h1 14.6c5.5-20.5 8.6-41.8 8.6-64s-3.1-43.5-8.5-64zM120 256c0-21.5 1.2-43 3.3-64H8.6C3.2 212.5 0 233.8 0 256s3.2 43.5 8.6 64h114.6c-2-21-3.2-42.5-3.2-64zm39.5 96c14.5 89.3 48.7 152 88.5 152s74-62.7 88.5-152h-177zm159.3 141.6c71.4-21.2 129.4 -73.7 158-141.6h-108c-8.8 56.9-25.6 107.8-50 141.6zM19.3 352c28.6 67.9 86.5 120.4 158 141.6-24.4-33.8-41.2-84.7-50-141.6h-108z\"/></svg>");
auto renderer = QSvgRenderer(data2);
auto pm = QPixmap(QSize(64, 64));
pm.fill(QColor(Qt::transparent));
auto painter = QPainter(&pm);
renderer.render(&painter);
return 0;
return runApplication(argc, argv);
}