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