bbf13c1fe5
Regression introduced with 2ae475cedefc7521a03602bf163864f08caad23e BUG: 301079 FIXED-IN: 4.9.0 REVIEW: 105138
1806 lines
56 KiB
C++
Executable file
1806 lines
56 KiB
C++
Executable file
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
|
|
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*********************************************************************/
|
|
|
|
/*
|
|
|
|
This file contains things relevant to direct user actions, such as
|
|
responses to global keyboard shortcuts, or selecting actions
|
|
from the window operations menu.
|
|
|
|
*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// NOTE: if you change the menu, keep kde-workspace/libs/taskmanager/taskactions.cpp in sync
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "client.h"
|
|
#include "workspace.h"
|
|
#include "effects.h"
|
|
#ifdef KWIN_BUILD_TILING
|
|
#include "tiling/tile.h"
|
|
#include "tiling/tilinglayout.h"
|
|
#include "tiling/tiling.h"
|
|
#endif
|
|
|
|
#ifdef KWIN_BUILD_ACTIVITIES
|
|
#include <KActivities/Info>
|
|
#endif
|
|
|
|
#include <X11/extensions/Xrandr.h>
|
|
#ifndef KWIN_NO_XF86VM
|
|
#include <X11/extensions/xf86vmode.h>
|
|
#endif
|
|
#include <fixx11h.h>
|
|
#include <QPushButton>
|
|
#include <QSlider>
|
|
|
|
#include <kglobalsettings.h>
|
|
#include <kiconloader.h>
|
|
#include <klocale.h>
|
|
#include <kconfig.h>
|
|
#include <kglobalaccel.h>
|
|
#include <kapplication.h>
|
|
#include <QRegExp>
|
|
#include <QMenu>
|
|
#include <QVBoxLayout>
|
|
#include <kauthorized.h>
|
|
#include <kactioncollection.h>
|
|
#include <kaction.h>
|
|
|
|
#include "killwindow.h"
|
|
#ifdef KWIN_BUILD_TABBOX
|
|
#include "tabbox.h"
|
|
#endif
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
//****************************************
|
|
// Workspace
|
|
//****************************************
|
|
|
|
QMenu* Workspace::clientPopup()
|
|
{
|
|
if (!popup) {
|
|
popup = new QMenu;
|
|
popup->setFont(KGlobalSettings::menuFont());
|
|
connect(popup, SIGNAL(aboutToShow()), this, SLOT(clientPopupAboutToShow()));
|
|
connect(popup, SIGNAL(triggered(QAction*)), this, SLOT(clientPopupActivated(QAction*)));
|
|
|
|
advanced_popup = new QMenu(popup);
|
|
advanced_popup->setFont(KGlobalSettings::menuFont());
|
|
|
|
mMoveOpAction = advanced_popup->addAction(i18n("&Move"));
|
|
mMoveOpAction->setIcon(KIcon("transform-move"));
|
|
KAction *kaction = qobject_cast<KAction*>(keys->action("Window Move"));
|
|
if (kaction != 0)
|
|
mMoveOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mMoveOpAction->setData(Options::UnrestrictedMoveOp);
|
|
|
|
mResizeOpAction = advanced_popup->addAction(i18n("Re&size"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window Resize"));
|
|
if (kaction != 0)
|
|
mResizeOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mResizeOpAction->setData(Options::ResizeOp);
|
|
|
|
mKeepAboveOpAction = advanced_popup->addAction(i18n("Keep &Above Others"));
|
|
mKeepAboveOpAction->setIcon(KIcon("go-up"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window Above Other Windows"));
|
|
if (kaction != 0)
|
|
mKeepAboveOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mKeepAboveOpAction->setCheckable(true);
|
|
mKeepAboveOpAction->setData(Options::KeepAboveOp);
|
|
|
|
mKeepBelowOpAction = advanced_popup->addAction(i18n("Keep &Below Others"));
|
|
mKeepBelowOpAction->setIcon(KIcon("go-down"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window Below Other Windows"));
|
|
if (kaction != 0)
|
|
mKeepBelowOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mKeepBelowOpAction->setCheckable(true);
|
|
mKeepBelowOpAction->setData(Options::KeepBelowOp);
|
|
|
|
mFullScreenOpAction = advanced_popup->addAction(i18n("&Fullscreen"));
|
|
mFullScreenOpAction->setIcon(KIcon("view-fullscreen"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window Fullscreen"));
|
|
if (kaction != 0)
|
|
mFullScreenOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mFullScreenOpAction->setCheckable(true);
|
|
mFullScreenOpAction->setData(Options::FullScreenOp);
|
|
|
|
mShadeOpAction = advanced_popup->addAction(i18n("Sh&ade"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window Shade"));
|
|
if (kaction != 0)
|
|
mShadeOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mShadeOpAction->setCheckable(true);
|
|
mShadeOpAction->setData(Options::ShadeOp);
|
|
|
|
mNoBorderOpAction = advanced_popup->addAction(i18n("&No Border"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window No Border"));
|
|
if (kaction != 0)
|
|
mNoBorderOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mNoBorderOpAction->setCheckable(true);
|
|
mNoBorderOpAction->setData(Options::NoBorderOp);
|
|
|
|
advanced_popup->addSeparator();
|
|
|
|
QAction *action = advanced_popup->addAction(i18n("Window &Shortcut..."));
|
|
action->setIcon(KIcon("configure-shortcuts"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Setup Window Shortcut"));
|
|
if (kaction != 0)
|
|
action->setShortcut(kaction->globalShortcut().primary());
|
|
action->setData(Options::SetupWindowShortcutOp);
|
|
|
|
action = advanced_popup->addAction(i18n("&Special Window Settings..."));
|
|
action->setIcon(KIcon("preferences-system-windows-actions"));
|
|
action->setData(Options::WindowRulesOp);
|
|
|
|
action = advanced_popup->addAction(i18n("S&pecial Application Settings..."));
|
|
action->setIcon(KIcon("preferences-system-windows-actions"));
|
|
action->setData(Options::ApplicationRulesOp);
|
|
if (!KGlobal::config()->isImmutable() &&
|
|
!KAuthorized::authorizeControlModules(Workspace::configModules(true)).isEmpty()) {
|
|
advanced_popup->addSeparator();
|
|
action = advanced_popup->addAction(i18nc("Entry in context menu of window decoration to open the configuration module of KWin",
|
|
"Window &Manager Settings..."));
|
|
action->setIcon(KIcon("configure"));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(configureWM()));
|
|
}
|
|
|
|
mMinimizeOpAction = popup->addAction(i18n("Mi&nimize"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window Minimize"));
|
|
if (kaction != 0)
|
|
mMinimizeOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mMinimizeOpAction->setData(Options::MinimizeOp);
|
|
|
|
mMaximizeOpAction = popup->addAction(i18n("Ma&ximize"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window Maximize"));
|
|
if (kaction != 0)
|
|
mMaximizeOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mMaximizeOpAction->setCheckable(true);
|
|
mMaximizeOpAction->setData(Options::MaximizeOp);
|
|
|
|
popup->addSeparator();
|
|
|
|
// Actions for window tabbing
|
|
if (decorationSupportsTabbing()) {
|
|
mRemoveFromTabGroup = popup->addAction(i18n("&Untab"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Untab"));
|
|
if (kaction != 0)
|
|
mRemoveFromTabGroup->setShortcut(kaction->globalShortcut().primary());
|
|
mRemoveFromTabGroup->setData(Options::RemoveTabFromGroupOp);
|
|
|
|
mCloseTabGroup = popup->addAction(i18n("Close Entire &Group"));
|
|
mCloseTabGroup->setIcon(KIcon("window-close"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Close TabGroup"));
|
|
if (kaction != 0)
|
|
mCloseTabGroup->setShortcut(kaction->globalShortcut().primary());
|
|
mCloseTabGroup->setData(Options::CloseTabGroupOp);
|
|
|
|
popup->addSeparator();
|
|
}
|
|
|
|
// create it anyway
|
|
mTilingStateOpAction = popup->addAction(i18nc("When in tiling mode, toggle's the window's floating/tiled state", "&Float Window"));
|
|
// then hide it
|
|
mTilingStateOpAction->setVisible(false);
|
|
#ifdef KWIN_BUILD_TILING
|
|
// actions for window tiling
|
|
if (m_tiling->isEnabled()) {
|
|
kaction = qobject_cast<KAction*>(keys->action("Toggle Floating"));
|
|
mTilingStateOpAction->setCheckable(true);
|
|
mTilingStateOpAction->setData(Options::ToggleClientTiledStateOp);
|
|
if (kaction != 0)
|
|
mTilingStateOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
}
|
|
#endif
|
|
|
|
popup->addSeparator();
|
|
|
|
action = popup->addMenu(advanced_popup);
|
|
action->setText(i18n("More Actions"));
|
|
|
|
popup->addSeparator();
|
|
|
|
mCloseOpAction = popup->addAction(i18n("&Close"));
|
|
mCloseOpAction->setIcon(KIcon("window-close"));
|
|
kaction = qobject_cast<KAction*>(keys->action("Window Close"));
|
|
if (kaction != 0)
|
|
mCloseOpAction->setShortcut(kaction->globalShortcut().primary());
|
|
mCloseOpAction->setData(Options::CloseOp);
|
|
}
|
|
return popup;
|
|
}
|
|
|
|
void Workspace::discardPopup()
|
|
{
|
|
delete popup;
|
|
popup = NULL;
|
|
desk_popup = NULL;
|
|
activity_popup = NULL;
|
|
switch_to_tab_popup = NULL;
|
|
add_tabs_popup = NULL;
|
|
}
|
|
|
|
void Workspace::slotIncreaseWindowOpacity()
|
|
{
|
|
if (!active_client) {
|
|
return;
|
|
}
|
|
active_client->setOpacity(qMin(active_client->opacity() + 0.05, 1.0));
|
|
}
|
|
|
|
void Workspace::slotLowerWindowOpacity()
|
|
{
|
|
if (!active_client) {
|
|
return;
|
|
}
|
|
active_client->setOpacity(qMax(active_client->opacity() - 0.05, 0.05));
|
|
}
|
|
|
|
/*!
|
|
The client popup menu will become visible soon.
|
|
|
|
Adjust the items according to the respective popup client.
|
|
*/
|
|
void Workspace::clientPopupAboutToShow()
|
|
{
|
|
if (!active_popup_client || !popup)
|
|
return;
|
|
|
|
if (numberOfDesktops() == 1) {
|
|
delete desk_popup;
|
|
desk_popup = 0;
|
|
} else {
|
|
initDesktopPopup();
|
|
}
|
|
#ifdef KWIN_BUILD_ACTIVITIES
|
|
updateActivityList(true, false, "showHideActivityMenu");
|
|
#endif
|
|
|
|
mResizeOpAction->setEnabled(active_popup_client->isResizable());
|
|
mMoveOpAction->setEnabled(active_popup_client->isMovableAcrossScreens());
|
|
mMaximizeOpAction->setEnabled(active_popup_client->isMaximizable());
|
|
mMaximizeOpAction->setChecked(active_popup_client->maximizeMode() == Client::MaximizeFull);
|
|
mShadeOpAction->setEnabled(active_popup_client->isShadeable());
|
|
mShadeOpAction->setChecked(active_popup_client->shadeMode() != ShadeNone);
|
|
mKeepAboveOpAction->setChecked(active_popup_client->keepAbove());
|
|
mKeepBelowOpAction->setChecked(active_popup_client->keepBelow());
|
|
mFullScreenOpAction->setEnabled(active_popup_client->userCanSetFullScreen());
|
|
mFullScreenOpAction->setChecked(active_popup_client->isFullScreen());
|
|
mNoBorderOpAction->setEnabled(active_popup_client->userCanSetNoBorder());
|
|
mNoBorderOpAction->setChecked(active_popup_client->noBorder());
|
|
mMinimizeOpAction->setEnabled(active_popup_client->isMinimizable());
|
|
mCloseOpAction->setEnabled(active_popup_client->isCloseable());
|
|
|
|
#ifdef KWIN_BUILD_TILING
|
|
if (m_tiling->isEnabled()) {
|
|
int desktop = active_popup_client->desktop();
|
|
if (m_tiling->tilingLayouts().value(desktop)) {
|
|
Tile *t = m_tiling->tilingLayouts()[desktop]->findTile(active_popup_client);
|
|
if (t)
|
|
mTilingStateOpAction->setChecked(t->floating());
|
|
}
|
|
}
|
|
mTilingStateOpAction->setVisible(m_tiling->isEnabled());
|
|
#endif
|
|
|
|
if (decorationSupportsTabbing()) {
|
|
initTabbingPopups();
|
|
} else {
|
|
delete add_tabs_popup;
|
|
add_tabs_popup = 0;
|
|
}
|
|
}
|
|
|
|
void Workspace::showHideActivityMenu()
|
|
{
|
|
#ifdef KWIN_BUILD_ACTIVITIES
|
|
kDebug() << "activities:" << openActivities_.size();
|
|
if (openActivities_.size() < 2) {
|
|
delete activity_popup;
|
|
activity_popup = 0;
|
|
} else {
|
|
initActivityPopup();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Workspace::selectPopupClientTab(QAction* action)
|
|
{
|
|
if (!(active_popup_client && active_popup_client->tabGroup()) || !action->data().isValid())
|
|
return;
|
|
|
|
if (Client *other = action->data().value<Client*>()) {
|
|
active_popup_client->tabGroup()->setCurrent(other);
|
|
return;
|
|
}
|
|
|
|
// failed conversion, try "1" & "2", being prev and next
|
|
int direction = action->data().toInt();
|
|
if (direction == 1)
|
|
active_popup_client->tabGroup()->activatePrev();
|
|
else if (direction == 2)
|
|
active_popup_client->tabGroup()->activateNext();
|
|
}
|
|
|
|
static QString shortCaption(const QString &s)
|
|
{
|
|
if (s.length() < 64)
|
|
return s;
|
|
QString ss = s;
|
|
return ss.replace(32,s.length()-64,"...");
|
|
}
|
|
|
|
void Workspace::rebuildTabListPopup()
|
|
{
|
|
Q_ASSERT(switch_to_tab_popup);
|
|
|
|
switch_to_tab_popup->clear();
|
|
// whatever happens "0x1" and "0x2" are no heap positions ;-)
|
|
switch_to_tab_popup->addAction(i18nc("Switch to tab -> Previous", "Previous"))->setData(1);
|
|
switch_to_tab_popup->addAction(i18nc("Switch to tab -> Next", "Next"))->setData(2);
|
|
|
|
switch_to_tab_popup->addSeparator();
|
|
|
|
for (QList<Client*>::const_iterator i = active_popup_client->tabGroup()->clients().constBegin(),
|
|
end = active_popup_client->tabGroup()->clients().constEnd(); i != end; ++i) {
|
|
if ((*i)->noBorder() || *i == active_popup_client->tabGroup()->current())
|
|
continue; // cannot tab there anyway
|
|
switch_to_tab_popup->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i));
|
|
}
|
|
|
|
}
|
|
|
|
void Workspace::entabPopupClient(QAction* action)
|
|
{
|
|
if (!active_popup_client || !action->data().isValid())
|
|
return;
|
|
Client *other = action->data().value<Client*>();
|
|
if (!clients.contains(other)) // might have been lost betwenn pop-up and selection
|
|
return;
|
|
active_popup_client->tabBehind(other, true);
|
|
if (options->focusPolicyIsReasonable())
|
|
requestFocus(active_popup_client);
|
|
}
|
|
|
|
void Workspace::rebuildTabGroupPopup()
|
|
{
|
|
Q_ASSERT(add_tabs_popup);
|
|
|
|
add_tabs_popup->clear();
|
|
QList<Client*> handled;
|
|
for (QList<Client*>::const_iterator i = clientList().constBegin(), end = clientList().constEnd(); i != end; ++i) {
|
|
if (*i == active_popup_client || (*i)->noBorder())
|
|
continue;
|
|
add_tabs_popup->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i));
|
|
}
|
|
}
|
|
|
|
void Workspace::initTabbingPopups()
|
|
{
|
|
bool needTabManagers = false;
|
|
if (active_popup_client->tabGroup() && active_popup_client->tabGroup()->count() > 1) {
|
|
needTabManagers = true;
|
|
if (!switch_to_tab_popup) {
|
|
switch_to_tab_popup = new QMenu(i18n("Switch to Tab"), popup);
|
|
switch_to_tab_popup->setFont(KGlobalSettings::menuFont());
|
|
connect(switch_to_tab_popup, SIGNAL(triggered(QAction*)), SLOT(selectPopupClientTab(QAction*)));
|
|
connect(switch_to_tab_popup, SIGNAL(aboutToShow()), SLOT(rebuildTabListPopup()));
|
|
popup->insertMenu(mRemoveFromTabGroup, switch_to_tab_popup);
|
|
}
|
|
} else {
|
|
delete switch_to_tab_popup;
|
|
switch_to_tab_popup = 0;
|
|
}
|
|
|
|
if (!add_tabs_popup) {
|
|
add_tabs_popup = new QMenu(i18n("Attach as tab to"), popup);
|
|
add_tabs_popup->setFont(KGlobalSettings::menuFont());
|
|
connect(add_tabs_popup, SIGNAL(triggered(QAction*)), SLOT(entabPopupClient(QAction*)));
|
|
connect(add_tabs_popup, SIGNAL(aboutToShow()), SLOT(rebuildTabGroupPopup()));
|
|
popup->insertMenu(mRemoveFromTabGroup, add_tabs_popup);
|
|
}
|
|
|
|
mRemoveFromTabGroup->setVisible(needTabManagers);
|
|
mCloseTabGroup->setVisible(needTabManagers);
|
|
}
|
|
|
|
void Workspace::initDesktopPopup()
|
|
{
|
|
if (desk_popup)
|
|
return;
|
|
|
|
desk_popup = new QMenu(popup);
|
|
desk_popup->setFont(KGlobalSettings::menuFont());
|
|
connect(desk_popup, SIGNAL(triggered(QAction*)), SLOT(slotSendToDesktop(QAction*)));
|
|
connect(desk_popup, SIGNAL(aboutToShow()), SLOT(desktopPopupAboutToShow()));
|
|
|
|
QAction *action = desk_popup->menuAction();
|
|
// set it as the first item
|
|
popup->insertAction(mMinimizeOpAction, action);
|
|
action->setText(i18n("Move To &Desktop"));
|
|
}
|
|
|
|
/*!
|
|
Creates activity popup.
|
|
I'm going with checkable ones instead of "copy to" and "move to" menus; I *think* it's an easier way.
|
|
Oh, and an 'all' option too of course
|
|
*/
|
|
void Workspace::initActivityPopup()
|
|
{
|
|
if (activity_popup)
|
|
return;
|
|
|
|
activity_popup = new QMenu(popup);
|
|
activity_popup->setFont(KGlobalSettings::menuFont());
|
|
connect(activity_popup, SIGNAL(triggered(QAction*)),
|
|
this, SLOT(slotToggleOnActivity(QAction*)));
|
|
connect(activity_popup, SIGNAL(aboutToShow()),
|
|
this, SLOT(activityPopupAboutToShow()));
|
|
|
|
QAction *action = activity_popup->menuAction();
|
|
// set it as the first item
|
|
popup->insertAction(mMinimizeOpAction, action);
|
|
action->setText(i18n("Ac&tivities")); //FIXME is that a good string?
|
|
}
|
|
|
|
/*!
|
|
Adjusts the desktop popup to the current values and the location of
|
|
the popup client.
|
|
*/
|
|
void Workspace::desktopPopupAboutToShow()
|
|
{
|
|
if (!desk_popup)
|
|
return;
|
|
|
|
desk_popup->clear();
|
|
QActionGroup *group = new QActionGroup(desk_popup);
|
|
QAction *action = desk_popup->addAction(i18n("&All Desktops"));
|
|
action->setData(0);
|
|
action->setCheckable(true);
|
|
group->addAction(action);
|
|
|
|
if (active_popup_client && active_popup_client->isOnAllDesktops())
|
|
action->setChecked(true);
|
|
desk_popup->addSeparator();
|
|
|
|
const int BASE = 10;
|
|
for (int i = 1; i <= numberOfDesktops(); i++) {
|
|
QString basic_name("%1 %2");
|
|
if (i < BASE) {
|
|
basic_name.prepend('&');
|
|
}
|
|
action = desk_popup->addAction(basic_name.arg(i).arg(desktopName(i).replace('&', "&&")));
|
|
action->setData(i);
|
|
action->setCheckable(true);
|
|
group->addAction(action);
|
|
|
|
if (active_popup_client &&
|
|
!active_popup_client->isOnAllDesktops() && active_popup_client->desktop() == i)
|
|
action->setChecked(true);
|
|
}
|
|
|
|
desk_popup->addSeparator();
|
|
action = desk_popup->addAction(i18nc("Create a new desktop and move there the window", "&New Desktop"));
|
|
action->setData(numberOfDesktops() + 1);
|
|
|
|
if (numberOfDesktops() >= Workspace::self()->maxNumberOfDesktops())
|
|
action->setEnabled(false);
|
|
}
|
|
|
|
/*!
|
|
Adjusts the activity popup to the current values and the location of
|
|
the popup client.
|
|
*/
|
|
void Workspace::activityPopupAboutToShow()
|
|
{
|
|
if (!activity_popup)
|
|
return;
|
|
|
|
#ifdef KWIN_BUILD_ACTIVITIES
|
|
activity_popup->clear();
|
|
QAction *action = activity_popup->addAction(i18n("&All Activities"));
|
|
action->setData(QString());
|
|
action->setCheckable(true);
|
|
|
|
if (active_popup_client && active_popup_client->isOnAllActivities())
|
|
action->setChecked(true);
|
|
activity_popup->addSeparator();
|
|
|
|
foreach (const QString &id, openActivities_) {
|
|
KActivities::Info activity(id);
|
|
QString name = activity.name();
|
|
name.replace('&', "&&");
|
|
action = activity_popup->addAction(KIcon(activity.icon()), name);
|
|
action->setData(id);
|
|
action->setCheckable(true);
|
|
|
|
if (active_popup_client &&
|
|
!active_popup_client->isOnAllActivities() && active_popup_client->isOnActivity(id))
|
|
action->setChecked(true);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Workspace::closeActivePopup()
|
|
{
|
|
if (active_popup) {
|
|
active_popup->close();
|
|
active_popup = NULL;
|
|
active_popup_client = NULL;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Create the global accel object \c keys.
|
|
*/
|
|
void Workspace::initShortcuts()
|
|
{
|
|
keys = new KActionCollection(this);
|
|
KActionCollection* actionCollection = keys;
|
|
QAction* a = 0L;
|
|
|
|
// a separate KActionCollection is needed for the shortcut for disabling global shortcuts,
|
|
// otherwise it would also disable itself
|
|
disable_shortcuts_keys = new KActionCollection(this);
|
|
// TODO: PORT ME (KGlobalAccel related)
|
|
// FIXME KAccel port... needed?
|
|
//disable_shortcuts_keys->disableBlocking( true );
|
|
#define IN_KWIN
|
|
#include "kwinbindings.cpp"
|
|
#ifdef KWIN_BUILD_TABBOX
|
|
if (tab_box) {
|
|
tab_box->initShortcuts(actionCollection);
|
|
}
|
|
#endif
|
|
#ifdef KWIN_BUILD_TILING
|
|
if (m_tiling) {
|
|
m_tiling->initShortcuts(actionCollection);
|
|
}
|
|
#endif
|
|
discardPopup(); // so that it's recreated next time
|
|
}
|
|
|
|
void Workspace::setupWindowShortcut(Client* c)
|
|
{
|
|
assert(client_keys_dialog == NULL);
|
|
// TODO: PORT ME (KGlobalAccel related)
|
|
//keys->setEnabled( false );
|
|
//disable_shortcuts_keys->setEnabled( false );
|
|
//client_keys->setEnabled( false );
|
|
client_keys_dialog = new ShortcutDialog(c->shortcut().primary());
|
|
client_keys_client = c;
|
|
connect(client_keys_dialog, SIGNAL(dialogDone(bool)), SLOT(setupWindowShortcutDone(bool)));
|
|
QRect r = clientArea(ScreenArea, c);
|
|
QSize size = client_keys_dialog->sizeHint();
|
|
QPoint pos = c->pos() + c->clientPos();
|
|
if (pos.x() + size.width() >= r.right())
|
|
pos.setX(r.right() - size.width());
|
|
if (pos.y() + size.height() >= r.bottom())
|
|
pos.setY(r.bottom() - size.height());
|
|
client_keys_dialog->move(pos);
|
|
client_keys_dialog->show();
|
|
active_popup = client_keys_dialog;
|
|
active_popup_client = c;
|
|
}
|
|
|
|
void Workspace::setupWindowShortcutDone(bool ok)
|
|
{
|
|
// keys->setEnabled( true );
|
|
// disable_shortcuts_keys->setEnabled( true );
|
|
// client_keys->setEnabled( true );
|
|
if (ok)
|
|
client_keys_client->setShortcut(KShortcut(client_keys_dialog->shortcut()).toString());
|
|
closeActivePopup();
|
|
client_keys_dialog->deleteLater();
|
|
client_keys_dialog = NULL;
|
|
client_keys_client = NULL;
|
|
}
|
|
|
|
void Workspace::clientShortcutUpdated(Client* c)
|
|
{
|
|
QString key = QString("_k_session:%1").arg(c->window());
|
|
QAction* action = client_keys->action(key.toLatin1().constData());
|
|
if (!c->shortcut().isEmpty()) {
|
|
if (action == NULL) { // new shortcut
|
|
action = client_keys->addAction(QString(key));
|
|
action->setText(i18n("Activate Window (%1)", c->caption()));
|
|
connect(action, SIGNAL(triggered(bool)), c, SLOT(shortcutActivated()));
|
|
}
|
|
|
|
KAction *kaction = qobject_cast<KAction*>(action);
|
|
// no autoloading, since it's configured explicitly here and is not meant to be reused
|
|
// (the key is the window id anyway, which is kind of random)
|
|
kaction->setGlobalShortcut(
|
|
c->shortcut(), KAction::ActiveShortcut, KAction::NoAutoloading);
|
|
kaction->setEnabled(true);
|
|
} else {
|
|
KAction *kaction = qobject_cast<KAction*>(action);
|
|
if (kaction) {
|
|
kaction->forgetGlobalShortcut();
|
|
}
|
|
delete action;
|
|
}
|
|
}
|
|
|
|
void Workspace::clientPopupActivated(QAction *action)
|
|
{
|
|
if (!action->data().isValid())
|
|
return;
|
|
|
|
WindowOperation op = static_cast< WindowOperation >(action->data().toInt());
|
|
Client* c = active_popup_client ? active_popup_client : active_client;
|
|
if (!c)
|
|
return;
|
|
QString type;
|
|
switch(op) {
|
|
case FullScreenOp:
|
|
if (!c->isFullScreen() && c->userCanSetFullScreen())
|
|
type = "fullscreenaltf3";
|
|
break;
|
|
case NoBorderOp:
|
|
if (!c->noBorder() && c->userCanSetNoBorder())
|
|
type = "noborderaltf3";
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
if (!type.isEmpty())
|
|
helperDialog(type, c);
|
|
performWindowOperation(c, op);
|
|
}
|
|
|
|
|
|
void Workspace::performWindowOperation(Client* c, Options::WindowOperation op)
|
|
{
|
|
if (!c)
|
|
return;
|
|
#ifdef KWIN_BUILD_TILING
|
|
// Allows us to float a window when it is maximized, if it is tiled.
|
|
if (m_tiling->isEnabled()
|
|
&& (op == Options::MaximizeOp
|
|
|| op == Options::HMaximizeOp
|
|
|| op == Options::VMaximizeOp
|
|
|| op == Options::RestoreOp)) {
|
|
m_tiling->notifyTilingWindowMaximized(c, op);
|
|
}
|
|
#endif
|
|
if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp)
|
|
QCursor::setPos(c->geometry().center());
|
|
if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp)
|
|
QCursor::setPos(c->geometry().bottomRight());
|
|
switch(op) {
|
|
case Options::MoveOp:
|
|
c->performMouseCommand(Options::MouseMove, cursorPos());
|
|
break;
|
|
case Options::UnrestrictedMoveOp:
|
|
c->performMouseCommand(Options::MouseUnrestrictedMove, cursorPos());
|
|
break;
|
|
case Options::ResizeOp:
|
|
c->performMouseCommand(Options::MouseResize, cursorPos());
|
|
break;
|
|
case Options::UnrestrictedResizeOp:
|
|
c->performMouseCommand(Options::MouseUnrestrictedResize, cursorPos());
|
|
break;
|
|
case Options::CloseOp:
|
|
c->closeWindow();
|
|
break;
|
|
case Options::MaximizeOp:
|
|
c->maximize(c->maximizeMode() == Client::MaximizeFull
|
|
? Client::MaximizeRestore : Client::MaximizeFull);
|
|
break;
|
|
case Options::HMaximizeOp:
|
|
c->maximize(c->maximizeMode() ^ Client::MaximizeHorizontal);
|
|
break;
|
|
case Options::VMaximizeOp:
|
|
c->maximize(c->maximizeMode() ^ Client::MaximizeVertical);
|
|
break;
|
|
case Options::RestoreOp:
|
|
c->maximize(Client::MaximizeRestore);
|
|
break;
|
|
case Options::MinimizeOp:
|
|
c->minimize();
|
|
break;
|
|
case Options::ShadeOp:
|
|
c->performMouseCommand(Options::MouseShade, cursorPos());
|
|
break;
|
|
case Options::OnAllDesktopsOp:
|
|
c->setOnAllDesktops(!c->isOnAllDesktops());
|
|
break;
|
|
case Options::FullScreenOp:
|
|
c->setFullScreen(!c->isFullScreen(), true);
|
|
break;
|
|
case Options::NoBorderOp:
|
|
c->setNoBorder(!c->noBorder());
|
|
break;
|
|
case Options::KeepAboveOp: {
|
|
StackingUpdatesBlocker blocker(this);
|
|
bool was = c->keepAbove();
|
|
c->setKeepAbove(!c->keepAbove());
|
|
if (was && !c->keepAbove())
|
|
raiseClient(c);
|
|
break;
|
|
}
|
|
case Options::KeepBelowOp: {
|
|
StackingUpdatesBlocker blocker(this);
|
|
bool was = c->keepBelow();
|
|
c->setKeepBelow(!c->keepBelow());
|
|
if (was && !c->keepBelow())
|
|
lowerClient(c);
|
|
break;
|
|
}
|
|
case Options::OperationsOp:
|
|
c->performMouseCommand(Options::MouseShade, cursorPos());
|
|
break;
|
|
case Options::WindowRulesOp:
|
|
editWindowRules(c, false);
|
|
break;
|
|
case Options::ApplicationRulesOp:
|
|
editWindowRules(c, true);
|
|
break;
|
|
case Options::SetupWindowShortcutOp:
|
|
setupWindowShortcut(c);
|
|
break;
|
|
case Options::LowerOp:
|
|
lowerClient(c);
|
|
break;
|
|
case Options::TabDragOp: // Handled by decoration itself
|
|
case Options::NoOp:
|
|
break;
|
|
case Options::RemoveTabFromGroupOp:
|
|
if (c->untab())
|
|
if (options->focusPolicyIsReasonable())
|
|
takeActivity(c, ActivityFocus | ActivityRaise, true);
|
|
break;
|
|
case Options::ActivateNextTabOp:
|
|
if (c->tabGroup())
|
|
c->tabGroup()->activateNext();
|
|
break;
|
|
case Options::ActivatePreviousTabOp:
|
|
if (c->tabGroup())
|
|
c->tabGroup()->activatePrev();
|
|
break;
|
|
case Options::CloseTabGroupOp:
|
|
c->tabGroup()->closeAll();
|
|
case Options::ToggleClientTiledStateOp: {
|
|
#ifdef KWIN_BUILD_TILING
|
|
int desktop = c->desktop();
|
|
if (m_tiling->tilingLayouts().value(desktop)) {
|
|
m_tiling->tilingLayouts()[desktop]->toggleFloatTile(c);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called by the decoration in the new API to determine what buttons the user has configured for
|
|
* window tab dragging and the operations menu.
|
|
*/
|
|
Options::WindowOperation Client::mouseButtonToWindowOperation(Qt::MouseButtons button)
|
|
{
|
|
Options::MouseCommand com = Options::MouseNothing;
|
|
bool active = isActive();
|
|
if (!wantsInput()) // we cannot be active, use it anyway
|
|
active = true;
|
|
|
|
if (button == Qt::LeftButton)
|
|
com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1();
|
|
else if (button == Qt::MidButton)
|
|
com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2();
|
|
else if (button == Qt::RightButton)
|
|
com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3();
|
|
|
|
// TODO: Complete the list
|
|
if (com == Options::MouseDragTab)
|
|
return Options::TabDragOp;
|
|
if (com == Options::MouseOperationsMenu)
|
|
return Options::OperationsOp;
|
|
return Options::NoOp;
|
|
}
|
|
|
|
/*!
|
|
Performs a mouse command on this client (see options.h)
|
|
*/
|
|
bool Client::performMouseCommand(Options::MouseCommand command, const QPoint &globalPos, bool handled)
|
|
{
|
|
bool replay = false;
|
|
switch(command) {
|
|
case Options::MouseRaise:
|
|
workspace()->raiseClient(this);
|
|
break;
|
|
case Options::MouseLower: {
|
|
workspace()->lowerClient(this);
|
|
// used to be activateNextClient(this), then topClientOnDesktop
|
|
// since this is a mouseOp it's however safe to use the client under the mouse instead
|
|
if (isActive() && options->focusPolicyIsReasonable()) {
|
|
Client *next = workspace()->clientUnderMouse(screen());
|
|
if (next && next != this)
|
|
workspace()->requestFocus(next, false);
|
|
}
|
|
break;
|
|
}
|
|
case Options::MouseShade :
|
|
toggleShade();
|
|
cancelShadeHoverTimer();
|
|
break;
|
|
case Options::MouseSetShade:
|
|
setShade(ShadeNormal);
|
|
cancelShadeHoverTimer();
|
|
break;
|
|
case Options::MouseUnsetShade:
|
|
setShade(ShadeNone);
|
|
cancelShadeHoverTimer();
|
|
break;
|
|
case Options::MouseOperationsMenu:
|
|
if (isActive() && options->isClickRaise())
|
|
autoRaise();
|
|
workspace()->showWindowMenu(globalPos, this);
|
|
break;
|
|
case Options::MouseToggleRaiseAndLower:
|
|
workspace()->raiseOrLowerClient(this);
|
|
break;
|
|
case Options::MouseActivateAndRaise: {
|
|
replay = isActive(); // for clickraise mode
|
|
bool mustReplay = !rules()->checkAcceptFocus(input);
|
|
if (mustReplay) {
|
|
ToplevelList::const_iterator it = workspace()->stackingOrder().constEnd(),
|
|
begin = workspace()->stackingOrder().constBegin();
|
|
while (mustReplay && --it != begin && *it != this) {
|
|
Client *c = qobject_cast<Client*>(*it);
|
|
if (!c || (c->keepAbove() && !keepAbove()) || (keepBelow() && !c->keepBelow()))
|
|
continue; // can never raise above "it"
|
|
mustReplay = !(c->isOnCurrentDesktop() && c->isOnCurrentActivity() && c->geometry().intersects(geometry()));
|
|
}
|
|
}
|
|
workspace()->takeActivity(this, ActivityFocus | ActivityRaise, handled && replay);
|
|
workspace()->setActiveScreenMouse(globalPos);
|
|
replay = replay || mustReplay;
|
|
break;
|
|
}
|
|
case Options::MouseActivateAndLower:
|
|
workspace()->requestFocus(this);
|
|
workspace()->lowerClient(this);
|
|
workspace()->setActiveScreenMouse(globalPos);
|
|
replay = replay || !rules()->checkAcceptFocus(input);
|
|
break;
|
|
case Options::MouseActivate:
|
|
replay = isActive(); // for clickraise mode
|
|
workspace()->takeActivity(this, ActivityFocus, handled && replay);
|
|
workspace()->setActiveScreenMouse(globalPos);
|
|
replay = replay || !rules()->checkAcceptFocus(input);
|
|
break;
|
|
case Options::MouseActivateRaiseAndPassClick:
|
|
workspace()->takeActivity(this, ActivityFocus | ActivityRaise, handled);
|
|
workspace()->setActiveScreenMouse(globalPos);
|
|
replay = true;
|
|
break;
|
|
case Options::MouseActivateAndPassClick:
|
|
workspace()->takeActivity(this, ActivityFocus, handled);
|
|
workspace()->setActiveScreenMouse(globalPos);
|
|
replay = true;
|
|
break;
|
|
case Options::MouseActivateRaiseAndMove:
|
|
case Options::MouseActivateRaiseAndUnrestrictedMove:
|
|
workspace()->raiseClient(this);
|
|
workspace()->requestFocus(this);
|
|
workspace()->setActiveScreenMouse(globalPos);
|
|
// fallthrough
|
|
case Options::MouseMove:
|
|
case Options::MouseUnrestrictedMove: {
|
|
if (!isMovableAcrossScreens())
|
|
break;
|
|
if (moveResizeMode)
|
|
finishMoveResize(false);
|
|
mode = PositionCenter;
|
|
buttonDown = true;
|
|
moveOffset = QPoint(globalPos.x() - x(), globalPos.y() - y()); // map from global
|
|
invertedMoveOffset = rect().bottomRight() - moveOffset;
|
|
unrestrictedMoveResize = (command == Options::MouseActivateRaiseAndUnrestrictedMove
|
|
|| command == Options::MouseUnrestrictedMove);
|
|
if (!startMoveResize())
|
|
buttonDown = false;
|
|
updateCursor();
|
|
break;
|
|
}
|
|
case Options::MouseResize:
|
|
case Options::MouseUnrestrictedResize: {
|
|
if (!isResizable() || isShade())
|
|
break;
|
|
if (moveResizeMode)
|
|
finishMoveResize(false);
|
|
buttonDown = true;
|
|
moveOffset = QPoint(globalPos.x() - x(), globalPos.y() - y()); // map from global
|
|
int x = moveOffset.x(), y = moveOffset.y();
|
|
bool left = x < width() / 3;
|
|
bool right = x >= 2 * width() / 3;
|
|
bool top = y < height() / 3;
|
|
bool bot = y >= 2 * height() / 3;
|
|
if (top)
|
|
mode = left ? PositionTopLeft : (right ? PositionTopRight : PositionTop);
|
|
else if (bot)
|
|
mode = left ? PositionBottomLeft : (right ? PositionBottomRight : PositionBottom);
|
|
else
|
|
mode = (x < width() / 2) ? PositionLeft : PositionRight;
|
|
invertedMoveOffset = rect().bottomRight() - moveOffset;
|
|
unrestrictedMoveResize = (command == Options::MouseUnrestrictedResize);
|
|
if (!startMoveResize())
|
|
buttonDown = false;
|
|
updateCursor();
|
|
break;
|
|
}
|
|
case Options::MouseMaximize:
|
|
maximize(Client::MaximizeFull);
|
|
break;
|
|
case Options::MouseRestore:
|
|
maximize(Client::MaximizeRestore);
|
|
break;
|
|
case Options::MouseMinimize:
|
|
minimize();
|
|
break;
|
|
case Options::MouseAbove: {
|
|
StackingUpdatesBlocker blocker(workspace());
|
|
if (keepBelow())
|
|
setKeepBelow(false);
|
|
else
|
|
setKeepAbove(true);
|
|
break;
|
|
}
|
|
case Options::MouseBelow: {
|
|
StackingUpdatesBlocker blocker(workspace());
|
|
if (keepAbove())
|
|
setKeepAbove(false);
|
|
else
|
|
setKeepBelow(true);
|
|
break;
|
|
}
|
|
case Options::MousePreviousDesktop:
|
|
workspace()->windowToPreviousDesktop(this);
|
|
break;
|
|
case Options::MouseNextDesktop:
|
|
workspace()->windowToNextDesktop(this);
|
|
break;
|
|
case Options::MouseOpacityMore:
|
|
if (!isDesktop()) // No point in changing the opacity of the desktop
|
|
setOpacity(qMin(opacity() + 0.1, 1.0));
|
|
break;
|
|
case Options::MouseOpacityLess:
|
|
if (!isDesktop()) // No point in changing the opacity of the desktop
|
|
setOpacity(qMax(opacity() - 0.1, 0.1));
|
|
break;
|
|
case Options::MousePreviousTab:
|
|
if (tabGroup())
|
|
tabGroup()->activatePrev();
|
|
break;
|
|
case Options::MouseNextTab:
|
|
if (tabGroup())
|
|
tabGroup()->activateNext();
|
|
break;
|
|
case Options::MouseClose:
|
|
closeWindow();
|
|
break;
|
|
case Options::MouseDragTab:
|
|
case Options::MouseNothing:
|
|
replay = true;
|
|
break;
|
|
}
|
|
return replay;
|
|
}
|
|
|
|
// KDE4 remove me
|
|
void Workspace::showWindowMenuAt(unsigned long, int, int)
|
|
{
|
|
slotWindowOperations();
|
|
}
|
|
|
|
void Workspace::loadEffect(const QString& name)
|
|
{
|
|
if (effects)
|
|
static_cast<EffectsHandlerImpl*>(effects)->loadEffect(name);
|
|
}
|
|
|
|
void Workspace::toggleEffect(const QString& name)
|
|
{
|
|
if (effects)
|
|
static_cast<EffectsHandlerImpl*>(effects)->toggleEffect(name);
|
|
}
|
|
|
|
void Workspace::unloadEffect(const QString& name)
|
|
{
|
|
if (effects)
|
|
static_cast<EffectsHandlerImpl*>(effects)->unloadEffect(name);
|
|
}
|
|
|
|
void Workspace::reconfigureEffect(const QString& name)
|
|
{
|
|
if (effects)
|
|
static_cast<EffectsHandlerImpl*>(effects)->reconfigureEffect(name);
|
|
}
|
|
|
|
QStringList Workspace::loadedEffects() const
|
|
{
|
|
QStringList listModulesLoaded;
|
|
if (effects)
|
|
listModulesLoaded = static_cast<EffectsHandlerImpl*>(effects)->loadedEffects();
|
|
return listModulesLoaded;
|
|
}
|
|
|
|
QStringList Workspace::listOfEffects() const
|
|
{
|
|
QStringList listModules;
|
|
if (effects)
|
|
listModules = static_cast<EffectsHandlerImpl*>(effects)->listOfEffects();
|
|
return listModules;
|
|
}
|
|
|
|
void Workspace::slotActivateAttentionWindow()
|
|
{
|
|
if (attention_chain.count() > 0)
|
|
activateClient(attention_chain.first());
|
|
}
|
|
|
|
void Workspace::slotSwitchDesktopNext()
|
|
{
|
|
int d = currentDesktop() + 1;
|
|
if (d > numberOfDesktops()) {
|
|
if (options->isRollOverDesktops()) {
|
|
d = 1;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
setCurrentDesktop(d);
|
|
}
|
|
|
|
void Workspace::slotSwitchDesktopPrevious()
|
|
{
|
|
int d = currentDesktop() - 1;
|
|
if (d <= 0) {
|
|
if (options->isRollOverDesktops())
|
|
d = numberOfDesktops();
|
|
else
|
|
return;
|
|
}
|
|
setCurrentDesktop(d);
|
|
}
|
|
|
|
void Workspace::slotSwitchDesktopRight()
|
|
{
|
|
int desktop = desktopToRight(currentDesktop(), options->isRollOverDesktops());
|
|
if (desktop == currentDesktop())
|
|
return;
|
|
setCurrentDesktop(desktop);
|
|
}
|
|
|
|
void Workspace::slotSwitchDesktopLeft()
|
|
{
|
|
int desktop = desktopToLeft(currentDesktop(), options->isRollOverDesktops());
|
|
if (desktop == currentDesktop())
|
|
return;
|
|
setCurrentDesktop(desktop);
|
|
}
|
|
|
|
void Workspace::slotSwitchDesktopUp()
|
|
{
|
|
int desktop = desktopAbove(currentDesktop(), options->isRollOverDesktops());
|
|
if (desktop == currentDesktop())
|
|
return;
|
|
setCurrentDesktop(desktop);
|
|
}
|
|
|
|
void Workspace::slotSwitchDesktopDown()
|
|
{
|
|
int desktop = desktopBelow(currentDesktop(), options->isRollOverDesktops());
|
|
if (desktop == currentDesktop())
|
|
return;
|
|
setCurrentDesktop(desktop);
|
|
}
|
|
|
|
static int senderValue(QObject *sender)
|
|
{
|
|
QAction *act = qobject_cast<QAction*>(sender);
|
|
bool ok = false; int i = -1;
|
|
if (act)
|
|
i = act->data().toUInt(&ok);
|
|
if (ok)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
void Workspace::slotSwitchToDesktop()
|
|
{
|
|
const int i = senderValue(sender());
|
|
if (i > 0)
|
|
setCurrentDesktop(i);
|
|
}
|
|
|
|
#define USABLE_ACTIVE_CLIENT (active_client && !(active_client->isDesktop() || active_client->isDock()))
|
|
|
|
void Workspace::slotWindowToDesktop()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT) {
|
|
const int i = senderValue(sender());
|
|
if (i < 1)
|
|
return;
|
|
|
|
if (i >= 1 && i <= numberOfDesktops())
|
|
sendClientToDesktop(active_client, i, true);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotSwitchToScreen()
|
|
{
|
|
const int i = senderValue(sender());
|
|
if (i > -1)
|
|
setCurrentScreen(i);
|
|
}
|
|
|
|
void Workspace::slotSwitchToNextScreen()
|
|
{
|
|
setCurrentScreen((activeScreen() + 1) % numScreens());
|
|
}
|
|
|
|
void Workspace::slotWindowToScreen()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT) {
|
|
const int i = senderValue(sender());
|
|
if (i < 0)
|
|
return;
|
|
if (i >= 0 && i <= numScreens()) {
|
|
sendClientToScreen(active_client, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToNextScreen()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
sendClientToScreen(active_client, (active_client->screen() + 1) % numScreens());
|
|
}
|
|
|
|
/*!
|
|
Maximizes the popup client
|
|
*/
|
|
void Workspace::slotWindowMaximize()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::MaximizeOp);
|
|
}
|
|
|
|
/*!
|
|
Maximizes the popup client vertically
|
|
*/
|
|
void Workspace::slotWindowMaximizeVertical()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::VMaximizeOp);
|
|
}
|
|
|
|
/*!
|
|
Maximizes the popup client horiozontally
|
|
*/
|
|
void Workspace::slotWindowMaximizeHorizontal()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::HMaximizeOp);
|
|
}
|
|
|
|
|
|
/*!
|
|
Minimizes the popup client
|
|
*/
|
|
void Workspace::slotWindowMinimize()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::MinimizeOp);
|
|
}
|
|
|
|
/*!
|
|
Shades/unshades the popup client respectively
|
|
*/
|
|
void Workspace::slotWindowShade()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::ShadeOp);
|
|
}
|
|
|
|
/*!
|
|
Raises the popup client
|
|
*/
|
|
void Workspace::slotWindowRaise()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
raiseClient(active_client);
|
|
}
|
|
|
|
/*!
|
|
Lowers the popup client
|
|
*/
|
|
void Workspace::slotWindowLower()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT) {
|
|
lowerClient(active_client);
|
|
// As this most likely makes the window no longer visible change the
|
|
// keyboard focus to the next available window.
|
|
//activateNextClient( c ); // Doesn't work when we lower a child window
|
|
if (active_client->isActive() && options->focusPolicyIsReasonable()) {
|
|
if (options->isNextFocusPrefersMouse()) {
|
|
Client *next = clientUnderMouse(active_client->screen());
|
|
if (next && next != active_client)
|
|
requestFocus(next, false);
|
|
} else {
|
|
activateClient(topClientOnDesktop(currentDesktop(), -1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Does a toggle-raise-and-lower on the popup client;
|
|
*/
|
|
void Workspace::slotWindowRaiseOrLower()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
raiseOrLowerClient(active_client);
|
|
}
|
|
|
|
void Workspace::slotWindowOnAllDesktops()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
active_client->setOnAllDesktops(!active_client->isOnAllDesktops());
|
|
}
|
|
|
|
void Workspace::slotWindowFullScreen()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::FullScreenOp);
|
|
}
|
|
|
|
void Workspace::slotWindowNoBorder()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::NoBorderOp);
|
|
}
|
|
|
|
void Workspace::slotWindowAbove()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::KeepAboveOp);
|
|
}
|
|
|
|
void Workspace::slotWindowBelow()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::KeepBelowOp);
|
|
}
|
|
void Workspace::slotSetupWindowShortcut()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::SetupWindowShortcutOp);
|
|
}
|
|
|
|
/*!
|
|
Toggles show desktop
|
|
*/
|
|
void Workspace::slotToggleShowDesktop()
|
|
{
|
|
setShowingDesktop(!showingDesktop());
|
|
}
|
|
|
|
/*!
|
|
Move window to next desktop
|
|
*/
|
|
void Workspace::slotWindowToNextDesktop()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
windowToNextDesktop(active_client);
|
|
}
|
|
|
|
void Workspace::windowToNextDesktop(Client* c)
|
|
{
|
|
int d = currentDesktop() + 1;
|
|
if (d > numberOfDesktops())
|
|
d = 1;
|
|
if (c && !c->isDesktop()
|
|
&& !c->isDock()) {
|
|
setClientIsMoving(c);
|
|
setCurrentDesktop(d);
|
|
setClientIsMoving(NULL);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Move window to previous desktop
|
|
*/
|
|
void Workspace::slotWindowToPreviousDesktop()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
windowToPreviousDesktop(active_client);
|
|
}
|
|
|
|
void Workspace::windowToPreviousDesktop(Client* c)
|
|
{
|
|
int d = currentDesktop() - 1;
|
|
if (d <= 0)
|
|
d = numberOfDesktops();
|
|
if (c && !c->isDesktop()
|
|
&& !c->isDock()) {
|
|
setClientIsMoving(c);
|
|
setCurrentDesktop(d);
|
|
setClientIsMoving(NULL);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToDesktopRight()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT) {
|
|
int d = desktopToRight(currentDesktop(), options->isRollOverDesktops());
|
|
if (d == currentDesktop())
|
|
return;
|
|
|
|
setClientIsMoving(active_client);
|
|
setCurrentDesktop(d);
|
|
setClientIsMoving(NULL);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToDesktopLeft()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT) {
|
|
int d = desktopToLeft(currentDesktop(), options->isRollOverDesktops());
|
|
if (d == currentDesktop())
|
|
return;
|
|
|
|
setClientIsMoving(active_client);
|
|
setCurrentDesktop(d);
|
|
setClientIsMoving(NULL);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToDesktopUp()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT) {
|
|
int d = desktopAbove(currentDesktop(), options->isRollOverDesktops());
|
|
if (d == currentDesktop())
|
|
return;
|
|
|
|
setClientIsMoving(active_client);
|
|
setCurrentDesktop(d);
|
|
setClientIsMoving(NULL);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotWindowToDesktopDown()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT) {
|
|
int d = desktopBelow(currentDesktop(), options->isRollOverDesktops());
|
|
if (d == currentDesktop())
|
|
return;
|
|
|
|
setClientIsMoving(active_client);
|
|
setCurrentDesktop(d);
|
|
setClientIsMoving(NULL);
|
|
}
|
|
}
|
|
|
|
void Workspace::slotActivateNextTab()
|
|
{
|
|
if (active_client && active_client->tabGroup())
|
|
active_client->tabGroup()->activateNext();
|
|
}
|
|
|
|
void Workspace::slotActivatePrevTab()
|
|
{
|
|
if (active_client && active_client->tabGroup())
|
|
active_client->tabGroup()->activatePrev();
|
|
}
|
|
|
|
void Workspace::slotUntab()
|
|
{
|
|
if (active_client)
|
|
active_client->untab();
|
|
}
|
|
|
|
/*!
|
|
Kill Window feature, similar to xkill
|
|
*/
|
|
void Workspace::slotKillWindow()
|
|
{
|
|
KillWindow kill(this);
|
|
kill.start();
|
|
}
|
|
|
|
/*!
|
|
Sends the popup client to desktop \a desk
|
|
|
|
Internal slot for the window operation menu
|
|
*/
|
|
void Workspace::slotSendToDesktop(QAction *action)
|
|
{
|
|
int desk = action->data().toInt();
|
|
if (!active_popup_client)
|
|
return;
|
|
if (desk == 0) {
|
|
// the 'on_all_desktops' menu entry
|
|
active_popup_client->setOnAllDesktops(!active_popup_client->isOnAllDesktops());
|
|
return;
|
|
} else if (desk > numberOfDesktops()) {
|
|
setNumberOfDesktops(desk);
|
|
}
|
|
|
|
sendClientToDesktop(active_popup_client, desk, false);
|
|
|
|
}
|
|
|
|
/*!
|
|
Toggles whether the popup client is on the \a activity
|
|
|
|
Internal slot for the window operation menu
|
|
*/
|
|
void Workspace::slotToggleOnActivity(QAction *action)
|
|
{
|
|
QString activity = action->data().toString();
|
|
if (!active_popup_client)
|
|
return;
|
|
if (activity.isEmpty()) {
|
|
// the 'on_all_activities' menu entry
|
|
active_popup_client->setOnAllActivities(!active_popup_client->isOnAllActivities());
|
|
return;
|
|
}
|
|
|
|
toggleClientOnActivity(active_popup_client, activity, false);
|
|
|
|
}
|
|
|
|
/*!
|
|
Switches to the nearest window in given direction
|
|
*/
|
|
void Workspace::switchWindow(Direction direction)
|
|
{
|
|
if (!active_client)
|
|
return;
|
|
Client *c = active_client;
|
|
Client *switchTo = 0;
|
|
int bestScore = 0;
|
|
int d = c->desktop();
|
|
// Centre of the active window
|
|
QPoint curPos(c->pos().x() + c->geometry().width() / 2,
|
|
c->pos().y() + c->geometry().height() / 2);
|
|
|
|
ToplevelList clist = stackingOrder();
|
|
for (ToplevelList::Iterator i = clist.begin(); i != clist.end(); ++i) {
|
|
Client *client = qobject_cast<Client*>(*i);
|
|
if (!client) {
|
|
continue;
|
|
}
|
|
if (client->wantsTabFocus() && *i != c &&
|
|
client->desktop() == d && !client->isMinimized() && (*i)->isOnCurrentActivity()) {
|
|
// Centre of the other window
|
|
QPoint other(client->pos().x() + client->geometry().width() / 2,
|
|
client->pos().y() + client->geometry().height() / 2);
|
|
|
|
int distance;
|
|
int offset;
|
|
switch(direction) {
|
|
case DirectionNorth:
|
|
distance = curPos.y() - other.y();
|
|
offset = qAbs(other.x() - curPos.x());
|
|
break;
|
|
case DirectionEast:
|
|
distance = other.x() - curPos.x();
|
|
offset = qAbs(other.y() - curPos.y());
|
|
break;
|
|
case DirectionSouth:
|
|
distance = other.y() - curPos.y();
|
|
offset = qAbs(other.x() - curPos.x());
|
|
break;
|
|
case DirectionWest:
|
|
distance = curPos.x() - other.x();
|
|
offset = qAbs(other.y() - curPos.y());
|
|
break;
|
|
default:
|
|
distance = -1;
|
|
offset = -1;
|
|
}
|
|
|
|
if (distance > 0) {
|
|
// Inverse score
|
|
int score = distance + offset + ((offset * offset) / distance);
|
|
if (score < bestScore || !switchTo) {
|
|
switchTo = client;
|
|
bestScore = score;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (switchTo) {
|
|
if (switchTo->tabGroup())
|
|
switchTo = switchTo->tabGroup()->current();
|
|
activateClient(switchTo);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Switches to upper window
|
|
*/
|
|
void Workspace::slotSwitchWindowUp()
|
|
{
|
|
switchWindow(DirectionNorth);
|
|
}
|
|
|
|
/*!
|
|
Switches to lower window
|
|
*/
|
|
void Workspace::slotSwitchWindowDown()
|
|
{
|
|
switchWindow(DirectionSouth);
|
|
}
|
|
|
|
/*!
|
|
Switches to window on the right
|
|
*/
|
|
void Workspace::slotSwitchWindowRight()
|
|
{
|
|
switchWindow(DirectionEast);
|
|
}
|
|
|
|
/*!
|
|
Switches to window on the left
|
|
*/
|
|
void Workspace::slotSwitchWindowLeft()
|
|
{
|
|
switchWindow(DirectionWest);
|
|
}
|
|
|
|
/*!
|
|
Shows the window operations popup menu for the activeClient()
|
|
*/
|
|
void Workspace::slotWindowOperations()
|
|
{
|
|
if (!active_client)
|
|
return;
|
|
QPoint pos = active_client->pos() + active_client->clientPos();
|
|
showWindowMenu(pos.x(), pos.y(), active_client);
|
|
}
|
|
|
|
void Workspace::showWindowMenu(const QRect &pos, Client* cl)
|
|
{
|
|
if (!KAuthorized::authorizeKAction("kwin_rmb"))
|
|
return;
|
|
if (!cl)
|
|
return;
|
|
if (active_popup_client != NULL) // recursion
|
|
return;
|
|
if (cl->isDesktop()
|
|
|| cl->isDock())
|
|
return;
|
|
|
|
active_popup_client = cl;
|
|
QMenu* p = clientPopup();
|
|
active_popup = p;
|
|
int x = pos.left();
|
|
int y = pos.bottom();
|
|
if (y == pos.top())
|
|
p->exec(QPoint(x, y));
|
|
else {
|
|
QRect area = clientArea(ScreenArea, QPoint(x, y), currentDesktop());
|
|
clientPopupAboutToShow(); // needed for sizeHint() to be correct :-/
|
|
int popupHeight = p->sizeHint().height();
|
|
if (y + popupHeight < area.height())
|
|
p->exec(QPoint(x, y));
|
|
else
|
|
p->exec(QPoint(x, pos.top() - popupHeight));
|
|
}
|
|
// active popup may be already changed (e.g. the window shortcut dialog)
|
|
if (active_popup == p)
|
|
closeActivePopup();
|
|
}
|
|
|
|
/*!
|
|
Closes the popup client
|
|
*/
|
|
void Workspace::slotWindowClose()
|
|
{
|
|
// TODO: why?
|
|
// if ( tab_box->isVisible())
|
|
// return;
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::CloseOp);
|
|
}
|
|
|
|
/*!
|
|
Starts keyboard move mode for the popup client
|
|
*/
|
|
void Workspace::slotWindowMove()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::UnrestrictedMoveOp);
|
|
}
|
|
|
|
/*!
|
|
Starts keyboard resize mode for the popup client
|
|
*/
|
|
void Workspace::slotWindowResize()
|
|
{
|
|
if (USABLE_ACTIVE_CLIENT)
|
|
performWindowOperation(active_client, Options::UnrestrictedResizeOp);
|
|
}
|
|
|
|
void Workspace::slotInvertScreen()
|
|
{
|
|
bool succeeded = false;
|
|
|
|
//BEGIN Xrandr inversion - does atm NOT work with the nvidia blob
|
|
XRRScreenResources *res = XRRGetScreenResources(display(), active_client ? active_client->window() : rootWindow());
|
|
if (res) {
|
|
for (int j = 0; j < res->ncrtc; ++j) {
|
|
XRRCrtcGamma *gamma = XRRGetCrtcGamma(display(), res->crtcs[j]);
|
|
if (gamma && gamma->size) {
|
|
kDebug(1212) << "inverting screen using XRRSetCrtcGamma";
|
|
const int half = gamma->size / 2 + 1;
|
|
unsigned short swap;
|
|
for (int i = 0; i < half; ++i) {
|
|
#define INVERT(_C_) swap = gamma->_C_[i]; gamma->_C_[i] = gamma->_C_[gamma->size - 1 - i]; gamma->_C_[gamma->size - 1 - i] = swap
|
|
INVERT(red);
|
|
INVERT(green);
|
|
INVERT(blue);
|
|
#undef INVERT
|
|
}
|
|
XRRSetCrtcGamma(display(), res->crtcs[j], gamma);
|
|
XRRFreeGamma(gamma);
|
|
succeeded = true;
|
|
}
|
|
}
|
|
XRRFreeScreenResources(res);
|
|
}
|
|
if (succeeded)
|
|
return;
|
|
|
|
//BEGIN XF86VidMode inversion - only works if optionally libXxf86vm is linked
|
|
#ifndef KWIN_NO_XF86VM
|
|
int size = 0;
|
|
// TODO: this doesn't work with screen numbers in twinview - probably relevant only for multihead?
|
|
const int scrn = 0; // active_screen
|
|
if (XF86VidModeGetGammaRampSize(display(), scrn, &size)) {
|
|
unsigned short *red, *green, *blue;
|
|
red = new unsigned short[size];
|
|
green = new unsigned short[size];
|
|
blue = new unsigned short[size];
|
|
if (XF86VidModeGetGammaRamp(display(), scrn, size, red, green, blue)) {
|
|
kDebug(1212) << "inverting screen using XF86VidModeSetGammaRamp";
|
|
const int half = size / 2 + 1;
|
|
unsigned short swap;
|
|
for (int i = 0; i < half; ++i) {
|
|
swap = red[i]; red[i] = red[size - 1 - i]; red[size - 1 - i] = swap;
|
|
swap = green[i]; green[i] = green[size - 1 - i]; green[size - 1 - i] = swap;
|
|
swap = blue[i]; blue[i] = blue[size - 1 - i]; blue[size - 1 - i] = swap;
|
|
}
|
|
XF86VidModeSetGammaRamp(display(), scrn, size, red, green, blue);
|
|
succeeded = true;
|
|
}
|
|
delete [] red;
|
|
delete [] green;
|
|
delete [] blue;
|
|
}
|
|
|
|
if (succeeded)
|
|
return;
|
|
#endif
|
|
|
|
//BEGIN effect plugin inversion - atm only works with OpenGL and has an overhead to it
|
|
if (effects) {
|
|
if (Effect *inverter = static_cast<EffectsHandlerImpl*>(effects)->provides(Effect::ScreenInversion)) {
|
|
kDebug(1212) << "inverting screen using Effect plugin";
|
|
QMetaObject::invokeMethod(inverter, "toggleScreenInversion", Qt::DirectConnection);
|
|
}
|
|
}
|
|
|
|
if (!succeeded)
|
|
kDebug(1212) << "sorry - neither Xrandr, nor XF86VidModeSetGammaRamp worked and there's no inversion supplying effect plugin either";
|
|
|
|
}
|
|
|
|
#undef USABLE_ACTIVE_CLIENT
|
|
|
|
void Client::setShortcut(const QString& _cut)
|
|
{
|
|
QString cut = rules()->checkShortcut(_cut);
|
|
if (cut.isEmpty())
|
|
return setShortcutInternal(KShortcut());
|
|
// Format:
|
|
// base+(abcdef)<space>base+(abcdef)
|
|
// E.g. Alt+Ctrl+(ABCDEF) Win+X,Win+(ABCDEF)
|
|
if (!cut.contains('(') && !cut.contains(')') && !cut.contains(' ')) {
|
|
if (workspace()->shortcutAvailable(KShortcut(cut), this))
|
|
setShortcutInternal(KShortcut(cut));
|
|
else
|
|
setShortcutInternal(KShortcut());
|
|
return;
|
|
}
|
|
QList< KShortcut > keys;
|
|
QStringList groups = cut.split(' ');
|
|
for (QStringList::ConstIterator it = groups.constBegin();
|
|
it != groups.constEnd();
|
|
++it) {
|
|
QRegExp reg("(.*\\+)\\((.*)\\)");
|
|
if (reg.indexIn(*it) > -1) {
|
|
QString base = reg.cap(1);
|
|
QString list = reg.cap(2);
|
|
for (int i = 0;
|
|
i < list.length();
|
|
++i) {
|
|
KShortcut c(base + list[ i ]);
|
|
if (!c.isEmpty())
|
|
keys.append(c);
|
|
}
|
|
}
|
|
}
|
|
for (QList< KShortcut >::ConstIterator it = keys.constBegin();
|
|
it != keys.constEnd();
|
|
++it) {
|
|
if (_shortcut == *it) // current one is in the list
|
|
return;
|
|
}
|
|
for (QList< KShortcut >::ConstIterator it = keys.constBegin();
|
|
it != keys.constEnd();
|
|
++it) {
|
|
if (workspace()->shortcutAvailable(*it, this)) {
|
|
setShortcutInternal(*it);
|
|
return;
|
|
}
|
|
}
|
|
setShortcutInternal(KShortcut());
|
|
}
|
|
|
|
void Client::setShortcutInternal(const KShortcut& cut)
|
|
{
|
|
if (_shortcut == cut)
|
|
return;
|
|
_shortcut = cut;
|
|
updateCaption();
|
|
#if 0
|
|
workspace()->clientShortcutUpdated(this);
|
|
#else
|
|
// Workaround for kwin<->kglobalaccel deadlock, when KWin has X grab and the kded
|
|
// kglobalaccel module tries to create the key grab. KWin should preferably grab
|
|
// they keys itself anyway :(.
|
|
QTimer::singleShot(0, this, SLOT(delayedSetShortcut()));
|
|
#endif
|
|
}
|
|
|
|
void Client::delayedSetShortcut()
|
|
{
|
|
workspace()->clientShortcutUpdated(this);
|
|
}
|
|
|
|
bool Workspace::shortcutAvailable(const KShortcut& cut, Client* ignore) const
|
|
{
|
|
// TODO check global shortcuts etc.
|
|
for (ClientList::ConstIterator it = clients.constBegin();
|
|
it != clients.constEnd();
|
|
++it) {
|
|
if ((*it) != ignore && (*it)->shortcut() == cut)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|