quick/quick_main.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "QuickMailHost.h" | ||
| 2 | #include "app/core/MailSessionCore.h" | ||
| 3 | #include "app/core/ProfileLease.h" | ||
| 4 | #include "app/core/ProfileActivation.h" | ||
| 5 | #include "app/core/ProcessTimeZone.h" | ||
| 6 | #include "app/MailtoUrl.h" | ||
| 7 | #include "presentation/AboutInfo.h" | ||
| 8 | #include "service/DesktopIntegration.h" | ||
| 9 | #include "util/LogConfigurator.h" | ||
| 10 | #include "version.h" | ||
| 11 | #include <QCommandLineParser> | ||
| 12 | #include <cstdio> | ||
| 13 | #include <QDir> | ||
| 14 | #include <QFileInfo> | ||
| 15 | #include <QSettings> | ||
| 16 | #include <QTranslator> | ||
| 17 | // QApplication (not QGuiApplication): the tray backends | ||
| 18 | // (QSystemTrayIcon, KStatusNotifierItem) live in the widgets stack; the | ||
| 19 | // frontend itself stays pure Qt Quick. | ||
| 20 | #include <QApplication> | ||
| 21 | #include <QtWebEngineQuick> | ||
| 22 | |||
| 23 | 3 | int main(int argc, char **argv) { | |
| 24 | // --version answers before any GUI exists, so it also works without a | ||
| 25 | // display (scripts, bug reports over SSH). The option is still registered | ||
| 26 | // below so --help lists it. | ||
| 27 |
2/2✓ Branch 19 → 3 taken 8 times.
✓ Branch 19 → 20 taken 2 times.
|
10 | for (int index = 1; index < argc; ++index) { |
| 28 |
7/8✓ Branch 3 → 4 taken 8 times.
✓ Branch 4 → 5 taken 7 times.
✓ Branch 4 → 7 taken 1 time.
✓ Branch 5 → 6 taken 7 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 7 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 18 taken 7 times.
|
8 | if (qstrcmp(argv[index], "--version") == 0 || qstrcmp(argv[index], "-v") == 0) { |
| 29 |
3/3✓ Branch 10 → 11 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
|
1 | fputs(qPrintable(AboutInfo::versionReport()), stdout); |
| 30 | 1 | return 0; | |
| 31 | } | ||
| 32 | } | ||
| 33 | // Before any time conversion: local time without a per-call zone reload. | ||
| 34 |
1/1✓ Branch 20 → 21 taken 2 times.
|
2 | ProcessTimeZone::pin(); |
| 35 |
1/1✓ Branch 21 → 22 taken 2 times.
|
2 | QtWebEngineQuick::initialize(); |
| 36 |
1/1✓ Branch 22 → 23 taken 2 times.
|
2 | QApplication app(argc, argv); |
| 37 |
1/1✓ Branch 23 → 24 taken 2 times.
|
2 | Q_INIT_RESOURCE(app_resources); |
| 38 |
2/2✓ Branch 24 → 25 taken 2 times.
✓ Branch 25 → 26 taken 2 times.
|
2 | app.setApplicationName("MailJD"); |
| 39 |
2/2✓ Branch 27 → 28 taken 2 times.
✓ Branch 28 → 29 taken 2 times.
|
2 | app.setOrganizationName("MailJD"); |
| 40 |
2/2✓ Branch 30 → 31 taken 2 times.
✓ Branch 31 → 32 taken 2 times.
|
2 | app.setDesktopFileName("mailjd"); |
| 41 |
1/1✓ Branch 36 → 37 taken 2 times.
|
2 | app.setApplicationVersion(QStringLiteral(MAILJD_VERSION_FULL)); |
| 42 |
1/1✓ Branch 39 → 40 taken 2 times.
|
2 | QCommandLineParser parser; |
| 43 |
2/2✓ Branch 40 → 41 taken 2 times.
✓ Branch 41 → 42 taken 2 times.
|
2 | parser.setApplicationDescription("MailJD - a keyboard-driven mail client with calendar, tasks and contacts."); |
| 44 |
1/1✓ Branch 43 → 44 taken 2 times.
|
2 | parser.addHelpOption(); |
| 45 | // Own version option: Qt's addVersionOption prints the version only; bug | ||
| 46 | // reports also need the commit and the Qt runtime (AboutInfo). | ||
| 47 |
5/7✓ Branch 48 → 49 taken 2 times.
✓ Branch 51 → 52 taken 2 times.
✓ Branch 52 → 53 taken 2 times.
✓ Branch 59 → 60 taken 4 times.
✓ Branch 59 → 61 taken 2 times.
✗ Branch 509 → 510 not taken.
✗ Branch 509 → 511 not taken.
|
6 | parser.addOption({{"v", "version"}, "Displays version, commit, Qt version and license."}); |
| 48 |
5/5✓ Branch 61 → 62 taken 2 times.
✓ Branch 62 → 63 taken 2 times.
✓ Branch 63 → 64 taken 2 times.
✓ Branch 65 → 66 taken 2 times.
✓ Branch 66 → 67 taken 2 times.
|
2 | parser.addOption({"profile", "Use this profile directory instead of the standard one.", "directory"}); |
| 49 |
4/4✓ Branch 72 → 73 taken 2 times.
✓ Branch 73 → 74 taken 2 times.
✓ Branch 76 → 77 taken 2 times.
✓ Branch 77 → 78 taken 2 times.
|
2 | parser.addOption({"offline", "Use local accounts and cache without keyring or mail connections."}); |
| 50 |
4/4✓ Branch 83 → 84 taken 2 times.
✓ Branch 84 → 85 taken 2 times.
✓ Branch 87 → 88 taken 2 times.
✓ Branch 88 → 89 taken 2 times.
|
2 | parser.addOption({"minimized", "Start minimized."}); |
| 51 |
4/4✓ Branch 94 → 95 taken 2 times.
✓ Branch 95 → 96 taken 2 times.
✓ Branch 98 → 99 taken 2 times.
✓ Branch 99 → 100 taken 2 times.
|
2 | parser.addOption({"compose", "Open a new message."}); |
| 52 |
4/4✓ Branch 105 → 106 taken 2 times.
✓ Branch 106 → 107 taken 2 times.
✓ Branch 109 → 110 taken 2 times.
✓ Branch 110 → 111 taken 2 times.
|
2 | parser.addOption({"activate", "Bring the application window to the foreground."}); |
| 53 |
4/4✓ Branch 116 → 117 taken 2 times.
✓ Branch 117 → 118 taken 2 times.
✓ Branch 118 → 119 taken 2 times.
✓ Branch 119 → 120 taken 2 times.
|
2 | parser.addPositionalArgument("mailto", "Optional mailto URL to open in the composer.", "[mailto:...]"); |
| 54 |
4/4✓ Branch 123 → 124 taken 2 times.
✓ Branch 124 → 125 taken 2 times.
✓ Branch 127 → 128 taken 2 times.
✓ Branch 128 → 129 taken 2 times.
|
2 | parser.addOption({"dark", "Start with the dark theme."}); |
| 55 |
4/4✓ Branch 134 → 135 taken 2 times.
✓ Branch 135 → 136 taken 2 times.
✓ Branch 138 → 139 taken 2 times.
✓ Branch 139 → 140 taken 2 times.
|
2 | parser.addOption({"wide", "Start with the wide layout."}); |
| 56 |
5/5✓ Branch 145 → 146 taken 2 times.
✓ Branch 146 → 147 taken 2 times.
✓ Branch 147 → 148 taken 2 times.
✓ Branch 149 → 150 taken 2 times.
✓ Branch 150 → 151 taken 2 times.
|
2 | parser.addOption({"language", "Interface language: auto, en or de (overrides saved preference).", "language"}); |
| 57 | // P90.6: register log options before process() and apply the resolved | ||
| 58 | // filter rules right after, so no startup category logs at unfiltered | ||
| 59 | // verbosity. Default keeps mailjd.* at Error (see LogConfigurator). | ||
| 60 |
1/1✓ Branch 156 → 157 taken 2 times.
|
2 | LogConfigurator::registerOptions(parser); |
| 61 |
1/1✓ Branch 157 → 158 taken 2 times.
|
2 | parser.process(app); |
| 62 |
1/1✓ Branch 158 → 159 taken 2 times.
|
2 | LogConfigurator::apply(parser); |
| 63 |
1/1✓ Branch 160 → 161 taken 2 times.
|
2 | ProcessTimeZone::logPinResult(); |
| 64 |
1/1✓ Branch 161 → 162 taken 2 times.
|
2 | const auto positional = parser.positionalArguments(); |
| 65 |
1/1✓ Branch 162 → 163 taken 2 times.
|
2 | const QString mailto = positional.value(0); |
| 66 |
4/6✓ Branch 164 → 165 taken 2 times.
✗ Branch 164 → 172 not taken.
✓ Branch 166 → 167 taken 1 time.
✓ Branch 166 → 173 taken 1 time.
✓ Branch 168 → 169 taken 1 time.
✗ Branch 168 → 172 not taken.
|
4 | if (positional.size() > 1 || (!positional.isEmpty() && |
| 67 |
5/9✓ Branch 169 → 170 taken 1 time.
✗ Branch 171 → 172 not taken.
✓ Branch 171 → 173 taken 1 time.
✓ Branch 174 → 175 taken 1 time.
✓ Branch 174 → 176 taken 1 time.
✗ Branch 176 → 177 not taken.
✓ Branch 176 → 182 taken 2 times.
✗ Branch 650 → 651 not taken.
✗ Branch 650 → 652 not taken.
|
4 | (mailto.size() > 65536 || !MailtoRequest::parse(mailto)))) { |
| 68 | ✗ | qCritical() << "Expected at most one valid mailto URL (maximum 64 KiB)."; | |
| 69 | ✗ | return 2; | |
| 70 | } | ||
| 71 | // F4.8: the Quick build is the product now, so the standard profile is the | ||
| 72 | // default. An explicit --profile still overrides it (tests, side-by-side). | ||
| 73 |
3/3✓ Branch 182 → 183 taken 2 times.
✓ Branch 183 → 184 taken 2 times.
✓ Branch 184 → 185 taken 2 times.
|
2 | QString profileDirectory = parser.value("profile").trimmed(); |
| 74 |
1/2✗ Branch 188 → 189 not taken.
✓ Branch 188 → 193 taken 2 times.
|
2 | if (profileDirectory.isEmpty()) |
| 75 | ✗ | profileDirectory = ProfileLease::defaultDirectory(); | |
| 76 | // Keep the lock alive until both the frontend and all session workers are gone. | ||
| 77 |
1/1✓ Branch 193 → 194 taken 2 times.
|
2 | ProfileLease profile; |
| 78 |
4/5✓ Branch 194 → 195 taken 2 times.
✓ Branch 195 → 196 taken 2 times.
✓ Branch 196 → 197 taken 2 times.
✓ Branch 199 → 200 taken 2 times.
✗ Branch 199 → 238 not taken.
|
2 | if (!profile.acquire(QDir(profileDirectory).absolutePath())) { |
| 79 | 2 | QString activationError; | |
| 80 | // The forwarding process passes its xdg-activation capability along so | ||
| 81 | // the owner can claim compositor focus for the raised window. | ||
| 82 |
2/2✓ Branch 210 → 211 taken 2 times.
✓ Branch 211 → 212 taken 2 times.
|
2 | if (ProfileActivation::forward(QDir(profileDirectory).absolutePath(), mailto, |
| 83 |
3/3✓ Branch 207 → 208 taken 2 times.
✓ Branch 208 → 209 taken 2 times.
✓ Branch 209 → 210 taken 2 times.
|
4 | parser.isSet("compose"), &activationError, 5000, |
| 84 |
1/1✓ Branch 200 → 201 taken 2 times.
|
2 | DesktopIntegration::currentActivationPlatformData() |
| 85 |
4/4✓ Branch 205 → 206 taken 2 times.
✓ Branch 206 → 207 taken 2 times.
✓ Branch 221 → 222 taken 1 time.
✓ Branch 221 → 223 taken 1 time.
|
6 | .value(QStringLiteral("activation-token")).toString())) |
| 86 | 1 | return 0; | |
| 87 |
2/2✓ Branch 224 → 225 taken 1 time.
✓ Branch 227 → 228 taken 1 time.
|
1 | qCritical().noquote() << profile.error(); |
| 88 |
2/2✓ Branch 231 → 232 taken 1 time.
✓ Branch 233 → 234 taken 1 time.
|
1 | qCritical().noquote() << activationError; |
| 89 | 1 | return 2; | |
| 90 | 2 | } | |
| 91 | ✗ | if (profile.directory() != QFileInfo(ProfileLease::defaultDirectory()).canonicalFilePath()) { | |
| 92 | ✗ | const QString settingsPath = QDir(profile.directory()).filePath("settings"); | |
| 93 | ✗ | QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, settingsPath); | |
| 94 | ✗ | QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settingsPath); | |
| 95 | ✗ | QSettings::setPath(QSettings::NativeFormat, QSettings::SystemScope, settingsPath); | |
| 96 | ✗ | QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, settingsPath); | |
| 97 | ✗ | } | |
| 98 | ✗ | QTranslator translator; | |
| 99 | ✗ | QString language = parser.isSet("language") ? parser.value("language") | |
| 100 | ✗ | : QSettings().value("i18n/language", "auto").toString(); | |
| 101 | ✗ | if (language != "auto" && language != "en" && language != "de") { | |
| 102 | ✗ | qCritical() << "Unsupported interface language:" << language; | |
| 103 | ✗ | return 2; | |
| 104 | } | ||
| 105 | ✗ | if (language == "auto") language = QLocale::system().language() == QLocale::German ? "de" : "en"; | |
| 106 | ✗ | else QLocale::setDefault(QLocale(language)); // Date/month names follow an explicit interface language. | |
| 107 | ✗ | if (language == "de") { | |
| 108 | ✗ | if (!translator.load(":/translations/mailjd_de.qm")) return 2; | |
| 109 | ✗ | app.installTranslator(&translator); | |
| 110 | } | ||
| 111 | ✗ | MailSessionCore session; | |
| 112 | ✗ | if (!session.openProfile(profile.directory())) { | |
| 113 | ✗ | qCritical().noquote() << session.profileError(); | |
| 114 | ✗ | return 2; | |
| 115 | } | ||
| 116 | ✗ | QuickMailHost host(session, profile.directory()); | |
| 117 | ✗ | if (!host.initialize(!parser.isSet("offline"), parser.isSet("dark") ? "dark" : "", | |
| 118 | ✗ | parser.isSet("wide") ? "wide" : "", QSettings("MailJD", "MailJD").fileName(), | |
| 119 | ✗ | parser.isSet("minimized"))) { | |
| 120 | ✗ | qCritical().noquote() << host.error(); | |
| 121 | ✗ | return 3; | |
| 122 | } | ||
| 123 | ✗ | if ((!mailto.isEmpty() || parser.isSet("compose") || parser.isSet("activate")) && | |
| 124 | ✗ | !host.activate(mailto, parser.isSet("compose"), | |
| 125 | ✗ | DesktopIntegration::currentActivationPlatformData() | |
| 126 | ✗ | .value(QStringLiteral("activation-token")).toString())) { | |
| 127 | ✗ | qCritical().noquote() << host.error(); | |
| 128 | ✗ | return 3; | |
| 129 | } | ||
| 130 | ✗ | ProfileActivation activation; | |
| 131 | ✗ | if (!activation.listen(profile, [&host](const QString &url, bool compose, | |
| 132 | const QString &activationToken) { | ||
| 133 | ✗ | return host.activate(url, compose, activationToken); | |
| 134 | })) | ||
| 135 | ✗ | qWarning().noquote() << "Desktop activation is unavailable:" << activation.error(); | |
| 136 | ✗ | const int result = app.exec(); | |
| 137 | ✗ | if (!host.flush()) { | |
| 138 | ✗ | qCritical().noquote() << host.error(); | |
| 139 | ✗ | return 3; | |
| 140 | } | ||
| 141 | ✗ | return result; | |
| 142 | 4 | } | |
| 143 |