FreeCAD

Форк
0
/
StartupProcess.cpp 
558 строк · 19.4 Кб
1
// SPDX-License-Identifier: LGPL-2.1-or-later
2

3
/***************************************************************************
4
 *   Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net>     *
5
 *                                                                         *
6
 *   This file is part of FreeCAD.                                         *
7
 *                                                                         *
8
 *   FreeCAD is free software: you can redistribute it and/or modify it    *
9
 *   under the terms of the GNU Lesser General Public License as           *
10
 *   published by the Free Software Foundation, either version 2.1 of the  *
11
 *   License, or (at your option) any later version.                       *
12
 *                                                                         *
13
 *   FreeCAD is distributed in the hope that it will be useful, but        *
14
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
15
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
16
 *   Lesser General Public License for more details.                       *
17
 *                                                                         *
18
 *   You should have received a copy of the GNU Lesser General Public      *
19
 *   License along with FreeCAD. If not, see                               *
20
 *   <https://www.gnu.org/licenses/>.                                      *
21
 *                                                                         *
22
 **************************************************************************/
23

24
#include "PreCompiled.h"
25
#ifndef _PreComp_
26
#include <QApplication>
27
#include <QDir>
28
#include <QImageReader>
29
#include <QLabel>
30
#include <QOpenGLContext>
31
#include <QOpenGLFunctions>
32
#include <QStatusBar>
33
#include <QWindow>
34
#include <Inventor/SoDB.h>
35
#endif
36

37
#include "StartupProcess.h"
38
#include "Application.h"
39
#include "AutoSaver.h"
40
#include "DlgCheckableMessageBox.h"
41
#include "FileDialog.h"
42
#include "GuiApplication.h"
43
#include "MainWindow.h"
44
#include "Language/Translator.h"
45
#include <App/Application.h>
46
#include <Base/Console.h>
47

48

49
using namespace Gui;
50

51

52
StartupProcess::StartupProcess() = default;
53

54
void StartupProcess::setupApplication()
55
{
56
    QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
57

58
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
59
    QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
60
#endif
61

62
    // Automatic scaling for legacy apps (disable once all parts of GUI are aware of HiDpi)
63
    ParameterGrp::handle hDPI =
64
        App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/HighDPI");
65
    bool disableDpiScaling = hDPI->GetBool("DisableDpiScaling", false);
66
    if (disableDpiScaling) {
67
#ifdef FC_OS_WIN32
68
        SetProcessDPIAware(); // call before the main event loop
69
#endif
70
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
71
        QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
72
#endif
73
    }
74
    else {
75
        // Enable automatic scaling based on pixel density of display (added in Qt 5.6)
76
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
77
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
78
#endif
79
#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) && defined(Q_OS_WIN)
80
        QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
81
#endif
82
    }
83

84
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
85
    //Enable support for highres images (added in Qt 5.1, but off by default)
86
    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
87
#endif
88

89
    // Use software rendering for OpenGL
90
    ParameterGrp::handle hOpenGL =
91
        App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/OpenGL");
92
    bool useSoftwareOpenGL = hOpenGL->GetBool("UseSoftwareOpenGL", false);
93
    if (useSoftwareOpenGL) {
94
        QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
95
    }
96

97
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
98
    // By default (on platforms that support it, see docs for
99
    // Qt::AA_CompressHighFrequencyEvents) QT applies compression
100
    // for high frequency events (mouse move, touch, window resizes)
101
    // to keep things smooth even when handling the event takes a
102
    // while (e.g. to calculate snapping).
103
    // However, tablet pen move events (and mouse move events
104
    // synthesised from those) are not compressed by default (to
105
    // allow maximum precision when e.g. hand-drawing curves),
106
    // leading to unacceptable slowdowns using a tablet pen. Enable
107
    // compression for tablet events here to solve that.
108
    QCoreApplication::setAttribute(Qt::AA_CompressTabletEvents);
109
#endif
110
}
111

112
void StartupProcess::execute()
113
{
114
    setLibraryPath();
115
    setStyleSheetPaths();
116
    setImagePaths();
117
    registerEventType();
118
    setThemePaths();
119
    setupFileDialog();
120
}
121

