File indexing completed on 2024-04-28 05:06:07
0001 /* 0002 SPDX-FileCopyrightText: 2001 Michael Edwardes <mte@users.sourceforge.net> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 0007 #include <config-kmymoney.h> 0008 #include <config-kmymoney-version.h> 0009 0010 // ---------------------------------------------------------------------------- 0011 // QT Includes 0012 0013 #include <QApplication> 0014 #include <QCommandLineParser> 0015 #include <QRegularExpression> 0016 #include <QRegularExpressionMatch> 0017 #include <QStandardPaths> 0018 #include <QStringList> 0019 #include <QWidget> 0020 #ifdef KMM_DBUS 0021 #include <QDBusConnection> 0022 #include <QDBusConnectionInterface> 0023 #include <QDBusInterface> 0024 #endif 0025 // ---------------------------------------------------------------------------- 0026 // KDE Includes 0027 0028 #include <KLocalizedString> 0029 #include <KMessageBox> 0030 0031 // ---------------------------------------------------------------------------- 0032 // Project Includes 0033 0034 #include <alkimia/alkenvironment.h> 0035 0036 #include "amountedit.h" 0037 #include "kcreditswindow.h" 0038 #include "kmymoney.h" 0039 #include "kmymoneysettings.h" 0040 #include "kmymoneyutils.h" 0041 #include "misc/webconnect.h" 0042 #include "mymoney/mymoneyfile.h" 0043 #include "mymoneyexception.h" 0044 #include "platformtools.h" 0045 0046 #ifdef Q_OS_WIN 0047 #include <windows.h> 0048 #endif 0049 0050 #ifdef KMM_DEBUG 0051 #include "mymoneyutils.h" 0052 #include "mymoneytracer.h" 0053 #endif 0054 0055 #ifdef IMPORT_SQLCIPHER_PLUGIN 0056 Q_IMPORT_PLUGIN(QSQLCipherDriverPlugin) 0057 #endif 0058 0059 bool timersOn = false; 0060 0061 KMyMoneyApp* kmymoney; 0062 0063 static int runKMyMoney(QApplication &a, const QUrl &file, bool noFile); 0064 static void migrateConfigFiles(); 0065 0066 int main(int argc, char *argv[]) 0067 { 0068 #ifdef Q_OS_WIN 0069 // enable console logging on Windows 0070 if (AttachConsole(ATTACH_PARENT_PROCESS)) { 0071 freopen("CONOUT$", "w", stdout); 0072 freopen("CONOUT$", "w", stderr); 0073 } 0074 #endif 0075 0076 /** 0077 * Create application first 0078 */ 0079 QApplication app(argc, argv); 0080 KLocalizedString::setApplicationDomain("kmymoney"); 0081 0082 AlkEnvironment::checkForAppImageEnvironment(argv[0]); 0083 0084 migrateConfigFiles(); 0085 0086 /** 0087 * construct and register about data 0088 */ 0089 KAboutData aboutData(QStringLiteral("kmymoney"), i18n("KMyMoney"), QStringLiteral(VERSION)); 0090 aboutData.setOrganizationDomain("kde.org"); 0091 KAboutData::setApplicationData(aboutData); 0092 0093 QStringList fileUrls; 0094 bool isNoCatchOption = false; 0095 bool isNoFileOption = false; 0096 0097 #ifdef KMM_DEBUG 0098 bool isDumpActionsOption = false; 0099 #endif 0100 0101 if (argc != 0) { 0102 /** 0103 * Create command line parser and feed it with known options 0104 */ 0105 QCommandLineParser parser; 0106 aboutData.setupCommandLine(&parser); 0107 0108 // language 0109 const QCommandLineOption langOption(QStringLiteral("lang"), i18n("language to be used"), "lang"); 0110 parser.addOption(langOption); 0111 0112 // no file 0113 const QCommandLineOption noFileOption(QStringLiteral("n"), i18n("do not open last used file")); 0114 parser.addOption(noFileOption); 0115 0116 // timers 0117 const QCommandLineOption timersOption(QStringLiteral("timers"), i18n("enable performance timers")); 0118 parser.addOption(timersOption); 0119 0120 // no catch 0121 const QCommandLineOption noCatchOption(QStringLiteral("nocatch"), i18n("do not globally catch uncaught exceptions")); 0122 parser.addOption(noCatchOption); 0123 0124 #ifdef KMM_DEBUG 0125 // The following options are only available when compiled in debug mode 0126 // trace 0127 const QCommandLineOption traceOption(QStringLiteral("trace"), i18n("turn on program traces")); 0128 parser.addOption(traceOption); 0129 0130 // dump actions 0131 const QCommandLineOption dumpActionsOption(QStringLiteral("dump-actions"), i18n("dump the names of all defined QAction objects to stdout and quit")); 0132 parser.addOption(dumpActionsOption); 0133 #endif 0134 0135 // INSERT YOUR COMMANDLINE OPTIONS HERE 0136 // url to open 0137 parser.addPositionalArgument(QStringLiteral("url"), i18n("file to open")); 0138 0139 /** 0140 * do the command line parsing (and handle --help and --version) 0141 */ 0142 parser.process(QApplication::arguments()); 0143 0144 if (parser.isSet(QStringLiteral("author")) || parser.isSet(QStringLiteral("license"))) { 0145 aboutData = initializeCreditsData(); 0146 } 0147 0148 if (parser.isSet(QStringLiteral("lang"))) { 0149 QString language = parser.value(langOption); 0150 if (!language.isEmpty()) { 0151 KLocalizedString::setLanguages(QStringList() << language); 0152 } 0153 } 0154 0155 /** 0156 * handle standard options 0157 */ 0158 aboutData.processCommandLine(&parser); 0159 0160 #ifdef KMM_DEBUG 0161 if (parser.isSet(traceOption)) 0162 MyMoneyTracer::on(); 0163 timersOn = parser.isSet(timersOption); 0164 isDumpActionsOption = parser.isSet(dumpActionsOption); 0165 #endif 0166 0167 isNoCatchOption = parser.isSet(noCatchOption); 0168 isNoFileOption = parser.isSet(noFileOption); 0169 fileUrls = parser.positionalArguments(); 0170 } 0171 0172 // create the singletons before we start memory checking 0173 // to avoid false error reports 0174 auto file = MyMoneyFile::instance(); 0175 Q_UNUSED(file) 0176 0177 KMyMoneyUtils::checkConstants(); 0178 0179 // setup the MyMoneyMoney locale settings according to the KDE settings 0180 MyMoneyMoney::setThousandSeparator(QLocale().groupSeparator()); 0181 MyMoneyMoney::setDecimalSeparator(QLocale().decimalPoint()); 0182 0183 // setup format of negative values 0184 MyMoneyMoney::setNegativeSpaceSeparatesSymbol(false); 0185 MyMoneyMoney::setNegativePrefixCurrencySymbol(false); 0186 MyMoneyMoney::setNegativeMonetarySignPosition(static_cast<eMyMoney::Money::signPosition>(platformTools::currencySignPosition(true))); 0187 switch(platformTools::currencySymbolPosition(true)) { 0188 case platformTools::AfterQuantityMoneyWithSpace: 0189 MyMoneyMoney::setNegativeSpaceSeparatesSymbol(true); 0190 // intentional fall through 0191 case platformTools::AfterQuantityMoney: 0192 break; 0193 0194 case platformTools::BeforeQuantityMoneyWithSpace: 0195 MyMoneyMoney::setNegativeSpaceSeparatesSymbol(true); 0196 // intentional fall through 0197 case platformTools::BeforeQuantityMoney: 0198 MyMoneyMoney::setNegativePrefixCurrencySymbol(true); 0199 break; 0200 } 0201 // setup format of positive values 0202 MyMoneyMoney::setPositiveSpaceSeparatesSymbol(false); 0203 MyMoneyMoney::setPositivePrefixCurrencySymbol(false); 0204 MyMoneyMoney::setPositiveMonetarySignPosition(static_cast<eMyMoney::Money::signPosition>(platformTools::currencySignPosition(false))); 0205 switch(platformTools::currencySymbolPosition(false)) { 0206 case platformTools::AfterQuantityMoneyWithSpace: 0207 MyMoneyMoney::setPositiveSpaceSeparatesSymbol(true); 0208 // intentional fall through 0209 case platformTools::AfterQuantityMoney: 0210 break; 0211 case platformTools::BeforeQuantityMoneyWithSpace: 0212 MyMoneyMoney::setPositiveSpaceSeparatesSymbol(true); 0213 // intentional fall through 0214 case platformTools::BeforeQuantityMoney: 0215 MyMoneyMoney::setPositivePrefixCurrencySymbol(true); 0216 break; 0217 } 0218 0219 kmymoney = new KMyMoneyApp(); 0220 0221 #ifdef KMM_DEBUG 0222 if (isDumpActionsOption) { 0223 kmymoney->dumpActions(); 0224 0225 // Before we delete the application, we make sure that we destroy all 0226 // widgets by running the event loop for some time to catch all those 0227 // widgets that are requested to be destroyed using the deleteLater() method. 0228 //QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput, 10); 0229 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 10); 0230 0231 delete kmymoney; 0232 exit(0); 0233 } 0234 #endif 0235 0236 QString fname; 0237 // in case a filename is provided we need to check if it is a local 0238 // file. In case the name does not start with "file://" or "./" or "/" 0239 // we need to prepend "./" to fake a relative filename. Otherwise, QUrl prepends 0240 // "http://" and uses the full path which will not work. 0241 // On MS-Windows we also need to check if the filename starts with a 0242 // drive letter or the backslash variants. 0243 // 0244 // The handling might be different on other OSes 0245 if (!fileUrls.isEmpty()) { 0246 fname = fileUrls.front(); 0247 QFileInfo fi(fname); 0248 auto needLeadIn = fi.isFile(); 0249 #ifdef Q_OS_WIN 0250 QRegularExpression exp("^[a-z]:", QRegularExpression::CaseInsensitiveOption); 0251 needLeadIn &= !exp.match(fname).hasMatch() // 0252 && !fname.startsWith(QLatin1String(".\\")) // 0253 && !fname.startsWith(QLatin1String("\\")); 0254 #endif 0255 needLeadIn &= !fname.startsWith(QLatin1String("file://")) // 0256 && !fname.startsWith(QLatin1String("./")) // 0257 && !fname.startsWith(QLatin1String("/")); 0258 if (needLeadIn) { 0259 fname.prepend(QLatin1String("./")); 0260 } 0261 } 0262 0263 const QUrl url = QUrl::fromUserInput(fname, QLatin1String("."), QUrl::AssumeLocalFile); 0264 int rc = 0; 0265 if (isNoCatchOption) { 0266 qDebug("Running w/o global try/catch block"); 0267 rc = runKMyMoney(app, url, isNoFileOption); 0268 } else { 0269 try { 0270 rc = runKMyMoney(app, url, isNoFileOption); 0271 } catch (const MyMoneyException &e) { 0272 KMessageBox::detailedError(0, i18n("Uncaught error. Please report the details to the developers"), QString::fromLatin1(e.what())); 0273 throw; 0274 } 0275 } 0276 0277 return rc; 0278 } 0279 0280 int runKMyMoney(QApplication &a, const QUrl &file, bool noFile) 0281 { 0282 0283 #ifdef Q_OS_MAC 0284 kmymoney->setUnifiedTitleAndToolBarOnMac(true); 0285 kmymoney->setAttribute(Qt::WA_TranslucentBackground, true); 0286 kmymoney->setWindowFlag(Qt::MacWindowToolBarButtonHint); 0287 #endif 0288 0289 bool instantQuit = false; 0290 0291 /** 0292 * enable high dpi icons 0293 */ 0294 a.setAttribute(Qt::AA_UseHighDpiPixmaps); 0295 0296 if (kmymoney->webConnect()->isClient()) { 0297 // If the user launches a second copy of the app and includes a file to 0298 // open, they are probably attempting a "WebConnect" session. In this case, 0299 // we'll check to make sure it's an importable file that's passed in, and if so, we'll 0300 // notify the primary instance of the file and kill ourselves. 0301 0302 if (file.isValid()) { 0303 if (kmymoney->isImportableFile(file)) { 0304 instantQuit = true; 0305 kmymoney->webConnect()->loadFile(file); 0306 } 0307 } 0308 } 0309 0310 // show the main window and force it to repaint 0311 kmymoney->show(); 0312 qApp->processEvents(); 0313 0314 // now disable input and repaint again 0315 kmymoney->centralWidget()->setEnabled(false); 0316 qApp->processEvents(); 0317 0318 if (!instantQuit) { 0319 QString importfile; 0320 QUrl url; 0321 // make sure, we take the file provided on the command 0322 // line before we go and open the last one used 0323 if (file.isValid()) { 0324 // Check to see if this is an importable file, as opposed to a loadable 0325 // file. If it is importable, what we really want to do is load the 0326 // last used file anyway and then immediately import this file. This 0327 // implements a "web connect" session where there is not already an 0328 // instance of the program running. 0329 0330 if (kmymoney->isImportableFile(file)) { 0331 importfile = file.path(); 0332 url = QUrl::fromUserInput(kmymoney->readLastUsedFile()); 0333 } else { 0334 url = file; 0335 } 0336 0337 } else { 0338 url = QUrl::fromUserInput(kmymoney->readLastUsedFile()); 0339 } 0340 0341 if (url.isValid() && !noFile) { 0342 kmymoney->slotFileOpenRecent(url); 0343 0344 } else if (KMyMoneySettings::firstTimeRun()) { 0345 kmymoney->slotFileNew(); 0346 } 0347 0348 KMyMoneySettings::setFirstTimeRun(false); 0349 0350 if (!importfile.isEmpty()) { 0351 kmymoney->webConnect(importfile, QByteArray()); 0352 } 0353 0354 } else { 0355 // the instantQuit flag is set, so we force the app to quit right away 0356 kmymoney->slotFileQuit(); 0357 } 0358 0359 kmymoney->centralWidget()->setEnabled(true); 0360 0361 const int rc = a.exec(); //krazy:exclude=crashy 0362 return rc; 0363 } 0364 0365 static void migrateConfigFiles() 0366 { 0367 const QString sMainConfigName(QStringLiteral("kmymoneyrc")); 0368 const QString sMainConfigSubdirectory(QStringLiteral("kmymoney/")); // all KMM config files should be in ~/.config/kmymoney/ 0369 const QString sMainConfigPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + 0370 QLatin1Char('/') + 0371 sMainConfigSubdirectory; 0372 0373 if (!QFile::exists(sMainConfigPath + sMainConfigName)) { // if main config file doesn't exist, then it's first run 0374 0375 // it could be migration from KDE4 to KF5 so prepare list of configuration files to migrate 0376 QStringList sConfigNames 0377 { 0378 sMainConfigName, 0379 QStringLiteral("csvimporterrc"), 0380 QStringLiteral("printcheckpluginrc"), 0381 QStringLiteral("icalendarexportpluginrc"), 0382 QStringLiteral("kbankingrc"), 0383 }; 0384 0385 QFileInfo fileInfo(sMainConfigPath + sMainConfigName); 0386 QDir().mkpath(fileInfo.absolutePath()); 0387 const QString sOldMainConfigPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + 0388 QLatin1Char('/'); 0389 0390 // some files have changed their names during switch to KF5, so prepare map for name replacements 0391 QMap<QString, QString> configNamesChange { 0392 {QStringLiteral("printcheckpluginrc"), QStringLiteral("checkprintingrc")}, 0393 {QStringLiteral("icalendarexportpluginrc"), QStringLiteral("icalendarexporterrc")}, 0394 }; 0395 0396 for (const auto& sConfigName : qAsConst(sConfigNames)) { 0397 const QString sOldConfigFilename = sOldMainConfigPath + sConfigName; 0398 const QString sNewConfigFilename = sMainConfigPath + configNamesChange.value(sConfigName, sConfigName); 0399 if (QFile::exists(sOldConfigFilename)) { 0400 if (QFile::copy(sOldConfigFilename, sNewConfigFilename)) 0401 QFile::remove(sOldConfigFilename); 0402 } 0403 } 0404 } 0405 KConfig::setMainConfigName(sMainConfigSubdirectory + sMainConfigName); // otherwise it would be ~/.config/kmymoneyrc and not ~/.config/kmymoney/kmymoneyrc 0406 }