]> icculus.org git repositories - dana/openbox.git/blob - openbox/action.c
stacking_list contains ObWindows that may or may not be ObClients
[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         if (WINDOW_IS_CLIENT(it->data)) {
1099             ObClient *cit = it->data;
1100
1101             if (cit == c) break;
1102             if (client_normal(cit) == client_normal(c) &&
1103                     cit->layer == c->layer &&
1104                     cit->frame->visible &&
1105                     !client_search_transient(c, cit))
1106             {
1107                 if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
1108                     raise = TRUE;
1109                     break;
1110                 }
1111             }
1112         }
1113     }
1114
1115     if (raise)
1116         action_raise(data);
1117     else
1118         action_lower(data);
1119 }
1120
1121 void action_raise(union ActionData *data)
1122 {
1123     client_action_start(data);
1124     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c), data->stacking.group);
1125     client_action_end(data);
1126 }
1127
1128 void action_unshaderaise(union ActionData *data)
1129 {
1130     if (data->client.any.c->shaded)
1131         action_unshade(data);
1132     else
1133         action_raise(data);
1134 }
1135
1136 void action_shadelower(union ActionData *data)
1137 {
1138     if (data->client.any.c->shaded)
1139         action_lower(data);
1140     else
1141         action_shade(data);
1142 }
1143
1144 void action_lower(union ActionData *data)
1145 {
1146     client_action_start(data);
1147     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c), data->stacking.group);
1148     client_action_end(data);
1149 }
1150
1151 void action_close(union ActionData *data)
1152 {
1153     client_close(data->client.any.c);
1154 }
1155
1156 void action_kill(union ActionData *data)
1157 {
1158     client_kill(data->client.any.c);
1159 }
1160
1161 void action_shade(union ActionData *data)
1162 {
1163     client_action_start(data);
1164     client_shade(data->client.any.c, TRUE);
1165     client_action_end(data);
1166 }
1167
1168 void action_unshade(union ActionData *data)
1169 {
1170     client_action_start(data);
1171     client_shade(data->client.any.c, FALSE);
1172     client_action_end(data);
1173 }
1174
1175 void action_toggle_shade(union ActionData *data)
1176 {
1177     client_action_start(data);
1178     client_shade(data->client.any.c, !data->client.any.c->shaded);
1179     client_action_end(data);
1180 }
1181
1182 void action_toggle_omnipresent(union ActionData *data)
1183
1184     client_set_desktop(data->client.any.c,
1185                        data->client.any.c->desktop == DESKTOP_ALL ?
1186                        screen_desktop : DESKTOP_ALL, FALSE);
1187 }
1188
1189 void action_move_relative_horz(union ActionData *data)
1190 {
1191     ObClient *c = data->relative.any.c;
1192     client_action_start(data);
1193     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1194     client_action_end(data);
1195 }
1196
1197 void action_move_relative_vert(union ActionData *data)
1198 {
1199     ObClient *c = data->relative.any.c;
1200     client_action_start(data);
1201     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1202     client_action_end(data);
1203 }
1204
1205 void action_move_to_center(union ActionData *data)
1206 {
1207     ObClient *c = data->client.any.c;
1208     Rect *area;
1209     area = screen_area_monitor(c->desktop, 0);
1210     client_action_start(data);
1211     client_move(c, area->width / 2 - c->area.width / 2,
1212                 area->height / 2 - c->area.height / 2);
1213     client_action_end(data);
1214 }
1215
1216 void action_resize_relative_horz(union ActionData *data)
1217 {
1218     ObClient *c = data->relative.any.c;
1219     client_action_start(data);
1220     client_resize(c,
1221                   c->area.width + data->relative.deltax * c->size_inc.width,
1222                   c->area.height);
1223     client_action_end(data);
1224 }
1225
1226 void action_resize_relative_vert(union ActionData *data)
1227 {
1228     ObClient *c = data->relative.any.c;
1229     if (!c->shaded) {
1230         client_action_start(data);
1231         client_resize(c, c->area.width, c->area.height +
1232                       data->relative.deltax * c->size_inc.height);
1233         client_action_end(data);
1234     }
1235 }
1236
1237 void action_move_relative(union ActionData *data)
1238 {
1239     ObClient *c = data->relative.any.c;
1240     client_action_start(data);
1241     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1242                 data->relative.deltay);
1243     client_action_end(data);
1244 }
1245
1246 void action_resize_relative(union ActionData *data)
1247 {
1248     ObClient *c = data->relative.any.c;
1249     client_action_start(data);
1250     client_move_resize(c,
1251                   c->area.x - data->relative.deltaxl * c->size_inc.width,
1252                   c->area.y - data->relative.deltayu * c->size_inc.height,
1253                   c->area.width + data->relative.deltax  * c->size_inc.width
1254                                 + data->relative.deltaxl * c->size_inc.width,
1255                   c->area.height + data->relative.deltay  * c->size_inc.height
1256                                  + data->relative.deltayu * c->size_inc.height);
1257     client_action_end(data);
1258 }
1259
1260 void action_maximize_full(union ActionData *data)
1261 {
1262     client_action_start(data);
1263     client_maximize(data->client.any.c, TRUE, 0, TRUE);
1264     client_action_end(data);
1265 }
1266
1267 void action_unmaximize_full(union ActionData *data)
1268 {
1269     client_action_start(data);
1270     client_maximize(data->client.any.c, FALSE, 0, TRUE);
1271     client_action_end(data);
1272 }
1273
1274 void action_toggle_maximize_full(union ActionData *data)
1275 {
1276     client_action_start(data);
1277     client_maximize(data->client.any.c,
1278                     !(data->client.any.c->max_horz ||
1279                       data->client.any.c->max_vert),
1280                     0, TRUE);
1281     client_action_end(data);
1282 }
1283
1284 void action_maximize_horz(union ActionData *data)
1285 {
1286     client_action_start(data);
1287     client_maximize(data->client.any.c, TRUE, 1, TRUE);
1288     client_action_end(data);
1289 }
1290
1291 void action_unmaximize_horz(union ActionData *data)
1292 {
1293     client_action_start(data);
1294     client_maximize(data->client.any.c, FALSE, 1, TRUE);
1295     client_action_end(data);
1296 }
1297
1298 void action_toggle_maximize_horz(union ActionData *data)
1299 {
1300     client_action_start(data);
1301     client_maximize(data->client.any.c,
1302                     !data->client.any.c->max_horz, 1, TRUE);
1303     client_action_end(data);
1304 }
1305
1306 void action_maximize_vert(union ActionData *data)
1307 {
1308     client_action_start(data);
1309     client_maximize(data->client.any.c, TRUE, 2, TRUE);
1310     client_action_end(data);
1311 }
1312
1313 void action_unmaximize_vert(union ActionData *data)
1314 {
1315     client_action_start(data);
1316     client_maximize(data->client.any.c, FALSE, 2, TRUE);
1317     client_action_end(data);
1318 }
1319
1320 void action_toggle_maximize_vert(union ActionData *data)
1321 {
1322     client_action_start(data);
1323     client_maximize(data->client.any.c,
1324                     !data->client.any.c->max_vert, 2, TRUE);
1325     client_action_end(data);
1326 }
1327
1328 void action_toggle_fullscreen(union ActionData *data)
1329 {
1330     client_action_start(data);
1331     client_fullscreen(data->client.any.c,
1332                       !(data->client.any.c->fullscreen), TRUE);
1333     client_action_end(data);
1334 }
1335
1336 void action_send_to_desktop(union ActionData *data)
1337 {
1338     ObClient *c = data->sendto.any.c;
1339
1340     if (!client_normal(c)) return;
1341
1342     if (data->sendto.desk < screen_num_desktops ||
1343         data->sendto.desk == DESKTOP_ALL) {
1344         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1345         if (data->sendto.follow)
1346             screen_set_desktop(data->sendto.desk);
1347     }
1348 }
1349
1350 void action_desktop(union ActionData *data)
1351 {
1352     static guint first = (unsigned) -1;
1353
1354     if (data->inter.any.interactive && first == (unsigned) -1)
1355         first = screen_desktop;
1356
1357     if (!data->inter.any.interactive ||
1358         (!data->inter.cancel && !data->inter.final))
1359     {
1360         if (data->desktop.desk < screen_num_desktops ||
1361             data->desktop.desk == DESKTOP_ALL)
1362         {
1363             screen_set_desktop(data->desktop.desk);
1364             if (data->inter.any.interactive)
1365                 screen_desktop_popup(data->desktop.desk, TRUE);
1366         }
1367     } else if (data->inter.cancel) {
1368         screen_set_desktop(first);
1369     }
1370
1371     if (!data->inter.any.interactive || data->inter.final) {
1372         screen_desktop_popup(0, FALSE);
1373         first = (unsigned) -1;
1374     }
1375 }
1376
1377 void action_desktop_dir(union ActionData *data)
1378 {
1379     guint d;
1380
1381     d = screen_cycle_desktop(data->desktopdir.dir,
1382                              data->desktopdir.wrap,
1383                              data->desktopdir.linear,
1384                              data->desktopdir.inter.any.interactive,
1385                              data->desktopdir.inter.final,
1386                              data->desktopdir.inter.cancel);
1387     if (!data->sendtodir.inter.any.interactive ||
1388         !data->sendtodir.inter.final ||
1389         data->sendtodir.inter.cancel)
1390     {
1391         screen_set_desktop(d);
1392     }
1393 }
1394
1395 void action_send_to_desktop_dir(union ActionData *data)
1396 {
1397     ObClient *c = data->sendtodir.inter.any.c;
1398     guint d;
1399
1400     if (!client_normal(c)) return;
1401
1402     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1403                              data->sendtodir.linear,
1404                              data->sendtodir.inter.any.interactive,
1405                              data->sendtodir.inter.final,
1406                              data->sendtodir.inter.cancel);
1407     if (!data->sendtodir.inter.any.interactive ||
1408         !data->sendtodir.inter.final ||
1409         data->sendtodir.inter.cancel)
1410     {
1411         client_set_desktop(c, d, data->sendtodir.follow);
1412         if (data->sendtodir.follow)
1413             screen_set_desktop(d);
1414     }
1415 }
1416
1417 void action_desktop_last(union ActionData *data)
1418 {
1419     screen_set_desktop(screen_last_desktop);
1420 }
1421
1422 void action_toggle_decorations(union ActionData *data)
1423 {
1424     ObClient *c = data->client.any.c;
1425
1426     client_action_start(data);
1427     client_set_undecorated(c, !c->undecorated);
1428     client_action_end(data);
1429 }
1430
1431 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch)
1432 {
1433     if (config_resize_four_corners) {
1434         if (x - cx > cw / 2) {
1435             if (y - cy > ch / 2)
1436                 return prop_atoms.net_wm_moveresize_size_bottomright;
1437             else
1438                 return prop_atoms.net_wm_moveresize_size_topright;
1439         } else {
1440             if (y - cy > ch / 2)
1441                 return prop_atoms.net_wm_moveresize_size_bottomleft;
1442             else
1443                 return prop_atoms.net_wm_moveresize_size_topleft;
1444         }
1445     } else {
1446         if (x - cx > cw * 2 / 3) {
1447             if (y - cy > ch * 2 / 3)
1448                 return prop_atoms.net_wm_moveresize_size_bottomright;
1449             else if (y - cy < ch / 3)
1450                 return prop_atoms.net_wm_moveresize_size_topright;
1451             else
1452                 return prop_atoms.net_wm_moveresize_size_right;
1453         } else if (x - cx < cw / 3) {
1454             if (y - cy > ch * 2 / 3)
1455                 return prop_atoms.net_wm_moveresize_size_bottomleft;
1456             else if (y - cy < ch / 3)
1457                 return prop_atoms.net_wm_moveresize_size_topleft;
1458             else
1459                 return prop_atoms.net_wm_moveresize_size_left;
1460         } else
1461             if (y - cy > ch * 2 / 3)
1462                 return prop_atoms.net_wm_moveresize_size_bottom;
1463             else if (y - cy < ch / 3)
1464                 return prop_atoms.net_wm_moveresize_size_top;
1465             else
1466                 return prop_atoms.net_wm_moveresize_move;
1467     }
1468 }
1469
1470 void action_moveresize(union ActionData *data)
1471 {
1472     ObClient *c = data->moveresize.any.c;
1473     guint32 corner;
1474
1475     if (!client_normal(c)) return;
1476
1477     if (data->moveresize.keyboard) {
1478         corner = (data->moveresize.move ?
1479                   prop_atoms.net_wm_moveresize_move_keyboard :
1480                   prop_atoms.net_wm_moveresize_size_keyboard);
1481     } else {
1482         corner = (data->moveresize.move ?
1483                   prop_atoms.net_wm_moveresize_move :
1484                   pick_corner(data->any.x, data->any.y,
1485                               c->frame->area.x, c->frame->area.y,
1486                               /* use the client size because the frame
1487                                  can be differently sized (shaded
1488                                  windows) and we want this based on the
1489                                  clients size */
1490                               c->area.width + c->frame->size.left +
1491                               c->frame->size.right,
1492                               c->area.height + c->frame->size.top +
1493                               c->frame->size.bottom));
1494     }
1495
1496     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1497 }
1498
1499 void action_reconfigure(union ActionData *data)
1500 {
1501     ob_reconfigure();
1502 }
1503
1504 void action_restart(union ActionData *data)
1505 {
1506     ob_restart_other(data->execute.path);
1507 }
1508
1509 void action_exit(union ActionData *data)
1510 {
1511     ob_exit(0);
1512 }
1513
1514 void action_showmenu(union ActionData *data)
1515 {
1516     if (data->showmenu.name) {
1517         menu_show(data->showmenu.name, data->any.x, data->any.y,
1518                   data->showmenu.any.c);
1519     }
1520 }
1521
1522 void action_cycle_windows(union ActionData *data)
1523 {
1524     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1525        on us */
1526     event_halt_focus_delay();
1527
1528     focus_cycle(data->cycle.forward, data->cycle.linear, data->any.interactive,
1529                 data->cycle.dialog,
1530                 data->cycle.inter.final, data->cycle.inter.cancel);
1531 }
1532
1533 void action_directional_focus(union ActionData *data)
1534 {
1535     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1536        on us */
1537     event_halt_focus_delay();
1538
1539     focus_directional_cycle(data->interdiraction.direction,
1540                             data->any.interactive,
1541                             data->interdiraction.dialog,
1542                             data->interdiraction.inter.final,
1543                             data->interdiraction.inter.cancel);
1544 }
1545
1546 void action_movetoedge(union ActionData *data)
1547 {
1548     gint x, y;
1549     ObClient *c = data->diraction.any.c;
1550
1551     x = c->frame->area.x;
1552     y = c->frame->area.y;
1553     
1554     switch(data->diraction.direction) {
1555     case OB_DIRECTION_NORTH:
1556         y = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1557         break;
1558     case OB_DIRECTION_WEST:
1559         x = client_directional_edge_search(c, OB_DIRECTION_WEST);
1560         break;
1561     case OB_DIRECTION_SOUTH:
1562         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH) -
1563             c->frame->area.height;
1564         break;
1565     case OB_DIRECTION_EAST:
1566         x = client_directional_edge_search(c, OB_DIRECTION_EAST) -
1567             c->frame->area.width;
1568         break;
1569     default:
1570         g_assert_not_reached();
1571     }
1572     frame_frame_gravity(c->frame, &x, &y);
1573     client_action_start(data);
1574     client_move(c, x, y);
1575     client_action_end(data);
1576 }
1577
1578 void action_growtoedge(union ActionData *data)
1579 {
1580     gint x, y, width, height, dest;
1581     ObClient *c = data->diraction.any.c;
1582     Rect *a;
1583
1584     //FIXME growtoedge resizes shaded windows to 0 height
1585     if (c->shaded)
1586         return;
1587
1588     a = screen_area(c->desktop);
1589     x = c->frame->area.x;
1590     y = c->frame->area.y;
1591     width = c->frame->area.width;
1592     height = c->frame->area.height;
1593
1594     switch(data->diraction.direction) {
1595     case OB_DIRECTION_NORTH:
1596         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1597         if (a->y == y)
1598             height = c->frame->area.height / 2;
1599         else {
1600             height = c->frame->area.y + c->frame->area.height - dest;
1601             y = dest;
1602         }
1603         break;
1604     case OB_DIRECTION_WEST:
1605         dest = client_directional_edge_search(c, OB_DIRECTION_WEST);
1606         if (a->x == x)
1607             width = c->frame->area.width / 2;
1608         else {
1609             width = c->frame->area.x + c->frame->area.width - dest;
1610             x = dest;
1611         }
1612         break;
1613     case OB_DIRECTION_SOUTH:
1614         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH);
1615         if (a->y + a->height == y + c->frame->area.height) {
1616             height = c->frame->area.height / 2;
1617             y = a->y + a->height - height;
1618         } else
1619             height = dest - c->frame->area.y;
1620         y += (height - c->frame->area.height) % c->size_inc.height;
1621         height -= (height - c->frame->area.height) % c->size_inc.height;
1622         break;
1623     case OB_DIRECTION_EAST:
1624         dest = client_directional_edge_search(c, OB_DIRECTION_EAST);
1625         if (a->x + a->width == x + c->frame->area.width) {
1626             width = c->frame->area.width / 2;
1627             x = a->x + a->width - width;
1628         } else
1629             width = dest - c->frame->area.x;
1630         x += (width - c->frame->area.width) % c->size_inc.width;
1631         width -= (width - c->frame->area.width) % c->size_inc.width;
1632         break;
1633     default:
1634         g_assert_not_reached();
1635     }
1636     frame_frame_gravity(c->frame, &x, &y);
1637     width -= c->frame->size.left + c->frame->size.right;
1638     height -= c->frame->size.top + c->frame->size.bottom;
1639     client_action_start(data);
1640     client_move_resize(c, x, y, width, height);
1641     client_action_end(data);
1642 }
1643
1644 void action_send_to_layer(union ActionData *data)
1645 {
1646     client_set_layer(data->layer.any.c, data->layer.layer);
1647 }
1648
1649 void action_toggle_layer(union ActionData *data)
1650 {
1651     ObClient *c = data->layer.any.c;
1652
1653     client_action_start(data);
1654     if (data->layer.layer < 0)
1655         client_set_layer(c, c->below ? 0 : -1);
1656     else if (data->layer.layer > 0)
1657         client_set_layer(c, c->above ? 0 : 1);
1658     client_action_end(data);
1659 }
1660
1661 void action_toggle_dockautohide(union ActionData *data)
1662 {
1663     config_dock_hide = !config_dock_hide;
1664     dock_configure();
1665 }
1666
1667 void action_toggle_show_desktop(union ActionData *data)
1668 {
1669     screen_show_desktop(!screen_showing_desktop);
1670 }
1671
1672 void action_show_desktop(union ActionData *data)
1673 {
1674     screen_show_desktop(TRUE);
1675 }
1676
1677 void action_unshow_desktop(union ActionData *data)
1678 {
1679     screen_show_desktop(FALSE);
1680 }