]> git.sven.stormbind.net Git - sven/vym.git/blob - src/main.cpp
New upstream version 2.9.22
[sven/vym.git] / src / main.cpp
1 #include <QApplication>
2 #include <QMessageBox>
3 #include <QStyleFactory>
4
5 #include <cstdlib>
6 #include <iostream>
7 using namespace std;
8
9 #include "command.h"
10 #include "debuginfo.h"
11 #include "findresultwidget.h"
12 #include "findwidget.h"
13 #include "flagrow.h"
14 #include "flagrowobj.h"
15 #include "headingeditor.h"
16 #include "macros.h"
17 #include "mainwindow.h"
18 #include "noteeditor.h"
19 #include "options.h"
20 #include "scripteditor.h"
21 #include "scriptoutput.h"
22 #include "settings.h"
23 #include "shortcuts.h"
24 #include "taskeditor.h"
25 #include "taskmodel.h"
26 #include "version.h"
27 #include "warningdialog.h"
28
29 #if defined(VYM_DBUS)
30 #include <QtDBus/QDBusConnection>
31 #include <QtDBus/QDBusError>
32 #endif
33
34 QString vymName;
35 QString vymVersion;
36 QString vymHome;
37 QString vymBuildDate;
38 QString vymCodeName;
39 QString vymCodeQuality;
40 QString vymInstanceName;
41 QString vymPlatform;
42 QString localeName;
43
44 QTextStream vout(stdout); // vymout - Testing for now. Flush after writing...
45
46 // Accessing JIRA and Confluence is done using agents
47 // Credentials may be stored in settings, but only on request
48 QString jiraPassword;   
49 QString confluencePassword;
50
51 TaskModel *taskModel;
52 TaskEditor *taskEditor;
53 ScriptEditor *scriptEditor;
54 ScriptOutput *scriptOutput;
55 HeadingEditor *headingEditor;
56 NoteEditor *noteEditor; // used in Constr. of LinkableMapObj
57 BranchPropertyEditor *branchPropertyEditor;
58
59 // initialized in mainwindow
60 Main *mainWindow;
61 FindWidget *findWidget;
62 FindResultWidget *findResultWidget;
63
64 FlagRowMaster *systemFlagsMaster;
65 FlagRowMaster *standardFlagsMaster;
66 FlagRowMaster *userFlagsMaster;
67
68 Macros macros;
69
70 ulong itemLastID = 0;  // Unique ID for all items in all models
71 ulong imageLastID = 0; // Unique ID for caching images, also flags not in tree
72
73 QDir tmpVymDir;          // All temp files go there, created in mainwindow
74 QDir cacheDir;            // tmp dir with cached svg files in tmpVymDir
75 QString clipboardDir;    // Clipboard used in all mapEditors
76 QString clipboardFile;   // Clipboard used in all mapEditors
77
78 QDir vymBaseDir;            // Containing all styles, scripts, images, ...
79
80 QDir vymTranslationsDir;    // Translation files (*.qm)
81 QTranslator vymTranslator;
82
83 QDir lastImageDir;
84 QDir lastMapDir;
85 QDir lastExportDir;
86 #if defined(Q_OS_WINDOWS)
87 QDir vymInstallDir;
88 #endif
89 QString iconPath;  // Pointing to icons used for toolbars
90 QString flagsPath; // Pointing to flags
91
92 bool debug;                // global debugging flag
93 bool testmode;             // Used to disable saving of autosave setting
94 bool restoreMode = false;  // During restore, existing lockfiles are ignored
95
96 QStringList ignoredLockedFiles;
97 QStringList lastSessionFiles;   //! Will be overwritten in setting after load, so read initially
98
99 Switchboard switchboard;
100
101 Settings settings("InSilmaril", "vym"); // Organization, Application name
102
103 bool zipToolAvailable = false;
104 bool unzipToolAvailable = false;
105 QString zipToolPath;   // Platform dependant zip tool
106 QString unzipToolPath; // For windows same as zipToolPath
107
108 QList<Command *> modelCommands;
109 QList<Command *> vymCommands;
110
111 Options options;
112 ImageIO imageIO;
113
114 int statusbarTime = 10000;
115
116 bool usingDarkTheme;
117 QColor vymBlue;
118
119 int warningCount = 0;
120 int criticalCount = 0;
121 int fatalCount = 0;
122
123 QString editorFocusStyle =
124     QString(" border-color: #3daee9; border-style:outset; border-width:3px; "
125             "color:black;");
126
127 #include <QScriptEngine>
128 QScriptValue scriptPrint(QScriptContext *ctx, QScriptEngine *eng);
129
130 void msgHandler(QtMsgType type, const QMessageLogContext &context,
131                 const QString &msg)
132 {
133     QByteArray localMsg = msg.toLocal8Bit();
134     switch (type) {
135     case QtDebugMsg:
136         fprintf(stderr, "%s (%s:%u, %s)\n", localMsg.constData(), context.file,
137                 context.line, context.function);
138         break;
139     case QtWarningMsg:
140         fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(),
141                 context.file, context.line, context.function);
142         warningCount++;
143         break;
144     case QtCriticalMsg:
145         fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(),
146                 context.file, context.line, context.function);
147         criticalCount++;
148         break;
149     case QtFatalMsg:
150         fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(),
151                 context.file, context.line, context.function);
152         fatalCount++;
153         break;
154     default:
155         fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(),
156                 context.file, context.line, context.function);
157     }
158 }
159
160 int main(int argc, char *argv[])
161 {
162     QApplication app(argc, argv);
163
164     // Define some constants shared in various places
165     vymName = __VYM_NAME;
166     vymVersion = __VYM_VERSION;
167     vymBuildDate = __VYM_BUILD_DATE;
168     vymCodeName = __VYM_CODENAME;
169     vymCodeQuality = __VYM_CODE_QUALITY;
170     vymHome = __VYM_HOME;
171
172     // Install our own handler for messages
173     qInstallMessageHandler(msgHandler);
174
175     // Testing for now
176     vout.setCodec("UTF-8");
177
178     // Reading and initializing options commandline options
179     options.add("batch", Option::Switch, "b", "batch");
180     options.add("commands", Option::Switch, "c", "commands");
181     options.add("commandslatex", Option::Switch, "cl", "commandslatex");
182     options.add("debug", Option::Switch, "d", "debug");
183     options.add("help", Option::Switch, "h", "help");
184     options.add("load", Option::String, "L", "load");
185     options.add("local", Option::Switch, "l", "local");
186     options.add("locale", Option::String, "locale", "locale");
187     options.add("name", Option::String, "n", "name");
188     options.add("quit", Option::Switch, "q", "quit");
189     options.add("run", Option::String, "R", "run");
190     options.add("recover", Option::Switch, "recover", "recover");
191     options.add("restore", Option::Switch, "r", "restore");
192     options.add("shortcuts", Option::Switch, "s", "shortcuts");
193     options.add("testmode", Option::Switch, "t", "testmode");
194     options.add("version", Option::Switch, "v", "version");
195     options.setHelpText(
196         "VYM - View Your Mind\n"
197         "--------------------\n\n"
198         "Information about vym can be found in vym.pdf,\n"
199         "which should be part of the vym package.\n"
200         "It is also available at the project homepage:\n\n"
201         "http://www.InSilmaril.de/vym\n\n"
202         "Usage: vym [OPTION]... [FILE]... \n"
203         "Open FILEs with vym\n\n"
204         "-b           batch         batch mode: hide windows\n"
205         "-c           commands      List all available commands\n"
206         "-cl          commandslatex List commands in LaTeX format\n"
207         "-d           debug         Show debugging output\n"
208         "-h           help          Show this help text\n"
209         "-L           load          Load script\n"
210         "-l           local         Run with ressources in current directory\n"
211         "--locale     locale        Override system locale setting to select\n"
212         "                           language\n"
213         "-n  STRING   name          Set name of instance for DBus access\n"
214         "-q           quit          Quit immediatly after start for benchmarking\n"
215         "-R  FILE     run           Run script\n"
216         "-r           restore       Restore last session\n"
217         "--recover    recover       Delete lockfiles during initial loading of\n"
218         "                           files\n"
219         "-s           shortcuts     Show Keyboard shortcuts on start\n"
220         "-t           testmode      Test mode, e.g. no autosave and changing\n"
221         "                           of its setting\n"
222         "-v           version       Show vym version\n");
223
224     if (options.parse()) {
225         cout << endl << qPrintable(options.getHelpText()) << endl;
226         return 1;
227     }
228
229     if (options.isOn("version")) {
230         QString s = QString("VYM - View Your Mind (c) 2004-%1").arg(QDate::currentDate().year());
231         s += " Uwe Drechsel\n";
232         s += "   Version: " + vymVersion;
233         if (!vymCodeName.isEmpty())
234             s += QString(" - \"%1\"").arg(vymCodeName);
235         s += "\n";
236         s += "   Quality: " + vymCodeQuality + "\n";
237         s += "Build date: " + vymBuildDate + "\n";
238         cout << s.toStdString();
239
240         return 0;
241     }
242
243     taskModel = new TaskModel();
244
245     debug = options.isOn("debug");
246
247     testmode = options.isOn("testmode");
248
249     QString pidString = QString::number(QCoreApplication::applicationPid());
250
251 #if defined(VYM_DBUS)
252     // Register for DBUS
253     QDBusConnection dbusConnection = QDBusConnection::sessionBus();
254     if (!dbusConnection.registerService("org.insilmaril.vym-" + pidString)) {
255         fprintf(
256             stderr, "%s\n",
257             qPrintable(QDBusConnection::sessionBus().lastError().message()));
258         exit(1);
259     }
260 #endif
261
262     if (options.isOn("name"))
263         vymInstanceName = options.getArg("name");
264     else
265         vymInstanceName = pidString;
266
267 #ifdef QT_DEBUG
268     qDebug() << "QT_DEBUG is set";
269     debug = true;
270 #endif
271
272     // Use /usr/share/vym or /usr/local/share/vym or . ?
273     // First try options
274     if (options.isOn("local")) {
275         vymBaseDir.setPath(vymBaseDir.currentPath());
276     }
277     else
278         // then look for environment variable
279         if (getenv("VYMHOME") != 0) {
280         vymBaseDir.setPath(getenv("VYMHOME"));
281     }
282     else
283     // ok, let's find my way on my own
284     {
285 #if defined(Q_OS_MACX)
286         // Executable is in vym.app/Contents/MacOS, so go up first:
287         vymBaseDir = QCoreApplication::applicationDirPath();
288         vymBaseDir.cdUp();
289         vymBaseDir.cd("Resources");
290 #elif defined(Q_OS_WINDOWS)
291         vymBaseDir.setPath(QCoreApplication::applicationDirPath());
292 #else
293         vymBaseDir.setPath(VYMBASEDIR);
294 #endif
295     }
296
297     // Platform specific settings
298     vymPlatform = QSysInfo::prettyProductName();
299
300 #if defined(Q_OS_WINDOWS)
301     // Only Windows 10 has tar. Older windows versions not supported.
302     zipToolPath = "tar";
303 #else
304     zipToolPath = "/usr/bin/zip";
305     unzipToolPath = "/usr/bin/unzip";
306 #endif
307     iconPath = vymBaseDir.path() + "/icons/";
308     flagsPath = vymBaseDir.path() + "/flags/";
309
310     // When running locally, use local macros. Otherwise settings are used
311     if (options.isOn("local"))
312         macros.setPath(vymBaseDir.path() + "/macros/macros.vys");
313     else
314         macros.setPath(
315             settings
316             .value("/macros/path", vymBaseDir.path() + "/macros/macros.vys")
317             .toString());
318
319     // Some directories
320     QDir useDir;
321     if (options.isOn("local"))
322         useDir = QDir().current();
323     else
324         useDir = QDir().home();
325     lastImageDir = useDir;
326     lastMapDir = useDir;
327     lastExportDir = useDir;
328
329     if (options.isOn("help")) {
330         cout << qPrintable(options.getHelpText()) << endl;
331         return 0;
332     }
333
334     // Initialize translations
335     if (options.isOn("locale"))
336         localeName = options.getArg("locale");
337
338     // Use dark theme depending on system appearance and preferences
339     int text_hsv_value = app.palette().color(QPalette::WindowText).value();
340     int bg_hsv_value = app.palette().color(QPalette::Base).value();
341     bool systemSeemsDark = (text_hsv_value > bg_hsv_value);
342     QString settingsDarkTheme = settings.value("/system/darkTheme", "system").toString();
343     usingDarkTheme = false;
344     if (settingsDarkTheme != "never") {
345         if (settingsDarkTheme == "always" || (settingsDarkTheme == "system" && systemSeemsDark))
346             usingDarkTheme = true;
347     }
348
349 #if defined(Q_OS_WINDOWS)
350     if (usingDarkTheme) {
351         qApp->setStyle(QStyleFactory::create("fusion"));
352
353         // On Windows, there is no dark palette predefined, let's do that on our own
354         QPalette palette;
355         palette.setColor(QPalette::Window, QColor(53,53,53));
356         palette.setColor(QPalette::WindowText, Qt::white);
357         palette.setColor(QPalette::Base, QColor(27, 30, 32));
358         palette.setColor(QPalette::AlternateBase, QColor(53,53,53));
359         palette.setColor(QPalette::ToolTipBase, Qt::white);
360         palette.setColor(QPalette::ToolTipText, Qt::white);
361         palette.setColor(QPalette::Text, Qt::white);
362         palette.setColor(QPalette::Button, QColor(53,53,53));
363         palette.setColor(QPalette::ButtonText, Qt::white);
364         palette.setColor(QPalette::BrightText, Qt::red);
365         palette.setColor(QPalette::Highlight, QColor(142,45,197).lighter());
366         palette.setColor(QPalette::HighlightedText, Qt::black);
367         qApp->setPalette(palette);
368     }
369 #endif
370
371     // Prepare and check translations
372     vymTranslationsDir = QDir(vymBaseDir.path() + "/translations");
373     vymTranslationsDir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
374
375     bool translationsMissing = false;
376     if(!vymTranslationsDir.exists())
377         translationsMissing = true;
378     else if (vymTranslationsDir.isEmpty())
379         translationsMissing = true;
380
381     if (translationsMissing) {
382         WarningDialog warn;
383         warn.setMinimumWidth(800);
384         warn.setMinimumHeight(350);
385         warn.showCancelButton(false);
386         warn.setShowAgainName("mainwindow/translations/qmFilesMissing");
387         warn.setCaption("Translations not available");
388         warn.setText(
389                 "vym has not been built correctly and only will be available in English: \n\n"
390                 "No translation files in\n" +
391                 vymTranslationsDir.path().toLatin1() + "\n\n" +
392                 "Please get vym from\n"
393                 " * https://sourceforge.net/projects/vym/  or \n"
394                 " * https://software.opensuse.org//download.html?project=home%3Ainsilmaril&package=vym");
395         warn.exec();
396     } else {
397         bool ok;
398         if (!localeName.isEmpty())
399             // Use localeName to load specific language
400             ok = vymTranslator.load(QString("vym.%1.qm").arg(localeName), vymTranslationsDir.path());
401         else
402             ok = vymTranslator.load(QLocale(), "vym", ".", vymTranslationsDir.path(), ".qm");
403
404         if (!ok) {
405             WarningDialog warn;
406             warn.showCancelButton(false);
407             warn.setText(
408                 QString("Couldn't load translation for locale \"%1\" from\n%2")
409                     .arg(localeName)
410                     .arg(vymTranslationsDir.path()));
411             warn.setShowAgainName("mainwindow/translations/localeMissing");
412             warn.exec();
413         } else
414             QCoreApplication::installTranslator(&vymTranslator);
415     }
416
417     // Initializing the master rows of flags
418     systemFlagsMaster = new FlagRowMaster;
419     systemFlagsMaster->setName("systemFlagsMaster");
420
421     standardFlagsMaster = new FlagRowMaster;
422     standardFlagsMaster->setName("standardFlagsMaster");
423     standardFlagsMaster->setPrefix("standard/");
424
425     userFlagsMaster = new FlagRowMaster;
426     userFlagsMaster->setName("userFlagsMaster");
427     userFlagsMaster->setPrefix("user/");
428
429     // Initialize editors
430     noteEditor = new NoteEditor("noteeditor");
431     noteEditor->setWindowIcon(QPixmap(":/vym-editor.png"));
432     headingEditor = new HeadingEditor("headingeditor");
433     branchPropertyEditor = new BranchPropertyEditor();
434
435     // Initially read filenames of last session, before settings are 
436     // overwritten during loading of maps
437     lastSessionFiles = settings.value("/mainwindow/sessionFileList", QStringList()).toStringList();
438
439
440     Main m;
441
442     // Check for zip tools
443     checkZipTool();
444     checkUnzipTool();
445
446 #if defined(Q_OS_WINDOWS)
447     if (!zipToolAvailable || QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows10) {
448         QMessageBox::critical(
449             0, QObject::tr("Critical Error"),
450             QObject::tr("Couldn't find tool to unzip data,"
451                         "or your Windows version is older than Windows 10."));
452         m.settingsZipTool();
453     }
454 #else
455     if (!zipToolAvailable || !unzipToolAvailable) {
456         QMessageBox::critical(
457             0, QObject::tr("Critical Error"),
458             QObject::tr("Couldn't find tool to zip/unzip data. "
459                         "Please install on your platform and set"
460                         "path in Settings menu:\n ",
461                         "zip tool missing on Linux/Mac platform"));
462         m.settingsZipTool();
463     }
464 #endif
465
466     m.setWindowIcon(QPixmap(":/vym.png"));
467     m.fileNew();
468
469     if (debug)
470         // Show debug info AFTER creating MainWindow
471         cout << debugInfo().toStdString() << endl;
472
473     if (options.isOn("commands")) {
474         cout << "Available commands in map:\n";
475         cout << "=========================:\n";
476         foreach (Command *c, modelCommands)
477             cout << c->getDescription().toStdString() << endl;
478
479         cout << "Available commands in vym:\n";
480         cout << "=========================:\n";
481         foreach (Command *c, vymCommands)
482             cout << c->getDescription().toStdString() << endl;
483         return 0;
484     }
485
486     if (options.isOn("commandslatex")) {
487         foreach (Command *c, modelCommands)
488             cout << c->getDescriptionLaTeX().toStdString() << endl;
489         foreach (Command *c, vymCommands)
490             cout << c->getDescriptionLaTeX().toStdString() << endl;
491         return 0;
492     }
493
494     if (options.isOn("batch"))
495         m.hide();
496     else {
497         // Paint Mainwindow first time
498         qApp->processEvents();
499         m.show();
500     }
501
502     // Show release notes and afterwards updates
503     m.checkReleaseNotesAndUpdates();
504
505     if (options.isOn("shortcuts"))
506         switchboard
507             .printASCII(); // FIXME-3 global switchboard and exit after listing
508
509     m.loadCmdLine();
510
511     // Restore last session
512     if (options.isOn("restore"))
513         m.fileRestoreSession();
514
515     // Load script
516     if (options.isOn("load")) {
517         QString fn = options.getArg("load");
518         if (!scriptEditor->loadScript(fn)) {
519             QString error(QObject::tr("Error"));
520             QString msg(QObject::tr("Couldn't open \"%1\"\n.").arg(fn));
521             if (options.isOn("batch"))
522                 qWarning() << error + ": " + msg;
523             else
524                 QMessageBox::warning(0, error, msg);
525             return 0;
526         }
527     }
528
529     // Run script
530     if (options.isOn("run")) {
531         QString script;
532         QString fn = options.getArg("run");
533         if (!scriptEditor->loadScript(fn)) {
534             QString error(QObject::tr("Error"));
535             QString msg(QObject::tr("Couldn't open \"%1\"\n.").arg(fn));
536             if (options.isOn("batch"))
537                 qWarning() << error + ": " + msg;
538             else
539                 QMessageBox::warning(0, error, msg);
540             return 0;
541         }
542         m.runScript(scriptEditor->getScriptFile());
543     }
544
545     // For benchmarking we may want to quit instead of entering event loop
546     if (options.isOn("quit"))
547         return 0;
548
549     // Enable some last minute cleanup
550     QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
551
552     app.exec();
553
554     // Cleanup
555     delete noteEditor;
556
557     int s = warningCount + criticalCount + fatalCount;
558     if (s > 0)
559         qDebug() << "vym exiting with:\n"
560                  << warningCount << " warning messages\n"
561                  << criticalCount << " critical messages\n"
562                  << fatalCount << " fatal messages";
563     return s;
564 }