]> git.decadent.org.uk Git - ion3.git/blob - ioncore/regbind.c
0b2f371d5912009df44ba98b629075bc0b200582
[ion3.git] / ioncore / regbind.c
1 /*
2  * ion/regbind.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <string.h>
13 #include <libtu/objp.h>
14 #include <libmainloop/defer.h>
15 #include "common.h"
16 #include "region.h"
17 #include "binding.h"
18 #include "regbind.h"
19
20
21 /*{{{ Grab/ungrab */
22
23
24 static void do_binding_grab_on_ungrab_on(const WRegion *reg, 
25                                          const WBinding *binding,
26                                          const WBindmap *bindmap, bool grab)
27 {
28     Window win=region_xwindow(reg);
29     WRegBindingInfo *r;
30     
31     for(r=reg->bindings; r!=NULL; r=r->next){
32         if(r->bindmap==bindmap)
33             continue;
34         if(bindmap_lookup_binding(r->bindmap, binding->act, binding->state,
35                                   binding->kcb)!=NULL)
36             break;
37     }
38     if(r==NULL && binding->area==0){
39         if(grab)
40             binding_grab_on(binding, win);
41         else
42             binding_ungrab_on(binding, win);
43     }
44 }
45
46
47 static void do_binding_grab_on_ungrab_ons(const WRegion *reg, 
48                                           const WBindmap *bindmap,
49                                           bool grab)
50 {
51     Rb_node node=NULL;
52     WBinding *binding=NULL;
53     
54     if(!(reg->flags&REGION_BINDINGS_ARE_GRABBED) ||
55        bindmap->bindings==NULL){
56         return;
57     }
58     
59     FOR_ALL_BINDINGS(binding, node, bindmap->bindings){
60         do_binding_grab_on_ungrab_on(reg, binding, bindmap, grab);
61     }
62 }
63
64
65 static void grab_ungrabbed_bindings(const WRegion *reg, const WBindmap *bindmap)
66 {
67     do_binding_grab_on_ungrab_ons(reg, bindmap, TRUE);
68 }
69
70
71 static void ungrab_freed_bindings(const WRegion *reg, const WBindmap *bindmap)
72 {
73     do_binding_grab_on_ungrab_ons(reg, bindmap, FALSE);
74 }
75
76
77 void rbind_binding_added(const WRegBindingInfo *rbind, 
78                          const WBinding *binding,
79                          const WBindmap *bindmap)
80 {
81     if(binding->area==0 && rbind->reg->flags&REGION_BINDINGS_ARE_GRABBED)
82         do_binding_grab_on_ungrab_on(rbind->reg, binding, rbind->bindmap, TRUE);
83 }
84
85
86 void rbind_binding_removed(const WRegBindingInfo *rbind, 
87                            const WBinding *binding,
88                            const WBindmap *bindmap)
89 {
90     if(binding->area==0 && rbind->reg->flags&REGION_BINDINGS_ARE_GRABBED)
91         do_binding_grab_on_ungrab_on(rbind->reg, binding, rbind->bindmap, FALSE);
92 }
93
94
95 /*}}}*/
96
97
98 /*{{{ Find */
99
100
101 static WRegBindingInfo *find_rbind(WRegion *reg, WBindmap *bindmap,
102                                    WRegion *owner)
103 {
104     WRegBindingInfo *rbind;
105     
106     for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){
107         if(rbind->bindmap==bindmap && rbind->owner==owner)
108             return rbind;
109     }
110     
111     return NULL;
112 }
113
114
115 /*}}}*/
116
117
118 /*{{{ Interface */
119
120
121 static WRegBindingInfo *region_do_add_bindmap_owned(WRegion *reg, 
122                                                     WBindmap *bindmap, 
123                                                     WRegion *owner,
124                                                     bool first)
125 {
126     WRegBindingInfo *rbind;
127     
128     if(bindmap==NULL)
129         return NULL;
130     
131     rbind=ALLOC(WRegBindingInfo);
132     
133     if(rbind==NULL)
134         return NULL;
135     
136     rbind->bindmap=bindmap;
137     rbind->owner=owner;
138     rbind->reg=reg;
139     rbind->tmp=0;
140     
141     LINK_ITEM(bindmap->rbind_list, rbind, bm_next, bm_prev);
142
143     if(region_xwindow(reg)!=None && !(reg->flags&REGION_GRAB_ON_PARENT))
144         grab_ungrabbed_bindings(reg, bindmap);
145     
146     /* Link to reg's rbind list*/ {
147         WRegBindingInfo *b=reg->bindings;
148         if(first){
149             LINK_ITEM_FIRST(b, rbind, next, prev);
150         }else{
151             LINK_ITEM_LAST(b, rbind, next, prev);
152         }
153         reg->bindings=b;
154     }
155     
156     return rbind;
157 }
158
159
160 bool region_add_bindmap(WRegion *reg, WBindmap *bindmap)
161 {
162     if(find_rbind(reg, bindmap, NULL)!=NULL)
163         return FALSE;
164     return (region_do_add_bindmap_owned(reg, bindmap, NULL, TRUE)!=NULL);
165 }
166
167
168 static void remove_rbind(WRegion *reg, WRegBindingInfo *rbind)
169 {
170     UNLINK_ITEM(rbind->bindmap->rbind_list, rbind, bm_next, bm_prev);
171     
172     /* Unlink from reg's rbind list*/ {
173         WRegBindingInfo *b=reg->bindings;
174         UNLINK_ITEM(b, rbind, next, prev);
175         reg->bindings=b;
176     }
177     
178     if(region_xwindow(reg)!=None && !(reg->flags&REGION_GRAB_ON_PARENT))
179         ungrab_freed_bindings(reg, rbind->bindmap);
180
181     free(rbind);
182 }
183
184
185 void region_remove_bindmap(WRegion *reg, WBindmap *bindmap)
186 {
187     WRegBindingInfo *rbind=find_rbind(reg, bindmap, NULL);
188     if(rbind!=NULL)
189         remove_rbind(reg, rbind);
190 }
191
192
193 void region_remove_bindings(WRegion *reg)
194 {
195     WRegBindingInfo *rbind;
196     
197     while((rbind=(WRegBindingInfo*)reg->bindings)!=NULL)
198         remove_rbind(reg, rbind);
199 }
200
201
202 WBinding *region_lookup_keybinding(WRegion *reg, const XKeyEvent *ev,
203                                    const WSubmapState *sc,
204                                    WRegion **binding_owner_ret)
205 {
206     WRegBindingInfo *rbind=NULL;
207     WBinding *binding=NULL;
208     const WSubmapState *s=NULL;
209     WBindmap *bindmap=NULL;
210     int i;
211     
212     *binding_owner_ret=reg;
213
214     for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){
215         bindmap=rbind->bindmap;
216         
217         for(s=sc; s!=NULL && bindmap!=NULL; s=s->next){
218             binding=bindmap_lookup_binding(bindmap, BINDING_KEYPRESS, s->state, s->key);
219
220             if(binding==NULL){
221                 bindmap=NULL;
222                 break;
223             }
224             
225             bindmap=binding->submap;
226         }
227         
228         if(bindmap==NULL){
229             /* There may be no next iteration so we must reset binding here
230              * because we have not found a proper binding.
231              */
232             binding=NULL;
233             continue;
234         }
235
236         binding=bindmap_lookup_binding(bindmap, BINDING_KEYPRESS, ev->state, ev->keycode);
237         
238         if(binding!=NULL)
239             break;
240     }
241     
242     if(binding!=NULL && rbind->owner!=NULL)
243         *binding_owner_ret=rbind->owner;
244     
245     return binding;
246 }
247
248
249 WBinding *region_lookup_binding(WRegion *reg, int act, uint state,
250                                 uint kcb, int area)
251 {
252     WRegBindingInfo *rbind;
253     WBinding *binding=NULL;
254     
255     for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){
256         if(rbind->owner!=NULL)
257             continue;
258         binding=bindmap_lookup_binding_area(rbind->bindmap, act, state, kcb, area);
259         if(binding!=NULL)
260             break;
261     }
262     
263     return binding;
264 }
265
266
267 /*}}}*/
268
269
270 /*{{{ Update */
271
272
273 static void add_bindings(WRegion *reg, WRegion *r2)
274 {
275     WRegion *rx=REGION_MANAGER(r2);
276     WRegBindingInfo *rbind, *rb2;
277     WBinding *binding=NULL;
278     
279     if(rx!=NULL && REGION_PARENT_REG(rx)==reg){
280         /* The recursion is here to get the bindmaps correctly ordered. */
281         add_bindings(reg, rx);
282     }
283     
284     if(r2->flags&REGION_GRAB_ON_PARENT){
285         for(rb2=(WRegBindingInfo*)r2->bindings; rb2!=NULL; rb2=rb2->next){
286             rbind=find_rbind(reg, rb2->bindmap, r2);
287             if(rbind==NULL){
288                 rbind=region_do_add_bindmap_owned(reg, rb2->bindmap, 
289                                                   r2, TRUE);
290             }
291             if(rbind!=NULL)
292                 rbind->tmp=1;
293         }
294     }
295 }
296
297
298 void region_do_update_owned_grabs(WRegion *reg)
299 {
300     WRegBindingInfo *rbind, *rb2;
301
302     reg->flags&=~REGION_BINDING_UPDATE_SCHEDULED;
303     
304     /* clear flags */
305     for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next)
306         rbind->tmp=0;
307     
308     /* make new grabs */
309     if(reg->active_sub!=NULL)
310         add_bindings(reg, reg->active_sub);
311     
312     /* remove old grabs */
313     for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rb2){
314         rb2=rbind->next;
315         if(rbind->tmp!=1 && rbind->owner!=NULL)
316             remove_rbind(reg, rbind);
317     }
318 }
319
320 void region_update_owned_grabs(WRegion *reg)
321 {
322     if(reg->flags&REGION_BINDING_UPDATE_SCHEDULED 
323        || OBJ_IS_BEING_DESTROYED(reg)
324        || ioncore_g.opmode==IONCORE_OPMODE_DEINIT){
325         return;
326     }
327     
328     if(mainloop_defer_action((Obj*)reg, 
329                              (WDeferredAction*)region_do_update_owned_grabs)){
330         reg->flags|=REGION_BINDING_UPDATE_SCHEDULED;
331     }else{
332         region_do_update_owned_grabs(reg);
333     }
334 }
335
336
337 /*}}}*/