f8393d2d9b035ca074b95440a32a8f0e70a63e1d
[dana/openbox.git] / openbox / actions / directionalwindows.c
1 #include "openbox/actions.h"
2 #include "openbox/event.h"
3 #include "openbox/stacking.h"
4 #include "openbox/window.h"
5 #include "openbox/focus_cycle.h"
6 #include "openbox/openbox.h"
7 #include "openbox/misc.h"
8 #include "gettext.h"
9 #include "obt/keyboard.h"
10
11 typedef struct {
12     gboolean interactive;
13     gboolean dialog;
14     gboolean dock_windows;
15     gboolean desktop_windows;
16     ObDirection direction;
17     gboolean bar;
18     gboolean raise;
19     GSList *actions;
20 } Options;
21
22 static gboolean cycling = FALSE;
23
24 static gpointer setup_func(xmlNodePtr node);
25 static gpointer setup_cycle_func(xmlNodePtr node,
26                                  ObActionsIPreFunc *pre,
27                                  ObActionsIInputFunc *input,
28                                  ObActionsICancelFunc *cancel,
29                                  ObActionsIPostFunc *post);
30 static gpointer setup_target_func(xmlNodePtr node);
31 static void     free_func(gpointer options);
32 static gboolean run_func(ObActionsData *data, gpointer options);
33 static gboolean i_input_func(guint initial_state,
34                              XEvent *e,
35                              ObtIC *ic,
36                              gpointer options,
37                              gboolean *used);
38 static void     i_cancel_func(gpointer options);
39
40 static void     end_cycle(gboolean cancel, guint state, Options *o);
41
42 /* 3.4-compatibility */
43 static gpointer setup_north_cycle_func(xmlNodePtr node,
44                                        ObActionsIPreFunc *pre,
45                                        ObActionsIInputFunc *in,
46                                        ObActionsICancelFunc *c,
47                                        ObActionsIPostFunc *post);
48 static gpointer setup_south_cycle_func(xmlNodePtr node,
49                                        ObActionsIPreFunc *pre,
50                                        ObActionsIInputFunc *in,
51                                        ObActionsICancelFunc *c,
52                                        ObActionsIPostFunc *post);
53 static gpointer setup_east_cycle_func(xmlNodePtr node,
54                                       ObActionsIPreFunc *pre,
55                                       ObActionsIInputFunc *in,
56                                       ObActionsICancelFunc *c,
57                                       ObActionsIPostFunc *post);
58 static gpointer setup_west_cycle_func(xmlNodePtr node,
59                                       ObActionsIPreFunc *pre,
60                                       ObActionsIInputFunc *in,
61                                       ObActionsICancelFunc *c,
62                                       ObActionsIPostFunc *post);
63 static gpointer setup_northwest_cycle_func(xmlNodePtr node,
64                                            ObActionsIPreFunc *pre,
65                                            ObActionsIInputFunc *in,
66                                            ObActionsICancelFunc *c,
67                                            ObActionsIPostFunc *post);
68 static gpointer setup_northeast_cycle_func(xmlNodePtr node,
69                                            ObActionsIPreFunc *pre,
70                                            ObActionsIInputFunc *in,
71                                            ObActionsICancelFunc *c,
72                                            ObActionsIPostFunc *post);
73 static gpointer setup_southwest_cycle_func(xmlNodePtr node,
74                                            ObActionsIPreFunc *pre,
75                                            ObActionsIInputFunc *in,
76                                            ObActionsICancelFunc *c,
77                                            ObActionsIPostFunc *post);
78 static gpointer setup_southeast_cycle_func(xmlNodePtr node,
79                                            ObActionsIPreFunc *pre,
80                                            ObActionsIInputFunc *in,
81                                            ObActionsICancelFunc *c,
82                                            ObActionsIPostFunc *post);
83 static gpointer setup_north_target_func(xmlNodePtr node);
84 static gpointer setup_south_target_func(xmlNodePtr node);
85 static gpointer setup_east_target_func(xmlNodePtr node);
86 static gpointer setup_west_target_func(xmlNodePtr node);
87 static gpointer setup_northwest_target_func(xmlNodePtr node);
88 static gpointer setup_northeast_target_func(xmlNodePtr node);
89 static gpointer setup_southwest_target_func(xmlNodePtr node);
90 static gpointer setup_southeast_target_func(xmlNodePtr node);
91
92 void action_directionalwindows_startup(void)
93 {
94     actions_register_i("DirectionalCycleWindows", setup_cycle_func, free_func,
95                        run_func);
96     actions_register("DirectionalTargetWindow", setup_target_func, free_func,
97                      run_func);
98     /* 3.4-compatibility */
99     actions_register_i("DirectionalFocusNorth", setup_north_cycle_func,
100                        free_func, run_func);
101     actions_register_i("DirectionalFocusSouth", setup_south_cycle_func,
102                        free_func, run_func);
103     actions_register_i("DirectionalFocusWest", setup_west_cycle_func,
104                        free_func, run_func);
105     actions_register_i("DirectionalFocusEast", setup_east_cycle_func,
106                        free_func, run_func);
107     actions_register_i("DirectionalFocusNorthWest", setup_northwest_cycle_func,
108                        free_func, run_func);
109     actions_register_i("DirectionalFocusNorthEast", setup_northeast_cycle_func,
110                        free_func, run_func);
111     actions_register_i("DirectionalFocusSouthWest", setup_southwest_cycle_func,
112                        free_func, run_func);
113     actions_register_i("DirectionalFocusSouthEast", setup_southeast_cycle_func,
114                        free_func, run_func);
115     actions_register("DirectionalTargetNorth", setup_north_target_func,
116                      free_func, run_func);
117     actions_register("DirectionalTargetSouth", setup_south_target_func,
118                      free_func, run_func);
119     actions_register("DirectionalTargetWest", setup_west_target_func,
120                      free_func, run_func);
121     actions_register("DirectionalTargetEast", setup_east_target_func,
122                      free_func, run_func);
123     actions_register("DirectionalTargetNorthWest", setup_northwest_target_func,
124                      free_func, run_func);
125     actions_register("DirectionalTargetNorthEast", setup_northeast_target_func,
126                      free_func, run_func);
127     actions_register("DirectionalTargetSouthWest", setup_southwest_target_func,
128                      free_func, run_func);
129     actions_register("DirectionalTargetSouthEast", setup_southeast_target_func,
130                      free_func, run_func);
131 }
132
133 static gpointer setup_func(xmlNodePtr node)
134 {
135     xmlNodePtr n;
136     Options *o;
137
138     o = g_slice_new0(Options);
139     o->dialog = TRUE;
140     o->bar = TRUE;
141
142     if ((n = obt_xml_find_node(node, "dialog")))
143         o->dialog = obt_xml_node_bool(n);
144     if ((n = obt_xml_find_node(node, "bar")))
145         o->bar = obt_xml_node_bool(n);
146     if ((n = obt_xml_find_node(node, "raise")))
147         o->raise = obt_xml_node_bool(n);
148     if ((n = obt_xml_find_node(node, "panels")))
149         o->dock_windows = obt_xml_node_bool(n);
150     if ((n = obt_xml_find_node(node, "desktop")))
151         o->desktop_windows = obt_xml_node_bool(n);
152     if ((n = obt_xml_find_node(node, "direction"))) {
153         gchar *s = obt_xml_node_string(n);
154         if (!g_ascii_strcasecmp(s, "north") ||
155             !g_ascii_strcasecmp(s, "up"))
156             o->direction = OB_DIRECTION_NORTH;
157         else if (!g_ascii_strcasecmp(s, "northwest"))
158             o->direction = OB_DIRECTION_NORTHWEST;
159         else if (!g_ascii_strcasecmp(s, "northeast"))
160             o->direction = OB_DIRECTION_NORTHEAST;
161         else if (!g_ascii_strcasecmp(s, "west") ||
162                  !g_ascii_strcasecmp(s, "left"))
163             o->direction = OB_DIRECTION_WEST;
164         else if (!g_ascii_strcasecmp(s, "east") ||
165                  !g_ascii_strcasecmp(s, "right"))
166             o->direction = OB_DIRECTION_EAST;
167         else if (!g_ascii_strcasecmp(s, "south") ||
168                  !g_ascii_strcasecmp(s, "down"))
169             o->direction = OB_DIRECTION_SOUTH;
170         else if (!g_ascii_strcasecmp(s, "southwest"))
171             o->direction = OB_DIRECTION_SOUTHWEST;
172         else if (!g_ascii_strcasecmp(s, "southeast"))
173             o->direction = OB_DIRECTION_SOUTHEAST;
174         g_free(s);
175     }
176
177     if ((n = obt_xml_find_node(node, "finalactions"))) {
178         xmlNodePtr m;
179
180         m = obt_xml_find_node(n->children, "action");
181         while (m) {
182             ObActionsAct *action = actions_parse(m);
183             if (action) o->actions = g_slist_append(o->actions, action);
184             m = obt_xml_find_node(m->next, "action");
185         }
186     }
187     else {
188         o->actions = g_slist_prepend(o->actions,
189                                      actions_parse_string("Focus"));
190         o->actions = g_slist_prepend(o->actions,
191                                      actions_parse_string("Raise"));
192         o->actions = g_slist_prepend(o->actions,
193                                      actions_parse_string("Unshade"));
194     }
195
196     return o;
197 }
198
199 static gpointer setup_cycle_func(xmlNodePtr node,
200                                  ObActionsIPreFunc *pre,
201                                  ObActionsIInputFunc *input,
202                                  ObActionsICancelFunc *cancel,
203                                  ObActionsIPostFunc *post)
204 {
205     Options *o = setup_func(node);
206     o->interactive = TRUE;
207     *input = i_input_func;
208     *cancel = i_cancel_func;
209     return o;
210 }
211
212 static gpointer setup_target_func(xmlNodePtr node)
213 {
214     Options *o = setup_func(node);
215     o->interactive = FALSE;
216     return o;
217 }
218
219 static void free_func(gpointer options)
220 {
221     Options *o = options;
222
223     while (o->actions) {
224         actions_act_unref(o->actions->data);
225         o->actions = g_slist_delete_link(o->actions, o->actions);
226     }
227
228     g_slice_free(Options, o);
229 }
230
231 static gboolean run_func(ObActionsData *data, gpointer options)
232 {
233     Options *o = options;
234
235     if (!o->interactive)
236         end_cycle(FALSE, data->state, o);
237     else {
238         struct _ObClient *ft;
239
240         ft = focus_directional_cycle(o->direction,
241                                      o->dock_windows,
242                                      o->desktop_windows,
243                                      TRUE,
244                                      o->bar,
245                                      o->dialog,
246                                      FALSE, FALSE);
247         cycling = TRUE;
248
249         stacking_restore();
250         if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
251     }
252
253     return o->interactive;
254 }
255
256 static gboolean i_input_func(guint initial_state,
257                              XEvent *e,
258                              ObtIC *ic,
259                              gpointer options,
260                              gboolean *used)
261 {
262     guint mods;
263
264     mods = obt_keyboard_only_modmasks(e->xkey.state);
265     if (e->type == KeyRelease) {
266         /* remove from the state the mask of the modifier key being
267            released, if it is a modifier key being released that is */
268         mods &= ~obt_keyboard_keycode_to_modmask(e->xkey.keycode);
269     }
270
271     if (e->type == KeyPress) {
272         KeySym sym = obt_keyboard_keypress_to_keysym(e);
273
274         /* Escape cancels no matter what */
275         if (sym == XK_Escape) {
276             end_cycle(TRUE, e->xkey.state, options);
277             return FALSE;
278         }
279
280         /* There were no modifiers and they pressed enter */
281         else if (sym == XK_Return && !initial_state) {
282             end_cycle(FALSE, e->xkey.state, options);
283             return FALSE;
284         }
285     }
286     /* They released the modifiers */
287     else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
288     {
289         end_cycle(FALSE, e->xkey.state, options);
290         return FALSE;
291     }
292
293     return TRUE;
294 }
295
296 static void i_cancel_func(gpointer options)
297 {
298     /* we get cancelled when we move focus, but we're not cycling anymore, so
299        just ignore that */
300     if (cycling)
301         end_cycle(TRUE, 0, options);
302 }
303
304 static void end_cycle(gboolean cancel, guint state, Options *o)
305 {
306     struct _ObClient *ft;
307
308     ft = focus_directional_cycle(o->direction,
309                                  o->dock_windows,
310                                  o->desktop_windows,
311                                  o->interactive,
312                                  o->bar,
313                                  o->dialog,
314                                  TRUE, cancel);
315     cycling = FALSE;
316
317     if (ft)
318         actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
319                          state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft);
320
321     stacking_restore();
322 }
323
324 /* 3.4-compatibility */
325 static gpointer setup_north_cycle_func(xmlNodePtr node,
326                                        ObActionsIPreFunc *pre,
327                                        ObActionsIInputFunc *input,
328                                        ObActionsICancelFunc *cancel,
329                                        ObActionsIPostFunc *post)
330 {
331     Options *o = setup_cycle_func(node, pre, input, cancel, post);
332     o->direction = OB_DIRECTION_NORTH;
333     return o;
334 }
335
336 static gpointer setup_south_cycle_func(xmlNodePtr node,
337                                        ObActionsIPreFunc *pre,
338                                        ObActionsIInputFunc *input,
339                                        ObActionsICancelFunc *cancel,
340                                        ObActionsIPostFunc *post)
341 {
342     Options *o = setup_cycle_func(node, pre, input, cancel, post);
343     o->direction = OB_DIRECTION_SOUTH;
344     return o;
345 }
346
347 static gpointer setup_east_cycle_func(xmlNodePtr node,
348                                       ObActionsIPreFunc *pre,
349                                       ObActionsIInputFunc *input,
350                                       ObActionsICancelFunc *cancel,
351                                       ObActionsIPostFunc *post)
352 {
353     Options *o = setup_cycle_func(node, pre, input, cancel, post);
354     o->direction = OB_DIRECTION_EAST;
355     return o;
356 }
357
358 static gpointer setup_west_cycle_func(xmlNodePtr node,
359                                       ObActionsIPreFunc *pre,
360                                       ObActionsIInputFunc *input,
361                                       ObActionsICancelFunc *cancel,
362                                       ObActionsIPostFunc *post)
363 {
364     Options *o = setup_cycle_func(node, pre, input, cancel, post);
365     o->direction = OB_DIRECTION_WEST;
366     return o;
367 }
368
369 static gpointer setup_northwest_cycle_func(xmlNodePtr node,
370                                            ObActionsIPreFunc *pre,
371                                            ObActionsIInputFunc *input,
372                                            ObActionsICancelFunc *cancel,
373                                            ObActionsIPostFunc *post)
374 {
375     Options *o = setup_cycle_func(node, pre, input, cancel, post);
376     o->direction = OB_DIRECTION_NORTHWEST;
377     return o;
378 }
379
380 static gpointer setup_northeast_cycle_func(xmlNodePtr node,
381                                            ObActionsIPreFunc *pre,
382                                            ObActionsIInputFunc *input,
383                                            ObActionsICancelFunc *cancel,
384                                            ObActionsIPostFunc *post)
385 {
386     Options *o = setup_cycle_func(node, pre, input, cancel, post);
387     o->direction = OB_DIRECTION_EAST;
388     return o;
389 }
390
391 static gpointer setup_southwest_cycle_func(xmlNodePtr node,
392                                            ObActionsIPreFunc *pre,
393                                            ObActionsIInputFunc *input,
394                                            ObActionsICancelFunc *cancel,
395                                            ObActionsIPostFunc *post)
396 {
397     Options *o = setup_cycle_func(node, pre, input, cancel, post);
398     o->direction = OB_DIRECTION_SOUTHWEST;
399     return o;
400 }
401
402 static gpointer setup_southeast_cycle_func(xmlNodePtr node,
403                                            ObActionsIPreFunc *pre,
404                                            ObActionsIInputFunc *input,
405                                            ObActionsICancelFunc *cancel,
406                                            ObActionsIPostFunc *post)
407 {
408     Options *o = setup_cycle_func(node, pre, input, cancel, post);
409     o->direction = OB_DIRECTION_SOUTHEAST;
410     return o;
411 }
412
413 static gpointer setup_north_target_func(xmlNodePtr node)
414 {
415     Options *o = setup_target_func(node);
416     o->direction = OB_DIRECTION_NORTH;
417     return o;
418 }
419
420 static gpointer setup_south_target_func(xmlNodePtr node)
421 {
422     Options *o = setup_target_func(node);
423     o->direction = OB_DIRECTION_SOUTH;
424     return o;
425 }
426
427 static gpointer setup_east_target_func(xmlNodePtr node)
428 {
429     Options *o = setup_target_func(node);
430     o->direction = OB_DIRECTION_EAST;
431     return o;
432 }
433
434 static gpointer setup_west_target_func(xmlNodePtr node)
435 {
436     Options *o = setup_target_func(node);
437     o->direction = OB_DIRECTION_WEST;
438     return o;
439 }
440
441 static gpointer setup_northwest_target_func(xmlNodePtr node)
442 {
443     Options *o = setup_target_func(node);
444     o->direction = OB_DIRECTION_NORTHWEST;
445     return o;
446 }
447
448 static gpointer setup_northeast_target_func(xmlNodePtr node)
449 {
450     Options *o = setup_target_func(node);
451     o->direction = OB_DIRECTION_NORTHEAST;
452     return o;
453 }
454
455 static gpointer setup_southwest_target_func(xmlNodePtr node)
456 {
457     Options *o = setup_target_func(node);
458     o->direction = OB_DIRECTION_SOUTHWEST;
459     return o;
460 }
461
462 static gpointer setup_southeast_target_func(xmlNodePtr node)
463 {
464     Options *o = setup_target_func(node);
465     o->direction = OB_DIRECTION_SOUTHEAST;
466     return o;
467 }
468