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