diff --git a/clients/aurorae/src/aurorae.cpp b/clients/aurorae/src/aurorae.cpp index d95cdfe43..5d377c5dc 100644 --- a/clients/aurorae/src/aurorae.cpp +++ b/clients/aurorae/src/aurorae.cpp @@ -1,611 +1,610 @@ /******************************************************************** 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 *parent) : KDecorationFactory(parent) , m_theme(new AuroraeTheme(this)) , m_engine(new QDeclarativeEngine(this)) , m_component(new QDeclarativeComponent(m_engine, this)) , m_engineType(AuroraeEngine) { init(); connect(options(), &KDecorationOptions::buttonsChanged, this, &AuroraeFactory::buttonsChanged); connect(options(), &KDecorationOptions::fontsChanged, this, &AuroraeFactory::titleFontChanged); connect(options(), &KDecorationOptions::configChanged, this, &AuroraeFactory::updateConfiguration); } void AuroraeFactory::init() { qRegisterMetaType("Qt::MouseButtons"); KConfig conf(QStringLiteral("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 == QStringLiteral("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(QStringLiteral("aurorae/themes/") + themeName + QStringLiteral("/") + themeName + QStringLiteral("rc"), KConfig::FullConfig, QStandardPaths::DataLocation); 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 /* 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_component->loadUrl(QUrl(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/aurorae/aurorae.qml")))); m_engine->rootContext()->setContextProperty(QStringLiteral("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"); qDebug() << "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()) { qCritical() << "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(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()) { qDebug() << "Could not find script file for " << pluginName; // TODO: what to do in error case? return; } m_engineType = QMLEngine; // 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()); } 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; } void AuroraeFactory::updateConfiguration() { const KConfig conf(QStringLiteral("auroraerc")); const KConfigGroup group(&conf, "Engine"); const QString themeName = group.readEntry("ThemeName", "example-deco"); const KConfig config(QStringLiteral("aurorae/themes/") + themeName + QStringLiteral("/") + themeName + QStringLiteral("rc"), KConfig::FullConfig, QStandardPaths::DataLocation); const KConfigGroup themeGroup(&conf, themeName); if (themeName != m_themeName) { m_engine->clearComponentCache(); init(); emit recreateDecorations(); } 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(); } 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(QStringLiteral("decoration"), client); return qobject_cast< QDeclarativeItem* >(m_component->create(context)); } AuroraeFactory *AuroraeFactory::s_instance = NULL; /******************************************************* * Client *******************************************************/ AuroraeClient::AuroraeClient(KDecorationBridge *bridge, KDecorationFactory *factory) : KDecoration(bridge, factory) , m_view(NULL) , m_scene(new QGraphicsScene(this)) , m_item(AuroraeFactory::instance()->createQmlDecoration(this)) { 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 KDecoration::eventFilter(object, event); } QWheelEvent *wheel = static_cast(event); if (mousePosition(wheel->pos()) == PositionCenter) { titlebarMouseWheelOperation(wheel->delta()); return true; } return false; } 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::borders(int &left, int &right, int &top, int &bottom) const { if (!m_item) { left = right = top = bottom = 0; return; } QObject *borders = NULL; if (maximizeMode() == MaximizeFull) { borders = m_item->findChild(QStringLiteral("maximizedBorders")); } else { borders = m_item->findChild(QStringLiteral("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) { left = right = top = bottom = 0; return; } sizesFromBorders(m_item->findChild(QStringLiteral("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()) { + if (isShade() || isMaximized()) { 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; int titleEdgeLeft, titleEdgeRight, titleEdgeTop, titleEdgeBottom; - AuroraeFactory::instance()->theme()->titleEdges(titleEdgeLeft, titleEdgeTop, titleEdgeRight, titleEdgeBottom, maximized); + AuroraeFactory::instance()->theme()->titleEdges(titleEdgeLeft, titleEdgeTop, titleEdgeRight, titleEdgeBottom, false); 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::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()); } 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< KDecoration* >(this), "doCloseWindow", Qt::QueuedConnection); } void AuroraeClient::doCloseWindow() { KDecoration::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< KDecoration* >(this), "doMaximzie", Qt::QueuedConnection, Q_ARG(int, button)); } void AuroraeClient::doMaximzie(int button) { KDecoration::maximize(static_cast(button)); } void AuroraeClient::titlebarDblClickOperation() { // the double click operation can result in a window being maximized // see maximize QMetaObject::invokeMethod(qobject_cast< KDecoration* >(this), "doTitlebarDblClickOperation", Qt::QueuedConnection); } void AuroraeClient::doTitlebarDblClickOperation() { KDecoration::titlebarDblClickOperation(); } QVariant AuroraeClient::readConfig(const QString &key, const QVariant &defaultValue) { KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("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(QStringLiteral("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/clients/laptop/laptopclient.cpp b/clients/laptop/laptopclient.cpp index 958cd2ec8..6462bd7cb 100644 --- a/clients/laptop/laptopclient.cpp +++ b/clients/laptop/laptopclient.cpp @@ -1,797 +1,797 @@ /******************************************************************** Laptop KWin Decoration Copyright (c) 2005 Sandro Giessl Port of this decoration to KDE 3.2, accessibility enhancement are Copyright (c) 2003 Luciano Montanaro 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 // up here to avoid X11 header conflict :P #include "laptopclient.h" #include #include #include #include #include #include namespace Laptop { static const unsigned char iconify_bits[] = { 0xff, 0xff, 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18}; static const unsigned char close_bits[] = { 0x42, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0x42}; static const unsigned char appmenu_bits[] = { 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff}; static const unsigned char maximize_bits[] = { 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0xff, 0xff }; static const unsigned char r_minmax_bits[] = { 0x0c, 0x18, 0x33, 0x67, 0xcf, 0x9f, 0x3f, 0x3f}; static const unsigned char l_minmax_bits[] = { 0x30, 0x18, 0xcc, 0xe6, 0xf3, 0xf9, 0xfc, 0xfc}; static const unsigned char question_bits[] = { 0x3c, 0x66, 0x60, 0x30, 0x18, 0x00, 0x18, 0x18}; static const unsigned char unsticky_bits[] = { 0x3c, 0x42, 0x99, 0xbd, 0xbd, 0x99, 0x42, 0x3c}; static const unsigned char sticky_bits[] = { 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c}; static QPixmap *titlePix; static QPixmap *aUpperGradient; static QPixmap *iUpperGradient; // buttons active, inactive, up, down, and 2 sizes :P static QPixmap *btnPix1; static QPixmap *iBtnPix1; static QPixmap *btnDownPix1; static QPixmap *iBtnDownPix1; static QPixmap *btnPix2; static QPixmap *btnDownPix2; static QPixmap *iBtnPix2; static QPixmap *iBtnDownPix2; static QColor btnForeground; static int titleHeight = 14; static int btnWidth1 = 17; static int btnWidth2 = 27; static int handleSize = 8; // the resize handle size in pixels static bool pixmaps_created = false; // ===================================== KWIN_DECORATION(Laptop::LaptopClientFactory) // ===================================== static inline const KDecorationOptions* options() { return KDecoration::options(); } static void gradientFill(QPixmap *pixmap, const QColor &color1, const QColor &color2, bool diagonal = false) { QPainter p(pixmap); QLinearGradient gradient(0, 0, diagonal ? pixmap->width() : 0, pixmap->height()); gradient.setColorAt(0.0, color1); gradient.setColorAt(1.0, color2); QBrush brush(gradient); p.fillRect(pixmap->rect(), brush); } static void drawButtonFrame(QPixmap *pix, const QPalette &g, bool sunken) { QPainter p; int w = pix->width(); int h = pix->height(); int x2 = w-1; int y2 = h-1; p.begin(pix); if(sunken){ qDrawShadePanel(&p, 0, 0, w, h, g, true, 2); } else{ p.setPen(g.color(QPalette::Dark )); p.drawRect(0, 0, w-2, h-2); p.setPen(g.color(QPalette::Light)); p.drawLine(x2, 0, x2, y2); p.drawLine(0, y2, x2, y2); p.drawLine(1, 1, x2-2, 1); p.drawLine(1, 1, 1, y2-2); p.end(); } } static void create_pixmaps() { if(pixmaps_created) return; pixmaps_created = true; titleHeight = QFontMetrics(options()->font(true)).height(); if (titleHeight < handleSize) titleHeight = handleSize; titleHeight &= ~1; // Make title height even if (titleHeight < 14) titleHeight = 14; btnWidth1 = titleHeight + 3; btnWidth2 = 3*titleHeight/2 + 6; // titlebar QPainter p; QPainter maskPainter; int i, x, y; titlePix = new QPixmap(33, 12); titlePix->fill( Qt::transparent ); QBitmap mask(33, 12); mask.fill(Qt::color0); p.begin(titlePix); maskPainter.begin(&mask); maskPainter.setPen(Qt::color1); for(i=0, y=2; i < 3; ++i, y+=4){ for(x=1; x <= 33; x+=3){ p.setPen(options()->color(KDecoration::ColorTitleBar, true).light(150)); p.drawPoint(x, y); maskPainter.drawPoint(x, y); p.setPen(options()->color(KDecoration::ColorTitleBar, true).dark(150)); p.drawPoint(x+1, y+1); maskPainter.drawPoint(x+1, y+1); } } p.end(); maskPainter.end(); titlePix->setMask(mask); if(QPixmap::defaultDepth() > 8){ aUpperGradient = new QPixmap(32, titleHeight+2); iUpperGradient = new QPixmap(32, titleHeight+2); QColor bgColor = options()->color(KDecoration::ColorTitleBar, true); gradientFill(aUpperGradient, bgColor.light(120), bgColor.dark(120)); bgColor = options()->color(KDecoration::ColorTitleBar, false); gradientFill(iUpperGradient, bgColor.light(120), bgColor.dark(120)); } // buttons (active/inactive, sunken/unsunken, 2 sizes each) QPalette g = options()->palette(KDecoration::ColorButtonBg, true); g.setCurrentColorGroup( QPalette::Active ); QColor c = g.color( QPalette::Background ); btnPix1 = new QPixmap(btnWidth1, titleHeight); btnDownPix1 = new QPixmap(btnWidth1, titleHeight); btnPix2 = new QPixmap(btnWidth2, titleHeight); btnDownPix2 = new QPixmap(btnWidth2, titleHeight); iBtnPix1 = new QPixmap(btnWidth1, titleHeight); iBtnDownPix1 = new QPixmap(btnWidth1, titleHeight); iBtnPix2 = new QPixmap(btnWidth2, titleHeight); iBtnDownPix2 = new QPixmap(btnWidth2, titleHeight); if(QPixmap::defaultDepth() > 8){ gradientFill(btnPix1, c.light(120), c.dark(130), true); gradientFill(btnPix2, c.light(120), c.dark(130), true); gradientFill(btnDownPix1, c.light(120), c.dark(130), true); gradientFill(btnDownPix2, c.light(120), c.dark(130), true); g = options()->palette(KDecoration::ColorButtonBg, false); g.setCurrentColorGroup( QPalette::Active ); c = g.color(QPalette::Background); gradientFill(iBtnPix1, c.light(120), c.dark(130), true); gradientFill(iBtnPix2, c.light(120), c.dark(130), true); gradientFill(iBtnDownPix1, c.light(120), c.dark(130), true); gradientFill(iBtnDownPix2, c.light(120), c.dark(130), true); } else{ btnPix1->fill(c.rgb()); btnDownPix1->fill(c.rgb()); btnPix2->fill(c.rgb()); btnDownPix2->fill(c.rgb()); g = options()->palette(KDecoration::ColorButtonBg, false); g.setCurrentColorGroup( QPalette::Active ); c = g.background().color(); iBtnPix1->fill(c.rgb()); iBtnDownPix1->fill(c.rgb()); iBtnPix2->fill(c.rgb()); iBtnDownPix2->fill(c.rgb()); } g = options()->palette(KDecoration::ColorButtonBg, true); g.setCurrentColorGroup( QPalette::Active ); c = g.background().color(); drawButtonFrame(btnPix1, g, false); drawButtonFrame(btnDownPix1, g, true); drawButtonFrame(btnPix2, g, false); drawButtonFrame(btnDownPix2, g, true); g = options()->palette(KDecoration::ColorButtonBg, false); g.setCurrentColorGroup( QPalette::Active ); c = g.background().color(); drawButtonFrame(iBtnPix1, g, false); drawButtonFrame(iBtnDownPix1, g, true); drawButtonFrame(iBtnPix2, g, false); drawButtonFrame(iBtnDownPix2, g, true); if(qGray(options()->color(KDecoration::ColorButtonBg, true).rgb()) > 128) btnForeground = Qt::black; else btnForeground = Qt::white; } static void delete_pixmaps() { delete titlePix; if(aUpperGradient){ delete aUpperGradient; delete iUpperGradient; delete btnPix1; delete btnDownPix1; delete iBtnPix1; delete iBtnDownPix1; delete btnPix2; delete btnDownPix2; delete iBtnPix2; delete iBtnDownPix2; } pixmaps_created = false; } // ===================================== LaptopButton::LaptopButton(ButtonType type, LaptopClient *parent, const char *name) : KCommonDecorationButton(type, parent) { Q_UNUSED( name ); setAttribute(Qt::WA_NoSystemBackground, true); } void LaptopButton::reset(unsigned long changed) { if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { switch (type() ) { case CloseButton: setBitmap(close_bits); break; case AppMenuButton: setBitmap(appmenu_bits); break; case HelpButton: setBitmap(question_bits); break; case MinButton: setBitmap(iconify_bits); break; case MaxButton: if (isChecked() ) { setBitmap(isLeft() ? l_minmax_bits : r_minmax_bits); } else { setBitmap(maximize_bits); } break; case OnAllDesktopsButton: setBitmap( isChecked() ? unsticky_bits : sticky_bits ); break; default: setBitmap(0); break; } this->update(); } } void LaptopButton::setBitmap(const unsigned char *bitmap) { if (bitmap) deco = QBitmap::fromData( QSize(8, 8), bitmap); else { deco = QBitmap(8,8); deco.fill(Qt::color0); } deco.setMask(deco); repaint(); } void LaptopButton::paintEvent(QPaintEvent *) { QPainter p(this); drawButton(&p); } void LaptopButton::drawButton(QPainter *p) { bool smallBtn = width() == btnWidth1; if(btnPix1){ if(decoration()->isActive()){ if(isDown()) p->drawPixmap(0, 0, smallBtn ? *btnDownPix1 : *btnDownPix2); else p->drawPixmap(0, 0, smallBtn ? *btnPix1 : *btnPix2); } else{ if(isDown()) p->drawPixmap(0, 0, smallBtn ? *iBtnDownPix1 : *iBtnDownPix2); else p->drawPixmap(0, 0, smallBtn ? *iBtnPix1 : *iBtnPix2); } } else{ QPalette g = options()->palette(KDecoration::ColorButtonBg, decoration()->isActive()); g.setCurrentColorGroup( QPalette::Active ); int w = width(); int h = height(); p->fillRect(1, 1, w-2, h-2, isDown() ? g.color(QPalette::Mid) : g.color(QPalette::Button) ); p->setPen(isDown() ? g.color( QPalette::Dark ) : g.color( QPalette::Light )); p->drawLine(0, 0, w-1, 0); p->drawLine(0, 0, 0, w-1); p->setPen(isDown() ? g.color( QPalette::Light ) : g.color( QPalette::Dark )); p->drawLine(w-1, 0, w-1, h-1); p->drawLine(0, h-1, w-1, h-1); } QPainterPath path; path.addRegion( deco ); QPoint offset( (width()-8)/2, (height()-8)/2 ); if( isDown() ) offset += QPoint( 1, 1 ); p->translate( offset ); p->setPen( Qt::NoPen ); p->setBrush( btnForeground ); p->drawPath( path ); } // ===================================== void LaptopClient::reset(unsigned long changed) { KCommonDecoration::reset(changed); } LaptopClient::LaptopClient(KDecorationBridge *b, KDecorationFactory *f) : KCommonDecoration(b, f) { } LaptopClient::~LaptopClient() { } QString LaptopClient::visibleName() const { return i18n("Laptop"); } QString LaptopClient::defaultButtonsLeft() const { return "X"; } QString LaptopClient::defaultButtonsRight() const { return "HSIA"; } bool LaptopClient::decorationBehaviour(DecorationBehaviour behaviour) const { switch (behaviour) { case DB_MenuClose: return false; case DB_WindowMask: return true; case DB_ButtonHide: return true; default: return KCommonDecoration::decorationBehaviour(behaviour); } } int LaptopClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const { switch (lm) { case LM_TitleEdgeLeft: case LM_TitleEdgeRight: case LM_BorderLeft: case LM_BorderRight: - return 4; + return (maximizeMode() & MaximizeHorizontal) ? 0 : 4; case LM_BorderBottom: - return mustDrawHandle() ? handleSize : 4; + return (maximizeMode() & MaximizeVertical) ? 0 : (isResizable() ? handleSize : 4); case LM_TitleEdgeTop: - return 3; + return (maximizeMode() & MaximizeVertical) ? 0 : 3; case LM_TitleEdgeBottom: return 1; case LM_TitleBorderLeft: case LM_TitleBorderRight: return 0; case LM_ButtonWidth: { if (btn && (btn->type()==HelpButton||btn->type()==OnAllDesktopsButton) ) { return btnWidth1; } else { return btnWidth2; } } case LM_ButtonHeight: case LM_TitleHeight: if (isToolWindow() ) return titleHeight-2; else return titleHeight; case LM_ButtonSpacing: return 0; default: return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); } } KCommonDecorationButton *LaptopClient::createButton(ButtonType type) { switch (type) { case OnAllDesktopsButton: return new LaptopButton(OnAllDesktopsButton, this, "on_all_desktops"); case HelpButton: return new LaptopButton(HelpButton, this, "help"); case MinButton: return new LaptopButton(MinButton, this, "minimize"); case MaxButton: return new LaptopButton(MaxButton, this, "maximize"); case CloseButton: return new LaptopButton(CloseButton, this, "close"); case AppMenuButton: return new LaptopButton(AppMenuButton, this, "Application Menu"); default: return 0; } } void LaptopClient::init() { bufferDirty = true; KCommonDecoration::init(); } void LaptopClient::captionChange() { bufferDirty = true; KCommonDecoration::captionChange(); } void LaptopClient::paintEvent( QPaintEvent* ) { QPainter p(widget()); QPalette g = options()->palette(KDecoration::ColorFrame, isActive()); g.setCurrentColorGroup( QPalette::Active ); QRect r(widget()->rect()); p.setPen(Qt::black); p.drawRect(r.adjusted(0, 0, -1, -1)); // fill mid frame... p.setPen(g.background().color()); p.drawLine(r.x()+2, r.y()+2, r.right()-2, r.y()+2); p.drawLine(r.left()+2, r.y()+3, r.left()+2, r.bottom()-layoutMetric(LM_BorderBottom)+1); p.drawLine(r.right()-2, r.y()+3, r.right()-2, r.bottom()-layoutMetric(LM_BorderBottom)+1); p.drawLine(r.left()+3, r.y()+3, r.left()+3, r.y()+layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight)+layoutMetric(LM_TitleEdgeTop) ); p.drawLine(r.right()-3, r.y()+3, r.right()-3, r.y()+layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight)+layoutMetric(LM_TitleEdgeTop) ); if (!mustDrawHandle() ) p.drawLine(r.left()+1, r.bottom()-2, r.right()-1, r.bottom()-2); // outer frame p.setPen(g.color(QPalette::Light)); p.drawLine(r.x()+1, r.y()+1, r.right()-1, r.y()+1); p.drawLine(r.x()+1, r.y()+1, r.x()+1, r.bottom()-1); p.setPen(g.dark().color()); p.drawLine(r.right()-1, r.y()+1, r.right()-1, r.bottom()-1); p.drawLine(r.x()+1, r.bottom()-1, r.right()-1, r.bottom()-1); int th = titleHeight; int bb = handleSize + 2; // Bottom border int bs = handleSize - 2; // inner size of bottom border if (!mustDrawHandle()) { bb = 6; bs = 0; } if ( isToolWindow() ) th -= 2; // inner rect p.drawRect(r.x() + 3, r.y() + th + 3, r.width() - 7, r.height() - th - bb - 1); // handles if (mustDrawHandle()) { if (r.width() > 3*handleSize + 20) { int range = 8 + 3*handleSize/2; qDrawShadePanel(&p, r.x() + 1, r.bottom() - bs, range, handleSize - 2, g, false, 1, &g.brush(QPalette::Mid)); qDrawShadePanel(&p, r.x() + range + 1, r.bottom() - bs, r.width() - 2*range - 2, handleSize - 2, g, false, 1, isActive() ? &g.brush(QPalette::Background) : &g.brush(QPalette::Mid)); qDrawShadePanel(&p, r.right() - range, r.bottom() - bs, range, bs, g, false, 1, &g.brush(QPalette::Mid)); } else { qDrawShadePanel(&p, r.x() + 1, r.bottom() - bs, r.width() - 2, bs, g, false, 1, isActive() ? &g.brush(QPalette::Background) : &g.brush(QPalette::Mid)); } } r = titleRect(); if(isActive()){ updateActiveBuffer(); p.drawPixmap(r.x(), r.y(), activeBuffer); p.setPen(g.background().color()); p.drawPoint(r.x(), r.y()); p.drawPoint(r.right(), r.y()); p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); } else{ if(iUpperGradient) p.drawTiledPixmap(r.x(), r.y(), r.width(), r.height()-1, *iUpperGradient); else p.fillRect(r.x(), r.y(), r.width(), r.height()-1, options()->color(KDecoration::ColorTitleBar, false)); p.setFont(options()->font(false, isToolWindow() )); QFontMetrics fm(options()->font(false)); g = options()->palette(KDecoration::ColorTitleBar, false); g.setCurrentColorGroup( QPalette::Active ); if(iUpperGradient) p.drawTiledPixmap(r.x()+((r.width()-fm.width(caption()))/2)-4, r.y(), fm.width(caption())+8, r.height()-1, *iUpperGradient); else p.fillRect(r.x()+((r.width()-fm.width(caption()))/2)-4, r.y(), fm.width(caption())+8, r.height()-1, g.brush(QPalette::Background)); p.setPen(g.mid().color()); p.drawLine(r.x(), r.y(), r.right(), r.y()); p.drawLine(r.x(), r.y(), r.x(), r.bottom()); p.setPen(g.color(QPalette::Button)); p.drawLine(r.right(), r.y(), r.right(), r.bottom()); p.drawLine(r.x(), r.bottom(), r.right(), r.bottom()); p.setPen(options()->color(KDecoration::ColorFont, false)); p.drawText(r.x(), r.y()+1, r.width(), r.height()-1, Qt::AlignCenter, caption() ); g = options()->palette(KDecoration::ColorFrame, true); g.setCurrentColorGroup( QPalette::Active ); p.setPen(g.background().color()); p.drawPoint(r.x(), r.y()); p.drawPoint(r.right(), r.y()); p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); } } QRegion LaptopClient::cornerShape(WindowCorner corner) { switch (corner) { case WC_TopLeft: return QRect(0, 0, 1, 1); case WC_TopRight: return QRect(width()-1, 0, 1, 1); case WC_BottomLeft: return QRect(0, height()-1, 1, 1); case WC_BottomRight: return QRect(width()-1, height()-1, 1, 1); default: return QRegion(); } } bool LaptopClient::mustDrawHandle() const { bool drawSmallBorders = !options()->moveResizeMaximizedWindows(); if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) { return false; } else { return isResizable(); } } void LaptopClient::updateActiveBuffer( ) { QRect rTitle = titleRect(); if( !bufferDirty && (lastBufferWidth == rTitle.width())) return; if ( rTitle.width() <= 0 || rTitle.height() <= 0 ) return; lastBufferWidth = rTitle.width(); bufferDirty = false; activeBuffer = QPixmap(rTitle.width(), rTitle.height()); QPainter p; QRect r(0, 0, activeBuffer.width(), activeBuffer.height()); p.begin(&activeBuffer); if(aUpperGradient){ p.drawTiledPixmap(r, *aUpperGradient); } else{ p.fillRect(r, options()->color(KDecoration::ColorTitleBar, true)); } if(titlePix) p.drawTiledPixmap(r, *titlePix); p.setFont(options()->font(true, isToolWindow() )); QFontMetrics fm(options()->font(true)); QPalette g = options()->palette(KDecoration::ColorTitleBar, true); g.setCurrentColorGroup( QPalette::Active ); if(aUpperGradient) p.drawTiledPixmap(r.x()+((r.width()-fm.width(caption()))/2)-4, r.y(), fm.width(caption())+8, r.height()-1, *aUpperGradient); else p.fillRect(r.x()+((r.width()-fm.width(caption()))/2)-4, 0, fm.width(caption())+8, r.height(), g.brush(QPalette::Background)); p.setPen(g.mid().color()); p.drawLine(r.x(), r.y(), r.right(), r.y()); p.drawLine(r.x(), r.y(), r.x(), r.bottom()); p.setPen(g.color(QPalette::Button)); p.drawLine(r.right(), r.y(), r.right(), r.bottom()); p.drawLine(r.x(), r.bottom(), r.right(), r.bottom()); p.setPen(options()->color(KDecoration::ColorFont, true)); p.drawText(r.x(), r.y()+1, r.width(), r.height()-1, Qt::AlignCenter, caption() ); g = options()->palette(KDecoration::ColorFrame, true); g.setCurrentColorGroup( QPalette::Active ); p.setPen(g.background().color()); p.drawPoint(r.x(), r.y()); p.drawPoint(r.right(), r.y()); p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); p.end(); } static const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | /*NET::OverrideMask |*/ NET::TopMenuMask | NET::UtilityMask | NET::SplashMask; bool LaptopClient::isTransient() const { NET::WindowType type = windowType(SUPPORTED_WINDOW_TYPES_MASK); return type == NET::Dialog; } // ===================================== LaptopClientFactory::LaptopClientFactory() { create_pixmaps(); } LaptopClientFactory::~LaptopClientFactory() { delete_pixmaps(); } KDecoration *LaptopClientFactory::createDecoration(KDecorationBridge *b) { findPreferredHandleSize(); return (new Laptop::LaptopClient(b, this))->decoration(); } bool LaptopClientFactory::reset(unsigned long changed) { findPreferredHandleSize(); // TODO Do not recreate decorations if it is not needed. Look at // ModernSystem for how to do that Laptop::delete_pixmaps(); Laptop::create_pixmaps(); bool needHardReset = true; if ((changed & ~SettingButtons) == 0) { // handled by KCommonDecoration needHardReset = false; } if (needHardReset) { return true; } else { resetDecorations(changed); return false; } } bool LaptopClientFactory::supports( Ability ability ) const { switch( ability ) { // announce case AbilityAnnounceButtons: case AbilityAnnounceColors: // buttons case AbilityButtonOnAllDesktops: case AbilityButtonHelp: case AbilityButtonMinimize: case AbilityButtonMaximize: case AbilityButtonClose: case AbilityButtonSpacer: case AbilityButtonApplicationMenu: // colors case AbilityColorTitleBack: case AbilityColorTitleFore: case AbilityColorButtonBack: return true; default: return false; }; } QList< LaptopClientFactory::BorderSize > LaptopClientFactory::borderSizes() const { // the list must be sorted return QList< BorderSize >() << BorderNormal << BorderLarge << BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; } void LaptopClientFactory::findPreferredHandleSize() { switch (options()->preferredBorderSize(this)) { case KDecoration::BorderLarge: handleSize = 11; break; case KDecoration::BorderVeryLarge: handleSize = 16; break; case KDecoration::BorderHuge: handleSize = 24; break; case KDecoration::BorderVeryHuge: handleSize = 32; break; case KDecoration::BorderOversized: handleSize = 40; break; case KDecoration::BorderTiny: case KDecoration::BorderNormal: default: handleSize = 8; } } } // Laptop namespace // vim: sw=4 diff --git a/effects/zoom/zoom.cpp b/effects/zoom/zoom.cpp index cc0ff91a5..f84280b53 100644 --- a/effects/zoom/zoom.cpp +++ b/effects/zoom/zoom.cpp @@ -1,533 +1,534 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2010 Sebastian Sauer 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 "zoom.h" // KConfigSkeleton #include "zoomconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #include #endif #include #include namespace KWin { KWIN_EFFECT(zoom, ZoomEffect) ZoomEffect::ZoomEffect() : Effect() , zoom(1) , target_zoom(1) , polling(false) , zoomFactor(1.25) , mouseTracking(MouseTrackingProportional) , enableFocusTracking(false) , followFocus(true) , mousePointer(MousePointerScale) , focusDelay(350) // in milliseconds , imageWidth(0) , imageHeight(0) , isMouseHidden(false) , xMove(0) , yMove(0) , moveFactor(20.0) { KActionCollection* actionCollection = new KActionCollection(this); QAction* a = 0; a = actionCollection->addAction(KStandardAction::ZoomIn, this, SLOT(zoomIn())); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Equal); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Equal); a = actionCollection->addAction(KStandardAction::ZoomOut, this, SLOT(zoomOut())); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Minus); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Minus); a = actionCollection->addAction(KStandardAction::ActualSize, this, SLOT(actualSize())); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_0); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_0); a = actionCollection->addAction(QStringLiteral("MoveZoomLeft")); a->setText(i18n("Move Zoomed Area to Left")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Left); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Left); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomLeft())); a = actionCollection->addAction(QStringLiteral("MoveZoomRight")); a->setText(i18n("Move Zoomed Area to Right")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Right); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Right); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomRight())); a = actionCollection->addAction(QStringLiteral("MoveZoomUp")); a->setText(i18n("Move Zoomed Area Upwards")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Up); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Up); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomUp())); a = actionCollection->addAction(QStringLiteral("MoveZoomDown")); a->setText(i18n("Move Zoomed Area Downwards")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Down); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Down); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomDown())); // TODO: these two actions don't belong into the effect. They need to be moved into KWin core a = actionCollection->addAction(QStringLiteral("MoveMouseToFocus")); a->setText(i18n("Move Mouse to Focus")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_F5); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_F5); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveMouseToFocus())); a = actionCollection->addAction(QStringLiteral("MoveMouseToCenter")); a->setText(i18n("Move Mouse to Center")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_F6); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_F6); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveMouseToCenter())); timeline.setDuration(350); timeline.setFrameRange(0, 100); connect(&timeline, SIGNAL(frameChanged(int)), this, SLOT(timelineFrameChanged(int))); connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); source_zoom = -1; // used to trigger initialZoom reading reconfigure(ReconfigureAll); } ZoomEffect::~ZoomEffect() { // switch off and free resources showCursor(); // Save the zoom value. KConfigGroup conf = EffectsHandler::effectConfig(QStringLiteral("Zoom")); conf.writeEntry("InitialZoom", target_zoom); conf.sync(); } void ZoomEffect::showCursor() { if (isMouseHidden) { // show the previously hidden mouse-pointer again and free the loaded texture/picture. xcb_xfixes_show_cursor(connection(), rootWindow()); texture.reset(); #ifdef KWIN_HAVE_XRENDER_COMPOSITING xrenderPicture.reset(); #endif isMouseHidden = false; } } void ZoomEffect::hideCursor() { if (mouseTracking == MouseTrackingProportional && mousePointer == MousePointerKeep) return; // don't replace the actual cursor by a static image for no reason. if (!isMouseHidden) { // try to load the cursor-theme into a OpenGL texture and if successful then hide the mouse-pointer recreateTexture(); bool shouldHide = false; if (effects->isOpenGLCompositing()) { shouldHide = !texture.isNull(); } else if (effects->compositingType() == XRenderCompositing) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING shouldHide = !xrenderPicture.isNull(); #endif } if (shouldHide) { xcb_xfixes_hide_cursor(connection(), rootWindow()); isMouseHidden = true; } } } void ZoomEffect::recreateTexture() { // read details about the mouse-cursor theme define per default KConfigGroup mousecfg(KSharedConfig::openConfig(QStringLiteral("kcminputrc")), "Mouse"); QString theme = mousecfg.readEntry("cursorTheme", QString()); QString size = mousecfg.readEntry("cursorSize", QString()); // fetch a reasonable size for the cursor-theme image bool ok; int iconSize = size.toInt(&ok); if (!ok) iconSize = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize); // load the cursor-theme image from the Xcursor-library XcursorImage *ximg = XcursorLibraryLoadImage("left_ptr", theme.toLocal8Bit().constData(), iconSize); if (!ximg) // default is better then nothing, so keep it as backup ximg = XcursorLibraryLoadImage("left_ptr", "default", iconSize); if (ximg) { // turn the XcursorImage into a QImage that will be used to create the GLTexture/XRenderPicture. imageWidth = ximg->width; imageHeight = ximg->height; QImage img((uchar*)ximg->pixels, imageWidth, imageHeight, QImage::Format_ARGB32_Premultiplied); if (effects->isOpenGLCompositing()) texture.reset(new GLTexture(img)); #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) xrenderPicture.reset(new XRenderPicture(img)); #endif XcursorImageDestroy(ximg); } else { qDebug() << "Loading cursor image (" << theme << ") FAILED -> falling back to proportional mouse tracking!"; mouseTracking = MouseTrackingProportional; } } void ZoomEffect::reconfigure(ReconfigureFlags) { ZoomConfig::self()->readConfig(); // On zoom-in and zoom-out change the zoom by the defined zoom-factor. zoomFactor = qMax(0.1, ZoomConfig::zoomFactor()); // Visibility of the mouse-pointer. mousePointer = MousePointerType(ZoomConfig::mousePointer()); // Track moving of the mouse. mouseTracking = MouseTrackingType(ZoomConfig::mouseTracking()); // Enable tracking of the focused location. bool _enableFocusTracking = ZoomConfig::enableFocusTracking(); if (enableFocusTracking != _enableFocusTracking) { enableFocusTracking = _enableFocusTracking; if (QDBusConnection::sessionBus().isConnected()) { if (enableFocusTracking) QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.kaccessibleapp"), QStringLiteral("/Adaptor"), QStringLiteral("org.kde.kaccessibleapp.Adaptor"), QStringLiteral("focusChanged"), this, SLOT(focusChanged(int,int,int,int,int,int))); else QDBusConnection::sessionBus().disconnect(QStringLiteral("org.kde.kaccessibleapp"), QStringLiteral("/Adaptor"), QStringLiteral("org.kde.kaccessibleapp.Adaptor"), QStringLiteral("focusChanged"), this, SLOT(focusChanged(int,int,int,int,int,int))); } } // When the focus changes, move the zoom area to the focused location. followFocus = ZoomConfig::enableFollowFocus(); // The time in milliseconds to wait before a focus-event takes away a mouse-move. focusDelay = qMax(uint(0), ZoomConfig::focusDelay()); // The factor the zoom-area will be moved on touching an edge on push-mode or using the navigation KAction's. moveFactor = qMax(0.1, ZoomConfig::moveFactor()); if (source_zoom < 0) { // Load the saved zoom value. source_zoom = 1.0; target_zoom = ZoomConfig::initialZoom(); if (target_zoom > 1.0) zoomIn(target_zoom); } else { source_zoom = 1.0; } } void ZoomEffect::prePaintScreen(ScreenPrePaintData& data, int time) { bool altered = false; if (zoom != target_zoom) { altered = true; const float zoomDist = qAbs(target_zoom - source_zoom); if (target_zoom > zoom) zoom = qMin(zoom + ((zoomDist * time) / animationTime(150*zoomFactor)), target_zoom); else zoom = qMax(zoom - ((zoomDist * time) / animationTime(150*zoomFactor)), target_zoom); } if (zoom == 1.0) { showCursor(); // reset the generic shader to avoid artifacts in plenty other effects if (altered && effects->isOpenGLCompositing()) ShaderBinder binder(ShaderManager::GenericShader, true); } else { hideCursor(); data.mask |= PAINT_SCREEN_TRANSFORMED; } effects->prePaintScreen(data, time); } void ZoomEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (zoom != 1.0) { data *= QVector2D(zoom, zoom); // mouse-tracking allows navigation of the zoom-area using the mouse. switch(mouseTracking) { case MouseTrackingProportional: data.setXTranslation(- int(cursorPoint.x() * (zoom - 1.0))); data.setYTranslation(- int(cursorPoint.y() * (zoom - 1.0))); prevPoint = cursorPoint; break; case MouseTrackingCentred: prevPoint = cursorPoint; // fall through case MouseTrackingDisabled: data.setXTranslation(qMin(0, qMax(int(displayWidth() - displayWidth() * zoom), int(displayWidth() / 2 - prevPoint.x() * zoom)))); data.setYTranslation(qMin(0, qMax(int(displayHeight() - displayHeight() * zoom), int(displayHeight() / 2 - prevPoint.y() * zoom)))); break; case MouseTrackingPush: { // touching an edge of the screen moves the zoom-area in that direction. int x = cursorPoint.x() * zoom - prevPoint.x() * (zoom - 1.0); int y = cursorPoint.y() * zoom - prevPoint.y() * (zoom - 1.0); int threshold = 4; xMove = yMove = 0; if (x < threshold) xMove = (x - threshold) / zoom; else if (x + threshold > displayWidth()) xMove = (x + threshold - displayWidth()) / zoom; if (y < threshold) yMove = (y - threshold) / zoom; else if (y + threshold > displayHeight()) yMove = (y + threshold - displayHeight()) / zoom; if (xMove) prevPoint.setX(qMax(0, qMin(displayWidth(), prevPoint.x() + xMove))); if (yMove) prevPoint.setY(qMax(0, qMin(displayHeight(), prevPoint.y() + yMove))); data.setXTranslation(- int(prevPoint.x() * (zoom - 1.0))); data.setYTranslation(- int(prevPoint.y() * (zoom - 1.0))); break; } } // use the focusPoint if focus tracking is enabled if (enableFocusTracking && followFocus) { bool acceptFocus = true; if (mouseTracking != MouseTrackingDisabled && focusDelay > 0) { // Wait some time for the mouse before doing the switch. This serves as threshold // to prevent the focus from jumping around to much while working with the mouse. const int msecs = lastMouseEvent.msecsTo(lastFocusEvent); acceptFocus = msecs > focusDelay; } if (acceptFocus) { data.setXTranslation(- int(focusPoint.x() * (zoom - 1.0))); data.setYTranslation(- int(focusPoint.y() * (zoom - 1.0))); prevPoint = focusPoint; } } } effects->paintScreen(mask, region, data); if (zoom != 1.0 && mousePointer != MousePointerHide) { // Draw the mouse-texture at the position matching to zoomed-in image of the desktop. Hiding the // previous mouse-cursor and drawing our own fake mouse-cursor is needed to be able to scale the // mouse-cursor up and to re-position those mouse-cursor to match to the chosen zoom-level. int w = imageWidth; int h = imageHeight; if (mousePointer == MousePointerScale) { w *= zoom; h *= zoom; } const QPoint p = effects->cursorPos(); QRect rect(p.x() * zoom + data.xTranslation(), p.y() * zoom + data.yTranslation(), w, h); if (texture) { texture->bind(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->render(region, rect); texture->unbind(); glDisable(GL_BLEND); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (xrenderPicture) { #define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536)) static xcb_render_transform_t xrenderIdentity = { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; if (mousePointer == MousePointerScale) { xcb_render_set_picture_filter(connection(), *xrenderPicture, 4, const_cast("good"), 0, NULL); const xcb_render_transform_t xform = { DOUBLE_TO_FIXED(1.0 / zoom), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1.0 / zoom), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; xcb_render_set_picture_transform(connection(), *xrenderPicture, xform); } xcb_render_composite(connection(), XCB_RENDER_PICT_OP_OVER, *xrenderPicture, XCB_RENDER_PICTURE_NONE, effects->xrenderBufferPicture(), 0, 0, 0, 0, rect.x(), rect.y(), rect.width(), rect.height()); if (mousePointer == MousePointerScale) xcb_render_set_picture_transform(connection(), *xrenderPicture, xrenderIdentity); #undef DOUBLE_TO_FIXED } #endif } } void ZoomEffect::postPaintScreen() { if (zoom != target_zoom) effects->addRepaintFull(); effects->postPaintScreen(); } void ZoomEffect::zoomIn(double to) { source_zoom = zoom; if (to < 0.0) target_zoom *= zoomFactor; else target_zoom = to; if (!polling) { polling = true; effects->startMousePolling(); } + cursorPoint = effects->cursorPos(); if (mouseTracking == MouseTrackingDisabled) - prevPoint = effects->cursorPos(); + prevPoint = cursorPoint; effects->addRepaintFull(); } void ZoomEffect::zoomOut() { source_zoom = zoom; target_zoom /= zoomFactor; if (target_zoom < 1) { target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } } if (mouseTracking == MouseTrackingDisabled) prevPoint = effects->cursorPos(); effects->addRepaintFull(); } void ZoomEffect::actualSize() { source_zoom = zoom; target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } effects->addRepaintFull(); } void ZoomEffect::timelineFrameChanged(int /* frame */) { prevPoint.setX(qMax(0, qMin(displayWidth(), prevPoint.x() + xMove))); prevPoint.setY(qMax(0, qMin(displayHeight(), prevPoint.y() + yMove))); cursorPoint = prevPoint; effects->addRepaintFull(); } void ZoomEffect::moveZoom(int x, int y) { if (timeline.state() == QTimeLine::Running) timeline.stop(); if (x < 0) xMove = - qMax(1.0, displayWidth() / zoom / moveFactor); else if (x > 0) xMove = qMax(1.0, displayWidth() / zoom / moveFactor); else xMove = 0; if (y < 0) yMove = - qMax(1.0, displayHeight() / zoom / moveFactor); else if (y > 0) yMove = qMax(1.0, displayHeight() / zoom / moveFactor); else yMove = 0; timeline.start(); } void ZoomEffect::moveZoomLeft() { moveZoom(-1, 0); } void ZoomEffect::moveZoomRight() { moveZoom(1, 0); } void ZoomEffect::moveZoomUp() { moveZoom(0, -1); } void ZoomEffect::moveZoomDown() { moveZoom(0, 1); } void ZoomEffect::moveMouseToFocus() { QCursor::setPos(focusPoint.x(), focusPoint.y()); } void ZoomEffect::moveMouseToCenter() { QRect r(0, 0, displayWidth(), displayHeight()); QCursor::setPos(r.x() + r.width() / 2, r.y() + r.height() / 2); } void ZoomEffect::slotMouseChanged(const QPoint& pos, const QPoint& old, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) { if (zoom == 1.0) return; cursorPoint = pos; if (pos != old) { lastMouseEvent = QTime::currentTime(); effects->addRepaintFull(); } } void ZoomEffect::focusChanged(int px, int py, int rx, int ry, int rwidth, int rheight) { if (zoom == 1.0) return; focusPoint = (px >= 0 && py >= 0) ? QPoint(px, py) : QPoint(rx + qMax(0, (qMin(displayWidth(), rwidth) / 2) - 60), ry + qMax(0, (qMin(displayHeight(), rheight) / 2) - 60)); if (enableFocusTracking) { lastFocusEvent = QTime::currentTime(); effects->addRepaintFull(); } } bool ZoomEffect::isActive() const { return zoom != 1.0 || zoom != target_zoom; } } // namespace #include "zoom.moc"