File indexing completed on 2024-11-10 04:56:49

0001 /*
0002     windows.cpp
0003 
0004     SPDX-FileCopyrightText: 1997 Patrick Dowler <dowler@morgul.fsh.uvic.ca>
0005     SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <QApplication>
0011 #include <QCheckBox>
0012 #include <QFormLayout>
0013 #include <QGroupBox>
0014 #include <QHBoxLayout>
0015 #include <QLabel>
0016 #include <QRadioButton>
0017 #include <QScreen>
0018 #include <QtDBus>
0019 
0020 #include <KConfig>
0021 #include <KConfigGroup>
0022 #include <KLocalizedString>
0023 #include <KWindowSystem>
0024 
0025 #include "kwinoptions_settings.h"
0026 #include "windows.h"
0027 #include <kwin_effects_interface.h>
0028 
0029 #include "kwinoptions_kdeglobals_settings.h"
0030 #include "kwinoptions_settings.h"
0031 
0032 #define CLICK_TO_FOCUS 0
0033 #define CLICK_TO_FOCUS_MOUSE_PRECEDENT 1
0034 #define FOCUS_FOLLOWS_MOUSE 2
0035 #define FOCUS_FOLLOWS_MOUSE_PRECEDENT 3
0036 #define FOCUS_UNDER_MOUSE 4
0037 #define FOCUS_STRICTLY_UNDER_MOUSE 5
0038 
0039 namespace
0040 {
0041 constexpr int defaultFocusPolicyIndex = CLICK_TO_FOCUS;
0042 }
0043 
0044 KWinFocusConfigForm::KWinFocusConfigForm(QWidget *parent)
0045     : QWidget(parent)
0046 {
0047     setupUi(parent);
0048 }
0049 
0050 KFocusConfig::KFocusConfig(bool _standAlone, KWinOptionsSettings *settings, QWidget *parent)
0051     : KCModule(parent, KPluginMetaData())
0052     , standAlone(_standAlone)
0053     , m_ui(new KWinFocusConfigForm(widget()))
0054 {
0055     if (settings) {
0056         initialize(settings);
0057     }
0058 }
0059 
0060 void KFocusConfig::initialize(KWinOptionsSettings *settings)
0061 {
0062     m_settings = settings;
0063     addConfig(m_settings, widget());
0064 
0065     connect(m_ui->windowFocusPolicy, qOverload<int>(&QComboBox::currentIndexChanged), this, &KFocusConfig::focusPolicyChanged);
0066     connect(m_ui->windowFocusPolicy, qOverload<int>(&QComboBox::currentIndexChanged), this, &KFocusConfig::updateDefaultIndicator);
0067     connect(this, SIGNAL(defaultsIndicatorsVisibleChanged(bool)), this, SLOT(updateDefaultIndicator()));
0068 
0069     connect(qApp, &QGuiApplication::screenAdded, this, &KFocusConfig::updateMultiScreen);
0070     connect(qApp, &QGuiApplication::screenRemoved, this, &KFocusConfig::updateMultiScreen);
0071     updateMultiScreen();
0072 }
0073 
0074 void KFocusConfig::updateMultiScreen()
0075 {
0076     m_ui->multiscreenBehaviorLabel->setVisible(QApplication::screens().count() > 1);
0077     m_ui->kcfg_SeparateScreenFocus->setVisible(QApplication::screens().count() > 1);
0078 }
0079 
0080 void KFocusConfig::updateDefaultIndicator()
0081 {
0082     const bool isDefault = m_ui->windowFocusPolicy->currentIndex() == defaultFocusPolicyIndex;
0083     m_ui->windowFocusPolicy->setProperty("_kde_highlight_neutral", defaultsIndicatorsVisible() && !isDefault);
0084     m_ui->windowFocusPolicy->update();
0085 }
0086 
0087 void KFocusConfig::updateFocusPolicyExplanatoryText()
0088 {
0089     const int focusPolicy = m_ui->windowFocusPolicy->currentIndex();
0090     switch (focusPolicy) {
0091     case CLICK_TO_FOCUS:
0092         m_ui->windowFocusPolicyDescriptionLabel->setText(i18n("<em>Click to focus:</em> A window becomes active when you click into it. This behavior is common on other operating systems and likely what you want."));
0093         break;
0094     case CLICK_TO_FOCUS_MOUSE_PRECEDENT:
0095         m_ui->windowFocusPolicyDescriptionLabel->setText(i18n("<em>Click to focus (mouse precedence):</em> Mostly the same as <em>Click to focus</em>. If an active window has to be chosen by the system (eg. because the currently active one was closed) the window under the mouse is the preferred candidate. Unusual, but possible variant of <em>Click to focus</em>."));
0096         break;
0097     case FOCUS_FOLLOWS_MOUSE:
0098         m_ui->windowFocusPolicyDescriptionLabel->setText(i18n("<em>Focus follows mouse:</em> Moving the mouse onto a window will activate it. Eg. windows randomly appearing under the mouse will not gain the focus. <em>Focus stealing prevention</em> takes place as usual. Think as <em>Click to focus</em> just without having to actually click."));
0099         break;
0100     case FOCUS_FOLLOWS_MOUSE_PRECEDENT:
0101         m_ui->windowFocusPolicyDescriptionLabel->setText(i18n("This is mostly the same as <em>Focus follows mouse</em>. If an active window has to be chosen by the system (eg. because the currently active one was closed) the window under the mouse is the preferred candidate. Choose this, if you want a hover controlled focus."));
0102         break;
0103     case FOCUS_UNDER_MOUSE:
0104         m_ui->windowFocusPolicyDescriptionLabel->setText(i18n("<em>Focus under mouse:</em> The focus always remains on the window under the mouse.<br/><strong>Warning:</strong> <em>Focus stealing prevention</em> and the <em>tabbox ('Alt+Tab')</em> contradict the activation policy and will not work. You very likely want to use <em>Focus follows mouse (mouse precedence)</em> instead!"));
0105         break;
0106     case FOCUS_STRICTLY_UNDER_MOUSE:
0107         m_ui->windowFocusPolicyDescriptionLabel->setText(i18n("<em>Focus strictly under mouse:</em> The focus is always on the window under the mouse (in doubt nowhere) very much like the focus behavior in an unmanaged legacy X11 environment.<br/><strong>Warning:</strong> <em>Focus stealing prevention</em> and the <em>tabbox ('Alt+Tab')</em> contradict the activation policy and will not work. You very likely want to use <em>Focus follows mouse (mouse precedence)</em> instead!"));
0108         break;
0109     }
0110 }
0111 
0112 void KFocusConfig::focusPolicyChanged()
0113 {
0114     int selectedFocusPolicy = 0;
0115     bool selectedNextFocusPrefersMouseItem = false;
0116     const bool loadedNextFocusPrefersMouseItem = m_settings->nextFocusPrefersMouse();
0117 
0118     updateFocusPolicyExplanatoryText();
0119 
0120     int focusPolicy = m_ui->windowFocusPolicy->currentIndex();
0121     switch (focusPolicy) {
0122     case CLICK_TO_FOCUS:
0123         selectedFocusPolicy = KWinOptionsSettings::EnumFocusPolicy::ClickToFocus;
0124         break;
0125     case CLICK_TO_FOCUS_MOUSE_PRECEDENT:
0126         selectedFocusPolicy = KWinOptionsSettings::EnumFocusPolicy::ClickToFocus;
0127         selectedNextFocusPrefersMouseItem = true;
0128         break;
0129     case FOCUS_FOLLOWS_MOUSE:
0130         selectedFocusPolicy = KWinOptionsSettings::EnumFocusPolicy::FocusFollowsMouse;
0131         break;
0132     case FOCUS_FOLLOWS_MOUSE_PRECEDENT:
0133         selectedFocusPolicy = KWinOptionsSettings::EnumFocusPolicy::FocusFollowsMouse;
0134         selectedNextFocusPrefersMouseItem = true;
0135         break;
0136     case FOCUS_UNDER_MOUSE:
0137         selectedFocusPolicy = KWinOptionsSettings::EnumFocusPolicy::FocusUnderMouse;
0138         break;
0139     case FOCUS_STRICTLY_UNDER_MOUSE:
0140         selectedFocusPolicy = KWinOptionsSettings::EnumFocusPolicy::FocusStrictlyUnderMouse;
0141         break;
0142     }
0143 
0144     unmanagedWidgetChangeState(m_settings->focusPolicy() != selectedFocusPolicy || loadedNextFocusPrefersMouseItem != selectedNextFocusPrefersMouseItem);
0145 
0146     unmanagedWidgetDefaultState(focusPolicy == defaultFocusPolicyIndex);
0147 
0148     // the auto raise related widgets are: autoRaise
0149     m_ui->kcfg_AutoRaise->setEnabled(focusPolicy != CLICK_TO_FOCUS && focusPolicy != CLICK_TO_FOCUS_MOUSE_PRECEDENT);
0150 
0151     m_ui->kcfg_FocusStealingPreventionLevel->setDisabled(focusPolicy == FOCUS_UNDER_MOUSE || focusPolicy == FOCUS_STRICTLY_UNDER_MOUSE);
0152 
0153     // the delayed focus related widgets are: delayFocus
0154     m_ui->delayFocusOnLabel->setEnabled(focusPolicy != CLICK_TO_FOCUS);
0155     m_ui->kcfg_DelayFocusInterval->setEnabled(focusPolicy != CLICK_TO_FOCUS);
0156 }
0157 
0158 void KFocusConfig::load(void)
0159 {
0160     KCModule::load();
0161 
0162     const bool loadedNextFocusPrefersMouseItem = m_settings->nextFocusPrefersMouse();
0163 
0164     int focusPolicy = m_settings->focusPolicy();
0165 
0166     switch (focusPolicy) {
0167     // the ClickToFocus and FocusFollowsMouse have special values when
0168     // NextFocusPrefersMouse is true
0169     case KWinOptionsSettings::EnumFocusPolicy::ClickToFocus:
0170         m_ui->windowFocusPolicy->setCurrentIndex(CLICK_TO_FOCUS + loadedNextFocusPrefersMouseItem);
0171         break;
0172     case KWinOptionsSettings::EnumFocusPolicy::FocusFollowsMouse:
0173         m_ui->windowFocusPolicy->setCurrentIndex(FOCUS_FOLLOWS_MOUSE + loadedNextFocusPrefersMouseItem);
0174         break;
0175     default:
0176         // +2 to ignore the two special values
0177         m_ui->windowFocusPolicy->setCurrentIndex(focusPolicy + 2);
0178         break;
0179     }
0180     updateFocusPolicyExplanatoryText();
0181 }
0182 
0183 void KFocusConfig::save(void)
0184 {
0185     KCModule::save();
0186 
0187     int idxFocusPolicy = m_ui->windowFocusPolicy->currentIndex();
0188     switch (idxFocusPolicy) {
0189     case CLICK_TO_FOCUS:
0190     case CLICK_TO_FOCUS_MOUSE_PRECEDENT:
0191         m_settings->setFocusPolicy(KWinOptionsSettings::EnumFocusPolicy::ClickToFocus);
0192         break;
0193     case FOCUS_FOLLOWS_MOUSE:
0194     case FOCUS_FOLLOWS_MOUSE_PRECEDENT:
0195         // the ClickToFocus and FocusFollowsMouse have special values when
0196         // NextFocusPrefersMouse is true
0197         m_settings->setFocusPolicy(KWinOptionsSettings::EnumFocusPolicy::FocusFollowsMouse);
0198         break;
0199     case FOCUS_UNDER_MOUSE:
0200         m_settings->setFocusPolicy(KWinOptionsSettings::EnumFocusPolicy::FocusUnderMouse);
0201         break;
0202     case FOCUS_STRICTLY_UNDER_MOUSE:
0203         m_settings->setFocusPolicy(KWinOptionsSettings::EnumFocusPolicy::FocusStrictlyUnderMouse);
0204         break;
0205     }
0206     m_settings->setNextFocusPrefersMouse(idxFocusPolicy == CLICK_TO_FOCUS_MOUSE_PRECEDENT || idxFocusPolicy == FOCUS_FOLLOWS_MOUSE_PRECEDENT);
0207 
0208     m_settings->save();
0209 
0210     if (standAlone) {
0211         // Send signal to all kwin instances
0212         QDBusMessage message =
0213             QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
0214         QDBusConnection::sessionBus().send(message);
0215     }
0216 }
0217 
0218 void KFocusConfig::defaults()
0219 {
0220     KCModule::defaults();
0221     m_ui->windowFocusPolicy->setCurrentIndex(defaultFocusPolicyIndex);
0222 }
0223 
0224 KWinAdvancedConfigForm::KWinAdvancedConfigForm(QWidget *parent)
0225     : QWidget(parent)
0226 {
0227     setupUi(parent);
0228 }
0229 
0230 KAdvancedConfig::KAdvancedConfig(bool _standAlone, KWinOptionsSettings *settings, KWinOptionsKDEGlobalsSettings *globalSettings, QWidget *parent)
0231     : KCModule(parent, KPluginMetaData())
0232     , standAlone(_standAlone)
0233     , m_ui(new KWinAdvancedConfigForm(widget()))
0234 {
0235     if (settings && globalSettings) {
0236         initialize(settings, globalSettings);
0237     }
0238 }
0239 
0240 void KAdvancedConfig::initialize(KWinOptionsSettings *settings, KWinOptionsKDEGlobalsSettings *globalSettings)
0241 {
0242     m_settings = settings;
0243     addConfig(m_settings, widget());
0244     addConfig(globalSettings, widget());
0245 
0246     m_ui->kcfg_Placement->setItemData(KWinOptionsSettings::PlacementChoices::Smart, "Smart");
0247     m_ui->kcfg_Placement->setItemData(KWinOptionsSettings::PlacementChoices::Maximizing, "Maximizing");
0248     m_ui->kcfg_Placement->setItemData(KWinOptionsSettings::PlacementChoices::Random, "Random");
0249     m_ui->kcfg_Placement->setItemData(KWinOptionsSettings::PlacementChoices::Centered, "Centered");
0250     m_ui->kcfg_Placement->setItemData(KWinOptionsSettings::PlacementChoices::ZeroCornered, "ZeroCornered");
0251     m_ui->kcfg_Placement->setItemData(KWinOptionsSettings::PlacementChoices::UnderMouse, "UnderMouse");
0252 
0253     // Don't show the option to prevent apps from remembering their window
0254     // positions on Wayland because it doesn't work on Wayland and the feature
0255     // will eventually be implemented in a different way there.
0256     // This option lives in the kdeglobals file because it is consumed by
0257     // kxmlgui.
0258     m_ui->kcfg_AllowKDEAppsToRememberWindowPositions->setVisible(KWindowSystem::isPlatformX11());
0259 
0260     m_ui->kcfg_ActivationDesktopPolicy->setItemData(KWinOptionsSettings::ActivationDesktopPolicyChoices::SwitchToOtherDesktop, "SwitchToOtherDesktop");
0261     m_ui->kcfg_ActivationDesktopPolicy->setItemData(KWinOptionsSettings::ActivationDesktopPolicyChoices::BringToCurrentDesktop, "BringToCurrentDesktop");
0262 }
0263 
0264 void KAdvancedConfig::save(void)
0265 {
0266     KCModule::save();
0267 
0268     if (standAlone) {
0269         // Send signal to all kwin instances
0270         QDBusMessage message =
0271             QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
0272         QDBusConnection::sessionBus().send(message);
0273     }
0274 }
0275 
0276 KWinMovingConfigForm::KWinMovingConfigForm(QWidget *parent)
0277     : QWidget(parent)
0278 {
0279     setupUi(parent);
0280 }
0281 
0282 KMovingConfig::KMovingConfig(bool _standAlone, KWinOptionsSettings *settings, QWidget *parent)
0283     : KCModule(parent, KPluginMetaData())
0284     , standAlone(_standAlone)
0285     , m_ui(new KWinMovingConfigForm(widget()))
0286 {
0287     if (settings) {
0288         initialize(settings);
0289     }
0290 }
0291 
0292 void KMovingConfig::initialize(KWinOptionsSettings *settings)
0293 {
0294     m_settings = settings;
0295     addConfig(m_settings, widget());
0296 }
0297 
0298 void KMovingConfig::save(void)
0299 {
0300     KCModule::save();
0301 
0302     if (standAlone) {
0303         // Send signal to all kwin instances
0304         QDBusMessage message =
0305             QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
0306         QDBusConnection::sessionBus().send(message);
0307     }
0308 }
0309 
0310 #include "moc_windows.cpp"