/
main.cpp
350 lines (252 loc) · 12.4 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*******************************************************************************
Copyright (C) The University of Auckland
OpenCOR is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenCOR is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
//==============================================================================
// Main
//==============================================================================
#include "checkforupdatesdialog.h"
#include "cliapplication.h"
#include "cliutils.h"
#include "guiapplication.h"
#include "guiutils.h"
#include "mainwindow.h"
#include "splashscreenwindow.h"
#ifdef Q_OS_MAC
#include "macos.h"
#endif
//==============================================================================
#include <QDir>
#include <QLocale>
#include <QProcess>
#include <QSettings>
#include <QVariant>
//---ISSUE1306--- REENABLE THE BELOW ONCE WE CAN BUILD QtWebKit...
//#ifdef Q_OS_WIN
// #include <QWebSettings>
//#endif
//==============================================================================
int main(int pArgC, char *pArgV[])
{
// Initialise Qt's message pattern
OpenCOR::initQtMessagePattern();
// On macOS, make sure that no ApplePersistenceIgnoreState message is shown
// and that some macOS specific menu items are not shown
#ifdef Q_OS_MAC
QProcess::execute("defaults",
QStringList() << "write"
<< "ws.opencor"
<< "ApplePersistenceIgnoreState"
<< "NO");
OpenCOR::removeMacosSpecificMenuItems();
#endif
// Determine whether we should try the CLI version of OpenCOR:
// - Windows: we never try the CLI version of OpenCOR. We go straight for
// its GUI version.
// - Linux: we always try the CLI version of OpenCOR and then go for its
// GUI version, if needed.
// - macOS: we try the CLI version of OpenCOR unless the user double clicks
// on the OpenCOR bundle or opens it from the command line by
// entering something like:
// open OpenCOR.app
// in which case we go for its GUI version.
// Note #1: on Windows, we have two binaries (.com and .exe that are for the
// CLI and GUI versions of OpenCOR, respectively). This means that
// when a console window is open, to enter something like:
// C:\>OpenCOR
// will effectively call OpenCOR.com. From there, should there be
// no argument that requires CLI treatment, then the GUI version of
// OpenCOR will be run. This is, unfortunately, the only way to
// have OpenCOR to behave as both a CLI and a GUI application on
// Windows, hence the [OpenCOR]/windows/main.cpp file, which is
// used to generate the CLI version of OpenCOR...
// Note #2: on macOS, if we were to try to open the OpenCOR bundle from the
// command line, then we would get an error message similar to:
// LSOpenURLsWithRole() failed with error -10810 for the file
// [SomePath]/OpenCOR.app.
// Fortunately, when double clicking on the OpenCOR bundle or
// opening it from the command line, a special argument in the form
// of -psn_0_1234567 is passed to OpenCOR, so we can use that to
// determine whether we need to force OpenCOR to be run in GUI mode
// or whether we first try the CLI version of OpenCOR, and then its
// GUI version, if needed...
#if defined(Q_OS_WIN)
bool tryCliVersion = false;
#elif defined(Q_OS_LINUX)
bool tryCliVersion = true;
#elif defined(Q_OS_MAC)
bool tryCliVersion = (pArgC == 1) || memcmp(pArgV[1], "-psn_", 5);
#else
#error Unsupported platform
#endif
// Run the CLI version of OpenCOR, if possible/needed
if (tryCliVersion) {
// Initialise the plugins path
OpenCOR::initPluginsPath(pArgV[0]);
// Create and initialise the CLI version of OpenCOR
OpenCOR::CliApplication *cliApp = new OpenCOR::CliApplication(pArgC, pArgV);
OpenCOR::initApplication();
// Try to run the CLI version of OpenCOR
int res;
bool runCliApplication = cliApp->run(&res);
delete cliApp;
if (runCliApplication) {
// OpenCOR was run as a CLI application, so leave
return res;
}
// Note: at this stage, we tried the CLI version of OpenCOR, but in the
// end we need to go for its GUI version, so start over but with
// the GUI version of OpenCOR this time...
}
// Initialise the plugins path
OpenCOR::initPluginsPath(pArgV[0]);
// Create the GUI version of OpenCOR
OpenCOR::GuiApplication *guiApp = new OpenCOR::GuiApplication(QFileInfo(pArgV[0]).baseName(),
pArgC, pArgV);
// Send a message (containing the arguments that were passed to this
// instance of OpenCOR minus the first one since it corresponds to the full
// path to our executable, which we are not interested in) to our 'official'
// instance of OpenCOR, should there be one (if there is none, then just
// carry on as normal, otherwise exit since we want only one instance of
// OpenCOR at any given time)
QStringList appArguments = guiApp->arguments();
appArguments.removeFirst();
QString arguments = appArguments.join('|');
if (guiApp->isRunning()) {
guiApp->sendMessage(arguments);
delete guiApp;
return 0;
}
// Initialise the GUI version of OpenCOR
QString appDate = QString();
OpenCOR::initApplication(&appDate);
// Check whether we want to check for new versions at startup and, if so,
// whether a new version of OpenCOR is available
#ifndef QT_DEBUG
QSettings settings;
settings.beginGroup(OpenCOR::SettingsCheckForUpdatesDialog);
bool checkForUpdatesAtStartup = settings.value(OpenCOR::SettingsCheckForUpdatesAtStartup, true).toBool();
bool includeSnapshots = settings.value(OpenCOR::SettingsIncludeSnapshots, false).toBool();
if (checkForUpdatesAtStartup) {
OpenCOR::CheckForUpdatesEngine *checkForUpdatesEngine = new OpenCOR::CheckForUpdatesEngine(appDate);
checkForUpdatesEngine->check();
if ( ( includeSnapshots && checkForUpdatesEngine->hasNewerVersion())
|| (!includeSnapshots && checkForUpdatesEngine->hasNewerOfficialVersion())) {
// Retrieve the language to be used to show the check for
// updates window
QString locale = OpenCOR::locale();
QLocale::setDefault(QLocale(locale));
QTranslator qtBaseTranslator;
QTranslator qtHelpTranslator;
QTranslator qtXmlPatternsTranslator;
QTranslator appTranslator;
qtBaseTranslator.load(QString(":/translations/qtbase_%1.qm").arg(locale));
guiApp->installTranslator(&qtBaseTranslator);
qtHelpTranslator.load(QString(":/translations/qt_help_%1.qm").arg(locale));
guiApp->installTranslator(&qtHelpTranslator);
qtXmlPatternsTranslator.load(QString(":/translations/qtxmlpatterns_%1.qm").arg(locale));
guiApp->installTranslator(&qtXmlPatternsTranslator);
appTranslator.load(":/app_"+locale);
guiApp->installTranslator(&appTranslator);
// Show the check for updates window
// Note: checkForUpdatesEngine gets deleted by
// checkForUpdatesDialog...
OpenCOR::CheckForUpdatesDialog checkForUpdatesDialog(&settings, checkForUpdatesEngine);
checkForUpdatesDialog.exec();
} else {
delete checkForUpdatesEngine;
}
}
settings.endGroup();
#endif
// Create and show our splash screen, if we are not in debug mode
#ifndef QT_DEBUG
OpenCOR::SplashScreenWindow *splashScreen = new OpenCOR::SplashScreenWindow();
splashScreen->show();
#endif
// Create our main window
OpenCOR::MainWindow *win = new OpenCOR::MainWindow(appDate);
// Keep track of our main window (required by QtSingleApplication so that it
// can do what it's supposed to be doing)
guiApp->setActivationWindow(win);
// Handle our arguments
win->handleArguments(appArguments);
// Show our main window
win->show();
// By default, we can and should execute our application
bool canExecuteAplication = true;
// Close and delete our splash screen once our main window is visible, if we
// are not in debug mode
#ifndef QT_DEBUG
splashScreen->closeAndDeleteAfter(win);
// Make sure that our main window is in the foreground, unless the user
// decided to close our main window while we were showing our splash screen
// Note: indeed, on Linux, to show our splash screen may result in our main
// window being shown in the background...
if (!OpenCOR::aboutToQuit())
win->showSelf();
else
canExecuteAplication = false;
#endif
// Execute our application, if possible
int res;
if (canExecuteAplication)
res = guiApp->exec();
else
res = 0;
// Keep track of our application file and directory paths (in case we need
// to restart OpenCOR)
QString appFilePath = guiApp->applicationFilePath();
QString appDirPath = guiApp->applicationDirPath();
// Delete our main window
delete win;
// We use QtWebKit, and QWebPage in particular, which results in some leak
// messages being generated on Windows when leaving OpenCOR. This is because
// an object cache is shared between all QWebPage instances. So to destroy a
// QWebPage instance doesn't clear the cache, hence the leak messages.
// However, those messages are 'only' warnings, so we can safely live with
// them. Still, it doesn't look 'good', so we clear the memory caches, thus
// avoiding those leak messages...
// Note: the below must absolutely be done after calling guiApp->exec() and
// before deleting guiApp...
//---ISSUE1306--- REENABLE THE BELOW ONCE WE CAN BUILD QtWebKit...
//#ifdef Q_OS_WIN
// QWebSettings::clearMemoryCaches();
//#endif
// Delete our application
delete guiApp;
// We are done with the execution of our application, so now the question is
// whether we need to restart
// Note: we do this here rather than 'within' the GUI because once we have
// launched a new instance of OpenCOR, we want this instance of
// OpenCOR to finish as soon as possible, which will be the case here
// since all that remains to be done is to return the result of the
// execution of our application...
if ((res == OpenCOR::CleanRestart) || (res == OpenCOR::NormalRestart)) {
// We want to restart, so the question is whether we want a normal
// restart or a clean one
if (res == OpenCOR::CleanRestart) {
// We want a clean restart, so clear all the user settings (indeed,
// this will ensure that the various windows are, for instance,
// properly reset with regards to their dimensions)
QSettings().clear();
}
// Restart OpenCOR, but without providing any of the arguments that were
// originally passed to us since we want to reset everything
QProcess::startDetached(appFilePath, QStringList(), appDirPath);
}
// We are done running the GUI version of OpenCOR, so leave
return res;
}
//==============================================================================
// End of file
//==============================================================================