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