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