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