add <desktop>yes</desktop> option for Next/Previous window and directional focus...
[mikachu/openbox.git] / openbox / action.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    action.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "client.h"
22 #include "focus.h"
23 #include "moveresize.h"
24 #include "menu.h"
25 #include "prop.h"
26 #include "stacking.h"
27 #include "screen.h"
28 #include "action.h"
29 #include "openbox.h"
30 #include "grab.h"
31 #include "keyboard.h"
32 #include "event.h"
33 #include "dock.h"
34 #include "config.h"
35 #include "mainloop.h"
36 #include "startupnotify.h"
37 #include "gettext.h"
38
39 #include <glib.h>
40
41 inline void client_action_start(union ActionData *data)
42 {
43     if (config_focus_follow)
44         if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button)
45             grab_pointer(TRUE, FALSE, OB_CURSOR_NONE);
46 }
47
48 inline void client_action_end(union ActionData *data)
49 {
50     if (config_focus_follow)
51         if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button)
52             grab_pointer(FALSE, FALSE, OB_CURSOR_NONE);
53 }
54
55 typedef struct
56 {
57     const gchar *name;
58     void (*func)(union ActionData *);
59     void (*setup)(ObAction **, ObUserAction uact);
60 } ActionString;
61
62 static ObAction *action_new(void (*func)(union ActionData *data))
63 {
64     ObAction *a = g_new0(ObAction, 1);
65     a->ref = 1;
66     a->func = func;
67
68     return a;
69 }
70
71 void action_ref(ObAction *a)
72 {
73     ++a->ref;
74 }
75
76 void action_unref(ObAction *a)
77 {
78     if (a == NULL) return;
79
80     if (--a->ref > 0) return;
81
82     /* deal with pointers */
83     if (a->func == action_execute || a->func == action_restart)
84         g_free(a->data.execute.path);
85     else if (a->func == action_showmenu)
86         g_free(a->data.showmenu.name);
87
88     g_free(a);
89 }
90
91 ObAction* action_copy(const ObAction *src)
92 {
93     ObAction *a = action_new(src->func);
94
95     a->data = src->data;
96
97     /* deal with pointers */
98     if (a->func == action_execute || a->func == action_restart)
99         a->data.execute.path = g_strdup(a->data.execute.path);
100     else if (a->func == action_showmenu)
101         a->data.showmenu.name = g_strdup(a->data.showmenu.name);
102
103     return a;
104 }
105
106 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
107 {
108     (*a)->data.interdiraction.inter.any.interactive = TRUE;
109     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
110     (*a)->data.interdiraction.dialog = TRUE;
111     (*a)->data.interdiraction.dock_windows = FALSE;
112     (*a)->data.interdiraction.desktop_windows = FALSE;
113 }
114
115 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
116 {
117     (*a)->data.interdiraction.inter.any.interactive = TRUE;
118     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
119     (*a)->data.interdiraction.dialog = TRUE;
120     (*a)->data.interdiraction.dock_windows = FALSE;
121     (*a)->data.interdiraction.desktop_windows = FALSE;
122 }
123
124 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
125 {
126     (*a)->data.interdiraction.inter.any.interactive = TRUE;
127     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
128     (*a)->data.interdiraction.dialog = TRUE;
129     (*a)->data.interdiraction.dock_windows = FALSE;
130     (*a)->data.interdiraction.desktop_windows = FALSE;
131 }
132
133 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
134 {
135     (*a)->data.interdiraction.inter.any.interactive = TRUE;
136     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
137     (*a)->data.interdiraction.dialog = TRUE;
138     (*a)->data.interdiraction.dock_windows = FALSE;
139     (*a)->data.interdiraction.desktop_windows = FALSE;
140 }
141
142 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
143 {
144     (*a)->data.interdiraction.inter.any.interactive = TRUE;
145     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
146     (*a)->data.interdiraction.dialog = TRUE;
147     (*a)->data.interdiraction.dock_windows = FALSE;
148     (*a)->data.interdiraction.desktop_windows = FALSE;
149 }
150
151 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
152 {
153     (*a)->data.interdiraction.inter.any.interactive = TRUE;
154     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
155     (*a)->data.interdiraction.dialog = TRUE;
156     (*a)->data.interdiraction.dock_windows = FALSE;
157     (*a)->data.interdiraction.desktop_windows = FALSE;
158 }
159
160 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
161 {
162     (*a)->data.interdiraction.inter.any.interactive = TRUE;
163     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
164     (*a)->data.interdiraction.dialog = TRUE;
165     (*a)->data.interdiraction.dock_windows = FALSE;
166     (*a)->data.interdiraction.desktop_windows = FALSE;
167 }
168
169 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
170 {
171     (*a)->data.interdiraction.inter.any.interactive = TRUE;
172     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
173     (*a)->data.interdiraction.dialog = TRUE;
174     (*a)->data.interdiraction.dock_windows = FALSE;
175     (*a)->data.interdiraction.desktop_windows = FALSE;
176 }
177
178 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
179 {
180     (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
181     (*a)->data.sendto.follow = TRUE;
182 }
183
184 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
185 {
186     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
187     (*a)->data.sendtodir.inter.any.interactive = TRUE;
188     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
189     (*a)->data.sendtodir.linear = TRUE;
190     (*a)->data.sendtodir.wrap = TRUE;
191     (*a)->data.sendtodir.follow = TRUE;
192 }
193
194 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
195 {
196     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
197     (*a)->data.sendtodir.inter.any.interactive = TRUE;
198     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
199     (*a)->data.sendtodir.linear = TRUE;
200     (*a)->data.sendtodir.wrap = TRUE;
201     (*a)->data.sendtodir.follow = TRUE;
202 }
203
204 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
205 {
206     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
207     (*a)->data.sendtodir.inter.any.interactive = TRUE;
208     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
209     (*a)->data.sendtodir.linear = FALSE;
210     (*a)->data.sendtodir.wrap = TRUE;
211     (*a)->data.sendtodir.follow = TRUE;
212 }
213
214 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
215 {
216     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
217     (*a)->data.sendtodir.inter.any.interactive = TRUE;
218     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
219     (*a)->data.sendtodir.linear = FALSE;
220     (*a)->data.sendtodir.wrap = TRUE;
221     (*a)->data.sendtodir.follow = TRUE;
222 }
223
224 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
225 {
226     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
227     (*a)->data.sendtodir.inter.any.interactive = TRUE;
228     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
229     (*a)->data.sendtodir.linear = FALSE;
230     (*a)->data.sendtodir.wrap = TRUE;
231     (*a)->data.sendtodir.follow = TRUE;
232 }
233
234 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
235 {
236     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
237     (*a)->data.sendtodir.inter.any.interactive = TRUE;
238     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
239     (*a)->data.sendtodir.linear = FALSE;
240     (*a)->data.sendtodir.wrap = TRUE;
241     (*a)->data.sendtodir.follow = TRUE;
242 }
243
244 void setup_action_desktop(ObAction **a, ObUserAction uact)
245 {
246     (*a)->data.desktop.inter.any.interactive = FALSE;
247 }
248
249 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
250 {
251     (*a)->data.desktopdir.inter.any.interactive = TRUE;
252     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
253     (*a)->data.desktopdir.linear = TRUE;
254     (*a)->data.desktopdir.wrap = TRUE;
255 }
256
257 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
258 {
259     (*a)->data.desktopdir.inter.any.interactive = TRUE;
260     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
261     (*a)->data.desktopdir.linear = TRUE;
262     (*a)->data.desktopdir.wrap = TRUE;
263 }
264
265 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
266 {
267     (*a)->data.desktopdir.inter.any.interactive = TRUE;
268     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
269     (*a)->data.desktopdir.linear = FALSE;
270     (*a)->data.desktopdir.wrap = TRUE;
271 }
272
273 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
274 {
275     (*a)->data.desktopdir.inter.any.interactive = TRUE;
276     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
277     (*a)->data.desktopdir.linear = FALSE;
278     (*a)->data.desktopdir.wrap = TRUE;
279 }
280
281 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
282 {
283     (*a)->data.desktopdir.inter.any.interactive = TRUE;
284     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
285     (*a)->data.desktopdir.linear = FALSE;
286     (*a)->data.desktopdir.wrap = TRUE;
287 }
288
289 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
290 {
291     (*a)->data.desktopdir.inter.any.interactive = TRUE;
292     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
293     (*a)->data.desktopdir.linear = FALSE;
294     (*a)->data.desktopdir.wrap = TRUE;
295 }
296
297 void setup_action_cycle_windows_next(ObAction **a, ObUserAction uact)
298 {
299     (*a)->data.cycle.inter.any.interactive = TRUE;
300     (*a)->data.cycle.linear = FALSE;
301     (*a)->data.cycle.forward = TRUE;
302     (*a)->data.cycle.dialog = TRUE;
303     (*a)->data.cycle.dock_windows = FALSE;
304     (*a)->data.cycle.desktop_windows = FALSE;
305     (*a)->data.cycle.all_desktops = FALSE;
306 }
307
308 void setup_action_cycle_windows_previous(ObAction **a, ObUserAction uact)
309 {
310     (*a)->data.cycle.inter.any.interactive = TRUE;
311     (*a)->data.cycle.linear = FALSE;
312     (*a)->data.cycle.forward = FALSE;
313     (*a)->data.cycle.dialog = TRUE;
314     (*a)->data.cycle.dock_windows = FALSE;
315     (*a)->data.cycle.desktop_windows = FALSE;
316     (*a)->data.cycle.all_desktops = FALSE;
317 }
318
319 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
320 {
321     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
322     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
323     (*a)->data.diraction.hang = TRUE;
324 }
325
326 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
327 {
328     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
329     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
330     (*a)->data.diraction.hang = TRUE;
331 }
332
333 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
334 {
335     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
336     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
337     (*a)->data.diraction.hang = TRUE;
338 }
339
340 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
341 {
342     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
343     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
344     (*a)->data.diraction.hang = TRUE;
345 }
346
347 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
348 {
349     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
350     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
351     (*a)->data.diraction.hang = FALSE;
352 }
353
354 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
355 {
356     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
357     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
358     (*a)->data.diraction.hang = FALSE;
359 }
360
361 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
362 {
363     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
364     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
365     (*a)->data.diraction.hang = FALSE;
366 }
367
368 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
369 {
370     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
371     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
372     (*a)->data.diraction.hang = FALSE;
373 }
374
375 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
376 {
377     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
378     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
379 }
380
381 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
382 {
383     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
384     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
385 }
386
387 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
388 {
389     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
390     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
391 }
392
393 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
394 {
395     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
396     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
397 }
398
399 void setup_action_top_layer(ObAction **a, ObUserAction uact)
400 {
401     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
402     (*a)->data.layer.layer = 1;
403 }
404
405 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
406 {
407     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
408     (*a)->data.layer.layer = 0;
409 }
410
411 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
412 {
413     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
414     (*a)->data.layer.layer = -1;
415 }
416
417 void setup_action_move(ObAction **a, ObUserAction uact)
418 {
419     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
420     (*a)->data.moveresize.move = TRUE;
421     (*a)->data.moveresize.keyboard =
422         (uact == OB_USER_ACTION_NONE ||
423          uact == OB_USER_ACTION_KEYBOARD_KEY ||
424          uact == OB_USER_ACTION_MENU_SELECTION);
425 }
426
427 void setup_action_resize(ObAction **a, ObUserAction uact)
428 {
429     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
430     (*a)->data.moveresize.move = FALSE;
431     (*a)->data.moveresize.keyboard =
432         (uact == OB_USER_ACTION_NONE ||
433          uact == OB_USER_ACTION_KEYBOARD_KEY ||
434          uact == OB_USER_ACTION_MENU_SELECTION);
435 }
436
437 void setup_action_showmenu(ObAction **a, ObUserAction uact)
438 {
439     (*a)->data.showmenu.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
440     /* you cannot call ShowMenu from inside a menu, cuz the menu code makes
441        assumptions that there is only one menu (and submenus) open at
442        a time! */
443     if (uact == OB_USER_ACTION_MENU_SELECTION) {
444         action_unref(*a);
445         *a = NULL;
446     }
447 }
448
449 void setup_action_focus(ObAction **a, ObUserAction uact)
450 {
451     (*a)->data.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
452 }
453
454 void setup_client_action(ObAction **a, ObUserAction uact)
455 {
456     (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
457 }
458
459 ActionString actionstrings[] =
460 {
461     {
462         "execute", 
463         action_execute, 
464         NULL
465     },
466     {
467         "directionalfocusnorth", 
468         action_directional_focus, 
469         setup_action_directional_focus_north
470     },
471     {
472         "directionalfocuseast", 
473         action_directional_focus, 
474         setup_action_directional_focus_east
475     },
476     {
477         "directionalfocussouth", 
478         action_directional_focus, 
479         setup_action_directional_focus_south
480     },
481     {
482         "directionalfocuswest",
483         action_directional_focus,
484         setup_action_directional_focus_west
485     },
486     {
487         "directionalfocusnortheast",
488         action_directional_focus,
489         setup_action_directional_focus_northeast
490     },
491     {
492         "directionalfocussoutheast",
493         action_directional_focus,
494         setup_action_directional_focus_southeast
495     },
496     {
497         "directionalfocussouthwest",
498         action_directional_focus,
499         setup_action_directional_focus_southwest
500     },
501     {
502         "directionalfocusnorthwest",
503         action_directional_focus,
504         setup_action_directional_focus_northwest
505     },
506     {
507         "activate",
508         action_activate,
509         setup_action_focus
510     },
511     {
512         "focus",
513         action_focus,
514         setup_action_focus
515     },
516     {
517         "unfocus",
518         action_unfocus,
519         setup_client_action
520     },
521     {
522         "iconify",
523         action_iconify,
524         setup_client_action
525     },
526     {
527         "focustobottom",
528         action_focus_order_to_bottom,
529         setup_client_action
530     },
531     {
532         "raiselower",
533         action_raiselower,
534         setup_client_action
535     },
536     {
537         "raise",
538         action_raise,
539         setup_client_action
540     },
541     {
542         "lower",
543         action_lower,
544         setup_client_action
545     },
546     {
547         "close",
548         action_close,
549         setup_client_action
550     },
551     {
552         "kill",
553         action_kill,
554         setup_client_action
555     },
556     {
557         "shadelower",
558         action_shadelower,
559         setup_client_action
560     },
561     {
562         "unshaderaise",
563         action_unshaderaise,
564         setup_client_action
565     },
566     {
567         "shade",
568         action_shade,
569         setup_client_action
570     },
571     {
572         "unshade",
573         action_unshade,
574         setup_client_action
575     },
576     {
577         "toggleshade",
578         action_toggle_shade,
579         setup_client_action
580     },
581     {
582         "toggleomnipresent",
583         action_toggle_omnipresent,
584         setup_client_action
585     },
586     {
587         "moverelativehorz",
588         action_move_relative_horz,
589         setup_client_action
590     },
591     {
592         "moverelativevert",
593         action_move_relative_vert,
594         setup_client_action
595     },
596     {
597         "movetocenter",
598         action_move_to_center,
599         setup_client_action
600     },
601     {
602         "resizerelativehorz",
603         action_resize_relative_horz,
604         setup_client_action
605     },
606     {
607         "resizerelativevert",
608         action_resize_relative_vert,
609         setup_client_action
610     },
611     {
612         "moverelative",
613         action_move_relative,
614         setup_client_action
615     },
616     {
617         "resizerelative",
618         action_resize_relative,
619         setup_client_action
620     },
621     {
622         "maximizefull",
623         action_maximize_full,
624         setup_client_action
625     },
626     {
627         "unmaximizefull",
628         action_unmaximize_full,
629         setup_client_action
630     },
631     {
632         "togglemaximizefull",
633         action_toggle_maximize_full,
634         setup_client_action
635     },
636     {
637         "maximizehorz",
638         action_maximize_horz,
639         setup_client_action
640     },
641     {
642         "unmaximizehorz",
643         action_unmaximize_horz,
644         setup_client_action
645     },
646     {
647         "togglemaximizehorz",
648         action_toggle_maximize_horz,
649         setup_client_action
650     },
651     {
652         "maximizevert",
653         action_maximize_vert,
654         setup_client_action
655     },
656     {
657         "unmaximizevert",
658         action_unmaximize_vert,
659         setup_client_action
660     },
661     {
662         "togglemaximizevert",
663         action_toggle_maximize_vert,
664         setup_client_action
665     },
666     {
667         "togglefullscreen",
668         action_toggle_fullscreen,
669         setup_client_action
670     },
671     {
672         "sendtodesktop",
673         action_send_to_desktop,
674         setup_action_send_to_desktop
675     },
676     {
677         "sendtodesktopnext",
678         action_send_to_desktop_dir,
679         setup_action_send_to_desktop_next
680     },
681     {
682         "sendtodesktopprevious",
683         action_send_to_desktop_dir,
684         setup_action_send_to_desktop_prev
685     },
686     {
687         "sendtodesktopright",
688         action_send_to_desktop_dir,
689         setup_action_send_to_desktop_right
690     },
691     {
692         "sendtodesktopleft",
693         action_send_to_desktop_dir,
694         setup_action_send_to_desktop_left
695     },
696     {
697         "sendtodesktopup",
698         action_send_to_desktop_dir,
699         setup_action_send_to_desktop_up
700     },
701     {
702         "sendtodesktopdown",
703         action_send_to_desktop_dir,
704         setup_action_send_to_desktop_down
705     },
706     {
707         "desktop",
708         action_desktop,
709         setup_action_desktop
710     },
711     {
712         "desktopnext",
713         action_desktop_dir,
714         setup_action_desktop_next
715     },
716     {
717         "desktopprevious",
718         action_desktop_dir,
719         setup_action_desktop_prev
720     },
721     {
722         "desktopright",
723         action_desktop_dir,
724         setup_action_desktop_right
725     },
726     {
727         "desktopleft",
728         action_desktop_dir,
729         setup_action_desktop_left
730     },
731     {
732         "desktopup",
733         action_desktop_dir,
734         setup_action_desktop_up
735     },
736     {
737         "desktopdown",
738         action_desktop_dir,
739         setup_action_desktop_down
740     },
741     {
742         "toggledecorations",
743         action_toggle_decorations,
744         setup_client_action
745     },
746     {
747         "move",
748         action_moveresize,
749         setup_action_move
750     },
751     {
752         "resize",
753         action_moveresize,
754         setup_action_resize
755     },
756     {
757         "toggledockautohide",
758         action_toggle_dockautohide,
759         NULL
760     },
761     {
762         "toggleshowdesktop",
763         action_toggle_show_desktop,
764         NULL
765     },
766     {
767         "showdesktop",
768         action_show_desktop,
769         NULL
770     },
771     {
772         "unshowdesktop",
773         action_unshow_desktop,
774         NULL
775     },
776     {
777         "desktoplast",
778         action_desktop_last,
779         NULL
780     },
781     {
782         "reconfigure",
783         action_reconfigure,
784         NULL
785     },
786     {
787         "restart",
788         action_restart,
789         NULL
790     },
791     {
792         "exit",
793         action_exit,
794         NULL
795     },
796     {
797         "showmenu",
798         action_showmenu,
799         setup_action_showmenu
800     },
801     {
802         "sendtotoplayer",
803         action_send_to_layer,
804         setup_action_top_layer
805     },
806     {
807         "togglealwaysontop",
808         action_toggle_layer,
809         setup_action_top_layer
810     },
811     {
812         "sendtonormallayer",
813         action_send_to_layer,
814         setup_action_normal_layer
815     },
816     {
817         "sendtobottomlayer",
818         action_send_to_layer,
819         setup_action_bottom_layer
820     },
821     {
822         "togglealwaysonbottom",
823         action_toggle_layer,
824         setup_action_bottom_layer
825     },
826     {
827         "nextwindow",
828         action_cycle_windows,
829         setup_action_cycle_windows_next
830     },
831     {
832         "previouswindow",
833         action_cycle_windows,
834         setup_action_cycle_windows_previous
835     },
836     {
837         "movefromedgenorth",
838         action_movetoedge,
839         setup_action_movefromedge_north
840     },
841     {
842         "movefromedgesouth",
843         action_movetoedge,
844         setup_action_movefromedge_south
845     },
846     {
847         "movefromedgewest",
848         action_movetoedge,
849         setup_action_movefromedge_west
850     },
851     {
852         "movefromedgeeast",
853         action_movetoedge,
854         setup_action_movefromedge_east
855     },
856     {
857         "movetoedgenorth",
858         action_movetoedge,
859         setup_action_movetoedge_north
860     },
861     {
862         "movetoedgesouth",
863         action_movetoedge,
864         setup_action_movetoedge_south
865     },
866     {
867         "movetoedgewest",
868         action_movetoedge,
869         setup_action_movetoedge_west
870     },
871     {
872         "movetoedgeeast",
873         action_movetoedge,
874         setup_action_movetoedge_east
875     },
876     {
877         "growtoedgenorth",
878         action_growtoedge,
879         setup_action_growtoedge_north
880     },
881     {
882         "growtoedgesouth",
883         action_growtoedge,
884         setup_action_growtoedge_south
885     },
886     {
887         "growtoedgewest",
888         action_growtoedge,
889         setup_action_growtoedge_west
890     },
891     {
892         "growtoedgeeast",
893         action_growtoedge,
894         setup_action_growtoedge_east
895     },
896     {
897         "breakchroot",
898         action_break_chroot,
899         NULL
900     },
901     {
902         NULL,
903         NULL,
904         NULL
905     }
906 };
907
908 /* only key bindings can be interactive. thus saith the xor.
909    because of how the mouse is grabbed, mouse events dont even get
910    read during interactive events, so no dice! >:) */
911 #define INTERACTIVE_LIMIT(a, uact) \
912     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
913         a->data.any.interactive = FALSE;
914
915 ObAction *action_from_string(const gchar *name, ObUserAction uact)
916 {
917     ObAction *a = NULL;
918     gboolean exist = FALSE;
919     gint i;
920
921     for (i = 0; actionstrings[i].name; i++)
922         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
923             exist = TRUE;
924             a = action_new(actionstrings[i].func);
925             if (actionstrings[i].setup)
926                 actionstrings[i].setup(&a, uact);
927             if (a)
928                 INTERACTIVE_LIMIT(a, uact);
929             break;
930         }
931     if (!exist)
932         g_message(_("Invalid action '%s' requested. No such action exists."),
933                   name);
934     if (!a)
935         g_message(_("Invalid use of action '%s'. Action will be ignored."),
936                   name);
937     return a;
938 }
939
940 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
941                        ObUserAction uact)
942 {
943     gchar *actname;
944     ObAction *act = NULL;
945     xmlNodePtr n;
946
947     if (parse_attr_string("name", node, &actname)) {
948         if ((act = action_from_string(actname, uact))) {
949             if (act->func == action_execute || act->func == action_restart) {
950                 if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
951                     gchar *s = parse_string(doc, n);
952                     act->data.execute.path = parse_expand_tilde(s);
953                     g_free(s);
954                 }
955                 if ((n = parse_find_node("startupnotify", node->xmlChildrenNode))) {
956                     xmlNodePtr m;
957                     if ((m = parse_find_node("enabled", n->xmlChildrenNode)))
958                         act->data.execute.startupnotify = parse_bool(doc, m);
959                     if ((m = parse_find_node("name", n->xmlChildrenNode)))
960                         act->data.execute.name = parse_string(doc, m);
961                     if ((m = parse_find_node("icon", n->xmlChildrenNode)))
962                         act->data.execute.icon_name = parse_string(doc, m);
963                 }
964             } else if (act->func == action_showmenu) {
965                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
966                     act->data.showmenu.name = parse_string(doc, n);
967             } else if (act->func == action_move_relative_horz ||
968                        act->func == action_move_relative_vert ||
969                        act->func == action_resize_relative_horz ||
970                        act->func == action_resize_relative_vert) {
971                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
972                     act->data.relative.deltax = parse_int(doc, n);
973             } else if (act->func == action_move_relative) {
974                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
975                     act->data.relative.deltax = parse_int(doc, n);
976                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
977                     act->data.relative.deltay = parse_int(doc, n);
978             } else if (act->func == action_resize_relative) {
979                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
980                     act->data.relative.deltaxl = parse_int(doc, n);
981                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
982                     act->data.relative.deltayu = parse_int(doc, n);
983                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
984                     act->data.relative.deltax = parse_int(doc, n);
985                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
986                     act->data.relative.deltay = parse_int(doc, n);
987             } else if (act->func == action_desktop) {
988                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
989                     act->data.desktop.desk = parse_int(doc, n);
990                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
991                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
992                     act->data.desktop.inter.any.interactive =
993                         parse_bool(doc, n);
994            } else if (act->func == action_send_to_desktop) {
995                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
996                     act->data.sendto.desk = parse_int(doc, n);
997                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
998                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
999                     act->data.sendto.follow = parse_bool(doc, n);
1000             } else if (act->func == action_desktop_dir) {
1001                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1002                     act->data.desktopdir.wrap = parse_bool(doc, n); 
1003                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1004                     act->data.desktopdir.inter.any.interactive =
1005                         parse_bool(doc, n);
1006             } else if (act->func == action_send_to_desktop_dir) {
1007                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1008                     act->data.sendtodir.wrap = parse_bool(doc, n);
1009                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1010                     act->data.sendtodir.follow = parse_bool(doc, n);
1011                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1012                     act->data.sendtodir.inter.any.interactive =
1013                         parse_bool(doc, n);
1014             } else if (act->func == action_activate) {
1015                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
1016                     act->data.activate.here = parse_bool(doc, n);
1017             } else if (act->func == action_cycle_windows) {
1018                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
1019                     act->data.cycle.linear = parse_bool(doc, n);
1020                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1021                     act->data.cycle.dialog = parse_bool(doc, n);
1022                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1023                     act->data.cycle.dock_windows = parse_bool(doc, n);
1024                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1025                     act->data.cycle.desktop_windows = parse_bool(doc, n);
1026                 if ((n = parse_find_node("allDesktops",
1027                                          node->xmlChildrenNode)))
1028                     act->data.cycle.all_desktops = parse_bool(doc, n);
1029             } else if (act->func == action_directional_focus) {
1030                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1031                     act->data.interdiraction.dialog = parse_bool(doc, n);
1032                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1033                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
1034                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1035                     act->data.interdiraction.desktop_windows =
1036                         parse_bool(doc, n);
1037             } else if (act->func == action_raise ||
1038                        act->func == action_lower ||
1039                        act->func == action_raiselower ||
1040                        act->func == action_shadelower ||
1041                        act->func == action_unshaderaise) {
1042             }
1043             INTERACTIVE_LIMIT(act, uact);
1044         }
1045         g_free(actname);
1046     }
1047     return act;
1048 }
1049
1050 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
1051                      guint state, guint button, gint x, gint y, Time time,
1052                      gboolean cancel, gboolean done)
1053 {
1054     GSList *it;
1055     ObAction *a;
1056     gboolean inter = FALSE;
1057
1058     if (!acts)
1059         return;
1060
1061     if (x < 0 && y < 0)
1062         screen_pointer_pos(&x, &y);
1063
1064     if (grab_on_keyboard())
1065         inter = TRUE;
1066     else
1067         for (it = acts; it; it = g_slist_next(it)) {
1068             a = it->data;
1069             if (a->data.any.interactive) {
1070                 inter = TRUE;
1071                 break;
1072             }
1073         }
1074
1075     if (!inter) {
1076         /* sometimes when we execute another app as an action,
1077            it won't work right unless we XUngrabKeyboard first,
1078            even though we grabbed the key/button Asychronously.
1079            e.g. "gnome-panel-control --main-menu" */
1080         grab_keyboard(FALSE);
1081     }
1082
1083     for (it = acts; it; it = g_slist_next(it)) {
1084         a = it->data;
1085
1086         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
1087             a->data.any.c = a->data.any.client_action ? c : NULL;
1088             a->data.any.context = context;
1089             a->data.any.x = x;
1090             a->data.any.y = y;
1091
1092             a->data.any.button = button;
1093
1094             a->data.any.time = time;
1095
1096             if (a->data.any.interactive) {
1097                 a->data.inter.cancel = cancel;
1098                 a->data.inter.final = done;
1099                 if (!(cancel || done))
1100                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
1101                         continue;
1102             }
1103
1104             /* XXX UGLY HACK race with motion event starting a move and the
1105                button release gettnig processed first. answer: don't queue
1106                moveresize starts. UGLY HACK XXX */
1107             if (a->data.any.interactive || a->func == action_moveresize) {
1108                 /* interactive actions are not queued */
1109                 a->func(&a->data);
1110             } else if ((context == OB_FRAME_CONTEXT_CLIENT ||
1111                         (c && c->type == OB_CLIENT_TYPE_DESKTOP &&
1112                          context == OB_FRAME_CONTEXT_DESKTOP)) &&
1113                        (a->func == action_focus ||
1114                         a->func == action_activate ||
1115                         a->func == action_showmenu))
1116             {
1117                 /* XXX MORE UGLY HACK
1118                    actions from clicks on client windows are NOT queued.
1119                    this solves the mysterious click-and-drag-doesnt-work
1120                    problem. it was because the window gets focused and stuff
1121                    after the button event has already been passed through. i
1122                    dont really know why it should care but it does and it makes
1123                    a difference.
1124
1125                    however this very bogus ! !
1126                    we want to send the button press to the window BEFORE
1127                    we do the action because the action might move the windows
1128                    (eg change desktops) and then the button press ends up on
1129                    the completely wrong window !
1130                    so, this is just for that bug, and it will only NOT queue it
1131                    if it is a focusing action that can be used with the mouse
1132                    pointer. ugh.
1133
1134                    also with the menus, there is a race going on. if the
1135                    desktop wants to pop up a menu, and we do to, we send them
1136                    the button before we pop up the menu, so they pop up their
1137                    menu first. but not always. if we pop up our menu before
1138                    sending them the button press, then the result is
1139                    deterministic. yay.
1140                 */
1141                 a->func(&a->data);
1142             } else
1143                 ob_main_loop_queue_action(ob_main_loop, a);
1144         }
1145     }
1146 }
1147
1148 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1149 {
1150     ObAction *a;
1151     GSList *l;
1152
1153     a = action_from_string(name, OB_USER_ACTION_NONE);
1154     g_assert(a);
1155
1156     l = g_slist_append(NULL, a);
1157
1158     action_run(l, c, 0, time);
1159 }
1160
1161 void action_execute(union ActionData *data)
1162 {
1163     GError *e = NULL;
1164     gchar *cmd, **argv = 0;
1165     if (data->execute.path) {
1166         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
1167         if (cmd) {
1168             if (!g_shell_parse_argv (cmd, NULL, &argv, &e)) {
1169                 g_message(_("Failed to execute '%s': %s"),
1170                           cmd, e->message);
1171                 g_error_free(e);
1172             } else if (data->execute.startupnotify) {
1173                 gchar *program;
1174                 
1175                 program = g_path_get_basename(argv[0]);
1176                 /* sets up the environment */
1177                 sn_setup_spawn_environment(program,
1178                                            data->execute.name,
1179                                            data->execute.icon_name,
1180                                            /* launch it on the current
1181                                               desktop */
1182                                            screen_desktop,
1183                                            data->execute.any.time);
1184                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1185                                    G_SPAWN_DO_NOT_REAP_CHILD,
1186                                    NULL, NULL, NULL, &e)) {
1187                     g_message(_("Failed to execute '%s': %s"),
1188                               cmd, e->message);
1189                     g_error_free(e);
1190                     sn_spawn_cancel();
1191                 }
1192                 unsetenv("DESKTOP_STARTUP_ID");
1193                 g_free(program);
1194                 g_strfreev(argv);
1195             } else {
1196                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1197                                    G_SPAWN_DO_NOT_REAP_CHILD,
1198                                    NULL, NULL, NULL, &e))
1199                 {
1200                     g_message(_("Failed to execute '%s': %s"),
1201                               cmd, e->message);
1202                     g_error_free(e);
1203                 }
1204                 g_strfreev(argv);
1205             }
1206             g_free(cmd);
1207         } else {
1208             g_message(_("Failed to convert the path '%s' from utf8"),
1209                       data->execute.path);
1210         }
1211     }
1212 }
1213
1214 void action_activate(union ActionData *data)
1215 {
1216     if (data->client.any.c) {
1217         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1218             data->any.context != OB_FRAME_CONTEXT_CLIENT)
1219         {
1220             /* if using focus_delay, stop the timer now so that focus doesn't
1221                go moving on us */
1222             event_halt_focus_delay();
1223
1224             client_activate(data->activate.any.c, data->activate.here, TRUE);
1225         }
1226     } else {
1227         /* focus action on something other than a client, make keybindings
1228            work for this openbox instance, but don't focus any specific client
1229         */
1230         focus_nothing();
1231     }
1232 }
1233
1234 void action_focus(union ActionData *data)
1235 {
1236     if (data->client.any.c) {
1237         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1238             data->any.context != OB_FRAME_CONTEXT_CLIENT)
1239         {
1240             /* if using focus_delay, stop the timer now so that focus doesn't
1241                go moving on us */
1242             event_halt_focus_delay();
1243
1244             client_focus(data->client.any.c);
1245         }
1246     } else {
1247         /* focus action on something other than a client, make keybindings
1248            work for this openbox instance, but don't focus any specific client
1249         */
1250         focus_nothing();
1251     }
1252 }
1253
1254 void action_unfocus (union ActionData *data)
1255 {
1256     if (data->client.any.c == focus_client)
1257         focus_fallback(FALSE);
1258 }
1259
1260 void action_iconify(union ActionData *data)
1261 {
1262     client_action_start(data);
1263     client_iconify(data->client.any.c, TRUE, TRUE, FALSE);
1264     client_action_end(data);
1265 }
1266
1267 void action_focus_order_to_bottom(union ActionData *data)
1268 {
1269     focus_order_to_bottom(data->client.any.c);
1270 }
1271
1272 void action_raiselower(union ActionData *data)
1273 {
1274     ObClient *c = data->client.any.c;
1275     GList *it;
1276     gboolean raise = FALSE;
1277
1278     for (it = stacking_list; it; it = g_list_next(it)) {
1279         if (WINDOW_IS_CLIENT(it->data)) {
1280             ObClient *cit = it->data;
1281
1282             if (cit == c) break;
1283             if (client_normal(cit) == client_normal(c) &&
1284                 cit->layer == c->layer &&
1285                 cit->frame->visible &&
1286                 !client_search_transient(c, cit))
1287             {
1288                 if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
1289                     raise = TRUE;
1290                     break;
1291                 }
1292             }
1293         }
1294     }
1295
1296     if (raise)
1297         action_raise(data);
1298     else
1299         action_lower(data);
1300 }
1301
1302 void action_raise(union ActionData *data)
1303 {
1304     client_action_start(data);
1305     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1306     client_action_end(data);
1307 }
1308
1309 void action_unshaderaise(union ActionData *data)
1310 {
1311     if (data->client.any.c->shaded)
1312         action_unshade(data);
1313     else
1314         action_raise(data);
1315 }
1316
1317 void action_shadelower(union ActionData *data)
1318 {
1319     if (data->client.any.c->shaded)
1320         action_lower(data);
1321     else
1322         action_shade(data);
1323 }
1324
1325 void action_lower(union ActionData *data)
1326 {
1327     client_action_start(data);
1328     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1329     client_action_end(data);
1330 }
1331
1332 void action_close(union ActionData *data)
1333 {
1334     client_close(data->client.any.c);
1335 }
1336
1337 void action_kill(union ActionData *data)
1338 {
1339     client_kill(data->client.any.c);
1340 }
1341
1342 void action_shade(union ActionData *data)
1343 {
1344     client_action_start(data);
1345     client_shade(data->client.any.c, TRUE);
1346     client_action_end(data);
1347 }
1348
1349 void action_unshade(union ActionData *data)
1350 {
1351     client_action_start(data);
1352     client_shade(data->client.any.c, FALSE);
1353     client_action_end(data);
1354 }
1355
1356 void action_toggle_shade(union ActionData *data)
1357 {
1358     client_action_start(data);
1359     client_shade(data->client.any.c, !data->client.any.c->shaded);
1360     client_action_end(data);
1361 }
1362
1363 void action_toggle_omnipresent(union ActionData *data)
1364
1365     client_set_desktop(data->client.any.c,
1366                        data->client.any.c->desktop == DESKTOP_ALL ?
1367                        screen_desktop : DESKTOP_ALL, FALSE);
1368 }
1369
1370 void action_move_relative_horz(union ActionData *data)
1371 {
1372     ObClient *c = data->relative.any.c;
1373     client_action_start(data);
1374     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1375     client_action_end(data);
1376 }
1377
1378 void action_move_relative_vert(union ActionData *data)
1379 {
1380     ObClient *c = data->relative.any.c;
1381     client_action_start(data);
1382     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1383     client_action_end(data);
1384 }
1385
1386 void action_move_to_center(union ActionData *data)
1387 {
1388     ObClient *c = data->client.any.c;
1389     Rect *area;
1390     area = screen_area_monitor(c->desktop, 0);
1391     client_action_start(data);
1392     client_move(c, area->width / 2 - c->area.width / 2,
1393                 area->height / 2 - c->area.height / 2);
1394     client_action_end(data);
1395 }
1396
1397 void action_resize_relative_horz(union ActionData *data)
1398 {
1399     ObClient *c = data->relative.any.c;
1400     client_action_start(data);
1401     client_resize(c,
1402                   c->area.width + data->relative.deltax * c->size_inc.width,
1403                   c->area.height);
1404     client_action_end(data);
1405 }
1406
1407 void action_resize_relative_vert(union ActionData *data)
1408 {
1409     ObClient *c = data->relative.any.c;
1410     if (!c->shaded) {
1411         client_action_start(data);
1412         client_resize(c, c->area.width, c->area.height +
1413                       data->relative.deltax * c->size_inc.height);
1414         client_action_end(data);
1415     }
1416 }
1417
1418 void action_move_relative(union ActionData *data)
1419 {
1420     ObClient *c = data->relative.any.c;
1421     client_action_start(data);
1422     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1423                 data->relative.deltay);
1424     client_action_end(data);
1425 }
1426
1427 void action_resize_relative(union ActionData *data)
1428 {
1429     ObClient *c = data->relative.any.c;
1430     gint x, y, ow, w, oh, h, lw, lh;
1431
1432     client_action_start(data);
1433
1434     x = c->area.x;
1435     y = c->area.y;
1436     ow = c->area.width;
1437     w = ow + data->relative.deltax * c->size_inc.width
1438         + data->relative.deltaxl * c->size_inc.width;
1439     oh = c->area.height;
1440     h = oh + data->relative.deltay * c->size_inc.height
1441         + data->relative.deltayu * c->size_inc.height;
1442     
1443     client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
1444     client_move_resize(c, x + (ow - w), y + (oh - h), w, h);
1445     client_action_end(data);
1446 }
1447
1448 void action_maximize_full(union ActionData *data)
1449 {
1450     client_action_start(data);
1451     client_maximize(data->client.any.c, TRUE, 0);
1452     client_action_end(data);
1453 }
1454
1455 void action_unmaximize_full(union ActionData *data)
1456 {
1457     client_action_start(data);
1458     client_maximize(data->client.any.c, FALSE, 0);
1459     client_action_end(data);
1460 }
1461
1462 void action_toggle_maximize_full(union ActionData *data)
1463 {
1464     client_action_start(data);
1465     client_maximize(data->client.any.c,
1466                     !(data->client.any.c->max_horz ||
1467                       data->client.any.c->max_vert),
1468                     0);
1469     client_action_end(data);
1470 }
1471
1472 void action_maximize_horz(union ActionData *data)
1473 {
1474     client_action_start(data);
1475     client_maximize(data->client.any.c, TRUE, 1);
1476     client_action_end(data);
1477 }
1478
1479 void action_unmaximize_horz(union ActionData *data)
1480 {
1481     client_action_start(data);
1482     client_maximize(data->client.any.c, FALSE, 1);
1483     client_action_end(data);
1484 }
1485
1486 void action_toggle_maximize_horz(union ActionData *data)
1487 {
1488     client_action_start(data);
1489     client_maximize(data->client.any.c,
1490                     !data->client.any.c->max_horz, 1);
1491     client_action_end(data);
1492 }
1493
1494 void action_maximize_vert(union ActionData *data)
1495 {
1496     client_action_start(data);
1497     client_maximize(data->client.any.c, TRUE, 2);
1498     client_action_end(data);
1499 }
1500
1501 void action_unmaximize_vert(union ActionData *data)
1502 {
1503     client_action_start(data);
1504     client_maximize(data->client.any.c, FALSE, 2);
1505     client_action_end(data);
1506 }
1507
1508 void action_toggle_maximize_vert(union ActionData *data)
1509 {
1510     client_action_start(data);
1511     client_maximize(data->client.any.c,
1512                     !data->client.any.c->max_vert, 2);
1513     client_action_end(data);
1514 }
1515
1516 void action_toggle_fullscreen(union ActionData *data)
1517 {
1518     client_action_start(data);
1519     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1520     client_action_end(data);
1521 }
1522
1523 void action_send_to_desktop(union ActionData *data)
1524 {
1525     ObClient *c = data->sendto.any.c;
1526
1527     if (!client_normal(c)) return;
1528
1529     if (data->sendto.desk < screen_num_desktops ||
1530         data->sendto.desk == DESKTOP_ALL) {
1531         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1532         if (data->sendto.follow)
1533             screen_set_desktop(data->sendto.desk, TRUE);
1534     }
1535 }
1536
1537 void action_desktop(union ActionData *data)
1538 {
1539     if (!data->inter.any.interactive ||
1540         (!data->inter.cancel && !data->inter.final))
1541     {
1542         if (data->desktop.desk < screen_num_desktops ||
1543             data->desktop.desk == DESKTOP_ALL)
1544         {
1545             screen_set_desktop(data->desktop.desk, TRUE);
1546             if (data->inter.any.interactive)
1547                 screen_desktop_popup(data->desktop.desk, TRUE);
1548         }
1549     } else
1550         screen_desktop_popup(0, FALSE);
1551 }
1552
1553 void action_desktop_dir(union ActionData *data)
1554 {
1555     guint d;
1556
1557     d = screen_cycle_desktop(data->desktopdir.dir,
1558                              data->desktopdir.wrap,
1559                              data->desktopdir.linear,
1560                              data->desktopdir.inter.any.interactive,
1561                              data->desktopdir.inter.final,
1562                              data->desktopdir.inter.cancel);
1563     /* only move the desktop when the action is complete. if we switch
1564        desktops during the interactive action, focus will move but with
1565        NotifyWhileGrabbed and applications don't like that. */
1566     if (!data->sendtodir.inter.any.interactive ||
1567         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1568     {
1569         if (d != screen_desktop) screen_set_desktop(d, TRUE);
1570     }
1571 }
1572
1573 void action_send_to_desktop_dir(union ActionData *data)
1574 {
1575     ObClient *c = data->sendtodir.inter.any.c;
1576     guint d;
1577
1578     if (!client_normal(c)) return;
1579
1580     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1581                              data->sendtodir.linear,
1582                              data->sendtodir.inter.any.interactive,
1583                              data->sendtodir.inter.final,
1584                              data->sendtodir.inter.cancel);
1585     /* only move the desktop when the action is complete. if we switch
1586        desktops during the interactive action, focus will move but with
1587        NotifyWhileGrabbed and applications don't like that. */
1588     if (!data->sendtodir.inter.any.interactive ||
1589         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1590     {
1591         client_set_desktop(c, d, data->sendtodir.follow);
1592         if (data->sendtodir.follow && d != screen_desktop)
1593             screen_set_desktop(d, TRUE);
1594     }
1595 }
1596
1597 void action_desktop_last(union ActionData *data)
1598 {
1599     screen_set_desktop(screen_last_desktop, TRUE);
1600 }
1601
1602 void action_toggle_decorations(union ActionData *data)
1603 {
1604     ObClient *c = data->client.any.c;
1605
1606     client_action_start(data);
1607     client_set_undecorated(c, !c->undecorated);
1608     client_action_end(data);
1609 }
1610
1611 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1612                            gboolean shaded)
1613 {
1614     /* let's make x and y client relative instead of screen relative */
1615     x = x - cx;
1616     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1617
1618 #define X x*ch/cw
1619 #define A -4*X + 7*ch/3
1620 #define B  4*X -15*ch/9
1621 #define C -X/4 + 2*ch/3
1622 #define D  X/4 + 5*ch/12
1623 #define E  X/4 +   ch/3
1624 #define F -X/4 + 7*ch/12
1625 #define G  4*X - 4*ch/3
1626 #define H -4*X + 8*ch/3
1627 #define a (y > 5*ch/9)
1628 #define b (x < 4*cw/9)
1629 #define c (x > 5*cw/9)
1630 #define d (y < 4*ch/9)
1631
1632     /*
1633       Each of these defines (except X which is just there for fun), represents
1634       the equation of a line. The lines they represent are shown in the diagram
1635       below. Checking y against these lines, we are able to choose a region
1636       of the window as shown.
1637
1638       +---------------------A-------|-------|-------B---------------------+
1639       |                     |A                     B|                     |
1640       |                     |A      |       |      B|                     |
1641       |                     | A                   B |                     |
1642       |                     | A     |       |     B |                     |
1643       |                     |  A                 B  |                     |
1644       |                     |  A    |       |    B  |                     |
1645       |        northwest    |   A     north     B   |   northeast         |
1646       |                     |   A   |       |   B   |                     |
1647       |                     |    A             B    |                     |
1648       C---------------------+----A--+-------+--B----+---------------------D
1649       |CCCCCCC              |     A           B     |              DDDDDDD|
1650       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1651       |               CCCCCCC      A         B      DDDDDDD               |
1652       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1653       |                     |       b       c       |                     | sh
1654       |             west    |       b  move c       |   east              | ad
1655       |                     |       b       c       |                     | ed
1656       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1657       |               EEEEEEE      G         H      FFFFFFF               |
1658       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1659       |EEEEEEE              |     G           H     |              FFFFFFF|
1660       E---------------------+----G--+-------+--H----+---------------------F
1661       |                     |    G             H    |                     |
1662       |                     |   G   |       |   H   |                     |
1663       |        southwest    |   G     south     H   |   southeast         |
1664       |                     |  G    |       |    H  |                     |
1665       |                     |  G                 H  |                     |
1666       |                     | G     |       |     H |                     |
1667       |                     | G                   H |                     |
1668       |                     |G      |       |      H|                     |
1669       |                     |G                     H|                     |
1670       +---------------------G-------|-------|-------H---------------------+
1671     */
1672
1673     if (shaded) {
1674         /* for shaded windows, you can only resize west/east and move */
1675         if (b)
1676             return prop_atoms.net_wm_moveresize_size_left;
1677         if (c)
1678             return prop_atoms.net_wm_moveresize_size_right;
1679         return prop_atoms.net_wm_moveresize_move;
1680     }
1681
1682     if (y < A && y >= C)
1683         return prop_atoms.net_wm_moveresize_size_topleft;
1684     else if (y >= A && y >= B && a)
1685         return prop_atoms.net_wm_moveresize_size_top;
1686     else if (y < B && y >= D)
1687         return prop_atoms.net_wm_moveresize_size_topright;
1688     else if (y < C && y >= E && b)
1689         return prop_atoms.net_wm_moveresize_size_left;
1690     else if (y < D && y >= F && c)
1691         return prop_atoms.net_wm_moveresize_size_right;
1692     else if (y < E && y >= G)
1693         return prop_atoms.net_wm_moveresize_size_bottomleft;
1694     else if (y < G && y < H && d)
1695         return prop_atoms.net_wm_moveresize_size_bottom;
1696     else if (y >= H && y < F)
1697         return prop_atoms.net_wm_moveresize_size_bottomright;
1698     else
1699         return prop_atoms.net_wm_moveresize_move;
1700
1701 #undef X
1702 #undef A
1703 #undef B
1704 #undef C
1705 #undef D
1706 #undef E
1707 #undef F
1708 #undef G
1709 #undef H
1710 #undef a
1711 #undef b
1712 #undef c
1713 #undef d
1714 }
1715
1716 void action_moveresize(union ActionData *data)
1717 {
1718     ObClient *c = data->moveresize.any.c;
1719     guint32 corner;
1720
1721     if (data->moveresize.keyboard) {
1722         corner = (data->moveresize.move ?
1723                   prop_atoms.net_wm_moveresize_move_keyboard :
1724                   prop_atoms.net_wm_moveresize_size_keyboard);
1725     } else {
1726         corner = (data->moveresize.move ?
1727                   prop_atoms.net_wm_moveresize_move :
1728                   pick_corner(data->any.x, data->any.y,
1729                               c->frame->area.x, c->frame->area.y,
1730                               /* use the client size because the frame
1731                                  can be differently sized (shaded
1732                                  windows) and we want this based on the
1733                                  clients size */
1734                               c->area.width + c->frame->size.left +
1735                               c->frame->size.right,
1736                               c->area.height + c->frame->size.top +
1737                               c->frame->size.bottom, c->shaded));
1738     }
1739
1740     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1741 }
1742
1743 void action_reconfigure(union ActionData *data)
1744 {
1745     ob_reconfigure();
1746 }
1747
1748 void action_restart(union ActionData *data)
1749 {
1750     ob_restart_other(data->execute.path);
1751 }
1752
1753 void action_exit(union ActionData *data)
1754 {
1755     ob_exit(0);
1756 }
1757
1758 void action_showmenu(union ActionData *data)
1759 {
1760     if (data->showmenu.name) {
1761         menu_show(data->showmenu.name, data->any.x, data->any.y,
1762                   data->any.button, data->showmenu.any.c);
1763     }
1764 }
1765
1766 void action_cycle_windows(union ActionData *data)
1767 {
1768     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1769        on us */
1770     event_halt_focus_delay();
1771
1772     focus_cycle(data->cycle.forward,
1773                 data->cycle.all_desktops,
1774                 data->cycle.dock_windows,
1775                 data->cycle.desktop_windows,
1776                 data->cycle.linear, data->any.interactive,
1777                 data->cycle.dialog,
1778                 data->cycle.inter.final, data->cycle.inter.cancel);
1779 }
1780
1781 void action_directional_focus(union ActionData *data)
1782 {
1783     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1784        on us */
1785     event_halt_focus_delay();
1786
1787     focus_directional_cycle(data->interdiraction.direction,
1788                             data->interdiraction.dock_windows,
1789                             data->interdiraction.desktop_windows,
1790                             data->any.interactive,
1791                             data->interdiraction.dialog,
1792                             data->interdiraction.inter.final,
1793                             data->interdiraction.inter.cancel);
1794 }
1795
1796 void action_movetoedge(union ActionData *data)
1797 {
1798     gint x, y;
1799     ObClient *c = data->diraction.any.c;
1800
1801     x = c->frame->area.x;
1802     y = c->frame->area.y;
1803     
1804     switch(data->diraction.direction) {
1805     case OB_DIRECTION_NORTH:
1806         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1807                                            data->diraction.hang)
1808             - (data->diraction.hang ? c->frame->area.height : 0);
1809         break;
1810     case OB_DIRECTION_WEST:
1811         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1812                                            data->diraction.hang)
1813             - (data->diraction.hang ? c->frame->area.width : 0);
1814         break;
1815     case OB_DIRECTION_SOUTH:
1816         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1817                                            data->diraction.hang)
1818             - (data->diraction.hang ? 0 : c->frame->area.height);
1819         break;
1820     case OB_DIRECTION_EAST:
1821         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1822                                            data->diraction.hang)
1823             - (data->diraction.hang ? 0 : c->frame->area.width);
1824         break;
1825     default:
1826         g_assert_not_reached();
1827     }
1828     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1829     client_action_start(data);
1830     client_move(c, x, y);
1831     client_action_end(data);
1832 }
1833
1834 void action_growtoedge(union ActionData *data)
1835 {
1836     gint x, y, width, height, dest;
1837     ObClient *c = data->diraction.any.c;
1838     Rect *a;
1839
1840     a = screen_area(c->desktop);
1841     x = c->frame->area.x;
1842     y = c->frame->area.y;
1843     /* get the unshaded frame's dimensions..if it is shaded */
1844     width = c->area.width + c->frame->size.left + c->frame->size.right;
1845     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1846
1847     switch(data->diraction.direction) {
1848     case OB_DIRECTION_NORTH:
1849         if (c->shaded) break; /* don't allow vertical resize if shaded */
1850
1851         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1852         if (a->y == y)
1853             height = height / 2;
1854         else {
1855             height = c->frame->area.y + height - dest;
1856             y = dest;
1857         }
1858         break;
1859     case OB_DIRECTION_WEST:
1860         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1861         if (a->x == x)
1862             width = width / 2;
1863         else {
1864             width = c->frame->area.x + width - dest;
1865             x = dest;
1866         }
1867         break;
1868     case OB_DIRECTION_SOUTH:
1869         if (c->shaded) break; /* don't allow vertical resize if shaded */
1870
1871         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1872         if (a->y + a->height == y + c->frame->area.height) {
1873             height = c->frame->area.height / 2;
1874             y = a->y + a->height - height;
1875         } else
1876             height = dest - c->frame->area.y;
1877         y += (height - c->frame->area.height) % c->size_inc.height;
1878         height -= (height - c->frame->area.height) % c->size_inc.height;
1879         break;
1880     case OB_DIRECTION_EAST:
1881         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1882         if (a->x + a->width == x + c->frame->area.width) {
1883             width = c->frame->area.width / 2;
1884             x = a->x + a->width - width;
1885         } else
1886             width = dest - c->frame->area.x;
1887         x += (width - c->frame->area.width) % c->size_inc.width;
1888         width -= (width - c->frame->area.width) % c->size_inc.width;
1889         break;
1890     default:
1891         g_assert_not_reached();
1892     }
1893     width -= c->frame->size.left + c->frame->size.right;
1894     height -= c->frame->size.top + c->frame->size.bottom;
1895     frame_frame_gravity(c->frame, &x, &y, width, height);
1896     client_action_start(data);
1897     client_move_resize(c, x, y, width, height);
1898     client_action_end(data);
1899 }
1900
1901 void action_send_to_layer(union ActionData *data)
1902 {
1903     client_set_layer(data->layer.any.c, data->layer.layer);
1904 }
1905
1906 void action_toggle_layer(union ActionData *data)
1907 {
1908     ObClient *c = data->layer.any.c;
1909
1910     client_action_start(data);
1911     if (data->layer.layer < 0)
1912         client_set_layer(c, c->below ? 0 : -1);
1913     else if (data->layer.layer > 0)
1914         client_set_layer(c, c->above ? 0 : 1);
1915     client_action_end(data);
1916 }
1917
1918 void action_toggle_dockautohide(union ActionData *data)
1919 {
1920     config_dock_hide = !config_dock_hide;
1921     dock_configure();
1922 }
1923
1924 void action_toggle_show_desktop(union ActionData *data)
1925 {
1926     screen_show_desktop(!screen_showing_desktop, NULL);
1927 }
1928
1929 void action_show_desktop(union ActionData *data)
1930 {
1931     screen_show_desktop(TRUE, NULL);
1932 }
1933
1934 void action_unshow_desktop(union ActionData *data)
1935 {
1936     screen_show_desktop(FALSE, NULL);
1937 }
1938
1939 void action_break_chroot(union ActionData *data)
1940 {
1941     /* break out of one chroot */
1942     keyboard_reset_chains(1);
1943 }