122
void StartupProcess::setLibraryPath()
123
{
124
    QString plugin;
125
    plugin = QString::fromStdString(App::Application::getHomePath());
126
    plugin += QLatin1String("/plugins");
127
    QCoreApplication::addLibraryPath(plugin);
128
}
129

130
void StartupProcess::setStyleSheetPaths()
131
{
132
    // setup the search paths for Qt style sheets
133
    QStringList qssPaths;
134
    qssPaths << QString::fromUtf8(
135
        (App::Application::getUserAppDataDir() + "Gui/Stylesheets/").c_str())
136
            << QString::fromUtf8((App::Application::getResourceDir() + "Gui/Stylesheets/").c_str())
137
            << QLatin1String(":/stylesheets");
138
    QDir::setSearchPaths(QString::fromLatin1("qss"), qssPaths);
139
    // setup the search paths for Qt overlay style sheets
140
    QStringList qssOverlayPaths;
141
    qssOverlayPaths << QString::fromUtf8((App::Application::getUserAppDataDir()
142
                        + "Gui/Stylesheets/overlay").c_str())
143
                    << QString::fromUtf8((App::Application::getResourceDir()
144
                        + "Gui/Stylesheets/overlay").c_str());
145
    QDir::setSearchPaths(QStringLiteral("overlay"), qssOverlayPaths);
146
}
147

148
void StartupProcess::setImagePaths()
149
{
150
    // set search paths for images
151
    QStringList imagePaths;
152
    imagePaths << QString::fromUtf8((App::Application::getUserAppDataDir() + "Gui/images").c_str())
153
            << QString::fromUtf8((App::Application::getUserAppDataDir() + "pixmaps").c_str())
154
            << QLatin1String(":/icons");
155
    QDir::setSearchPaths(QString::fromLatin1("images"), imagePaths);
156
}
157

158
void StartupProcess::registerEventType()
159
{
160
    // register action style event type
161
    ActionStyleEvent::EventType = QEvent::registerEventType(QEvent::User + 1);
162
}
163

164
void StartupProcess::setThemePaths()
165
{
166
#if !defined(Q_OS_LINUX)
167
    QIcon::setThemeSearchPaths(QIcon::themeSearchPaths()
168
                            << QString::fromLatin1(":/icons/FreeCAD-default"));
169
#endif
170

171
    ParameterGrp::handle hTheme = App::GetApplication().GetParameterGroupByPath(
172
        "User parameter:BaseApp/Preferences/Bitmaps/Theme");
173

174
    std::string searchpath = hTheme->GetASCII("SearchPath");
175
    if (!searchpath.empty()) {
176
        QStringList searchPaths = QIcon::themeSearchPaths();
177
        searchPaths.prepend(QString::fromUtf8(searchpath.c_str()));
178
        QIcon::setThemeSearchPaths(searchPaths);
179
    }
180

181
    std::string name = hTheme->GetASCII("Name");
182
    if (!name.empty()) {
183
        QIcon::setThemeName(QString::fromLatin1(name.c_str()));
184
    }
185
}
186

187
void StartupProcess::setupFileDialog()
188
{
189
#if defined(FC_OS_LINUX)
190
    // See #0001588
191
    QString path = FileDialog::restoreLocation();
192
    FileDialog::setWorkingDirectory(QDir::currentPath());
193
    FileDialog::saveLocation(path);
194
#else
195
    FileDialog::setWorkingDirectory(FileDialog::restoreLocation());
196
#endif
197
}
198

199
// ------------------------------------------------------------------------------------------------
200

201
StartupPostProcess::StartupPostProcess(MainWindow* mw, Application& guiApp, QApplication* app)
202
    : mainWindow{mw}
203
    , guiApp{guiApp}
204
    , qtApp(app)
205
{
206
}
207

208
void StartupPostProcess::setLoadFromPythonModule(bool value)
209
{
210
    loadFromPythonModule = value;
211
}
212

