kwin/clients/aurorae/src/aurorae.cpp
Martin Gräßlin 060b310082 [aurorae] Do not update shadow from ::paint
Paint is in the middle of the compositor rendering loop. Updating
the shadow at that point breaks KWin. Thus we delay the update to
the next event cycle. Obviously it would be even better to only
update the shadow if it changed, but that might be too expensive.
2014-12-12 22:12:11 +01:00

735 lines
26 KiB
C++

/********************************************************************
Copyright (C) 2009, 2010, 2012 Martin Gräßlin <mgraesslin@kde.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 2 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/>.
*********************************************************************/
#include "aurorae.h"
#include "auroraetheme.h"
#include "config-kwin.h"
// qml imports
#include "decorationoptions.h"
// KDecoration2
#include <KDecoration2/DecoratedClient>
#include <KDecoration2/DecorationSettings>
#include <KDecoration2/DecorationShadow>
// KDE
#include <KConfigGroup>
#include <KConfigLoader>
#include <KConfigDialogManager>
#include <KDesktopFile>
#include <KLocalizedTranslator>
#include <KPluginFactory>
#include <KSharedConfig>
#include <KService>
#include <KServiceTypeTrader>
// Qt
#include <QDebug>
#include <QDirIterator>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
#include <QPainter>
#include <QQuickItem>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
#define HAVE_RENDER_CONTROL 1
#else
#define HAVE_RENDER_CONTROL 0
#endif
#if HAVE_RENDER_CONTROL
#include <QQuickRenderControl>
#endif
#include <QQuickWindow>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlEngine>
#include <QStandardPaths>
#include <QTimer>
#include <QUiLoader>
#include <QVBoxLayout>
K_PLUGIN_FACTORY_WITH_JSON(AuroraeDecoFactory,
"aurorae.json",
registerPlugin<Aurorae::Decoration>();
registerPlugin<Aurorae::ThemeFinder>(QStringLiteral("themes"));
registerPlugin<Aurorae::ConfigurationModule>(QStringLiteral("kcmodule"));
)
namespace Aurorae
{
class Helper
{
public:
void ref();
void unref();
QQmlComponent *component(const QString &theme);
QQmlContext *rootContext();
QQmlComponent *svgComponent() {
return m_svgComponent.data();
}
static Helper &instance();
private:
Helper() = default;
void init();
QQmlComponent *loadComponent(const QString &themeName);
int m_refCount = 0;
QScopedPointer<QQmlEngine> m_engine;
QHash<QString, QQmlComponent*> m_components;
QScopedPointer<QQmlComponent> m_svgComponent;
};
Helper &Helper::instance()
{
static Helper s_helper;
return s_helper;
}
void Helper::ref()
{
m_refCount++;
if (m_refCount == 1) {
m_engine.reset(new QQmlEngine);
init();
}
}
void Helper::unref()
{
m_refCount--;
if (m_refCount == 0) {
// cleanup
m_svgComponent.reset();
m_engine.reset();
m_components.clear();
}
}
static const QString s_defaultTheme = QStringLiteral("kwin4_decoration_qml_plastik");
QQmlComponent *Helper::component(const QString &themeName)
{
// maybe it's an SVG theme?
if (themeName.startsWith(QLatin1Literal("__aurorae__svg__"))) {
if (m_svgComponent.isNull()) {
/* use logic from KDeclarative::setupBindings():
"addImportPath adds the path at the beginning, so to honour user's
paths we need to traverse the list in reverse order" */
QStringListIterator paths(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("module/imports"), QStandardPaths::LocateDirectory));
paths.toBack();
while (paths.hasPrevious()) {
m_engine->addImportPath(paths.previous());
}
m_svgComponent.reset(new QQmlComponent(m_engine.data()));
m_svgComponent->loadUrl(QUrl(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/aurorae/aurorae.qml"))));
}
return m_svgComponent.data();
}
// try finding the QML package
auto it = m_components.constFind(themeName);
if (it != m_components.constEnd()) {
return it.value();
}
auto component = loadComponent(themeName);
if (component) {
m_components.insert(themeName, component);
return component;
}
// try loading default component
if (themeName != s_defaultTheme) {
return loadComponent(s_defaultTheme);
}
return nullptr;
}
QQmlComponent *Helper::loadComponent(const QString &themeName)
{
qCDebug(AURORAE) << "Trying to load QML Decoration " << themeName;
const QString internalname = themeName.toLower();
QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(internalname);
KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("KWin/Decoration"), constraint);
if (offers.isEmpty()) {
qCCritical(AURORAE) << "Couldn't find QML Decoration " << themeName << endl;
// TODO: what to do in error case?
return nullptr;
}
KService::Ptr service = offers.first();
const QString pluginName = service->property(QStringLiteral("X-KDE-PluginInfo-Name")).toString();
const QString scriptName = service->property(QStringLiteral("X-Plasma-MainScript")).toString();
const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(KWIN_NAME) + QStringLiteral("/decorations/") + pluginName + QStringLiteral("/contents/") + scriptName);
if (file.isNull()) {
qCDebug(AURORAE) << "Could not find script file for " << pluginName;
// TODO: what to do in error case?
return nullptr;
}
// setup the QML engine
/* use logic from KDeclarative::setupBindings():
"addImportPath adds the path at the beginning, so to honour user's
paths we need to traverse the list in reverse order" */
QStringListIterator paths(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("module/imports"), QStandardPaths::LocateDirectory));
paths.toBack();
while (paths.hasPrevious()) {
m_engine->addImportPath(paths.previous());
}
QQmlComponent *component = new QQmlComponent(m_engine.data(), m_engine.data());
component->loadUrl(QUrl::fromLocalFile(file));
return component;
}
QQmlContext *Helper::rootContext()
{
return m_engine->rootContext();
}
void Helper::init()
{
// we need to first load our decoration plugin
// once it's loaded we can provide the Borders and access them from C++ side
// so let's try to locate our plugin:
QString pluginPath;
for (const QString &path : m_engine->importPathList()) {
QDirIterator it(path, QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
QFileInfo fileInfo = it.fileInfo();
if (!fileInfo.isFile()) {
continue;
}
if (!fileInfo.path().endsWith(QLatin1String("/org/kde/kwin/decoration"))) {
continue;
}
if (fileInfo.fileName() == QLatin1String("libdecorationplugin.so")) {
pluginPath = fileInfo.absoluteFilePath();
break;
}
}
if (!pluginPath.isEmpty()) {
break;
}
}
m_engine->importPlugin(pluginPath, "org.kde.kwin.decoration", nullptr);
qmlRegisterType<KWin::Borders>("org.kde.kwin.decoration", 0, 1, "Borders");
qmlRegisterType<KDecoration2::Decoration>();
qmlRegisterType<KDecoration2::DecoratedClient>();
qRegisterMetaType<KDecoration2::BorderSize>();
}
static QString findTheme(const QVariantList &args)
{
if (args.isEmpty()) {
return QString();
}
const auto map = args.first().toMap();
auto it = map.constFind(QStringLiteral("theme"));
if (it == map.constEnd()) {
return QString();
}
return it.value().toString();
}
Decoration::Decoration(QObject *parent, const QVariantList &args)
: KDecoration2::Decoration(parent, args)
, m_item(nullptr)
, m_borders(nullptr)
, m_maximizedBorders(nullptr)
, m_extendedBorders(nullptr)
, m_padding(nullptr)
, m_themeName(s_defaultTheme)
, m_mutex(QMutex::Recursive)
{
m_themeName = findTheme(args);
Helper::instance().ref();
}
Decoration::~Decoration()
{
Helper::instance().unref();
#if HAVE_RENDER_CONTROL
if (m_context) {
m_context->makeCurrent(m_offscreenSurface.data());
delete m_renderControl;
delete m_view.data();
m_fbo.reset();
delete m_item;
m_context->doneCurrent();
}
#endif
}
void Decoration::init()
{
KDecoration2::Decoration::init();
auto s = settings();
connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, this, &Decoration::configChanged);
// recreate scene when compositing gets disabled, TODO: remove with rendercontrol
#if !HAVE_RENDER_CONTROL
if (!m_recreateNonCompositedConnection) {
m_recreateNonCompositedConnection = connect(s.data(), &KDecoration2::DecorationSettings::alphaChannelSupportedChanged,
this, [this](bool alpha) {
if (!alpha && m_item) {
m_item->deleteLater();
m_decorationWindow.reset();
init();
}
});
}
#endif
QQmlContext *context = new QQmlContext(Helper::instance().rootContext(), this);
context->setContextProperty(QStringLiteral("decoration"), this);
context->setContextProperty(QStringLiteral("decorationSettings"), s.data());
auto component = Helper::instance().component(m_themeName);
if (!component) {
return;
}
if (component == Helper::instance().svgComponent()) {
// load SVG theme
const QString themeName = m_themeName.mid(16);
KConfig config(QStringLiteral("aurorae/themes/") + themeName + QStringLiteral("/") + themeName + QStringLiteral("rc"),
KConfig::FullConfig, QStandardPaths::GenericDataLocation);
// KConfigGroup themeGroup(&conf, themeName);
AuroraeTheme *theme = new AuroraeTheme(this);
theme->loadTheme(themeName, config);
theme->setBorderSize(s->borderSize());
connect(s.data(), &KDecoration2::DecorationSettings::borderSizeChanged, theme, &AuroraeTheme::setBorderSize);
// m_theme->setButtonSize((KDecorationDefines::BorderSize)themeGroup.readEntry<int>("ButtonSize", KDecorationDefines::BorderNormal));
// m_theme->setTabDragMimeType(tabDragMimeType());
context->setContextProperty(QStringLiteral("auroraeTheme"), theme);
}
m_item = qobject_cast< QQuickItem* >(component->create(context));
if (!m_item) {
return;
}
m_item->setParent(this);
QVariant visualParent = property("visualParent");
if (visualParent.isValid()) {
m_item->setParentItem(visualParent.value<QQuickItem*>());
visualParent.value<QQuickItem*>()->setProperty("drawBackground", false);
} else {
#if HAVE_RENDER_CONTROL
// first create the context
QSurfaceFormat format;
format.setDepthBufferSize(16);
format.setStencilBufferSize(8);
m_context.reset(new QOpenGLContext);
m_context->setFormat(format);
m_context->create();
// and the offscreen surface
m_offscreenSurface.reset(new QOffscreenSurface);
m_offscreenSurface->setFormat(m_context->format());
m_offscreenSurface->create();
m_renderControl = new QQuickRenderControl(this);
m_view = new QQuickWindow(m_renderControl);
m_view->setColor(Qt::transparent);
// delay rendering a little bit for better performance
m_updateTimer.reset(new QTimer);
m_updateTimer->setSingleShot(true);
m_updateTimer->setInterval(5);
connect(m_updateTimer.data(), &QTimer::timeout, this,
[this] {
if (!m_context->makeCurrent(m_offscreenSurface.data())) {
return;
}
if (m_fbo.isNull() || m_fbo->size() != m_view->size()) {
m_fbo.reset(new QOpenGLFramebufferObject(m_view->size(), QOpenGLFramebufferObject::CombinedDepthStencil));
if (!m_fbo->isValid()) {
qCWarning(AURORAE) << "Creating FBO as render target failed";
m_fbo.reset();
return;
}
}
m_view->setRenderTarget(m_fbo.data());
m_renderControl->polishItems();
m_renderControl->sync();
m_renderControl->render();
m_view->resetOpenGLState();
m_buffer = m_fbo->toImage();
QOpenGLFramebufferObject::bindDefault();
update();
}
);
auto requestUpdate = [this] {
if (m_updateTimer->isActive()) {
return;
}
m_updateTimer->start();
};
connect(m_renderControl, &QQuickRenderControl::renderRequested, this, requestUpdate);
connect(m_renderControl, &QQuickRenderControl::sceneChanged, this, requestUpdate);
m_item->setParentItem(m_view->contentItem());
m_context->makeCurrent(m_offscreenSurface.data());
m_renderControl->initialize(m_context.data());
m_context->doneCurrent();
#else
// we need a QQuickWindow till we depend on Qt 5.4
m_decorationWindow.reset(QWindow::fromWinId(client().data()->decorationId()));
m_view = new QQuickWindow(m_decorationWindow.data());
m_view->setFlags(Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput);
m_view->setColor(Qt::transparent);
connect(m_view.data(), &QQuickWindow::beforeRendering, [this]() {
if (!settings()->isAlphaChannelSupported()) {
// directly render to QQuickWindow
m_fbo.reset();
return;
}
if (m_fbo.isNull() || m_fbo->size() != m_view->size()) {
m_fbo.reset(new QOpenGLFramebufferObject(m_view->size(), QOpenGLFramebufferObject::CombinedDepthStencil));
if (!m_fbo->isValid()) {
qCWarning(AURORAE) << "Creating FBO as render target failed";
m_fbo.reset();
return;
}
}
m_view->setRenderTarget(m_fbo.data());
});
connect(m_view.data(), &QQuickWindow::afterRendering, [this] {
if (!m_fbo) {
return;
}
QMutexLocker locker(&m_mutex);
m_buffer = m_fbo->toImage();
});
connect(s.data(), &KDecoration2::DecorationSettings::alphaChannelSupportedChanged,
m_view.data(), &QQuickWindow::update);
connect(m_view.data(), &QQuickWindow::afterRendering, this, [this] { update(); }, Qt::QueuedConnection);
m_item->setParentItem(m_view->contentItem());
#endif
}
setupBorders(m_item);
if (m_extendedBorders) {
auto updateExtendedBorders = [this] {
setResizeOnlyBorders(*m_extendedBorders);
};
updateExtendedBorders();
connect(m_extendedBorders, &KWin::Borders::leftChanged, this, updateExtendedBorders);
connect(m_extendedBorders, &KWin::Borders::rightChanged, this, updateExtendedBorders);
connect(m_extendedBorders, &KWin::Borders::topChanged, this, updateExtendedBorders);
connect(m_extendedBorders, &KWin::Borders::bottomChanged, this, updateExtendedBorders);
}
connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateBorders, Qt::QueuedConnection);
updateBorders();
if (!m_view.isNull()) {
#if !HAVE_RENDER_CONTROL
m_view->setVisible(true);
#endif
auto resizeWindow = [this] {
QRect rect(QPoint(0, 0), size());
if (m_padding && !client().data()->isMaximized()) {
rect = rect.adjusted(-m_padding->left(), -m_padding->top(), m_padding->right(), m_padding->bottom());
}
m_view->setGeometry(rect);
#if !HAVE_RENDER_CONTROL
m_view->lower();
m_view->update();
#endif
};
connect(this, &Decoration::bordersChanged, this, resizeWindow);
connect(client().data(), &KDecoration2::DecoratedClient::widthChanged, this, resizeWindow);
connect(client().data(), &KDecoration2::DecoratedClient::heightChanged, this, resizeWindow);
connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, resizeWindow);
resizeWindow();
} else {
// create a dummy shadow for the configuration interface
if (m_padding) {
auto s = QSharedPointer<KDecoration2::DecorationShadow>::create();
s->setPadding(*m_padding);
s->setInnerShadowRect(QRect(m_padding->left(), m_padding->top(), 1, 1));
setShadow(s);
}
}
}
QVariant Decoration::readConfig(const QString &key, const QVariant &defaultValue)
{
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("auroraerc"));
return config->group(m_themeName).readEntry(key, defaultValue);
}
void Decoration::setupBorders(QQuickItem *item)
{
m_borders = item->findChild<KWin::Borders*>(QStringLiteral("borders"));
m_maximizedBorders = item->findChild<KWin::Borders*>(QStringLiteral("maximizedBorders"));
m_extendedBorders = item->findChild<KWin::Borders*>(QStringLiteral("extendedBorders"));
m_padding = item->findChild<KWin::Borders*>(QStringLiteral("padding"));
}
void Decoration::updateBorders()
{
KWin::Borders *b = m_borders;
if (client().data()->isMaximized() && m_maximizedBorders) {
b = m_maximizedBorders;
}
if (!b) {
return;
}
setBorders(*b);
}
void Decoration::paint(QPainter *painter, const QRect &repaintRegion)
{
Q_UNUSED(repaintRegion)
#if !HAVE_RENDER_CONTROL
if (!settings()->isAlphaChannelSupported()) {
return;
}
QMutexLocker locker(&m_mutex);
#endif
painter->fillRect(rect(), Qt::transparent);
QRectF r(QPointF(0, 0), m_buffer.size());
if (m_padding &&
(m_padding->left() > 0 || m_padding->top() > 0 || m_padding->right() > 0 || m_padding->bottom() > 0) &&
!client().data()->isMaximized()) {
r = r.adjusted(m_padding->left(), m_padding->top(), -m_padding->right(), -m_padding->bottom());
auto s = QSharedPointer<KDecoration2::DecorationShadow>::create();
s->setShadow(m_buffer);
s->setPadding(*m_padding);
s->setInnerShadowRect(QRect(m_padding->left(),
m_padding->top(),
m_buffer.width() - m_padding->left() - m_padding->right(),
m_buffer.height() - m_padding->top() - m_padding->bottom()));
m_scheduledShadow = s;
} else {
m_scheduledShadow = QSharedPointer<KDecoration2::DecorationShadow>();
}
QMetaObject::invokeMethod(this, "updateShadow", Qt::QueuedConnection);
painter->drawImage(rect(), m_buffer, r);
}
void Decoration::updateShadow()
{
setShadow(m_scheduledShadow);
}
QMouseEvent Decoration::translatedMouseEvent(QMouseEvent *orig)
{
if (!m_padding || client().data()->isMaximized()) {
orig->setAccepted(false);
return *orig;
}
QMouseEvent event(orig->type(), orig->localPos() + QPointF(m_padding->left(), m_padding->top()), orig->button(), orig->buttons(), orig->modifiers());
event.setAccepted(false);
return event;
}
void Decoration::hoverEnterEvent(QHoverEvent *event)
{
if (m_view) {
event->setAccepted(false);
QCoreApplication::sendEvent(m_view.data(), event);
}
KDecoration2::Decoration::hoverEnterEvent(event);
}
void Decoration::hoverLeaveEvent(QHoverEvent *event)
{
if (m_view) {
event->setAccepted(false);
QCoreApplication::sendEvent(m_view.data(), event);
}
KDecoration2::Decoration::hoverLeaveEvent(event);
}
void Decoration::hoverMoveEvent(QHoverEvent *event)
{
if (m_view) {
QMouseEvent mouseEvent(QEvent::MouseMove, event->posF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QMouseEvent ev = translatedMouseEvent(&mouseEvent);
QCoreApplication::sendEvent(m_view.data(), &ev);
event->setAccepted(ev.isAccepted());
}
KDecoration2::Decoration::hoverMoveEvent(event);
}
void Decoration::mouseMoveEvent(QMouseEvent *event)
{
if (m_view) {
QMouseEvent ev = translatedMouseEvent(event);
QCoreApplication::sendEvent(m_view.data(), &ev);
event->setAccepted(ev.isAccepted());
}
KDecoration2::Decoration::mouseMoveEvent(event);
}
void Decoration::mousePressEvent(QMouseEvent *event)
{
if (m_view) {
QMouseEvent ev = translatedMouseEvent(event);
QCoreApplication::sendEvent(m_view.data(), &ev);
event->setAccepted(ev.isAccepted());
}
KDecoration2::Decoration::mousePressEvent(event);
}
void Decoration::mouseReleaseEvent(QMouseEvent *event)
{
if (m_view) {
QMouseEvent ev = translatedMouseEvent(event);
QCoreApplication::sendEvent(m_view.data(), &ev);
event->setAccepted(ev.isAccepted());
}
KDecoration2::Decoration::mouseReleaseEvent(event);
}
void Decoration::installTitleItem(QQuickItem *item)
{
auto update = [this, item] {
QRect rect = item->mapRectToScene(item->childrenRect()).toRect();
if (rect.isNull()) {
rect = item->parentItem()->mapRectToScene(QRectF(item->x(), item->y(), item->width(), item->height())).toRect();
}
setTitleBar(rect);
};
update();
connect(item, &QQuickItem::widthChanged, this, update);
connect(item, &QQuickItem::heightChanged, this, update);
connect(item, &QQuickItem::xChanged, this, update);
connect(item, &QQuickItem::yChanged, this, update);
}
KDecoration2::DecoratedClient *Decoration::clientPointer() const
{
return client().data();
}
ThemeFinder::ThemeFinder(QObject *parent, const QVariantList &args)
: QObject(parent)
{
Q_UNUSED(args)
init();
}
void ThemeFinder::init()
{
findAllQmlThemes();
findAllSvgThemes();
}
void ThemeFinder::findAllQmlThemes()
{
const KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("KWin/Decoration"));
for (const auto &offer : offers) {
m_themes.insert(offer->name(), offer->property(QStringLiteral("X-KDE-PluginInfo-Name")).toString());
}
}
void ThemeFinder::findAllSvgThemes()
{
QStringList themes;
const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("aurorae/themes/"), QStandardPaths::LocateDirectory);
QStringList themeDirectories;
for (const QString &dir : dirs) {
QDir directory = QDir(dir);
for (const QString &themeDir : directory.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
themeDirectories << dir + themeDir;
}
}
for (const QString &dir : themeDirectories) {
for (const QString & file : QDir(dir).entryList(QStringList() << QStringLiteral("metadata.desktop"))) {
themes.append(dir + '/' + file);
}
}
for (const QString & theme : themes) {
int themeSepIndex = theme.lastIndexOf('/', -1);
QString themeRoot = theme.left(themeSepIndex);
int themeNameSepIndex = themeRoot.lastIndexOf('/', -1);
QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1);
KDesktopFile df(theme);
QString name = df.readName();
if (name.isEmpty()) {
name = packageName;
}
m_themes.insert(name, QStringLiteral("__aurorae__svg__") + packageName);
}
}
static const QString s_configUiPath = QStringLiteral("kwin/decorations/%1/contents/ui/config.ui");
static const QString s_configXmlPath = QStringLiteral("kwin/decorations/%1/contents/config/main.xml");
bool ThemeFinder::hasConfiguration(const QString &theme) const
{
if (theme.startsWith(QLatin1String("__aurorae__svg__"))) {
return false;
}
const QString ui = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
s_configUiPath.arg(theme));
const QString xml = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
s_configXmlPath.arg(theme));
return !(ui.isEmpty() || xml.isEmpty());
}
ConfigurationModule::ConfigurationModule(QWidget *parent, const QVariantList &args)
: KCModule(parent, args)
, m_theme(findTheme(args))
{
setLayout(new QVBoxLayout(this));
init();
}
void ConfigurationModule::init()
{
const QString ui = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
s_configUiPath.arg(m_theme));
const QString xml = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
s_configXmlPath.arg(m_theme));
if (ui.isEmpty() || xml.isEmpty()) {
return;
}
KLocalizedTranslator *translator = new KLocalizedTranslator(this);
QCoreApplication::instance()->installTranslator(translator);
const KDesktopFile metaData(QStandardPaths::locate(QStandardPaths::GenericDataLocation,
QStringLiteral("kwin/decorations/%1/metadata.desktop").arg(m_theme)));
const QString translationDomain = metaData.desktopGroup().readEntry("X-KWin-Config-TranslationDomain", QString());
if (!translationDomain.isEmpty()) {
translator->setTranslationDomain(translationDomain);
}
// load the KConfigSkeleton
QFile configFile(xml);
KSharedConfigPtr auroraeConfig = KSharedConfig::openConfig("auroraerc");
KConfigGroup configGroup = auroraeConfig->group(m_theme);
m_skeleton = new KConfigLoader(configGroup, &configFile, this);
// load the ui file
QUiLoader *loader = new QUiLoader(this);
loader->setLanguageChangeEnabled(true);
QFile uiFile(ui);
uiFile.open(QFile::ReadOnly);
QWidget *customConfigForm = loader->load(&uiFile, this);
translator->addContextToMonitor(customConfigForm->objectName());
uiFile.close();
layout()->addWidget(customConfigForm);
// connect the ui file with the skeleton
addConfig(m_skeleton, customConfigForm);
// send a custom event to the translator to retranslate using our translator
QEvent le(QEvent::LanguageChange);
QCoreApplication::sendEvent(customConfigForm, &le);
}
}
#include "aurorae.moc"