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