File indexing completed on 2024-11-10 04:57:44
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "focuschain.h" 0010 #include "window.h" 0011 #include "workspace.h" 0012 0013 namespace KWin 0014 { 0015 0016 void FocusChain::remove(Window *window) 0017 { 0018 for (auto it = m_desktopFocusChains.begin(); 0019 it != m_desktopFocusChains.end(); 0020 ++it) { 0021 it.value().removeAll(window); 0022 } 0023 m_mostRecentlyUsed.removeAll(window); 0024 } 0025 0026 void FocusChain::addDesktop(VirtualDesktop *desktop) 0027 { 0028 m_desktopFocusChains.insert(desktop, Chain()); 0029 } 0030 0031 void FocusChain::removeDesktop(VirtualDesktop *desktop) 0032 { 0033 if (m_currentDesktop == desktop) { 0034 m_currentDesktop = nullptr; 0035 } 0036 m_desktopFocusChains.remove(desktop); 0037 } 0038 0039 Window *FocusChain::getForActivation(VirtualDesktop *desktop) const 0040 { 0041 return getForActivation(desktop, workspace()->activeOutput()); 0042 } 0043 0044 Window *FocusChain::getForActivation(VirtualDesktop *desktop, Output *output) const 0045 { 0046 auto it = m_desktopFocusChains.constFind(desktop); 0047 if (it == m_desktopFocusChains.constEnd()) { 0048 return nullptr; 0049 } 0050 const auto &chain = it.value(); 0051 for (int i = chain.size() - 1; i >= 0; --i) { 0052 auto tmp = chain.at(i); 0053 // TODO: move the check into Window 0054 if (!tmp->isShade() && tmp->isShown() && tmp->isOnCurrentActivity() 0055 && (!m_separateScreenFocus || tmp->output() == output)) { 0056 return tmp; 0057 } 0058 } 0059 return nullptr; 0060 } 0061 0062 void FocusChain::update(Window *window, FocusChain::Change change) 0063 { 0064 if (!window->wantsTabFocus()) { 0065 // Doesn't want tab focus, remove 0066 remove(window); 0067 return; 0068 } 0069 0070 if (window->isOnAllDesktops()) { 0071 // Now on all desktops, add it to focus chains it is not already in 0072 for (auto it = m_desktopFocusChains.begin(); 0073 it != m_desktopFocusChains.end(); 0074 ++it) { 0075 auto &chain = it.value(); 0076 // Making first/last works only on current desktop, don't affect all desktops 0077 if (it.key() == m_currentDesktop 0078 && (change == MakeFirst || change == MakeLast)) { 0079 if (change == MakeFirst) { 0080 makeFirstInChain(window, chain); 0081 } else { 0082 makeLastInChain(window, chain); 0083 } 0084 } else { 0085 insertWindowIntoChain(window, chain); 0086 } 0087 } 0088 } else { 0089 // Now only on desktop, remove it anywhere else 0090 for (auto it = m_desktopFocusChains.begin(); 0091 it != m_desktopFocusChains.end(); 0092 ++it) { 0093 auto &chain = it.value(); 0094 if (window->isOnDesktop(it.key())) { 0095 updateWindowInChain(window, change, chain); 0096 } else { 0097 chain.removeAll(window); 0098 } 0099 } 0100 } 0101 0102 // add for most recently used chain 0103 updateWindowInChain(window, change, m_mostRecentlyUsed); 0104 } 0105 0106 void FocusChain::updateWindowInChain(Window *window, FocusChain::Change change, Chain &chain) 0107 { 0108 if (change == MakeFirst) { 0109 makeFirstInChain(window, chain); 0110 } else if (change == MakeLast) { 0111 makeLastInChain(window, chain); 0112 } else { 0113 insertWindowIntoChain(window, chain); 0114 } 0115 } 0116 0117 void FocusChain::insertWindowIntoChain(Window *window, Chain &chain) 0118 { 0119 Q_ASSERT(!window->isDeleted()); 0120 if (chain.contains(window)) { 0121 return; 0122 } 0123 if (m_activeWindow && m_activeWindow != window && !chain.empty() && chain.last() == m_activeWindow) { 0124 // Add it after the active window 0125 chain.insert(chain.size() - 1, window); 0126 } else { 0127 // Otherwise add as the first one 0128 chain.append(window); 0129 } 0130 } 0131 0132 void FocusChain::moveAfterWindow(Window *window, Window *reference) 0133 { 0134 Q_ASSERT(!window->isDeleted()); 0135 if (!window->wantsTabFocus()) { 0136 return; 0137 } 0138 0139 for (auto it = m_desktopFocusChains.begin(); 0140 it != m_desktopFocusChains.end(); 0141 ++it) { 0142 if (!window->isOnDesktop(it.key())) { 0143 continue; 0144 } 0145 moveAfterWindowInChain(window, reference, it.value()); 0146 } 0147 moveAfterWindowInChain(window, reference, m_mostRecentlyUsed); 0148 } 0149 0150 void FocusChain::moveAfterWindowInChain(Window *window, Window *reference, Chain &chain) 0151 { 0152 Q_ASSERT(!window->isDeleted()); 0153 if (!chain.contains(reference)) { 0154 return; 0155 } 0156 if (Window::belongToSameApplication(reference, window)) { 0157 chain.removeAll(window); 0158 chain.insert(chain.indexOf(reference), window); 0159 } else { 0160 chain.removeAll(window); 0161 for (int i = chain.size() - 1; i >= 0; --i) { 0162 if (Window::belongToSameApplication(reference, chain.at(i))) { 0163 chain.insert(i, window); 0164 break; 0165 } 0166 } 0167 } 0168 } 0169 0170 Window *FocusChain::firstMostRecentlyUsed() const 0171 { 0172 if (m_mostRecentlyUsed.isEmpty()) { 0173 return nullptr; 0174 } 0175 return m_mostRecentlyUsed.first(); 0176 } 0177 0178 Window *FocusChain::nextMostRecentlyUsed(Window *reference) const 0179 { 0180 if (m_mostRecentlyUsed.isEmpty()) { 0181 return nullptr; 0182 } 0183 const int index = m_mostRecentlyUsed.indexOf(reference); 0184 if (index == -1) { 0185 return m_mostRecentlyUsed.first(); 0186 } 0187 if (index == 0) { 0188 return m_mostRecentlyUsed.last(); 0189 } 0190 return m_mostRecentlyUsed.at(index - 1); 0191 } 0192 0193 // copied from activation.cpp 0194 bool FocusChain::isUsableFocusCandidate(Window *c, Window *prev) const 0195 { 0196 return c != prev && !c->isShade() && c->isShown() && c->isOnCurrentDesktop() && c->isOnCurrentActivity() && (!m_separateScreenFocus || c->isOnOutput(prev ? prev->output() : workspace()->activeOutput())); 0197 } 0198 0199 Window *FocusChain::nextForDesktop(Window *reference, VirtualDesktop *desktop) const 0200 { 0201 auto it = m_desktopFocusChains.constFind(desktop); 0202 if (it == m_desktopFocusChains.constEnd()) { 0203 return nullptr; 0204 } 0205 const auto &chain = it.value(); 0206 for (int i = chain.size() - 1; i >= 0; --i) { 0207 auto window = chain.at(i); 0208 if (isUsableFocusCandidate(window, reference)) { 0209 return window; 0210 } 0211 } 0212 return nullptr; 0213 } 0214 0215 void FocusChain::makeFirstInChain(Window *window, Chain &chain) 0216 { 0217 Q_ASSERT(!window->isDeleted()); 0218 chain.removeAll(window); 0219 chain.append(window); 0220 } 0221 0222 void FocusChain::makeLastInChain(Window *window, Chain &chain) 0223 { 0224 Q_ASSERT(!window->isDeleted()); 0225 chain.removeAll(window); 0226 chain.prepend(window); 0227 } 0228 0229 bool FocusChain::contains(Window *window, VirtualDesktop *desktop) const 0230 { 0231 auto it = m_desktopFocusChains.constFind(desktop); 0232 if (it == m_desktopFocusChains.constEnd()) { 0233 return false; 0234 } 0235 return it.value().contains(window); 0236 } 0237 0238 } // namespace 0239 0240 #include "moc_focuschain.cpp"