diff --git a/applets/pager/package/contents/ui/main.qml b/applets/pager/package/contents/ui/main.qml index 065056184..f166374ad 100644 --- a/applets/pager/package/contents/ui/main.qml +++ b/applets/pager/package/contents/ui/main.qml @@ -1,330 +1,372 @@ /* * Copyright 2012 Luís Gabriel Lima + * Copyright 2016 Kai Uwe Broulik * * 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 . */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddonsComponents import org.kde.draganddrop 2.0 import org.kde.plasma.private.pager 2.0 import "utils.js" as Utils MouseArea { id: root property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical) Layout.minimumWidth: !root.vertical ? pager.preferredSize.width : 1 Layout.minimumHeight: root.vertical ? pager.preferredSize.height : 1 Layout.maximumWidth: !root.vertical ? pager.preferredSize.width : Infinity Layout.maximumHeight: root.vertical ? pager.preferredSize.height : Infinity Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation Plasmoid.status: pager.desktopCount > 1 ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.HiddenStatus Layout.fillWidth: root.vertical Layout.fillHeight: !root.vertical property bool dragging: false property int dragId property int dragSwitchDesktopId: -1 anchors.fill: parent acceptedButtons: Qt.NoButton function colorWithAlpha(color, alpha) { return Qt.rgba(color.r, color.g, color.b, alpha) } readonly property color windowActiveOnActiveDesktopColor: colorWithAlpha(theme.textColor, 0.6) readonly property color windowInactiveOnActiveDesktopColor: colorWithAlpha(theme.textColor, 0.35) readonly property color windowActiveColor: colorWithAlpha(theme.textColor, 0.5) readonly property color windowActiveBorderColor: theme.textColor readonly property color windowInactiveColor: colorWithAlpha(theme.textColor, 0.17) readonly property color windowInactiveBorderColor: colorWithAlpha(theme.textColor, 0.5) function action_addDesktop() { pager.slotAddDesktop(); } function action_removeDesktop() { pager.slotRemoveDesktop(); } function action_openKCM() { KQuickControlsAddonsComponents.KCMShell.open("desktop") } onWheel: { if (wheel.angleDelta.y > 0 || wheel.angleDelta.x > 0) { pager.changeDesktop((repeater.count + pager.currentDesktop - 2) % repeater.count) } else { pager.changeDesktop(pager.currentDesktop % repeater.count) } } Component.onCompleted: { if (KQuickControlsAddonsComponents.KCMShell.authorize("desktop.desktop").length > 0) { plasmoid.setAction("addDesktop", i18n("Add Virtual Desktop"), "list-add"); plasmoid.setAction("removeDesktop", i18n("Remove Virtual Desktop"), "list-remove"); plasmoid.action("removeDesktop").enabled = Qt.binding(function() { return repeater.count > 1; }); plasmoid.setAction("openKCM", i18n("Configure Desktops..."), "configure"); } } Pager { id: pager // don't bother updating the models when we're not visible enabled: root.visible orientation: plasmoid.formFactor == PlasmaCore.Types.Vertical ? Qt.Vertical : Qt.Horizontal size: Qt.size(root.width, root.height) showWindowIcons: plasmoid.configuration.showWindowIcons currentDesktopSelected: plasmoid.configuration.currentDesktopSelected displayedText: plasmoid.configuration.displayedText } Timer { id: dragTimer interval: 1000 onTriggered: { if (dragSwitchDesktopId != -1 && dragSwitchDesktopId !== pager.currentDesktop-1) { pager.changeDesktop(dragSwitchDesktopId); } } } Repeater { id: repeater model: pager.model PlasmaCore.ToolTipArea { id: desktop property int desktopId: index property string desktopName: model.desktopName ? model.desktopName : "" property bool active: (desktopId === pager.currentDesktop-1) mainText: desktopName + // our ToolTip has maximumLineCount of 8 which doesn't fit but QML doesn't + // respect that in RichText so we effectively can put in as much as we like :) + // it also gives us more flexibility when it comes to styling the
  • + textFormat: Text.RichText + + function updateSubText() { + var generateWindowList = function windowList(windows) { + // if we have 5 windows, we would show "4 and another one" with the + // hint that there's 1 more taking the same amount of space than just showing it + var maximum = windows.length === 5 ? 5 : 4 + + var text = "
    • " + + windows.slice(0, maximum).join("
    • ") + + "
    " + + if (windows.length > maximum) { + text += i18np("...and %1 other window", "...and %1 other windows", windows.length - maximum) + } + + return text + } + + var text = "" + var visibleWindows = [] + var minimizedWindows = [] + + for (var i = 0, length = windowRectRepeater.count; i < length; ++i) { + var window = windowRectRepeater.itemAt(i) + if (window) { + if (window.minimized) { + minimizedWindows.push(window.visibleName) + } else { + visibleWindows.push(window.visibleName) + } + } + } + + if (visibleWindows.length) { + text += i18np("%1 Window:", "%1 Windows:", visibleWindows.length) + + generateWindowList(visibleWindows) + } + + if (visibleWindows.length && minimizedWindows.length) { + text += "
    " + } + + if (minimizedWindows.length > 0) { + text += i18np("%1 Minimized Window:", "%1 Minimized Windows:", minimizedWindows.length) + + generateWindowList(minimizedWindows) + } + + if (text.length) { + // Get rid of the spacing
      would cause + text = "" + text + } + + subText = text + } x: model.x y: model.y width: model.width height: model.height PlasmaCore.FrameSvgItem { anchors.fill: parent z: 1 // to make sure that the FrameSvg will be placed on top of the windows imagePath: "widgets/pager" prefix: (desktopMouseArea.enabled && desktopMouseArea.containsMouse) || (root.dragging && root.dragId == desktopId) ? "hover" : (desktop.active ? "active" : "normal") } DropArea { id: droparea anchors.fill: parent preventStealing: true onDragEnter: { root.dragSwitchDesktopId = desktop.desktopId; dragTimer.start(); } onDragLeave: { root.dragSwitchDesktopId = -1; dragTimer.stop(); } onDrop: { pager.dropMimeData(event.mimeData, desktop.desktopId); root.dragSwitchDesktopId = -1; dragTimer.stop(); } } MouseArea { id: desktopMouseArea anchors.fill: parent hoverEnabled : true onClicked: pager.changeDesktop(desktopId); } Item { id: clipRect x: Math.round(units.devicePixelRatio) y: Math.round(units.devicePixelRatio) width: desktop.width - 2 * x height: desktop.height - 2 * y clip: true PlasmaComponents.Label { id: desktopText anchors.centerIn: parent text: pager.displayedText == Pager.Name ? desktop.desktopName : (pager.displayedText == Pager.Number ? desktop.desktopId+1 : "") } Repeater { + id: windowRectRepeater model: windows - - //update the tooltip whenever we add or remove a window - onCountChanged: { - var tooltipText = i18np("%1 window", "%1 windows", count) - - if (count) { - var i; - tooltipText += "
        "; - for (i=0; i < Math.min(count,4); i++) { - if (itemAt(i)) { - tooltipText += "
      • "+ itemAt(i).visibleName +"
      • "; - } - } - tooltipText += "
      "; - if (i < count) { - tooltipText += "
      " - tooltipText += i18np("and %1 other window", "and %1 other windows", count-i) - } - } - - desktop.subText = tooltipText - } + onCountChanged: desktop.updateSubText() Rectangle { id: windowRect property int windowId: model.windowId property string visibleName: model.visibleName + property bool minimized: model.minimized + onMinimizedChanged: desktop.updateSubText() /* since we move clipRect with 1, move it back */ x: model.x - Math.round(units.devicePixelRatio) y: model.y - Math.round(units.devicePixelRatio) width: model.width height: model.height + visible: !model.visible color: { if (desktop.active) { if (model.active) return windowActiveOnActiveDesktopColor; else return windowInactiveOnActiveDesktopColor; } else { if (model.active) return windowActiveColor; else return windowInactiveColor; } } border.width: Math.round(units.devicePixelRatio) border.color: model.active ? windowActiveBorderColor : windowInactiveBorderColor KQuickControlsAddonsComponents.QPixmapItem { id: icon anchors.centerIn: parent pixmap: model.icon height: nativeHeight width: nativeWidth visible: pager.showWindowIcons } MouseArea { id: windowMouseArea anchors.fill: parent drag.target: windowRect drag.axis: Drag.XandYAxis drag.minimumX: -windowRect.width/2 drag.maximumX: root.width - windowRect.width/2 drag.minimumY: -windowRect.height/2 drag.maximumY: root.height - windowRect.height/2 // used to save the state of some properties before the dragging QtObject { id: saveState property int x: -1 property int y: -1 property variant parent property int desktop: -1 property int mouseX: -1 property int mouseY: -1 } drag.onActiveChanged: { root.dragging = drag.active; desktopMouseArea.enabled = !drag.active; } // reparent windowRect to enable the dragging for other desktops onPressed: { if (windowRect.parent == root) return; saveState.x = windowRect.x; saveState.y = windowRect.y saveState.parent = windowRect.parent; saveState.desktop = desktop.desktopId; saveState.mouseX = mouseX; saveState.mouseY = mouseY; var value = root.mapFromItem(clipRect, windowRect.x, windowRect.y); windowRect.x = value.x; windowRect.y = value.y windowRect.parent = root; } onReleased: { if (root.dragging) { pager.moveWindow(windowRect.windowId, windowRect.x, windowRect.y, root.dragId, saveState.desktop); } else { // when there is no dragging (just a click), the event is passed // to the desktop mousearea desktopMouseArea.clicked(mouse); } windowRect.x = saveState.x; windowRect.y = saveState.y; windowRect.parent = saveState.parent; } } function checkDesktopHover() { if (!windowMouseArea.drag.active) return; var mouse = root.mapFromItem(windowRect, saveState.mouseX, saveState.mouseY); for (var i = 0; i < root.children.length; i++) { var item = root.children[i]; if (item.desktopId != undefined && Utils.contains(item, mouse)) { root.dragId = item.desktopId; return; } } } onXChanged: checkDesktopHover(); onYChanged: checkDesktopHover(); } } } } } } diff --git a/applets/pager/plugin/model.cpp b/applets/pager/plugin/model.cpp index 595e3c65a..a64e3caab 100644 --- a/applets/pager/plugin/model.cpp +++ b/applets/pager/plugin/model.cpp @@ -1,247 +1,252 @@ /* * Copyright 2012 Luís Gabriel Lima * * 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 "model.h" RectangleModel::RectangleModel(QObject *parent) : QAbstractListModel(parent) { } QHash RectangleModel::roleNames() const { QHash rectRoles; rectRoles[WidthRole] = "width"; rectRoles[HeightRole] = "height"; rectRoles[XRole] = "x"; rectRoles[YRole] = "y"; return rectRoles; } void RectangleModel::clear() { m_rects.clear(); } void RectangleModel::append(const QRectF &rect) { m_rects.append(rect); } QRectF &RectangleModel::rectAt(int index) { return m_rects[index]; } int RectangleModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_rects.count(); } QVariant RectangleModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() > m_rects.count()) return QVariant(); const QRectF &rect = m_rects[index.row()]; switch(role) { case WidthRole: return rect.width(); case HeightRole: return rect.height(); case XRole: return rect.x(); case YRole: return rect.y(); default: return QVariant(); } } WindowModel::WindowModel(QObject *parent) : RectangleModel(parent) { } QHash WindowModel::roleNames() const { QHash rectRoles = RectangleModel::roleNames(); rectRoles[IdRole] = "windowId"; rectRoles[ActiveRole] = "active"; rectRoles[IconRole] = "icon"; rectRoles[VisibleNameRole] = "visibleName"; + rectRoles[MinimizedRole] = "minimized"; return rectRoles; } void WindowModel::clear() { beginResetModel(); RectangleModel::clear(); m_ids.clear(); m_active.clear(); m_icons.clear(); m_visibleNames.clear(); + m_minimized.clear(); endResetModel(); } void WindowModel::append(WId windowId, const QRectF &rect, bool active, - const QPixmap &icon, const QString &name) + const QPixmap &icon, const QString &name, bool minimized) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_ids.append(windowId); RectangleModel::append(rect); m_active.append(active); m_icons.append(icon); m_visibleNames.append(name); + m_minimized.append(minimized); endInsertRows(); } WId WindowModel::idAt(int index) const { return m_ids[index]; } QString WindowModel::visibleNameAt(int index) const { return m_visibleNames[index]; } QVariant WindowModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= rowCount()) return QVariant(); if (role >= RectangleModel::WidthRole && role < IdRole) return RectangleModel::data(index, role); switch (role) { case IdRole: return int(m_ids[index.row()]); case ActiveRole: return m_active[index.row()]; case IconRole: return m_icons[index.row()]; case VisibleNameRole: return m_visibleNames[index.row()]; + case MinimizedRole: + return m_minimized.value(index.row()); default: return QVariant(); } } PagerModel::PagerModel(QObject *parent) : QAbstractListModel(parent) { } WindowModel *PagerModel::windowsAt(int index) const { if (index < 0 || index >= m_windows.count()) return 0; return qobject_cast(m_windows[index]); } QHash PagerModel::roleNames() const { QHash rectRoles = m_desktops.roleNames(); rectRoles[WindowsRole] = "windows"; rectRoles[DesktopNameRole] = "desktopName"; return rectRoles; } void PagerModel::clearDesktopRects() { beginResetModel(); m_desktops.clear(); m_names.clear(); endResetModel(); } void PagerModel::appendDesktopRect(const QRectF &rect, const QString &name) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_desktops.append(rect); m_names.append(name); endInsertRows(); } QRectF& PagerModel::desktopRectAt(int index) { return m_desktops.rectAt(index); } void PagerModel::clearWindowRects() { int removeIndex = -1; for (int i = 0; i < m_windows.count(); i++) { windowsAt(i)->clear(); if (i >= rowCount()) removeIndex = (removeIndex == -1) ? i : -1; } if (removeIndex != -1) { // remove the windows model if the number of desktop has decreased for (int i = m_windows.count()-1; i >= removeIndex; i--) { windowsAt(i)->deleteLater(); m_windows.removeAt(i); } } // append more windows model if the number of desktop has increased for (int i = m_windows.count(); i < rowCount(); i++) m_windows.append(new WindowModel(this)); } -void PagerModel::appendWindowRect(int desktopId, WId windowId, const QRectF &rect, - bool active, const QPixmap &icon, const QString &name) +void PagerModel::appendWindowRect(int desktopId, WId windowId, const QRectF &rect, bool active, + const QPixmap &icon, const QString &name, bool minimized) { WindowModel *windows = windowsAt(desktopId); if (!windows) return; - windows->append(windowId, rect, active, icon, name); + windows->append(windowId, rect, active, icon, name, minimized); QModelIndex i = index(desktopId); emit dataChanged(i, i); } QVariant PagerModel::data(const QModelIndex &index, int role) const { if (role >= RectangleModel::WidthRole && role < WindowsRole) return m_desktops.data(index, role); if (index.row() < 0 || index.row() >= m_windows.count()) return QVariant(); switch (role) { case WindowsRole: return QVariant::fromValue(m_windows[index.row()]); case DesktopNameRole: return m_names[index.row()]; default: return QVariant(); } } int PagerModel::rowCount(const QModelIndex &index) const { return m_desktops.rowCount(index); } diff --git a/applets/pager/plugin/model.h b/applets/pager/plugin/model.h index 91dabc2e9..9fb6ad7d1 100644 --- a/applets/pager/plugin/model.h +++ b/applets/pager/plugin/model.h @@ -1,112 +1,114 @@ /* * Copyright 2012 Luís Gabriel Lima * * 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 . */ #ifndef MODEL_H #define MODEL_H #include #include #include // For WId #include class RectangleModel : public QAbstractListModel { Q_OBJECT public: enum RectangleRoles { WidthRole = Qt::UserRole + 1, HeightRole, XRole, YRole }; RectangleModel(QObject *parent = 0); virtual QHash roleNames() const; virtual void clear(); void append(const QRectF &rect); QRectF &rectAt(int index); int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; private: QList m_rects; }; class WindowModel : public RectangleModel { Q_OBJECT public: enum WindowRole { IdRole = RectangleModel::YRole + 1, ActiveRole, IconRole, - VisibleNameRole + VisibleNameRole, + MinimizedRole }; WindowModel(QObject *parent = 0); QHash roleNames() const; void clear(); - void append(WId, const QRectF &, bool active, const QPixmap &icon, const QString &name); + void append(WId, const QRectF &, bool active, const QPixmap &icon, const QString &name, bool minimized); WId idAt(int index) const; QString visibleNameAt(int index) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; private: QList m_ids; QList m_active; QList m_icons; QStringList m_visibleNames; + QVector m_minimized; }; class PagerModel : public QAbstractListModel { Q_OBJECT public: enum PagerRoles { WindowsRole = RectangleModel::YRole + 1, DesktopNameRole }; PagerModel(QObject *parent = 0); QHash roleNames() const; void clearDesktopRects(); void appendDesktopRect(const QRectF &rect, const QString &name); QRectF &desktopRectAt(int index); void clearWindowRects(); void appendWindowRect(int desktopId, WId, const QRectF &, bool active, - const QPixmap &icon, const QString &name); + const QPixmap &icon, const QString &name, bool minimized); WindowModel *windowsAt(int index) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; private: RectangleModel m_desktops; QList m_windows; QStringList m_names; }; #endif // MODEL_H diff --git a/applets/pager/plugin/pager.cpp b/applets/pager/plugin/pager.cpp index c70eb88be..880a4d03f 100644 --- a/applets/pager/plugin/pager.cpp +++ b/applets/pager/plugin/pager.cpp @@ -1,634 +1,634 @@ /*************************************************************************** * Copyright (C) 2007 by Daniel Laidig * * Copyright (C) 2012 by Luís Gabriel Lima * * * * 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 . * ***************************************************************************/ #include "pager.h" #include #include #include #include #include #if HAVE_X11 #include #endif #include #include #include #include #include #include #include #include #include #if HAVE_X11 #include #endif #include #include #include #include const int FAST_UPDATE_DELAY = 100; const int UPDATE_DELAY = 500; const int MAXDESKTOPS = 20; // random(), find a less magic one if you can. -sreich const qreal MAX_TEXT_WIDTH = 800; Pager::Pager(QObject *parent) : QObject(parent), m_displayedText(None), m_currentDesktopSelected(DoNothing), m_columns(0), m_currentDesktop(0), m_orientation(Qt::Horizontal), m_showWindowIcons(false), m_desktopDown(false), m_validSizes(false), m_enabled(true), m_desktopWidget(QApplication::desktop()) #if HAVE_X11 , m_isX11(QX11Info::isPlatformX11()) #else , m_isX11(false) #endif { //give rows the minimum default for non X11 systems m_rows = 1; #if HAVE_X11 if (m_isX11) { NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::DesktopNames, NET::WM2DesktopLayout); m_rows = info.desktopLayoutColumnsRows().height(); } #endif // initialize with a decent default m_desktopCount = qMax(1, KWindowSystem::numberOfDesktops()); m_pagerModel = new PagerModel(this); m_timer = new QTimer(this); m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &Pager::recalculateWindowRects); connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(currentDesktopChanged(int))); connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, &Pager::startTimerFast); connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, this, &Pager::startTimerFast); connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &Pager::startTimerFast); connect(KWindowSystem::self(), &KWindowSystem::numberOfDesktopsChanged, this, &Pager::numberOfDesktopsChanged); connect(KWindowSystem::self(), &KWindowSystem::desktopNamesChanged, this, &Pager::desktopNamesChanged); connect(KWindowSystem::self(), &KWindowSystem::stackingOrderChanged, this, &Pager::startTimerFast); connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,const ulong*)), this, SLOT(windowChanged(WId,const ulong*))); connect(KWindowSystem::self(), SIGNAL(showingDesktopChanged(bool)), this, SLOT(startTimer())); connect(m_desktopWidget, &QDesktopWidget::screenCountChanged, this, &Pager::desktopsSizeChanged); connect(m_desktopWidget, &QDesktopWidget::resized, this, &Pager::desktopsSizeChanged); // connect to KWin's reloadConfig signal to get updates on the desktop layout QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.connect(QString(), QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig"), this, SLOT(desktopsSizeChanged())); recalculateGridSizes(m_rows); setCurrentDesktop(KWindowSystem::currentDesktop()); KActivities::Consumer *act = new KActivities::Consumer(this); connect(act, &KActivities::Consumer::currentActivityChanged, this, &Pager::currentActivityChanged); m_currentActivity = act->currentActivity(); } Pager::~Pager() { } void Pager::setEnabled(bool enabled) { if (m_enabled != enabled) { m_enabled = enabled; emit enabledChanged(); if (enabled) { startTimerFast(); } } } int Pager::desktopCount() const { return m_desktopCount; } void Pager::setCurrentDesktop(int desktop) { if (m_currentDesktop != desktop) { m_currentDesktop = desktop; emit currentDesktopChanged(); } } void Pager::setShowWindowIcons(bool show) { if (m_showWindowIcons != show) { m_showWindowIcons = show; emit showWindowIconsChanged(); } } Qt::Orientation Pager::orientation() const { return m_orientation; } void Pager::setOrientation(Qt::Orientation orientation) { if (m_orientation == orientation) { return; } m_orientation = orientation; emit orientationChanged(); // whenever we switch to/from vertical form factor, swap the rows and columns around if (m_columns != m_rows) { // pass in columns as the new rows recalculateGridSizes(m_columns); recalculateWindowRects(); } } QSizeF Pager::size() const { return m_size; } QSize Pager::preferredSize() const { return m_preferredSize; } void Pager::setSize(const QSizeF &size) { if (m_size == size) { return; } m_size = size; emit sizeChanged(); m_validSizes = false; startTimer(); } Pager::CurrentDesktopSelected Pager::currentDesktopSelected() const { return m_currentDesktopSelected; } void Pager::setCurrentDesktopSelected(CurrentDesktopSelected cur) { if (m_currentDesktopSelected == cur) { return; } m_currentDesktopSelected = cur; emit currentDesktopSelectedChanged(); } Pager::DisplayedText Pager::displayedText() const { return m_displayedText; } void Pager::setDisplayedText(Pager::DisplayedText disp) { if (m_displayedText == disp) { return; } m_displayedText = disp; emit displayedTextChanged(); } #if HAVE_X11 void Pager::slotAddDesktop() { if (!m_isX11) { return; } NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops); info.setNumberOfDesktops(info.numberOfDesktops() + 1); } void Pager::slotRemoveDesktop() { if (!m_isX11) { return; } NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops); int desktops = info.numberOfDesktops(); if (desktops > 1) { info.setNumberOfDesktops(info.numberOfDesktops() - 1); } } #endif void Pager::recalculateGridSizes(int rows) { // recalculate the number of rows and columns in the grid rows = qBound(1, rows, m_desktopCount); // avoid weird cases like having 3 rows for 4 desktops, where the last row is unused int columns = m_desktopCount / rows; if (m_desktopCount % rows > 0) { columns++; } rows = m_desktopCount / columns; if (m_desktopCount % columns > 0) { rows++; } // update the grid size m_rows = rows; m_columns = columns; updateSizes(); } void Pager::updateSizes() { const int padding = 2; // Space between miniatures of desktops const int textMargin = 3; // Space between name of desktop and border const qreal leftMargin = 0; const qreal topMargin = 0; const qreal rightMargin = 0; const qreal bottomMargin = 0; QRect totalRect; for (int x = 0; x < m_desktopWidget->screenCount(); x++) { totalRect |= m_desktopWidget->screenGeometry(x); } const qreal ratio = (qreal) totalRect.width() / (qreal) totalRect.height(); qreal itemHeight; qreal itemWidth; qreal preferredItemHeight; qreal preferredItemWidth; if (orientation() == Qt::Vertical) { itemWidth = (m_size.width() - leftMargin - rightMargin - padding * (m_columns - 1)) / m_columns; itemHeight = itemWidth / ratio; } else { // work out the preferred size based on the height of the geometry preferredItemHeight = (m_size.height() - topMargin - bottomMargin - padding * (m_rows - 1)) / m_rows; preferredItemWidth = preferredItemHeight * ratio; if (m_displayedText == Name) { // When containment is in this position we are not limited by low width and we can // afford increasing width of applet to be able to display every name of desktops for (int i = 0; i < m_desktopCount; i++) { QFontMetricsF metrics(KGlobalSettings::taskbarFont()); QSizeF textSize = metrics.size(Qt::TextSingleLine, KWindowSystem::desktopName(i+1)); if (textSize.width() + textMargin * 2 > preferredItemWidth) { preferredItemWidth = textSize.width() + textMargin * 2; } } } itemWidth = (m_size.width() - leftMargin - rightMargin - padding * (m_columns - 1)) / m_columns; if (itemWidth > preferredItemWidth) { itemWidth = preferredItemWidth; } itemHeight = preferredItemHeight; if (itemWidth < itemHeight * ratio) { itemWidth = itemHeight * ratio; } } m_widthScaleFactor = itemWidth / totalRect.width(); m_heightScaleFactor = itemHeight / totalRect.height(); m_pagerModel->clearDesktopRects(); int pw = (int)(leftMargin + (itemWidth + padding) * m_columns + rightMargin); int ph = (int)(topMargin + (itemHeight + padding) * m_rows + bottomMargin); if (orientation() == Qt::Vertical) { pw = m_size.width(); } else { ph = m_size.height(); } m_preferredSize = QSize(pw, ph); emit preferredSizeChanged(); QRectF itemRect(QPointF(leftMargin, topMargin) , QSizeF(itemWidth, itemHeight)); for (int i = 0; i < m_desktopCount; i++) { itemRect.moveLeft(leftMargin + (i % m_columns) * (itemWidth + padding)); itemRect.moveTop(topMargin + (i / m_columns) * (itemHeight + padding)); QString name = KWindowSystem::desktopName(i + 1); m_pagerModel->appendDesktopRect(itemRect, name); } m_validSizes = true; } void Pager::recalculateWindowRects() { if (!m_enabled) { return; } if (!m_isX11) { return; } #if HAVE_X11 NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::DesktopNames, NET::WM2DesktopLayout); m_rows = info.desktopLayoutColumnsRows().height(); if (!m_validSizes) { recalculateGridSizes(m_rows); updateSizes(); } QList windows = KWindowSystem::stackingOrder(); m_pagerModel->clearWindowRects(); foreach (WId window, windows) { KWindowInfo info = KWindowSystem::windowInfo(window, NET::WMGeometry | NET::WMFrameExtents | NET::WMWindowType | NET::WMDesktop | NET::WMState | NET::XAWMState | NET::WMVisibleName); NET::WindowType type = info.windowType(NET::NormalMask | NET::DialogMask | NET::OverrideMask | NET::UtilityMask | NET::DesktopMask | NET::DockMask | NET::TopMenuMask | NET::SplashMask | NET::ToolbarMask | NET::MenuMask); // the reason we don't check for -1 or Net::Unknown here is that legitimate windows, such // as some java application windows, may not have a type set for them. // apparently sane defaults on properties is beyond the wisdom of x11. if (type == NET::Desktop || type == NET::Dock || type == NET::TopMenu || type == NET::Splash || type == NET::Menu || type == NET::Toolbar || - info.hasState(NET::SkipPager) || info.isMinimized()) { + info.hasState(NET::SkipPager)) { continue; } //check activity NETWinInfo netInfo(QX11Info::connection(), window, QX11Info::appRootWindow(), 0, NET::WM2Activities); QString result(netInfo.activities()); if (!result.isEmpty() && result != QLatin1String("00000000-0000-0000-0000-000000000000")) { QStringList activities = result.split(','); if (!activities.contains(m_currentActivity)) { continue; } } for (int i = 0; i < m_desktopCount; i++) { if (!info.isOnDesktop(i+1)) { continue; } QRectF windowRect = info.frameGeometry(); if (KWindowSystem::mapViewport()) { windowRect = fixViewportPosition(windowRect.toRect()); } windowRect = QRectF(windowRect.x() * m_widthScaleFactor, windowRect.y() * m_heightScaleFactor, windowRect.width() * m_widthScaleFactor, windowRect.height() * m_heightScaleFactor).toRect(); bool active = (window == KWindowSystem::activeWindow()); int windowIconSize = KIconLoader::global()->currentSize(KIconLoader::Small); int windowRectSize = qMin(windowRect.width(), windowRect.height()); windowIconSize = qMax(windowIconSize, windowRectSize / 2); QPixmap icon = KWindowSystem::icon(info.win(), windowIconSize, windowIconSize, true); - m_pagerModel->appendWindowRect(i, window, windowRect, active, icon, info.visibleName()); + m_pagerModel->appendWindowRect(i, window, windowRect, active, icon, info.visibleName(), info.isMinimized()); } } #endif } void Pager::currentDesktopChanged(int desktop) { if (desktop < 1) { return; // bogus value, don't accept it } setCurrentDesktop(desktop); m_desktopDown = false; startTimerFast(); } void Pager::currentActivityChanged(const QString &activity) { m_currentActivity = activity; startTimerFast(); } void Pager::numberOfDesktopsChanged(int num) { if (!m_isX11) { return; } #if HAVE_X11 if (num < 1) { return; // refuse to update to zero desktops } NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::DesktopNames, NET::WM2DesktopLayout); m_rows = info.desktopLayoutColumnsRows().height(); if (num != m_desktopCount) { m_desktopCount = num; emit desktopCountChanged(); } m_pagerModel->clearDesktopRects(); recalculateGridSizes(m_rows); recalculateWindowRects(); #endif } void Pager::desktopNamesChanged() { m_pagerModel->clearDesktopRects(); m_validSizes = false; startTimer(); } void Pager::windowChanged(WId id, const unsigned long* dirty) { if (!m_isX11) { return; } Q_UNUSED(id) #if HAVE_X11 if (dirty[NETWinInfo::PROTOCOLS] & (NET::WMGeometry | NET::WMDesktop) || dirty[NETWinInfo::PROTOCOLS2] & NET::WM2Activities) { startTimer(); } #endif } void Pager::desktopsSizeChanged() { m_pagerModel->clearDesktopRects(); m_validSizes = false; startTimer(); } void Pager::startTimer() { if (!m_timer->isActive()) { m_timer->start(UPDATE_DELAY); } } void Pager::startTimerFast() { if (!m_timer->isActive()) { m_timer->start(FAST_UPDATE_DELAY); } } void Pager::moveWindow(int window, double x, double y, int targetDesktop, int sourceDesktop) { #if HAVE_X11 if (!m_isX11) { return; } WId windowId = (WId) window; QPointF dest = QPointF(x, y) - m_pagerModel->desktopRectAt(targetDesktop).topLeft(); dest = QPointF(dest.x()/m_widthScaleFactor, dest.y()/m_heightScaleFactor); // don't move windows to negative positions dest = QPointF(qMax(dest.x(), qreal(0.0)), qMax(dest.y(), qreal(0.0))); // use _NET_MOVERESIZE_WINDOW rather than plain move, so that the WM knows this is a pager request NETRootInfo info(QX11Info::connection(), 0); int flags = (0x20 << 12) | (0x03 << 8) | 1; // from tool, x/y, northwest gravity if (!KWindowSystem::mapViewport()) { KWindowInfo windowInfo = KWindowSystem::windowInfo(windowId, NET::WMDesktop | NET::WMState); if (!windowInfo.onAllDesktops()) { KWindowSystem::setOnDesktop(windowId, targetDesktop+1); } // only move the window if it is not full screen and if it is kept within the same desktop // moving when dropping between desktop is too annoying due to the small drop area. if (!(windowInfo.state() & NET::FullScreen) && (targetDesktop == sourceDesktop || windowInfo.onAllDesktops())) { QPoint d = dest.toPoint(); info.moveResizeWindowRequest(windowId, flags, d.x(), d.y(), 0, 0); } } else { // setOnDesktop() with viewports is also moving a window, and since it takes a moment // for the WM to do the move, there's a race condition with figuring out how much to move, // so do it only as one move dest += KWindowSystem::desktopToViewport(targetDesktop+1, false); QPoint d = KWindowSystem::constrainViewportRelativePosition(dest.toPoint()); info.moveResizeWindowRequest(windowId, flags, d.x(), d.y(), 0, 0); } m_timer->start(); #else Q_UNUSED(window) Q_UNUSED(x) Q_UNUSED(y) Q_UNUSED(targetDesktop) Q_UNUSED(sourceDesktop) #endif } void Pager::changeDesktop(int newDesktop) { #if HAVE_X11 if (!m_isX11) { return; } if (m_currentDesktop == newDesktop+1) { // toogle the desktop if (m_currentDesktopSelected == ShowDesktop) { NETRootInfo info(QX11Info::connection(), 0); m_desktopDown = !m_desktopDown; info.setShowingDesktop(m_desktopDown); } } else { KWindowSystem::setCurrentDesktop(newDesktop + 1); setCurrentDesktop(newDesktop + 1); } #else Q_UNUSED(newDesktop) #endif } void Pager::dropMimeData(QMimeData *mimeData, int desktopId) { if (!mimeData) { return; } bool ok; const QList &ids = LegacyTaskManager::Task::idsFromMimeData(mimeData, &ok); if (ok) { foreach (const WId &id, ids) { KWindowSystem::setOnDesktop(id, desktopId + 1); } } } // KWindowSystem does not translate position when mapping viewports // to virtual desktops (it'd probably break more things than fix), // so the offscreen coordinates need to be fixed QRect Pager::fixViewportPosition( const QRect& r ) { QRect desktopGeom = m_desktopWidget->geometry(); int x = r.center().x() % desktopGeom.width(); int y = r.center().y() % desktopGeom.height(); if( x < 0 ) { x = x + desktopGeom.width(); } if( y < 0 ) { y = y + desktopGeom.height(); } return QRect( x - r.width() / 2, y - r.height() / 2, r.width(), r.height()); } #include "moc_pager.cpp"