]> icculus.org git repositories - mikachu/openbox.git/blob - util/epist/keytree.cc
added config.h includes
[mikachu/openbox.git] / util / epist / keytree.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // keytree.cc for Epistrophy - a key handler for NETWM/EWMH window managers.
3 // Copyright (c) 2002 - 2002 Ben Jansens <ben at orodu.net>
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
22
23 #ifdef    HAVE_CONFIG_H
24 #  include "../../config.h"
25 #endif // HAVE_CONFIG_H
26
27 #include "keytree.hh"
28 #include "epist.hh"
29 #include "config.hh"
30
31 #include <string>
32
33 using std::string;
34
35 keytree::keytree(Display *display, epist *ep)
36   : _display(display), _timeout_screen(NULL), _timer(NULL), _epist(ep)
37 {
38   _head = new keynode;
39   _head->parent = NULL;
40   _head->action = NULL; // head's action is always NULL
41   _current = _head;
42   // for complete initialization, initialize() has to be called as well. We
43   // call initialize() when we are certain that the config object (which the
44   // timer uses) has been fully initialized. (see parser::parse())
45 }
46
47 keytree::~keytree()
48 {
49   clearTree(_head);
50   delete _timer;
51 }
52
53 void keytree::unloadBindings()
54 {
55   ChildList::iterator it, end = _head->children.end();
56   for (it = _head->children.begin(); it != end; ++it)
57     clearTree(*it);
58
59   _head->children.clear();
60   reset();
61 }
62
63 void keytree::clearTree(keynode *node)
64 {
65   if (!node)
66     return;
67
68   ChildList::iterator it, end = node->children.end();
69   for (it = node->children.begin(); it != end; ++it)
70     clearTree(*it);
71
72   node->children.clear();
73
74   if (node->action)
75     delete node->action;
76   delete node;
77   node = NULL;
78 }
79
80 void keytree::grabDefaults(screen *scr)
81 {
82   grabChildren(_head, scr);
83 }
84
85 void keytree::ungrabDefaults(screen *scr)
86 {
87   ChildList::const_iterator it, end = _head->children.end();
88   for (it = _head->children.begin(); it != end; ++it)
89     if ( (*it)->action && (*it)->action->type() != Action::toggleGrabs)
90       scr->ungrabKey( (*it)->action->keycode(), (*it)->action->modifierMask() );
91 }
92
93 void keytree::grabChildren(keynode *node, screen *scr)
94 {
95   ChildList::const_iterator it, end = node->children.end();
96   for (it = node->children.begin(); it != end; ++it)
97     if ( (*it)->action )
98       scr->grabKey( (*it)->action->keycode(), (*it)->action->modifierMask() );
99 }
100
101 void keytree::ungrabChildren(keynode *node, screen *scr)
102 {
103   ChildList::const_iterator head_it, head_end = _head->children.end();
104   ChildList::const_iterator it, end = node->children.end();
105   bool ungrab = true;
106  
107   // when ungrabbing children, make sure that we don't ungrab any topmost keys
108   // (children of the head node) This would render those topmost keys useless.
109   // Topmost keys are _never_ ungrabbed, since they are only grabbed at startup
110   
111   for (it = node->children.begin(); it != end; ++it) {
112     if ( (*it)->action ) {
113       for (head_it = _head->children.begin(); head_it != head_end; ++head_it) {
114         if ( (*it)->action->modifierMask() == (*head_it)->action->modifierMask() &&
115              (*it)->action->keycode() == (*head_it)->action->keycode())
116         {
117           ungrab = false;
118           break;
119         }
120       }
121       
122       if (ungrab) 
123         scr->ungrabKey( (*it)->action->keycode(), (*it)->action->modifierMask());
124       
125       ungrab = true;
126     }
127   }
128 }
129
130 const Action * keytree::getAction(const XEvent &e, unsigned int state,
131                                   screen *scr)
132 {
133   Action *act;
134
135   // we're done with the children. ungrab them
136   if (_current != _head)
137     ungrabChildren(_current, scr);
138
139   ChildList::const_iterator it, end = _current->children.end();
140   for (it = _current->children.begin(); it != end; ++it) {
141     act = (*it)->action;
142     if (e.xkey.keycode == act->keycode() && state == act->modifierMask()) {
143       if (act->type() == Action::cancelChain) {
144         // user is cancelling the chain explicitly
145         _current = _head;
146         return (const Action *)NULL;
147       }
148       else if ( isLeaf(*it) ) {
149         // node is a leaf, so an action will be executed
150         if (_timer->isTiming()) {
151           _timer->stop();
152           _timeout_screen = NULL;
153         }
154
155         _current = _head;
156         return act;
157       }
158       else {
159         // node is not a leaf, so we advance down the tree, and grab the
160         // children of the new current node. no action is executed
161         if (_timer->isTiming())
162           _timer->stop();
163         _timer->start();
164         _timeout_screen = scr;
165
166         _current = *it;
167         grabChildren(_current, scr);
168         return (const Action *)NULL;
169       }
170     }
171   }
172
173   // action not found. back to the head
174   _current = _head;
175   return (const Action *)NULL;
176 }
177
178 void keytree::addAction(Action::ActionType action, unsigned int mask,
179                         string key, string arg)
180 {
181   keynode *tmp = new keynode;
182
183   if (action == Action::toggleGrabs && _current != _head) {
184     // the toggleGrabs key can only be set up as a root key, since if
185     // it was a chain key, we'd have to not ungrab the whole chain up
186     // to that key. which kinda defeats the purpose of this function.
187     return;
188   }
189
190   tmp->action = new Action(action,
191                            XKeysymToKeycode(_display,
192                                             XStringToKeysym(key.c_str())),
193                            mask, arg);
194   tmp->parent = _current;
195   _current->children.push_back(tmp);
196 }
197
198 void keytree::advanceOnNewNode()
199 {
200   keynode *tmp = new keynode;
201   tmp->action = NULL;
202   tmp->parent = _current;
203   _current->children.push_back(tmp);
204   _current = tmp;
205 }
206
207 void keytree::retract()
208 {
209   if (_current != _head)
210     _current = _current->parent;
211 }
212
213 void keytree::setCurrentNodeProps(Action::ActionType action, unsigned int mask,
214                                   string key, string arg)
215 {
216   if (_current->action)
217     delete _current->action;
218   
219   _current->action = new Action(action,
220                                 XKeysymToKeycode(_display,
221                                                  XStringToKeysym(key.c_str())),
222                                 mask, arg);
223 }
224
225 void keytree::initialize(void)
226 {
227   int tval = 0;
228
229   _epist->getConfig()->getValue(Config::chainTimeout, tval);
230   _timer = new BTimer(_epist, this);
231
232   if (tval <= 0)
233     tval = 3000; // set default timeout to 3 seconds
234
235   _timer->setTimeout(tval);
236 }
237
238 void keytree::timeout(void)
239 {
240   assert(_timeout_screen != NULL);
241
242   if (_current != _head) {
243     ungrabChildren(_current, _timeout_screen);
244     _current = _head;
245   }
246   _timeout_screen = NULL;
247 }