Gui: refactor Application::runApplication
This commit is contained in:
@@ -29,7 +29,6 @@
|
||||
# include <QCloseEvent>
|
||||
# include <QDir>
|
||||
# include <QFileInfo>
|
||||
# include <QImageReader>
|
||||
# include <QLocale>
|
||||
# include <QMessageBox>
|
||||
# include <QMessageLogContext>
|
||||
@@ -60,7 +59,6 @@
|
||||
#include <Quarter/Quarter.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "AutoSaver.h"
|
||||
#include "AxisOriginPy.h"
|
||||
#include "BitmapFactory.h"
|
||||
#include "Command.h"
|
||||
@@ -68,7 +66,6 @@
|
||||
#include "CommandPy.h"
|
||||
#include "Control.h"
|
||||
#include "PreferencePages/DlgSettingsCacheDirectory.h"
|
||||
#include "DlgCheckableMessageBox.h"
|
||||
#include "DocumentPy.h"
|
||||
#include "DocumentRecovery.h"
|
||||
#include "EditorView.h"
|
||||
@@ -89,6 +86,7 @@
|
||||
#include "SelectionFilterPy.h"
|
||||
#include "SoQtOffscreenRendererPy.h"
|
||||
#include "SplitView3DInventor.h"
|
||||
#include "StartupProcess.h"
|
||||
#include "TaskView/TaskView.h"
|
||||
#include "TaskView/TaskDialogPython.h"
|
||||
#include "TransactionObject.h"
|
||||
@@ -140,357 +138,8 @@ namespace sp = std::placeholders;
|
||||
|
||||
Application* Application::Instance = nullptr;
|
||||
|
||||
namespace {
|
||||
void setImportImageFormats();
|
||||
}
|
||||
|
||||
#ifdef FC_DEBUG // redirect Coin messages to FreeCAD
|
||||
void messageHandlerCoin(const SoError * error, void * /*userdata*/);
|
||||
#endif
|
||||
|
||||
|
||||
namespace Gui {
|
||||
|
||||
|
||||
|
||||
void initGuiAppPreMainWindow(bool calledByGuiPy)
|
||||
{
|
||||
QString plugin;
|
||||
plugin = QString::fromStdString(App::Application::getHomePath());
|
||||
plugin += QLatin1String("/plugins");
|
||||
QCoreApplication::addLibraryPath(plugin);
|
||||
|
||||
// setup the search paths for Qt style sheets
|
||||
QStringList qssPaths;
|
||||
qssPaths << QString::fromUtf8(
|
||||
(App::Application::getUserAppDataDir() + "Gui/Stylesheets/").c_str())
|
||||
<< QString::fromUtf8((App::Application::getResourceDir() + "Gui/Stylesheets/").c_str())
|
||||
<< QLatin1String(":/stylesheets");
|
||||
QDir::setSearchPaths(QString::fromLatin1("qss"), qssPaths);
|
||||
// setup the search paths for Qt overlay style sheets
|
||||
QStringList qssOverlayPaths;
|
||||
qssOverlayPaths << QString::fromUtf8((App::Application::getUserAppDataDir()
|
||||
+ "Gui/Stylesheets/overlay").c_str())
|
||||
<< QString::fromUtf8((App::Application::getResourceDir()
|
||||
+ "Gui/Stylesheets/overlay").c_str());
|
||||
QDir::setSearchPaths(QStringLiteral("overlay"), qssOverlayPaths);
|
||||
|
||||
// set search paths for images
|
||||
QStringList imagePaths;
|
||||
imagePaths << QString::fromUtf8((App::Application::getUserAppDataDir() + "Gui/images").c_str())
|
||||
<< QString::fromUtf8((App::Application::getUserAppDataDir() + "pixmaps").c_str())
|
||||
<< QLatin1String(":/icons");
|
||||
QDir::setSearchPaths(QString::fromLatin1("images"), imagePaths);
|
||||
|
||||
// register action style event type
|
||||
ActionStyleEvent::EventType = QEvent::registerEventType(QEvent::User + 1);
|
||||
|
||||
ParameterGrp::handle hTheme = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/Bitmaps/Theme");
|
||||
#if !defined(Q_OS_LINUX)
|
||||
QIcon::setThemeSearchPaths(QIcon::themeSearchPaths()
|
||||
<< QString::fromLatin1(":/icons/FreeCAD-default"));
|
||||
QIcon::setThemeName(QLatin1String("FreeCAD-default"));
|
||||
#else
|
||||
// Option to opt-out from using a Linux desktop icon theme.
|
||||
// https://forum.freecad.org/viewtopic.php?f=4&t=35624
|
||||
bool themePaths = hTheme->GetBool("ThemeSearchPaths",true);
|
||||
if (!themePaths) {
|
||||
QStringList searchPaths;
|
||||
searchPaths.prepend(QString::fromUtf8(":/icons"));
|
||||
QIcon::setThemeSearchPaths(searchPaths);
|
||||
QIcon::setThemeName(QLatin1String("FreeCAD-default"));
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string searchpath = hTheme->GetASCII("SearchPath");
|
||||
if (!searchpath.empty()) {
|
||||
QStringList searchPaths = QIcon::themeSearchPaths();
|
||||
searchPaths.prepend(QString::fromUtf8(searchpath.c_str()));
|
||||
QIcon::setThemeSearchPaths(searchPaths);
|
||||
}
|
||||
|
||||
std::string name = hTheme->GetASCII("Name");
|
||||
if (!name.empty()) {
|
||||
QIcon::setThemeName(QString::fromLatin1(name.c_str()));
|
||||
}
|
||||
|
||||
#if defined(FC_OS_LINUX)
|
||||
// See #0001588
|
||||
QString path = FileDialog::restoreLocation();
|
||||
FileDialog::setWorkingDirectory(QDir::currentPath());
|
||||
FileDialog::saveLocation(path);
|
||||
#else
|
||||
FileDialog::setWorkingDirectory(FileDialog::restoreLocation());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void initGuiAppPostMainWindow(bool calledByGuiPy, QApplication &mApp, MainWindow &mw, GUIApplicationNativeEventAware *pmAppNativeEventAware)
|
||||
{
|
||||
Application &app = *Gui::Application::Instance;
|
||||
|
||||
// allow to disable version number
|
||||
ParameterGrp::handle hGen =
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General");
|
||||
bool showVersion = hGen->GetBool("ShowVersionInTitle", true);
|
||||
|
||||
if (showVersion) {
|
||||
// set main window title with FreeCAD Version
|
||||
std::map<std::string, std::string>& config = App::Application::Config();
|
||||
QString major = QString::fromLatin1(config["BuildVersionMajor"].c_str());
|
||||
QString minor = QString::fromLatin1(config["BuildVersionMinor"].c_str());
|
||||
QString point = QString::fromLatin1(config["BuildVersionPoint"].c_str());
|
||||
QString suffix = QString::fromLatin1(config["BuildVersionSuffix"].c_str());
|
||||
QString title =
|
||||
QString::fromLatin1("%1 %2.%3.%4%5").arg(mApp.applicationName(), major, minor, point, suffix);
|
||||
mw.setWindowTitle(title);
|
||||
}
|
||||
else {
|
||||
QString title = mApp.applicationName();
|
||||
if (title.isEmpty()) {
|
||||
title = QString::fromLatin1(App::Application::Config()["ExeName"].c_str());
|
||||
}
|
||||
mw.setWindowTitle(title);
|
||||
}
|
||||
|
||||
if (pmAppNativeEventAware != nullptr) {
|
||||
QObject::connect(&mApp, SIGNAL(messageReceived(const QList<QByteArray> &)),
|
||||
&mw, SLOT(processMessages(const QList<QByteArray> &)));
|
||||
}
|
||||
|
||||
ParameterGrp::handle hDocGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
|
||||
int timeout = hDocGrp->GetInt("AutoSaveTimeout", 15); // 15 min
|
||||
if (!hDocGrp->GetBool("AutoSaveEnabled", true))
|
||||
timeout = 0;
|
||||
AutoSaver::instance()->setTimeout(timeout * 60000);
|
||||
AutoSaver::instance()->setCompressed(hDocGrp->GetBool("AutoSaveCompressed", true));
|
||||
|
||||
// set toolbar icon size
|
||||
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
|
||||
int size = hGrp->GetInt("ToolbarIconSize", 0);
|
||||
if (size >= 16) // must not be lower than this
|
||||
mw.setIconSize(QSize(size,size));
|
||||
|
||||
// filter wheel events for combo boxes
|
||||
if (hGrp->GetBool("ComboBoxWheelEventFilter", false)) {
|
||||
auto filter = new WheelEventFilter(&mApp);
|
||||
mApp.installEventFilter(filter);
|
||||
}
|
||||
|
||||
// For values different to 1 and 2 use the OS locale settings
|
||||
auto localeFormat = hGrp->GetInt("UseLocaleFormatting", 0);
|
||||
if (localeFormat == 1) {
|
||||
Translator::instance()->setLocale(
|
||||
hGrp->GetASCII("Language", Translator::instance()->activeLanguage().c_str()));
|
||||
}
|
||||
else if (localeFormat == 2) {
|
||||
Translator::instance()->setLocale("C");
|
||||
}
|
||||
|
||||
// set text cursor blinking state
|
||||
int blinkTime = hGrp->GetBool("EnableCursorBlinking", true) ? -1 : 0;
|
||||
qApp->setCursorFlashTime(blinkTime);
|
||||
|
||||
{
|
||||
QWindow window;
|
||||
window.setSurfaceType(QWindow::OpenGLSurface);
|
||||
window.create();
|
||||
|
||||
QOpenGLContext context;
|
||||
if (context.create()) {
|
||||
context.makeCurrent(&window);
|
||||
if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) {
|
||||
Base::Console().Log("This system does not support framebuffer objects\n");
|
||||
}
|
||||
if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
|
||||
Base::Console().Log("This system does not support NPOT textures\n");
|
||||
}
|
||||
|
||||
int major = context.format().majorVersion();
|
||||
int minor = context.format().minorVersion();
|
||||
|
||||
#ifdef NDEBUG
|
||||
// In release mode, issue a warning to users that their version of OpenGL is
|
||||
// potentially going to cause problems
|
||||
if (major < 2) {
|
||||
auto message =
|
||||
QObject::tr("This system is running OpenGL %1.%2. "
|
||||
"FreeCAD requires OpenGL 2.0 or above. "
|
||||
"Please upgrade your graphics driver and/or card as required.")
|
||||
.arg(major)
|
||||
.arg(minor)
|
||||
+ QStringLiteral("\n");
|
||||
Base::Console().Warning(message.toStdString().c_str());
|
||||
Dialog::DlgCheckableMessageBox::showMessage(
|
||||
Gui::GUISingleApplication::applicationName() + QStringLiteral(" - ")
|
||||
+ QObject::tr("Invalid OpenGL Version"),
|
||||
message);
|
||||
}
|
||||
#endif
|
||||
const char* glVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
Base::Console().Log("OpenGL version is: %d.%d (%s)\n", major, minor, glVersion);
|
||||
}
|
||||
}
|
||||
|
||||
if (!calledByGuiPy) {
|
||||
assert(!SoDB::isInitialized());
|
||||
}
|
||||
if (!SoDB::isInitialized()) {
|
||||
// init the Inventor subsystem
|
||||
Application::initOpenInventor();
|
||||
}
|
||||
|
||||
QString home = QString::fromStdString(App::Application::getHomePath());
|
||||
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
std::map<std::string,std::string>::const_iterator it;
|
||||
it = cfg.find("WindowTitle");
|
||||
if (it != cfg.end()) {
|
||||
QString title = QString::fromUtf8(it->second.c_str());
|
||||
mw.setWindowTitle(title);
|
||||
}
|
||||
it = cfg.find("WindowIcon");
|
||||
if (it != cfg.end()) {
|
||||
QString path = QString::fromUtf8(it->second.c_str());
|
||||
if (QDir(path).isRelative()) {
|
||||
path = QFileInfo(QDir(home), path).absoluteFilePath();
|
||||
}
|
||||
QApplication::setWindowIcon(QIcon(path));
|
||||
}
|
||||
it = cfg.find("ProgramLogo");
|
||||
if (it != cfg.end()) {
|
||||
QString path = QString::fromUtf8(it->second.c_str());
|
||||
if (QDir(path).isRelative()) {
|
||||
path = QFileInfo(QDir(home), path).absoluteFilePath();
|
||||
}
|
||||
QPixmap px(path);
|
||||
if (!px.isNull()) {
|
||||
auto logo = new QLabel();
|
||||
logo->setPixmap(px.scaledToHeight(32));
|
||||
mw.statusBar()->addPermanentWidget(logo, 0);
|
||||
logo->setFrameShape(QFrame::NoFrame);
|
||||
}
|
||||
}
|
||||
bool hidden = false;
|
||||
it = cfg.find("StartHidden");
|
||||
if (it != cfg.end()) {
|
||||
hidden = true;
|
||||
}
|
||||
|
||||
if (calledByGuiPy) {
|
||||
assert(!hidden);
|
||||
}
|
||||
|
||||
// show splasher while initializing the GUI
|
||||
if (!hidden)
|
||||
mw.startSplasher();
|
||||
|
||||
// running the GUI init script
|
||||
try {
|
||||
Base::Console().Log("Run Gui init script\n");
|
||||
Application::runInitGuiScript();
|
||||
setImportImageFormats();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Error in FreeCADGuiInit.py: %s\n", e.what());
|
||||
mw.stopSplasher();
|
||||
throw;
|
||||
|
||||
}
|
||||
|
||||
// stop splash screen and set immediately the active window that may be of interest
|
||||
// for scripts using Python binding for Qt
|
||||
mw.stopSplasher();
|
||||
mApp.setActiveWindow(&mw);
|
||||
|
||||
// Activate the correct workbench
|
||||
std::string start = App::Application::Config()["StartWorkbench"];
|
||||
Base::Console().Log("Init: Activating default workbench %s\n", start.c_str());
|
||||
std::string autoload =
|
||||
App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->GetASCII("AutoloadModule", start.c_str());
|
||||
if ("$LastModule" == autoload) {
|
||||
start = App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->GetASCII("LastModule", start.c_str());
|
||||
}
|
||||
else {
|
||||
start = autoload;
|
||||
}
|
||||
// if the auto workbench is not visible then force to use the default workbech
|
||||
// and replace the wrong entry in the parameters
|
||||
QStringList wb = app.workbenches();
|
||||
if (!wb.contains(QString::fromLatin1(start.c_str()))) {
|
||||
start = App::Application::Config()["StartWorkbench"];
|
||||
if ("$LastModule" == autoload) {
|
||||
App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->SetASCII("LastModule", start.c_str());
|
||||
}
|
||||
else {
|
||||
App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->SetASCII("AutoloadModule", start.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Call this before showing the main window because otherwise:
|
||||
// 1. it shows a white window for a few seconds which doesn't look nice
|
||||
// 2. the layout of the toolbars is completely broken
|
||||
app.activateWorkbench(start.c_str());
|
||||
|
||||
// show the main window
|
||||
if (!hidden) {
|
||||
Base::Console().Log("Init: Showing main window\n");
|
||||
mw.loadWindowSettings();
|
||||
}
|
||||
|
||||
//initialize spaceball.
|
||||
if (pmAppNativeEventAware != nullptr) {
|
||||
pmAppNativeEventAware->initSpaceball(&mw);
|
||||
}
|
||||
|
||||
#ifdef FC_DEBUG // redirect Coin messages to FreeCAD
|
||||
SoDebugError::setHandlerCallback( messageHandlerCoin, 0 );
|
||||
#endif
|
||||
|
||||
hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/MainWindow");
|
||||
std::string style = hGrp->GetASCII("StyleSheet");
|
||||
if (style.empty()) {
|
||||
// check the branding settings
|
||||
const auto& config = App::Application::Config();
|
||||
auto it = config.find("StyleSheet");
|
||||
if (it != config.end())
|
||||
style = it->second;
|
||||
}
|
||||
|
||||
app.setStyleSheet(QLatin1String(style.c_str()), hGrp->GetBool("TiledBackground", false));
|
||||
|
||||
// Now run the background autoload, for workbenches that should be loaded at startup, but not
|
||||
// displayed to the user immediately
|
||||
std::string autoloadCSV =
|
||||
App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->GetASCII("BackgroundAutoloadModules", "");
|
||||
|
||||
// Tokenize the comma-separated list and load the requested workbenches if they exist in this
|
||||
// installation
|
||||
std::vector<std::string> backgroundAutoloadedModules;
|
||||
std::stringstream stream(autoloadCSV);
|
||||
std::string workbench;
|
||||
while (std::getline(stream, workbench, ',')) {
|
||||
if (wb.contains(QString::fromLatin1(workbench.c_str())))
|
||||
app.activateWorkbench(workbench.c_str());
|
||||
}
|
||||
|
||||
// Reactivate the startup workbench
|
||||
app.activateWorkbench(start.c_str());
|
||||
}
|
||||
|
||||
|
||||
class ViewProviderMap {
|
||||
std::unordered_map<const App::DocumentObject *, ViewProvider *> map;
|
||||
|
||||
@@ -689,22 +338,6 @@ struct PyMethodDef FreeCADGui_methods[] = {
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
namespace {
|
||||
void setImportImageFormats()
|
||||
{
|
||||
QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
|
||||
std::stringstream str;
|
||||
str << "Image formats (";
|
||||
for (const auto& ext : supportedFormats) {
|
||||
str << "*." << ext.constData() << " *." << ext.toUpper().constData() << " ";
|
||||
}
|
||||
str << ")";
|
||||
|
||||
std::string filter = str.str();
|
||||
App::GetApplication().addImportType(filter.c_str(), "FreeCADGui");
|
||||
}
|
||||
}
|
||||
|
||||
Application::Application(bool GUIenabled)
|
||||
{
|
||||
//App::GetApplication().Attach(this);
|
||||
@@ -2312,83 +1945,11 @@ void Application::runInitGuiScript()
|
||||
Base::Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADGuiInit"));
|
||||
}
|
||||
|
||||
void Application::runApplication()
|
||||
namespace {
|
||||
bool onlySingleInstance(GUISingleApplication& mainApp)
|
||||
{
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
std::map<std::string,std::string>::const_iterator it;
|
||||
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
|
||||
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
|
||||
#endif
|
||||
|
||||
// Automatic scaling for legacy apps (disable once all parts of GUI are aware of HiDpi)
|
||||
ParameterGrp::handle hDPI =
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/HighDPI");
|
||||
bool disableDpiScaling = hDPI->GetBool("DisableDpiScaling", false);
|
||||
if (disableDpiScaling) {
|
||||
#ifdef FC_OS_WIN32
|
||||
SetProcessDPIAware(); // call before the main event loop
|
||||
#endif
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// Enable automatic scaling based on pixel density of display (added in Qt 5.6)
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
#endif
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) && defined(Q_OS_WIN)
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
//Enable support for highres images (added in Qt 5.1, but off by default)
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
|
||||
// Use software rendering for OpenGL
|
||||
ParameterGrp::handle hOpenGL =
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/OpenGL");
|
||||
bool useSoftwareOpenGL = hOpenGL->GetBool("UseSoftwareOpenGL", false);
|
||||
if (useSoftwareOpenGL) {
|
||||
QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||
// By default (on platforms that support it, see docs for
|
||||
// Qt::AA_CompressHighFrequencyEvents) QT applies compression
|
||||
// for high frequency events (mouse move, touch, window resizes)
|
||||
// to keep things smooth even when handling the event takes a
|
||||
// while (e.g. to calculate snapping).
|
||||
// However, tablet pen move events (and mouse move events
|
||||
// synthesised from those) are not compressed by default (to
|
||||
// allow maximum precision when e.g. hand-drawing curves),
|
||||
// leading to unacceptable slowdowns using a tablet pen. Enable
|
||||
// compression for tablet events here to solve that.
|
||||
QCoreApplication::setAttribute(Qt::AA_CompressTabletEvents);
|
||||
#endif
|
||||
|
||||
// A new QApplication
|
||||
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
|
||||
|
||||
// if application not yet created by the splasher
|
||||
int argc = App::Application::GetARGC();
|
||||
GUISingleApplication mainApp(argc, App::Application::GetARGV());
|
||||
// https://forum.freecad.org/viewtopic.php?f=3&t=15540
|
||||
mainApp.setAttribute(Qt::AA_DontShowIconsInMenus, false);
|
||||
|
||||
// Make sure that we use '.' as decimal point. See also
|
||||
// http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=559846
|
||||
// and issue #0002891
|
||||
// http://doc.qt.io/qt-5/qcoreapplication.html#locale-settings
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
// check if a single or multiple instances can run
|
||||
it = cfg.find("SingleInstance");
|
||||
auto it = cfg.find("SingleInstance");
|
||||
if (it != cfg.end() && mainApp.isRunning()) {
|
||||
// send the file names to be opened to the server application so that this
|
||||
// opens them
|
||||
@@ -2411,81 +1972,79 @@ void Application::runApplication()
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void setAppNameAndIcon()
|
||||
{
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
|
||||
// set application icon and window title
|
||||
it = cfg.find("Application");
|
||||
auto it = cfg.find("Application");
|
||||
if (it != cfg.end()) {
|
||||
mainApp.setApplicationName(QString::fromUtf8(it->second.c_str()));
|
||||
QApplication::setApplicationName(QString::fromUtf8(it->second.c_str()));
|
||||
}
|
||||
else {
|
||||
mainApp.setApplicationName(QString::fromStdString(App::Application::getExecutableName()));
|
||||
QApplication::setApplicationName(QString::fromStdString(App::Application::getExecutableName()));
|
||||
}
|
||||
#ifndef Q_OS_MACX
|
||||
mainApp.setWindowIcon(
|
||||
QApplication::setWindowIcon(
|
||||
Gui::BitmapFactory().pixmap(App::Application::Config()["AppIcon"].c_str()));
|
||||
#endif
|
||||
}
|
||||
|
||||
initGuiAppPreMainWindow(false);
|
||||
|
||||
Application app(true);
|
||||
MainWindow mw;
|
||||
mw.setProperty("QuitOnClosed", true);
|
||||
void tryRunEventLoop(GUISingleApplication& mainApp)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << App::Application::getUserCachePath() << App::Application::getExecutableName()
|
||||
<< "_" << QCoreApplication::applicationPid() << ".lock";
|
||||
// open a lock file with the PID
|
||||
Base::FileInfo fi(s.str());
|
||||
Base::ofstream lock(fi);
|
||||
|
||||
initGuiAppPostMainWindow(false, mainApp, mw, &mainApp);
|
||||
|
||||
Instance->d->startingUp = false;
|
||||
|
||||
// gets called once we start the event loop
|
||||
QTimer::singleShot(0, &mw, SLOT(delayedStartup()));
|
||||
|
||||
// run the Application event loop
|
||||
Base::Console().Log("Init: Entering event loop\n");
|
||||
|
||||
// boot phase reference point
|
||||
// https://forum.freecad.org/viewtopic.php?f=10&t=21665
|
||||
Gui::getMainWindow()->setProperty("eventLoop", true);
|
||||
|
||||
try {
|
||||
std::stringstream s;
|
||||
s << App::Application::getUserCachePath() << App::Application::getExecutableName()
|
||||
<< "_" << QCoreApplication::applicationPid() << ".lock";
|
||||
// open a lock file with the PID
|
||||
Base::FileInfo fi(s.str());
|
||||
Base::ofstream lock(fi);
|
||||
|
||||
// In case the file_lock cannot be created start FreeCAD without IPC support.
|
||||
// In case the file_lock cannot be created start FreeCAD without IPC support.
|
||||
#if !defined(FC_OS_WIN32) || (BOOST_VERSION < 107600)
|
||||
std::string filename = s.str();
|
||||
std::string filename = s.str();
|
||||
#else
|
||||
std::wstring filename = fi.toStdWString();
|
||||
std::wstring filename = fi.toStdWString();
|
||||
#endif
|
||||
std::unique_ptr<boost::interprocess::file_lock> flock;
|
||||
try {
|
||||
flock = std::make_unique<boost::interprocess::file_lock>(filename.c_str());
|
||||
flock->lock();
|
||||
}
|
||||
catch (const boost::interprocess::interprocess_exception& e) {
|
||||
QString msg = QString::fromLocal8Bit(e.what());
|
||||
Base::Console().Warning("Failed to create a file lock for the IPC: %s\n",
|
||||
msg.toUtf8().constData());
|
||||
}
|
||||
std::unique_ptr<boost::interprocess::file_lock> flock;
|
||||
try {
|
||||
flock = std::make_unique<boost::interprocess::file_lock>(filename.c_str());
|
||||
flock->lock();
|
||||
}
|
||||
catch (const boost::interprocess::interprocess_exception& e) {
|
||||
QString msg = QString::fromLocal8Bit(e.what());
|
||||
Base::Console().Warning("Failed to create a file lock for the IPC: %s\n",
|
||||
msg.toUtf8().constData());
|
||||
}
|
||||
|
||||
Base::Console().Log("Init: Executing event loop...\n");
|
||||
mainApp.exec();
|
||||
Base::Console().Log("Init: Executing event loop...\n");
|
||||
QApplication::exec();
|
||||
|
||||
// Qt can't handle exceptions thrown from event handlers, so we need
|
||||
// to manually rethrow SystemExitExceptions.
|
||||
if (mainApp.caughtException.get())
|
||||
throw Base::SystemExitException(*mainApp.caughtException.get());
|
||||
// Qt can't handle exceptions thrown from event handlers, so we need
|
||||
// to manually rethrow SystemExitExceptions.
|
||||
if (mainApp.caughtException) {
|
||||
throw Base::SystemExitException(*mainApp.caughtException.get());
|
||||
}
|
||||
|
||||
// close the lock file, in case of a crash we can see the existing lock file
|
||||
// on the next restart and try to repair the documents, if needed.
|
||||
if (flock.get())
|
||||
flock->unlock();
|
||||
lock.close();
|
||||
fi.deleteFile();
|
||||
// close the lock file, in case of a crash we can see the existing lock file
|
||||
// on the next restart and try to repair the documents, if needed.
|
||||
if (flock) {
|
||||
flock->unlock();
|
||||
}
|
||||
lock.close();
|
||||
fi.deleteFile();
|
||||
}
|
||||
|
||||
void runEventLoop(GUISingleApplication& mainApp)
|
||||
{
|
||||
try {
|
||||
tryRunEventLoop(mainApp);
|
||||
}
|
||||
catch (const Base::SystemExitException&) {
|
||||
Base::Console().Message("System exit\n");
|
||||
@@ -2503,6 +2062,62 @@ void Application::runApplication()
|
||||
App::Application::destructObserver();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::runApplication()
|
||||
{
|
||||
StartupProcess::setupApplication();
|
||||
|
||||
// A new QApplication
|
||||
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
|
||||
|
||||
// if application not yet created by the splasher
|
||||
int argc = App::Application::GetARGC();
|
||||
GUISingleApplication mainApp(argc, App::Application::GetARGV());
|
||||
// https://forum.freecad.org/viewtopic.php?f=3&t=15540
|
||||
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus, false);
|
||||
|
||||
// Make sure that we use '.' as decimal point. See also
|
||||
// http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=559846
|
||||
// and issue #0002891
|
||||
// http://doc.qt.io/qt-5/qcoreapplication.html#locale-settings
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
// check if a single or multiple instances can run
|
||||
if (onlySingleInstance(mainApp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAppNameAndIcon();
|
||||
|
||||
StartupProcess process;
|
||||
process.execute();
|
||||
|
||||
Application app(true);
|
||||
MainWindow mw;
|
||||
mw.setProperty("QuitOnClosed", true);
|
||||
|
||||
#ifdef FC_DEBUG // redirect Coin messages to FreeCAD
|
||||
SoDebugError::setHandlerCallback( messageHandlerCoin, 0 );
|
||||
#endif
|
||||
|
||||
StartupPostProcess postProcess(&mw, app, &mainApp);
|
||||
postProcess.execute();
|
||||
|
||||
Instance->d->startingUp = false;
|
||||
|
||||
// gets called once we start the event loop
|
||||
QTimer::singleShot(0, &mw, SLOT(delayedStartup()));
|
||||
|
||||
// run the Application event loop
|
||||
Base::Console().Log("Init: Entering event loop\n");
|
||||
|
||||
// boot phase reference point
|
||||
// https://forum.freecad.org/viewtopic.php?f=10&t=21665
|
||||
Gui::getMainWindow()->setProperty("eventLoop", true);
|
||||
|
||||
runEventLoop(mainApp);
|
||||
|
||||
Base::Console().Log("Finish: Event loop left\n");
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#define putpix()
|
||||
|
||||
#include <App/Application.h>
|
||||
#include "GuiApplication.h"
|
||||
|
||||
class QCloseEvent;
|
||||
class SoNode;
|
||||
@@ -48,11 +47,6 @@ class PreferencePackManager;
|
||||
class ViewProvider;
|
||||
class ViewProviderDocumentObject;
|
||||
|
||||
|
||||
GuiExport void initGuiAppPreMainWindow(bool calledByGuiPy);
|
||||
GuiExport void initGuiAppPostMainWindow(bool calledByGuiPy, QApplication &mApp, MainWindow &mw, GUIApplicationNativeEventAware *pmAppNativeEventAware);
|
||||
|
||||
|
||||
/** The Application main class
|
||||
* This is the central class of the GUI
|
||||
* @author Jürgen Riegel, Werner Mayer
|
||||
|
||||
@@ -1191,6 +1191,7 @@ SET(FreeCADGui_CPP_SRCS
|
||||
Utilities.cpp
|
||||
WaitCursor.cpp
|
||||
ManualAlignment.cpp
|
||||
StartupProcess.cpp
|
||||
TransactionObject.cpp
|
||||
UserSettings.cpp
|
||||
)
|
||||
@@ -1228,6 +1229,7 @@ SET(FreeCADGui_SRCS
|
||||
Utilities.h
|
||||
WaitCursor.h
|
||||
ManualAlignment.h
|
||||
StartupProcess.h
|
||||
TransactionObject.h
|
||||
UserSettings.h
|
||||
${FreeCADGui_SDK_MOC_HDRS}
|
||||
|
||||
569
src/Gui/StartupProcess.cpp
Normal file
569
src/Gui/StartupProcess.cpp
Normal file
@@ -0,0 +1,569 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QImageReader>
|
||||
#include <QLabel>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QStatusBar>
|
||||
#include <QWindow>
|
||||
#include <Inventor/SoDB.h>
|
||||
#endif
|
||||
|
||||
#include "StartupProcess.h"
|
||||
#include "Application.h"
|
||||
#include "AutoSaver.h"
|
||||
#include "DlgCheckableMessageBox.h"
|
||||
#include "FileDialog.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Language/Translator.h"
|
||||
#include <App/Application.h>
|
||||
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
|
||||
StartupProcess::StartupProcess() = default;
|
||||
|
||||
void StartupProcess::setupApplication()
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
|
||||
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
|
||||
#endif
|
||||
|
||||
// Automatic scaling for legacy apps (disable once all parts of GUI are aware of HiDpi)
|
||||
ParameterGrp::handle hDPI =
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/HighDPI");
|
||||
bool disableDpiScaling = hDPI->GetBool("DisableDpiScaling", false);
|
||||
if (disableDpiScaling) {
|
||||
#ifdef FC_OS_WIN32
|
||||
SetProcessDPIAware(); // call before the main event loop
|
||||
#endif
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// Enable automatic scaling based on pixel density of display (added in Qt 5.6)
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
#endif
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) && defined(Q_OS_WIN)
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
//Enable support for highres images (added in Qt 5.1, but off by default)
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
|
||||
// Use software rendering for OpenGL
|
||||
ParameterGrp::handle hOpenGL =
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/OpenGL");
|
||||
bool useSoftwareOpenGL = hOpenGL->GetBool("UseSoftwareOpenGL", false);
|
||||
if (useSoftwareOpenGL) {
|
||||
QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||
// By default (on platforms that support it, see docs for
|
||||
// Qt::AA_CompressHighFrequencyEvents) QT applies compression
|
||||
// for high frequency events (mouse move, touch, window resizes)
|
||||
// to keep things smooth even when handling the event takes a
|
||||
// while (e.g. to calculate snapping).
|
||||
// However, tablet pen move events (and mouse move events
|
||||
// synthesised from those) are not compressed by default (to
|
||||
// allow maximum precision when e.g. hand-drawing curves),
|
||||
// leading to unacceptable slowdowns using a tablet pen. Enable
|
||||
// compression for tablet events here to solve that.
|
||||
QCoreApplication::setAttribute(Qt::AA_CompressTabletEvents);
|
||||
#endif
|
||||
}
|
||||
|
||||
void StartupProcess::execute()
|
||||
{
|
||||
setLibraryPath();
|
||||
setStyleSheetPaths();
|
||||
setImagePaths();
|
||||
registerEventType();
|
||||
setThemePaths();
|
||||
setupFileDialog();
|
||||
}
|
||||
|
||||
void StartupProcess::setLibraryPath()
|
||||
{
|
||||
QString plugin;
|
||||
plugin = QString::fromStdString(App::Application::getHomePath());
|
||||
plugin += QLatin1String("/plugins");
|
||||
QCoreApplication::addLibraryPath(plugin);
|
||||
}
|
||||
|
||||
void StartupProcess::setStyleSheetPaths()
|
||||
{
|
||||
// setup the search paths for Qt style sheets
|
||||
QStringList qssPaths;
|
||||
qssPaths << QString::fromUtf8(
|
||||
(App::Application::getUserAppDataDir() + "Gui/Stylesheets/").c_str())
|
||||
<< QString::fromUtf8((App::Application::getResourceDir() + "Gui/Stylesheets/").c_str())
|
||||
<< QLatin1String(":/stylesheets");
|
||||
QDir::setSearchPaths(QString::fromLatin1("qss"), qssPaths);
|
||||
// setup the search paths for Qt overlay style sheets
|
||||
QStringList qssOverlayPaths;
|
||||
qssOverlayPaths << QString::fromUtf8((App::Application::getUserAppDataDir()
|
||||
+ "Gui/Stylesheets/overlay").c_str())
|
||||
<< QString::fromUtf8((App::Application::getResourceDir()
|
||||
+ "Gui/Stylesheets/overlay").c_str());
|
||||
QDir::setSearchPaths(QStringLiteral("overlay"), qssOverlayPaths);
|
||||
}
|
||||
|
||||
void StartupProcess::setImagePaths()
|
||||
{
|
||||
// set search paths for images
|
||||
QStringList imagePaths;
|
||||
imagePaths << QString::fromUtf8((App::Application::getUserAppDataDir() + "Gui/images").c_str())
|
||||
<< QString::fromUtf8((App::Application::getUserAppDataDir() + "pixmaps").c_str())
|
||||
<< QLatin1String(":/icons");
|
||||
QDir::setSearchPaths(QString::fromLatin1("images"), imagePaths);
|
||||
}
|
||||
|
||||
void StartupProcess::registerEventType()
|
||||
{
|
||||
// register action style event type
|
||||
ActionStyleEvent::EventType = QEvent::registerEventType(QEvent::User + 1);
|
||||
}
|
||||
|
||||
void StartupProcess::setThemePaths()
|
||||
{
|
||||
ParameterGrp::handle hTheme = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/Bitmaps/Theme");
|
||||
#if !defined(Q_OS_LINUX)
|
||||
QIcon::setThemeSearchPaths(QIcon::themeSearchPaths()
|
||||
<< QString::fromLatin1(":/icons/FreeCAD-default"));
|
||||
QIcon::setThemeName(QLatin1String("FreeCAD-default"));
|
||||
#else
|
||||
// Option to opt-out from using a Linux desktop icon theme.
|
||||
// https://forum.freecad.org/viewtopic.php?f=4&t=35624
|
||||
bool themePaths = hTheme->GetBool("ThemeSearchPaths",true);
|
||||
if (!themePaths) {
|
||||
QStringList searchPaths;
|
||||
searchPaths.prepend(QString::fromUtf8(":/icons"));
|
||||
QIcon::setThemeSearchPaths(searchPaths);
|
||||
QIcon::setThemeName(QLatin1String("FreeCAD-default"));
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string searchpath = hTheme->GetASCII("SearchPath");
|
||||
if (!searchpath.empty()) {
|
||||
QStringList searchPaths = QIcon::themeSearchPaths();
|
||||
searchPaths.prepend(QString::fromUtf8(searchpath.c_str()));
|
||||
QIcon::setThemeSearchPaths(searchPaths);
|
||||
}
|
||||
|
||||
std::string name = hTheme->GetASCII("Name");
|
||||
if (!name.empty()) {
|
||||
QIcon::setThemeName(QString::fromLatin1(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
void StartupProcess::setupFileDialog()
|
||||
{
|
||||
#if defined(FC_OS_LINUX)
|
||||
// See #0001588
|
||||
QString path = FileDialog::restoreLocation();
|
||||
FileDialog::setWorkingDirectory(QDir::currentPath());
|
||||
FileDialog::saveLocation(path);
|
||||
#else
|
||||
FileDialog::setWorkingDirectory(FileDialog::restoreLocation());
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
StartupPostProcess::StartupPostProcess(MainWindow* mw, Application& guiApp, QApplication* app)
|
||||
: mainWindow{mw}
|
||||
, guiApp{guiApp}
|
||||
, qtApp(app)
|
||||
{
|
||||
}
|
||||
|
||||
void StartupPostProcess::setLoadFromPythonModule(bool value)
|
||||
{
|
||||
loadFromPythonModule = value;
|
||||
}
|
||||
|
||||
void StartupPostProcess::execute()
|
||||
{
|
||||
setWindowTitle();
|
||||
setProcessMessages();
|
||||
setAutoSaving();
|
||||
setToolBarIconSize();
|
||||
setWheelEventFilter();
|
||||
setLocale();
|
||||
setCursorFlashing();
|
||||
checkOpenGL();
|
||||
loadOpenInventor();
|
||||
setBranding();
|
||||
showMainWindow();
|
||||
activateWorkbench();
|
||||
}
|
||||
|
||||
void StartupPostProcess::setWindowTitle()
|
||||
{
|
||||
// allow to disable version number
|
||||
ParameterGrp::handle hGen =
|
||||
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General");
|
||||
bool showVersion = hGen->GetBool("ShowVersionInTitle", true);
|
||||
|
||||
QString appName = QCoreApplication::applicationName();
|
||||
if (appName.isEmpty()) {
|
||||
appName = QString::fromLatin1(App::Application::Config()["ExeName"].c_str());
|
||||
}
|
||||
if (showVersion) {
|
||||
// set main window title with FreeCAD Version
|
||||
std::map<std::string, std::string>& config = App::Application::Config();
|
||||
QString major = QString::fromLatin1(config["BuildVersionMajor"].c_str());
|
||||
QString minor = QString::fromLatin1(config["BuildVersionMinor"].c_str());
|
||||
QString point = QString::fromLatin1(config["BuildVersionPoint"].c_str());
|
||||
QString suffix = QString::fromLatin1(config["BuildVersionSuffix"].c_str());
|
||||
QString title = QString::fromLatin1("%1 %2.%3.%4%5").arg(
|
||||
appName, major, minor, point, suffix
|
||||
);
|
||||
mainWindow->setWindowTitle(title);
|
||||
}
|
||||
else {
|
||||
mainWindow->setWindowTitle(appName);
|
||||
}
|
||||
}
|
||||
|
||||
void StartupPostProcess::setProcessMessages()
|
||||
{
|
||||
if (!loadFromPythonModule) {
|
||||
QObject::connect(qtApp, SIGNAL(messageReceived(const QList<QByteArray> &)),
|
||||
mainWindow, SLOT(processMessages(const QList<QByteArray> &)));
|
||||
}
|
||||
}
|
||||
|
||||
void StartupPostProcess::setAutoSaving()
|
||||
{
|
||||
ParameterGrp::handle hDocGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
|
||||
int timeout = int(hDocGrp->GetInt("AutoSaveTimeout", 15L)); // 15 min
|
||||
if (!hDocGrp->GetBool("AutoSaveEnabled", true)) {
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
AutoSaver::instance()->setTimeout(timeout * 60000); // NOLINT
|
||||
AutoSaver::instance()->setCompressed(hDocGrp->GetBool("AutoSaveCompressed", true));
|
||||
}
|
||||
|
||||
void StartupPostProcess::setToolBarIconSize()
|
||||
{
|
||||
// set toolbar icon size
|
||||
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
|
||||
int size = int(hGrp->GetInt("ToolbarIconSize", 0));
|
||||
// must not be lower than this
|
||||
if (size >= 16) { // NOLINT
|
||||
mainWindow->setIconSize(QSize(size,size));
|
||||
}
|
||||
}
|
||||
|
||||
void StartupPostProcess::setWheelEventFilter()
|
||||
{
|
||||
// filter wheel events for combo boxes
|
||||
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
|
||||
if (hGrp->GetBool("ComboBoxWheelEventFilter", false)) {
|
||||
auto filter = new WheelEventFilter(qtApp);
|
||||
qtApp->installEventFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
void StartupPostProcess::setLocale()
|
||||
{
|
||||
// For values different to 1 and 2 use the OS locale settings
|
||||
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
|
||||
auto localeFormat = hGrp->GetInt("UseLocaleFormatting", 0);
|
||||
if (localeFormat == 1) {
|
||||
Translator::instance()->setLocale(
|
||||
hGrp->GetASCII("Language", Translator::instance()->activeLanguage().c_str()));
|
||||
}
|
||||
else if (localeFormat == 2) {
|
||||
Translator::instance()->setLocale("C");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void StartupPostProcess::setCursorFlashing()
|
||||
{
|
||||
// set text cursor blinking state
|
||||
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
|
||||
int blinkTime = hGrp->GetBool("EnableCursorBlinking", true) ? -1 : 0;
|
||||
QApplication::setCursorFlashTime(blinkTime);
|
||||
}
|
||||
|
||||
void StartupPostProcess::checkOpenGL()
|
||||
{
|
||||
QWindow window;
|
||||
window.setSurfaceType(QWindow::OpenGLSurface);
|
||||
window.create();
|
||||
|
||||
QOpenGLContext context;
|
||||
if (context.create()) {
|
||||
context.makeCurrent(&window);
|
||||
if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) {
|
||||
Base::Console().Log("This system does not support framebuffer objects\n");
|
||||
}
|
||||
if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
|
||||
Base::Console().Log("This system does not support NPOT textures\n");
|
||||
}
|
||||
|
||||
int major = context.format().majorVersion();
|
||||
int minor = context.format().minorVersion();
|
||||
|
||||
#ifdef NDEBUG
|
||||
// In release mode, issue a warning to users that their version of OpenGL is
|
||||
// potentially going to cause problems
|
||||
if (major < 2) {
|
||||
auto message =
|
||||
QObject::tr("This system is running OpenGL %1.%2. "
|
||||
"FreeCAD requires OpenGL 2.0 or above. "
|
||||
"Please upgrade your graphics driver and/or card as required.")
|
||||
.arg(major)
|
||||
.arg(minor)
|
||||
+ QStringLiteral("\n");
|
||||
Base::Console().Warning(message.toStdString().c_str());
|
||||
Dialog::DlgCheckableMessageBox::showMessage(
|
||||
QCoreApplication::applicationName() + QStringLiteral(" - ")
|
||||
+ QObject::tr("Invalid OpenGL Version"),
|
||||
message);
|
||||
}
|
||||
#endif
|
||||
const char* glVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
Base::Console().Log("OpenGL version is: %d.%d (%s)\n", major, minor, glVersion);
|
||||
}
|
||||
}
|
||||
|
||||
void StartupPostProcess::loadOpenInventor()
|
||||
{
|
||||
bool loadedInventor = false;
|
||||
if (loadFromPythonModule) {
|
||||
loadedInventor = SoDB::isInitialized();
|
||||
}
|
||||
|
||||
if (!loadedInventor) {
|
||||
// init the Inventor subsystem
|
||||
Application::initOpenInventor();
|
||||
}
|
||||
}
|
||||
|
||||
void StartupPostProcess::setBranding()
|
||||
{
|
||||
QString home = QString::fromStdString(App::Application::getHomePath());
|
||||
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
std::map<std::string,std::string>::const_iterator it;
|
||||
it = cfg.find("WindowTitle");
|
||||
if (it != cfg.end()) {
|
||||
QString title = QString::fromUtf8(it->second.c_str());
|
||||
mainWindow->setWindowTitle(title);
|
||||
}
|
||||
it = cfg.find("WindowIcon");
|
||||
if (it != cfg.end()) {
|
||||
QString path = QString::fromUtf8(it->second.c_str());
|
||||
if (QDir(path).isRelative()) {
|
||||
path = QFileInfo(QDir(home), path).absoluteFilePath();
|
||||
}
|
||||
QApplication::setWindowIcon(QIcon(path));
|
||||
}
|
||||
it = cfg.find("ProgramLogo");
|
||||
if (it != cfg.end()) {
|
||||
QString path = QString::fromUtf8(it->second.c_str());
|
||||
if (QDir(path).isRelative()) {
|
||||
path = QFileInfo(QDir(home), path).absoluteFilePath();
|
||||
}
|
||||
QPixmap px(path);
|
||||
if (!px.isNull()) {
|
||||
auto logo = new QLabel();
|
||||
logo->setPixmap(px.scaledToHeight(32));
|
||||
mainWindow->statusBar()->addPermanentWidget(logo, 0);
|
||||
logo->setFrameShape(QFrame::NoFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StartupPostProcess::setImportImageFormats()
|
||||
{
|
||||
QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
|
||||
std::stringstream str;
|
||||
str << "Image formats (";
|
||||
for (const auto& ext : supportedFormats) {
|
||||
str << "*." << ext.constData() << " *." << ext.toUpper().constData() << " ";
|
||||
}
|
||||
str << ")";
|
||||
|
||||
std::string filter = str.str();
|
||||
App::GetApplication().addImportType(filter.c_str(), "FreeCADGui");
|
||||
}
|
||||
|
||||
bool StartupPostProcess::hiddenMainWindow() const
|
||||
{
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
bool hidden = false;
|
||||
auto it = cfg.find("StartHidden");
|
||||
if (it != cfg.end()) {
|
||||
hidden = true;
|
||||
}
|
||||
|
||||
return hidden;
|
||||
}
|
||||
|
||||
void StartupPostProcess::showMainWindow()
|
||||
{
|
||||
bool hidden = hiddenMainWindow();
|
||||
|
||||
// show splasher while initializing the GUI
|
||||
if (!hidden && !loadFromPythonModule) {
|
||||
mainWindow->startSplasher();
|
||||
}
|
||||
|
||||
// running the GUI init script
|
||||
try {
|
||||
Base::Console().Log("Run Gui init script\n");
|
||||
Application::runInitGuiScript();
|
||||
setImportImageFormats();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().Error("Error in FreeCADGuiInit.py: %s\n", e.what());
|
||||
mainWindow->stopSplasher();
|
||||
throw;
|
||||
|
||||
}
|
||||
|
||||
// stop splash screen and set immediately the active window that may be of interest
|
||||
// for scripts using Python binding for Qt
|
||||
mainWindow->stopSplasher();
|
||||
qtApp->setActiveWindow(mainWindow);
|
||||
}
|
||||
|
||||
void StartupPostProcess::activateWorkbench()
|
||||
{
|
||||
// Activate the correct workbench
|
||||
std::string start = App::Application::Config()["StartWorkbench"];
|
||||
Base::Console().Log("Init: Activating default workbench %s\n", start.c_str());
|
||||
std::string autoload =
|
||||
App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->GetASCII("AutoloadModule", start.c_str());
|
||||
if ("$LastModule" == autoload) {
|
||||
start = App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->GetASCII("LastModule", start.c_str());
|
||||
}
|
||||
else {
|
||||
start = autoload;
|
||||
}
|
||||
// if the auto workbench is not visible then force to use the default workbech
|
||||
// and replace the wrong entry in the parameters
|
||||
QStringList wb = guiApp.workbenches();
|
||||
if (!wb.contains(QString::fromLatin1(start.c_str()))) {
|
||||
start = App::Application::Config()["StartWorkbench"];
|
||||
if ("$LastModule" == autoload) {
|
||||
App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->SetASCII("LastModule", start.c_str());
|
||||
}
|
||||
else {
|
||||
App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->SetASCII("AutoloadModule", start.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Call this before showing the main window because otherwise:
|
||||
// 1. it shows a white window for a few seconds which doesn't look nice
|
||||
// 2. the layout of the toolbars is completely broken
|
||||
guiApp.activateWorkbench(start.c_str());
|
||||
|
||||
// show the main window
|
||||
if (!hiddenMainWindow()) {
|
||||
Base::Console().Log("Init: Showing main window\n");
|
||||
mainWindow->loadWindowSettings();
|
||||
}
|
||||
|
||||
//initialize spaceball.
|
||||
if (auto fcApp = qobject_cast<GUIApplicationNativeEventAware*>(qtApp)) {
|
||||
fcApp->initSpaceball(mainWindow);
|
||||
}
|
||||
|
||||
setStyleSheet();
|
||||
|
||||
// Now run the background autoload, for workbenches that should be loaded at startup, but not
|
||||
// displayed to the user immediately
|
||||
autoloadModules(wb);
|
||||
|
||||
// Reactivate the startup workbench
|
||||
guiApp.activateWorkbench(start.c_str());
|
||||
}
|
||||
|
||||
void StartupPostProcess::setStyleSheet()
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/MainWindow");
|
||||
std::string style = hGrp->GetASCII("StyleSheet");
|
||||
if (style.empty()) {
|
||||
// check the branding settings
|
||||
const auto& config = App::Application::Config();
|
||||
auto it = config.find("StyleSheet");
|
||||
if (it != config.end()) {
|
||||
style = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
guiApp.setStyleSheet(QLatin1String(style.c_str()), hGrp->GetBool("TiledBackground", false));
|
||||
}
|
||||
|
||||
void StartupPostProcess::autoloadModules(const QStringList& wb)
|
||||
{
|
||||
// Now run the background autoload, for workbenches that should be loaded at startup, but not
|
||||
// displayed to the user immediately
|
||||
std::string autoloadCSV =
|
||||
App::GetApplication()
|
||||
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
||||
->GetASCII("BackgroundAutoloadModules", "");
|
||||
|
||||
// Tokenize the comma-separated list and load the requested workbenches if they exist in this
|
||||
// installation
|
||||
std::stringstream stream(autoloadCSV);
|
||||
std::string workbench;
|
||||
while (std::getline(stream, workbench, ',')) {
|
||||
if (wb.contains(QString::fromLatin1(workbench.c_str()))) {
|
||||
guiApp.activateWorkbench(workbench.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
88
src/Gui/StartupProcess.h
Normal file
88
src/Gui/StartupProcess.h
Normal file
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef GUI_STARTUPPROCESS_H
|
||||
#define GUI_STARTUPPROCESS_H
|
||||
|
||||
#include <FCGlobal.h>
|
||||
#include <QStringList>
|
||||
|
||||
class QApplication;
|
||||
|
||||
namespace Gui {
|
||||
|
||||
class Application;
|
||||
class MainWindow;
|
||||
|
||||
class GuiExport StartupProcess
|
||||
{
|
||||
public:
|
||||
StartupProcess();
|
||||
static void setupApplication();
|
||||
void execute();
|
||||
|
||||
private:
|
||||
void setLibraryPath();
|
||||
void setStyleSheetPaths();
|
||||
void setImagePaths();
|
||||
void registerEventType();
|
||||
void setThemePaths();
|
||||
void setupFileDialog();
|
||||
};
|
||||
|
||||
class GuiExport StartupPostProcess
|
||||
{
|
||||
public:
|
||||
StartupPostProcess(MainWindow* mw, Application& guiApp, QApplication* app);
|
||||
void setLoadFromPythonModule(bool value);
|
||||
void execute();
|
||||
|
||||
private:
|
||||
void setWindowTitle();
|
||||
void setProcessMessages();
|
||||
void setAutoSaving();
|
||||
void setToolBarIconSize();
|
||||
void setWheelEventFilter();
|
||||
void setLocale();
|
||||
void setCursorFlashing();
|
||||
void checkOpenGL();
|
||||
void loadOpenInventor();
|
||||
void setBranding();
|
||||
void setStyleSheet();
|
||||
void autoloadModules(const QStringList& wb);
|
||||
void setImportImageFormats();
|
||||
bool hiddenMainWindow() const;
|
||||
void showMainWindow();
|
||||
void activateWorkbench();
|
||||
|
||||
private:
|
||||
bool loadFromPythonModule = false;
|
||||
MainWindow* mainWindow;
|
||||
Application& guiApp;
|
||||
QApplication* qtApp;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // GUI_STARTUPPROCESS_H
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Gui/MainWindow.h>
|
||||
#include <Gui/StartupProcess.h>
|
||||
#include <Gui/SoFCDB.h>
|
||||
#include <Gui/Quarter/Quarter.h>
|
||||
#include <Inventor/SoDB.h>
|
||||
@@ -286,7 +287,8 @@ QWidget* setupMainWindow()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Gui::initGuiAppPreMainWindow(true);
|
||||
Gui::StartupProcess process;
|
||||
process.execute();
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
// It's sufficient to create the config key
|
||||
@@ -301,7 +303,9 @@ QWidget* setupMainWindow()
|
||||
mw->setWindowIcon(qApp->windowIcon());
|
||||
|
||||
try {
|
||||
Gui::initGuiAppPostMainWindow(true, *qApp, *mw, nullptr);
|
||||
Gui::StartupPostProcess postProcess(mw, *Gui::Application::Instance, qApp);
|
||||
postProcess.setLoadFromPythonModule(true);
|
||||
postProcess.execute();
|
||||
}
|
||||
catch (const Base::Exception&) {
|
||||
return nullptr;
|
||||
|
||||
Reference in New Issue
Block a user