key chains get built properly!
[mikachu/openbox.git] / src / bindings.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "bindings.hh"
8 #include "otk/display.hh"
9
10 extern "C" {
11 #include <X11/Xlib.h>
12
13 #include "gettext.h"
14 #define _(str) gettext(str)
15 }
16
17 namespace ob {
18
19 #include <stdio.h>
20 static void print_branch(BindingTree *first, std::string str)
21 {
22   BindingTree *p = first;
23   
24   while (p) {
25     if (p->first_child)
26       print_branch(p->first_child, str + " " + p->text);
27     if (!p->chain)
28       printf("%d%s\n", p->id, (str + " " + p->text).c_str());
29     p = p->next_sibling;
30   }
31 }
32
33
34 void OBBindings::display()
35 {
36   if (_tree.first_child)
37     print_branch(_tree.first_child, "");
38 }
39
40
41
42 bool OBBindings::translate(const std::string &str, Binding &b)
43 {
44   unsigned int mods = 0;
45   
46   // parse out the base key name
47   std::string::size_type keybegin = str.find_last_of('-');
48   keybegin = (keybegin == std::string::npos) ? 0 : keybegin + 1;
49   std::string key(str, keybegin);
50
51   // XXX: get some modifiers up in the hizzie
52   // parse out the requested modifier keys
53   std::string::size_type begin = 0, end;
54   while (begin != keybegin) {
55     end = str.find_first_of('-', begin);
56
57     std::string mod(str, begin, end-begin);
58
59     if (mod == "C") {           // control
60       mods |= ControlMask;
61     } else if (mod == "S") {    // shift
62       mods |= ShiftMask;
63     } else if (mod == "A" ||    // alt/mod1
64                mod == "M" ||
65                mod == "M1" ||
66                mod == "Mod1") {
67       mods |= Mod1Mask;
68     } else if (mod == "M2" ||   // mod2
69                mod == "Mod2") {
70       mods |= Mod2Mask;
71     } else if (mod == "M3" ||   // mod3
72                mod == "Mod3") {
73       mods |= Mod3Mask;
74     } else if (mod == "W" ||    // windows/mod4
75                mod == "M4" ||
76                mod == "Mod4") {
77       mods |= Mod4Mask;
78     } else if (mod == "M5" ||   // mod5
79                mod == "Mod5") {
80       mods |= Mod5Mask;
81     } else {                    // invalid
82       printf(_("Invalid modifier element in key binding: %s\n"), mod.c_str());
83       return false;
84     }
85     begin = end + 1;
86   }
87   
88   KeySym sym = XStringToKeysym(const_cast<char *>(key.c_str()));
89   if (sym == NoSymbol) return false;
90   b.modifiers = mods;
91   b.key = XKeysymToKeycode(otk::OBDisplay::display, sym);
92   return b.key != 0;
93 }
94
95 BindingTree *OBBindings::buildtree(const StringVect &keylist, int id)
96 {
97   if (keylist.empty()) return 0; // nothing in the list.. return 0
98
99   BindingTree *ret = new BindingTree(id), *p = 0;
100
101   StringVect::const_iterator it, end = keylist.end();
102   for (it = keylist.begin(); it != end; ++it) {
103     if (p)
104       p = p->first_child = new BindingTree(id);
105     else
106       p = ret; // the first node
107     
108     if (!translate(*it, p->binding))
109       break;
110     p->text = *it;
111   }
112   if (it != end) {
113     // build failed.. clean up and return 0
114     p = ret;
115     while (p->first_child) {
116       BindingTree *c = p->first_child;
117       delete p;
118       p = c;      
119     }
120     delete p;
121     return 0;
122   } else {
123     // set the proper chain status on the last node
124     p->chain = false;
125   }
126
127 //  printf("BUILDING:\n");
128 //  print_branch(ret, "");
129   
130   // successfully built a tree
131   return ret;
132 }
133
134 static void destroytree(BindingTree *tree)
135 {
136   while (tree) {
137     BindingTree *c = tree->first_child;
138     delete tree;
139     tree = c;
140   }
141 }
142
143 OBBindings::OBBindings()
144 {
145 }
146
147
148 OBBindings::~OBBindings()
149 {
150   remove_all();
151 }
152
153
154 void OBBindings::assimilate(BindingTree *node)
155 {
156   BindingTree *a, *b, *tmp, *last;
157
158   if (!_tree.first_child) {
159     // there are no nodes at this level yet
160     _tree.first_child = node;
161     return;
162   } else {
163     a = _tree.first_child;
164     last = a;
165     b = node;
166     while (a) {
167       last = a;
168       if (a->binding != b->binding) {
169         a = a->next_sibling;
170       } else {
171         tmp = b;
172         b = b->first_child;
173         delete tmp;
174         a = a->first_child;
175       }
176     }
177     if (last->binding != b->binding)
178       last->next_sibling = b;
179     else
180       last->first_child = b->first_child;
181     delete b;
182   }
183 }
184
185
186 int OBBindings::find(BindingTree *search) {
187   BindingTree *a, *b;
188   a = _tree.first_child;
189   b = search;
190   while (a && b) {
191     if (a->binding != b->binding) {
192       a = a->next_sibling;
193     } else {
194       if (a->chain == b->chain) {
195         if (!a->chain)
196           return a->id; // found it! (return the actual id, not the search's)
197       } else
198           return -2; // the chain status' don't match (conflict!)
199       b = b->first_child;
200       a = a->first_child;
201     }
202   }
203   return -1; // it just isn't in here
204 }
205
206 /*
207 static int find(BindingTree *parent, BindingTree *node) {
208   BindingTree *p, *lastsib, *nextparent, *nextnode = node->first_child;
209
210   if (!parent->first_child)
211     return -1;
212
213   p = parent->first_child;
214   while (p) {
215     if (node->binding == p->binding) {
216       if (node->chain == p->chain) {
217         if (!node->chain) {
218           return p->id; // found it! (return the actual id, not the search's)
219         } else {
220           break; // go on to the next child in the chain
221         }
222       } else {
223         return -2; // the chain status' don't match (conflict!)
224       }
225     }
226     p = p->next_sibling;
227   }
228   if (!p) return -1; // doesn't exist
229
230   if (node->chain) {
231     assert(node->first_child);
232     return find(p, node->first_child);
233   } else
234     return -1; // it just isnt in here
235 }
236 */
237
238 bool OBBindings::add(const StringVect &keylist, int id)
239 {
240   BindingTree *tree;
241
242   if (!(tree = buildtree(keylist, id)))
243     return false; // invalid binding requested
244
245   if (find(tree) < -1) {
246     // conflicts with another binding
247     destroytree(tree);
248     return false;
249   }
250
251   // assimilate this built tree into the main tree
252   assimilate(tree); // assimilation destroys/uses the tree
253   return true;
254 }
255
256
257 int OBBindings::find(const StringVect &keylist)
258 {
259   BindingTree *tree;
260   bool ret;
261
262   if (!(tree = buildtree(keylist, 0)))
263     return false; // invalid binding requested
264
265   ret = find(tree) >= 0;
266
267   destroytree(tree);
268
269   return ret;
270 }
271
272
273 int OBBindings::remove(const StringVect &keylist)
274 {
275   (void)keylist;
276   assert(false); // XXX: function not implemented yet
277 }
278
279
280 static void remove_branch(BindingTree *first)
281 {
282   BindingTree *p = first;
283
284   while (p) {
285     if (p->first_child)
286       remove_branch(p->first_child);
287     BindingTree *s = p->next_sibling;
288     delete p;
289     p = s;
290   }
291 }
292
293
294 void OBBindings::remove_all()
295 {
296   if (_tree.first_child)
297     remove_branch(_tree.first_child);
298 }
299
300 }