Merge branch 'backport' into work
[mikachu/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 static gpointer setup_target_func(xmlNodePtr node);
26 static void     free_func(gpointer options);
27 static gboolean run_func(ObActionsData *data, gpointer options);
28 static gboolean i_input_func(guint initial_state,
29                              XEvent *e,
30                              gpointer options,
31                              gboolean *used);
32 static void     i_cancel_func(gpointer options);
33
34 static void     end_cycle(gboolean cancel, guint state, Options *o);
35
36 /* 3.4-compatibility */
37 static gpointer setup_north_cycle_func(xmlNodePtr node);
38 static gpointer setup_south_cycle_func(xmlNodePtr node);
39 static gpointer setup_east_cycle_func(xmlNodePtr node);
40 static gpointer setup_west_cycle_func(xmlNodePtr node);
41 static gpointer setup_northwest_cycle_func(xmlNodePtr node);
42 static gpointer setup_northeast_cycle_func(xmlNodePtr node);
43 static gpointer setup_southwest_cycle_func(xmlNodePtr node);
44 static gpointer setup_southeast_cycle_func(xmlNodePtr node);
45 static gpointer setup_north_target_func(xmlNodePtr node);
46 static gpointer setup_south_target_func(xmlNodePtr node);
47 static gpointer setup_east_target_func(xmlNodePtr node);
48 static gpointer setup_west_target_func(xmlNodePtr node);
49 static gpointer setup_northwest_target_func(xmlNodePtr node);
50 static gpointer setup_northeast_target_func(xmlNodePtr node);
51 static gpointer setup_southwest_target_func(xmlNodePtr node);
52 static gpointer setup_southeast_target_func(xmlNodePtr node);
53
54 void action_directionalwindows_startup(void)
55 {
56     actions_register("DirectionalCycleWindows", setup_cycle_func, free_func,
57                      run_func, i_input_func, i_cancel_func);
58     actions_register("DirectionalTargetWindow", setup_target_func, free_func,
59                      run_func, NULL, NULL);
60     /* 3.4-compatibility */
61     actions_register("DirectionalFocusNorth", setup_north_cycle_func,
62                      free_func, run_func, i_input_func, i_cancel_func);
63     actions_register("DirectionalFocusSouth", setup_south_cycle_func,
64                      free_func, run_func, i_input_func, i_cancel_func);
65     actions_register("DirectionalFocusWest", setup_west_cycle_func,
66                      free_func, run_func, i_input_func, i_cancel_func);
67     actions_register("DirectionalFocusEast", setup_east_cycle_func,
68                      free_func, run_func, i_input_func, i_cancel_func);
69     actions_register("DirectionalFocusNorthWest", setup_northwest_cycle_func,
70                      free_func, run_func, i_input_func, i_cancel_func);
71     actions_register("DirectionalFocusNorthEast", setup_northeast_cycle_func,
72                      free_func, run_func, i_input_func, i_cancel_func);
73     actions_register("DirectionalFocusSouthWest", setup_southwest_cycle_func,
74                      free_func, run_func, i_input_func, i_cancel_func);
75     actions_register("DirectionalFocusSouthEast", setup_southeast_cycle_func,
76                      free_func, run_func, i_input_func, i_cancel_func);
77     actions_register("DirectionalTargetNorth", setup_north_target_func,
78                      free_func, run_func, i_input_func, i_cancel_func);
79     actions_register("DirectionalTargetSouth", setup_south_target_func,
80                      free_func, run_func, i_input_func, i_cancel_func);
81     actions_register("DirectionalTargetWest", setup_west_target_func,
82                      free_func, run_func, i_input_func, i_cancel_func);
83     actions_register("DirectionalTargetEast", setup_east_target_func,
84                      free_func, run_func, i_input_func, i_cancel_func);
85     actions_register("DirectionalTargetNorthWest", setup_northwest_target_func,
86                      free_func, run_func, i_input_func, i_cancel_func);
87     actions_register("DirectionalTargetNorthEast", setup_northeast_target_func,
88                      free_func, run_func, i_input_func, i_cancel_func);
89     actions_register("DirectionalTargetSouthWest", setup_southwest_target_func,
90                      free_func, run_func, i_input_func, i_cancel_func);
91     actions_register("DirectionalTargetSouthEast", setup_southeast_target_func,
92                      free_func, run_func, i_input_func, i_cancel_func);
93 }
94
95 static gpointer setup_func(xmlNodePtr node)
96 {
97     xmlNodePtr n;
98     Options *o;
99
100     o = g_new0(Options, 1);
101     o->dialog = TRUE;
102     o->bar = TRUE;
103
104     if ((n = obt_parse_find_node(node, "dialog")))
105         o->dialog = obt_parse_node_bool(n);
106     if ((n = obt_parse_find_node(node, "bar")))
107         o->bar = obt_parse_node_bool(n);
108     if ((n = obt_parse_find_node(node, "raise")))
109         o->raise = obt_parse_node_bool(n);
110     if ((n = obt_parse_find_node(node, "panels")))
111         o->dock_windows = obt_parse_node_bool(n);
112     if ((n = obt_parse_find_node(node, "desktop")))
113         o->desktop_windows = obt_parse_node_bool(n);
114     if ((n = obt_parse_find_node(node, "direction"))) {
115         gchar *s = obt_parse_node_string(n);
116         if (!g_ascii_strcasecmp(s, "north") ||
117             !g_ascii_strcasecmp(s, "up"))
118             o->direction = OB_DIRECTION_NORTH;
119         else if (!g_ascii_strcasecmp(s, "northwest"))
120             o->direction = OB_DIRECTION_NORTHWEST;
121         else if (!g_ascii_strcasecmp(s, "northeast"))
122             o->direction = OB_DIRECTION_NORTHEAST;
123         else if (!g_ascii_strcasecmp(s, "west") ||
124                  !g_ascii_strcasecmp(s, "left"))
125             o->direction = OB_DIRECTION_WEST;
126         else if (!g_ascii_strcasecmp(s, "east") ||
127                  !g_ascii_strcasecmp(s, "right"))
128             o->direction = OB_DIRECTION_EAST;
129         else if (!g_ascii_strcasecmp(s, "south") ||
130                  !g_ascii_strcasecmp(s, "down"))
131             o->direction = OB_DIRECTION_SOUTH;
132         else if (!g_ascii_strcasecmp(s, "southwest"))
133             o->direction = OB_DIRECTION_SOUTHWEST;
134         else if (!g_ascii_strcasecmp(s, "southeast"))
135             o->direction = OB_DIRECTION_SOUTHEAST;
136         g_free(s);
137     }
138
139     if ((n = obt_parse_find_node(node, "finalactions"))) {
140         xmlNodePtr m;
141
142         m = obt_parse_find_node(n->children, "action");
143         while (m) {
144             ObActionsAct *action = actions_parse(m);
145             if (action) o->actions = g_slist_append(o->actions, action);
146             m = obt_parse_find_node(m->next, "action");
147         }
148     }
149     else {
150         o->actions = g_slist_prepend(o->actions,
151                                      actions_parse_string("Focus"));
152         o->actions = g_slist_prepend(o->actions,
153                                      actions_parse_string("Raise"));
154         o->actions = g_slist_prepend(o->actions,
155                                      actions_parse_string("Unshade"));
156     }
157
158     return o;
159 }
160
161 static gpointer setup_cycle_func(xmlNodePtr node)
162 {
163     Options *o = setup_func(node);
164     o->interactive = TRUE;
165     return o;
166 }
167
168 static gpointer setup_target_func(xmlNodePtr node)
169 {
170     Options *o = setup_func(node);
171     o->interactive = FALSE;
172     return o;
173 }
174
175 static void free_func(gpointer options)
176 {
177     Options *o = options;
178
179     while (o->actions) {
180         actions_act_unref(o->actions->data);
181         o->actions = g_slist_delete_link(o->actions, o->actions);
182     }
183
184     g_free(o);
185 }
186
187 static gboolean run_func(ObActionsData *data, gpointer options)
188 {
189     Options *o = options;
190
191     if (!o->interactive)
192         end_cycle(FALSE, data->state, o);
193     else {
194         struct _ObClient *ft;
195
196         ft = focus_directional_cycle(o->direction,
197                                      o->dock_windows,
198                                      o->desktop_windows,
199                                      TRUE,
200                                      o->bar,
201                                      o->dialog,
202                                      FALSE, FALSE);
203         cycling = TRUE;
204
205         stacking_restore();
206         if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
207     }
208
209     return o->interactive;
210 }
211
212 static gboolean i_input_func(guint initial_state,
213                              XEvent *e,
214                              gpointer options,
215                              gboolean *used)
216 {
217     if (e->type == KeyPress) {
218         /* Escape cancels no matter what */
219         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
220             end_cycle(TRUE, e->xkey.state, options);
221             return FALSE;
222         }
223
224         /* There were no modifiers and they pressed enter */
225         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN) &&
226                  !initial_state)
227         {
228             end_cycle(FALSE, e->xkey.state, options);
229             return FALSE;
230         }
231     }
232     /* They released the modifiers */
233     else if (e->type == KeyRelease && initial_state &&
234              (e->xkey.state & initial_state) == 0)
235     {
236         end_cycle(FALSE, e->xkey.state, options);
237         return FALSE;
238     }
239
240     return TRUE;
241 }
242
243 static void i_cancel_func(gpointer options)
244 {
245     /* we get cancelled when we move focus, but we're not cycling anymore, so
246        just ignore that */
247     if (cycling)
248         end_cycle(TRUE, 0, options);
249 }
250
251 static void end_cycle(gboolean cancel, guint state, Options *o)
252 {
253     struct _ObClient *ft;
254
255     ft = focus_directional_cycle(o->direction,
256                                  o->dock_windows,
257                                  o->desktop_windows,
258                                  o->interactive,
259                                  o->bar,
260                                  o->dialog,
261                                  TRUE, cancel);
262     cycling = FALSE;
263
264     if (ft)
265         actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
266                          state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft);
267
268     stacking_restore();
269 }
270
271 /* 3.4-compatibility */
272 static gpointer setup_north_cycle_func(xmlNodePtr node)
273 {
274     Options *o = setup_cycle_func(node);
275     o->direction = OB_DIRECTION_NORTH;
276     return o;
277 }
278
279 static gpointer setup_south_cycle_func(xmlNodePtr node)
280 {
281     Options *o = setup_cycle_func(node);
282     o->direction = OB_DIRECTION_SOUTH;
283     return o;
284 }
285
286 static gpointer setup_east_cycle_func(xmlNodePtr node)
287 {
288     Options *o = setup_cycle_func(node);
289     o->direction = OB_DIRECTION_EAST;
290     return o;
291 }
292
293 static gpointer setup_west_cycle_func(xmlNodePtr node)
294 {
295     Options *o = setup_cycle_func(node);
296     o->direction = OB_DIRECTION_WEST;
297     return o;
298 }
299
300 static gpointer setup_northwest_cycle_func(xmlNodePtr node)
301 {
302     Options *o = setup_cycle_func(node);
303     o->direction = OB_DIRECTION_NORTHWEST;
304     return o;
305 }
306
307 static gpointer setup_northeast_cycle_func(xmlNodePtr node)
308 {
309     Options *o = setup_cycle_func(node);
310     o->direction = OB_DIRECTION_EAST;
311     return o;
312 }
313
314 static gpointer setup_southwest_cycle_func(xmlNodePtr node)
315 {
316     Options *o = setup_cycle_func(node);
317     o->direction = OB_DIRECTION_SOUTHWEST;
318     return o;
319 }
320
321 static gpointer setup_southeast_cycle_func(xmlNodePtr node)
322 {
323     Options *o = setup_cycle_func(node);
324     o->direction = OB_DIRECTION_SOUTHEAST;
325     return o;
326 }
327
328 static gpointer setup_north_target_func(xmlNodePtr node)
329 {
330     Options *o = setup_target_func(node);
331     o->direction = OB_DIRECTION_NORTH;
332     return o;
333 }
334
335 static gpointer setup_south_target_func(xmlNodePtr node)
336 {
337     Options *o = setup_target_func(node);
338     o->direction = OB_DIRECTION_SOUTH;
339     return o;
340 }
341
342 static gpointer setup_east_target_func(xmlNodePtr node)
343 {
344     Options *o = setup_target_func(node);
345     o->direction = OB_DIRECTION_EAST;
346     return o;
347 }
348
349 static gpointer setup_west_target_func(xmlNodePtr node)
350 {
351     Options *o = setup_target_func(node);
352     o->direction = OB_DIRECTION_WEST;
353     return o;
354 }
355
356 static gpointer setup_northwest_target_func(xmlNodePtr node)
357 {
358     Options *o = setup_target_func(node);
359     o->direction = OB_DIRECTION_NORTHWEST;
360     return o;
361 }
362
363 static gpointer setup_northeast_target_func(xmlNodePtr node)
364 {
365     Options *o = setup_target_func(node);
366     o->direction = OB_DIRECTION_NORTHEAST;
367     return o;
368 }
369
370 static gpointer setup_southwest_target_func(xmlNodePtr node)
371 {
372     Options *o = setup_target_func(node);
373     o->direction = OB_DIRECTION_SOUTHWEST;
374     return o;
375 }
376
377 static gpointer setup_southeast_target_func(xmlNodePtr node)
378 {
379     Options *o = setup_target_func(node);
380     o->direction = OB_DIRECTION_SOUTHEAST;
381     return o;
382 }
383