diff --git a/clients/aurorae/src/aurorae.cpp b/clients/aurorae/src/aurorae.cpp index a000d5184..17423b620 100644 --- a/clients/aurorae/src/aurorae.cpp +++ b/clients/aurorae/src/aurorae.cpp @@ -1,655 +1,665 @@ /******************************************************************** Copyright (C) 2009, 2010, 2012 Martin Gräßlin 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 . *********************************************************************/ #include "aurorae.h" #include "auroraetheme.h" #include "config-kwin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Aurorae { AuroraeFactory::AuroraeFactory() : QObject() , KDecorationFactoryUnstable() , m_theme(new AuroraeTheme(this)) , m_engine(new QDeclarativeEngine(this)) , m_component(new QDeclarativeComponent(m_engine, this)) , m_engineType(AuroraeEngine) { init(); } void AuroraeFactory::init() { qRegisterMetaType("Qt::MouseButtons"); KConfig conf("auroraerc"); KConfigGroup group(&conf, "Engine"); if (!group.hasKey("EngineType") && !group.hasKey("ThemeName")) { // neither engine type and theme name are configured, use the only available theme initQML(group); } else if (group.hasKey("EngineType")) { const QString engineType = group.readEntry("EngineType", "aurorae").toLower(); if (engineType == "qml") { initQML(group); } else { // fallback to classic Aurorae Themes initAurorae(conf, group); } } else { // fallback to classic Aurorae Themes initAurorae(conf, group); } } void AuroraeFactory::initAurorae(KConfig &conf, KConfigGroup &group) { m_engineType = AuroraeEngine; const QString themeName = group.readEntry("ThemeName"); if (themeName.isEmpty()) { // no theme configured, fall back to Plastik QML theme initQML(group); return; } KConfig config("aurorae/themes/" + themeName + '/' + themeName + "rc", KConfig::FullConfig, "data"); KConfigGroup themeGroup(&conf, themeName); m_theme->loadTheme(themeName, config); m_theme->setBorderSize((KDecorationDefines::BorderSize)themeGroup.readEntry("BorderSize", KDecorationDefines::BorderNormal)); m_theme->setButtonSize((KDecorationDefines::BorderSize)themeGroup.readEntry("ButtonSize", KDecorationDefines::BorderNormal)); m_theme->setTabDragMimeType(tabDragMimeType()); // setup the QML engine - foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { - m_engine->addImportPath(importPath); + /* 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(KGlobal::dirs()->findDirs("module", "imports")); + paths.toBack(); + while (paths.hasPrevious()) { + m_engine->addImportPath(paths.previous()); } m_component->loadUrl(QUrl(KStandardDirs::locate("data", "kwin/aurorae/aurorae.qml"))); m_engine->rootContext()->setContextProperty("auroraeTheme", m_theme); m_themeName = themeName; } void AuroraeFactory::initQML(const KConfigGroup &group) { // try finding the QML package const QString themeName = group.readEntry("ThemeName", "kwin4_decoration_qml_plastik"); kDebug(1212) << "Trying to load QML Decoration " << themeName; const QString internalname = themeName.toLower(); QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(internalname); KService::List offers = KServiceTypeTrader::self()->query("KWin/Decoration", constraint); if (offers.isEmpty()) { kError(1212) << "Couldn't find QML Decoration " << themeName << endl; // TODO: what to do in error case? return; } KService::Ptr service = offers.first(); KPluginInfo plugininfo(service); const QString pluginName = service->property("X-KDE-PluginInfo-Name").toString(); const QString scriptName = service->property("X-Plasma-MainScript").toString(); const QString file = KStandardDirs::locate("data", QLatin1String(KWIN_NAME) + "/decorations/" + pluginName + "/contents/" + scriptName); if (file.isNull()) { kDebug(1212) << "Could not find script file for " << pluginName; // TODO: what to do in error case? return; } m_engineType = QMLEngine; // setup the QML engine - foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { - m_engine->addImportPath(importPath); + /* 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(KGlobal::dirs()->findDirs("module", "imports")); + paths.toBack(); + while (paths.hasPrevious()) { + m_engine->addImportPath(paths.previous()); } m_component->loadUrl(QUrl::fromLocalFile(file)); m_themeName = themeName; } AuroraeFactory::~AuroraeFactory() { s_instance = NULL; } AuroraeFactory *AuroraeFactory::instance() { if (!s_instance) { s_instance = new AuroraeFactory; } return s_instance; } bool AuroraeFactory::reset(unsigned long changed) { if (changed & SettingButtons) { emit buttonsChanged(); } if (changed & SettingFont){ emit titleFontChanged(); } if (changed & SettingCompositing) { return false; } const KConfig conf("auroraerc"); const KConfigGroup group(&conf, "Engine"); const QString themeName = group.readEntry("ThemeName", "example-deco"); const KConfig config("aurorae/themes/" + themeName + '/' + themeName + "rc", KConfig::FullConfig, "data"); const KConfigGroup themeGroup(&conf, themeName); if (themeName != m_themeName) { m_engine->clearComponentCache(); init(); // recreate all decorations return true; } if (m_engineType == AuroraeEngine) { m_theme->setBorderSize((KDecorationDefines::BorderSize)themeGroup.readEntry("BorderSize", KDecorationDefines::BorderNormal)); m_theme->setButtonSize((KDecorationDefines::BorderSize)themeGroup.readEntry("ButtonSize", KDecorationDefines::BorderNormal)); } emit configChanged(); return false; // need hard reset } bool AuroraeFactory::supports(Ability ability) const { switch (ability) { case AbilityAnnounceButtons: case AbilityUsesAlphaChannel: case AbilityAnnounceAlphaChannel: case AbilityButtonMenu: case AbilityButtonSpacer: case AbilityExtendIntoClientArea: case AbilityButtonMinimize: case AbilityButtonMaximize: case AbilityButtonClose: case AbilityButtonAboveOthers: case AbilityButtonBelowOthers: case AbilityButtonShade: case AbilityButtonOnAllDesktops: case AbilityButtonHelp: case AbilityButtonApplicationMenu: case AbilityProvidesShadow: return true; // TODO: correct value from theme case AbilityTabbing: return false; case AbilityUsesBlurBehind: return true; default: return false; } } KDecoration *AuroraeFactory::createDecoration(KDecorationBridge *bridge) { AuroraeClient *client = new AuroraeClient(bridge, this); return client; } QList< KDecorationDefines::BorderSize > AuroraeFactory::borderSizes() const { return QList< BorderSize >() << BorderTiny << BorderNormal << BorderLarge << BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; } QDeclarativeItem *AuroraeFactory::createQmlDecoration(Aurorae::AuroraeClient *client) { QDeclarativeContext *context = new QDeclarativeContext(m_engine->rootContext(), this); context->setContextProperty("decoration", client); return qobject_cast< QDeclarativeItem* >(m_component->create(context)); } AuroraeFactory *AuroraeFactory::s_instance = NULL; /******************************************************* * Client *******************************************************/ AuroraeClient::AuroraeClient(KDecorationBridge *bridge, KDecorationFactory *factory) : KDecorationUnstable(bridge, factory) , m_view(NULL) , m_scene(new QGraphicsScene(this)) , m_item(AuroraeFactory::instance()->createQmlDecoration(this)) { connect(this, SIGNAL(keepAboveChanged(bool)), SIGNAL(keepAboveChangedWrapper())); connect(this, SIGNAL(keepBelowChanged(bool)), SIGNAL(keepBelowChangedWrapper())); connect(AuroraeFactory::instance(), SIGNAL(buttonsChanged()), SIGNAL(buttonsChanged())); connect(AuroraeFactory::instance(), SIGNAL(configChanged()), SIGNAL(configChanged())); connect(AuroraeFactory::instance(), SIGNAL(titleFontChanged()), SIGNAL(fontChanged())); connect(m_item, SIGNAL(alphaChanged()), SLOT(slotAlphaChanged())); connect(this, SIGNAL(appMenuAvailable()), SIGNAL(appMenuAvailableChanged())); connect(this, SIGNAL(appMenuUnavailable()), SIGNAL(appMenuAvailableChanged())); } AuroraeClient::~AuroraeClient() { if (m_item) { m_item->setParent(NULL); m_item->deleteLater(); } m_scene->setParent(NULL); m_scene->deleteLater(); m_view->setParent(NULL); m_view->deleteLater(); } void AuroraeClient::init() { m_scene->setItemIndexMethod(QGraphicsScene::NoIndex); // HACK: we need to add the GraphicsView as a child widget to a normal widget // the GraphicsView eats the mouse release event and by that kwin core starts to move // the decoration each time the decoration is clicked // therefore we use two widgets and inject an own mouse release event to the parent widget // when the graphics view eats a mouse event createMainWidget(); widget()->setAttribute(Qt::WA_TranslucentBackground); widget()->setAttribute(Qt::WA_NoSystemBackground); widget()->installEventFilter(this); m_view = new QGraphicsView(m_scene, widget()); m_view->setAttribute(Qt::WA_TranslucentBackground); m_view->setWindowFlags(Qt::X11BypassWindowManagerHint); m_view->setFrameShape(QFrame::NoFrame); m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_view->setOptimizationFlags(QGraphicsView::DontSavePainterState); m_view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); QPalette pal = m_view->palette(); pal.setColor(m_view->backgroundRole(), Qt::transparent); m_view->setPalette(pal); QPalette pal2 = widget()->palette(); pal2.setColor(widget()->backgroundRole(), Qt::transparent); widget()->setPalette(pal2); if (m_item) m_scene->addItem(m_item); slotAlphaChanged(); AuroraeFactory::instance()->theme()->setCompositingActive(compositingActive()); } bool AuroraeClient::eventFilter(QObject *object, QEvent *event) { // we need to filter the wheel events on the decoration // QML does not yet provide a way to accept wheel events, this will change with Qt 5 // TODO: remove in KDE5 // see BUG: 304248 if (object != widget() || event->type() != QEvent::Wheel) { return KDecorationUnstable::eventFilter(object, event); } QWheelEvent *wheel = static_cast(event); if (mousePosition(wheel->pos()) == PositionCenter) { titlebarMouseWheelOperation(wheel->delta()); return true; } return false; } void AuroraeClient::activeChange() { emit activeChanged(); } void AuroraeClient::captionChange() { emit captionChanged(); } void AuroraeClient::iconChange() { emit iconChanged(); } void AuroraeClient::desktopChange() { emit desktopChanged(); } void AuroraeClient::maximizeChange() { if (!options()->moveResizeMaximizedWindows()) { emit maximizeChanged(); } } void AuroraeClient::resize(const QSize &s) { if (m_item) { m_item->setWidth(s.width()); m_item->setHeight(s.height()); } m_scene->setSceneRect(QRectF(QPoint(0, 0), s)); m_view->resize(s); widget()->resize(s); } void AuroraeClient::shadeChange() { emit shadeChanged(); } void AuroraeClient::borders(int &left, int &right, int &top, int &bottom) const { if (!m_item) { left = right = top = bottom = 0; return; } const bool maximized = maximizeMode() == MaximizeFull && !options()->moveResizeMaximizedWindows(); QObject *borders = NULL; if (maximized) { borders = m_item->findChild("maximizedBorders"); } else { borders = m_item->findChild("borders"); } sizesFromBorders(borders, left, right, top, bottom); } void AuroraeClient::padding(int &left, int &right, int &top, int &bottom) const { if (!m_item) { left = right = top = bottom = 0; return; } if (maximizeMode() == MaximizeFull && !options()->moveResizeMaximizedWindows()) { left = right = top = bottom = 0; return; } sizesFromBorders(m_item->findChild("padding"), left, right, top, bottom); } void AuroraeClient::sizesFromBorders(const QObject *borders, int &left, int &right, int &top, int &bottom) const { if (!borders) { return; } left = borders->property("left").toInt(); right = borders->property("right").toInt(); top = borders->property("top").toInt(); bottom = borders->property("bottom").toInt(); } QSize AuroraeClient::minimumSize() const { return widget()->minimumSize(); } KDecorationDefines::Position AuroraeClient::mousePosition(const QPoint &point) const { // based on the code from deKorator int pos = PositionCenter; if (isShade()) { return Position(pos); } int borderLeft, borderTop, borderRight, borderBottom; borders(borderLeft, borderRight, borderTop, borderBottom); int paddingLeft, paddingTop, paddingRight, paddingBottom; padding(paddingLeft, paddingRight, paddingTop, paddingBottom); const bool maximized = maximizeMode() == MaximizeFull && !options()->moveResizeMaximizedWindows(); int titleEdgeLeft, titleEdgeRight, titleEdgeTop, titleEdgeBottom; AuroraeFactory::instance()->theme()->titleEdges(titleEdgeLeft, titleEdgeTop, titleEdgeRight, titleEdgeBottom, maximized); switch (AuroraeFactory::instance()->theme()->decorationPosition()) { case DecorationTop: borderTop = titleEdgeTop; break; case DecorationLeft: borderLeft = titleEdgeLeft; break; case DecorationRight: borderRight = titleEdgeRight; break; case DecorationBottom: borderBottom = titleEdgeBottom; break; default: break; // nothing } if (point.x() >= (m_view->width() - borderRight - paddingRight)) { pos |= PositionRight; } else if (point.x() <= borderLeft + paddingLeft) { pos |= PositionLeft; } if (point.y() >= m_view->height() - borderBottom - paddingBottom) { pos |= PositionBottom; } else if (point.y() <= borderTop + paddingTop ) { pos |= PositionTop; } return Position(pos); } void AuroraeClient::reset(long unsigned int changed) { KDecoration::reset(changed); } void AuroraeClient::menuClicked() { showWindowMenu(QCursor::pos()); } void AuroraeClient::appMenuClicked() { showApplicationMenu(QCursor::pos()); } void AuroraeClient::toggleShade() { setShade(!isShade()); } void AuroraeClient::toggleKeepAbove() { setKeepAbove(!keepAbove()); } void AuroraeClient::toggleKeepBelow() { setKeepBelow(!keepBelow()); } bool AuroraeClient::isMaximized() const { return maximizeMode()==KDecorationDefines::MaximizeFull && !options()->moveResizeMaximizedWindows(); } void AuroraeClient::titlePressed(int button, int buttons) { titlePressed(static_cast(button), static_cast(buttons)); } void AuroraeClient::titleReleased(int button, int buttons) { titleReleased(static_cast(button), static_cast(buttons)); } void AuroraeClient::titleMouseMoved(int button, int buttons) { titleMouseMoved(static_cast(button), static_cast(buttons)); } void AuroraeClient::titlePressed(Qt::MouseButton button, Qt::MouseButtons buttons) { const QPoint cursor = QCursor::pos(); QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, widget()->mapFromGlobal(cursor), cursor, button, buttons, Qt::NoModifier); processMousePressEvent(event); delete event; event = 0; } void AuroraeClient::titleReleased(Qt::MouseButton button, Qt::MouseButtons buttons) { const QPoint cursor = QCursor::pos(); QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonRelease, widget()->mapFromGlobal(cursor), cursor, button, buttons, Qt::NoModifier); QApplication::sendEvent(widget(), event); delete event; event = 0; } void AuroraeClient::titleMouseMoved(Qt::MouseButton button, Qt::MouseButtons buttons) { const QPoint cursor = QCursor::pos(); QMouseEvent *event = new QMouseEvent(QEvent::MouseMove, widget()->mapFromGlobal(cursor), cursor, button, buttons, Qt::NoModifier); QApplication::sendEvent(widget(), event); delete event; event = 0; } void AuroraeClient::themeChanged() { m_scene->clear(); m_item = AuroraeFactory::instance()->createQmlDecoration(this); if (!m_item) { return; } m_item->setWidth(m_scene->sceneRect().width()); m_item->setHeight(m_scene->sceneRect().height()); m_scene->addItem(m_item); connect(m_item, SIGNAL(alphaChanged()), SLOT(slotAlphaChanged())); slotAlphaChanged(); } int AuroraeClient::doubleClickInterval() const { return QApplication::doubleClickInterval(); } void AuroraeClient::closeWindow() { QMetaObject::invokeMethod(qobject_cast< KDecorationUnstable* >(this), "doCloseWindow", Qt::QueuedConnection); } void AuroraeClient::doCloseWindow() { KDecorationUnstable::closeWindow(); } void AuroraeClient::maximize(int button) { // a maximized window does not need to have a window decoration // in that case we need to delay handling by one cycle // BUG: 304870 QMetaObject::invokeMethod(qobject_cast< KDecorationUnstable* >(this), "doMaximzie", Qt::QueuedConnection, Q_ARG(int, button)); } void AuroraeClient::doMaximzie(int button) { KDecorationUnstable::maximize(static_cast(button)); } void AuroraeClient::titlebarDblClickOperation() { // the double click operation can result in a window being maximized // see maximize QMetaObject::invokeMethod(qobject_cast< KDecorationUnstable* >(this), "doTitlebarDblClickOperation", Qt::QueuedConnection); } void AuroraeClient::doTitlebarDblClickOperation() { KDecorationUnstable::titlebarDblClickOperation(); } QVariant AuroraeClient::readConfig(const QString &key, const QVariant &defaultValue) { KSharedConfigPtr config = KSharedConfig::openConfig("auroraerc"); return config->group(AuroraeFactory::instance()->currentThemeName()).readEntry(key, defaultValue); } void AuroraeClient::slotAlphaChanged() { if (!m_item) { setAlphaEnabled(false); return; } QVariant alphaProperty = m_item->property("alpha"); if (alphaProperty.isValid() && alphaProperty.canConvert()) { setAlphaEnabled(alphaProperty.toBool()); } else { // by default all Aurorae themes use the alpha channel setAlphaEnabled(true); } } QRegion AuroraeClient::region(KDecorationDefines::Region r) { if (r != ExtendedBorderRegion) { return QRegion(); } if (!m_item) { return QRegion(); } if (isMaximized()) { // empty region for maximized windows return QRegion(); } int left, right, top, bottom; left = right = top = bottom = 0; sizesFromBorders(m_item->findChild("extendedBorders"), left, right, top, bottom); if (top == 0 && right == 0 && bottom == 0 && left == 0) { // no extended borders return QRegion(); } int paddingLeft, paddingRight, paddingTop, paddingBottom; paddingLeft = paddingRight = paddingTop = paddingBottom = 0; padding(paddingLeft, paddingRight, paddingTop, paddingBottom); QRect rect = widget()->rect().adjusted(paddingLeft, paddingTop, -paddingRight, -paddingBottom); rect.translate(-paddingLeft, -paddingTop); return QRegion(rect.adjusted(-left, -top, right, bottom)).subtract(rect); } bool AuroraeClient::animationsSupported() const { if (!compositingActive()) { return false; } QPixmap pix(1,1); QPainter p(&pix); const bool raster = p.paintEngine()->type() == QPaintEngine::Raster; p.end(); return raster; } } // namespace Aurorae extern "C" { KDE_EXPORT KDecorationFactory *create_factory() { return Aurorae::AuroraeFactory::instance(); } KWIN_EXPORT int decoration_version() { return KWIN_DECORATION_API_VERSION; } } #include "aurorae.moc" diff --git a/kcmkwin/kwindecoration/kwindecoration.cpp b/kcmkwin/kwindecoration/kwindecoration.cpp index 79d8e3e73..9b1697d5e 100644 --- a/kcmkwin/kwindecoration/kwindecoration.cpp +++ b/kcmkwin/kwindecoration/kwindecoration.cpp @@ -1,618 +1,623 @@ /* This is the new kwindecoration kcontrol module Copyright (c) 2001 Karol Szwed http://gallium.n3.net/ Copyright 2009, 2010 Martin Gräßlin Supports new kwin configuration plugins, and titlebar button position modification via dnd interface. Based on original "kwintheme" (Window Borders) Copyright (C) 2001 Rik Hemsley (rikkus) 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // own #include "kwindecoration.h" #include "buttonsconfigdialog.h" #include "configdialog.h" #include "decorationmodel.h" #include "auroraetheme.h" // Qt #include #include #include #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include // KCModule plugin interface // ========================= K_PLUGIN_FACTORY(KWinDecoFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KWinDecoFactory("kcmkwindecoration")) namespace KWin { KWinDecorationForm::KWinDecorationForm(QWidget* parent) : QWidget(parent) { setupUi(this); } KWinDecorationModule::KWinDecorationModule(QWidget* parent, const QVariantList &) : KCModule(KWinDecoFactory::componentData(), parent) , kwinConfig(KSharedConfig::openConfig("kwinrc")) , m_showTooltips(false) , m_model(NULL) , m_proxyModel(NULL) , m_configLoaded(false) , m_decorationButtons(new DecorationButtons(this)) , m_lastPreviewWidth(-1) , m_previewUpdateTimer(NULL) { qmlRegisterType("org.kde.kwin.aurorae", 0, 1, "AuroraeTheme"); m_ui = new KWinDecorationForm(this); m_ui->configureDecorationButton->setIcon(KIcon("configure")); m_ui->configureButtonsButton->setIcon(KIcon("configure")); m_ui->ghnsButton->setIcon(KIcon("get-hot-new-stuff")); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); KAboutData *about = new KAboutData(I18N_NOOP("kcmkwindecoration"), 0, ki18n("Window Decoration Control Module"), 0, KLocalizedString(), KAboutData::License_GPL, ki18n("(c) 2001 Karol Szwed")); about->addAuthor(ki18n("Karol Szwed"), KLocalizedString(), "gallium@kde.org"); setAboutData(about); } KWinDecorationModule::~KWinDecorationModule() { } void KWinDecorationModule::showEvent(QShowEvent *ev) { KCModule::showEvent(ev); init(); } void KWinDecorationModule::init() { if (m_model) { // init already called return; } const QString mainQmlPath = KStandardDirs::locate("data", "kwin/kcm_kwindecoration/main.qml"); if (mainQmlPath.isNull()) { // TODO 4.11 i18n this KMessageBox::error(this, "

