1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> |
6 | Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> |
7 | |
8 | This program is free software; you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation; either version 2 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along 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 | |
87 | namespace KWin |
88 | { |
89 | |
90 | 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 | |
117 | UserActionsMenu::() |
118 | { |
119 | discard(); |
120 | } |
121 | |
122 | bool UserActionsMenu::() const |
123 | { |
124 | return m_menu && m_menu->isVisible(); |
125 | } |
126 | |
127 | bool UserActionsMenu::() const |
128 | { |
129 | return !m_client.isNull() && isShown(); |
130 | } |
131 | |
132 | void UserActionsMenu::() |
133 | { |
134 | if (!m_menu) { |
135 | return; |
136 | } |
137 | m_menu->close(); |
138 | m_client.clear(); |
139 | } |
140 | |
141 | bool UserActionsMenu::(const Client *c) const |
142 | { |
143 | if (!c || m_client.isNull()) { |
144 | return false; |
145 | } |
146 | return c == m_client.data(); |
147 | } |
148 | |
149 | void UserActionsMenu::(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 = 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 | |
186 | void UserActionsMenu::(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 | |
231 | QStringList 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 | |
253 | void UserActionsMenu::() |
254 | { |
255 | QStringList args; |
256 | args << "--icon" << "preferences-system-windows" << configModules(false); |
257 | KToolInvocation::kdeinitExec("kcmshell4" , args); |
258 | } |
259 | |
260 | void UserActionsMenu::() |
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 * = 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 | |
398 | void UserActionsMenu::() |
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 | |
412 | void UserActionsMenu::() |
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 | |
477 | void UserActionsMenu::() |
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 | |
491 | void UserActionsMenu::(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 | |
509 | static 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 | |
517 | void UserActionsMenu::() |
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 | |
537 | void UserActionsMenu::(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 | |
549 | void UserActionsMenu::() |
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 | |
565 | void UserActionsMenu::() |
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 | |
595 | void UserActionsMenu::() |
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 | |
611 | void UserActionsMenu::() |
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 | |
628 | void UserActionsMenu::() |
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 | |
646 | void UserActionsMenu::() |
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 | |
687 | void UserActionsMenu::() |
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 | |
710 | void UserActionsMenu::() |
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 | |
754 | void UserActionsMenu::(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 | |
781 | void UserActionsMenu::(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 | |
803 | void UserActionsMenu::(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 | |
816 | void UserActionsMenu::(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 | //**************************************** |
851 | ShortcutDialog::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 | |
882 | void 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 | |
901 | void ShortcutDialog::done(int r) |
902 | { |
903 | KDialog::done(r); |
904 | emit dialogDone(r == Accepted); |
905 | } |
906 | |
907 | void 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 | |
939 | QKeySequence ShortcutDialog::shortcut() const |
940 | { |
941 | return _shortcut; |
942 | } |
943 | |
944 | //**************************************** |
945 | // Workspace |
946 | //**************************************** |
947 | |
948 | void Workspace::slotIncreaseWindowOpacity() |
949 | { |
950 | if (!active_client) { |
951 | return; |
952 | } |
953 | active_client->setOpacity(qMin(active_client->opacity() + 0.05, 1.0)); |
954 | } |
955 | |
956 | void Workspace::slotLowerWindowOpacity() |
957 | { |
958 | if (!active_client) { |
959 | return; |
960 | } |
961 | active_client->setOpacity(qMax(active_client->opacity() - 0.05, 0.05)); |
962 | } |
963 | |
964 | void Workspace::() |
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 | */ |
977 | void 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 | |
998 | void 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 | |
1021 | void 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 | |
1036 | void 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 | |
1062 | void 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 | */ |
1170 | Options::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 | */ |
1195 | bool 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 | |
1380 | void Workspace::slotActivateAttentionWindow() |
1381 | { |
1382 | if (attention_chain.count() > 0) |
1383 | activateClient(attention_chain.first()); |
1384 | } |
1385 | |
1386 | static 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 | |
1399 | void 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 | |
1411 | static 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 | |
1422 | void Workspace::slotSwitchToScreen() |
1423 | { |
1424 | if (screenSwitchImpossible()) |
1425 | return; |
1426 | const int i = senderValue(sender()); |
1427 | if (i > -1) |
1428 | setCurrentScreen(i); |
1429 | } |
1430 | |
1431 | void Workspace::slotSwitchToNextScreen() |
1432 | { |
1433 | if (screenSwitchImpossible()) |
1434 | return; |
1435 | setCurrentScreen((screens()->current() + 1) % screens()->count()); |
1436 | } |
1437 | |
1438 | void Workspace::slotSwitchToPrevScreen() |
1439 | { |
1440 | if (screenSwitchImpossible()) |
1441 | return; |
1442 | setCurrentScreen((screens()->current() + screens()->count() - 1) % screens()->count()); |
1443 | } |
1444 | |
1445 | void 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 | |
1457 | void Workspace::slotWindowToNextScreen() |
1458 | { |
1459 | if (USABLE_ACTIVE_CLIENT) |
1460 | sendClientToScreen(active_client, (active_client->screen() + 1) % screens()->count()); |
1461 | } |
1462 | |
1463 | void 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 | */ |
1472 | void Workspace::slotWindowMaximize() |
1473 | { |
1474 | if (USABLE_ACTIVE_CLIENT) |
1475 | performWindowOperation(active_client, Options::MaximizeOp); |
1476 | } |
1477 | |
1478 | /*! |
1479 | Maximizes the popup client vertically |
1480 | */ |
1481 | void Workspace::slotWindowMaximizeVertical() |
1482 | { |
1483 | if (USABLE_ACTIVE_CLIENT) |
1484 | performWindowOperation(active_client, Options::VMaximizeOp); |
1485 | } |
1486 | |
1487 | /*! |
1488 | Maximizes the popup client horiozontally |
1489 | */ |
1490 | void Workspace::slotWindowMaximizeHorizontal() |
1491 | { |
1492 | if (USABLE_ACTIVE_CLIENT) |
1493 | performWindowOperation(active_client, Options::HMaximizeOp); |
1494 | } |
1495 | |
1496 | |
1497 | /*! |
1498 | Minimizes the popup client |
1499 | */ |
1500 | void 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 | */ |
1509 | void Workspace::slotWindowShade() |
1510 | { |
1511 | if (USABLE_ACTIVE_CLIENT) |
1512 | performWindowOperation(active_client, Options::ShadeOp); |
1513 | } |
1514 | |
1515 | /*! |
1516 | Raises the popup client |
1517 | */ |
1518 | void Workspace::slotWindowRaise() |
1519 | { |
1520 | if (USABLE_ACTIVE_CLIENT) |
1521 | raiseClient(active_client); |
1522 | } |
1523 | |
1524 | /*! |
1525 | Lowers the popup client |
1526 | */ |
1527 | void 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 | */ |
1549 | void Workspace::slotWindowRaiseOrLower() |
1550 | { |
1551 | if (USABLE_ACTIVE_CLIENT) |
1552 | raiseOrLowerClient(active_client); |
1553 | } |
1554 | |
1555 | void Workspace::slotWindowOnAllDesktops() |
1556 | { |
1557 | if (USABLE_ACTIVE_CLIENT) |
1558 | active_client->setOnAllDesktops(!active_client->isOnAllDesktops()); |
1559 | } |
1560 | |
1561 | void Workspace::slotWindowFullScreen() |
1562 | { |
1563 | if (USABLE_ACTIVE_CLIENT) |
1564 | performWindowOperation(active_client, Options::FullScreenOp); |
1565 | } |
1566 | |
1567 | void Workspace::slotWindowNoBorder() |
1568 | { |
1569 | if (USABLE_ACTIVE_CLIENT) |
1570 | performWindowOperation(active_client, Options::NoBorderOp); |
1571 | } |
1572 | |
1573 | void Workspace::slotWindowAbove() |
1574 | { |
1575 | if (USABLE_ACTIVE_CLIENT) |
1576 | performWindowOperation(active_client, Options::KeepAboveOp); |
1577 | } |
1578 | |
1579 | void Workspace::slotWindowBelow() |
1580 | { |
1581 | if (USABLE_ACTIVE_CLIENT) |
1582 | performWindowOperation(active_client, Options::KeepBelowOp); |
1583 | } |
1584 | void Workspace::slotSetupWindowShortcut() |
1585 | { |
1586 | if (USABLE_ACTIVE_CLIENT) |
1587 | performWindowOperation(active_client, Options::SetupWindowShortcutOp); |
1588 | } |
1589 | |
1590 | /*! |
1591 | Toggles show desktop |
1592 | */ |
1593 | void Workspace::slotToggleShowDesktop() |
1594 | { |
1595 | setShowingDesktop(!showingDesktop()); |
1596 | } |
1597 | |
1598 | template <typename Direction> |
1599 | void 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 | */ |
1617 | void Workspace::slotWindowToNextDesktop() |
1618 | { |
1619 | if (USABLE_ACTIVE_CLIENT) |
1620 | windowToNextDesktop(active_client); |
1621 | } |
1622 | |
1623 | void Workspace::windowToNextDesktop(Client* c) |
1624 | { |
1625 | windowToDesktop<DesktopNext>(c); |
1626 | } |
1627 | |
1628 | /*! |
1629 | Move window to previous desktop |
1630 | */ |
1631 | void Workspace::slotWindowToPreviousDesktop() |
1632 | { |
1633 | if (USABLE_ACTIVE_CLIENT) |
1634 | windowToPreviousDesktop(active_client); |
1635 | } |
1636 | |
1637 | void Workspace::windowToPreviousDesktop(Client* c) |
1638 | { |
1639 | windowToDesktop<DesktopPrevious>(c); |
1640 | } |
1641 | |
1642 | template <typename Direction> |
1643 | void 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 | |
1658 | void Workspace::slotWindowToDesktopRight() |
1659 | { |
1660 | if (USABLE_ACTIVE_CLIENT) { |
1661 | activeClientToDesktop<DesktopRight>(); |
1662 | } |
1663 | } |
1664 | |
1665 | void Workspace::slotWindowToDesktopLeft() |
1666 | { |
1667 | if (USABLE_ACTIVE_CLIENT) { |
1668 | activeClientToDesktop<DesktopLeft>(); |
1669 | } |
1670 | } |
1671 | |
1672 | void Workspace::slotWindowToDesktopUp() |
1673 | { |
1674 | if (USABLE_ACTIVE_CLIENT) { |
1675 | activeClientToDesktop<DesktopAbove>(); |
1676 | } |
1677 | } |
1678 | |
1679 | void Workspace::slotWindowToDesktopDown() |
1680 | { |
1681 | if (USABLE_ACTIVE_CLIENT) { |
1682 | activeClientToDesktop<DesktopBelow>(); |
1683 | } |
1684 | } |
1685 | |
1686 | void Workspace::slotActivateNextTab() |
1687 | { |
1688 | if (active_client && active_client->tabGroup()) |
1689 | active_client->tabGroup()->activateNext(); |
1690 | } |
1691 | |
1692 | void Workspace::slotActivatePrevTab() |
1693 | { |
1694 | if (active_client && active_client->tabGroup()) |
1695 | active_client->tabGroup()->activatePrev(); |
1696 | } |
1697 | |
1698 | void 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 | */ |
1707 | void 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 | */ |
1718 | void 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 | */ |
1786 | void Workspace::slotSwitchWindowUp() |
1787 | { |
1788 | switchWindow(DirectionNorth); |
1789 | } |
1790 | |
1791 | /*! |
1792 | Switches to lower window |
1793 | */ |
1794 | void Workspace::slotSwitchWindowDown() |
1795 | { |
1796 | switchWindow(DirectionSouth); |
1797 | } |
1798 | |
1799 | /*! |
1800 | Switches to window on the right |
1801 | */ |
1802 | void Workspace::slotSwitchWindowRight() |
1803 | { |
1804 | switchWindow(DirectionEast); |
1805 | } |
1806 | |
1807 | /*! |
1808 | Switches to window on the left |
1809 | */ |
1810 | void Workspace::slotSwitchWindowLeft() |
1811 | { |
1812 | switchWindow(DirectionWest); |
1813 | } |
1814 | |
1815 | /*! |
1816 | Shows the window operations popup menu for the activeClient() |
1817 | */ |
1818 | void 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 | |
1826 | void Workspace::(const QRect &pos, Client* cl) |
1827 | { |
1828 | m_userActionsMenu->show(pos, cl); |
1829 | } |
1830 | |
1831 | /*! |
1832 | Closes the popup client |
1833 | */ |
1834 | void 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 | */ |
1846 | void 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 | */ |
1855 | void Workspace::slotWindowResize() |
1856 | { |
1857 | if (USABLE_ACTIVE_CLIENT) |
1858 | performWindowOperation(active_client, Options::UnrestrictedResizeOp); |
1859 | } |
1860 | |
1861 | void 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 | |
1937 | void 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 | |
1996 | void 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 | |
2012 | void Client::delayedSetShortcut() |
2013 | { |
2014 | workspace()->clientShortcutUpdated(this); |
2015 | } |
2016 | |
2017 | bool 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 | |