summaryrefslogtreecommitdiffstats
path: root/kkbswitch/kbswitchapp.cpp
blob: 50ce80acde47483cbfc718c215077d6ebce74f23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/***************************************************************************
                          kbswitchapp.cpp  -  description
                             -------------------
    begin                : Sun Jul 1 2001
    copyright            : (C) 2001 by Leonid Zeitlin
    email                : lz@europe.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "config.h" 
#include "kbswitchapp.h"
#include "kbconfigdlg.h"
#include "singlewindowwatcher.h"
#include "windowclasswatcher.h"

#include <kdebug.h>
#include <tdelocale.h>
#include <twinmodule.h>

KBSwitchApp::KBSwitchApp()
{
#ifndef HAVE_LIBXKLAVIER
  //m_twin_module = NULL;
  m_watcher = NULL;
#endif	
	if (!m_xkb.xkbAvailable()) return; // oops! No XKB in the server
  m_kbconf.load(config());
  m_cur_groupno = m_next_groupno = -1;
#ifdef HAVE_LIBXKLAVIER
  XklSetGroupPerApp(m_kbconf.group_scope() != KBConfig::SCOPE_GLOBAL);
  if (m_kbconf.toggle_mode())
    XklSetSecondaryGroupsMask(12); /* binary 1100 */
#else
  //resetWindowMap();
  //enableKWinModule();
  enableWatcher();
#endif

  m_intf = new KBSwitchIntf(this, &m_kbconf);
  TQObject::connect(m_intf, SIGNAL(nextGroupSelected()), this, SLOT(slotSelectNextGroup()));
  TQObject::connect(m_intf, SIGNAL(groupSelected(int)), this, SLOT(slotGroupSelected(int)));

  TQObject::connect(&m_xkb, SIGNAL(layoutChanged()), this, SLOT(reconfigure()));
  TQObject::connect(&m_xkb, SIGNAL(groupChanged(int)), this, SLOT(slotXkbGroupChanged(int)));

  m_force_group_setting = false;
  int start_group = m_kbconf.default_groupno();

  m_trayicon = new KBSwitchTrayIcon(&m_kbconf);
  TQObject::connect(m_trayicon, SIGNAL(groupSelected(int)), this,
    SLOT(slotGroupSelected(int)));
  TQObject::connect(m_trayicon, SIGNAL(clicked()), this, SLOT(slotSelectNextGroup()));
  TQObject::connect(m_trayicon, SIGNAL(preferencesSelected()), this,
    SLOT(slotPreferences()));
  
  if (start_group != m_xkb.getGroupNo()) {
    setStartGroup(start_group);
  }
  else {
    adaptToGroup(start_group);
  }

  setMainWidget(m_trayicon);
  m_trayicon->show();

#ifdef HAVE_LIBXKLAVIER  
  XklStartListen();
#endif
}

KBSwitchApp::~KBSwitchApp(){
#ifndef HAVE_LIBXKLAVIER  	
  //disableKWinModule();
  disableWatcher();
#endif
}

/** No descriptions */
bool KBSwitchApp::x11EventFilter(XEvent *e){
  // let m_xkb process the event and emit signals if necessary
  m_xkb.processEvent(e);
  return TDEApplication::x11EventFilter(e);
}

/** Update the tray icon to show the flag corresponding to the current keyboard group */
void KBSwitchApp::updateIcon(int groupno){
  if (groupno >= 0 && groupno < m_kbconf.groupCount()) { // check just in case
    m_trayicon->updateTrayIcon(groupno);
    if (m_kbconf.toggle_mode()) m_trayicon->setToggleGroups(m_cur_groupno, m_next_groupno);
  }
}

/** No descriptions */
void KBSwitchApp::slotGroupSelected(int groupno) 
{
#ifdef HAVE_LIBXKLAVIER
  XklAllowOneSwitchToSecondaryGroup();
  m_xkb.setGroupNo(groupno);
#else
  if (m_cur_groupno != groupno) {
    setGroups(groupno, m_cur_groupno);
  }
#endif  
}

/** No descriptions */
void KBSwitchApp::internalToggleGroups()
{
  int tmp = m_next_groupno;
  m_next_groupno = m_cur_groupno;
  m_cur_groupno = tmp;
}

/** No descriptions */
void KBSwitchApp::forceSetGroup(int groupno)
{
  m_force_group_setting = true;
#ifndef HAVE_LIBXKLAVIER  
  //if (m_active_window != m_window_map.end())
  //  m_active_window.data().groupno = groupno;
#endif    
  m_xkb.setGroupNo(groupno);
}