Installation error

" "The resource

kwin/kcm_kwindecoration/main.qml

could not be located in any application data path." "

Please contact your distribution

" "The application will now abort", "Installation Error"); abort(); } KConfigGroup style(kwinConfig, "Style"); // Set up the decoration lists and other UI settings m_model = new DecorationModel(kwinConfig, this); m_proxyModel = new QSortFilterProxyModel(this); m_proxyModel->setSourceModel(m_model); m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_ui->decorationList->setResizeMode(QDeclarativeView::SizeRootObjectToView); - foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { - m_ui->decorationList->engine()->addImportPath(importPath); + /* 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(KGlobal::dirs()->findDirs("module", "imports")); + paths.toBack(); + while (paths.hasPrevious()) { + m_ui->decorationList->engine()->addImportPath(paths.previous()); } m_ui->decorationList->rootContext()->setContextProperty("decorationModel", m_proxyModel); m_ui->decorationList->rootContext()->setContextProperty("decorationBaseModel", m_model); m_ui->decorationList->rootContext()->setContextProperty("options", m_decorationButtons); m_ui->decorationList->rootContext()->setContextProperty("highlightColor", m_ui->decorationList->palette().color(QPalette::Highlight)); m_ui->decorationList->rootContext()->setContextProperty("sliderWidth", m_ui->decorationList->verticalScrollBar()->width()); m_ui->decorationList->rootContext()->setContextProperty("auroraeSource", KStandardDirs::locate("data", "kwin/aurorae/aurorae.qml")); m_ui->decorationList->rootContext()->setContextProperty("decorationActiveCaptionColor", KDecoration::options()->color(ColorFont, true)); m_ui->decorationList->rootContext()->setContextProperty("decorationInactiveCaptionColor", KDecoration::options()->color(ColorFont, false)); m_ui->decorationList->rootContext()->setContextProperty("decorationActiveTitleBarColor", KDecoration::options()->color(ColorTitleBar, true)); m_ui->decorationList->rootContext()->setContextProperty("decorationInactiveTitleBarColor", KDecoration::options()->color(ColorTitleBar, false)); m_ui->decorationList->setSource(mainQmlPath); readConfig(style); connect(m_ui->decorationList->rootObject(), SIGNAL(currentIndexChanged()), SLOT(slotSelectionChanged())); connect(m_ui->decorationList->rootObject(), SIGNAL(widthChanged()), SLOT(updatePreviewWidth())); connect(m_ui->configureButtonsButton, SIGNAL(clicked(bool)), this, SLOT(slotConfigureButtons())); connect(m_ui->ghnsButton, SIGNAL(clicked(bool)), SLOT(slotGHNSClicked())); connect(m_ui->searchEdit, SIGNAL(textChanged(QString)), m_proxyModel, SLOT(setFilterFixedString(QString))); connect(m_ui->searchEdit, SIGNAL(textChanged(QString)), m_ui->decorationList->rootObject(), SLOT(returnToBounds()), Qt::QueuedConnection); connect(m_ui->searchEdit, SIGNAL(textChanged(QString)), SLOT(updateScrollbarRange()), Qt::QueuedConnection); connect(m_ui->configureDecorationButton, SIGNAL(clicked(bool)), SLOT(slotConfigureDecoration())); m_ui->decorationList->disconnect(m_ui->decorationList->verticalScrollBar()); m_ui->decorationList->verticalScrollBar()->disconnect(m_ui->decorationList); connect(m_ui->decorationList->rootObject(), SIGNAL(contentYChanged()), SLOT(updateScrollbarValue())); connect(m_ui->decorationList->rootObject(), SIGNAL(contentHeightChanged()), SLOT(updateScrollbarRange())); connect(m_ui->decorationList->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), SLOT(updateScrollbarRange())); connect(m_ui->decorationList->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(updateViewPosition(int))); m_ui->decorationList->installEventFilter(this); m_ui->decorationList->viewport()->installEventFilter(this); QMetaObject::invokeMethod(this, "updatePreviews", Qt::QueuedConnection); updateScrollbarRange(); } int KWinDecorationModule::itemWidth() const { const int width = m_ui->decorationList->rootObject()->property("width").toInt(); const int sliderWidth = m_ui->decorationList->verticalScrollBar()->width(); return width - sliderWidth; } // This is the selection handler setting void KWinDecorationModule::slotSelectionChanged() { emit KCModule::changed(true); } // Reads the kwin config settings, and sets all UI controls to those settings // Updating the config plugin if required void KWinDecorationModule::readConfig(const KConfigGroup & conf) { m_showTooltips = conf.readEntry("ShowToolTips", true); // Find the corresponding decoration name to that of // the current plugin library name QString libraryName = conf.readEntry("PluginLib", "kwin3_oxygen"); if (libraryName.isEmpty()) { // Selected decoration doesn't exist, use the default libraryName = "kwin3_oxygen"; } const int bsize = conf.readEntry("BorderSize", (int)BorderNormal); BorderSize borderSize = BorderNormal; if (bsize >= BorderTiny && bsize < BordersCount) borderSize = static_cast< BorderSize >(bsize); if (libraryName == "kwin3_aurorae") { KConfig auroraeConfig("auroraerc"); KConfigGroup group(&auroraeConfig, "Engine"); const QString themeName = group.readEntry("ThemeName", "example-deco"); const QString type = group.readEntry("EngineType", "aurorae"); const QModelIndex index = m_proxyModel->mapFromSource(m_model->indexOfAuroraeName(themeName, type)); if (index.isValid()) { m_ui->decorationList->rootObject()->setProperty("currentIndex", index.row()); } } else { const QModelIndex index = m_proxyModel->mapFromSource(m_model->indexOfLibrary(libraryName)); if (index.isValid()) { m_model->setBorderSize(index, borderSize); m_ui->decorationList->rootObject()->setProperty("currentIndex", index.row()); } } // Buttons tab // ============ m_decorationButtons->setCustomPositions(conf.readEntry("CustomButtonPositions", false)); // Menu and onAllDesktops buttons are default on LHS m_decorationButtons->setLeftButtons(conf.readEntry("ButtonsOnLeft", KDecorationOptions::defaultTitleButtonsLeft())); // Help, Minimize, Maximize and Close are default on RHS m_decorationButtons->setRightButtons(conf.readEntry("ButtonsOnRight", KDecorationOptions::defaultTitleButtonsRight())); if (m_configLoaded) m_model->changeButtons(m_decorationButtons); else { m_configLoaded = true; m_model->setButtons(m_decorationButtons->customPositions(), m_decorationButtons->leftButtons(), m_decorationButtons->rightButtons()); } emit KCModule::changed(false); } // Writes the selected user configuration to the kwin config file void KWinDecorationModule::writeConfig(KConfigGroup & conf) { const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(m_ui->decorationList->rootObject()->property("currentIndex").toInt(), 0)); const QString libName = m_model->data(index, DecorationModel::LibraryNameRole).toString(); // General settings conf.writeEntry("PluginLib", libName); conf.writeEntry("CustomButtonPositions", m_decorationButtons->customPositions()); conf.writeEntry("ShowToolTips", m_showTooltips); // Button settings conf.writeEntry("ButtonsOnLeft", m_decorationButtons->leftButtons()); conf.writeEntry("ButtonsOnRight", m_decorationButtons->rightButtons()); conf.writeEntry("BorderSize", static_cast(m_model->data(index, DecorationModel::BorderSizeRole).toInt())); if (m_model->data(index, DecorationModel::TypeRole).toInt() == DecorationModelData::AuroraeDecoration || m_model->data(index, DecorationModel::TypeRole).toInt() == DecorationModelData::QmlDecoration) { KConfig auroraeConfig("auroraerc"); KConfigGroup group(&auroraeConfig, "Engine"); group.writeEntry("ThemeName", m_model->data(index, DecorationModel::AuroraeNameRole).toString()); if (m_model->data(index, DecorationModel::TypeRole).toInt() == DecorationModelData::QmlDecoration) { group.writeEntry("EngineType", "qml"); } else { group.deleteEntry("EngineType"); } group.sync(); } // We saved, so tell kcmodule that there have been no new user changes made. emit KCModule::changed(false); } // Virutal functions required by KCModule void KWinDecorationModule::load() { const KConfigGroup config(kwinConfig, "Style"); // Reset by re-reading the config readConfig(config); } void KWinDecorationModule::save() { KConfigGroup config(kwinConfig, "Style"); writeConfig(config); config.sync(); // Send signal to all kwin instances QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig"); QDBusConnection::sessionBus().send(message); } void KWinDecorationModule::defaults() { // Set the KDE defaults m_showTooltips = true; const QModelIndex index = m_proxyModel->mapFromSource(m_model->indexOfName(i18n("Oxygen"))); if (index.isValid()) m_ui->decorationList->rootObject()->setProperty("currentIndex", index.row()); m_decorationButtons->resetToDefaults(); m_model->changeButtons(m_decorationButtons); emit changed(true); } QString KWinDecorationModule::quickHelp() const { return i18n("

Window Manager Decoration

" "

This module allows you to choose the window border decorations, " "as well as titlebar button positions and custom decoration options.

" "To choose a theme for your window decoration click on its name and apply your choice by clicking the \"Apply\" button below." " If you do not want to apply your choice you can click the \"Reset\" button to discard your changes." "

You can configure each theme. There are different options specific for each theme.

" "

On the \"Buttons\" tab check the \"Use custom titlebar button positions\" box " "and you can change the positions of the buttons to your liking.

"); } void KWinDecorationModule::slotConfigureButtons() { QPointer< KWinDecorationButtonsConfigDialog > configDialog = new KWinDecorationButtonsConfigDialog(m_decorationButtons, m_showTooltips, this); if (configDialog->exec() == KDialog::Accepted) { m_decorationButtons->setCustomPositions(configDialog->customPositions()); m_showTooltips = configDialog->showTooltips(); m_decorationButtons->setLeftButtons(configDialog->buttonsLeft()); m_decorationButtons->setRightButtons(configDialog->buttonsRight()); m_model->changeButtons(m_decorationButtons); emit changed(true); } delete configDialog; } void KWinDecorationModule::slotGHNSClicked() { QPointer downloadDialog = new KNS3::DownloadDialog("aurorae.knsrc", this); if (downloadDialog->exec() == KDialog::Accepted) { if (!downloadDialog->changedEntries().isEmpty()) { const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(m_ui->decorationList->rootObject()->property("currentIndex").toInt(), 0)); const QString libraryName = m_model->data(index, DecorationModel::LibraryNameRole).toString(); bool aurorae = m_model->data(index, DecorationModel::TypeRole).toInt() == DecorationModelData::AuroraeDecoration; bool qml = m_model->data(index, DecorationModel::TypeRole).toInt() == DecorationModelData::QmlDecoration; const QString auroraeName = m_model->data(index, DecorationModel::AuroraeNameRole).toString(); m_model->reload(); if (aurorae) { const QModelIndex proxyIndex = m_proxyModel->mapFromSource(m_model->indexOfAuroraeName(auroraeName, "aurorae")); if (proxyIndex.isValid()) m_ui->decorationList->rootObject()->setProperty("currentIndex", proxyIndex.row()); } else if (qml) { const QModelIndex proxyIndex = m_proxyModel->mapFromSource(m_model->indexOfAuroraeName(auroraeName, "qml")); if (proxyIndex.isValid()) m_ui->decorationList->rootObject()->setProperty("currentIndex", proxyIndex.row()); } else { const QModelIndex proxyIndex = m_proxyModel->mapFromSource(m_model->indexOfLibrary(libraryName)); if (proxyIndex.isValid()) m_ui->decorationList->rootObject()->setProperty("currentIndex", proxyIndex.row()); } m_lastPreviewWidth = 0; updatePreviews(); } } delete downloadDialog; } void KWinDecorationModule::slotConfigureDecoration() { const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(m_ui->decorationList->rootObject()->property("currentIndex").toInt(), 0)); bool reload = false; if (index.data(DecorationModel::TypeRole).toInt() == DecorationModelData::AuroraeDecoration || index.data(DecorationModel::TypeRole).toInt() == DecorationModelData::QmlDecoration) { QPointer< KDialog > dlg = new KDialog(this); dlg->setCaption(i18n("Decoration Options")); dlg->setButtons(KDialog::Ok | KDialog::Cancel); KWinAuroraeConfigForm *form = new KWinAuroraeConfigForm(dlg); form->enableNoSideBorderSupport(index.data(DecorationModel::TypeRole).toInt() == DecorationModelData::QmlDecoration); dlg->setMainWidget(form); form->borderSizesCombo->setCurrentIndex(index.data(DecorationModel::BorderSizeRole).toInt()); form->buttonSizesCombo->setCurrentIndex(index.data(DecorationModel::ButtonSizeRole).toInt()); form->closeWindowsDoubleClick->setChecked(index.data(DecorationModel::CloseOnDblClickRole).toBool()); // in case of QmlDecoration look for a config.ui in the package structure KConfigDialogManager *configManager = NULL; if (index.data(DecorationModel::TypeRole).toInt() == DecorationModelData::QmlDecoration) { const QString packageName = index.data(DecorationModel::AuroraeNameRole).toString(); const QString uiPath = KStandardDirs::locate("data", "kwin/decorations/" + packageName + "/contents/ui/config.ui"); const QString configPath = KStandardDirs::locate("data", "kwin/decorations/" + packageName + "/contents/config/main.xml"); if (!uiPath.isEmpty() && !configPath.isEmpty()) { // load the KConfigSkeleton QFile configFile(configPath); KSharedConfigPtr auroraeConfig = KSharedConfig::openConfig("auroraerc"); KConfigGroup configGroup = auroraeConfig->group(packageName); Plasma::ConfigLoader *skeleton = new Plasma::ConfigLoader(&configGroup, &configFile, dlg); // load the ui file QUiLoader *loader = new QUiLoader(dlg); QFile uiFile(uiPath); uiFile.open(QFile::ReadOnly); QWidget *customConfigForm = loader->load(&uiFile, form); uiFile.close(); form->layout()->addWidget(customConfigForm); // connect the ui file with the skeleton configManager = new KConfigDialogManager(customConfigForm, skeleton); configManager->updateWidgets(); } } if (dlg->exec() == KDialog::Accepted) { m_model->setData(index, form->borderSizesCombo->currentIndex(), DecorationModel::BorderSizeRole); m_model->setData(index, form->buttonSizesCombo->currentIndex(), DecorationModel::ButtonSizeRole); m_model->setData(index, form->closeWindowsDoubleClick->isChecked(), DecorationModel::CloseOnDblClickRole); if (configManager && configManager->hasChanged()) { // we have a config manager and the settings changed configManager->updateSettings(); m_model->notifyConfigChanged(index); } reload = true; } delete dlg; } else { QString name = index.data(DecorationModel::LibraryNameRole).toString(); QList< QVariant > borderSizes = index.data(DecorationModel::BorderSizesRole).toList(); const KDecorationDefines::BorderSize size = static_cast(index.data(DecorationModel::BorderSizeRole).toInt()); QPointer< KWinDecorationConfigDialog > configDialog = new KWinDecorationConfigDialog(name, borderSizes, size, this); if (configDialog->exec() == KDialog::Accepted) { m_model->setData(index, configDialog->borderSize(), DecorationModel::BorderSizeRole); reload = true; } delete configDialog; } if (reload) { // Send signal to all kwin instances QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig"); QDBusConnection::sessionBus().send(message); } } bool KWinDecorationModule::eventFilter(QObject *o, QEvent *e) { if (o == m_ui->decorationList) { if (e->type() == QEvent::Resize) updateScrollbarRange(); else if (e->type() == QEvent::KeyPress) { int d = 0; const int currentRow = m_ui->decorationList->rootObject()->property("currentIndex").toInt(); const int key = static_cast(e)->key(); switch (key) { case Qt::Key_Home: d = -currentRow; break; case Qt::Key_End: d = m_proxyModel->rowCount() - (1 + currentRow); break; case Qt::Key_Up: d = -1; break; case Qt::Key_Down: d = 1; break; case Qt::Key_PageUp: case Qt::Key_PageDown: d = 150; if (QObject *decoItem = m_ui->decorationList->rootObject()->findChild("decorationItem")) { QVariant v = decoItem->property("height"); if (v.isValid()) d = v.toInt(); } if (d > 0) d = qMax(m_ui->decorationList->height() / d, 1); if (key == Qt::Key_PageUp) d = -d; break; default: break; } if (d) { d = qMin(qMax(0, currentRow + d), m_proxyModel->rowCount()); m_ui->decorationList->rootObject()->setProperty("currentIndex", d); return true; } } } else if (m_ui->decorationList->viewport()) { if (e->type() == QEvent::Wheel) { return static_cast(e)->orientation() == Qt::Horizontal; } } return KCModule::eventFilter(o, e); } void KWinDecorationModule::updatePreviews() { if (!m_model) { return; } m_ui->decorationList->rootContext()->setContextProperty("sliderWidth", m_ui->decorationList->verticalScrollBar()->width()); const int newWidth = m_ui->decorationList->rootObject()->property("width").toInt(); if (newWidth == m_lastPreviewWidth) return; m_lastPreviewWidth = newWidth; const int h = m_ui->decorationList->rootObject()->property("contentHeight").toInt(); const int y = m_ui->decorationList->rootObject()->property("contentY").toInt(); // start at first element in sight int row = 0; if (h > 0) row = qMin(qMax(0, y*m_model->rowCount()/h), m_model->rowCount()); m_model->regeneratePreviews(row); } void KWinDecorationModule::updatePreviewWidth() { if (!m_previewUpdateTimer) { m_previewUpdateTimer = new QTimer(this); m_previewUpdateTimer->setSingleShot(true); connect(m_previewUpdateTimer, SIGNAL(timeout()), this, SLOT(updatePreviews())); } m_model->stopPreviewGeneration(); m_previewUpdateTimer->start(100); } void KWinDecorationModule::updateScrollbarRange() { m_ui->decorationList->verticalScrollBar()->blockSignals(true); const bool atMinimum = m_ui->decorationList->rootObject()->property("atYBeginning").toBool(); const int h = m_ui->decorationList->rootObject()->property("contentHeight").toInt(); const int y = atMinimum ? m_ui->decorationList->rootObject()->property("contentY").toInt() : 0; m_ui->decorationList->verticalScrollBar()->setRange(y, y + h - m_ui->decorationList->height()); m_ui->decorationList->verticalScrollBar()->setPageStep(m_ui->decorationList->verticalScrollBar()->maximum()/m_model->rowCount()); m_ui->decorationList->verticalScrollBar()->blockSignals(false); } void KWinDecorationModule::updateScrollbarValue() { const int v = m_ui->decorationList->rootObject()->property("contentY").toInt(); m_ui->decorationList->verticalScrollBar()->blockSignals(true); // skippig this will kill kinetic scrolling but the scrollwidth is too low m_ui->decorationList->verticalScrollBar()->setValue(v); m_ui->decorationList->verticalScrollBar()->blockSignals(false); } void KWinDecorationModule::updateViewPosition(int v) { QGraphicsObject *list = m_ui->decorationList->rootObject(); list->setProperty("contentY", v); } DecorationButtons::DecorationButtons(QObject *parent) : QObject(parent) , m_customPositions(false) , m_leftButtons(KDecorationOptions::defaultTitleButtonsLeft()) , m_rightButtons(KDecorationOptions::defaultTitleButtonsRight()) { } DecorationButtons::~DecorationButtons() { } bool DecorationButtons::customPositions() const { return m_customPositions; } const QString &DecorationButtons::leftButtons() const { return m_leftButtons; } const QString &DecorationButtons::rightButtons() const { return m_rightButtons; } void DecorationButtons::setCustomPositions(bool set) { if (m_customPositions == set) { return; } m_customPositions = set; emit customPositionsChanged(); } void DecorationButtons::setLeftButtons(const QString &leftButtons) { if (m_leftButtons == leftButtons) { return; } m_leftButtons = leftButtons; emit leftButtonsChanged(); } void DecorationButtons::setRightButtons (const QString &rightButtons) { if (m_rightButtons == rightButtons) { return; } m_rightButtons = rightButtons; emit rightButtonsChanged(); } void DecorationButtons::resetToDefaults() { setCustomPositions(false); setLeftButtons(KDecorationOptions::defaultTitleButtonsLeft()); setRightButtons(KDecorationOptions::defaultTitleButtonsRight()); } } // namespace KWin #include "kwindecoration.moc" diff --git a/scripting/scripting.cpp b/scripting/scripting.cpp index 0bd07f7f1..c67704de0 100644 --- a/scripting/scripting.cpp +++ b/scripting/scripting.cpp @@ -1,775 +1,770 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Rohan Prabhu Copyright (C) 2011 Martin Gräßlin 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 . *********************************************************************/ #include "scripting.h" // own #include "meta.h" #include "scriptingutils.h" #include "workspace_wrapper.h" #include "scripting_model.h" #include "../client.h" #include "../thumbnailitem.h" #include "../options.h" #include "../workspace.h" // KDE #include #include #include #include #include #include #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include QScriptValue kwinScriptPrint(QScriptContext *context, QScriptEngine *engine) { KWin::AbstractScript *script = qobject_cast(context->callee().data().toQObject()); if (!script) { return engine->undefinedValue(); } QString result; QTextStream stream(&result); for (int i = 0; i < context->argumentCount(); ++i) { if (i > 0) { stream << " "; } QScriptValue argument = context->argument(i); if (KWin::Client *client = qscriptvalue_cast(argument)) { client->print(stream); } else { stream << argument.toString(); } } script->printMessage(result); return engine->undefinedValue(); } QScriptValue kwinScriptReadConfig(QScriptContext *context, QScriptEngine *engine) { KWin::AbstractScript *script = qobject_cast(context->callee().data().toQObject()); if (!script) { return engine->undefinedValue(); } if (context->argumentCount() < 1 || context->argumentCount() > 2) { kDebug(1212) << "Incorrect number of arguments"; return engine->undefinedValue(); } const QString key = context->argument(0).toString(); QVariant defaultValue; if (context->argumentCount() == 2) { defaultValue = context->argument(1).toVariant(); } return engine->newVariant(script->config().readEntry(key, defaultValue)); } QScriptValue kwinScriptGlobalShortcut(QScriptContext *context, QScriptEngine *engine) { return KWin::globalShortcut(context, engine); } QScriptValue kwinAssertTrue(QScriptContext *context, QScriptEngine *engine) { return KWin::scriptingAssert(context, engine, 1, 2, true); } QScriptValue kwinAssertFalse(QScriptContext *context, QScriptEngine *engine) { return KWin::scriptingAssert(context, engine, 1, 2, false); } QScriptValue kwinAssertEquals(QScriptContext *context, QScriptEngine *engine) { return KWin::scriptingAssert(context, engine, 2, 3); } QScriptValue kwinAssertNull(QScriptContext *context, QScriptEngine *engine) { if (!KWin::validateParameters(context, 1, 2)) { return engine->undefinedValue(); } if (!context->argument(0).isNull()) { if (context->argumentCount() == 2) { context->throwError(QScriptContext::UnknownError, context->argument(1).toString()); } else { context->throwError(QScriptContext::UnknownError, i18nc("Assertion failed in KWin script with given value", "Assertion failed: %1 is not null", context->argument(0).toString())); } return engine->undefinedValue(); } return true; } QScriptValue kwinAssertNotNull(QScriptContext *context, QScriptEngine *engine) { if (!KWin::validateParameters(context, 1, 2)) { return engine->undefinedValue(); } if (context->argument(0).isNull()) { if (context->argumentCount() == 2) { context->throwError(QScriptContext::UnknownError, context->argument(1).toString()); } else { context->throwError(QScriptContext::UnknownError, i18nc("Assertion failed in KWin script", "Assertion failed: argument is null")); } return engine->undefinedValue(); } return true; } QScriptValue kwinRegisterScreenEdge(QScriptContext *context, QScriptEngine *engine) { return KWin::registerScreenEdge(context, engine); } QScriptValue kwinRegisterUserActionsMenu(QScriptContext *context, QScriptEngine *engine) { return KWin::registerUserActionsMenu(context, engine); } QScriptValue kwinCallDBus(QScriptContext *context, QScriptEngine *engine) { KWin::AbstractScript *script = qobject_cast(context->callee().data().toQObject()); if (!script) { context->throwError(QScriptContext::UnknownError, "Internal Error: script not registered"); return engine->undefinedValue(); } if (context->argumentCount() < 4) { context->throwError(QScriptContext::SyntaxError, i18nc("Error in KWin Script", "Invalid number of arguments. At least service, path, interface and method need to be provided")); return engine->undefinedValue(); } if (!KWin::validateArgumentType(context)) { context->throwError(QScriptContext::SyntaxError, i18nc("Error in KWin Script", "Invalid type. Service, path, interface and method need to be string values")); return engine->undefinedValue(); } const QString service = context->argument(0).toString(); const QString path = context->argument(1).toString(); const QString interface = context->argument(2).toString(); const QString method = context->argument(3).toString(); int argumentsCount = context->argumentCount(); if (context->argument(argumentsCount-1).isFunction()) { --argumentsCount; } QDBusMessage msg = QDBusMessage::createMethodCall(service, path, interface, method); QVariantList arguments; for (int i=4; iargument(i).isArray()) { QStringList stringArray = engine->fromScriptValue(context->argument(i)); arguments << qVariantFromValue(stringArray); } else { arguments << context->argument(i).toVariant(); } } if (!arguments.isEmpty()) { msg.setArguments(arguments); } if (argumentsCount == context->argumentCount()) { // no callback, just fire and forget QDBusConnection::sessionBus().asyncCall(msg); } else { // with a callback QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(QDBusConnection::sessionBus().asyncCall(msg), script); watcher->setProperty("callback", script->registerCallback(context->argument(context->argumentCount()-1))); QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), script, SLOT(slotPendingDBusCall(QDBusPendingCallWatcher*))); } return engine->undefinedValue(); } KWin::AbstractScript::AbstractScript(int id, QString scriptName, QString pluginName, QObject *parent) : QObject(parent) , m_scriptId(id) , m_pluginName(pluginName) , m_running(false) , m_workspace(new WorkspaceWrapper(this)) { m_scriptFile.setFileName(scriptName); if (m_pluginName.isNull()) { m_pluginName = scriptName; } } KWin::AbstractScript::~AbstractScript() { } KConfigGroup KWin::AbstractScript::config() const { return KGlobal::config()->group("Script-" + m_pluginName); } void KWin::AbstractScript::stop() { deleteLater(); } void KWin::AbstractScript::printMessage(const QString &message) { kDebug(1212) << scriptFile().fileName() << ":" << message; emit print(message); } void KWin::AbstractScript::registerShortcut(QAction *a, QScriptValue callback) { m_shortcutCallbacks.insert(a, callback); connect(a, SIGNAL(triggered(bool)), SLOT(globalShortcutTriggered())); } void KWin::AbstractScript::globalShortcutTriggered() { callGlobalShortcutCallback(this, sender()); } bool KWin::AbstractScript::borderActivated(KWin::ElectricBorder edge) { screenEdgeActivated(this, edge); return true; } void KWin::AbstractScript::installScriptFunctions(QScriptEngine* engine) { // add our print QScriptValue printFunc = engine->newFunction(kwinScriptPrint); printFunc.setData(engine->newQObject(this)); engine->globalObject().setProperty("print", printFunc); // add read config QScriptValue configFunc = engine->newFunction(kwinScriptReadConfig); configFunc.setData(engine->newQObject(this)); engine->globalObject().setProperty("readConfig", configFunc); QScriptValue dbusCallFunc = engine->newFunction(kwinCallDBus); dbusCallFunc.setData(engine->newQObject(this)); engine->globalObject().setProperty("callDBus", dbusCallFunc); // add global Shortcut registerGlobalShortcutFunction(this, engine, kwinScriptGlobalShortcut); // add screen edge registerScreenEdgeFunction(this, engine, kwinRegisterScreenEdge); // add user actions menu register function regesterUserActionsMenuFunction(this, engine, kwinRegisterUserActionsMenu); // add assertions QScriptValue assertTrueFunc = engine->newFunction(kwinAssertTrue); engine->globalObject().setProperty("assertTrue", assertTrueFunc); engine->globalObject().setProperty("assert", assertTrueFunc); QScriptValue assertFalseFunc = engine->newFunction(kwinAssertFalse); engine->globalObject().setProperty("assertFalse", assertFalseFunc); QScriptValue assertEqualsFunc = engine->newFunction(kwinAssertEquals); engine->globalObject().setProperty("assertEquals", assertEqualsFunc); QScriptValue assertNullFunc = engine->newFunction(kwinAssertNull); engine->globalObject().setProperty("assertNull", assertNullFunc); engine->globalObject().setProperty("assertEquals", assertEqualsFunc); QScriptValue assertNotNullFunc = engine->newFunction(kwinAssertNotNull); engine->globalObject().setProperty("assertNotNull", assertNotNullFunc); // global properties engine->globalObject().setProperty("KWin", engine->newQMetaObject(&WorkspaceWrapper::staticMetaObject)); QScriptValue workspace = engine->newQObject(AbstractScript::workspace(), QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassContents | QScriptEngine::ExcludeDeleteLater); engine->globalObject().setProperty("workspace", workspace, QScriptValue::Undeletable); // install meta functions KWin::MetaScripting::registration(engine); } int KWin::AbstractScript::registerCallback(QScriptValue value) { int id = m_callbacks.size(); m_callbacks.insert(id, value); return id; } void KWin::AbstractScript::slotPendingDBusCall(QDBusPendingCallWatcher* watcher) { if (watcher->isError()) { kDebug(1212) << "Received D-Bus message is error"; watcher->deleteLater(); return; } const int id = watcher->property("callback").toInt(); QDBusMessage reply = watcher->reply(); QScriptValue callback (m_callbacks.value(id)); QScriptValueList arguments; foreach (const QVariant &argument, reply.arguments()) { arguments << callback.engine()->newVariant(argument); } callback.call(QScriptValue(), arguments); m_callbacks.remove(id); watcher->deleteLater(); } void KWin::AbstractScript::registerUseractionsMenuCallback(QScriptValue callback) { m_userActionsMenuCallbacks.append(callback); } QList< QAction * > KWin::AbstractScript::actionsForUserActionMenu(KWin::Client *c, QMenu *parent) { QList returnActions; for (QList::const_iterator it = m_userActionsMenuCallbacks.constBegin(); it != m_userActionsMenuCallbacks.constEnd(); ++it) { QScriptValue callback(*it); QScriptValueList arguments; arguments << callback.engine()->newQObject(c); QScriptValue actions = callback.call(QScriptValue(), arguments); if (!actions.isValid() || actions.isUndefined() || actions.isNull()) { // script does not want to handle this Client continue; } if (actions.isObject()) { QAction *a = scriptValueToAction(actions, parent); if (a) { returnActions << a; } } } return returnActions; } QAction *KWin::AbstractScript::scriptValueToAction(QScriptValue &value, QMenu *parent) { QScriptValue titleValue = value.property("text"); QScriptValue checkableValue = value.property("checkable"); QScriptValue checkedValue = value.property("checked"); QScriptValue itemsValue = value.property("items"); QScriptValue triggeredValue = value.property("triggered"); if (!titleValue.isValid()) { // title not specified - does not make any sense to include return NULL; } const QString title = titleValue.toString(); const bool checkable = checkableValue.isValid() && checkableValue.toBool(); const bool checked = checkable && checkedValue.isValid() && checkedValue.toBool(); // either a menu or a menu item if (itemsValue.isValid()) { if (!itemsValue.isArray()) { // not an array, so cannot be a menu return NULL; } QScriptValue lengthValue = itemsValue.property("length"); if (!lengthValue.isValid() || !lengthValue.isNumber() || lengthValue.toInteger() == 0) { // length property missing return NULL; } return createMenu(title, itemsValue, parent); } else if (triggeredValue.isValid()) { // normal item return createAction(title, checkable, checked, triggeredValue, parent); } return NULL; } QAction *KWin::AbstractScript::createAction(const QString &title, bool checkable, bool checked, QScriptValue &callback, QMenu *parent) { QAction *action = new QAction(title, parent); action->setCheckable(checkable); action->setChecked(checked); // TODO: rename m_shortcutCallbacks m_shortcutCallbacks.insert(action, callback); connect(action, SIGNAL(triggered(bool)), SLOT(globalShortcutTriggered())); connect(action, SIGNAL(destroyed(QObject*)), SLOT(actionDestroyed(QObject*))); return action; } QAction *KWin::AbstractScript::createMenu(const QString &title, QScriptValue &items, QMenu *parent) { QMenu *menu = new QMenu(title, parent); const int length = static_cast(items.property("length").toInteger()); for (int i=0; iaddAction(a); } } } return menu->menuAction(); } void KWin::AbstractScript::actionDestroyed(QObject *object) { // TODO: Qt 5 - change to lambda function m_shortcutCallbacks.remove(static_cast(object)); } KWin::Script::Script(int id, QString scriptName, QString pluginName, QObject* parent) : AbstractScript(id, scriptName, pluginName, parent) , m_engine(new QScriptEngine(this)) , m_starting(false) , m_agent(new ScriptUnloaderAgent(this)) { QDBusConnection::sessionBus().registerObject('/' + QString::number(scriptId()), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables); } KWin::Script::~Script() { QDBusConnection::sessionBus().unregisterObject('/' + QString::number(scriptId())); } void KWin::Script::run() { if (running() || m_starting) { return; } m_starting = true; QFutureWatcher *watcher = new QFutureWatcher(this); connect(watcher, SIGNAL(finished()), SLOT(slotScriptLoadedFromFile())); watcher->setFuture(QtConcurrent::run(this, &KWin::Script::loadScriptFromFile)); } QByteArray KWin::Script::loadScriptFromFile() { if (!scriptFile().open(QIODevice::ReadOnly)) { return QByteArray(); } QByteArray result(scriptFile().readAll()); scriptFile().close(); return result; } void KWin::Script::slotScriptLoadedFromFile() { QFutureWatcher *watcher = dynamic_cast< QFutureWatcher< QByteArray>* >(sender()); if (!watcher) { // not invoked from a QFutureWatcher return; } if (watcher->result().isNull()) { // do not load empty script deleteLater(); watcher->deleteLater(); return; } QScriptValue optionsValue = m_engine->newQObject(options, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassContents | QScriptEngine::ExcludeDeleteLater); m_engine->globalObject().setProperty("options", optionsValue, QScriptValue::Undeletable); m_engine->globalObject().setProperty("QTimer", constructTimerClass(m_engine)); QObject::connect(m_engine, SIGNAL(signalHandlerException(QScriptValue)), this, SLOT(sigException(QScriptValue))); KWin::MetaScripting::supplyConfig(m_engine); installScriptFunctions(m_engine); QScriptValue ret = m_engine->evaluate(watcher->result()); if (ret.isError()) { sigException(ret); deleteLater(); } watcher->deleteLater(); setRunning(true); m_starting = false; } void KWin::Script::sigException(const QScriptValue& exception) { QScriptValue ret = exception; if (ret.isError()) { kDebug(1212) << "defaultscript encountered an error at [Line " << m_engine->uncaughtExceptionLineNumber() << "]"; kDebug(1212) << "Message: " << ret.toString(); kDebug(1212) << "-----------------"; QScriptValueIterator iter(ret); while (iter.hasNext()) { iter.next(); qDebug() << " " << iter.name() << ": " << iter.value().toString(); } } emit printError(exception.toString()); stop(); } KWin::ScriptUnloaderAgent::ScriptUnloaderAgent(KWin::Script *script) : QScriptEngineAgent(script->engine()) , m_script(script) { script->engine()->setAgent(this); } void KWin::ScriptUnloaderAgent::scriptUnload(qint64 id) { Q_UNUSED(id) m_script->stop(); } KWin::DeclarativeScript::DeclarativeScript(int id, QString scriptName, QString pluginName, QObject* parent) : AbstractScript(id, scriptName, pluginName, parent) , m_engine(new QDeclarativeEngine(this)) , m_component(new QDeclarativeComponent(m_engine, this)) , m_scene(new QGraphicsScene(this)) { } KWin::DeclarativeScript::~DeclarativeScript() { } void KWin::DeclarativeScript::run() { if (running()) { return; } - - foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { - m_engine->addImportPath(importPath); - } - // add read config KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(m_engine); kdeclarative.initialize(); kdeclarative.setupBindings(); installScriptFunctions(kdeclarative.scriptEngine()); qmlRegisterType("org.kde.kwin", 0, 1, "DesktopThumbnailItem"); qmlRegisterType("org.kde.kwin", 0, 1, "ThumbnailItem"); qmlRegisterType(); qmlRegisterType("org.kde.kwin", 0, 1, "ClientModel"); qmlRegisterType("org.kde.kwin", 0, 1, "ClientModelByScreen"); qmlRegisterType("org.kde.kwin", 0, 1, "ClientModelByScreenAndDesktop"); qmlRegisterType("org.kde.kwin", 0, 1, "ClientFilterModel"); qmlRegisterType(); m_engine->rootContext()->setContextProperty("options", options); m_component->loadUrl(QUrl::fromLocalFile(scriptFile().fileName())); if (m_component->isLoading()) { connect(m_component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), SLOT(createComponent())); } else { createComponent(); } } void KWin::DeclarativeScript::createComponent() { if (m_component->isError()) { kDebug(1212) << "Component failed to load: " << m_component->errors(); } else { m_scene->addItem(qobject_cast(m_component->create())); } setRunning(true); } KWin::Scripting *KWin::Scripting::s_self = NULL; KWin::Scripting *KWin::Scripting::create(QObject *parent) { Q_ASSERT(!s_self); s_self = new Scripting(parent); return s_self; } KWin::Scripting::Scripting(QObject *parent) : QObject(parent) , m_scriptsLock(new QMutex(QMutex::Recursive)) { QDBusConnection::sessionBus().registerObject("/Scripting", this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables); QDBusConnection::sessionBus().registerService("org.kde.kwin.Scripting"); connect(Workspace::self(), SIGNAL(configChanged()), SLOT(start())); connect(Workspace::self(), SIGNAL(workspaceInitialized()), SLOT(start())); } void KWin::Scripting::start() { #if 0 // TODO make this threaded again once KConfigGroup is sufficiently thread safe, bug #305361 and friends // perform querying for the services in a thread QFutureWatcher *watcher = new QFutureWatcher(this); connect(watcher, SIGNAL(finished()), this, SLOT(slotScriptsQueried())); watcher->setFuture(QtConcurrent::run(this, &KWin::Scripting::queryScriptsToLoad, pluginStates, offers)); #else LoadScriptList scriptsToLoad = queryScriptsToLoad(); for (LoadScriptList::const_iterator it = scriptsToLoad.constBegin(); it != scriptsToLoad.constEnd(); ++it) { if (it->first) { loadScript(it->second.first, it->second.second); } else { loadDeclarativeScript(it->second.first, it->second.second); } } runScripts(); #endif } LoadScriptList KWin::Scripting::queryScriptsToLoad() { KSharedConfig::Ptr _config = KGlobal::config(); static bool s_started = false; if (s_started) { _config->reparseConfiguration(); } else { s_started = true; } QMap pluginStates = KConfigGroup(_config, "Plugins").entryMap(); KService::List offers = KServiceTypeTrader::self()->query("KWin/Script"); LoadScriptList scriptsToLoad; foreach (const KService::Ptr & service, offers) { KPluginInfo plugininfo(service); const QString value = pluginStates.value(plugininfo.pluginName() + QString::fromLatin1("Enabled"), QString()); plugininfo.setPluginEnabled(value.isNull() ? plugininfo.isPluginEnabledByDefault() : QVariant(value).toBool()); const bool javaScript = service->property("X-Plasma-API").toString() == "javascript"; const bool declarativeScript = service->property("X-Plasma-API").toString() == "declarativescript"; if (!javaScript && !declarativeScript) { continue; } if (!plugininfo.isPluginEnabled()) { if (isScriptLoaded(plugininfo.pluginName())) { // unload the script unloadScript(plugininfo.pluginName()); } continue; } const QString pluginName = service->property("X-KDE-PluginInfo-Name").toString(); const QString scriptName = service->property("X-Plasma-MainScript").toString(); const QString file = KStandardDirs::locate("data", QLatin1String(KWIN_NAME) + "/scripts/" + pluginName + "/contents/" + scriptName); if (file.isNull()) { kDebug(1212) << "Could not find script file for " << pluginName; continue; } scriptsToLoad << qMakePair(javaScript, qMakePair(file, pluginName)); } return scriptsToLoad; } void KWin::Scripting::slotScriptsQueried() { QFutureWatcher *watcher = dynamic_cast< QFutureWatcher* >(sender()); if (!watcher) { // slot invoked not from a FutureWatcher return; } LoadScriptList scriptsToLoad = watcher->result(); for (LoadScriptList::const_iterator it = scriptsToLoad.constBegin(); it != scriptsToLoad.constEnd(); ++it) { if (it->first) { loadScript(it->second.first, it->second.second); } else { loadDeclarativeScript(it->second.first, it->second.second); } } runScripts(); watcher->deleteLater(); } bool KWin::Scripting::isScriptLoaded(const QString &pluginName) const { QMutexLocker locker(m_scriptsLock.data()); foreach (AbstractScript *script, scripts) { if (script->pluginName() == pluginName) { return true; } } return false; } bool KWin::Scripting::unloadScript(const QString &pluginName) { QMutexLocker locker(m_scriptsLock.data()); foreach (AbstractScript *script, scripts) { if (script->pluginName() == pluginName) { script->deleteLater(); return true; } } return false; } void KWin::Scripting::runScripts() { QMutexLocker locker(m_scriptsLock.data()); for (int i = 0; i < scripts.size(); i++) { scripts.at(i)->run(); } } void KWin::Scripting::scriptDestroyed(QObject *object) { QMutexLocker locker(m_scriptsLock.data()); scripts.removeAll(static_cast(object)); } int KWin::Scripting::loadScript(const QString &filePath, const QString& pluginName) { QMutexLocker locker(m_scriptsLock.data()); if (isScriptLoaded(pluginName)) { return -1; } const int id = scripts.size(); KWin::Script *script = new KWin::Script(id, filePath, pluginName, this); connect(script, SIGNAL(destroyed(QObject*)), SLOT(scriptDestroyed(QObject*))); scripts.append(script); return id; } int KWin::Scripting::loadDeclarativeScript(const QString& filePath, const QString& pluginName) { QMutexLocker locker(m_scriptsLock.data()); if (isScriptLoaded(pluginName)) { return -1; } const int id = scripts.size(); KWin::DeclarativeScript *script = new KWin::DeclarativeScript(id, filePath, pluginName, this); connect(script, SIGNAL(destroyed(QObject*)), SLOT(scriptDestroyed(QObject*))); scripts.append(script); return id; } KWin::Scripting::~Scripting() { QDBusConnection::sessionBus().unregisterObject("/Scripting"); QDBusConnection::sessionBus().unregisterService("org.kde.kwin.Scripting"); s_self = NULL; } QList< QAction * > KWin::Scripting::actionsForUserActionMenu(KWin::Client *c, QMenu *parent) { QList actions; foreach (AbstractScript *script, scripts) { actions << script->actionsForUserActionMenu(c, parent); } return actions; } #include "scripting.moc" diff --git a/tabbox/declarative.cpp b/tabbox/declarative.cpp index 4af5471e3..a6b12502c 100644 --- a/tabbox/declarative.cpp +++ b/tabbox/declarative.cpp @@ -1,454 +1,451 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2011 Martin Gräßlin 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 . *********************************************************************/ // own #include "declarative.h" #include "tabboxhandler.h" #include "clientmodel.h" // Qt #include #include #include #include #include #include #include // include KDE #include #include #include #include #include #include #include #include #include #include // KWin #include "thumbnailitem.h" #include #include "../effects.h" #include "../client.h" #include "../workspace.h" namespace KWin { namespace TabBox { ImageProvider::ImageProvider(QAbstractItemModel *model) : QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap) , m_model(model) { } QPixmap ImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) { bool ok = false; QStringList parts = id.split('/'); const int row = parts.first().toInt(&ok); if (!ok) { return QPixmap(); } QModelIndex parentIndex; const int parentRow = parts.at(1).toInt(&ok); if (ok) { // we have parent index parentIndex = m_model->index(parentRow, 0); if (!parentIndex.isValid()) { return QPixmap(); } } const QModelIndex index = m_model->index(row, 0, parentIndex); if (!index.isValid()) { return QPixmap(); } TabBoxClient* client = static_cast< TabBoxClient* >(index.model()->data(index, ClientModel::ClientRole).value()); if (!client) { return QPixmap(); } QSize s(32, 32); if (requestedSize.isValid()) { s = requestedSize; } *size = s; QPixmap icon = client->icon(s); if (s.width() > icon.width() || s.height() > icon.height()) { // icon is smaller than what we requested - QML would scale it which looks bad QPixmap temp(s); temp.fill(Qt::transparent); QPainter p(&temp); p.drawPixmap(s.width()/2 - icon.width()/2, s.height()/2 - icon.height()/2, icon); icon = temp; } if (parts.size() > 2) { KIconEffect *effect = KIconLoader::global()->iconEffect(); KIconLoader::States state = KIconLoader::DefaultState; if (parts.last() == QLatin1String("selected")) { state = KIconLoader::ActiveState; } else if (parts.last() == QLatin1String("disabled")) { state = KIconLoader::DisabledState; } icon = effect->apply(icon, KIconLoader::Desktop, state); } return icon; } // WARNING: this code exists to cover a bug in Qt which prevents plasma from detecting the state change // of the compositor through KWindowSystem. // once plasma uses (again) a KSelectionWatcher or Qt is fixed in this regard, the code can go. static QString plasmaThemeVariant() { #ifndef TABBOX_KCM if (!Workspace::self()->compositing() || !effects) { return Plasma::Theme::defaultTheme()->currentThemeHasImage("opaque/dialogs/background") ? QLatin1String("opaque/") : QLatin1String(""); } if (static_cast(effects)->provides(Effect::Blur)) { return Plasma::Theme::defaultTheme()->currentThemeHasImage("translucent/dialogs/background") ? QLatin1String("translucent/") : QLatin1String(""); } #endif return QLatin1String(""); } DeclarativeView::DeclarativeView(QAbstractItemModel *model, TabBoxConfig::TabBoxMode mode, QWidget *parent) : QDeclarativeView(parent) , m_model(model) , m_mode(mode) , m_currentScreenGeometry() , m_frame(new Plasma::FrameSvg(this)) , m_currentLayout() , m_cachedWidth(0) , m_cachedHeight(0) { setAttribute(Qt::WA_TranslucentBackground); setWindowFlags(Qt::X11BypassWindowManagerHint); if (tabBox->embedded()) { setResizeMode(QDeclarativeView::SizeRootObjectToView); } else { setResizeMode(QDeclarativeView::SizeViewToRootObject); } QPalette pal = palette(); pal.setColor(backgroundRole(), Qt::transparent); setPalette(pal); - foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { - engine()->addImportPath(importPath); - } engine()->addImageProvider(QLatin1String("client"), new ImageProvider(model)); KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.initialize(); kdeclarative.setupBindings(); #ifndef TABBOX_KCM qmlRegisterType("org.kde.kwin", 0, 1, "DesktopThumbnailItem"); #endif qmlRegisterType("org.kde.kwin", 0, 1, "ThumbnailItem"); rootContext()->setContextProperty("viewId", static_cast(winId())); rootContext()->setContextProperty("plasmaThemeVariant", plasmaThemeVariant()); if (m_mode == TabBoxConfig::ClientTabBox) { rootContext()->setContextProperty("clientModel", model); } else if (m_mode == TabBoxConfig::DesktopTabBox) { rootContext()->setContextProperty("clientModel", model); } setSource(QUrl(KStandardDirs::locate("data", QLatin1String(KWIN_NAME) + QLatin1String("/tabbox/tabbox.qml")))); // FrameSvg m_frame->setImagePath("dialogs/background"); m_frame->setCacheAllRenderedFrames(true); m_frame->setEnabledBorders(Plasma::FrameSvg::AllBorders); connect(tabBox, SIGNAL(configChanged()), SLOT(updateQmlSource())); if (m_mode == TabBoxConfig::ClientTabBox) { connect(tabBox, SIGNAL(embeddedChanged(bool)), SLOT(slotEmbeddedChanged(bool))); } } void DeclarativeView::showEvent(QShowEvent *event) { #ifndef TABBOX_KCM if (tabBox->embedded()) { Client *c = Workspace::self()->findClient(WindowMatchPredicate(tabBox->embedded())); if (c) { connect(c, SIGNAL(geometryChanged()), this, SLOT(slotUpdateGeometry())); } } #endif updateQmlSource(); m_currentScreenGeometry = QApplication::desktop()->screenGeometry(tabBox->activeScreen()); rootObject()->setProperty("screenWidth", m_currentScreenGeometry.width()); rootObject()->setProperty("screenHeight", m_currentScreenGeometry.height()); rootObject()->setProperty("allDesktops", tabBox->config().tabBoxMode() == TabBoxConfig::ClientTabBox && tabBox->config().clientDesktopMode() == TabBoxConfig::AllDesktopsClients); if (ClientModel *clientModel = qobject_cast(m_model)) { rootObject()->setProperty("longestCaption", clientModel->longestCaption()); } if (QObject *item = rootObject()->findChild("listView")) { item->setProperty("currentIndex", tabBox->first().row()); connect(item, SIGNAL(currentIndexChanged(int)), SLOT(currentIndexChanged(int))); } rootContext()->setContextProperty("plasmaThemeVariant", plasmaThemeVariant()); slotUpdateGeometry(); QResizeEvent re(size(), size()); // to set mask and blurring. resizeEvent(&re); QGraphicsView::showEvent(event); } void DeclarativeView::resizeEvent(QResizeEvent *event) { if (tabBox->embedded()) { Plasma::WindowEffects::enableBlurBehind(winId(), false); } else { const QString maskImagePath = rootObject()->property("maskImagePath").toString(); if (maskImagePath.isEmpty()) { clearMask(); Plasma::WindowEffects::enableBlurBehind(winId(), false); } else { const double maskWidth = rootObject()->property("maskWidth").toDouble(); const double maskHeight = rootObject()->property("maskHeight").toDouble(); const int maskTopMargin = rootObject()->property("maskTopMargin").toInt(); const int maskLeftMargin = rootObject()->property("maskLeftMargin").toInt(); m_frame->setImagePath(maskImagePath); m_frame->resizeFrame(QSizeF(maskWidth, maskHeight)); QRegion mask = m_frame->mask().translated(maskLeftMargin, maskTopMargin); #ifndef TABBOX_KCM // notice: this covers an issue with plasma detecting the compositing state. see plasmaThemeVariant() if (Workspace::self()->compositing() && effects) { // blur background?! Plasma::WindowEffects::enableBlurBehind(winId(), static_cast(effects)->provides(Effect::Blur), mask); clearMask(); } else #endif { // do not trim to mask with compositing enabled, otherwise shadows are cropped setMask(mask); } } } QDeclarativeView::resizeEvent(event); } void DeclarativeView::hideEvent(QHideEvent *event) { QWidget::hideEvent(event); #ifndef TABBOX_KCM if (tabBox->embedded()) { Client *c = Workspace::self()->findClient(WindowMatchPredicate(tabBox->embedded())); if (c) { disconnect(c, SIGNAL(geometryChanged()), this, SLOT(slotUpdateGeometry())); } } #endif } bool DeclarativeView::x11Event(XEvent *e) { if (tabBox->embedded() && (e->type == ButtonPress || e->type == ButtonRelease || e->type == MotionNotify)) { XEvent ev; memcpy(&ev, e, sizeof(ev)); if (e->type == ButtonPress || e->type == ButtonRelease) { ev.xbutton.x += m_relativePos.x(); ev.xbutton.y += m_relativePos.y(); ev.xbutton.window = tabBox->embedded(); } else if (e->type == MotionNotify) { ev.xmotion.x += m_relativePos.x(); ev.xmotion.y += m_relativePos.y(); ev.xmotion.window = tabBox->embedded(); } XSendEvent( QX11Info::display(), tabBox->embedded(), False, NoEventMask, &ev ); } return QDeclarativeView::x11Event(e); } void DeclarativeView::slotUpdateGeometry() { const WId embeddedId = tabBox->embedded(); if (embeddedId != 0) { const KWindowInfo info = KWindowSystem::windowInfo(embeddedId, NET::WMGeometry); const Qt::Alignment alignment = tabBox->embeddedAlignment(); const QPoint offset = tabBox->embeddedOffset(); int x = info.geometry().left(); int y = info.geometry().top(); int width = tabBox->embeddedSize().width(); int height = tabBox->embeddedSize().height(); if (alignment.testFlag(Qt::AlignLeft) || alignment.testFlag(Qt::AlignHCenter)) { x += offset.x(); } if (alignment.testFlag(Qt::AlignRight)) { x = x + info.geometry().width() - offset.x() - width; } if (alignment.testFlag(Qt::AlignHCenter)) { width = info.geometry().width() - 2 * offset.x(); } if (alignment.testFlag(Qt::AlignTop) || alignment.testFlag(Qt::AlignVCenter)) { y += offset.y(); } if (alignment.testFlag(Qt::AlignBottom)) { y = y + info.geometry().height() - offset.y() - height; } if (alignment.testFlag(Qt::AlignVCenter)) { height = info.geometry().height() - 2 * offset.y(); } setGeometry(QRect(x, y, width, height)); m_relativePos = QPoint(info.geometry().x(), info.geometry().x()); } else { const int width = rootObject()->property("width").toInt(); const int height = rootObject()->property("height").toInt(); setGeometry(m_currentScreenGeometry.x() + static_cast(m_currentScreenGeometry.width()) * 0.5 - static_cast(width) * 0.5, m_currentScreenGeometry.y() + static_cast(m_currentScreenGeometry.height()) * 0.5 - static_cast(height) * 0.5, width, height); m_relativePos = pos(); } } void DeclarativeView::setCurrentIndex(const QModelIndex &index, bool disableAnimation) { if (tabBox->config().tabBoxMode() != m_mode) { return; } if (QObject *item = rootObject()->findChild("listView")) { QVariant durationRestore; if (disableAnimation) { durationRestore = item->property("highlightMoveDuration"); item->setProperty("highlightMoveDuration", QVariant(1)); } item->setProperty("currentIndex", index.row()); if (disableAnimation) { item->setProperty("highlightMoveDuration", durationRestore); } } } void DeclarativeView::currentIndexChanged(int row) { tabBox->setCurrentIndex(m_model->index(row, 0)); KWindowSystem::forceActiveWindow(m_model->data(m_model->index(row, 0), ClientModel::WIdRole).toLongLong()); } void DeclarativeView::updateQmlSource(bool force) { if (tabBox->config().tabBoxMode() != m_mode) { return; } if (!force && tabBox->config().layoutName() == m_currentLayout) { return; } const bool desktopMode = (m_mode == TabBoxConfig::DesktopTabBox); m_currentLayout = tabBox->config().layoutName(); KService::Ptr service = desktopMode ? findDesktopSwitcher() : findWindowSwitcher(); if (service.isNull()) { return; } if (service->property("X-Plasma-API").toString() != "declarativeappletscript") { kDebug(1212) << "Window Switcher Layout is no declarativeappletscript"; return; } const QString file = desktopMode ? findDesktopSwitcherScriptFile(service) : findWindowSwitcherScriptFile(service); if (file.isNull()) { kDebug(1212) << "Could not find QML file for window switcher"; return; } rootObject()->setProperty("source", QUrl(file)); } KService::Ptr DeclarativeView::findWindowSwitcher() { QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(tabBox->config().layoutName()); KService::List offers = KServiceTypeTrader::self()->query("KWin/WindowSwitcher", constraint); if (offers.isEmpty()) { // load default constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg("informative"); offers = KServiceTypeTrader::self()->query("KWin/WindowSwitcher", constraint); if (offers.isEmpty()) { kDebug(1212) << "could not find default window switcher layout"; return KService::Ptr(); } } return offers.first(); } KService::Ptr DeclarativeView::findDesktopSwitcher() { QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(tabBox->config().layoutName()); KService::List offers = KServiceTypeTrader::self()->query("KWin/DesktopSwitcher", constraint); if (offers.isEmpty()) { // load default constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg("informative"); offers = KServiceTypeTrader::self()->query("KWin/DesktopSwitcher", constraint); if (offers.isEmpty()) { kDebug(1212) << "could not find default desktop switcher layout"; return KService::Ptr(); } } return offers.first(); } QString DeclarativeView::findWindowSwitcherScriptFile(KService::Ptr service) { const QString pluginName = service->property("X-KDE-PluginInfo-Name").toString(); const QString scriptName = service->property("X-Plasma-MainScript").toString(); return KStandardDirs::locate("data", QLatin1String(KWIN_NAME) + "/tabbox/" + pluginName + "/contents/" + scriptName); } QString DeclarativeView::findDesktopSwitcherScriptFile(KService::Ptr service) { const QString pluginName = service->property("X-KDE-PluginInfo-Name").toString(); const QString scriptName = service->property("X-Plasma-MainScript").toString(); return KStandardDirs::locate("data", QLatin1String(KWIN_NAME) + "/desktoptabbox/" + pluginName + "/contents/" + scriptName); } void DeclarativeView::slotEmbeddedChanged(bool enabled) { if (enabled) { // cache the width setResizeMode(QDeclarativeView::SizeRootObjectToView); m_cachedWidth = rootObject()->property("width").toInt(); m_cachedHeight = rootObject()->property("height").toInt(); } else { setResizeMode(QDeclarativeView::SizeViewToRootObject); if (m_cachedWidth != 0 && m_cachedHeight != 0) { rootObject()->setProperty("width", m_cachedWidth); rootObject()->setProperty("height", m_cachedHeight); } updateQmlSource(true); } } void DeclarativeView::slotWindowChanged(WId wId, unsigned int properties) { if (wId != tabBox->embedded()) { return; } if (properties & NET::WMGeometry) { slotUpdateGeometry(); } } bool DeclarativeView::sendKeyEvent(QKeyEvent *e) { return event(e); } } // namespace TabBox } // namespace KWin