cycling between dock windows now possible with the <panels>yes</panels> option
[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         NULL,
897         NULL,
898         NULL
899     }
900 };
901
902 /* only key bindings can be interactive. thus saith the xor.
903    because of how the mouse is grabbed, mouse events dont even get
904    read during interactive events, so no dice! >:) */
905 #define INTERACTIVE_LIMIT(a, uact) \
906     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
907         a->data.any.interactive = FALSE;
908
909 ObAction *action_from_string(const gchar *name, ObUserAction uact)
910 {
911     ObAction *a = NULL;
912     gboolean exist = FALSE;
913     gint i;
914
915     for (i = 0; actionstrings[i].name; i++)
916         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
917             exist = TRUE;
918             a = action_new(actionstrings[i].func);
919             if (actionstrings[i].setup)
920                 actionstrings[i].setup(&a, uact);
921             if (a)
922                 INTERACTIVE_LIMIT(a, uact);
923             break;
924         }
925     if (!exist)
926         g_message(_("Invalid action '%s' requested. No such action exists."),
927                   name);
928     if (!a)
929         g_message(_("Invalid use of action '%s'. Action will be ignored."),
930                   name);
931     return a;
932 }
933
934 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
935                        ObUserAction uact)
936 {
937     gchar *actname;
938     ObAction *act = NULL;
939     xmlNodePtr n;
940
941     if (parse_attr_string("name", node, &actname)) {
942         if ((act = action_from_string(actname, uact))) {
943             if (act->func == action_execute || act->func == action_restart) {
944                 if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
945                     gchar *s = parse_string(doc, n);
946                     act->data.execute.path = parse_expand_tilde(s);
947                     g_free(s);
948                 }
949                 if ((n = parse_find_node("startupnotify", node->xmlChildrenNode))) {
950                     xmlNodePtr m;
951                     if ((m = parse_find_node("enabled", n->xmlChildrenNode)))
952                         act->data.execute.startupnotify = parse_bool(doc, m);
953                     if ((m = parse_find_node("name", n->xmlChildrenNode)))
954                         act->data.execute.name = parse_string(doc, m);
955                     if ((m = parse_find_node("icon", n->xmlChildrenNode)))
956                         act->data.execute.icon_name = parse_string(doc, m);
957                 }
958             } else if (act->func == action_showmenu) {
959                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
960                     act->data.showmenu.name = parse_string(doc, n);
961             } else if (act->func == action_move_relative_horz ||
962                        act->func == action_move_relative_vert ||
963                        act->func == action_resize_relative_horz ||
964                        act->func == action_resize_relative_vert) {
965                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
966                     act->data.relative.deltax = parse_int(doc, n);
967             } else if (act->func == action_move_relative) {
968                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
969                     act->data.relative.deltax = parse_int(doc, n);
970                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
971                     act->data.relative.deltay = parse_int(doc, n);
972             } else if (act->func == action_resize_relative) {
973                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
974                     act->data.relative.deltaxl = parse_int(doc, n);
975                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
976                     act->data.relative.deltayu = parse_int(doc, n);
977                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
978                     act->data.relative.deltax = parse_int(doc, n);
979                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
980                     act->data.relative.deltay = parse_int(doc, n);
981             } else if (act->func == action_desktop) {
982                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
983                     act->data.desktop.desk = parse_int(doc, n);
984                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
985                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
986                     act->data.desktop.inter.any.interactive =
987                         parse_bool(doc, n);
988            } else if (act->func == action_send_to_desktop) {
989                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
990                     act->data.sendto.desk = parse_int(doc, n);
991                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
992                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
993                     act->data.sendto.follow = parse_bool(doc, n);
994             } else if (act->func == action_desktop_dir) {
995                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
996                     act->data.desktopdir.wrap = parse_bool(doc, n); 
997                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
998                     act->data.desktopdir.inter.any.interactive =
999                         parse_bool(doc, n);
1000             } else if (act->func == action_send_to_desktop_dir) {
1001                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1002                     act->data.sendtodir.wrap = parse_bool(doc, n);
1003                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1004                     act->data.sendtodir.follow = parse_bool(doc, n);
1005                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1006                     act->data.sendtodir.inter.any.interactive =
1007                         parse_bool(doc, n);
1008             } else if (act->func == action_activate) {
1009                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
1010                     act->data.activate.here = parse_bool(doc, n);
1011             } else if (act->func == action_cycle_windows) {
1012                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
1013                     act->data.cycle.linear = parse_bool(doc, n);
1014                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1015                     act->data.cycle.dialog = parse_bool(doc, n);
1016                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1017                     act->data.cycle.dock_windows = parse_bool(doc, n);
1018             } else if (act->func == action_directional_focus) {
1019                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1020                     act->data.interdiraction.dialog = parse_bool(doc, n);
1021                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1022                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
1023             } else if (act->func == action_raise ||
1024                        act->func == action_lower ||
1025                        act->func == action_raiselower ||
1026                        act->func == action_shadelower ||
1027                        act->func == action_unshaderaise) {
1028             }
1029             INTERACTIVE_LIMIT(act, uact);
1030         }
1031         g_free(actname);
1032     }
1033     return act;
1034 }
1035
1036 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
1037                      guint state, guint button, gint x, gint y, Time time,
1038                      gboolean cancel, gboolean done)
1039 {
1040     GSList *it;
1041     ObAction *a;
1042     gboolean inter = FALSE;
1043
1044     if (!acts)
1045         return;
1046
1047     if (x < 0 && y < 0)
1048         screen_pointer_pos(&x, &y);
1049
1050     if (grab_on_keyboard())
1051         inter = TRUE;
1052     else
1053         for (it = acts; it; it = g_slist_next(it)) {
1054             a = it->data;
1055             if (a->data.any.interactive) {
1056                 inter = TRUE;
1057                 break;
1058             }
1059         }
1060
1061     if (!inter) {
1062         /* sometimes when we execute another app as an action,
1063            it won't work right unless we XUngrabKeyboard first,
1064            even though we grabbed the key/button Asychronously.
1065            e.g. "gnome-panel-control --main-menu" */
1066         grab_keyboard(FALSE);
1067     }
1068
1069     for (it = acts; it; it = g_slist_next(it)) {
1070         a = it->data;
1071
1072         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
1073             a->data.any.c = a->data.any.client_action ? c : NULL;
1074             a->data.any.context = context;
1075             a->data.any.x = x;
1076             a->data.any.y = y;
1077
1078             a->data.any.button = button;
1079
1080             a->data.any.time = time;
1081
1082             if (a->data.any.interactive) {
1083                 a->data.inter.cancel = cancel;
1084                 a->data.inter.final = done;
1085                 if (!(cancel || done))
1086                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
1087                         continue;
1088             }
1089
1090             /* XXX UGLY HACK race with motion event starting a move and the
1091                button release gettnig processed first. answer: don't queue
1092                moveresize starts. UGLY HACK XXX */
1093             if (a->data.any.interactive || a->func == action_moveresize) {
1094                 /* interactive actions are not queued */
1095                 a->func(&a->data);
1096             } else
1097                 ob_main_loop_queue_action(ob_main_loop, a);
1098         }
1099     }
1100 }
1101
1102 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1103 {
1104     ObAction *a;
1105     GSList *l;
1106
1107     a = action_from_string(name, OB_USER_ACTION_NONE);
1108     g_assert(a);
1109
1110     l = g_slist_append(NULL, a);
1111
1112     action_run(l, c, 0, time);
1113 }
1114
1115 void action_execute(union ActionData *data)
1116 {
1117     GError *e = NULL;
1118     gchar *cmd, **argv = 0;
1119     if (data->execute.path) {
1120         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
1121         if (cmd) {
1122             if (!g_shell_parse_argv (cmd, NULL, &argv, &e)) {
1123                 g_message(_("Failed to execute '%s': %s"),
1124                           cmd, e->message);
1125                 g_error_free(e);
1126             } else if (data->execute.startupnotify) {
1127                 gchar *program;
1128                 
1129                 program = g_path_get_basename(argv[0]);
1130                 /* sets up the environment */
1131                 sn_setup_spawn_environment(program,
1132                                            data->execute.name,
1133                                            data->execute.icon_name,
1134                                            /* launch it on the current
1135                                               desktop */
1136                                            screen_desktop,
1137                                            data->execute.any.time);
1138                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1139                                    G_SPAWN_DO_NOT_REAP_CHILD,
1140                                    NULL, NULL, NULL, &e)) {
1141                     g_message(_("Failed to execute '%s': %s"),
1142                               cmd, e->message);
1143                     g_error_free(e);
1144                     sn_spawn_cancel();
1145                 }
1146                 unsetenv("DESKTOP_STARTUP_ID");
1147                 g_free(program);
1148                 g_strfreev(argv);
1149             } else {
1150                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1151                                    G_SPAWN_DO_NOT_REAP_CHILD,
1152                                    NULL, NULL, NULL, &e))
1153                 {
1154                     g_message(_("Failed to execute '%s': %s"),
1155                               cmd, e->message);
1156                     g_error_free(e);
1157                 }
1158                 g_strfreev(argv);
1159             }
1160             g_free(cmd);
1161         } else {
1162             g_message(_("Failed to convert the path '%s' from utf8"),
1163                       data->execute.path);
1164         }
1165     }
1166 }
1167
1168 void action_activate(union ActionData *data)
1169 {
1170     /* similar to the openbox dock for dockapps, don't let user actions give
1171        focus to 3rd-party docks (panels) either (unless they ask for it
1172        themselves). */
1173     if (data->client.any.c->type != OB_CLIENT_TYPE_DOCK) {
1174         /* if using focus_delay, stop the timer now so that focus doesn't go
1175            moving on us */
1176         event_halt_focus_delay();
1177
1178         client_activate(data->activate.any.c, data->activate.here, TRUE);
1179     }
1180 }
1181
1182 void action_focus(union ActionData *data)
1183 {
1184     if (data->client.any.c) {
1185         /* similar to the openbox dock for dockapps, don't let user actions
1186            give focus to 3rd-party docks (panels) either (unless they ask for
1187            it themselves). */
1188         if (data->client.any.c->type != OB_CLIENT_TYPE_DOCK) {
1189             /* if using focus_delay, stop the timer now so that focus doesn't
1190                go moving on us */
1191             event_halt_focus_delay();
1192
1193             client_focus(data->client.any.c);
1194         }
1195     } else {
1196         /* focus action on something other than a client, make keybindings
1197            work for this openbox instance, but don't focus any specific client
1198         */
1199         focus_nothing();
1200     }
1201 }
1202
1203 void action_unfocus (union ActionData *data)
1204 {
1205     if (data->client.any.c == focus_client)
1206         focus_fallback(FALSE);
1207 }
1208
1209 void action_iconify(union ActionData *data)
1210 {
1211     client_action_start(data);
1212     client_iconify(data->client.any.c, TRUE, TRUE);
1213     client_action_end(data);
1214 }
1215
1216 void action_focus_order_to_bottom(union ActionData *data)
1217 {
1218     focus_order_to_bottom(data->client.any.c);
1219 }
1220
1221 void action_raiselower(union ActionData *data)
1222 {
1223     ObClient *c = data->client.any.c;
1224     GList *it;
1225     gboolean raise = FALSE;
1226
1227     for (it = stacking_list; it; it = g_list_next(it)) {
1228         if (WINDOW_IS_CLIENT(it->data)) {
1229             ObClient *cit = it->data;
1230
1231             if (cit == c) break;
1232             if (client_normal(cit) == client_normal(c) &&
1233                     cit->layer == c->layer &&
1234                     cit->frame->visible &&
1235                     !client_search_transient(c, cit))
1236             {
1237                 if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
1238                     raise = TRUE;
1239                     break;
1240                 }
1241             }
1242         }
1243     }
1244
1245     if (raise)
1246         action_raise(data);
1247     else
1248         action_lower(data);
1249 }
1250
1251 void action_raise(union ActionData *data)
1252 {
1253     client_action_start(data);
1254     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1255     client_action_end(data);
1256 }
1257
1258 void action_unshaderaise(union ActionData *data)
1259 {
1260     if (data->client.any.c->shaded)
1261         action_unshade(data);
1262     else
1263         action_raise(data);
1264 }
1265
1266 void action_shadelower(union ActionData *data)
1267 {
1268     if (data->client.any.c->shaded)
1269         action_lower(data);
1270     else
1271         action_shade(data);
1272 }
1273
1274 void action_lower(union ActionData *data)
1275 {
1276     client_action_start(data);
1277     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1278     client_action_end(data);
1279 }
1280
1281 void action_close(union ActionData *data)
1282 {
1283     client_close(data->client.any.c);
1284 }
1285
1286 void action_kill(union ActionData *data)
1287 {
1288     client_kill(data->client.any.c);
1289 }
1290
1291 void action_shade(union ActionData *data)
1292 {
1293     client_action_start(data);
1294     client_shade(data->client.any.c, TRUE);
1295     client_action_end(data);
1296 }
1297
1298 void action_unshade(union ActionData *data)
1299 {
1300     client_action_start(data);
1301     client_shade(data->client.any.c, FALSE);
1302     client_action_end(data);
1303 }
1304
1305 void action_toggle_shade(union ActionData *data)
1306 {
1307     client_action_start(data);
1308     client_shade(data->client.any.c, !data->client.any.c->shaded);
1309     client_action_end(data);
1310 }
1311
1312 void action_toggle_omnipresent(union ActionData *data)
1313
1314     client_set_desktop(data->client.any.c,
1315                        data->client.any.c->desktop == DESKTOP_ALL ?
1316                        screen_desktop : DESKTOP_ALL, FALSE);
1317 }
1318
1319 void action_move_relative_horz(union ActionData *data)
1320 {
1321     ObClient *c = data->relative.any.c;
1322     client_action_start(data);
1323     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1324     client_action_end(data);
1325 }
1326
1327 void action_move_relative_vert(union ActionData *data)
1328 {
1329     ObClient *c = data->relative.any.c;
1330     client_action_start(data);
1331     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1332     client_action_end(data);
1333 }
1334
1335 void action_move_to_center(union ActionData *data)
1336 {
1337     ObClient *c = data->client.any.c;
1338     Rect *area;
1339     area = screen_area_monitor(c->desktop, 0);
1340     client_action_start(data);
1341     client_move(c, area->width / 2 - c->area.width / 2,
1342                 area->height / 2 - c->area.height / 2);
1343     client_action_end(data);
1344 }
1345
1346 void action_resize_relative_horz(union ActionData *data)
1347 {
1348     ObClient *c = data->relative.any.c;
1349     client_action_start(data);
1350     client_resize(c,
1351                   c->area.width + data->relative.deltax * c->size_inc.width,
1352                   c->area.height);
1353     client_action_end(data);
1354 }
1355
1356 void action_resize_relative_vert(union ActionData *data)
1357 {
1358     ObClient *c = data->relative.any.c;
1359     if (!c->shaded) {
1360         client_action_start(data);
1361         client_resize(c, c->area.width, c->area.height +
1362                       data->relative.deltax * c->size_inc.height);
1363         client_action_end(data);
1364     }
1365 }
1366
1367 void action_move_relative(union ActionData *data)
1368 {
1369     ObClient *c = data->relative.any.c;
1370     client_action_start(data);
1371     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1372                 data->relative.deltay);
1373     client_action_end(data);
1374 }
1375
1376 void action_resize_relative(union ActionData *data)
1377 {
1378     ObClient *c = data->relative.any.c;
1379     gint x, y, ow, w, oh, h, lw, lh;
1380
1381     client_action_start(data);
1382
1383     x = c->area.x;
1384     y = c->area.y;
1385     ow = c->area.width;
1386     w = ow + data->relative.deltax * c->size_inc.width
1387         + data->relative.deltaxl * c->size_inc.width;
1388     oh = c->area.height;
1389     h = oh + data->relative.deltay * c->size_inc.height
1390         + data->relative.deltayu * c->size_inc.height;
1391     
1392     client_try_configure(c, OB_CORNER_TOPLEFT, &x, &y, &w, &h, &lw, &lh, TRUE);
1393     client_move_resize(c, x + (ow - w), y + (oh - h), w, h);
1394     client_action_end(data);
1395 }
1396
1397 void action_maximize_full(union ActionData *data)
1398 {
1399     client_action_start(data);
1400     client_maximize(data->client.any.c, TRUE, 0);
1401     client_action_end(data);
1402 }
1403
1404 void action_unmaximize_full(union ActionData *data)
1405 {
1406     client_action_start(data);
1407     client_maximize(data->client.any.c, FALSE, 0);
1408     client_action_end(data);
1409 }
1410
1411 void action_toggle_maximize_full(union ActionData *data)
1412 {
1413     client_action_start(data);
1414     client_maximize(data->client.any.c,
1415                     !(data->client.any.c->max_horz ||
1416                       data->client.any.c->max_vert),
1417                     0);
1418     client_action_end(data);
1419 }
1420
1421 void action_maximize_horz(union ActionData *data)
1422 {
1423     client_action_start(data);
1424     client_maximize(data->client.any.c, TRUE, 1);
1425     client_action_end(data);
1426 }
1427
1428 void action_unmaximize_horz(union ActionData *data)
1429 {
1430     client_action_start(data);
1431     client_maximize(data->client.any.c, FALSE, 1);
1432     client_action_end(data);
1433 }
1434
1435 void action_toggle_maximize_horz(union ActionData *data)
1436 {
1437     client_action_start(data);
1438     client_maximize(data->client.any.c,
1439                     !data->client.any.c->max_horz, 1);
1440     client_action_end(data);
1441 }
1442
1443 void action_maximize_vert(union ActionData *data)
1444 {
1445     client_action_start(data);
1446     client_maximize(data->client.any.c, TRUE, 2);
1447     client_action_end(data);
1448 }
1449
1450 void action_unmaximize_vert(union ActionData *data)
1451 {
1452     client_action_start(data);
1453     client_maximize(data->client.any.c, FALSE, 2);
1454     client_action_end(data);
1455 }
1456
1457 void action_toggle_maximize_vert(union ActionData *data)
1458 {
1459     client_action_start(data);
1460     client_maximize(data->client.any.c,
1461                     !data->client.any.c->max_vert, 2);
1462     client_action_end(data);
1463 }
1464
1465 void action_toggle_fullscreen(union ActionData *data)
1466 {
1467     client_action_start(data);
1468     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1469     client_action_end(data);
1470 }
1471
1472 void action_send_to_desktop(union ActionData *data)
1473 {
1474     ObClient *c = data->sendto.any.c;
1475
1476     if (!client_normal(c)) return;
1477
1478     if (data->sendto.desk < screen_num_desktops ||
1479         data->sendto.desk == DESKTOP_ALL) {
1480         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1481         if (data->sendto.follow)
1482             screen_set_desktop(data->sendto.desk);
1483     }
1484 }
1485
1486 void action_desktop(union ActionData *data)
1487 {
1488     static guint first = (unsigned) -1;
1489
1490     if (data->inter.any.interactive && first == (unsigned) -1)
1491         first = screen_desktop;
1492
1493     if (!data->inter.any.interactive ||
1494         (!data->inter.cancel && !data->inter.final))
1495     {
1496         if (data->desktop.desk < screen_num_desktops ||
1497             data->desktop.desk == DESKTOP_ALL)
1498         {
1499             screen_set_desktop(data->desktop.desk);
1500             if (data->inter.any.interactive)
1501                 screen_desktop_popup(data->desktop.desk, TRUE);
1502         }
1503     } else if (data->inter.cancel) {
1504         screen_set_desktop(first);
1505     }
1506
1507     if (!data->inter.any.interactive || data->inter.final) {
1508         screen_desktop_popup(0, FALSE);
1509         first = (unsigned) -1;
1510     }
1511 }
1512
1513 void action_desktop_dir(union ActionData *data)
1514 {
1515     guint d;
1516
1517     d = screen_cycle_desktop(data->desktopdir.dir,
1518                              data->desktopdir.wrap,
1519                              data->desktopdir.linear,
1520                              data->desktopdir.inter.any.interactive,
1521                              data->desktopdir.inter.final,
1522                              data->desktopdir.inter.cancel);
1523     if (!data->sendtodir.inter.any.interactive ||
1524         !data->sendtodir.inter.final ||
1525         data->sendtodir.inter.cancel)
1526     {
1527         screen_set_desktop(d);
1528     }
1529 }
1530
1531 void action_send_to_desktop_dir(union ActionData *data)
1532 {
1533     ObClient *c = data->sendtodir.inter.any.c;
1534     guint d;
1535
1536     if (!client_normal(c)) return;
1537
1538     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1539                              data->sendtodir.linear,
1540                              data->sendtodir.inter.any.interactive,
1541                              data->sendtodir.inter.final,
1542                              data->sendtodir.inter.cancel);
1543     if (!data->sendtodir.inter.any.interactive ||
1544         !data->sendtodir.inter.final ||
1545         data->sendtodir.inter.cancel)
1546     {
1547         client_set_desktop(c, d, data->sendtodir.follow);
1548         if (data->sendtodir.follow)
1549             screen_set_desktop(d);
1550     }
1551 }
1552
1553 void action_desktop_last(union ActionData *data)
1554 {
1555     screen_set_desktop(screen_last_desktop);
1556 }
1557
1558 void action_toggle_decorations(union ActionData *data)
1559 {
1560     ObClient *c = data->client.any.c;
1561
1562     client_action_start(data);
1563     client_set_undecorated(c, !c->undecorated);
1564     client_action_end(data);
1565 }
1566
1567 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch)
1568 {
1569     if (config_resize_four_corners) {
1570         if (x - cx > cw / 2) {
1571             if (y - cy > ch / 2)
1572                 return prop_atoms.net_wm_moveresize_size_bottomright;
1573             else
1574                 return prop_atoms.net_wm_moveresize_size_topright;
1575         } else {
1576             if (y - cy > ch / 2)
1577                 return prop_atoms.net_wm_moveresize_size_bottomleft;
1578             else
1579                 return prop_atoms.net_wm_moveresize_size_topleft;
1580         }
1581     } else {
1582         if (x - cx > cw * 2 / 3) {
1583             if (y - cy > ch * 2 / 3)
1584                 return prop_atoms.net_wm_moveresize_size_bottomright;
1585             else if (y - cy < ch / 3)
1586                 return prop_atoms.net_wm_moveresize_size_topright;
1587             else
1588                 return prop_atoms.net_wm_moveresize_size_right;
1589         } else if (x - cx < cw / 3) {
1590             if (y - cy > ch * 2 / 3)
1591                 return prop_atoms.net_wm_moveresize_size_bottomleft;
1592             else if (y - cy < ch / 3)
1593                 return prop_atoms.net_wm_moveresize_size_topleft;
1594             else
1595                 return prop_atoms.net_wm_moveresize_size_left;
1596         } else
1597             if (y - cy > ch * 2 / 3)
1598                 return prop_atoms.net_wm_moveresize_size_bottom;
1599             else if (y - cy < ch / 3)
1600                 return prop_atoms.net_wm_moveresize_size_top;
1601             else
1602                 return prop_atoms.net_wm_moveresize_move;
1603     }
1604 }
1605
1606 void action_moveresize(union ActionData *data)
1607 {
1608     ObClient *c = data->moveresize.any.c;
1609     guint32 corner;
1610
1611     if (!client_normal(c)) return;
1612
1613     if (data->moveresize.keyboard) {
1614         corner = (data->moveresize.move ?
1615                   prop_atoms.net_wm_moveresize_move_keyboard :
1616                   prop_atoms.net_wm_moveresize_size_keyboard);
1617     } else {
1618         corner = (data->moveresize.move ?
1619                   prop_atoms.net_wm_moveresize_move :
1620                   pick_corner(data->any.x, data->any.y,
1621                               c->frame->area.x, c->frame->area.y,
1622                               /* use the client size because the frame
1623                                  can be differently sized (shaded
1624                                  windows) and we want this based on the
1625                                  clients size */
1626                               c->area.width + c->frame->size.left +
1627                               c->frame->size.right,
1628                               c->area.height + c->frame->size.top +
1629                               c->frame->size.bottom));
1630     }
1631
1632     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1633 }
1634
1635 void action_reconfigure(union ActionData *data)
1636 {
1637     ob_reconfigure();
1638 }
1639
1640 void action_restart(union ActionData *data)
1641 {
1642     ob_restart_other(data->execute.path);
1643 }
1644
1645 void action_exit(union ActionData *data)
1646 {
1647     ob_exit(0);
1648 }
1649
1650 void action_showmenu(union ActionData *data)
1651 {
1652     if (data->showmenu.name) {
1653         menu_show(data->showmenu.name, data->any.x, data->any.y,
1654                   data->showmenu.any.c);
1655     }
1656 }
1657
1658 void action_cycle_windows(union ActionData *data)
1659 {
1660     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1661        on us */
1662     event_halt_focus_delay();
1663
1664     focus_cycle(data->cycle.forward,
1665                 data->cycle.dock_windows,
1666                 data->cycle.linear, data->any.interactive,
1667                 data->cycle.dialog,
1668                 data->cycle.inter.final, data->cycle.inter.cancel);
1669 }
1670
1671 void action_directional_focus(union ActionData *data)
1672 {
1673     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1674        on us */
1675     event_halt_focus_delay();
1676
1677     focus_directional_cycle(data->interdiraction.direction,
1678                             data->interdiraction.dock_windows,
1679                             data->any.interactive,
1680                             data->interdiraction.dialog,
1681                             data->interdiraction.inter.final,
1682                             data->interdiraction.inter.cancel);
1683 }
1684
1685 void action_movetoedge(union ActionData *data)
1686 {
1687     gint x, y;
1688     ObClient *c = data->diraction.any.c;
1689
1690     x = c->frame->area.x;
1691     y = c->frame->area.y;
1692     
1693     switch(data->diraction.direction) {
1694     case OB_DIRECTION_NORTH:
1695         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1696                                            data->diraction.hang)
1697             - (data->diraction.hang ? c->frame->area.height : 0);
1698         break;
1699     case OB_DIRECTION_WEST:
1700         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1701                                            data->diraction.hang)
1702             - (data->diraction.hang ? c->frame->area.width : 0);
1703         break;
1704     case OB_DIRECTION_SOUTH:
1705         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1706                                            data->diraction.hang)
1707             - (data->diraction.hang ? 0 : c->frame->area.height);
1708         break;
1709     case OB_DIRECTION_EAST:
1710         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1711                                            data->diraction.hang)
1712             - (data->diraction.hang ? 0 : c->frame->area.width);
1713         break;
1714     default:
1715         g_assert_not_reached();
1716     }
1717     frame_frame_gravity(c->frame, &x, &y);
1718     client_action_start(data);
1719     client_move(c, x, y);
1720     client_action_end(data);
1721 }
1722
1723 void action_growtoedge(union ActionData *data)
1724 {
1725     gint x, y, width, height, dest;
1726     ObClient *c = data->diraction.any.c;
1727     Rect *a;
1728
1729     //FIXME growtoedge resizes shaded windows to 0 height
1730     if (c->shaded)
1731         return;
1732
1733     a = screen_area(c->desktop);
1734     x = c->frame->area.x;
1735     y = c->frame->area.y;
1736     width = c->frame->area.width;
1737     height = c->frame->area.height;
1738
1739     switch(data->diraction.direction) {
1740     case OB_DIRECTION_NORTH:
1741         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1742         if (a->y == y)
1743             height = c->frame->area.height / 2;
1744         else {
1745             height = c->frame->area.y + c->frame->area.height - dest;
1746             y = dest;
1747         }
1748         break;
1749     case OB_DIRECTION_WEST:
1750         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1751         if (a->x == x)
1752             width = c->frame->area.width / 2;
1753         else {
1754             width = c->frame->area.x + c->frame->area.width - dest;
1755             x = dest;
1756         }
1757         break;
1758     case OB_DIRECTION_SOUTH:
1759         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1760         if (a->y + a->height == y + c->frame->area.height) {
1761             height = c->frame->area.height / 2;
1762             y = a->y + a->height - height;
1763         } else
1764             height = dest - c->frame->area.y;
1765         y += (height - c->frame->area.height) % c->size_inc.height;
1766         height -= (height - c->frame->area.height) % c->size_inc.height;
1767         break;
1768     case OB_DIRECTION_EAST:
1769         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1770         if (a->x + a->width == x + c->frame->area.width) {
1771             width = c->frame->area.width / 2;
1772             x = a->x + a->width - width;
1773         } else
1774             width = dest - c->frame->area.x;
1775         x += (width - c->frame->area.width) % c->size_inc.width;
1776         width -= (width - c->frame->area.width) % c->size_inc.width;
1777         break;
1778     default:
1779         g_assert_not_reached();
1780     }
1781     frame_frame_gravity(c->frame, &x, &y);
1782     width -= c->frame->size.left + c->frame->size.right;
1783     height -= c->frame->size.top + c->frame->size.bottom;
1784     client_action_start(data);
1785     client_move_resize(c, x, y, width, height);
1786     client_action_end(data);
1787 }
1788
1789 void action_send_to_layer(union ActionData *data)
1790 {
1791     client_set_layer(data->layer.any.c, data->layer.layer);
1792 }
1793
1794 void action_toggle_layer(union ActionData *data)
1795 {
1796     ObClient *c = data->layer.any.c;
1797
1798     client_action_start(data);
1799     if (data->layer.layer < 0)
1800         client_set_layer(c, c->below ? 0 : -1);
1801     else if (data->layer.layer > 0)
1802         client_set_layer(c, c->above ? 0 : 1);
1803     client_action_end(data);
1804 }
1805
1806 void action_toggle_dockautohide(union ActionData *data)
1807 {
1808     config_dock_hide = !config_dock_hide;
1809     dock_configure();
1810 }
1811
1812 void action_toggle_show_desktop(union ActionData *data)
1813 {
1814     screen_show_desktop(!screen_showing_desktop);
1815 }
1816
1817 void action_show_desktop(union ActionData *data)
1818 {
1819     screen_show_desktop(TRUE);
1820 }
1821
1822 void action_unshow_desktop(union ActionData *data)
1823 {
1824     screen_show_desktop(FALSE);
1825 }