way sexier buildtree
[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   // parse out the base key name
45   std::string::size_type keybegin = str.find_last_of('-');
46   keybegin = (keybegin == std::string::npos) ? 0 : keybegin + 1;
47   std::string key(str, keybegin);
48
49   // parse out the requested modifier keys
50   unsigned int mods = 0;
51   std::string::size_type begin = 0, end;
52   while (begin != keybegin) {
53     end = str.find_first_of('-', begin);
54
55     std::string mod(str, begin, end-begin);
56
57     if (mod == "C") {           // control
58       mods |= ControlMask;
59     } else if (mod == "S") {    // shift
60       mods |= ShiftMask;
61     } else if (mod == "A" ||    // alt/mod1
62                mod == "M" ||
63                mod == "M1" ||
64                mod == "Mod1") {
65       mods |= Mod1Mask;
66     } else if (mod == "M2" ||   // mod2
67                mod == "Mod2") {
68       mods |= Mod2Mask;
69     } else if (mod == "M3" ||   // mod3
70                mod == "Mod3") {
71       mods |= Mod3Mask;
72     } else if (mod == "W" ||    // windows/mod4
73                mod == "M4" ||
74                mod == "Mod4") {
75       mods |= Mod4Mask;
76     } else if (mod == "M5" ||   // mod5
77                mod == "Mod5") {
78       mods |= Mod5Mask;
79     } else {                    // invalid
80       printf(_("Invalid modifier element in key binding: %s\n"), mod.c_str());
81       return false;
82     }
83     begin = end + 1;
84   }
85   
86   KeySym sym = XStringToKeysym(const_cast<char *>(key.c_str()));
87   if (sym == NoSymbol) return false;
88   b.modifiers = mods;
89   b.key = XKeysymToKeycode(otk::OBDisplay::display, sym);
90   return b.key != 0;
91 }
92
93 static void destroytree(BindingTree *tree)
94 {
95   while (tree) {
96     BindingTree *c = tree->first_child;
97     delete tree;
98     tree = c;
99   }
100 }
101
102 BindingTree *OBBindings::buildtree(const StringVect &keylist, int id)
103 {
104   if (keylist.empty()) return 0; // nothing in the list.. return 0
105
106   BindingTree *ret = 0, *p;
107
108   StringVect::const_reverse_iterator it, end = keylist.rend();
109   for (it = keylist.rbegin(); it != end; ++it) {
110     p = ret;
111     ret = new BindingTree(id);
112     if (!p) ret->chain = false;
113     ret->first_child = p;
114     if (!translate(*it, ret->binding)) {
115       destroytree(ret);
116       ret = 0;
117       break;
118     }
119     ret->text = *it; // XXX: rm me
120   }
121   return ret;
122 }
123
124
125 OBBindings::OBBindings()
126 {
127 }
128
129
130 OBBindings::~OBBindings()
131 {
132   remove_all();
133 }
134
135
136 void OBBindings::assimilate(BindingTree *node)
137 {
138   BindingTree *a, *b, *tmp, *last;
139
140   if (!_tree.first_child) {
141     // there are no nodes at this level yet
142     _tree.first_child = node;
143     return;
144   } else {
145     a = _tree.first_child;
146     last = a;
147     b = node;
148     while (a) {
149       last = a;
150       if (a->binding != b->binding) {
151         a = a->next_sibling;
152       } else {
153         tmp = b;
154         b = b->first_child;
155         delete tmp;
156         a = a->first_child;
157       }
158     }
159     if (last->binding != b->binding)
160       last->next_sibling = b;
161     else
162       last->first_child = b->first_child;
163     delete b;
164   }
165 }
166
167
168 int OBBindings::find(BindingTree *search) {
169   BindingTree *a, *b;
170   a = _tree.first_child;
171   b = search;
172   while (a && b) {
173     if (a->binding != b->binding) {
174       a = a->next_sibling;
175     } else {
176       if (a->chain == b->chain) {
177         if (!a->chain)
178           return a->id; // found it! (return the actual id, not the search's)
179       } else
180           return -2; // the chain status' don't match (conflict!)
181       b = b->first_child;
182       a = a->first_child;
183     }
184   }
185   return -1; // it just isn't in here
186 }
187
188 /*
189 static int find(BindingTree *parent, BindingTree *node) {
190   BindingTree *p, *lastsib, *nextparent, *nextnode = node->first_child;
191
192   if (!parent->first_child)
193     return -1;
194
195   p = parent->first_child;
196   while (p) {
197     if (node->binding == p->binding) {
198       if (node->chain == p->chain) {
199         if (!node->chain) {
200           return p->id; // found it! (return the actual id, not the search's)
201         } else {
202           break; // go on to the next child in the chain
203         }
204       } else {
205         return -2; // the chain status' don't match (conflict!)
206       }
207     }
208     p = p->next_sibling;
209   }
210   if (!p) return -1; // doesn't exist
211
212   if (node->chain) {
213     assert(node->first_child);
214     return find(p, node->first_child);
215   } else
216     return -1; // it just isnt in here
217 }
218 */
219
220 bool OBBindings::add(const StringVect &keylist, int id)
221 {
222   BindingTree *tree;
223
224   if (!(tree = buildtree(keylist, id)))
225     return false; // invalid binding requested
226
227   if (find(tree) < -1) {
228     // conflicts with another binding
229     destroytree(tree);
230     return false;
231   }
232
233   // assimilate this built tree into the main tree
234   assimilate(tree); // assimilation destroys/uses the tree
235   return true;
236 }
237
238
239 int OBBindings::find(const StringVect &keylist)
240 {
241   BindingTree *tree;
242   bool ret;
243
244   if (!(tree = buildtree(keylist, 0)))
245     return false; // invalid binding requested
246
247   ret = find(tree) >= 0;
248
249   destroytree(tree);
250
251   return ret;
252 }
253
254
255 int OBBindings::remove(const StringVect &keylist)
256 {
257   (void)keylist;
258   assert(false); // XXX: function not implemented yet
259 }
260
261
262 static void remove_branch(BindingTree *first)
263 {
264   BindingTree *p = first;
265
266   while (p) {
267     if (p->first_child)
268       remove_branch(p->first_child);
269     BindingTree *s = p->next_sibling;
270     delete p;
271     p = s;
272   }
273 }
274
275
276 void OBBindings::remove_all()
277 {
278   if (_tree.first_child)
279     remove_branch(_tree.first_child);
280 }
281
282 }