213
void StartupPostProcess::execute()
214
{
215
    setWindowTitle();
216
    setProcessMessages();
217
    setAutoSaving();
218
    setToolBarIconSize();
219
    setWheelEventFilter();
220
    setLocale();
221
    setCursorFlashing();
222
    setQtStyle();
223
    checkOpenGL();
224
    loadOpenInventor();
225
    setBranding();
226
    showMainWindow();
227
    activateWorkbench();
228
    checkParameters();
229
}
230

231
void StartupPostProcess::setWindowTitle()
232
{
233
    // empty window title QString sets default title (app + version)
234
    mainWindow->setWindowTitle(QString());
235
}
236

237
void StartupPostProcess::setProcessMessages()
238
{
239
    if (!loadFromPythonModule) {
240
        QObject::connect(qtApp, SIGNAL(messageReceived(const QList<QString> &)),
241
                         mainWindow, SLOT(processMessages(const QList<QString> &)));
242
    }
243
}
244

245
void StartupPostProcess::setAutoSaving()
246
{
247
    ParameterGrp::handle hDocGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
248
    int timeout = int(hDocGrp->GetInt("AutoSaveTimeout", 15L)); // 15 min
249
    if (!hDocGrp->GetBool("AutoSaveEnabled", true)) {
250
        timeout = 0;
251
    }
252

253
    AutoSaver::instance()->setTimeout(timeout * 60000);  // NOLINT
254
    AutoSaver::instance()->setCompressed(hDocGrp->GetBool("AutoSaveCompressed", true));
255
}
256

257
void StartupPostProcess::setToolBarIconSize()
258
{
259
    // set toolbar icon size
260
    ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
261
    int size = int(hGrp->GetInt("ToolbarIconSize", 0));
262
    // must not be lower than this
263
    if (size >= 16) {  // NOLINT
264
        mainWindow->setIconSize(QSize(size,size));
265
    }
266
}
267

268
void StartupPostProcess::setWheelEventFilter()
269
{
270
    // filter wheel events for combo boxes
271
    ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
272
    if (hGrp->GetBool("ComboBoxWheelEventFilter", false)) {
273
        auto filter = new WheelEventFilter(qtApp);
274
        qtApp->installEventFilter(filter);
275
    }
276
}
277

278
void StartupPostProcess::setLocale()
279
{
280
    // For values different to 1 and 2 use the OS locale settings
281
    ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
282
    auto localeFormat = hGrp->GetInt("UseLocaleFormatting", 0);
283
    if (localeFormat == 1) {
284
        Translator::instance()->setLocale(
285
            hGrp->GetASCII("Language", Translator::instance()->activeLanguage().c_str()));
286
    }
287
    else if (localeFormat == 2) {
288
        Translator::instance()->setLocale("C");
289
    }
290
}
291

292
void StartupPostProcess::setCursorFlashing()
293
{
294
    // set text cursor blinking state
295
    ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
296
    int blinkTime = hGrp->GetBool("EnableCursorBlinking", true) ? -1 : 0;
297
    QApplication::setCursorFlashTime(blinkTime);
298
}
299

300
void StartupPostProcess::setQtStyle()
301
{
302
    ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("MainWindow");
303
    auto qtStyle = hGrp->GetASCII("QtStyle");
304
    QApplication::setStyle(QString::fromStdString(qtStyle));
305
}
306

307
void StartupPostProcess::checkOpenGL()
308
{
309
    QWindow window;
310
    window.setSurfaceType(QWindow::OpenGLSurface);
311
    window.create();
312

313
    QOpenGLContext context;
314
    if (context.create()) {
315
        context.makeCurrent(&window);
316
        if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) {
317
            Base::Console().Log("This system does not support framebuffer objects\n");
318
        }
319
        if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
320
            Base::Console().Log("This system does not support NPOT textures\n");
321
        }
322

323
        int major = context.format().majorVersion();
324
        int minor = context.format().minorVersion();
325

326
#ifdef NDEBUG
327
        // In release mode, issue a warning to users that their version of OpenGL is
328
        // potentially going to cause problems