/** No descriptions */
void KBSwitchApp::slotSelectNextGroup()
{
#ifdef HAVE_LIBXKLAVIER
  m_xkb.setGroupNo(XklGetNextGroup());
#else  
  //forceSetGroup(m_next_groupno);
  m_xkb.setGroupNo(m_next_groupno);
#endif  
}

/** No descriptions */
void KBSwitchApp::slotPreferences(){
  KBConfigDlg dlg(&m_kbconf);
  TQObject::connect(&dlg, SIGNAL(okClicked()), m_trayicon, SLOT(slotUpdateIcons()));
  TQObject::connect(&dlg, SIGNAL(applyClicked()), m_trayicon, SLOT(slotUpdateIcons()));
  TQObject::connect(&dlg, SIGNAL(okClicked()), this, SLOT(slotPrefChanged()));
  TQObject::connect(&dlg, SIGNAL(applyClicked()), this, SLOT(slotPrefChanged()));
  dlg.exec();
}

/** Called when XKeyboard configuration changes */
void KBSwitchApp::reconfigure(){
  m_kbconf.load(config());
  m_trayicon->reconfigure();

  // make sure default group is still valid
  if (m_kbconf.default_groupno() >= m_xkb.getNumKbdGroups())
    m_kbconf.set_default_groupno(0);
    
  int groupno = m_xkb.getGroupNo();
  if (groupno >= m_xkb.getNumKbdGroups()) {
    // current group no longer valid, reset to default group
    setStartGroup(m_kbconf.default_groupno());
  }
  else if (!m_force_group_setting) {
    adaptToGroup(groupno);
  }
#ifndef HAVE_LIBXKLAVIER  
  //resetWindowMap();
  if (m_watcher) m_watcher->reset();
#endif  
}

/** Respond to XKB changing the current group */
void KBSwitchApp::slotXkbGroupChanged(int groupno){
#ifdef HAVE_LIBXKLAVIER
  updateIcon(groupno);
/*  XWindowAttributes attrs;
  XGetWindowAttributes(tqt_xdisplay(), tqt_xrootwin(), &attrs);
  kdDebug() << "root event mask is " << attrs.your_event_mask << endl;
  kdDebug() << "SubstructureNotifyMask is " <<
   ((attrs.your_event_mask & SubstructureNotifyMask) ? "ON" : "OFF") << endl;*/
#else
  bool accept = false;
  if (m_force_group_setting) { // the change of group is forced by us
    if (groupno == m_cur_groupno) { // the group is what we wanted, fine
      m_force_group_setting = false;
      accept = true;
    }
    else { // oops, not the group we expected
      forceSetGroup(m_cur_groupno); 
    }
  }
  else { // the change is caused by something external, we didn't request it
    if (m_kbconf.toggle_mode() && groupno != m_next_groupno) { // toggle mode, and the group is not correct
      m_xkb.setGroupNo(m_next_groupno);
    }
    else { // adjust to this group
      if (m_kbconf.toggle_mode()) internalToggleGroups();
      else {
        m_cur_groupno = groupno;
        m_next_groupno = m_kbconf.getNextGroup(groupno);
      }
      accept = true;
    }
  }
  if (accept) {
    updateIcon(groupno);
    if (m_watcher) m_watcher->changeGroup(groupno, m_next_groupno);
  }
  /*if (m_kbconf.toggle_mode() && !force_group_setting && groupno != m_next_groupno) {
    forceSetGroup(m_next_groupno);
  }
  else {
    updateIcon(groupno);
    force_group_setting = false;
    if (m_kbconf.toggle_mode()) internalToggleGroups();
    else {
      kdDebug() << "slotXkbGroupChanged, change group to " << groupno << endl;
      m_cur_groupno = groupno;
      m_next_groupno = m_kbconf.getNextGroup(groupno);
    }
    if (m_watcher) m_watcher->changeGroup(groupno, m_next_groupno);    
  }*/
#endif  
}

/** Set the keyboard to the given group and set internal variable accordingly */
void KBSwitchApp::setStartGroup(int start_group){
  /*m_next_groupno = start_group;
  if (m_kbconf.toggle_mode()) {
    m_cur_groupno = getNextGroup(start_group);
  }
  else {
    m_cur_groupno = start_group;
  }
  forceSetGroup(m_next_groupno);*/
  setGroups(start_group, m_kbconf.getNextGroup(start_group));
}

