Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
c136b73e0This file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* This program 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.
*
* This program 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/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "application.h"
#include "build_env.h"
#include "fileio/transactionalfilesystem.h"
#include "font/strokefontpool.h"
#include "types/angle.h"
#include "types/length.h"
#include "types/point.h"
#include <QtCore>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
Application::Application(int& argc, char** argv) noexcept
: QApplication(argc, argv),
mAppVersion(
Version::fromString(QString(LIBREPCB_APP_VERSION).section('-', 0, 0))),
mAppVersionLabel(QString(LIBREPCB_APP_VERSION).section('-', 1, 1)),
mGitRevision(GIT_COMMIT_SHA),
mFileFormatVersion(Version::fromString(LIBREPCB_FILE_FORMAT_VERSION)),
mIsFileFormatStable(LIBREPCB_FILE_FORMAT_STABLE) {
// register meta types
qRegisterMetaType<FilePath>();
qRegisterMetaType<Point>();
qRegisterMetaType<Length>();
qRegisterMetaType<Angle>();
// set application version
QApplication::setApplicationVersion(LIBREPCB_APP_VERSION);
// set build timestamp
QDate buildDate =
QLocale(QLocale::C)
.toDate(QString(__DATE__).simplified(), QLatin1String("MMM d yyyy"));
QTime buildTime = QTime::fromString(__TIME__, Qt::TextDate);
mBuildDate = QDateTime(buildDate, buildTime);
// check file format version
if (!mFileFormatVersion.isPrefixOf(mAppVersion)) {
qFatal(
"The file format version is not a prefix of the application version!");
}
// get the directory of the currently running executable
FilePath executableFilePath(QApplication::applicationFilePath());
Q_ASSERT(executableFilePath.isValid());
// determine the path to the resources directory (e.g. /usr/share/librepcb)
#if defined(LIBREPCB_BINARY_DIR) && defined(LIBREPCB_SHARE_SOURCE)
// TODO: The following code checks for paths related to the application
// binary, even though this code is located in the library source. This is a
// bit of a layer violation and should be refactored.
FilePath buildOutputDirPath(LIBREPCB_BINARY_DIR);
bool runningFromBuildOutput =
executableFilePath.isLocatedInDir(buildOutputDirPath);
if (runningFromBuildOutput) {
// The executable is located inside the build output directory, so we assume
// this is a developer build and thus we use the "share" directory from the
// repository root.
mResourcesDir = FilePath(LIBREPCB_SHARE_SOURCE).getPathTo("librepcb");
}
#endif
if (!mResourcesDir.isValid()) {
if (QDir::isAbsolutePath(LIBREPCB_SHARE)) {
mResourcesDir.setPath(LIBREPCB_SHARE);
} else {
mResourcesDir =
executableFilePath.getParentDir().getPathTo(LIBREPCB_SHARE);
}
}
// warn if runtime resource files are not found
if (!getResourcesFilePath("README.md").isExistingFile()) {
qCritical()
<< "Could not find resource files! Probably packaging went wrong?!";
qCritical() << "Expected resources location:" << mResourcesDir.toNative();
qCritical() << "Executable location: "
<< executableFilePath.toNative();
qCritical() << "LIBREPCB_SHARE: " << QString(LIBREPCB_SHARE);
#ifdef LIBREPCB_BINARY_DIR
qCritical() << "LIBREPCB_BINARY_DIR: "
<< QString(LIBREPCB_BINARY_DIR);
#endif
#ifdef LIBREPCB_SHARE_SOURCE
qCritical() << "LIBREPCB_SHARE_SOURCE: "
<< QString(LIBREPCB_SHARE_SOURCE);
#endif
}
// load all bundled TrueType/OpenType fonts
QDir fontsDir(getResourcesFilePath("fonts").toStr());
fontsDir.setFilter(QDir::Files);
fontsDir.setNameFilters({"*.ttf", "*.otf"});
foreach (const QFileInfo& info, fontsDir.entryInfoList()) {
QString fp = info.absoluteFilePath();
int id = QFontDatabase::addApplicationFont(fp);
if (id < 0) {
qCritical().nospace() << "Failed to register font " << fp << ".";
}
}
// set default sans serif font
mSansSerifFont.setStyleStrategy(
QFont::StyleStrategy(QFont::OpenGLCompatible | QFont::PreferQuality));
mSansSerifFont.setStyleHint(QFont::SansSerif);
mSansSerifFont.setFamily("Noto Sans");
// set default monospace font
mMonospaceFont.setStyleStrategy(
QFont::StyleStrategy(QFont::OpenGLCompatible | QFont::PreferQuality));
mMonospaceFont.setStyleHint(QFont::TypeWriter);
mMonospaceFont.setFamily("Noto Sans Mono");
// load all stroke fonts
TransactionalFileSystem strokeFontsDir(
getResourcesFilePath("fontobene"), false,
&TransactionalFileSystem::RestoreMode::no);
mStrokeFontPool.reset(new StrokeFontPool(strokeFontsDir));
getDefaultStrokeFont(); // ensure that the default font is available (aborts
// if not)
}
Application::~Application() noexcept {
// Not sure if needed, but let's unregister translators before destroying
// (maybe otherwise QCoreApplication has dangling pointers to translators).
removeAllTranslators();
}
/*******************************************************************************
* Getters
******************************************************************************/
FilePath Application::getResourcesFilePath(const QString& filepath) const
noexcept {
return mResourcesDir.getPathTo(filepath);
}
QStringList Application::getAvailableTranslationLocales() const noexcept {
QStringList locales;
QDir dir(getResourcesFilePath("i18n").toStr());
foreach (QString filename, dir.entryList({"*.qm"}, QDir::Files, QDir::Name)) {
filename.remove("librepcb_");
filename.remove(".qm");
locales.append(filename);
}
return locales;
}
const StrokeFont& Application::getDefaultStrokeFont() const noexcept {
try {
return mStrokeFontPool->getFont(getDefaultStrokeFontName());
} catch (const Exception& e) {
// Abort the application!!!
qFatal("Default stroke font could not be loaded, terminating application!");
}
}
/*******************************************************************************
* Setters
******************************************************************************/
void Application::setTranslationLocale(const QLocale& locale) noexcept {
// First, remove all currently installed translations to avoid falling back to
// wrong languages. The fallback language must always be en_US, i.e.
// untranslated strings. See https://github.com/LibrePCB/LibrePCB/issues/611
removeAllTranslators();
// Install Qt translations
auto qtTranslator = std::make_shared<QTranslator>(this);
qtTranslator->load("qt_" % locale.name(),
QLibraryInfo::location(QLibraryInfo::TranslationsPath));
installTranslator(qtTranslator.get());
mTranslators.append(qtTranslator);
// Install system language translations (all system languages defined in the
// system settings, in the defined order)
const QString dir = getResourcesFilePath("i18n").toStr();
auto systemTranslator = std::make_shared<QTranslator>(this);
systemTranslator->load(locale, "librepcb", "_", dir);
installTranslator(systemTranslator.get());
mTranslators.append(systemTranslator);
// Install language translations (like "de" for German)
auto appTranslator1 = std::make_shared<QTranslator>(this);
appTranslator1->load("librepcb_" % locale.name().split("_").at(0), dir);
installTranslator(appTranslator1.get());
mTranslators.append(appTranslator1);
// Install language/country translations (like "de_ch" for German/Switzerland)
auto appTranslator2 = std::make_shared<QTranslator>(this);
appTranslator2->load("librepcb_" % locale.name(), dir);
installTranslator(appTranslator2.get());
mTranslators.append(appTranslator2);
}
/*******************************************************************************
* Reimplemented from QApplication
******************************************************************************/
bool Application::notify(QObject* receiver, QEvent* e) {
try {
return QApplication::notify(receiver, e);
} catch (...) {
qCritical() << "Exception caught in Application::notify()!";
}
return false;
}
/*******************************************************************************
* Static Methods
******************************************************************************/
Application* Application::instance() noexcept {
Application* app = dynamic_cast<Application*>(QCoreApplication::instance());
Q_ASSERT(app);
return app;
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void Application::removeAllTranslators() noexcept {
foreach (auto& translator, mTranslators) {
if (!qApp->removeTranslator(translator.get())) {
qWarning() << "Failed to remove translator.";
}
}
mTranslators.clear();
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb