1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>.
20*********************************************************************/
21
22/*
23
24 This file contains things relevant to direct user actions, such as
25 responses to global keyboard shortcuts, or selecting actions
26 from the window operations menu.
27
28*/
29
30///////////////////////////////////////////////////////////////////////////////
31// NOTE: if you change the menu, keep kde-workspace/libs/taskmanager/taskactions.cpp in sync
32//////////////////////////////////////////////////////////////////////////////
33
34#include "useractions.h"
35#include "cursor.h"
36#include "client.h"
37#include "decorations.h"
38#include "workspace.h"
39#include "effects.h"
40#include "screens.h"
41#include "virtualdesktops.h"
42
43#ifdef KWIN_BUILD_SCRIPTING
44#include "scripting/scripting.h"
45#endif
46
47#ifdef KWIN_BUILD_ACTIVITIES
48#include "activities.h"
49#include <KActivities/Info>
50#endif
51
52#include <KDE/KKeySequenceWidget>
53#include <KDE/KProcess>
54#include <KDE/KToolInvocation>
55
56#include <X11/extensions/Xrandr.h>
57#ifndef KWIN_NO_XF86VM
58#include <X11/extensions/xf86vmode.h>
59#endif
60#include <fixx11h.h>
61#include <QCheckBox>
62#include <QPointer>
63#include <QPushButton>
64
65#include <kglobalsettings.h>
66#include <KDE/KIcon>
67#include <kiconloader.h>
68#include <KDE/KLocalizedString>
69#include <kconfig.h>
70#include <KDE/KGlobal>
71#include <KDE/KPushButton>
72#include <kglobalaccel.h>
73#include <kapplication.h>
74#include <QRegExp>
75#include <QLabel>
76#include <QMenu>
77#include <QVBoxLayout>
78#include <kauthorized.h>
79#include <kactioncollection.h>
80#include <kaction.h>
81
82#include "killwindow.h"
83#ifdef KWIN_BUILD_TABBOX
84#include "tabbox.h"
85#endif
86
87namespace KWin
88{
89
90UserActionsMenu::UserActionsMenu(QObject *parent)
91 : QObject(parent)
92 , m_menu(NULL)
93 , m_desktopMenu(NULL)
94 , m_screenMenu(NULL)
95 , m_activityMenu(NULL)
96 , m_addTabsMenu(NULL)
97 , m_switchToTabMenu(NULL)
98#ifdef KWIN_BUILD_SCRIPTING
99 , m_scriptsMenu(NULL)
100#endif
101 , m_resizeOperation(NULL)
102 , m_moveOperation(NULL)
103 , m_maximizeOperation(NULL)
104 , m_shadeOperation(NULL)
105 , m_keepAboveOperation(NULL)
106 , m_keepBelowOperation(NULL)
107 , m_fullScreenOperation(NULL)
108 , m_noBorderOperation(NULL)
109 , m_minimizeOperation(NULL)
110 , m_closeOperation(NULL)
111 , m_removeFromTabGroup(NULL)
112 , m_closeTabGroup(NULL)
113 , m_client(QWeakPointer<Client>())
114{
115}
116
117UserActionsMenu::~UserActionsMenu()
118{
119 discard();
120}
121
122bool UserActionsMenu::isShown() const
123{
124 return m_menu && m_menu->isVisible();
125}
126
127bool UserActionsMenu::hasClient() const
128{
129 return !m_client.isNull() && isShown();
130}
131
132void UserActionsMenu::close()
133{
134 if (!m_menu) {
135 return;
136 }
137 m_menu->close();
138 m_client.clear();
139}
140
141bool UserActionsMenu::isMenuClient(const Client *c) const
142{
143 if (!c || m_client.isNull()) {
144 return false;
145 }
146 return c == m_client.data();
147}
148
149void UserActionsMenu::show(const QRect &pos, const QWeakPointer<Client> &cl)
150{
151 if (!KAuthorized::authorizeKAction("kwin_rmb"))
152 return;
153 if (cl.isNull())
154 return;
155 if (isShown()) // recursion
156 return;
157 if (cl.data()->isDesktop()
158 || cl.data()->isDock())
159 return;
160
161 m_client = cl;
162 init();
163 Workspace *ws = Workspace::self();
164 int x = pos.left();
165 int y = pos.bottom();
166 if (y == pos.top()) {
167 m_client.data()->blockActivityUpdates(true);
168 m_menu->exec(QPoint(x, y));
169 if (!m_client.isNull())
170 m_client.data()->blockActivityUpdates(false);
171 }
172 else {
173 m_client.data()->blockActivityUpdates(true);
174 QRect area = ws->clientArea(ScreenArea, QPoint(x, y), VirtualDesktopManager::self()->current());
175 menuAboutToShow(); // needed for sizeHint() to be correct :-/
176 int popupHeight = m_menu->sizeHint().height();
177 if (y + popupHeight < area.height())
178 m_menu->exec(QPoint(x, y));
179 else
180 m_menu->exec(QPoint(x, pos.top() - popupHeight));
181 if (!m_client.isNull())
182 m_client.data()->blockActivityUpdates(true);
183 }
184}
185
186void UserActionsMenu::helperDialog(const QString& message, const QWeakPointer<Client> &c)
187{
188 QStringList args;
189 QString type;
190 KActionCollection *keys = Workspace::self()->actionCollection();
191 if (message == "noborderaltf3") {
192 KAction* action = qobject_cast<KAction*>(keys->action("Window Operations Menu"));
193 assert(action != NULL);
194 QString shortcut = QString("%1 (%2)").arg(action->text())
195 .arg(action->globalShortcut().primary().toString(QKeySequence::NativeText));
196 args << "--msgbox" << i18n(
197 "You have selected to show a window without its border.\n"
198 "Without the border, you will not be able to enable the border "
199 "again using the mouse: use the window operations menu instead, "
200 "activated using the %1 keyboard shortcut.",
201 shortcut);
202 type = "altf3warning";
203 } else if (message == "fullscreenaltf3") {
204 KAction* action = qobject_cast<KAction*>(keys->action("Window Operations Menu"));
205 assert(action != NULL);
206 QString shortcut = QString("%1 (%2)").arg(action->text())
207 .arg(action->globalShortcut().primary().toString(QKeySequence::NativeText));
208 args << "--msgbox" << i18n(
209 "You have selected to show a window in fullscreen mode.\n"
210 "If the application itself does not have an option to turn the fullscreen "
211 "mode off you will not be able to disable it "
212 "again using the mouse: use the window operations menu instead, "
213 "activated using the %1 keyboard shortcut.",
214 shortcut);
215 type = "altf3warning";
216 } else
217 abort();
218 if (!type.isEmpty()) {
219 KConfig cfg("kwin_dialogsrc");
220 KConfigGroup cg(&cfg, "Notification Messages"); // Depends on KMessageBox
221 if (!cg.readEntry(type, true))
222 return;
223 args << "--dontagain" << "kwin_dialogsrc:" + type;
224 }
225 if (!c.isNull())
226 args << "--embed" << QString::number(c.data()->window());
227 KProcess::startDetached("kdialog", args);
228}
229
230
231QStringList configModules(bool controlCenter)
232{
233 QStringList args;
234 args << "kwindecoration";
235 if (controlCenter)
236 args << "kwinoptions";
237 else if (KAuthorized::authorizeControlModule("kde-kwinoptions.desktop"))
238 args << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced"
239 << "kwinrules" << "kwincompositing"
240#ifdef KWIN_BUILD_TABBOX
241 << "kwintabbox"
242#endif
243#ifdef KWIN_BUILD_SCREENEDGES
244 << "kwinscreenedges"
245#endif
246#ifdef KWIN_BUILD_SCRIPTING
247 << "kwinscripts"
248#endif
249 ;
250 return args;
251}
252
253void UserActionsMenu::configureWM()
254{
255 QStringList args;
256 args << "--icon" << "preferences-system-windows" << configModules(false);
257 KToolInvocation::kdeinitExec("kcmshell4", args);
258}
259
260void UserActionsMenu::init()
261{
262 if (m_menu) {
263 return;
264 }
265 m_menu = new QMenu;
266 m_menu->setFont(KGlobalSettings::menuFont());
267 connect(m_menu, SIGNAL(aboutToShow()), this, SLOT(menuAboutToShow()));
268 connect(m_menu, SIGNAL(triggered(QAction*)), this, SLOT(slotWindowOperation(QAction*)), Qt::QueuedConnection);
269
270 QMenu *advancedMenu = new QMenu(m_menu);
271 advancedMenu->setFont(KGlobalSettings::menuFont());
272
273 m_moveOperation = advancedMenu->addAction(i18n("&Move"));
274 m_moveOperation->setIcon(KIcon("transform-move"));
275 KActionCollection *keys = Workspace::self()->actionCollection();
276 KAction *kaction = qobject_cast<KAction*>(keys->action("Window Move"));
277 if (kaction != 0)
278 m_moveOperation->setShortcut(kaction->globalShortcut().primary());
279 m_moveOperation->setData(Options::UnrestrictedMoveOp);
280
281 m_resizeOperation = advancedMenu->addAction(i18n("Re&size"));
282 kaction = qobject_cast<KAction*>(keys->action("Window Resize"));
283 if (kaction != 0)
284 m_resizeOperation->setShortcut(kaction->globalShortcut().primary());
285 m_resizeOperation->setData(Options::ResizeOp);
286
287 m_keepAboveOperation = advancedMenu->addAction(i18n("Keep &Above Others"));
288 m_keepAboveOperation->setIcon(KIcon("go-up"));
289 kaction = qobject_cast<KAction*>(keys->action("Window Above Other Windows"));
290 if (kaction != 0)
291 m_keepAboveOperation->setShortcut(kaction->globalShortcut().primary());
292 m_keepAboveOperation->setCheckable(true);
293 m_keepAboveOperation->setData(Options::KeepAboveOp);
294
295 m_keepBelowOperation = advancedMenu->addAction(i18n("Keep &Below Others"));
296 m_keepBelowOperation->setIcon(KIcon("go-down"));
297 kaction = qobject_cast<KAction*>(keys->action("Window Below Other Windows"));
298 if (kaction != 0)
299 m_keepBelowOperation->setShortcut(kaction->globalShortcut().primary());
300 m_keepBelowOperation->setCheckable(true);
301 m_keepBelowOperation->setData(Options::KeepBelowOp);
302
303 m_fullScreenOperation = advancedMenu->addAction(i18n("&Fullscreen"));
304 m_fullScreenOperation->setIcon(KIcon("view-fullscreen"));
305 kaction = qobject_cast<KAction*>(keys->action("Window Fullscreen"));
306 if (kaction != 0)
307 m_fullScreenOperation->setShortcut(kaction->globalShortcut().primary());
308 m_fullScreenOperation->setCheckable(true);
309 m_fullScreenOperation->setData(Options::FullScreenOp);
310
311 m_shadeOperation = advancedMenu->addAction(i18n("Sh&ade"));
312 kaction = qobject_cast<KAction*>(keys->action("Window Shade"));
313 if (kaction != 0)
314 m_shadeOperation->setShortcut(kaction->globalShortcut().primary());
315 m_shadeOperation->setCheckable(true);
316 m_shadeOperation->setData(Options::ShadeOp);
317
318 m_noBorderOperation = advancedMenu->addAction(i18n("&No Border"));
319 kaction = qobject_cast<KAction*>(keys->action("Window No Border"));
320 if (kaction != 0)
321 m_noBorderOperation->setShortcut(kaction->globalShortcut().primary());
322 m_noBorderOperation->setCheckable(true);
323 m_noBorderOperation->setData(Options::NoBorderOp);
324
325 advancedMenu->addSeparator();
326
327 QAction *action = advancedMenu->addAction(i18n("Window &Shortcut..."));
328 action->setIcon(KIcon("configure-shortcuts"));
329 kaction = qobject_cast<KAction*>(keys->action("Setup Window Shortcut"));
330 if (kaction != 0)
331 action->setShortcut(kaction->globalShortcut().primary());
332 action->setData(Options::SetupWindowShortcutOp);
333
334 action = advancedMenu->addAction(i18n("&Special Window Settings..."));
335 action->setIcon(KIcon("preferences-system-windows-actions"));
336 action->setData(Options::WindowRulesOp);
337
338 action = advancedMenu->addAction(i18n("S&pecial Application Settings..."));
339 action->setIcon(KIcon("preferences-system-windows-actions"));
340 action->setData(Options::ApplicationRulesOp);
341 if (!KGlobal::config()->isImmutable() &&
342 !KAuthorized::authorizeControlModules(configModules(true)).isEmpty()) {
343 advancedMenu->addSeparator();
344 action = advancedMenu->addAction(i18nc("Entry in context menu of window decoration to open the configuration module of KWin",
345 "Window &Manager Settings..."));
346 action->setIcon(KIcon("configure"));
347 connect(action, SIGNAL(triggered()), this, SLOT(configureWM()));
348 }
349
350 m_minimizeOperation = m_menu->addAction(i18n("Mi&nimize"));
351 kaction = qobject_cast<KAction*>(keys->action("Window Minimize"));
352 if (kaction != 0)
353 m_minimizeOperation->setShortcut(kaction->globalShortcut().primary());
354 m_minimizeOperation->setData(Options::MinimizeOp);
355
356 m_maximizeOperation = m_menu->addAction(i18n("Ma&ximize"));
357 kaction = qobject_cast<KAction*>(keys->action("Window Maximize"));
358 if (kaction != 0)
359 m_maximizeOperation->setShortcut(kaction->globalShortcut().primary());
360 m_maximizeOperation->setCheckable(true);
361 m_maximizeOperation->setData(Options::MaximizeOp);
362
363 m_menu->addSeparator();
364
365 // Actions for window tabbing
366 if (decorationPlugin()->supportsTabbing()) {
367 m_removeFromTabGroup = m_menu->addAction(i18n("&Untab"));
368 kaction = qobject_cast<KAction*>(keys->action("Untab"));
369 if (kaction != 0)
370 m_removeFromTabGroup->setShortcut(kaction->globalShortcut().primary());
371 m_removeFromTabGroup->setData(Options::RemoveTabFromGroupOp);
372
373 m_closeTabGroup = m_menu->addAction(i18n("Close Entire &Group"));
374 m_closeTabGroup->setIcon(KIcon("window-close"));
375 kaction = qobject_cast<KAction*>(keys->action("Close TabGroup"));
376 if (kaction != 0)
377 m_closeTabGroup->setShortcut(kaction->globalShortcut().primary());
378 m_closeTabGroup->setData(Options::CloseTabGroupOp);
379
380 m_menu->addSeparator();
381 }
382
383 m_menu->addSeparator();
384
385 action = m_menu->addMenu(advancedMenu);
386 action->setText(i18n("&More Actions"));
387
388 m_menu->addSeparator();
389
390 m_closeOperation = m_menu->addAction(i18n("&Close"));
391 m_closeOperation->setIcon(KIcon("window-close"));
392 kaction = qobject_cast<KAction*>(keys->action("Window Close"));
393 if (kaction != 0)
394 m_closeOperation->setShortcut(kaction->globalShortcut().primary());
395 m_closeOperation->setData(Options::CloseOp);
396}
397
398void UserActionsMenu::discard()
399{
400 delete m_menu;
401 m_menu = NULL;
402 m_desktopMenu = NULL;
403 m_screenMenu = NULL;
404 m_activityMenu = NULL;
405 m_switchToTabMenu = NULL;
406 m_addTabsMenu = NULL;
407#ifdef KWIN_BUILD_SCRIPTING
408 m_scriptsMenu = NULL;
409#endif
410}
411
412void UserActionsMenu::menuAboutToShow()
413{
414 if (m_client.isNull() || !m_menu)
415 return;
416
417 if (VirtualDesktopManager::self()->count() == 1) {
418 delete m_desktopMenu;
419 m_desktopMenu = 0;
420 } else {
421 initDesktopPopup();
422 }
423 if (screens()->count() == 1 || (!m_client.data()->isMovable() && !m_client.data()->isMovableAcrossScreens())) {
424 delete m_screenMenu;
425 m_screenMenu = NULL;
426 } else {
427 initScreenPopup();
428 }
429#ifdef KWIN_BUILD_ACTIVITIES
430 Activities::self()->update(true, false, this, "showHideActivityMenu");
431#endif
432
433 m_resizeOperation->setEnabled(m_client.data()->isResizable());
434 m_moveOperation->setEnabled(m_client.data()->isMovableAcrossScreens());
435 m_maximizeOperation->setEnabled(m_client.data()->isMaximizable());
436 m_maximizeOperation->setChecked(m_client.data()->maximizeMode() == Client::MaximizeFull);
437 m_shadeOperation->setEnabled(m_client.data()->isShadeable());
438 m_shadeOperation->setChecked(m_client.data()->shadeMode() != ShadeNone);
439 m_keepAboveOperation->setChecked(m_client.data()->keepAbove());
440 m_keepBelowOperation->setChecked(m_client.data()->keepBelow());
441 m_fullScreenOperation->setEnabled(m_client.data()->userCanSetFullScreen());
442 m_fullScreenOperation->setChecked(m_client.data()->isFullScreen());
443 m_noBorderOperation->setEnabled(m_client.data()->userCanSetNoBorder());
444 m_noBorderOperation->setChecked(m_client.data()->noBorder());
445 m_minimizeOperation->setEnabled(m_client.data()->isMinimizable());
446 m_closeOperation->setEnabled(m_client.data()->isCloseable());
447
448 if (decorationPlugin()->supportsTabbing()) {
449 initTabbingPopups();
450 } else {
451 delete m_addTabsMenu;
452 m_addTabsMenu = 0;
453 }
454
455#ifdef KWIN_BUILD_SCRIPTING
456 // drop the existing scripts menu
457 delete m_scriptsMenu;
458 m_scriptsMenu = NULL;
459 // ask scripts whether they want to add entries for the given Client
460 m_scriptsMenu = new QMenu(m_menu);
461 QList<QAction*> scriptActions = Scripting::self()->actionsForUserActionMenu(m_client.data(), m_scriptsMenu);
462 if (!scriptActions.isEmpty()) {
463 m_scriptsMenu->setFont(KGlobalSettings::menuFont());
464 m_scriptsMenu->addActions(scriptActions);
465
466 QAction *action = m_scriptsMenu->menuAction();
467 // set it as the first item after desktop
468 m_menu->insertAction(m_closeOperation, action);
469 action->setText(i18n("&Extensions"));
470 } else {
471 delete m_scriptsMenu;
472 m_scriptsMenu = NULL;
473 }
474#endif
475}
476
477void UserActionsMenu::showHideActivityMenu()
478{
479#ifdef KWIN_BUILD_ACTIVITIES
480 const QStringList &openActivities_ = Activities::self()->running();
481 kDebug() << "activities:" << openActivities_.size();
482 if (openActivities_.size() < 2) {
483 delete m_activityMenu;
484 m_activityMenu = 0;
485 } else {
486 initActivityPopup();
487 }
488#endif
489}
490
491void UserActionsMenu::selectPopupClientTab(QAction* action)
492{
493 if (!(!m_client.isNull() && m_client.data()->tabGroup()) || !action->data().isValid())
494 return;
495
496 if (Client *other = action->data().value<Client*>()) {
497 m_client.data()->tabGroup()->setCurrent(other);
498 return;
499 }
500
501 // failed conversion, try "1" & "2", being prev and next
502 int direction = action->data().toInt();
503 if (direction == 1)
504 m_client.data()->tabGroup()->activatePrev();
505 else if (direction == 2)
506 m_client.data()->tabGroup()->activateNext();
507}
508
509static QString shortCaption(const QString &s)
510{
511 if (s.length() < 64)
512 return s;
513 QString ss = s;
514 return ss.replace(32,s.length()-64,"...");
515}
516
517void UserActionsMenu::rebuildTabListPopup()
518{
519 Q_ASSERT(m_switchToTabMenu);
520
521 m_switchToTabMenu->clear();
522 // whatever happens "0x1" and "0x2" are no heap positions ;-)
523 m_switchToTabMenu->addAction(i18nc("Switch to tab -> Previous", "Previous"))->setData(1);
524 m_switchToTabMenu->addAction(i18nc("Switch to tab -> Next", "Next"))->setData(2);
525
526 m_switchToTabMenu->addSeparator();
527
528 for (QList<Client*>::const_iterator i = m_client.data()->tabGroup()->clients().constBegin(),
529 end = m_client.data()->tabGroup()->clients().constEnd(); i != end; ++i) {
530 if ((*i)->noBorder() || *i == m_client.data()->tabGroup()->current())
531 continue; // cannot tab there anyway
532 m_switchToTabMenu->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i));
533 }
534
535}
536
537void UserActionsMenu::entabPopupClient(QAction* action)
538{
539 if (m_client.isNull() || !action->data().isValid())
540 return;
541 Client *other = action->data().value<Client*>();
542 if (!Workspace::self()->clientList().contains(other)) // might have been lost betwenn pop-up and selection
543 return;
544 m_client.data()->tabBehind(other, true);
545 if (options->focusPolicyIsReasonable())
546 Workspace::self()->requestFocus(m_client.data());
547}
548
549void UserActionsMenu::rebuildTabGroupPopup()
550{
551 Q_ASSERT(m_addTabsMenu);
552
553 m_addTabsMenu->clear();
554 QList<Client*> handled;
555 const ClientList &clientList = Workspace::self()->clientList();
556 for (QList<Client*>::const_iterator i = clientList.constBegin(), end = clientList.constEnd(); i != end; ++i) {
557 if (*i == m_client.data() || (*i)->noBorder())
558 continue;
559 m_addTabsMenu->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i));
560 }
561 if (m_addTabsMenu->actions().isEmpty())
562 m_addTabsMenu->addAction(i18nc("There's no window available to be attached as tab to this one", "None available"))->setEnabled(false);
563}
564
565void UserActionsMenu::initTabbingPopups()
566{
567 bool needTabManagers = false;
568 if (m_client.data()->tabGroup() && m_client.data()->tabGroup()->count() > 1) {
569 needTabManagers = true;
570 if (!m_switchToTabMenu) {
571 m_switchToTabMenu = new QMenu(i18n("Switch to Tab"), m_menu);
572 m_switchToTabMenu->setFont(KGlobalSettings::menuFont());
573 connect(m_switchToTabMenu, SIGNAL(triggered(QAction*)), SLOT(selectPopupClientTab(QAction*)));
574 connect(m_switchToTabMenu, SIGNAL(aboutToShow()), SLOT(rebuildTabListPopup()));
575 m_menu->insertMenu(m_removeFromTabGroup, m_switchToTabMenu);
576 }
577 } else {
578 delete m_switchToTabMenu;
579 m_switchToTabMenu = 0;
580 }
581
582 if (!m_addTabsMenu) {
583 m_addTabsMenu = new QMenu(i18n("&Attach as tab to"), m_menu);
584 m_addTabsMenu->setFont(KGlobalSettings::menuFont());
585 connect(m_addTabsMenu, SIGNAL(triggered(QAction*)), SLOT(entabPopupClient(QAction*)));
586 connect(m_addTabsMenu, SIGNAL(aboutToShow()), SLOT(rebuildTabGroupPopup()));
587 m_menu->insertMenu(m_removeFromTabGroup, m_addTabsMenu);
588 }
589
590 m_addTabsMenu->menuAction()->setEnabled(!m_client.data()->isFullScreen());
591 m_removeFromTabGroup->setVisible(needTabManagers);
592 m_closeTabGroup->setVisible(needTabManagers);
593}
594
595void UserActionsMenu::initDesktopPopup()
596{
597 if (m_desktopMenu)
598 return;
599
600 m_desktopMenu = new QMenu(m_menu);
601 m_desktopMenu->setFont(KGlobalSettings::menuFont());
602 connect(m_desktopMenu, SIGNAL(triggered(QAction*)), SLOT(slotSendToDesktop(QAction*)));
603 connect(m_desktopMenu, SIGNAL(aboutToShow()), SLOT(desktopPopupAboutToShow()));
604
605 QAction *action = m_desktopMenu->menuAction();
606 // set it as the first item
607 m_menu->insertAction(m_minimizeOperation, action);
608 action->setText(i18n("Move To &Desktop"));
609}
610
611void UserActionsMenu::initScreenPopup()
612{
613 if (m_screenMenu) {
614 return;
615 }
616
617 m_screenMenu = new QMenu(m_menu);
618 m_screenMenu->setFont(KGlobalSettings::menuFont());
619 connect(m_screenMenu, SIGNAL(triggered(QAction*)), SLOT(slotSendToScreen(QAction*)));
620 connect(m_screenMenu, SIGNAL(aboutToShow()), SLOT(screenPopupAboutToShow()));
621
622 QAction *action = m_screenMenu->menuAction();
623 // set it as the first item after desktop
624 m_menu->insertAction(m_activityMenu ? m_activityMenu->menuAction() : m_minimizeOperation, action);
625 action->setText(i18n("Move To &Screen"));
626}
627
628void UserActionsMenu::initActivityPopup()
629{
630 if (m_activityMenu)
631 return;
632
633 m_activityMenu = new QMenu(m_menu);
634 m_activityMenu->setFont(KGlobalSettings::menuFont());
635 connect(m_activityMenu, SIGNAL(triggered(QAction*)),
636 this, SLOT(slotToggleOnActivity(QAction*)));
637 connect(m_activityMenu, SIGNAL(aboutToShow()),
638 this, SLOT(activityPopupAboutToShow()));
639
640 QAction *action = m_activityMenu->menuAction();
641 // set it as the first item
642 m_menu->insertAction(m_minimizeOperation, action);
643 action->setText(i18n("Ac&tivities")); //FIXME is that a good string?
644}
645
646void UserActionsMenu::desktopPopupAboutToShow()
647{
648 if (!m_desktopMenu)
649 return;
650 const VirtualDesktopManager *vds = VirtualDesktopManager::self();
651
652 m_desktopMenu->clear();
653 QActionGroup *group = new QActionGroup(m_desktopMenu);
654 QAction *action = m_desktopMenu->addAction(i18n("&All Desktops"));
655 action->setData(0);
656 action->setCheckable(true);
657 group->addAction(action);
658
659 if (!m_client.isNull() && m_client.data()->isOnAllDesktops())
660 action->setChecked(true);
661 m_desktopMenu->addSeparator();
662
663 const uint BASE = 10;
664 for (uint i = 1; i <= vds->count(); ++i) {
665 QString basic_name("%1 %2");
666 if (i < BASE) {
667 basic_name.prepend('&');
668 }
669 action = m_desktopMenu->addAction(basic_name.arg(i).arg(vds->name(i).replace('&', "&&")));
670 action->setData(i);
671 action->setCheckable(true);
672 group->addAction(action);
673
674 if (!m_client.isNull() &&
675 !m_client.data()->isOnAllDesktops() && m_client.data()->isOnDesktop(i))
676 action->setChecked(true);
677 }
678
679 m_desktopMenu->addSeparator();
680 action = m_desktopMenu->addAction(i18nc("Create a new desktop and move there the window", "&New Desktop"));
681 action->setData(vds->count() + 1);
682
683 if (vds->count() >= vds->maximum())
684 action->setEnabled(false);
685}
686
687void UserActionsMenu::screenPopupAboutToShow()
688{
689 if (!m_screenMenu) {
690 return;
691 }
692
693 m_screenMenu->clear();
694 QActionGroup *group = new QActionGroup(m_screenMenu);
695
696 for (int i = 0; i<screens()->count(); ++i) {
697 // TODO: retrieve the screen name?
698 // assumption: there are not more than 9 screens attached.
699 QAction *action = m_screenMenu->addAction(i18nc("@item:inmenu List of all Screens to send a window to",
700 "Screen &%1", (i+1)));
701 action->setData(i);
702 action->setCheckable(true);
703 if (!m_client.isNull() && i == m_client.data()->screen()) {
704 action->setChecked(true);
705 }
706 group->addAction(action);
707 }
708}
709
710void UserActionsMenu::activityPopupAboutToShow()
711{
712 if (!m_activityMenu)
713 return;
714
715#ifdef KWIN_BUILD_ACTIVITIES
716 m_activityMenu->clear();
717 QAction *action = m_activityMenu->addAction(i18n("&All Activities"));
718 action->setData(QString());
719 action->setCheckable(true);
720 static QPointer<QActionGroup> allActivitiesGroup;
721 if (!allActivitiesGroup) {
722 allActivitiesGroup = new QActionGroup(m_activityMenu);
723 }
724 allActivitiesGroup->addAction(action);
725
726 if (!m_client.isNull() && m_client.data()->isOnAllActivities())
727 action->setChecked(true);
728 m_activityMenu->addSeparator();
729
730 foreach (const QString &id, Activities::self()->running()) {
731 KActivities::Info activity(id);
732 QString name = activity.name();
733 name.replace('&', "&&");
734 QWidgetAction *action = new QWidgetAction(m_activityMenu);
735 QCheckBox *box = new QCheckBox(name, m_activityMenu);
736 action->setDefaultWidget(box);
737 const QString icon = activity.icon();
738 if (!icon.isEmpty())
739 box->setIcon(KIcon(icon));
740 box->setBackgroundRole(m_activityMenu->backgroundRole());
741 box->setForegroundRole(m_activityMenu->foregroundRole());
742 box->setPalette(m_activityMenu->palette());
743 connect (box, SIGNAL(clicked(bool)), action, SIGNAL(triggered(bool)));
744 m_activityMenu->addAction(action);
745 action->setData(id);
746
747 if (!m_client.isNull() &&
748 !m_client.data()->isOnAllActivities() && m_client.data()->isOnActivity(id))
749 box->setChecked(true);
750 }
751#endif
752}
753
754void UserActionsMenu::slotWindowOperation(QAction *action)
755{
756 if (!action->data().isValid())
757 return;
758
759 Options::WindowOperation op = static_cast< Options::WindowOperation >(action->data().toInt());
760 QWeakPointer<Client> c = (!m_client.isNull()) ? m_client : QWeakPointer<Client>(Workspace::self()->activeClient());
761 if (c.isNull())
762 return;
763 QString type;
764 switch(op) {
765 case Options::FullScreenOp:
766 if (!c.data()->isFullScreen() && c.data()->userCanSetFullScreen())
767 type = "fullscreenaltf3";
768 break;
769 case Options::NoBorderOp:
770 if (!c.data()->noBorder() && c.data()->userCanSetNoBorder())
771 type = "noborderaltf3";
772 break;
773 default:
774 break;
775 };
776 if (!type.isEmpty())
777 helperDialog(type, c);
778 Workspace::self()->performWindowOperation(c.data(), op);
779}
780
781void UserActionsMenu::slotSendToDesktop(QAction *action)
782{
783 bool ok = false;
784 uint desk = action->data().toUInt(&ok);
785 if (!ok) {
786 return;
787 }
788 if (m_client.isNull())
789 return;
790 Workspace *ws = Workspace::self();
791 VirtualDesktopManager *vds = VirtualDesktopManager::self();
792 if (desk == 0) {
793 // the 'on_all_desktops' menu entry
794 m_client.data()->setOnAllDesktops(!m_client.data()->isOnAllDesktops());
795 return;
796 } else if (desk > vds->count()) {
797 vds->setCount(desk);
798 }
799
800 ws->sendClientToDesktop(m_client.data(), desk, false);
801}
802
803void UserActionsMenu::slotSendToScreen(QAction *action)
804{
805 const int screen = action->data().toInt();
806 if (m_client.isNull()) {
807 return;
808 }
809 if (screen >= screens()->count()) {
810 return;
811 }
812
813 Workspace::self()->sendClientToScreen(m_client.data(), screen);
814}
815
816void UserActionsMenu::slotToggleOnActivity(QAction *action)
817{
818#ifdef KWIN_BUILD_ACTIVITIES
819 QString activity = action->data().toString();
820 if (m_client.isNull())
821 return;
822 if (activity.isEmpty()) {
823 // the 'on_all_activities' menu entry
824 m_client.data()->setOnAllActivities(!m_client.data()->isOnAllActivities());
825 return;
826 }
827
828 Activities::self()->toggleClientOnActivity(m_client.data(), activity, false);
829 if (m_activityMenu && m_activityMenu->isVisible() && m_activityMenu->actions().count()) {
830 const bool isOnAll = m_client.data()->isOnAllActivities();
831 m_activityMenu->actions().at(0)->setChecked(isOnAll);
832 if (isOnAll) {
833 // toggleClientOnActivity interprets "on all" as "on none" and
834 // susequent toggling ("off") would move the client to only that activity.
835 // bug #330838 -> set all but "on all" off to "force proper usage"
836 for (int i = 1; i < m_activityMenu->actions().count(); ++i) {
837 if (QWidgetAction *qwa = qobject_cast<QWidgetAction*>(m_activityMenu->actions().at(i))) {
838 if (QCheckBox *qcb = qobject_cast<QCheckBox*>(qwa->defaultWidget())) {
839 qcb->setChecked(false);
840 }
841 }
842 }
843 }
844 }
845#endif
846}
847
848//****************************************
849// ShortcutDialog
850//****************************************
851ShortcutDialog::ShortcutDialog(const QKeySequence& cut)
852 : _shortcut(cut)
853{
854 QWidget *vBoxContainer = new QWidget(this);
855 vBoxContainer->setLayout(new QVBoxLayout(vBoxContainer));
856 vBoxContainer->layout()->addWidget(widget = new KKeySequenceWidget(vBoxContainer));
857 vBoxContainer->layout()->addWidget(warning = new QLabel(vBoxContainer));
858 warning->hide();
859 widget->setKeySequence(cut);
860
861 // To not check for conflicting shortcuts. The widget would use a message
862 // box which brings down kwin.
863 widget->setCheckForConflictsAgainst(KKeySequenceWidget::None);
864 // It's a global shortcut so don't allow multikey shortcuts
865 widget->setMultiKeyShortcutsAllowed(false);
866
867 // Listen to changed shortcuts
868 connect(
869 widget, SIGNAL(keySequenceChanged(QKeySequence)),
870 SLOT(keySequenceChanged(QKeySequence)));
871
872 setMainWidget(vBoxContainer);
873 widget->setFocus();
874
875 // make it a popup, so that it has the grab
876 XSetWindowAttributes attrs;
877 attrs.override_redirect = True;
878 XChangeWindowAttributes(display(), winId(), CWOverrideRedirect, &attrs);
879 setWindowFlags(Qt::Popup);
880}
881
882void ShortcutDialog::accept()
883{
884 QKeySequence seq = shortcut();
885 if (!seq.isEmpty()) {
886 if (seq[0] == Qt::Key_Escape) {
887 reject();
888 return;
889 }
890 if (seq[0] == Qt::Key_Space
891 || (seq[0] & Qt::KeyboardModifierMask) == 0) {
892 // clear
893 widget->clearKeySequence();
894 KDialog::accept();
895 return;
896 }
897 }
898 KDialog::accept();
899}
900
901void ShortcutDialog::done(int r)
902{
903 KDialog::done(r);
904 emit dialogDone(r == Accepted);
905}
906
907void ShortcutDialog::keySequenceChanged(const QKeySequence &seq)
908{
909 activateWindow(); // where is the kbd focus lost? cause of popup state?
910 if (_shortcut == seq)
911 return; // don't try to update the same
912
913 if (seq.isEmpty()) { // clear
914 _shortcut = seq;
915 return;
916 }
917
918 // Check if the key sequence is used currently
919 QString sc = seq.toString();
920 // NOTICE - seq.toString() & the entries in "conflicting" randomly get invalidated after the next call (if no sc has been set & conflicting isn't empty?!)
921 QList<KGlobalShortcutInfo> conflicting = KGlobalAccel::getGlobalShortcutsByKey(seq);
922 if (!conflicting.isEmpty()) {
923 const KGlobalShortcutInfo &conflict = conflicting.at(0);
924 warning->setText(i18nc("'%1' is a keyboard shortcut like 'ctrl+w'",
925 "<b>%1</b> is already in use", sc));
926 warning->setToolTip(i18nc("keyboard shortcut '%1' is used by action '%2' in application '%3'",
927 "<b>%1</b> is used by %2 in %3", sc, conflict.friendlyName(), conflict.componentFriendlyName()));
928 warning->show();
929 widget->setKeySequence(shortcut());
930 } else if (seq != _shortcut) {
931 warning->hide();
932 if (KPushButton *ok = button(KDialog::Ok))
933 ok->setFocus();
934 }
935
936 _shortcut = seq;
937}
938
939QKeySequence ShortcutDialog::shortcut() const
940{
941 return _shortcut;
942}
943
944//****************************************
945// Workspace
946//****************************************
947
948void Workspace::slotIncreaseWindowOpacity()
949{
950 if (!active_client) {
951 return;
952 }
953 active_client->setOpacity(qMin(active_client->opacity() + 0.05, 1.0));
954}
955
956void Workspace::slotLowerWindowOpacity()
957{
958 if (!active_client) {
959 return;
960 }
961 active_client->setOpacity(qMax(active_client->opacity() - 0.05, 0.05));
962}
963
964void Workspace::closeActivePopup()
965{
966 if (active_popup) {
967 active_popup->close();
968 active_popup = NULL;
969 active_popup_client = NULL;
970 }
971 m_userActionsMenu->close();
972}
973
974/*!
975 Create the global accel object \c keys.
976 */
977void Workspace::initShortcuts()
978{
979 keys = new KActionCollection(this);
980 KActionCollection* actionCollection = keys;
981 QAction* a = 0L;
982
983 // a separate KActionCollection is needed for the shortcut for disabling global shortcuts,
984 // otherwise it would also disable itself
985 disable_shortcuts_keys = new KActionCollection(this);
986 // TODO: PORT ME (KGlobalAccel related)
987 // FIXME KAccel port... needed?
988 //disable_shortcuts_keys->disableBlocking( true );
989#define IN_KWIN
990#include "kwinbindings.cpp"
991#ifdef KWIN_BUILD_TABBOX
992 TabBox::TabBox::self()->initShortcuts(actionCollection);
993#endif
994 VirtualDesktopManager::self()->initShortcuts(actionCollection);
995 m_userActionsMenu->discard(); // so that it's recreated next time
996}
997
998void Workspace::setupWindowShortcut(Client* c)
999{
1000 assert(client_keys_dialog == NULL);
1001 // TODO: PORT ME (KGlobalAccel related)
1002 //keys->setEnabled( false );
1003 //disable_shortcuts_keys->setEnabled( false );
1004 //client_keys->setEnabled( false );
1005 client_keys_dialog = new ShortcutDialog(c->shortcut().primary());
1006 client_keys_client = c;
1007 connect(client_keys_dialog, SIGNAL(dialogDone(bool)), SLOT(setupWindowShortcutDone(bool)));
1008 QRect r = clientArea(ScreenArea, c);
1009 QSize size = client_keys_dialog->sizeHint();
1010 QPoint pos = c->pos() + c->clientPos();
1011 if (pos.x() + size.width() >= r.right())
1012 pos.setX(r.right() - size.width());
1013 if (pos.y() + size.height() >= r.bottom())
1014 pos.setY(r.bottom() - size.height());
1015 client_keys_dialog->move(pos);
1016 client_keys_dialog->show();
1017 active_popup = client_keys_dialog;
1018 active_popup_client = c;
1019}
1020
1021void Workspace::setupWindowShortcutDone(bool ok)
1022{
1023// keys->setEnabled( true );
1024// disable_shortcuts_keys->setEnabled( true );
1025// client_keys->setEnabled( true );
1026 if (ok)
1027 client_keys_client->setShortcut(KShortcut(client_keys_dialog->shortcut()).toString());
1028 closeActivePopup();
1029 client_keys_dialog->deleteLater();
1030 client_keys_dialog = NULL;
1031 client_keys_client = NULL;
1032 if (active_client)
1033 active_client->takeFocus();
1034}
1035
1036void Workspace::clientShortcutUpdated(Client* c)
1037{
1038 QString key = QString("_k_session:%1").arg(c->window());
1039 QAction* action = client_keys->action(key.toLatin1().constData());
1040 if (!c->shortcut().isEmpty()) {
1041 if (action == NULL) { // new shortcut
1042 action = client_keys->addAction(QString(key));
1043 action->setText(i18n("Activate Window (%1)", c->caption()));
1044 connect(action, SIGNAL(triggered(bool)), c, SLOT(shortcutActivated()));
1045 }
1046
1047 KAction *kaction = qobject_cast<KAction*>(action);
1048 // no autoloading, since it's configured explicitly here and is not meant to be reused
1049 // (the key is the window id anyway, which is kind of random)
1050 kaction->setGlobalShortcut(
1051 c->shortcut(), KAction::ActiveShortcut, KAction::NoAutoloading);
1052 kaction->setEnabled(true);
1053 } else {
1054 KAction *kaction = qobject_cast<KAction*>(action);
1055 if (kaction) {
1056 kaction->forgetGlobalShortcut();
1057 }
1058 delete action;
1059 }
1060}
1061
1062void Workspace::performWindowOperation(Client* c, Options::WindowOperation op)
1063{
1064 if (!c)
1065 return;
1066 if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp)
1067 Cursor::setPos(c->geometry().center());
1068 if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp)
1069 Cursor::setPos(c->geometry().bottomRight());
1070 switch(op) {
1071 case Options::MoveOp:
1072 c->performMouseCommand(Options::MouseMove, cursorPos());
1073 break;
1074 case Options::UnrestrictedMoveOp:
1075 c->performMouseCommand(Options::MouseUnrestrictedMove, cursorPos());
1076 break;
1077 case Options::ResizeOp:
1078 c->performMouseCommand(Options::MouseResize, cursorPos());
1079 break;
1080 case Options::UnrestrictedResizeOp:
1081 c->performMouseCommand(Options::MouseUnrestrictedResize, cursorPos());
1082 break;
1083 case Options::CloseOp:
1084 QMetaObject::invokeMethod(c, "closeWindow", Qt::QueuedConnection);
1085 break;
1086 case Options::MaximizeOp:
1087 c->maximize(c->maximizeMode() == Client::MaximizeFull
1088 ? Client::MaximizeRestore : Client::MaximizeFull);
1089 break;
1090 case Options::HMaximizeOp:
1091 c->maximize(c->maximizeMode() ^ Client::MaximizeHorizontal);
1092 break;
1093 case Options::VMaximizeOp:
1094 c->maximize(c->maximizeMode() ^ Client::MaximizeVertical);
1095 break;
1096 case Options::RestoreOp:
1097 c->maximize(Client::MaximizeRestore);
1098 break;
1099 case Options::MinimizeOp:
1100 c->minimize();
1101 break;
1102 case Options::ShadeOp:
1103 c->performMouseCommand(Options::MouseShade, cursorPos());
1104 break;
1105 case Options::OnAllDesktopsOp:
1106 c->setOnAllDesktops(!c->isOnAllDesktops());
1107 break;
1108 case Options::FullScreenOp:
1109 c->setFullScreen(!c->isFullScreen(), true);
1110 break;
1111 case Options::NoBorderOp:
1112 c->setNoBorder(!c->noBorder());
1113 break;
1114 case Options::KeepAboveOp: {
1115 StackingUpdatesBlocker blocker(this);
1116 bool was = c->keepAbove();
1117 c->setKeepAbove(!c->keepAbove());
1118 if (was && !c->keepAbove())
1119 raiseClient(c);
1120 break;
1121 }
1122 case Options::KeepBelowOp: {
1123 StackingUpdatesBlocker blocker(this);
1124 bool was = c->keepBelow();
1125 c->setKeepBelow(!c->keepBelow());
1126 if (was && !c->keepBelow())
1127 lowerClient(c);
1128 break;
1129 }
1130 case Options::OperationsOp:
1131 c->performMouseCommand(Options::MouseShade, cursorPos());
1132 break;
1133 case Options::WindowRulesOp:
1134 RuleBook::self()->edit(c, false);
1135 break;
1136 case Options::ApplicationRulesOp:
1137 RuleBook::self()->edit(c, true);
1138 break;
1139 case Options::SetupWindowShortcutOp:
1140 setupWindowShortcut(c);
1141 break;
1142 case Options::LowerOp:
1143 lowerClient(c);
1144 break;
1145 case Options::TabDragOp: // Handled by decoration itself
1146 case Options::NoOp:
1147 break;
1148 case Options::RemoveTabFromGroupOp:
1149 if (c->untab(c->geometry().translated(cascadeOffset(c))) && options->focusPolicyIsReasonable())
1150 takeActivity(c, ActivityFocus | ActivityRaise, true);
1151 break;
1152 case Options::ActivateNextTabOp:
1153 if (c->tabGroup())
1154 c->tabGroup()->activateNext();
1155 break;
1156 case Options::ActivatePreviousTabOp:
1157 if (c->tabGroup())
1158 c->tabGroup()->activatePrev();
1159 break;
1160 case Options::CloseTabGroupOp:
1161 c->tabGroup()->closeAll();
1162 break;
1163 }
1164}
1165
1166/**
1167 * Called by the decoration in the new API to determine what buttons the user has configured for
1168 * window tab dragging and the operations menu.
1169 */
1170Options::WindowOperation Client::mouseButtonToWindowOperation(Qt::MouseButtons button)
1171{
1172 Options::MouseCommand com = Options::MouseNothing;
1173 bool active = isActive();
1174 if (!wantsInput()) // we cannot be active, use it anyway
1175 active = true;
1176
1177 if (button == Qt::LeftButton)
1178 com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1();
1179 else if (button == Qt::MidButton)
1180 com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2();
1181 else if (button == Qt::RightButton)
1182 com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3();
1183
1184 // TODO: Complete the list
1185 if (com == Options::MouseDragTab)
1186 return Options::TabDragOp;
1187 if (com == Options::MouseOperationsMenu)
1188 return Options::OperationsOp;
1189 return Options::NoOp;
1190}
1191
1192/*!
1193 Performs a mouse command on this client (see options.h)
1194 */
1195bool Client::performMouseCommand(Options::MouseCommand command, const QPoint &globalPos, bool handled)
1196{
1197 bool replay = false;
1198 switch(command) {
1199 case Options::MouseRaise:
1200 workspace()->raiseClient(this);
1201 break;
1202 case Options::MouseLower: {
1203 workspace()->lowerClient(this);
1204 // used to be activateNextClient(this), then topClientOnDesktop
1205 // since this is a mouseOp it's however safe to use the client under the mouse instead
1206 if (isActive() && options->focusPolicyIsReasonable()) {
1207 Client *next = workspace()->clientUnderMouse(screen());
1208 if (next && next != this)
1209 workspace()->requestFocus(next, false);
1210 }
1211 break;
1212 }
1213 case Options::MouseShade :
1214 toggleShade();
1215 cancelShadeHoverTimer();
1216 break;
1217 case Options::MouseSetShade:
1218 setShade(ShadeNormal);
1219 cancelShadeHoverTimer();
1220 break;
1221 case Options::MouseUnsetShade:
1222 setShade(ShadeNone);
1223 cancelShadeHoverTimer();
1224 break;
1225 case Options::MouseOperationsMenu:
1226 if (isActive() && options->isClickRaise())
1227 autoRaise();
1228 workspace()->showWindowMenu(QRect(globalPos, globalPos), this);
1229 break;
1230 case Options::MouseToggleRaiseAndLower:
1231 workspace()->raiseOrLowerClient(this);
1232 break;
1233 case Options::MouseActivateAndRaise: {
1234 replay = isActive(); // for clickraise mode
1235 bool mustReplay = !rules()->checkAcceptFocus(input);
1236 if (mustReplay) {
1237 ToplevelList::const_iterator it = workspace()->stackingOrder().constEnd(),
1238 begin = workspace()->stackingOrder().constBegin();
1239 while (mustReplay && --it != begin && *it != this) {
1240 Client *c = qobject_cast<Client*>(*it);
1241 if (!c || (c->keepAbove() && !keepAbove()) || (keepBelow() && !c->keepBelow()))
1242 continue; // can never raise above "it"
1243 mustReplay = !(c->isOnCurrentDesktop() && c->isOnCurrentActivity() && c->geometry().intersects(geometry()));
1244 }
1245 }
1246 workspace()->takeActivity(this, ActivityFocus | ActivityRaise, handled && replay);
1247 screens()->setCurrent(globalPos);
1248 replay = replay || mustReplay;
1249 break;
1250 }
1251 case Options::MouseActivateAndLower:
1252 workspace()->requestFocus(this);
1253 workspace()->lowerClient(this);
1254 screens()->setCurrent(globalPos);
1255 replay = replay || !rules()->checkAcceptFocus(input);
1256 break;
1257 case Options::MouseActivate:
1258 replay = isActive(); // for clickraise mode
1259 workspace()->takeActivity(this, ActivityFocus, handled && replay);
1260 screens()->setCurrent(globalPos);
1261 replay = replay || !rules()->checkAcceptFocus(input);
1262 break;
1263 case Options::MouseActivateRaiseAndPassClick:
1264 workspace()->takeActivity(this, ActivityFocus | ActivityRaise, handled);
1265 screens()->setCurrent(globalPos);
1266 replay = true;
1267 break;
1268 case Options::MouseActivateAndPassClick:
1269 workspace()->takeActivity(this, ActivityFocus, handled);
1270 screens()->setCurrent(globalPos);
1271 replay = true;
1272 break;
1273 case Options::MouseActivateRaiseAndMove:
1274 case Options::MouseActivateRaiseAndUnrestrictedMove:
1275 workspace()->raiseClient(this);
1276 workspace()->requestFocus(this);
1277 screens()->setCurrent(globalPos);
1278 // fallthrough
1279 case Options::MouseMove:
1280 case Options::MouseUnrestrictedMove: {
1281 if (!isMovableAcrossScreens())
1282 break;
1283 if (moveResizeMode)
1284 finishMoveResize(false);
1285 mode = PositionCenter;
1286 buttonDown = true;
1287 moveOffset = QPoint(globalPos.x() - x(), globalPos.y() - y()); // map from global
1288 invertedMoveOffset = rect().bottomRight() - moveOffset;
1289 unrestrictedMoveResize = (command == Options::MouseActivateRaiseAndUnrestrictedMove
1290 || command == Options::MouseUnrestrictedMove);
1291 if (!startMoveResize())
1292 buttonDown = false;
1293 updateCursor();
1294 break;
1295 }
1296 case Options::MouseResize:
1297 case Options::MouseUnrestrictedResize: {
1298 if (!isResizable() || isShade())
1299 break;
1300 if (moveResizeMode)
1301 finishMoveResize(false);
1302 buttonDown = true;
1303 moveOffset = QPoint(globalPos.x() - x(), globalPos.y() - y()); // map from global
1304 int x = moveOffset.x(), y = moveOffset.y();
1305 bool left = x < width() / 3;
1306 bool right = x >= 2 * width() / 3;
1307 bool top = y < height() / 3;
1308 bool bot = y >= 2 * height() / 3;
1309 if (top)
1310 mode = left ? PositionTopLeft : (right ? PositionTopRight : PositionTop);
1311 else if (bot)
1312 mode = left ? PositionBottomLeft : (right ? PositionBottomRight : PositionBottom);
1313 else
1314 mode = (x < width() / 2) ? PositionLeft : PositionRight;
1315 invertedMoveOffset = rect().bottomRight() - moveOffset;
1316 unrestrictedMoveResize = (command == Options::MouseUnrestrictedResize);
1317 if (!startMoveResize())
1318 buttonDown = false;
1319 updateCursor();
1320 break;
1321 }
1322 case Options::MouseMaximize:
1323 maximize(Client::MaximizeFull);
1324 break;
1325 case Options::MouseRestore:
1326 maximize(Client::MaximizeRestore);
1327 break;
1328 case Options::MouseMinimize:
1329 minimize();
1330 break;
1331 case Options::MouseAbove: {
1332 StackingUpdatesBlocker blocker(workspace());
1333 if (keepBelow())
1334 setKeepBelow(false);
1335 else
1336 setKeepAbove(true);
1337 break;
1338 }
1339 case Options::MouseBelow: {
1340 StackingUpdatesBlocker blocker(workspace());
1341 if (keepAbove())
1342 setKeepAbove(false);
1343 else
1344 setKeepBelow(true);
1345 break;
1346 }
1347 case Options::MousePreviousDesktop:
1348 workspace()->windowToPreviousDesktop(this);
1349 break;
1350 case Options::MouseNextDesktop:
1351 workspace()->windowToNextDesktop(this);
1352 break;
1353 case Options::MouseOpacityMore:
1354 if (!isDesktop()) // No point in changing the opacity of the desktop
1355 setOpacity(qMin(opacity() + 0.1, 1.0));
1356 break;
1357 case Options::MouseOpacityLess:
1358 if (!isDesktop()) // No point in changing the opacity of the desktop
1359 setOpacity(qMax(opacity() - 0.1, 0.1));
1360 break;
1361 case Options::MousePreviousTab:
1362 if (tabGroup())
1363 tabGroup()->activatePrev();
1364 break;
1365 case Options::MouseNextTab:
1366 if (tabGroup())
1367 tabGroup()->activateNext();
1368 break;
1369 case Options::MouseClose:
1370 closeWindow();
1371 break;
1372 case Options::MouseDragTab:
1373 case Options::MouseNothing:
1374 replay = true;
1375 break;
1376 }
1377 return replay;
1378}
1379
1380void Workspace::slotActivateAttentionWindow()
1381{
1382 if (attention_chain.count() > 0)
1383 activateClient(attention_chain.first());
1384}
1385
1386static uint senderValue(QObject *sender)
1387{
1388 QAction *act = qobject_cast<QAction*>(sender);
1389 bool ok = false; uint i = -1;
1390 if (act)
1391 i = act->data().toUInt(&ok);
1392 if (ok)
1393 return i;
1394 return -1;
1395}
1396
1397#define USABLE_ACTIVE_CLIENT (active_client && !(active_client->isDesktop() || active_client->isDock()))
1398
1399void Workspace::slotWindowToDesktop()
1400{
1401 if (USABLE_ACTIVE_CLIENT) {
1402 const uint i = senderValue(sender());
1403 if (i < 1)
1404 return;
1405
1406 if (i >= 1 && i <= VirtualDesktopManager::self()->count())
1407 sendClientToDesktop(active_client, i, true);
1408 }
1409}
1410
1411static bool screenSwitchImpossible()
1412{
1413 if (!screens()->isCurrentFollowsMouse())
1414 return false;
1415 QStringList args;
1416 args << "--passivepopup" << i18n("The window manager is configured to consider the screen with the mouse on it as active one.\n"
1417 "Therefore it is not possible to switch to a screen explicitly.") << "20";
1418 KProcess::startDetached("kdialog", args);
1419 return true;
1420}
1421
1422void Workspace::slotSwitchToScreen()
1423{
1424 if (screenSwitchImpossible())
1425 return;
1426 const int i = senderValue(sender());
1427 if (i > -1)
1428 setCurrentScreen(i);
1429}
1430
1431void Workspace::slotSwitchToNextScreen()
1432{
1433 if (screenSwitchImpossible())
1434 return;
1435 setCurrentScreen((screens()->current() + 1) % screens()->count());
1436}
1437
1438void Workspace::slotSwitchToPrevScreen()
1439{
1440 if (screenSwitchImpossible())
1441 return;
1442 setCurrentScreen((screens()->current() + screens()->count() - 1) % screens()->count());
1443}
1444
1445void Workspace::slotWindowToScreen()
1446{
1447 if (USABLE_ACTIVE_CLIENT) {
1448 const int i = senderValue(sender());
1449 if (i < 0)
1450 return;
1451 if (i >= 0 && i <= screens()->count()) {
1452 sendClientToScreen(active_client, i);
1453 }
1454 }
1455}
1456
1457void Workspace::slotWindowToNextScreen()
1458{
1459 if (USABLE_ACTIVE_CLIENT)
1460 sendClientToScreen(active_client, (active_client->screen() + 1) % screens()->count());
1461}
1462
1463void Workspace::slotWindowToPrevScreen()
1464{
1465 if (USABLE_ACTIVE_CLIENT)
1466 sendClientToScreen(active_client, (active_client->screen() + screens()->count() - 1) % screens()->count());
1467}
1468
1469/*!
1470 Maximizes the popup client
1471 */
1472void Workspace::slotWindowMaximize()
1473{
1474 if (USABLE_ACTIVE_CLIENT)
1475 performWindowOperation(active_client, Options::MaximizeOp);
1476}
1477
1478/*!
1479 Maximizes the popup client vertically
1480 */
1481void Workspace::slotWindowMaximizeVertical()
1482{
1483 if (USABLE_ACTIVE_CLIENT)
1484 performWindowOperation(active_client, Options::VMaximizeOp);
1485}
1486
1487/*!
1488 Maximizes the popup client horiozontally
1489 */
1490void Workspace::slotWindowMaximizeHorizontal()
1491{
1492 if (USABLE_ACTIVE_CLIENT)
1493 performWindowOperation(active_client, Options::HMaximizeOp);
1494}
1495
1496
1497/*!
1498 Minimizes the popup client
1499 */
1500void Workspace::slotWindowMinimize()
1501{
1502 if (USABLE_ACTIVE_CLIENT)
1503 performWindowOperation(active_client, Options::MinimizeOp);
1504}
1505
1506/*!
1507 Shades/unshades the popup client respectively
1508 */
1509void Workspace::slotWindowShade()
1510{
1511 if (USABLE_ACTIVE_CLIENT)
1512 performWindowOperation(active_client, Options::ShadeOp);
1513}
1514
1515/*!
1516 Raises the popup client
1517 */
1518void Workspace::slotWindowRaise()
1519{
1520 if (USABLE_ACTIVE_CLIENT)
1521 raiseClient(active_client);
1522}
1523
1524/*!
1525 Lowers the popup client
1526 */
1527void Workspace::slotWindowLower()
1528{
1529 if (USABLE_ACTIVE_CLIENT) {
1530 lowerClient(active_client);
1531 // As this most likely makes the window no longer visible change the
1532 // keyboard focus to the next available window.
1533 //activateNextClient( c ); // Doesn't work when we lower a child window
1534 if (active_client->isActive() && options->focusPolicyIsReasonable()) {
1535 if (options->isNextFocusPrefersMouse()) {
1536 Client *next = clientUnderMouse(active_client->screen());
1537 if (next && next != active_client)
1538 requestFocus(next, false);
1539 } else {
1540 activateClient(topClientOnDesktop(VirtualDesktopManager::self()->current(), -1));
1541 }
1542 }
1543 }
1544}
1545
1546/*!
1547 Does a toggle-raise-and-lower on the popup client;
1548 */
1549void Workspace::slotWindowRaiseOrLower()
1550{
1551 if (USABLE_ACTIVE_CLIENT)
1552 raiseOrLowerClient(active_client);
1553}
1554
1555void Workspace::slotWindowOnAllDesktops()
1556{
1557 if (USABLE_ACTIVE_CLIENT)
1558 active_client->setOnAllDesktops(!active_client->isOnAllDesktops());
1559}
1560
1561void Workspace::slotWindowFullScreen()
1562{
1563 if (USABLE_ACTIVE_CLIENT)
1564 performWindowOperation(active_client, Options::FullScreenOp);
1565}
1566
1567void Workspace::slotWindowNoBorder()
1568{
1569 if (USABLE_ACTIVE_CLIENT)
1570 performWindowOperation(active_client, Options::NoBorderOp);
1571}
1572
1573void Workspace::slotWindowAbove()
1574{
1575 if (USABLE_ACTIVE_CLIENT)
1576 performWindowOperation(active_client, Options::KeepAboveOp);
1577}
1578
1579void Workspace::slotWindowBelow()
1580{
1581 if (USABLE_ACTIVE_CLIENT)
1582 performWindowOperation(active_client, Options::KeepBelowOp);
1583}
1584void Workspace::slotSetupWindowShortcut()
1585{
1586 if (USABLE_ACTIVE_CLIENT)
1587 performWindowOperation(active_client, Options::SetupWindowShortcutOp);
1588}
1589
1590/*!
1591 Toggles show desktop
1592 */
1593void Workspace::slotToggleShowDesktop()
1594{
1595 setShowingDesktop(!showingDesktop());
1596}
1597
1598template <typename Direction>
1599void windowToDesktop(Client *c)
1600{
1601 VirtualDesktopManager *vds = VirtualDesktopManager::self();
1602 Workspace *ws = Workspace::self();
1603 Direction functor;
1604 // TODO: why is options->isRollOverDesktops() not honored?
1605 const int desktop = functor(0, true);
1606 if (c && !c->isDesktop()
1607 && !c->isDock()) {
1608 ws->setClientIsMoving(c);
1609 vds->setCurrent(desktop);
1610 ws->setClientIsMoving(NULL);
1611 }
1612}
1613
1614/*!
1615 Move window to next desktop
1616 */
1617void Workspace::slotWindowToNextDesktop()
1618{
1619 if (USABLE_ACTIVE_CLIENT)
1620 windowToNextDesktop(active_client);
1621}
1622
1623void Workspace::windowToNextDesktop(Client* c)
1624{
1625 windowToDesktop<DesktopNext>(c);
1626}
1627
1628/*!
1629 Move window to previous desktop
1630 */
1631void Workspace::slotWindowToPreviousDesktop()
1632{
1633 if (USABLE_ACTIVE_CLIENT)
1634 windowToPreviousDesktop(active_client);
1635}
1636
1637void Workspace::windowToPreviousDesktop(Client* c)
1638{
1639 windowToDesktop<DesktopPrevious>(c);
1640}
1641
1642template <typename Direction>
1643void activeClientToDesktop()
1644{
1645 VirtualDesktopManager *vds = VirtualDesktopManager::self();
1646 Workspace *ws = Workspace::self();
1647 const int current = vds->current();
1648 Direction functor;
1649 const int d = functor(current, options->isRollOverDesktops());
1650 if (d == current) {
1651 return;
1652 }
1653 ws->setClientIsMoving(ws->activeClient());
1654 vds->setCurrent(d);
1655 ws->setClientIsMoving(NULL);
1656}
1657
1658void Workspace::slotWindowToDesktopRight()
1659{
1660 if (USABLE_ACTIVE_CLIENT) {
1661 activeClientToDesktop<DesktopRight>();
1662 }
1663}
1664
1665void Workspace::slotWindowToDesktopLeft()
1666{
1667 if (USABLE_ACTIVE_CLIENT) {
1668 activeClientToDesktop<DesktopLeft>();
1669 }
1670}
1671
1672void Workspace::slotWindowToDesktopUp()
1673{
1674 if (USABLE_ACTIVE_CLIENT) {
1675 activeClientToDesktop<DesktopAbove>();
1676 }
1677}
1678
1679void Workspace::slotWindowToDesktopDown()
1680{
1681 if (USABLE_ACTIVE_CLIENT) {
1682 activeClientToDesktop<DesktopBelow>();
1683 }
1684}
1685
1686void Workspace::slotActivateNextTab()
1687{
1688 if (active_client && active_client->tabGroup())
1689 active_client->tabGroup()->activateNext();
1690}
1691
1692void Workspace::slotActivatePrevTab()
1693{
1694 if (active_client && active_client->tabGroup())
1695 active_client->tabGroup()->activatePrev();
1696}
1697
1698void Workspace::slotUntab()
1699{
1700 if (active_client)
1701 active_client->untab(active_client->geometry().translated(cascadeOffset(active_client)));
1702}
1703
1704/*!
1705 Kill Window feature, similar to xkill
1706 */
1707void Workspace::slotKillWindow()
1708{
1709 if (m_windowKiller.isNull()) {
1710 m_windowKiller.reset(new KillWindow());
1711 }
1712 m_windowKiller->start();
1713}
1714
1715/*!
1716 Switches to the nearest window in given direction
1717 */
1718void Workspace::switchWindow(Direction direction)
1719{
1720 if (!active_client)
1721 return;
1722 Client *c = active_client;
1723 Client *switchTo = 0;
1724 int bestScore = 0;
1725 int d = c->desktop();
1726 // Centre of the active window
1727 QPoint curPos(c->pos().x() + c->geometry().width() / 2,
1728 c->pos().y() + c->geometry().height() / 2);
1729
1730 ToplevelList clist = stackingOrder();
1731 for (ToplevelList::Iterator i = clist.begin(); i != clist.end(); ++i) {
1732 Client *client = qobject_cast<Client*>(*i);
1733 if (!client) {
1734 continue;
1735 }
1736 if (client->wantsTabFocus() && *i != c &&
1737 client->desktop() == d && !client->isMinimized() && (*i)->isOnCurrentActivity()) {
1738 // Centre of the other window
1739 QPoint other(client->pos().x() + client->geometry().width() / 2,
1740 client->pos().y() + client->geometry().height() / 2);
1741
1742 int distance;
1743 int offset;
1744 switch(direction) {
1745 case DirectionNorth:
1746 distance = curPos.y() - other.y();
1747 offset = qAbs(other.x() - curPos.x());
1748 break;
1749 case DirectionEast:
1750 distance = other.x() - curPos.x();
1751 offset = qAbs(other.y() - curPos.y());
1752 break;
1753 case DirectionSouth:
1754 distance = other.y() - curPos.y();
1755 offset = qAbs(other.x() - curPos.x());
1756 break;
1757 case DirectionWest:
1758 distance = curPos.x() - other.x();
1759 offset = qAbs(other.y() - curPos.y());
1760 break;
1761 default:
1762 distance = -1;
1763 offset = -1;
1764 }
1765
1766 if (distance > 0) {
1767 // Inverse score
1768 int score = distance + offset + ((offset * offset) / distance);
1769 if (score < bestScore || !switchTo) {
1770 switchTo = client;
1771 bestScore = score;
1772 }
1773 }
1774 }
1775 }
1776 if (switchTo) {
1777 if (switchTo->tabGroup())
1778 switchTo = switchTo->tabGroup()->current();
1779 activateClient(switchTo);
1780 }
1781}
1782
1783/*!
1784 Switches to upper window
1785 */
1786void Workspace::slotSwitchWindowUp()
1787{
1788 switchWindow(DirectionNorth);
1789}
1790
1791/*!
1792 Switches to lower window
1793 */
1794void Workspace::slotSwitchWindowDown()
1795{
1796 switchWindow(DirectionSouth);
1797}
1798
1799/*!
1800 Switches to window on the right
1801 */
1802void Workspace::slotSwitchWindowRight()
1803{
1804 switchWindow(DirectionEast);
1805}
1806
1807/*!
1808 Switches to window on the left
1809 */
1810void Workspace::slotSwitchWindowLeft()
1811{
1812 switchWindow(DirectionWest);
1813}
1814
1815/*!
1816 Shows the window operations popup menu for the activeClient()
1817 */
1818void Workspace::slotWindowOperations()
1819{
1820 if (!active_client)
1821 return;
1822 QPoint pos = active_client->pos() + active_client->clientPos();
1823 showWindowMenu(QRect(pos, pos), active_client);
1824}
1825
1826void Workspace::showWindowMenu(const QRect &pos, Client* cl)
1827{
1828 m_userActionsMenu->show(pos, cl);
1829}
1830
1831/*!
1832 Closes the popup client
1833 */
1834void Workspace::slotWindowClose()
1835{
1836 // TODO: why?
1837// if ( tab_box->isVisible())
1838// return;
1839 if (USABLE_ACTIVE_CLIENT)
1840 performWindowOperation(active_client, Options::CloseOp);
1841}
1842
1843/*!
1844 Starts keyboard move mode for the popup client
1845 */
1846void Workspace::slotWindowMove()
1847{
1848 if (USABLE_ACTIVE_CLIENT)
1849 performWindowOperation(active_client, Options::UnrestrictedMoveOp);
1850}
1851
1852/*!
1853 Starts keyboard resize mode for the popup client
1854 */
1855void Workspace::slotWindowResize()
1856{
1857 if (USABLE_ACTIVE_CLIENT)
1858 performWindowOperation(active_client, Options::UnrestrictedResizeOp);
1859}
1860
1861void Workspace::slotInvertScreen()
1862{
1863 bool succeeded = false;
1864
1865 //BEGIN Xrandr inversion - does atm NOT work with the nvidia blob
1866 XRRScreenResources *res = XRRGetScreenResources(display(), active_client ? active_client->window() : rootWindow());
1867 if (res) {
1868 for (int j = 0; j < res->ncrtc; ++j) {
1869 XRRCrtcGamma *gamma = XRRGetCrtcGamma(display(), res->crtcs[j]);
1870 if (gamma && gamma->size) {
1871 kDebug(1212) << "inverting screen using XRRSetCrtcGamma";
1872 const int half = gamma->size / 2 + 1;
1873 unsigned short swap;
1874 for (int i = 0; i < half; ++i) {
1875#define INVERT(_C_) swap = gamma->_C_[i]; gamma->_C_[i] = gamma->_C_[gamma->size - 1 - i]; gamma->_C_[gamma->size - 1 - i] = swap
1876 INVERT(red);
1877 INVERT(green);
1878 INVERT(blue);
1879#undef INVERT
1880 }
1881 XRRSetCrtcGamma(display(), res->crtcs[j], gamma);
1882 XRRFreeGamma(gamma);
1883 succeeded = true;
1884 }
1885 }
1886 XRRFreeScreenResources(res);
1887 }
1888 if (succeeded)
1889 return;
1890
1891 //BEGIN XF86VidMode inversion - only works if optionally libXxf86vm is linked
1892#ifndef KWIN_NO_XF86VM
1893 int size = 0;
1894 // TODO: this doesn't work with screen numbers in twinview - probably relevant only for multihead?
1895 const int scrn = 0; // active_screen
1896 if (XF86VidModeGetGammaRampSize(display(), scrn, &size)) {
1897 unsigned short *red, *green, *blue;
1898 red = new unsigned short[size];
1899 green = new unsigned short[size];
1900 blue = new unsigned short[size];
1901 if (XF86VidModeGetGammaRamp(display(), scrn, size, red, green, blue)) {
1902 kDebug(1212) << "inverting screen using XF86VidModeSetGammaRamp";
1903 const int half = size / 2 + 1;
1904 unsigned short swap;
1905 for (int i = 0; i < half; ++i) {
1906 swap = red[i]; red[i] = red[size - 1 - i]; red[size - 1 - i] = swap;
1907 swap = green[i]; green[i] = green[size - 1 - i]; green[size - 1 - i] = swap;
1908 swap = blue[i]; blue[i] = blue[size - 1 - i]; blue[size - 1 - i] = swap;
1909 }
1910 XF86VidModeSetGammaRamp(display(), scrn, size, red, green, blue);
1911 succeeded = true;
1912 }
1913 delete [] red;
1914 delete [] green;
1915 delete [] blue;
1916 }
1917
1918 if (succeeded)
1919 return;
1920#endif
1921
1922 //BEGIN effect plugin inversion - atm only works with OpenGL and has an overhead to it
1923 if (effects) {
1924 if (Effect *inverter = static_cast<EffectsHandlerImpl*>(effects)->provides(Effect::ScreenInversion)) {
1925 kDebug(1212) << "inverting screen using Effect plugin";
1926 QMetaObject::invokeMethod(inverter, "toggleScreenInversion", Qt::DirectConnection);
1927 }
1928 }
1929
1930 if (!succeeded)
1931 kDebug(1212) << "sorry - neither Xrandr, nor XF86VidModeSetGammaRamp worked and there's no inversion supplying effect plugin either";
1932
1933}
1934
1935#undef USABLE_ACTIVE_CLIENT
1936
1937void Client::setShortcut(const QString& _cut)
1938{
1939 QString cut = rules()->checkShortcut(_cut);
1940 if (cut.isEmpty())
1941 return setShortcutInternal(KShortcut());
1942 if (cut == shortcut().toString()) {
1943 return; // no change
1944 }
1945// Format:
1946// base+(abcdef)<space>base+(abcdef)
1947// E.g. Alt+Ctrl+(ABCDEF);Meta+X,Meta+(ABCDEF)
1948 if (!cut.contains('(') && !cut.contains(')') && !cut.contains(" - ")) {
1949 if (workspace()->shortcutAvailable(KShortcut(cut), this))
1950 setShortcutInternal(KShortcut(cut));
1951 else
1952 setShortcutInternal(KShortcut());
1953 return;
1954 }
1955 QList< KShortcut > keys;
1956 QStringList groups = cut.split(" - ");
1957 for (QStringList::ConstIterator it = groups.constBegin();
1958 it != groups.constEnd();
1959 ++it) {
1960 QRegExp reg("(.*\\+)\\((.*)\\)");
1961 if (reg.indexIn(*it) > -1) {
1962 QString base = reg.cap(1);
1963 QString list = reg.cap(2);
1964 for (int i = 0;
1965 i < list.length();
1966 ++i) {
1967 KShortcut c(base + list[ i ]);
1968 if (!c.isEmpty())
1969 keys.append(c);
1970 }
1971 } else {
1972 // regexp doesn't match, so it should be a normal shortcut
1973 KShortcut c(*it);
1974 if (!c.isEmpty()) {
1975 keys.append(c);
1976 }
1977 }
1978 }
1979 for (QList< KShortcut >::ConstIterator it = keys.constBegin();
1980 it != keys.constEnd();
1981 ++it) {
1982 if (_shortcut == *it) // current one is in the list
1983 return;
1984 }
1985 for (QList< KShortcut >::ConstIterator it = keys.constBegin();
1986 it != keys.constEnd();
1987 ++it) {
1988 if (workspace()->shortcutAvailable(*it, this)) {
1989 setShortcutInternal(*it);
1990 return;
1991 }
1992 }
1993 setShortcutInternal(KShortcut());
1994}
1995
1996void Client::setShortcutInternal(const KShortcut& cut)
1997{
1998 if (_shortcut == cut)
1999 return;
2000 _shortcut = cut;
2001 updateCaption();
2002#if 0
2003 workspace()->clientShortcutUpdated(this);
2004#else
2005 // Workaround for kwin<->kglobalaccel deadlock, when KWin has X grab and the kded
2006 // kglobalaccel module tries to create the key grab. KWin should preferably grab
2007 // they keys itself anyway :(.
2008 QTimer::singleShot(0, this, SLOT(delayedSetShortcut()));
2009#endif
2010}
2011
2012void Client::delayedSetShortcut()
2013{
2014 workspace()->clientShortcutUpdated(this);
2015}
2016
2017bool Workspace::shortcutAvailable(const KShortcut& cut, Client* ignore) const
2018{
2019 if (ignore && cut == ignore->shortcut())
2020 return true;
2021 Q_FOREACH (const QKeySequence &seq, cut.toList()) {
2022 if (!KGlobalAccel::getGlobalShortcutsByKey(seq).isEmpty()) {
2023 return false;
2024 }
2025 }
2026 for (ClientList::ConstIterator it = clients.constBegin();
2027 it != clients.constEnd();
2028 ++it) {
2029 if ((*it) != ignore && (*it)->shortcut() == cut)
2030 return false;
2031 }
2032 return true;
2033}
2034
2035} // namespace
2036