mirror of
https://invent.kde.org/plasma/plasma-desktop.git
synced 2026-11-03 14:49:44 -09:00
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the techbase wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://community.kde.org/Frameworks/GitOldHistory If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo kde-workspace, frameworks branch, at commit 049113e719dd2fc4446d054fa1a3aada330094f0
601 lines
19 KiB
C++
601 lines
19 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2007 by Daniel Laidig <d.laidig@gmx.de> *
|
|
* Copyright (C) 2012 by Luís Gabriel Lima <lampih@gmail.com> *
|
|
* *
|
|
* 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 <math.h>
|
|
|
|
#include <QAction>
|
|
#include <QApplication>
|
|
#include <QFont>
|
|
#include <QTimer>
|
|
#if HAVE_X11
|
|
#include <QX11Info>
|
|
#endif
|
|
#include <QDBusInterface>
|
|
#include <QTextDocument>
|
|
#include <QDesktopWidget>
|
|
|
|
#include <KColorScheme>
|
|
#include <KGlobalSettings>
|
|
#include <KIconLoader>
|
|
#include <KLocalizedString>
|
|
#include <KWindowSystem>
|
|
#if HAVE_X11
|
|
#include <netwm.h>
|
|
#endif
|
|
|
|
#include <Plasma/FrameSvg>
|
|
#include <Plasma/Package>
|
|
|
|
#include <kactivities/consumer.h>
|
|
|
|
#include <task.h>
|
|
|
|
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_rows(2),
|
|
m_columns(0),
|
|
m_currentDesktop(0),
|
|
m_orientation(Qt::Horizontal),
|
|
m_addDesktopAction(0),
|
|
m_removeDesktopAction(0),
|
|
m_showWindowIcons(false),
|
|
m_desktopDown(false),
|
|
m_validSizes(false),
|
|
m_desktopWidget(QApplication::desktop())
|
|
#if HAVE_X11
|
|
, m_isX11(QX11Info::isPlatformX11())
|
|
#endif
|
|
{
|
|
// initialize with a decent default
|
|
m_desktopCount = qMax(1, KWindowSystem::numberOfDesktops());
|
|
|
|
m_pagerModel = new PagerModel(this);
|
|
|
|
createMenu();
|
|
|
|
m_timer = new QTimer(this);
|
|
m_timer->setSingleShot(true);
|
|
connect(m_timer, SIGNAL(timeout()), this, SLOT(recalculateWindowRects()));
|
|
|
|
connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(currentDesktopChanged(int)));
|
|
connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)), this, SLOT(startTimerFast()));
|
|
connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(startTimerFast()));
|
|
connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(startTimerFast()));
|
|
connect(KWindowSystem::self(), SIGNAL(numberOfDesktopsChanged(int)), this, SLOT(numberOfDesktopsChanged(int)));
|
|
connect(KWindowSystem::self(), SIGNAL(desktopNamesChanged()), this, SLOT(desktopNamesChanged()));
|
|
connect(KWindowSystem::self(), SIGNAL(stackingOrderChanged()), this, SLOT(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, SIGNAL(screenCountChanged(int)), SLOT(desktopsSizeChanged()));
|
|
connect(m_desktopWidget, SIGNAL(resized(int)), SLOT(desktopsSizeChanged()));
|
|
|
|
// connect to KWin's reloadConfig signal to get updates on the desktop layout
|
|
QDBusConnection dbus = QDBusConnection::sessionBus();
|
|
dbus.connect(QString(), "/KWin", "org.kde.KWin", "reloadConfig",
|
|
this, SLOT(configChanged()));
|
|
|
|
recalculateGridSizes(m_rows);
|
|
|
|
setCurrentDesktop(KWindowSystem::currentDesktop());
|
|
|
|
KActivities::Consumer *act = new KActivities::Consumer(this);
|
|
connect(act, SIGNAL(currentActivityChanged(QString)), this, SLOT(currentActivityChanged(QString)));
|
|
m_currentActivity = act->currentActivity();
|
|
}
|
|
|
|
Pager::~Pager()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
void Pager::createMenu()
|
|
{
|
|
#if HAVE_X11
|
|
m_addDesktopAction = new QAction(SmallIcon("list-add"),i18n("&Add Virtual Desktop"), this);
|
|
m_actions.append(m_addDesktopAction);
|
|
connect(m_addDesktopAction, SIGNAL(triggered(bool)), this , SLOT(slotAddDesktop()));
|
|
m_removeDesktopAction = new QAction(SmallIcon("list-remove"),i18n("&Remove Last Virtual Desktop"), this);
|
|
m_actions.append(m_removeDesktopAction);
|
|
connect(m_removeDesktopAction, SIGNAL(triggered(bool)), this , SLOT(slotRemoveDesktop()));
|
|
|
|
if (m_desktopCount <= 1) {
|
|
m_removeDesktopAction->setEnabled(false);
|
|
} else if (m_desktopCount >= MAXDESKTOPS) {
|
|
m_addDesktopAction->setEnabled(false);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
QList<QAction*> Pager::contextualActions()
|
|
{
|
|
return m_actions;
|
|
}
|
|
|
|
#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_validSizes) {
|
|
updateSizes();
|
|
}
|
|
|
|
QList<WId> windows = KWindowSystem::stackingOrder();
|
|
m_pagerModel->clearWindowRects();
|
|
|
|
#if HAVE_X11
|
|
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()) {
|
|
continue;
|
|
}
|
|
|
|
//check activity
|
|
NETWinInfo netInfo(QX11Info::connection(), window, QX11Info::appRootWindow(), 0, NET::WM2Activities);
|
|
QString result(netInfo.activities());
|
|
if (!result.isEmpty() && result != "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());
|
|
}
|
|
}
|
|
#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 (num < 1) {
|
|
return; // refuse to update to zero desktops
|
|
}
|
|
|
|
#if HAVE_X11
|
|
m_removeDesktopAction->setEnabled(num > 1);
|
|
m_addDesktopAction->setEnabled(num < MAXDESKTOPS);
|
|
#endif
|
|
|
|
m_desktopCount = num;
|
|
|
|
m_pagerModel->clearDesktopRects();
|
|
recalculateGridSizes(m_rows);
|
|
recalculateWindowRects();
|
|
}
|
|
|
|
void Pager::desktopNamesChanged()
|
|
{
|
|
m_pagerModel->clearDesktopRects();
|
|
m_validSizes = false;
|
|
startTimer();
|
|
}
|
|
|
|
void Pager::windowChanged(WId id, const unsigned long* dirty)
|
|
{
|
|
Q_UNUSED(id)
|
|
|
|
if (dirty[NETWinInfo::PROTOCOLS] & (NET::WMGeometry | NET::WMDesktop) ||
|
|
dirty[NETWinInfo::PROTOCOLS2] & NET::WM2Activities) {
|
|
startTimer();
|
|
}
|
|
}
|
|
|
|
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 or the dashboard
|
|
if (m_currentDesktopSelected == ShowDesktop) {
|
|
NETRootInfo info(QX11Info::connection(), 0);
|
|
m_desktopDown = !m_desktopDown;
|
|
info.setShowingDesktop(m_desktopDown);
|
|
} else if (m_currentDesktopSelected == ShowDashboard) {
|
|
QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App");
|
|
plasmaApp.call("toggleDashboard");
|
|
}
|
|
} else {
|
|
KWindowSystem::setCurrentDesktop(newDesktop + 1);
|
|
setCurrentDesktop(newDesktop + 1);
|
|
}
|
|
#else
|
|
Q_UNUSED(newDesktop)
|
|
#endif
|
|
}
|
|
|
|
// 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"
|