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