File indexing completed on 2024-04-21 16:33:19

0001 /*
0002     SPDX-FileCopyrightText: 2000 Shie Erlich <krusader@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2000 Rafi Yanai <krusader@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include <csignal>
0010 #include <unistd.h>
0011 
0012 // QtCore
0013 #include <QAbstractEventDispatcher>
0014 #include <QCommandLineOption>
0015 #include <QCommandLineParser>
0016 #include <QDebug>
0017 #include <QDir>
0018 #include <QEventLoop>
0019 #include <QStandardPaths>
0020 // QtGui
0021 #include <QPixmap>
0022 // QtDBus
0023 #include <QDBusConnectionInterface>
0024 #include <QDBusInterface>
0025 // QtWidgets
0026 #include <QApplication>
0027 #include <QSplashScreen>
0028 
0029 #include <KConfigCore/KSharedConfig>
0030 #include <KCoreAddons/KAboutData>
0031 #include <KI18n/KLocalizedString>
0032 #include <KWidgetsAddons/KActionMenu>
0033 #include <KWindowSystem/KStartupInfo>
0034 
0035 #include "../Archive/krarchandler.h"
0036 
0037 #include "defaults.h"
0038 #include "icon.h"
0039 #include "krservices.h"
0040 #include "krslots.h"
0041 #include "krusader.h"
0042 #include "krusaderversion.h"
0043 #include "krusaderview.h"
0044 #include "panelmanager.h"
0045 
0046 static const char *description = I18N_NOOP("Krusader\nTwin-Panel File Manager by KDE");
0047 
0048 static void sigterm_handler(int i)
0049 {
0050     fprintf(stderr, "Signal: %d\n", i);
0051 
0052     QAbstractEventDispatcher *instance = QAbstractEventDispatcher::instance();
0053     if (instance)
0054         instance->wakeUp();
0055     QApplication::exit(-15);
0056 }
0057 
0058 void openTabsRemote(QStringList tabs, bool left, const QString &appName)
0059 {
0060     // make sure left or right are not relative paths
0061     for (int i = 0; i != tabs.count(); i++) {
0062         tabs[i] = tabs[i].trimmed();
0063         if (!tabs[i].startsWith('/') && tabs[i].indexOf(":/") < 0)
0064             tabs[i] = QDir::currentPath() + '/' + tabs[i];
0065     }
0066 
0067     QDBusInterface remoteApp("org.krusader",
0068                              "/Instances/" + appName + (left ? "/left_manager" : "/right_manager"),
0069                              "org.krusader.PanelManager",
0070                              QDBusConnection::sessionBus());
0071     QDBusReply<void> reply;
0072     if (remoteApp.isValid())
0073         reply = remoteApp.call("newTabs", tabs);
0074 
0075     if (!reply.isValid())
0076         fprintf(stderr, "DBus Error: %s, %s\n", reply.error().name().toLocal8Bit().constData(), reply.error().message().toLocal8Bit().constData());
0077 }
0078 
0079 int main(int argc, char *argv[])
0080 {
0081     // set global log message format
0082     qSetMessagePattern(KrServices::GLOBAL_MESSAGE_PATTERN);
0083 
0084     // enable high dpi support
0085     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
0086     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
0087 
0088     // prevent qt5-webengine crashing
0089     QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
0090 
0091     // create the application and set application domain so that calls to i18n get strings from right place.
0092     QApplication app(argc, argv);
0093     KLocalizedString::setApplicationDomain("krusader");
0094 
0095     // init icon theme
0096     qDebug() << "System icon theme:" << QIcon::themeName();
0097     // [WORKAROUND] setThemeName sets user theme in QIconLoader and allows to avoid Qt issues with invalid icon caching later
0098     // IMPORTANT: this must be done before the first QIcon::fromTheme / QIcon::hasThemeIcon call
0099     QIcon::setThemeName(QIcon::themeName());
0100 
0101     // ABOUT data information
0102 #ifdef RELEASE_NAME
0103     QString versionName = QStringLiteral("%1 \"%2\"").arg(VERSION, RELEASE_NAME);
0104 #else
0105     QString versionName = VERSION;
0106 #endif
0107 
0108 #ifdef GIT_REVISION
0109     versionName += QString(" (gitrev: %1)").arg(GIT_REVISION);
0110 #endif
0111 
0112     KAboutData aboutData(QStringLiteral("krusader"),
0113                          (geteuid() ? i18n("Krusader") : i18n("Krusader - ROOT PRIVILEGES")),
0114                          versionName,
0115                          i18n(description),
0116                          KAboutLicense::GPL_V2,
0117                          i18n("© 2000-2003 Shie Erlich, Rafi Yanai\n© 2004-2022 Krusader Krew"),
0118                          i18n("Feedback:\nhttps://forum.kde.org/viewforum.php?f=225"),
0119                          QStringLiteral("https://krusader.org"));
0120 
0121     aboutData.setOrganizationDomain(QByteArray("kde.org"));
0122     aboutData.setDesktopFileName(QStringLiteral("org.kde.krusader"));
0123 
0124     aboutData.addAuthor(i18n("Davide Gianforte"), i18n("Developer"), QStringLiteral("davide@gengisdave.org"), nullptr);
0125     aboutData.addAuthor(i18n("Toni Asensi Esteve"), i18n("Developer"), QStringLiteral("toni.asensi@kdemail.net"), nullptr);
0126     aboutData.addAuthor(i18n("Nikita Melnichenko"), i18n("Developer"), QStringLiteral("nikita+kde@melnichenko.name"), nullptr);
0127     aboutData.addAuthor(i18n("Yuri Chornoivan"), i18n("Documentation"), QStringLiteral("yurchor@ukr.net"), nullptr);
0128     aboutData.addAuthor(i18n("Rafi Yanai"), i18n("Author (retired)"), QStringLiteral("yanai@users.sourceforge.net"));
0129     aboutData.addAuthor(i18n("Shie Erlich"), i18n("Author (retired)"), QStringLiteral("erlich@users.sourceforge.net"));
0130     aboutData.addAuthor(i18n("Csaba Karai"), i18n("Developer (retired)"), QStringLiteral("ckarai@users.sourceforge.net"), nullptr);
0131     aboutData.addAuthor(i18n("Heiner Eichmann"), i18n("Developer (retired)"), QStringLiteral("h.eichmann@gmx.de"), nullptr);
0132     aboutData.addAuthor(i18n("Jonas Bähr"), i18n("Developer (retired)"), QStringLiteral("jonas.baehr@web.de"), nullptr);
0133     aboutData.addAuthor(i18n("Václav Jůza"), i18n("Developer (retired)"), QStringLiteral("vaclavjuza@gmail.com"), nullptr);
0134     aboutData.addAuthor(i18n("Jan Lepper"), i18n("Developer (retired)"), QStringLiteral("jan_lepper@gmx.de"), nullptr);
0135     aboutData.addAuthor(i18n("Andrey Matveyakin"), i18n("Developer (retired)"), QStringLiteral("a.matveyakin@gmail.com"), nullptr);
0136     aboutData.addAuthor(i18n("Simon Persson"), i18n("Developer (retired)"), QStringLiteral("simon.persson@mykolab.com"), nullptr);
0137     aboutData.addAuthor(i18n("Alexander Bikadorov"), i18n("Developer (retired)"), QStringLiteral("alex.bikadorov@kdemail.net"), nullptr);
0138     aboutData.addAuthor(i18n("Martin Kostolný"), i18n("Developer (retired)"), QStringLiteral("clearmartin@gmail.com"), nullptr);
0139     aboutData.addAuthor(i18n("Dirk Eschler"), i18n("Webmaster (retired)"), QStringLiteral("deschler@users.sourceforge.net"), nullptr);
0140     aboutData.addAuthor(i18n("Frank Schoolmeesters"),
0141                         i18n("Documentation and marketing coordinator (retired)"),
0142                         QStringLiteral("frank_schoolmeesters@yahoo.com"),
0143                         nullptr);
0144     aboutData.addAuthor(i18n("Richard Holt"), i18n("Documentation & Proofing (retired)"), QStringLiteral("richard.holt@gmail.com"), nullptr);
0145     aboutData.addAuthor(i18n("Matej Urbancic"), i18n("Marketing & Product Research (retired)"), QStringLiteral("matej.urban@gmail.com"), nullptr);
0146     aboutData.addCredit(i18n("kde.org"), i18n("Everyone involved in KDE"), nullptr, nullptr);
0147     aboutData.addCredit(i18n("l10n.kde.org"), i18n("KDE Translation Teams"), nullptr, nullptr);
0148     aboutData.addCredit(i18n("Jiří Paleček"), i18n("QA, bug-hunting, patches and general help"), QStringLiteral("jpalecek@web.de"), nullptr);
0149     aboutData.addCredit(i18n("Jiří Klement"), i18n("Important help in KDE 4 porting"), nullptr, nullptr);
0150     aboutData.addCredit(i18n("Andrew Neupokoev"), i18n("Killer Logo and Icons for Krusader (contest winner)"), QStringLiteral("doom-blue@yandex.ru"), nullptr);
0151     aboutData.addCredit(i18n("The UsefulArts Organization"), i18n("Icon for Krusader"), QStringLiteral("mail@usefularts.org"), nullptr);
0152     aboutData.addCredit(i18n("Gábor Lehel"), i18n("Viewer module for 3rd Hand"), QStringLiteral("illissius@gmail.com"), nullptr);
0153     aboutData.addCredit(i18n("Mark Eatough"), i18n("Handbook Proof-Reader"), QStringLiteral("markeatough@yahoo.com"), nullptr);
0154     aboutData.addCredit(i18n("Jan Halasa"), i18n("The old Bookmark Module"), QStringLiteral("xhalasa@fi.muni.cz"), nullptr);
0155     aboutData.addCredit(i18n("Hans Löffler"), i18n("Dir history button"), nullptr, nullptr);
0156     aboutData.addCredit(i18n("Szombathelyi György"), i18n("ISO KIO slave"), nullptr, nullptr);
0157     aboutData.addCredit(i18n("Jan Willem van de Meent (Adios)"), i18n("Icons for Krusader"), QStringLiteral("janwillem@lorentz.leidenuniv.nl"), nullptr);
0158     aboutData.addCredit(i18n("Mikolaj Machowski"), i18n("Usability and QA"), QStringLiteral("<mikmach@wp.pl>"), nullptr);
0159     aboutData.addCredit(i18n("Cristi Dumitrescu"), i18n("QA, bug-hunting, patches and general help"), QStringLiteral("cristid@chip.ro"), nullptr);
0160     aboutData.addCredit(i18n("Aurelien Gateau"), i18n("patch for KViewer"), QStringLiteral("aurelien.gateau@free.fr"), nullptr);
0161     aboutData.addCredit(i18n("Milan Brabec"), i18n("the first patch ever!"), QStringLiteral("mbrabec@volny.cz"), nullptr);
0162     aboutData.addCredit(i18n("Asim Husanovic"), i18n("Bosnian translation"), QStringLiteral("asim@megatel.ba"), nullptr);
0163     aboutData.addCredit(i18n("Doutor Zero"), i18n("Brazilian Portuguese translation"), QStringLiteral("doutor.zero@gmail.com"), nullptr);
0164     aboutData.addCredit(i18n("Milen Ivanov"), i18n("Bulgarian translation"), QStringLiteral("milen.ivanov@abv.bg"), nullptr);
0165     aboutData.addCredit(i18n("Quim Perez"), i18n("Catalan translation"), QStringLiteral("noguer@osona.com"), nullptr);
0166     aboutData.addCredit(i18n("Jinghua Luo"), i18n("Chinese Simplified translation"), QStringLiteral("luojinghua@msn.com"), nullptr);
0167     aboutData.addCredit(i18n("Mitek"), i18n("Old Czech translation"), QStringLiteral("mitek@email.cz"), nullptr);
0168     aboutData.addCredit(i18n("Martin Sixta"), i18n("Old Czech translation"), QStringLiteral("lukumo84@seznam.cz"), nullptr);
0169     aboutData.addCredit(i18n("Vaclav Jůza"), i18n("Czech translation"), QStringLiteral("VaclavJuza@gmail.com"), nullptr);
0170     aboutData.addCredit(i18n("Anders Bruun Olsen"), i18n("Old Danish translation"), QStringLiteral("anders@bruun-olsen.net"), nullptr);
0171     aboutData.addCredit(i18n("Peter H. Sorensen"), i18n("Danish translation"), QStringLiteral("peters@skydebanen.net"), nullptr);
0172     aboutData.addCredit(i18n("Frank Schoolmeesters"), i18n("Dutch translation"), QStringLiteral("frank_schoolmeesters@yahoo.com"), nullptr);
0173     aboutData.addCredit(i18n("Rene-Pierre Lehmann"), i18n("Old French translation"), QStringLiteral("ripi@lepi.org"), nullptr);
0174     aboutData.addCredit(i18n("David Guillerm"), i18n("French translation"), QStringLiteral("dguillerm@gmail.com"), nullptr);
0175     aboutData.addCredit(i18n("Christoph Thielecke"), i18n("Old German translation"), QStringLiteral("crissi99@gmx.de"), nullptr);
0176     aboutData.addCredit(i18n("Dirk Eschler"), i18n("German translation"), QStringLiteral("deschler@users.sourceforge.net"), nullptr);
0177     aboutData.addCredit(i18n("Spiros Georgaras"), i18n("Greek translation"), QStringLiteral("sngeorgaras@gmail.com"), nullptr);
0178     aboutData.addCredit(i18n("Kukk Zoltan"), i18n("Old Hungarian translation"), QStringLiteral("kukkzoli@freemail.hu"), nullptr);
0179     aboutData.addCredit(i18n("Arpad Biro"), i18n("Hungarian translation"), QStringLiteral("biro_arpad@yahoo.com"), nullptr);
0180     aboutData.addCredit(i18n("Giuseppe Bordoni"), i18n("Italian translation"), QStringLiteral("geppo@geppozone.com"), nullptr);
0181     aboutData.addCredit(i18n("Hideki Kimura"), i18n("Japanese translation"), QStringLiteral("hangyo1973@gmail.com"), nullptr);
0182     aboutData.addCredit(i18n("UTUMI Hirosi"), i18n("Old Japanese translation"), QStringLiteral("utuhiro@mx12.freecom.ne.jp"), nullptr);
0183     aboutData.addCredit(i18n("Dovydas Sankauskas"), i18n("Lithuanian translation"), QStringLiteral("laisve@gmail.com"), nullptr);
0184     aboutData.addCredit(i18n("Bruno Queiros"), i18n("Portuguese translation"), QStringLiteral("brunoqueiros@portugalmail.com"), nullptr);
0185     aboutData.addCredit(i18n("Lukasz Janyst"), i18n("Old Polish translation"), QStringLiteral("ljan@wp.pl"), nullptr);
0186     aboutData.addCredit(i18n("Pawel Salawa"), i18n("Polish translation"), QStringLiteral("boogie@myslenice.one.pl"), nullptr);
0187     aboutData.addCredit(i18n("Tomek Grzejszczyk"), i18n("Polish translation"), QStringLiteral("tgrzej@onet.eu"), nullptr);
0188     aboutData.addCredit(i18n("Dmitry A. Bugay"), i18n("Russian translation"), QStringLiteral("sam@vhnet.ru"), nullptr);
0189     aboutData.addCredit(i18n("Dmitry Chernyak"), i18n("Old Russian translation"), QStringLiteral("chernyak@mail.ru"), nullptr);
0190     aboutData.addCredit(i18n("Sasa Tomic"), i18n("Serbian translation"), QStringLiteral("stomic@gmx.net"), nullptr);
0191     aboutData.addCredit(i18n("Zdenko Podobný and Ondrej Pačay (Yogi)"), i18n("Slovak translation"), QStringLiteral("zdenop@gmail.com"), nullptr);
0192     aboutData.addCredit(i18n("Matej Urbancic"), i18n("Slovenian translation"), QStringLiteral("matej.urban@gmail.com"), nullptr);
0193     aboutData.addCredit(i18n("Rafael Munoz"), i18n("Old Spanish translation"), QStringLiteral("muror@hotpop.com"), nullptr);
0194     aboutData.addCredit(i18n("Alejandro Araiza Alvarado"), i18n("Spanish translation"), QStringLiteral("mebrelith@gmail.com"), nullptr);
0195     aboutData.addCredit(i18n("Erik Johanssen"), i18n("Old Swedish translation"), QStringLiteral("erre@telia.com"), nullptr);
0196     aboutData.addCredit(i18n("Anders Linden"), i18n("Old Swedish translation"), QStringLiteral("connyosis@gmx.net"), nullptr);
0197     aboutData.addCredit(i18n("Peter Landgren"), i18n("Swedish translation"), QStringLiteral("peter.talken@telia.com"), nullptr);
0198     aboutData.addCredit(i18n("Bekir Sonat"), i18n("Turkish translation"), QStringLiteral("bekirsonat@kde.org.tr"), nullptr);
0199     aboutData.addCredit(i18n("Ivan Petrouchtchak"), i18n("Ukrainian translation"), QStringLiteral("connyosis@gmx.net"), nullptr);
0200     aboutData.addCredit(i18n("Seongnam Jee"), i18n("Korean translation"), QStringLiteral("snjee@intellicam.com"), nullptr);
0201 
0202     // This will call QCoreApplication::setApplicationName, etc for us by using info in the KAboutData instance.
0203     // The only thing not called for us is setWindowIcon(), which is why we do it ourselves here.
0204     KAboutData::setApplicationData(aboutData);
0205     app.setWindowIcon(Icon(Krusader::appIconName()));
0206 
0207     // Command line arguments ...
0208     QCommandLineParser parser;
0209     aboutData.setupCommandLine(&parser);
0210     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("left"), i18n("Start left panel at <path>"), QLatin1String("path")));
0211     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("right"), i18n("Start right panel at <path>"), QLatin1String("path")));
0212     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("profile"), i18n("Load this profile on startup"), QLatin1String("panel-profile")));
0213     parser.addOption(QCommandLineOption(QStringList() << "d" << QLatin1String("debug"), i18n("Enable debug output")));
0214     parser.addPositionalArgument(QLatin1String("url"), i18n("URL to open"));
0215 
0216     // check for command line arguments
0217     parser.process(app);
0218     aboutData.processCommandLine(&parser);
0219 
0220     // set global message handler
0221     KrServices::setGlobalKrMessageHandler(parser.isSet("debug"));
0222 
0223     KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("Look&Feel"));
0224     bool singleInstanceMode = cfg.readEntry("Single Instance Mode", _SingleInstanceMode);
0225 
0226     QString url;
0227     if (!parser.positionalArguments().isEmpty()) {
0228         url = parser.positionalArguments().first();
0229     }
0230 
0231     QString appName = "krusader";
0232     if (!singleInstanceMode)
0233         appName += QString("%1").arg(getpid());
0234 
0235     if (!QDBusConnection::sessionBus().isConnected()) {
0236         fprintf(stderr,
0237                 "Cannot connect to the D-BUS session bus.\n"
0238                 "To start it, run:\n"
0239                 "\teval `dbus-launch --auto-syntax`\n");
0240     }
0241 
0242     if (singleInstanceMode) {
0243         QDBusInterface remoteApp("org.krusader", "/Instances/" + appName, "org.krusader.Instance", QDBusConnection::sessionBus());
0244         QDBusReply<bool> reply;
0245         if (remoteApp.isValid())
0246             reply = remoteApp.call("isRunning");
0247 
0248         if (!reply.isValid() && reply.error().type() != QDBusError::ServiceUnknown && reply.error().type() != QDBusError::UnknownObject)
0249             fprintf(stderr, "DBus Error: %s, %s\n", reply.error().name().toLocal8Bit().constData(), reply.error().message().toLocal8Bit().constData());
0250 
0251         if (reply.isValid() && (bool)reply) {
0252             KStartupInfo::appStarted();
0253             if (parser.isSet("left"))
0254                 openTabsRemote(parser.value("left").split(','), true, appName);
0255             if (parser.isSet("right"))
0256                 openTabsRemote(parser.value("right").split(','), false, appName);
0257             if (!url.isEmpty()) {
0258                 reply = remoteApp.call("openUrl", url);
0259                 if (!reply.isValid())
0260                     fprintf(stderr, "DBus Error: %s, %s\n", reply.error().name().toLocal8Bit().constData(), reply.error().message().toLocal8Bit().constData());
0261             }
0262             return 0;
0263         }
0264     }
0265 
0266     // splash screen - if the user wants one
0267     QSplashScreen *splash = nullptr;
0268     { // don't remove bracket
0269         KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("Look&Feel"));
0270         if (cfg.readEntry("Show splashscreen", _ShowSplashScreen)) {
0271             QString splashFilename = QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("splash.png"));
0272             QPixmap pixmap(splashFilename);
0273             if (!pixmap.isNull()) {
0274                 splash = new QSplashScreen(pixmap);
0275                 splash->show();
0276             }
0277         }
0278     } // don't remove bracket
0279 
0280     Krusader::AppName = appName;
0281     auto *krusader = new Krusader(parser);
0282 
0283     if (!url.isEmpty())
0284         krusader->openUrl(url);
0285 
0286     QDBusConnection dbus = QDBusConnection::sessionBus();
0287     if (!dbus.interface()->isServiceRegistered("org.krusader") && !dbus.registerService("org.krusader")) {
0288         fprintf(stderr, "DBus Error: %s, %s\n", dbus.lastError().name().toLocal8Bit().constData(), dbus.lastError().message().toLocal8Bit().constData());
0289     }
0290     if (!dbus.registerObject("/Instances/" + appName, krusader, QDBusConnection::ExportScriptableSlots)) {
0291         fprintf(stderr, "DBus Error: %s, %s\n", dbus.lastError().name().toLocal8Bit().constData(), dbus.lastError().message().toLocal8Bit().constData());
0292     }
0293     if (!dbus.registerObject("/Instances/" + appName + "/left_manager", LEFT_MNG, QDBusConnection::ExportScriptableSlots)) {
0294         fprintf(stderr, "DBus Error: %s, %s\n", dbus.lastError().name().toLocal8Bit().constData(), dbus.lastError().message().toLocal8Bit().constData());
0295     }
0296     if (!dbus.registerObject("/Instances/" + appName + "/right_manager", RIGHT_MNG, QDBusConnection::ExportScriptableSlots)) {
0297         fprintf(stderr, "DBus Error: %s, %s\n", dbus.lastError().name().toLocal8Bit().constData(), dbus.lastError().message().toLocal8Bit().constData());
0298     }
0299 
0300     // catching SIGTERM, SIGHUP, SIGQUIT
0301     signal(SIGTERM, sigterm_handler);
0302     signal(SIGPIPE, sigterm_handler);
0303     signal(SIGHUP, sigterm_handler);
0304 
0305     QObject::connect(&app, &QGuiApplication::applicationStateChanged, SLOTS, &KrSlots::applicationStateChanged);
0306 
0307     // hide splashscreen
0308     if (splash) {
0309         splash->finish(krusader);
0310         delete splash;
0311     }
0312     // let's go.
0313     return app.exec();
0314 }