/* * Copyright (C) 2006 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License version 2 as * published by the Free Software Foundation * * 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 Library 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 "krunnerdialog.h" #include #include #include #include #include #ifdef Q_WS_X11 #include #endif #include #include #include #ifdef Q_WS_X11 #include #endif #include "kworkspace/kdisplaymanager.h" #include #include #include #include #include #include "configdialog.h" #include "krunnerapp.h" #include "krunnersettings.h" #include "panelshadows.h" #ifdef Q_WS_X11 #include #endif KRunnerDialog::KRunnerDialog(Plasma::RunnerManager *runnerManager, QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), m_runnerManager(runnerManager), m_configWidget(0), m_background(new Plasma::FrameSvg(this)), m_shownOnScreen(-1), m_offset(.5), m_floating(!KRunnerSettings::freeFloating()), m_resizing(false), m_rightResize(false), m_vertResize(false), m_runningTimer(false), m_desktopWidget(qApp->desktop()) { setAttribute(Qt::WA_TranslucentBackground); setMouseTracking(true); //setButtons(0); setWindowTitle(i18nc("@title:window", "Run Command")); setWindowIcon(KIcon(QLatin1String("system-run"))); QPalette pal = palette(); pal.setColor(backgroundRole(), Qt::transparent); setPalette(pal); m_iconSvg = new Plasma::Svg(this); m_iconSvg->setImagePath(QLatin1String("widgets/configuration-icons")); connect(m_background, SIGNAL(repaintNeeded()), this, SLOT(themeUpdated())); connect(m_desktopWidget, SIGNAL(resized(int)), this, SLOT(screenGeometryChanged(int))); connect(m_desktopWidget, SIGNAL(screenCountChanged(int)), this, SLOT(screenGeometryChanged(int))); connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SLOT(resetScreenPos())); connect(KWindowSystem::self(), SIGNAL(compositingChanged(bool)), this, SLOT(compositingChanged(bool))); setFreeFloating(KRunnerSettings::freeFloating()); } KRunnerDialog::~KRunnerDialog() { //kDebug( )<< "!!!!!!!!!! deleting" << m_floating << m_screenPos.count(); if (!m_floating) { KConfigGroup cg(KGlobal::config(), "EdgePositions"); cg.writeEntry(QLatin1String("Offset"), m_offset); } } void KRunnerDialog::screenResized(int screen) { if (isVisible() && screen == m_shownOnScreen) { positionOnScreen(); } } void KRunnerDialog::screenGeometryChanged(int screenCount) { if (isVisible()) { positionOnScreen(); } } void KRunnerDialog::resetScreenPos() { if (isVisible() && !m_floating) { positionOnScreen(); } } void KRunnerDialog::positionOnScreen() { if (m_desktopWidget->screenCount() < 2) { m_shownOnScreen = m_desktopWidget->primaryScreen(); } else if (isVisible()) { m_shownOnScreen = m_desktopWidget->screenNumber(geometry().center()); } else { m_shownOnScreen = m_desktopWidget->screenNumber(QCursor::pos()); } const QRect r = m_desktopWidget->screenGeometry(m_shownOnScreen); if (m_floating && !m_customPos.isNull()) { int x = qBound(r.left(), m_customPos.x(), r.right() - width()); int y = qBound(r.top(), m_customPos.y(), r.bottom() - height()); move(x, y); show(); return; } const int w = width(); int x = r.left() + (r.width() * m_offset) - (w / 2); int y = r.top(); if (m_floating) { y += r.height() / 3; } x = qBound(r.left(), x, r.right() - width()); y = qBound(r.top(), y, r.bottom() - height()); move(x, y); if (!m_floating) { checkBorders(r); } show(); if (m_floating) { KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop()); //Turn the sliding effect off Plasma::WindowEffects::slideWindow(this, Plasma::Floating); } else { KWindowSystem::setOnAllDesktops(winId(), true); Plasma::WindowEffects::slideWindow(this, Plasma::TopEdge); } KWindowSystem::forceActiveWindow(winId()); //kDebug() << "moving to" << m_screenPos[screen]; } void KRunnerDialog::moveEvent(QMoveEvent *) { if (m_floating) { m_customPos = pos(); } else { const QRect screen = m_desktopWidget->screenGeometry(m_shownOnScreen); m_offset = qRound((geometry().center().x() - screen.x()) / qreal(screen.width()) * 100) / 100.0; } } void KRunnerDialog::setFreeFloating(bool floating) { if (m_floating == floating) { return; } m_floating = floating; m_shownOnScreen = -1; unsetCursor(); updatePresentation(); } void KRunnerDialog::updatePresentation() { if (m_floating) { KWindowSystem::setType(winId(), NET::Normal); m_background->setImagePath(QLatin1String("dialogs/krunner")); m_background->setElementPrefix(QString()); themeUpdated(); } else { if (KWindowSystem::compositingActive()) { m_background->setImagePath(QLatin1String("widgets/translucentbackground")); } if (!m_background->isValid()) { m_background->setImagePath(QLatin1String("widgets/panel-background")); } m_background->resizeFrame(size()); m_background->setElementPrefix("north-mini"); // load the positions for each screen from our config KConfigGroup cg(KGlobal::config(), "EdgePositions"); m_offset = cg.readEntry(QLatin1String("Offset"), m_offset); const QRect r = m_desktopWidget->screenGeometry(m_shownOnScreen); checkBorders(r); KWindowSystem::setType(winId(), NET::Dock); } if (isVisible()) { positionOnScreen(); } } void KRunnerDialog::compositingChanged(bool) { updatePresentation(); updateMask(); adjustSize(); } bool KRunnerDialog::freeFloating() const { return m_floating; } KRunnerDialog::ResizeMode KRunnerDialog::manualResizing() const { if (!m_resizing) { return NotResizing; } else if (m_vertResize) { return VerticalResizing; } else { return HorizontalResizing; } } void KRunnerDialog::setStaticQueryMode(bool staticQuery) { Q_UNUSED(staticQuery) } void KRunnerDialog::toggleConfigDialog() { if (m_configWidget) { delete m_configWidget; m_configWidget = 0; if (!m_floating) { KWindowSystem::setType(winId(), NET::Dock); } } else { m_configWidget = new KRunnerConfigWidget(m_runnerManager, this); connect(m_configWidget, SIGNAL(finished()), this, SLOT(configCompleted())); setConfigWidget(m_configWidget); KWindowSystem::setType(winId(), NET::Normal); } } void KRunnerDialog::configCompleted() { if (m_configWidget) { m_configWidget->deleteLater(); m_configWidget = 0; } if (!m_floating) { KWindowSystem::setType(winId(), NET::Dock); } } void KRunnerDialog::themeUpdated() { if (m_floating) { // recalc the contents margins m_background->blockSignals(true); if (KWindowSystem::compositingActive()) { m_background->setEnabledBorders(Plasma::FrameSvg::NoBorder); } else { m_background->setEnabledBorders(Plasma::FrameSvg::AllBorders); } m_background->blockSignals(false); } m_leftBorderWidth = qMax(0, int(m_background->marginSize(Plasma::LeftMargin))); m_rightBorderWidth = qMax(0, int(m_background->marginSize(Plasma::RightMargin))); m_bottomBorderHeight = qMax(0, int(m_background->marginSize(Plasma::BottomMargin))); // the -1 in the non-floating case is not optimal, but it gives it a bit of a "more snug to the // top" feel; best would be if we could tell exactly where the edge/shadow of the frame svg was // but this works nicely m_topBorderHeight = m_floating ? qMax(0, int(m_background->marginSize(Plasma::TopMargin))) : Plasma::Theme::defaultTheme()->windowTranslucencyEnabled() ? qMax(1, m_bottomBorderHeight / 2) : qMax(1, m_bottomBorderHeight - 1); kDebug() << m_leftBorderWidth << m_topBorderHeight << m_rightBorderWidth << m_bottomBorderHeight; // the +1 gives us the extra mouseMoveEvent needed to always reset the resize cursor setContentsMargins(m_leftBorderWidth + 1, m_topBorderHeight, m_rightBorderWidth + 1, m_bottomBorderHeight + 1); update(); } void KRunnerDialog::paintEvent(QPaintEvent *e) { QPainter p(this); p.setCompositionMode(QPainter::CompositionMode_Source); p.setClipRect(e->rect()); //kDebug() << "clip rect set to: " << e->rect(); m_background->paintFrame(&p); } void KRunnerDialog::showEvent(QShowEvent *) { unsigned long state = NET::SkipTaskbar | NET::KeepAbove; if (m_floating) { KWindowSystem::clearState(winId(), state); } else { KWindowSystem::setState(winId(), state); } m_runnerManager->setupMatchSession(); } void KRunnerDialog::hideEvent(QHideEvent *) { // We delay the call to matchSessionComplete until next event cycle // This is necessary since we might hide the dialog right before running // a match, and the runner might still need to be prepped to // succesfully run a match QTimer::singleShot(0, m_runnerManager, SLOT(matchSessionComplete())); delete m_configWidget; m_configWidget = 0; } void KRunnerDialog::updateMask() { // Enable the mask only when compositing is disabled; // As this operation is quite slow, it would be nice to find some // way to workaround it for no-compositing users. if (KWindowSystem::compositingActive()) { clearMask(); const QRegion mask = m_background->mask(); } else { setMask(m_background->mask()); } } void KRunnerDialog::resizeEvent(QResizeEvent *e) { m_background->resizeFrame(e->size()); bool maskDirty = true; if (m_resizing && !m_vertResize) { const QRect r = m_desktopWidget->screenGeometry(m_shownOnScreen); //kDebug() << "if" << x() << ">" << r.left() << "&&" << r.right() << ">" << (x() + width()); const Plasma::FrameSvg::EnabledBorders borders = m_background->enabledBorders(); if (borders & Plasma::FrameSvg::LeftBorder) { const int dx = x() + (e->oldSize().width() - width()) / 2 ; const int dy = (m_floating ? pos().y() : r.top()); move(qBound(r.left(), dx, r.right() - width() + 1), dy); maskDirty = m_floating || !checkBorders(r); } } if (maskDirty) { updateMask(); } } void KRunnerDialog::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { m_lastPressPos = e->globalPos(); const bool leftResize = e->x() < qMax(5, m_leftBorderWidth); m_rightResize = e->x() > width() - qMax(5, m_rightBorderWidth); m_vertResize = e->y() > height() - qMax(5, m_bottomBorderHeight); kWarning() << "right:" << m_rightResize << "left:" << leftResize << "vert:" << m_vertResize; if (m_rightResize || m_vertResize || leftResize) { // let's do a resize! :) grabMouse(); m_resizing = true; } else if (m_floating) { #ifdef Q_WS_X11 m_lastPressPos = QPoint(); // We have to release the mouse grab before initiating the move operation. // Ideally we would call releaseMouse() to do this, but when we only have an // implicit passive grab, Qt is unaware of it, and will refuse to release it. XUngrabPointer(x11Info().display(), CurrentTime); // Ask the window manager to start an interactive move operation. NETRootInfo rootInfo(x11Info().display(), NET::WMMoveResize); rootInfo.moveResizeRequest(winId(), e->globalX(), e->globalY(), NET::Move); #endif } e->accept(); } } void KRunnerDialog::mouseReleaseEvent(QMouseEvent *) { if (!m_lastPressPos.isNull()) { releaseMouse(); unsetCursor(); m_lastPressPos = QPoint(); m_resizing = false; } } bool KRunnerDialog::checkBorders(const QRect &screenGeom) { Q_ASSERT(!m_floating); Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::BottomBorder; if (x() > screenGeom.left()) { borders |= Plasma::FrameSvg::LeftBorder; } if (x() + width() < screenGeom.right()) { borders |= Plasma::FrameSvg::RightBorder; } if (borders != m_background->enabledBorders()) { m_background->setEnabledBorders(borders); themeUpdated(); updateMask(); update(); return true; } return false; } void KRunnerDialog::leaveEvent(QEvent *) { unsetCursor(); } void KRunnerDialog::mouseMoveEvent(QMouseEvent *e) { //kDebug() << e->x() << m_leftBorderWidth << width() << m_rightBorderWidth; if (m_lastPressPos.isNull()) { // not press positiong, so we aren't going to resize or move. checkCursor(e->pos()); } else if (m_resizing) { // resizing if (m_vertResize) { const int deltaY = e->globalY() - m_lastPressPos.y(); resize(width(), qMax(80, height() + deltaY)); m_lastPressPos = e->globalPos(); } else { const QRect r = m_desktopWidget->availableGeometry(m_shownOnScreen); const int deltaX = (m_rightResize ? -1 : 1) * (m_lastPressPos.x() - e->globalX()); int newWidth = width() + deltaX; // don't let it grow beyond the opposite screen edge if (m_rightResize) { if (m_leftBorderWidth > 0) { newWidth += qMin(deltaX, x() - r.left()); } } else if (m_rightBorderWidth > 0) { newWidth += qMin(deltaX, r.right() - (x() + width() - 1)); } else if (newWidth > minimumWidth() && newWidth < width()) { move(r.right() - newWidth + 1, y()); } if (newWidth > minimumWidth()) { resize(newWidth, height()); m_lastPressPos = e->globalPos(); } } } else { // moving const QRect r = m_desktopWidget->availableGeometry(m_shownOnScreen); int newX = qBound(r.left(), x() - (m_lastPressPos.x() - e->globalX()), r.right() - width() + 1); if (abs(r.center().x() - (newX + (width() / 2))) < 20) { newX = r.center().x() - (width() / 2); } else { m_lastPressPos = e->globalPos(); } move(newX, y()); checkBorders(r); } } void KRunnerDialog::timerEvent(QTimerEvent *event) { killTimer(event->timerId()); if (checkCursor(mapFromGlobal(QCursor::pos()))) { m_runningTimer = true; startTimer(100); } else { m_runningTimer = false; } } bool KRunnerDialog::checkCursor(const QPoint &pos) { //Plasma::FrameSvg borders = m_background->enabledBoders(); if ((m_leftBorderWidth > 0 && pos.x() < qMax(5, m_leftBorderWidth)) || (m_rightBorderWidth > 0 && pos.x() > width() - qMax(5, m_rightBorderWidth))) { if (cursor().shape() != Qt::SizeHorCursor) { setCursor(Qt::SizeHorCursor); if (!m_runningTimer) { m_runningTimer = true; startTimer(100); } return false; } return true; } else if ((pos.y() > height() - qMax(5, m_bottomBorderHeight)) && (pos.y() < height())) { if (cursor().shape() != Qt::SizeVerCursor) { setCursor(Qt::SizeVerCursor); if (!m_runningTimer) { m_runningTimer = true; startTimer(100); } return false; } return true; } unsetCursor(); return false; } #include "moc_krunnerdialog.cpp"