File indexing completed on 2024-12-08 13:18:52

0001 /*
0002    SPDX-FileCopyrightText: 1999-2001 Lubos Lunak <l.lunak@kde.org>
0003    SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
0004 
0005    SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "action_data/action_data.h"
0009 #include "triggers/triggers.h"
0010 #include "windows_handler.h"
0011 #include "windows_helper/window_selection_list.h"
0012 
0013 #include <KConfig>
0014 #include <KConfigGroup>
0015 #include <QDebug>
0016 
0017 #include <X11/X.h>
0018 
0019 namespace KHotKeys
0020 {
0021 WindowTriggerVisitor::~WindowTriggerVisitor()
0022 {
0023 }
0024 
0025 WindowTrigger::WindowTrigger(ActionData *data_P, Windowdef_list *windows_P, WindowEvents window_actions_P)
0026     : Trigger(data_P)
0027     , _windows(windows_P)
0028     , window_actions(window_actions_P)
0029     , existing_windows()
0030     , last_active_window(None)
0031     , active(true)
0032 {
0033     if (!_windows) {
0034         _windows = new Windowdef_list("Windowdef_list comment");
0035     }
0036 
0037     Q_ASSERT(_windows->isEmpty());
0038     init();
0039 }
0040 
0041 WindowTrigger::~WindowTrigger()
0042 {
0043     //    qDebug() << "~WindowTrigger :" << this;
0044     disconnect(windows_handler, nullptr, this, nullptr);
0045     delete _windows;
0046 }
0047 
0048 void WindowTrigger::accept(TriggerVisitor &visitor)
0049 {
0050     if (WindowTriggerVisitor *v = dynamic_cast<WindowTriggerVisitor *>(&visitor)) {
0051         v->visit(*this);
0052     } else {
0053         qDebug() << "Visitor error";
0054     }
0055 }
0056 
0057 void WindowTrigger::init()
0058 {
0059     qDebug() << "WindowTrigger::init()";
0060     disconnect(windows_handler, nullptr, this, nullptr);
0061     connect(windows_handler, SIGNAL(window_added(WId)), this, SLOT(window_added(WId)));
0062     connect(windows_handler, SIGNAL(window_removed(WId)), this, SLOT(window_removed(WId)));
0063     if (window_actions & (WINDOW_ACTIVATES | WINDOW_DEACTIVATES /*| WINDOW_DISAPPEARS*/))
0064         connect(windows_handler, SIGNAL(active_window_changed(WId)), this, SLOT(active_window_changed(WId)));
0065     // clang-format off
0066     connect(windows_handler, SIGNAL(window_changed(WId,uint)), this, SLOT(window_changed(WId,uint)));
0067     // clang-format on
0068 }
0069 
0070 void WindowTrigger::activate(bool activate_P)
0071 {
0072     active = activate_P;
0073 }
0074 
0075 void WindowTrigger::active_window_changed(WId window_P)
0076 {
0077     if (!existing_windows.contains(window_P)) {
0078         existing_windows[window_P] = windows()->match(Window_data(window_P));
0079     }
0080 
0081     if (!active || !khotkeys_active()) {
0082         // We still keep track of the last active window so we have valid data
0083         // if khotkeys is switched on again.
0084         last_active_window = window_P;
0085         return;
0086     }
0087 
0088     // Check if the last active window was a match for us
0089     bool was_match = existing_windows.contains(last_active_window) ? existing_windows[last_active_window] : false;
0090 
0091     if (was_match && (window_actions & WINDOW_DEACTIVATES)) {
0092         windows_handler->set_action_window(window_P);
0093         data->execute();
0094     }
0095 
0096     if (existing_windows[window_P] && (window_actions & WINDOW_ACTIVATES)) {
0097         qDebug() << "Executing data";
0098         windows_handler->set_action_window(window_P);
0099         data->execute();
0100     }
0101 
0102     last_active_window = window_P;
0103 }
0104 
0105 void WindowTrigger::cfg_write(KConfigGroup &cfg_P) const
0106 {
0107     base::cfg_write(cfg_P);
0108     KConfigGroup windowsConfig(cfg_P.config(), cfg_P.name() + "Windows");
0109     if (windows()) {
0110         windows()->cfg_write(windowsConfig);
0111     }
0112     cfg_P.writeEntry("WindowActions", static_cast<int>(window_actions));
0113     cfg_P.writeEntry("Type", "WINDOW"); // overwrites value set in base::cfg_write()
0114 }
0115 
0116 const QString WindowTrigger::description() const
0117 {
0118     return i18n("Window trigger: ") + windows()->comment();
0119 }
0120 
0121 void WindowTrigger::setOnWindowEvents(WindowEvents events)
0122 {
0123     window_actions = events;
0124     init();
0125 }
0126 
0127 void WindowTrigger::set_window_rules(Windowdef_list *list)
0128 {
0129     delete _windows;
0130     _windows = list;
0131 }
0132 
0133 bool WindowTrigger::triggers_on(window_action_t w_action_P) const
0134 {
0135     return window_actions & w_action_P;
0136 }
0137 
0138 void WindowTrigger::window_added(WId window_P)
0139 {
0140     // Always keep track of windows,
0141     existing_windows[window_P] = windows()->match(Window_data(window_P));
0142 
0143     if (!active || !khotkeys_active()) {
0144         return;
0145     }
0146 
0147     if (existing_windows[window_P] && (window_actions & WINDOW_APPEARS)) {
0148         windows_handler->set_action_window(window_P);
0149         data->execute();
0150     }
0151 }
0152 
0153 void WindowTrigger::window_removed(WId window_P)
0154 {
0155     // Always keep track of windows,
0156     bool matches = false;
0157     if (existing_windows.contains(window_P)) {
0158         matches = existing_windows[window_P];
0159         existing_windows.remove(window_P);
0160     }
0161 
0162     if (!active || !khotkeys_active()) {
0163         return;
0164     }
0165 
0166     if (matches && (window_actions & WINDOW_DISAPPEARS)) {
0167         windows_handler->set_action_window(window_P);
0168         data->execute();
0169     }
0170 }
0171 
0172 void WindowTrigger::window_changed(WId window_P, unsigned int dirty_P)
0173 {
0174     if (!(dirty_P & (NET::WMName | NET::WMWindowType)))
0175         return;
0176 
0177     // Check if the old state was a match
0178     bool was_match = false;
0179     if (existing_windows.contains(window_P))
0180         was_match = existing_windows[window_P];
0181 
0182     // Check if the new state is a match
0183     bool matches = windows()->match(Window_data(window_P));
0184     existing_windows[window_P] = matches;
0185 
0186     if (!active || !khotkeys_active()) {
0187         return;
0188     }
0189 
0190     if (matches && !was_match) {
0191         if (window_actions & WINDOW_APPEARS) {
0192             windows_handler->set_action_window(window_P);
0193             data->execute();
0194         } else if (window_actions & WINDOW_ACTIVATES && window_P == windows_handler->active_window()) {
0195             windows_handler->set_action_window(window_P);
0196             data->execute();
0197         }
0198     }
0199 }
0200 
0201 const Windowdef_list *WindowTrigger::windows() const
0202 {
0203     return _windows;
0204 }
0205 
0206 Windowdef_list *WindowTrigger::windows()
0207 {
0208     return _windows;
0209 }
0210 
0211 WindowTrigger *WindowTrigger::copy(ActionData *data_P) const
0212 {
0213     WindowTrigger *ret = new WindowTrigger(data_P ? data_P : data, windows()->copy(), window_actions);
0214     ret->existing_windows = existing_windows; // CHECKME je tohle vazne treba ?
0215     return ret;
0216 }
0217 
0218 } // namespace KHotKeys