329
        if (major < 2) {
330
            auto message =
331
                QObject::tr("This system is running OpenGL %1.%2. "
332
                            "FreeCAD requires OpenGL 2.0 or above. "
333
                            "Please upgrade your graphics driver and/or card as required.")
334
                    .arg(major)
335
                    .arg(minor)
336
                + QStringLiteral("\n");
337
            Base::Console().Warning(message.toStdString().c_str());
338
            Dialog::DlgCheckableMessageBox::showMessage(
339
                QCoreApplication::applicationName() + QStringLiteral(" - ")
340
                    + QObject::tr("Invalid OpenGL Version"),
341
                message);
342
        }
343
#endif
344
        const char* glVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
345
        Base::Console().Log("OpenGL version is: %d.%d (%s)\n", major, minor, glVersion);
346
    }
347
}
348

349
void StartupPostProcess::loadOpenInventor()
350
{
351
    bool loadedInventor = false;
352
    if (loadFromPythonModule) {
353
        loadedInventor = SoDB::isInitialized();
354
    }
355

356
    if (!loadedInventor) {
357
        // init the Inventor subsystem
358
        Application::initOpenInventor();
359
    }
360
}
361

362
void StartupPostProcess::setBranding()
363
{
364
    QString home = QString::fromStdString(App::Application::getHomePath());
365

366
    const std::map<std::string,std::string>& cfg = App::Application::Config();
367
    std::map<std::string,std::string>::const_iterator it;
368
    it = cfg.find("WindowTitle");
369
    if (it != cfg.end()) {
370
        QString title = QString::fromUtf8(it->second.c_str());
371
        mainWindow->setWindowTitle(title);
372
    }
373
    it = cfg.find("WindowIcon");
374
    if (it != cfg.end()) {
375
        QString path = QString::fromUtf8(it->second.c_str());
376
        if (QDir(path).isRelative()) {
377
            path = QFileInfo(QDir(home), path).absoluteFilePath();
378
        }
379
        QApplication::setWindowIcon(QIcon(path));
380
    }
381
    it = cfg.find("ProgramLogo");
382
    if (it != cfg.end()) {
383
        QString path = QString::fromUtf8(it->second.c_str());
384
        if (QDir(path).isRelative()) {
385
            path = QFileInfo(QDir(home), path).absoluteFilePath();
386
        }
387
        QPixmap px(path);
388
        if (!px.isNull()) {
389
            auto logo = new QLabel();
390
            logo->setPixmap(px.scaledToHeight(32));
391
            mainWindow->statusBar()->addPermanentWidget(logo, 0);
392
            logo->setFrameShape(QFrame::NoFrame);
393
        }
394
    }
395
}
396

397
void StartupPostProcess::setImportImageFormats()
398
{
399
    QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
400
    std::stringstream str;
401
    str << "Image formats (";
402
    for (const auto& ext : supportedFormats) {
403
        str << "*." << ext.constData() << " *." << ext.toUpper().constData() << " ";
404
    }
405
    str << ")";
406

407
    std::string filter = str.str();
408
    App::GetApplication().addImportType(filter.c_str(), "FreeCADGui");
409
}
410

411
bool StartupPostProcess::hiddenMainWindow() const
412
{
413
    const std::map<std::string,std::string>& cfg = App::Application::Config();
414
    bool hidden = false;
415
    auto it = cfg.find("StartHidden");
416
    if (it != cfg.end()) {
417
        hidden = true;
418
    }
419

420
    return hidden;
421
}
422

423
void StartupPostProcess::showMainWindow()
424
{
425
    bool hidden = hiddenMainWindow();
426

427
    // show splasher while initializing the GUI
428
    if (!hidden && !loadFromPythonModule) {
429
        mainWindow->startSplasher();
430
    }
431

432
    // running the GUI init script
433
    try {
434
        Base::Console().Log("Run Gui init script\n");
435
        Application::runInitGuiScript();
436
        setImportImageFormats();
437
    }
438
    catch (const Base::Exception& e) {
439
        Base::Console().Error("Error in FreeCADGuiInit.py: %s\n", e.what());
440
        mainWindow->stopSplasher();
441
        throw;
442

443
    }
444

445
    // stop splash screen and set immediately the active window that may be of interest
446
    // for scripts using Python binding for Qt
447
    mainWindow->stopSplasher();
448
    mainWindow->activateWindow();
449
}
450

