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