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