/** adapt internal state to the given group */
void KBSwitchApp::adaptToGroup(int groupno) {
  if (! m_kbconf.toggle_mode() || // if not in toggle mode
      (m_cur_groupno != groupno    // or in toggle mode and internal variables are invalid
      || m_next_groupno >= m_kbconf.groupCount()
      || m_next_groupno == m_cur_groupno)) {
      m_cur_groupno = groupno;
      m_next_groupno = m_kbconf.getNextGroup(groupno);
  }
#ifndef HAVE_LIBXKLAVIER  
  if (m_watcher) m_watcher->changeGroup(groupno, m_next_groupno);
  /*if (m_active_window != m_window_map.end()) {
    m_active_window.data().groupno = groupno;
    m_active_window.data().next_groupno = m_next_groupno;
  }*/
#endif  
  updateIcon(groupno);
}

/*void KBSwitchApp::slotWindowChanged(WId activeWindow)
{
#ifndef HAVE_LIBXKLAVIER
  m_active_window = m_window_map.find(activeWindow);
  if (m_active_window == m_window_map.end())
    addWindowToMap(activeWindow);
  if (m_active_window.data().groupno != m_cur_groupno) {
    setGroups(m_active_window.data().groupno, m_active_window.data().next_groupno);
  }
  else m_next_groupno = m_active_window.data().next_groupno;
#endif  
}*/

void KBSwitchApp::slotWindowChanged(int groupno, int next_groupno) 
{
  if (groupno != m_cur_groupno) {
    setGroups(groupno, next_groupno);
  }  
  else m_next_groupno = next_groupno;
}

/*void KBSwitchApp::slotWindowRemoved(WId window)
{
#ifndef HAVE_LIBXKLAVIER
  m_window_map.remove(window);
#endif  
}

#ifndef HAVE_LIBXKLAVIER
void KBSwitchApp::resetWindowMap()
{
  WId active_window_id;

  m_window_map.clear();
  
  if (m_twin_module && (active_window_id = m_twin_module->activeWindow())) 
    addWindowToMap(active_window_id);
  else
    m_active_window = m_window_map.end();
}*/

/** Enable window manager notifications */
/*void KBSwitchApp::enableKWinModule()
{
  if (m_twin_module == NULL) {
    m_twin_module = new KWinModule();
    connect(m_twin_module, SIGNAL(activeWindowChanged(WId)), SLOT(slotWindowChanged(WId)));
    connect(m_twin_module, SIGNAL(windowRemoved(WId)), SLOT(slotWindowRemoved(WId)));
    resetWindowMap();
    if (m_cur_groupno != -1 && m_cur_groupno != m_kbconf.default_groupno())
      setStartGroup(m_kbconf.default_groupno());
  }
}*/

/** Disable window manager notifications */
/*void KBSwitchApp::disableKWinModule()
{
  if (m_twin_module) {
    m_twin_module->disconnect();
    delete m_twin_module;
    m_twin_module = NULL;
    resetWindowMap();
  }
}*/

/** adds a new window to the internal window map */
/*void KBSwitchApp::addWindowToMap(WId window_id)
{
  KBWinInfo wininfo = { m_kbconf.default_groupno(),
    m_kbconf.getNextGroup(m_kbconf.default_groupno()) };

  m_active_window = m_window_map.insert(window_id, wininfo);
}
#endif*/

void KBSwitchApp::enableWatcher()
{
  if (!m_watcher) {
    m_watcher_type = m_kbconf.group_scope();
    if (m_watcher_type == KBConfig::SCOPE_WINDOW)
      m_watcher = new SingleWindowWatcher(&m_kbconf, this);
    else if (m_watcher_type == KBConfig::SCOPE_CLASS)
      m_watcher = new WindowClassWatcher(&m_kbconf, this);
    else return; // if scope is global, don't create watcher
    connect(m_watcher, SIGNAL(windowChanged(int, int )), 
      SLOT(slotWindowChanged(int, int)));
  }     
}

void KBSwitchApp::disableWatcher()
{
  if (m_watcher) {
    m_watcher->disconnect();
    delete m_watcher;
    m_watcher = NULL;
  }  
}

/** React to a change in KKBSwitch's user preferences,
  * made by user in Configure dialog */
void KBSwitchApp::slotPrefChanged()
{
#ifndef HAVE_LIBXKLAVIER	
  if (m_kbconf.group_scope() != m_watcher_type) {
    disableWatcher();
    enableWatcher();
  }  
#endif    
}

/** Set the current and next groups */
void KBSwitchApp::setGroups(int group, int next_group)
{
  m_cur_groupno = group;
  m_next_groupno = next_group;
  forceSetGroup(group);
}