]> icculus.org git repositories - dana/openbox.git/blob - openbox/action.c
add the maginificent client_update_transient_tree.
[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_client_action
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     /* similar to the openbox dock for dockapps, don't let user actions give
1187        focus to 3rd-party docks (panels) either (unless they ask for it
1188        themselves). */
1189     if (data->client.any.c->type != OB_CLIENT_TYPE_DOCK) {
1190         /* if using focus_delay, stop the timer now so that focus doesn't go
1191            moving on us */
1192         event_halt_focus_delay();
1193
1194         client_activate(data->activate.any.c, data->activate.here, TRUE);
1195     }
1196 }
1197
1198 void action_focus(union ActionData *data)
1199 {
1200     if (data->client.any.c) {
1201         /* similar to the openbox dock for dockapps, don't let user actions
1202            give focus to 3rd-party docks (panels) either (unless they ask for
1203            it themselves). */
1204         if (data->client.any.c->type != OB_CLIENT_TYPE_DOCK) {
1205             /* if using focus_delay, stop the timer now so that focus doesn't
1206                go moving on us */
1207             event_halt_focus_delay();
1208
1209             client_focus(data->client.any.c);
1210         }
1211     } else {
1212         /* focus action on something other than a client, make keybindings
1213            work for this openbox instance, but don't focus any specific client
1214         */
1215         focus_nothing();
1216     }
1217 }
1218
1219 void action_unfocus (union ActionData *data)
1220 {
1221     if (data->client.any.c == focus_client)
1222         focus_fallback(FALSE);
1223 }
1224
1225 void action_iconify(union ActionData *data)
1226 {
1227     client_action_start(data);
1228     client_iconify(data->client.any.c, TRUE, TRUE);
1229     client_action_end(data);
1230 }
1231
1232 void action_focus_order_to_bottom(union ActionData *data)
1233 {
1234     focus_order_to_bottom(data->client.any.c);
1235 }
1236
1237 void action_raiselower(union ActionData *data)
1238 {
1239     ObClient *c = data->client.any.c;
1240     GList *it;
1241     gboolean raise = FALSE;
1242
1243     for (it = stacking_list; it; it = g_list_next(it)) {
1244         if (WINDOW_IS_CLIENT(it->data)) {
1245             ObClient *cit = it->data;
1246
1247             if (cit == c) break;
1248             if (client_normal(cit) == client_normal(c) &&
1249                     cit->layer == c->layer &&
1250                     cit->frame->visible &&
1251                     !client_search_transient(c, cit))
1252             {
1253                 if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
1254                     raise = TRUE;
1255                     break;
1256                 }
1257             }
1258         }
1259     }
1260
1261     if (raise)
1262         action_raise(data);
1263     else
1264         action_lower(data);
1265 }
1266
1267 void action_raise(union ActionData *data)
1268 {
1269     client_action_start(data);
1270     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1271     client_action_end(data);
1272 }
1273
1274 void action_unshaderaise(union ActionData *data)
1275 {
1276     if (data->client.any.c->shaded)
1277         action_unshade(data);
1278     else
1279         action_raise(data);
1280 }
1281
1282 void action_shadelower(union ActionData *data)
1283 {
1284     if (data->client.any.c->shaded)
1285         action_lower(data);
1286     else
1287         action_shade(data);
1288 }
1289
1290 void action_lower(union ActionData *data)
1291 {
1292     client_action_start(data);
1293     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1294     client_action_end(data);
1295 }
1296
1297 void action_close(union ActionData *data)
1298 {
1299     client_close(data->client.any.c);
1300 }
1301
1302 void action_kill(union ActionData *data)
1303 {
1304     client_kill(data->client.any.c);
1305 }
1306
1307 void action_shade(union ActionData *data)
1308 {
1309     client_action_start(data);
1310     client_shade(data->client.any.c, TRUE);
1311     client_action_end(data);
1312 }
1313
1314 void action_unshade(union ActionData *data)
1315 {
1316     client_action_start(data);
1317     client_shade(data->client.any.c, FALSE);
1318     client_action_end(data);
1319 }
1320
1321 void action_toggle_shade(union ActionData *data)
1322 {
1323     client_action_start(data);
1324     client_shade(data->client.any.c, !data->client.any.c->shaded);
1325     client_action_end(data);
1326 }
1327
1328 void action_toggle_omnipresent(union ActionData *data)
1329
1330     client_set_desktop(data->client.any.c,
1331                        data->client.any.c->desktop == DESKTOP_ALL ?
1332                        screen_desktop : DESKTOP_ALL, FALSE);
1333 }
1334
1335 void action_move_relative_horz(union ActionData *data)
1336 {
1337     ObClient *c = data->relative.any.c;
1338     client_action_start(data);
1339     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1340     client_action_end(data);
1341 }
1342
1343 void action_move_relative_vert(union ActionData *data)
1344 {
1345     ObClient *c = data->relative.any.c;
1346     client_action_start(data);
1347     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1348     client_action_end(data);
1349 }
1350
1351 void action_move_to_center(union ActionData *data)
1352 {
1353     ObClient *c = data->client.any.c;
1354     Rect *area;
1355     area = screen_area_monitor(c->desktop, 0);
1356     client_action_start(data);
1357     client_move(c, area->width / 2 - c->area.width / 2,
1358                 area->height / 2 - c->area.height / 2);
1359     client_action_end(data);
1360 }
1361
1362 void action_resize_relative_horz(union ActionData *data)
1363 {
1364     ObClient *c = data->relative.any.c;
1365     client_action_start(data);
1366     client_resize(c,
1367                   c->area.width + data->relative.deltax * c->size_inc.width,
1368                   c->area.height);
1369     client_action_end(data);
1370 }
1371
1372 void action_resize_relative_vert(union ActionData *data)
1373 {
1374     ObClient *c = data->relative.any.c;
1375     if (!c->shaded) {
1376         client_action_start(data);
1377         client_resize(c, c->area.width, c->area.height +
1378                       data->relative.deltax * c->size_inc.height);
1379         client_action_end(data);
1380     }
1381 }
1382
1383 void action_move_relative(union ActionData *data)
1384 {
1385     ObClient *c = data->relative.any.c;
1386     client_action_start(data);
1387     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1388                 data->relative.deltay);
1389     client_action_end(data);
1390 }
1391
1392 void action_resize_relative(union ActionData *data)
1393 {
1394     ObClient *c = data->relative.any.c;
1395     gint x, y, ow, w, oh, h, lw, lh;
1396
1397     client_action_start(data);
1398
1399     x = c->area.x;
1400     y = c->area.y;
1401     ow = c->area.width;
1402     w = ow + data->relative.deltax * c->size_inc.width
1403         + data->relative.deltaxl * c->size_inc.width;
1404     oh = c->area.height;
1405     h = oh + data->relative.deltay * c->size_inc.height
1406         + data->relative.deltayu * c->size_inc.height;
1407     
1408     client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
1409     client_move_resize(c, x + (ow - w), y + (oh - h), w, h);
1410     client_action_end(data);
1411 }
1412
1413 void action_maximize_full(union ActionData *data)
1414 {
1415     client_action_start(data);
1416     client_maximize(data->client.any.c, TRUE, 0);
1417     client_action_end(data);
1418 }
1419
1420 void action_unmaximize_full(union ActionData *data)
1421 {
1422     client_action_start(data);
1423     client_maximize(data->client.any.c, FALSE, 0);
1424     client_action_end(data);
1425 }
1426
1427 void action_toggle_maximize_full(union ActionData *data)
1428 {
1429     client_action_start(data);
1430     client_maximize(data->client.any.c,
1431                     !(data->client.any.c->max_horz ||
1432                       data->client.any.c->max_vert),
1433                     0);
1434     client_action_end(data);
1435 }
1436
1437 void action_maximize_horz(union ActionData *data)
1438 {
1439     client_action_start(data);
1440     client_maximize(data->client.any.c, TRUE, 1);
1441     client_action_end(data);
1442 }
1443
1444 void action_unmaximize_horz(union ActionData *data)
1445 {
1446     client_action_start(data);
1447     client_maximize(data->client.any.c, FALSE, 1);
1448     client_action_end(data);
1449 }
1450
1451 void action_toggle_maximize_horz(union ActionData *data)
1452 {
1453     client_action_start(data);
1454     client_maximize(data->client.any.c,
1455                     !data->client.any.c->max_horz, 1);
1456     client_action_end(data);
1457 }
1458
1459 void action_maximize_vert(union ActionData *data)
1460 {
1461     client_action_start(data);
1462     client_maximize(data->client.any.c, TRUE, 2);
1463     client_action_end(data);
1464 }
1465
1466 void action_unmaximize_vert(union ActionData *data)
1467 {
1468     client_action_start(data);
1469     client_maximize(data->client.any.c, FALSE, 2);
1470     client_action_end(data);
1471 }
1472
1473 void action_toggle_maximize_vert(union ActionData *data)
1474 {
1475     client_action_start(data);
1476     client_maximize(data->client.any.c,
1477                     !data->client.any.c->max_vert, 2);
1478     client_action_end(data);
1479 }
1480
1481 void action_toggle_fullscreen(union ActionData *data)
1482 {
1483     client_action_start(data);
1484     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1485     client_action_end(data);
1486 }
1487
1488 void action_send_to_desktop(union ActionData *data)
1489 {
1490     ObClient *c = data->sendto.any.c;
1491
1492     if (!client_normal(c)) return;
1493
1494     if (data->sendto.desk < screen_num_desktops ||
1495         data->sendto.desk == DESKTOP_ALL) {
1496         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1497         if (data->sendto.follow)
1498             screen_set_desktop(data->sendto.desk);
1499     }
1500 }
1501
1502 void action_desktop(union ActionData *data)
1503 {
1504     static guint first = (unsigned) -1;
1505
1506     if (data->inter.any.interactive && first == (unsigned) -1)
1507         first = screen_desktop;
1508
1509     if (!data->inter.any.interactive ||
1510         (!data->inter.cancel && !data->inter.final))
1511     {
1512         if (data->desktop.desk < screen_num_desktops ||
1513             data->desktop.desk == DESKTOP_ALL)
1514         {
1515             screen_set_desktop(data->desktop.desk);
1516             if (data->inter.any.interactive)
1517                 screen_desktop_popup(data->desktop.desk, TRUE);
1518         }
1519     } else if (data->inter.cancel) {
1520         screen_set_desktop(first);
1521     }
1522
1523     if (!data->inter.any.interactive || data->inter.final) {
1524         screen_desktop_popup(0, FALSE);
1525         first = (unsigned) -1;
1526     }
1527 }
1528
1529 void action_desktop_dir(union ActionData *data)
1530 {
1531     guint d;
1532
1533     d = screen_cycle_desktop(data->desktopdir.dir,
1534                              data->desktopdir.wrap,
1535                              data->desktopdir.linear,
1536                              data->desktopdir.inter.any.interactive,
1537                              data->desktopdir.inter.final,
1538                              data->desktopdir.inter.cancel);
1539     if (!data->sendtodir.inter.any.interactive ||
1540         !data->sendtodir.inter.final ||
1541         data->sendtodir.inter.cancel)
1542     {
1543         screen_set_desktop(d);
1544     }
1545 }
1546
1547 void action_send_to_desktop_dir(union ActionData *data)
1548 {
1549     ObClient *c = data->sendtodir.inter.any.c;
1550     guint d;
1551
1552     if (!client_normal(c)) return;
1553
1554     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1555                              data->sendtodir.linear,
1556                              data->sendtodir.inter.any.interactive,
1557                              data->sendtodir.inter.final,
1558                              data->sendtodir.inter.cancel);
1559     if (!data->sendtodir.inter.any.interactive ||
1560         !data->sendtodir.inter.final ||
1561         data->sendtodir.inter.cancel)
1562     {
1563         client_set_desktop(c, d, data->sendtodir.follow);
1564         if (data->sendtodir.follow)
1565             screen_set_desktop(d);
1566     }
1567 }
1568
1569 void action_desktop_last(union ActionData *data)
1570 {
1571     screen_set_desktop(screen_last_desktop);
1572 }
1573
1574 void action_toggle_decorations(union ActionData *data)
1575 {
1576     ObClient *c = data->client.any.c;
1577
1578     client_action_start(data);
1579     client_set_undecorated(c, !c->undecorated);
1580     client_action_end(data);
1581 }
1582
1583 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch)
1584 {
1585     /* let's make x and y client relative instead of screen relative */
1586     x = x - cx;
1587     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1588
1589 #define X x*ch/cw
1590 #define A -4*X + 7*ch/3
1591 #define B  4*X -15*ch/9
1592 #define C -X/4 + 2*ch/3
1593 #define D  X/4 + 5*ch/12
1594 #define E  X/4 +   ch/3
1595 #define F -X/4 + 7*ch/12
1596 #define G  4*X - 4*ch/3
1597 #define H -4*X + 8*ch/3
1598 #define a (y > 5*ch/9)
1599 #define b (x < 4*cw/9)
1600 #define c (x > 5*cw/9)
1601 #define d (y < 4*ch/9)
1602
1603     /*
1604       Each of these defines (except X which is just there for fun), represents
1605       the equation of a line. The lines they represent are shown in the diagram
1606       below. Checking y against these lines, we are able to choose a region
1607       of the window as shown.
1608
1609       +---------------------A-------|-------|-------B---------------------+
1610       |                     |A                     B|                     |
1611       |                     |A      |       |      B|                     |
1612       |                     | A                   B |                     |
1613       |                     | A     |       |     B |                     |
1614       |                     |  A                 B  |                     |
1615       |                     |  A    |       |    B  |                     |
1616       |        northwest    |   A     north     B   |   northeast         |
1617       |                     |   A   |       |   B   |                     |
1618       |                     |    A             B    |                     |
1619       C---------------------+----A--+-------+--B----+---------------------D
1620       |CCCCCCC              |     A           B     |              DDDDDDD|
1621       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1622       |               CCCCCCC      A         B      DDDDDDD               |
1623       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - -
1624       |                     |       b       c       |                     |
1625       |             west    |       b  move c       |   east              |
1626       |                     |       b       c       |                     |
1627       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - 
1628       |               EEEEEEE      G         H      FFFFFFF               |
1629       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1630       |EEEEEEE              |     G           H     |              FFFFFFF|
1631       E---------------------+----G--+-------+--H----+---------------------F
1632       |                     |    G             H    |                     |
1633       |                     |   G   |       |   H   |                     |
1634       |        southwest    |   G     south     H   |   southeast         |
1635       |                     |  G    |       |    H  |                     |
1636       |                     |  G                 H  |                     |
1637       |                     | G     |       |     H |                     |
1638       |                     | G                   H |                     |
1639       |                     |G      |       |      H|                     |
1640       |                     |G                     H|                     |
1641       +---------------------G-------|-------|-------H---------------------+
1642     */
1643
1644     if (y < A && y >= C)
1645         return prop_atoms.net_wm_moveresize_size_topleft;
1646     else if (y >= A && y >= B && a)
1647         return prop_atoms.net_wm_moveresize_size_top;
1648     else if (y < B && y >= D)
1649         return prop_atoms.net_wm_moveresize_size_topright;
1650     else if (y < C && y >= E && b)
1651         return prop_atoms.net_wm_moveresize_size_left;
1652     else if (y < D && y >= F && c)
1653         return prop_atoms.net_wm_moveresize_size_right;
1654     else if (y < E && y >= G)
1655         return prop_atoms.net_wm_moveresize_size_bottomleft;
1656     else if (y < G && y < H && d)
1657         return prop_atoms.net_wm_moveresize_size_bottom;
1658     else if (y >= H && y < F)
1659         return prop_atoms.net_wm_moveresize_size_bottomright;
1660     else
1661         return prop_atoms.net_wm_moveresize_move;
1662
1663 #undef X
1664 #undef A
1665 #undef B
1666 #undef C
1667 #undef D
1668 #undef E
1669 #undef F
1670 #undef G
1671 #undef H
1672 #undef a
1673 #undef b
1674 #undef c
1675 #undef d
1676 }
1677
1678 void action_moveresize(union ActionData *data)
1679 {
1680     ObClient *c = data->moveresize.any.c;
1681     guint32 corner;
1682
1683     if (!client_normal(c)) return;
1684
1685     if (data->moveresize.keyboard) {
1686         corner = (data->moveresize.move ?
1687                   prop_atoms.net_wm_moveresize_move_keyboard :
1688                   prop_atoms.net_wm_moveresize_size_keyboard);
1689     } else {
1690         corner = (data->moveresize.move ?
1691                   prop_atoms.net_wm_moveresize_move :
1692                   pick_corner(data->any.x, data->any.y,
1693                               c->frame->area.x, c->frame->area.y,
1694                               /* use the client size because the frame
1695                                  can be differently sized (shaded
1696                                  windows) and we want this based on the
1697                                  clients size */
1698                               c->area.width + c->frame->size.left +
1699                               c->frame->size.right,
1700                               c->area.height + c->frame->size.top +
1701                               c->frame->size.bottom));
1702         const gchar *c;
1703         if (corner == prop_atoms.net_wm_moveresize_size_topright)
1704             c = "topright";
1705         else if (corner == prop_atoms.net_wm_moveresize_size_top)
1706             c = "top";
1707         else if (corner == prop_atoms.net_wm_moveresize_size_topleft)
1708             c = "topleft";
1709         else if (corner == prop_atoms.net_wm_moveresize_size_right)
1710             c = "right";
1711         else if (corner == prop_atoms.net_wm_moveresize_move)
1712             c = "middle";
1713         else if (corner == prop_atoms.net_wm_moveresize_size_left)
1714             c = "left";
1715         else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
1716             c = "bottomright";
1717         else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
1718             c = "bottom";
1719         else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
1720             c = "bottomleft";
1721         ob_debug("corner: %s\n", c);
1722     }
1723
1724     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1725 }
1726
1727 void action_reconfigure(union ActionData *data)
1728 {
1729     ob_reconfigure();
1730 }
1731
1732 void action_restart(union ActionData *data)
1733 {
1734     ob_restart_other(data->execute.path);
1735 }
1736
1737 void action_exit(union ActionData *data)
1738 {
1739     ob_exit(0);
1740 }
1741
1742 void action_showmenu(union ActionData *data)
1743 {
1744     if (data->showmenu.name) {
1745         menu_show(data->showmenu.name, data->any.x, data->any.y,
1746                   data->any.button, data->showmenu.any.c);
1747     }
1748 }
1749
1750 void action_cycle_windows(union ActionData *data)
1751 {
1752     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1753        on us */
1754     event_halt_focus_delay();
1755
1756     focus_cycle(data->cycle.forward,
1757                 data->cycle.dock_windows,
1758                 data->cycle.linear, data->any.interactive,
1759                 data->cycle.dialog,
1760                 data->cycle.inter.final, data->cycle.inter.cancel);
1761 }
1762
1763 void action_directional_focus(union ActionData *data)
1764 {
1765     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1766        on us */
1767     event_halt_focus_delay();
1768
1769     focus_directional_cycle(data->interdiraction.direction,
1770                             data->interdiraction.dock_windows,
1771                             data->any.interactive,
1772                             data->interdiraction.dialog,
1773                             data->interdiraction.inter.final,
1774                             data->interdiraction.inter.cancel);
1775 }
1776
1777 void action_movetoedge(union ActionData *data)
1778 {
1779     gint x, y;
1780     ObClient *c = data->diraction.any.c;
1781
1782     x = c->frame->area.x;
1783     y = c->frame->area.y;
1784     
1785     switch(data->diraction.direction) {
1786     case OB_DIRECTION_NORTH:
1787         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1788                                            data->diraction.hang)
1789             - (data->diraction.hang ? c->frame->area.height : 0);
1790         break;
1791     case OB_DIRECTION_WEST:
1792         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1793                                            data->diraction.hang)
1794             - (data->diraction.hang ? c->frame->area.width : 0);
1795         break;
1796     case OB_DIRECTION_SOUTH:
1797         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1798                                            data->diraction.hang)
1799             - (data->diraction.hang ? 0 : c->frame->area.height);
1800         break;
1801     case OB_DIRECTION_EAST:
1802         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1803                                            data->diraction.hang)
1804             - (data->diraction.hang ? 0 : c->frame->area.width);
1805         break;
1806     default:
1807         g_assert_not_reached();
1808     }
1809     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1810     client_action_start(data);
1811     client_move(c, x, y);
1812     client_action_end(data);
1813 }
1814
1815 void action_growtoedge(union ActionData *data)
1816 {
1817     gint x, y, width, height, dest;
1818     ObClient *c = data->diraction.any.c;
1819     Rect *a;
1820
1821     //FIXME growtoedge resizes shaded windows to 0 height
1822     if (c->shaded)
1823         return;
1824
1825     a = screen_area(c->desktop);
1826     x = c->frame->area.x;
1827     y = c->frame->area.y;
1828     width = c->frame->area.width;
1829     height = c->frame->area.height;
1830
1831     switch(data->diraction.direction) {
1832     case OB_DIRECTION_NORTH:
1833         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1834         if (a->y == y)
1835             height = c->frame->area.height / 2;
1836         else {
1837             height = c->frame->area.y + c->frame->area.height - dest;
1838             y = dest;
1839         }
1840         break;
1841     case OB_DIRECTION_WEST:
1842         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1843         if (a->x == x)
1844             width = c->frame->area.width / 2;
1845         else {
1846             width = c->frame->area.x + c->frame->area.width - dest;
1847             x = dest;
1848         }
1849         break;
1850     case OB_DIRECTION_SOUTH:
1851         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1852         if (a->y + a->height == y + c->frame->area.height) {
1853             height = c->frame->area.height / 2;
1854             y = a->y + a->height - height;
1855         } else
1856             height = dest - c->frame->area.y;
1857         y += (height - c->frame->area.height) % c->size_inc.height;
1858         height -= (height - c->frame->area.height) % c->size_inc.height;
1859         break;
1860     case OB_DIRECTION_EAST:
1861         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1862         if (a->x + a->width == x + c->frame->area.width) {
1863             width = c->frame->area.width / 2;
1864             x = a->x + a->width - width;
1865         } else
1866             width = dest - c->frame->area.x;
1867         x += (width - c->frame->area.width) % c->size_inc.width;
1868         width -= (width - c->frame->area.width) % c->size_inc.width;
1869         break;
1870     default:
1871         g_assert_not_reached();
1872     }
1873     width -= c->frame->size.left + c->frame->size.right;
1874     height -= c->frame->size.top + c->frame->size.bottom;
1875     frame_frame_gravity(c->frame, &x, &y, width, height);
1876     client_action_start(data);
1877     client_move_resize(c, x, y, width, height);
1878     client_action_end(data);
1879 }
1880
1881 void action_send_to_layer(union ActionData *data)
1882 {
1883     client_set_layer(data->layer.any.c, data->layer.layer);
1884 }
1885
1886 void action_toggle_layer(union ActionData *data)
1887 {
1888     ObClient *c = data->layer.any.c;
1889
1890     client_action_start(data);
1891     if (data->layer.layer < 0)
1892         client_set_layer(c, c->below ? 0 : -1);
1893     else if (data->layer.layer > 0)
1894         client_set_layer(c, c->above ? 0 : 1);
1895     client_action_end(data);
1896 }
1897
1898 void action_toggle_dockautohide(union ActionData *data)
1899 {
1900     config_dock_hide = !config_dock_hide;
1901     dock_configure();
1902 }
1903
1904 void action_toggle_show_desktop(union ActionData *data)
1905 {
1906     screen_show_desktop(!screen_showing_desktop);
1907 }
1908
1909 void action_show_desktop(union ActionData *data)
1910 {
1911     screen_show_desktop(TRUE);
1912 }
1913
1914 void action_unshow_desktop(union ActionData *data)
1915 {
1916     screen_show_desktop(FALSE);
1917 }
1918
1919 void action_break_chroot(union ActionData *data)
1920 {
1921     /* break out of one chroot */
1922     keyboard_reset_chains(1);
1923 }