2
* Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU Library General Public License version 2 as
6
* published by the Free Software Foundation
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details
13
* You should have received a copy of the GNU Library General Public
14
* License along with this program; if not, write to the
15
* Free Software Foundation, Inc.,
16
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
#include "krunnerdialog.h"
22
#include <QSvgRenderer>
23
#include <QResizeEvent>
24
#include <QMouseEvent>
32
#include <KWindowSystem>
33
#include <KPluginInfo>
35
#include <NETRootInfo>
38
#include <QtCore/QStringBuilder> // % operator for QString
40
#include "kworkspace/kdisplaymanager.h"
42
#include <Plasma/AbstractRunner>
43
#include <Plasma/FrameSvg>
44
#include <Plasma/RunnerManager>
45
#include <Plasma/Theme>
46
#include <Plasma/WindowEffects>
48
#include "configdialog.h"
49
#include "krunnerapp.h"
50
#include "krunnersettings.h"
56
KRunnerDialog::KRunnerDialog(Plasma::RunnerManager *runnerManager, QWidget *parent, Qt::WindowFlags f)
58
m_runnerManager(runnerManager),
61
m_floating(!KRunnerSettings::freeFloating()),
67
setAttribute(Qt::WA_TranslucentBackground);
68
setMouseTracking(true);
70
setWindowTitle(i18n("Run Command"));
71
setWindowIcon(KIcon(QLatin1String("system-run")));
73
QPalette pal = palette();
74
pal.setColor(backgroundRole(), Qt::transparent);
77
m_iconSvg = new Plasma::Svg(this);
78
m_iconSvg->setImagePath(QLatin1String("widgets/configuration-icons"));
80
m_background = new Plasma::FrameSvg(this);
81
connect(m_background, SIGNAL(repaintNeeded()), this, SLOT(themeUpdated()));
83
connect(Kephal::Screens::self(), SIGNAL(screenRemoved(int)),
84
this, SLOT(screenRemoved(int)));
85
connect(Kephal::Screens::self(), SIGNAL(screenResized(Kephal::Screen*,QSize,QSize)),
86
this, SLOT(screenChanged(Kephal::Screen*)));
87
connect(Kephal::Screens::self(), SIGNAL(screenMoved(Kephal::Screen*,QPoint,QPoint)),
88
this, SLOT(screenChanged(Kephal::Screen*)));
89
connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(resetScreenPos()));
91
setFreeFloating(KRunnerSettings::freeFloating());
94
KRunnerDialog::~KRunnerDialog()
96
//kDebug( )<< "!!!!!!!!!! deleting" << m_floating << m_screenPos.count();
98
KConfigGroup cg(KGlobal::config(), "EdgePositions");
99
QHashIterator<int, QPoint> it(m_screenPos);
100
while (it.hasNext()) {
102
//kDebug() << "saving" << "Screen" + QString::number(it.key()) << it.value();
103
cg.writeEntry(QLatin1String( "Screen" ) % QString::number(it.key()), it.value());
108
void KRunnerDialog::screenRemoved(int screen)
110
m_screenPos.remove(screen);
113
void KRunnerDialog::screenChanged(Kephal::Screen* screen)
115
m_screenPos.remove(screen->id());
116
if (m_oldScreen == screen->id()) {
121
void KRunnerDialog::resetScreenPos()
124
QMutableHashIterator<int, QPoint> it(m_screenPos);
125
QRect r = KWindowSystem::workArea();
126
while (it.hasNext()) {
127
QPoint &p = it.next().value();
129
if (r.left() > p.x()) {
131
} else if (r.right() < p.x() + width() - 1) {
132
p.setX(r.right() - width());
145
void KRunnerDialog::positionOnScreen()
147
int screen = Kephal::ScreenUtils::primaryScreenId();
148
if (Kephal::ScreenUtils::numScreens() > 1) {
150
screen = Kephal::ScreenUtils::screenId(geometry().center());
152
screen = Kephal::ScreenUtils::screenId(QCursor::pos());
156
QRect r = Kephal::ScreenUtils::screenGeometry(screen);
157
if (m_oldScreen != screen) {
158
//kDebug() << "old screen be the new screen" << m_oldScreen << screen;
159
if (m_oldScreen != -1) {
160
QRect oldRect = Kephal::ScreenUtils::screenGeometry(m_oldScreen);
161
// Store the position relative to the screen topLeft corner.
162
// Since the geometry of screens might change between sessions
163
// storing the absolute position might lead to issues such as bug #243898
164
m_screenPos.insert(m_oldScreen, pos() - oldRect.topLeft());
167
m_oldScreen = screen;
168
if (m_screenPos.contains(screen)) {
169
//kDebug() << "moving to" << m_screenPos[screen];
171
// Checks that the stored position is still a valid position on screen
172
// if not, remove the stored position so that it is reset later
173
if (r.contains(m_screenPos[screen] + r.topLeft()) &&
174
r.contains(m_screenPos[screen] + r.topLeft() + QPoint(width()-1, 0))) {
175
move(m_screenPos[screen] + r.topLeft());
177
m_screenPos.remove(screen);
181
if (!m_screenPos.contains(screen)) {
182
const int w = width();
183
const int dx = r.left() + (r.width() / 2) - (w / 2);
186
dy += r.height() / 3;
200
KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
201
//Turn the sliding effect off
202
Plasma::WindowEffects::slideWindow(this, Plasma::Floating);
204
KWindowSystem::setOnAllDesktops(winId(), true);
205
Plasma::WindowEffects::slideWindow(this, Plasma::TopEdge);
208
KWindowSystem::forceActiveWindow(winId());
209
//kDebug() << "moving to" << m_screenPos[screen];
212
void KRunnerDialog::moveEvent(QMoveEvent *)
214
m_screenPos.insert(m_oldScreen, pos() - Kephal::ScreenUtils::screenGeometry(m_oldScreen).topLeft());
217
void KRunnerDialog::setFreeFloating(bool floating)
219
if (m_floating == floating) {
223
m_floating = floating;
229
m_background->setImagePath(QLatin1String("dialogs/krunner"));
230
m_background->setElementPrefix(QString());
231
m_background->setEnabledBorders(Plasma::FrameSvg::AllBorders);
232
KWindowSystem::setType(winId(), NET::Normal);
233
// recalc the contents margins
236
m_background->setImagePath(QLatin1String("widgets/panel-background"));
237
m_background->resizeFrame(size());
238
m_background->setElementPrefix("north-mini");
239
// load the positions for each screen from our config
240
const int numScreens = Kephal::ScreenUtils::numScreens();
241
KConfigGroup cg(KGlobal::config(), "EdgePositions");
242
for (int i = 0; i < numScreens; ++i) {
243
QPoint p = cg.readEntry(QLatin1String( "Screen" ) % QString::number(i), QPoint());
245
QRect r = Kephal::ScreenUtils::screenGeometry(i);
246
m_screenPos.insert(i, QPoint(p.x(), r.top()));
249
QRect r = Kephal::ScreenUtils::screenGeometry(qMax(m_oldScreen, 0));
251
KWindowSystem::setType(winId(), NET::Dock);
259
bool KRunnerDialog::freeFloating() const
264
bool KRunnerDialog::isManualResizing() const
269
void KRunnerDialog::setStaticQueryMode(bool staticQuery)
271
Q_UNUSED(staticQuery)
274
void KRunnerDialog::toggleConfigDialog()
276
if (m_configWidget) {
277
delete m_configWidget;
281
KWindowSystem::setType(winId(), NET::Dock);
284
m_configWidget = new KRunnerConfigWidget(m_runnerManager, this);
285
connect(m_configWidget, SIGNAL(finished()), this, SLOT(configCompleted()));
286
setConfigWidget(m_configWidget);
287
KWindowSystem::setType(winId(), NET::Normal);
291
void KRunnerDialog::configCompleted()
293
if (m_configWidget) {
294
m_configWidget->deleteLater();
299
KWindowSystem::setType(winId(), NET::Dock);
303
void KRunnerDialog::themeUpdated()
305
m_leftBorderWidth = qMax(0, int(m_background->marginSize(Plasma::LeftMargin)));
306
m_rightBorderWidth = qMax(0, int(m_background->marginSize(Plasma::RightMargin)));
307
m_bottomBorderHeight = qMax(0, int(m_background->marginSize(Plasma::BottomMargin)));
308
// the -2 in the non-floating case is not optimal, but it gives it a bit of a "more snug to the
309
// top" feel; best would be if we could tell exactly where the edge/shadow of the frame svg was
310
// but this works nicely
311
const int topHeight = m_floating ? qMax(0, int(m_background->marginSize(Plasma::TopMargin)))
312
: Plasma::Theme::defaultTheme()->windowTranslucencyEnabled() ?
313
qMax(1, m_bottomBorderHeight / 2)
314
: qMax(1, m_bottomBorderHeight - 2);
316
//kDebug() << m_leftBorderWidth<< topHeight<< m_rightBorderWidth<< m_bottomBorderHeight;
317
// the +1 gives us the extra mouseMoveEvent needed to always reset the resize cursor
318
setContentsMargins(m_leftBorderWidth + 1, topHeight, m_rightBorderWidth + 1, m_bottomBorderHeight + 1);
323
void KRunnerDialog::paintEvent(QPaintEvent *e)
326
p.setCompositionMode(QPainter::CompositionMode_Source);
327
p.setClipRect(e->rect());
328
//kDebug() << "clip rect set to: " << e->rect();
330
m_background->paintFrame(&p);
333
bool KRunnerDialog::event(QEvent *event)
335
if (event->type() == QEvent::Paint) {
337
p.setCompositionMode(QPainter::CompositionMode_Source);
338
p.fillRect(rect(), Qt::transparent);
341
return QWidget::event(event);
344
void KRunnerDialog::showEvent(QShowEvent *)
346
unsigned long state = NET::SkipTaskbar | NET::KeepAbove | NET::StaysOnTop;
348
KWindowSystem::clearState(winId(), state);
350
KWindowSystem::setState(winId(), state);
352
m_runnerManager->setupMatchSession();
355
void KRunnerDialog::hideEvent(QHideEvent *)
357
// We delay the call to matchSessionComplete until next event cycle
358
// This is necessary since we might hide the dialog right before running
359
// a match, and the runner might still need to be prepped to
360
// succesfully run a match
361
QTimer::singleShot(0, m_runnerManager, SLOT(matchSessionComplete()));
362
delete m_configWidget;
366
void KRunnerDialog::updateMask()
368
// Enable the mask only when compositing is disabled;
369
// As this operation is quite slow, it would be nice to find some
370
// way to workaround it for no-compositing users.
372
if (KWindowSystem::compositingActive()) {
373
const QRegion mask = m_background->mask();
374
Plasma::WindowEffects::enableBlurBehind(winId(), true, mask);
375
Plasma::WindowEffects::overrideShadow(winId(), true);
377
setMask(m_background->mask());
381
void KRunnerDialog::resizeEvent(QResizeEvent *e)
383
m_background->resizeFrame(e->size());
385
if (m_resizing && !m_vertResize) {
386
QRect r = Kephal::ScreenUtils::screenGeometry(m_oldScreen);
387
//kDebug() << "if" << x() << ">" << r.left() << "&&" << r.right() << ">" << (x() + width());
388
const Plasma::FrameSvg::EnabledBorders borders = m_background->enabledBorders();
389
if (borders & Plasma::FrameSvg::LeftBorder) {
390
const int dx = x() + (e->oldSize().width() - width()) / 2 ;
391
const int dy = (m_floating ? pos().y() : r.top());
392
move(qBound(r.left(), dx, r.right() - width() + 1), dy);
394
m_screenPos.insert(m_oldScreen, pos() - Kephal::ScreenUtils::screenGeometry(m_oldScreen).topLeft());
396
if (m_floating || !checkBorders(r)) {
407
void KRunnerDialog::mousePressEvent(QMouseEvent *e)
409
if (e->button() == Qt::LeftButton) {
410
m_lastPressPos = e->globalPos();
412
const bool leftResize = e->x() < qMax(5, m_leftBorderWidth);
413
m_rightResize = e->x() > width() - qMax(5, m_rightBorderWidth);
414
m_vertResize = e->y() > height() - qMax(5, m_bottomBorderHeight);
415
kWarning() << "right:" << m_rightResize << "left:" << leftResize << "vert:" << m_vertResize;
416
if (m_rightResize || m_vertResize || leftResize) {
417
// let's do a resize! :)
420
} else if (m_floating) {
422
m_lastPressPos = QPoint();
423
// We have to release the mouse grab before initiating the move operation.
424
// Ideally we would call releaseMouse() to do this, but when we only have an
425
// implicit passive grab, Qt is unaware of it, and will refuse to release it.
426
XUngrabPointer(x11Info().display(), CurrentTime);
428
// Ask the window manager to start an interactive move operation.
429
NETRootInfo rootInfo(x11Info().display(), NET::WMMoveResize);
430
rootInfo.moveResizeRequest(winId(), e->globalX(), e->globalY(), NET::Move);
437
void KRunnerDialog::mouseReleaseEvent(QMouseEvent *)
439
if (!m_lastPressPos.isNull()) {
442
m_lastPressPos = QPoint();
447
bool KRunnerDialog::checkBorders(const QRect &screenGeom)
449
Q_ASSERT(!m_floating);
450
Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::BottomBorder;
452
if (x() > screenGeom.left()) {
453
borders |= Plasma::FrameSvg::LeftBorder;
456
if (x() + width() < screenGeom.right()) {
457
borders |= Plasma::FrameSvg::RightBorder;
460
if (borders != m_background->enabledBorders()) {
461
m_background->setEnabledBorders(borders);
471
void KRunnerDialog::leaveEvent(QEvent *)
476
void KRunnerDialog::mouseMoveEvent(QMouseEvent *e)
478
//kDebug() << e->x() << m_leftBorderWidth << width() << m_rightBorderWidth;
479
if (m_lastPressPos.isNull()) {
480
checkCursor(e->pos());
484
const int deltaY = e->globalY() - m_lastPressPos.y();
485
resize(width(), qMax(80, height() + deltaY));
486
m_lastPressPos = e->globalPos();
488
QRect r = Kephal::ScreenUtils::screenGeometry(m_oldScreen);
489
const int deltaX = (m_rightResize ? -1 : 1) * (m_lastPressPos.x() - e->globalX());
490
int newWidth = width() + deltaX;
491
// don't let it grow beyond the opposite screen edge
493
if (m_leftBorderWidth > 0) {
494
newWidth += qMin(deltaX, x() - r.left());
496
} else if (m_rightBorderWidth > 0) {
497
newWidth += qMin(deltaX, r.right() - (x() + width() - 1));
498
} else if (newWidth > minimumWidth() && newWidth < width()) {
499
move(r.right() - newWidth + 1, y());
502
if (newWidth > minimumWidth()) {;
503
resize(newWidth, height());
504
m_lastPressPos = e->globalPos();
508
QRect r = Kephal::ScreenUtils::screenGeometry(m_oldScreen);
509
int newX = qBound(r.left(), x() - (m_lastPressPos.x() - e->globalX()), r.right() - width() + 1);
510
if (abs(r.center().x() - (newX + (width() / 2))) < 20) {
511
newX = r.center().x() - (width() / 2);
513
m_lastPressPos = e->globalPos();
522
void KRunnerDialog::timerEvent(QTimerEvent *event)
524
killTimer(event->timerId());
525
if (checkCursor(mapFromGlobal(QCursor::pos()))) {
526
m_runningTimer = true;
529
m_runningTimer = false;
533
bool KRunnerDialog::checkCursor(const QPoint &pos)
535
//Plasma::FrameSvg borders = m_background->enabledBoders();
536
if ((m_leftBorderWidth > 0 && pos.x() < qMax(5, m_leftBorderWidth)) ||
537
(m_rightBorderWidth > 0 && pos.x() > width() - qMax(5, m_rightBorderWidth))) {
538
if (cursor().shape() != Qt::SizeHorCursor) {
539
setCursor(Qt::SizeHorCursor);
540
if (!m_runningTimer) {
541
m_runningTimer = true;
548
} else if ((pos.y() > height() - qMax(5, m_bottomBorderHeight)) && (pos.y() < height())) {
549
if (cursor().shape() != Qt::SizeVerCursor) {
550
setCursor(Qt::SizeVerCursor);
551
if (!m_runningTimer) {
552
m_runningTimer = true;
565
#include "krunnerdialog.moc"