451
void StartupPostProcess::activateWorkbench()
452
{
453
    // Activate the correct workbench
454
    std::string start = App::Application::Config()["StartWorkbench"];
455
    Base::Console().Log("Init: Activating default workbench %s\n", start.c_str());
456
    std::string autoload =
457
        App::GetApplication()
458
            .GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
459
            ->GetASCII("AutoloadModule", start.c_str());
460
    if ("$LastModule" == autoload) {
461
        start = App::GetApplication()
462
                    .GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
463
                    ->GetASCII("LastModule", start.c_str());
464
    }
465
    else {
466
        start = autoload;
467
    }
468
    // if the auto workbench is not visible then force to use the default workbech
469
    // and replace the wrong entry in the parameters
470
    QStringList wb = guiApp.workbenches();
471
    if (!wb.contains(QString::fromLatin1(start.c_str()))) {
472
        start = App::Application::Config()["StartWorkbench"];
473
        if ("$LastModule" == autoload) {
474
            App::GetApplication()
475
                .GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
476
                ->SetASCII("LastModule", start.c_str());
477
        }
478
        else {
479
            App::GetApplication()
480
                .GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
481
                ->SetASCII("AutoloadModule", start.c_str());
482
        }
483
    }
484

485
    // Call this before showing the main window because otherwise:
486
    // 1. it shows a white window for a few seconds which doesn't look nice
487
    // 2. the layout of the toolbars is completely broken
488
    guiApp.activateWorkbench(start.c_str());
489

490
    // show the main window
491
    if (!hiddenMainWindow()) {
492
        Base::Console().Log("Init: Showing main window\n");
493
        mainWindow->loadWindowSettings();
494
    }
495

496
    //initialize spaceball.
497
    if (auto fcApp = qobject_cast<GUIApplicationNativeEventAware*>(qtApp)) {
498
        fcApp->initSpaceball(mainWindow);
499
    }
500

501
    setStyleSheet();
502

503
    // Now run the background autoload, for workbenches that should be loaded at startup, but not
504
    // displayed to the user immediately
505
    autoloadModules(wb);
506

507
    // Reactivate the startup workbench
508
    guiApp.activateWorkbench(start.c_str());
509
}
510

511
void StartupPostProcess::setStyleSheet()
512
{
513
    ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
514
        "User parameter:BaseApp/Preferences/MainWindow");
515
    std::string style = hGrp->GetASCII("StyleSheet");
516
    if (style.empty()) {
517
        // check the branding settings
518
        const auto& config = App::Application::Config();
519
        auto it = config.find("StyleSheet");
520
        if (it != config.end()) {
521
            style = it->second;
522
        }
523
    }
524

525
    guiApp.setStyleSheet(QLatin1String(style.c_str()), hGrp->GetBool("TiledBackground", false));
526
}
527

528
void StartupPostProcess::autoloadModules(const QStringList& wb)
529
{
530
    // Now run the background autoload, for workbenches that should be loaded at startup, but not
531
    // displayed to the user immediately
532
    std::string autoloadCSV =
533
        App::GetApplication()
534
            .GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
535
            ->GetASCII("BackgroundAutoloadModules", "");
536

537
    // Tokenize the comma-separated list and load the requested workbenches if they exist in this
538
    // installation
539
    std::stringstream stream(autoloadCSV);
540
    std::string workbench;
541
    while (std::getline(stream, workbench, ',')) {
542
        if (wb.contains(QString::fromLatin1(workbench.c_str()))) {
543
            guiApp.activateWorkbench(workbench.c_str());
544
        }
545
    }
546
}
547

548
void StartupPostProcess::checkParameters()
549
{
550
    if (App::GetApplication().GetSystemParameter().IgnoreSave()) {
551
        Base::Console().Warning("System parameter file couldn't be opened.\n"
552
                                "Continue with an empty configuration that won't be saved.\n");
553
    }
554
    if (App::GetApplication().GetUserParameter().IgnoreSave()) {
555
        Base::Console().Warning("User parameter file couldn't be opened.\n"
556
                                "Continue with an empty configuration that won't be saved.\n");
557
    }
558
}
559

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.