]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/action.c
when ending actions which can move windows, and we were using the mouse, instead...
[mikachu/openbox.git] / openbox / action.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    action.c for the Openbox window manager
4    Copyright (c) 2003        Ben Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "debug.h"
20 #include "client.h"
21 #include "focus.h"
22 #include "moveresize.h"
23 #include "menu.h"
24 #include "prop.h"
25 #include "stacking.h"
26 #include "screen.h"
27 #include "action.h"
28 #include "openbox.h"
29 #include "grab.h"
30 #include "keyboard.h"
31 #include "event.h"
32 #include "config.h"
33
34 #include <glib.h>
35
36 inline void client_action_start(union ActionData *data)
37 {
38     if (config_focus_follow)
39         if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button)
40             grab_pointer(TRUE, OB_CURSOR_NONE);
41 }
42
43 inline void client_action_end(union ActionData *data)
44 {
45     if (config_focus_follow)
46         if (data->any.context != OB_FRAME_CONTEXT_CLIENT) {
47             if (!data->any.button) {
48                 grab_pointer(FALSE, OB_CURSOR_NONE);
49             } else {
50                 ObClient *c;
51
52                 if ((c = client_under_pointer()))
53                     event_enter_client(c);
54             }
55         }
56 }
57
58 typedef struct ActionString {
59     const gchar *name;
60     void (*func)(union ActionData *);
61     void (*setup)(ObAction **, ObUserAction uact);
62 } ActionString;
63
64 static ObAction *action_new(void (*func)(union ActionData *data),
65                             ObUserAction uact)
66 {
67     ObAction *a = g_new0(ObAction, 1);
68     a->func = func;
69
70     return a;
71 }
72
73 void action_free(ObAction *a)
74 {
75     if (a == NULL) return;
76
77     /* deal with pointers */
78     if (a->func == action_execute || a->func == action_restart)
79         g_free(a->data.execute.path);
80     else if (a->func == action_showmenu)
81         g_free(a->data.showmenu.name);
82
83     g_free(a);
84 }
85
86 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
87
88     (*a)->data.interdiraction.inter.any.interactive = TRUE;
89     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
90 }
91
92 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
93 {
94     (*a)->data.interdiraction.inter.any.interactive = TRUE;
95     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
96 }
97
98 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
99 {
100     (*a)->data.interdiraction.inter.any.interactive = TRUE;
101     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
102 }
103
104 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
105 {
106     (*a)->data.interdiraction.inter.any.interactive = TRUE;
107     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
108 }
109
110 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
111 {
112     (*a)->data.interdiraction.inter.any.interactive = TRUE;
113     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
114 }
115
116 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
117 {
118     (*a)->data.interdiraction.inter.any.interactive = TRUE;
119     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
120 }
121
122 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
123 {
124     (*a)->data.interdiraction.inter.any.interactive = TRUE;
125     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
126 }
127
128 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
129 {
130     (*a)->data.interdiraction.inter.any.interactive = TRUE;
131     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
132 }
133
134 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
135 {
136     (*a)->data.sendto.follow = TRUE;
137 }
138
139 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
140 {
141     (*a)->data.sendtodir.inter.any.interactive = TRUE;
142     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
143     (*a)->data.sendtodir.linear = TRUE;
144     (*a)->data.sendtodir.wrap = TRUE;
145     (*a)->data.sendtodir.follow = TRUE;
146 }
147
148 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
149 {
150     (*a)->data.sendtodir.inter.any.interactive = TRUE;
151     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
152     (*a)->data.sendtodir.linear = TRUE;
153     (*a)->data.sendtodir.wrap = TRUE;
154     (*a)->data.sendtodir.follow = TRUE;
155 }
156
157 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
158 {
159     (*a)->data.sendtodir.inter.any.interactive = TRUE;
160     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
161     (*a)->data.sendtodir.linear = FALSE;
162     (*a)->data.sendtodir.wrap = TRUE;
163     (*a)->data.sendtodir.follow = TRUE;
164 }
165
166 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
167 {
168     (*a)->data.sendtodir.inter.any.interactive = TRUE;
169     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
170     (*a)->data.sendtodir.linear = FALSE;
171     (*a)->data.sendtodir.wrap = TRUE;
172     (*a)->data.sendtodir.follow = TRUE;
173 }
174
175 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
176 {
177     (*a)->data.sendtodir.inter.any.interactive = TRUE;
178     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
179     (*a)->data.sendtodir.linear = FALSE;
180     (*a)->data.sendtodir.wrap = TRUE;
181     (*a)->data.sendtodir.follow = TRUE;
182 }
183
184 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
185 {
186     (*a)->data.sendtodir.inter.any.interactive = TRUE;
187     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
188     (*a)->data.sendtodir.linear = FALSE;
189     (*a)->data.sendtodir.wrap = TRUE;
190     (*a)->data.sendtodir.follow = TRUE;
191 }
192
193 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
194 {
195     (*a)->data.desktopdir.inter.any.interactive = TRUE;
196     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
197     (*a)->data.desktopdir.linear = TRUE;
198     (*a)->data.desktopdir.wrap = TRUE;
199 }
200
201 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
202 {
203     (*a)->data.desktopdir.inter.any.interactive = TRUE;
204     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
205     (*a)->data.desktopdir.linear = TRUE;
206     (*a)->data.desktopdir.wrap = TRUE;
207 }
208
209 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
210 {
211     (*a)->data.desktopdir.inter.any.interactive = TRUE;
212     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
213     (*a)->data.desktopdir.linear = FALSE;
214     (*a)->data.desktopdir.wrap = TRUE;
215 }
216
217 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
218 {
219     (*a)->data.desktopdir.inter.any.interactive = TRUE;
220     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
221     (*a)->data.desktopdir.linear = FALSE;
222     (*a)->data.desktopdir.wrap = TRUE;
223 }
224
225 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
226 {
227     (*a)->data.desktopdir.inter.any.interactive = TRUE;
228     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
229     (*a)->data.desktopdir.linear = FALSE;
230     (*a)->data.desktopdir.wrap = TRUE;
231 }
232
233 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
234 {
235     (*a)->data.desktopdir.inter.any.interactive = TRUE;
236     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
237     (*a)->data.desktopdir.linear = FALSE;
238     (*a)->data.desktopdir.wrap = TRUE;
239 }
240
241 void setup_action_cycle_windows_next(ObAction **a, ObUserAction uact)
242 {
243     (*a)->data.cycle.inter.any.interactive = TRUE;
244     (*a)->data.cycle.linear = FALSE;
245     (*a)->data.cycle.forward = TRUE;
246 }
247
248 void setup_action_cycle_windows_previous(ObAction **a, ObUserAction uact)
249 {
250     (*a)->data.cycle.inter.any.interactive = TRUE;
251     (*a)->data.cycle.linear = FALSE;
252     (*a)->data.cycle.forward = FALSE;
253 }
254
255 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
256 {
257     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
258 }
259
260 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
261 {
262     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
263 }
264
265 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
266 {
267     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
268 }
269
270 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
271 {
272     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
273 }
274
275 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
276 {
277     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
278 }
279
280 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
281 {
282     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
283 }
284
285 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
286 {
287     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
288 }
289
290 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
291 {
292     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
293 }
294
295 void setup_action_top_layer(ObAction **a, ObUserAction uact)
296 {
297     (*a)->data.layer.layer = 1;
298 }
299
300 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
301 {
302     (*a)->data.layer.layer = 0;
303 }
304
305 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
306 {
307     (*a)->data.layer.layer = -1;
308 }
309
310 void setup_action_move(ObAction **a, ObUserAction uact)
311 {
312     (*a)->data.moveresize.move = TRUE;
313     (*a)->data.moveresize.keyboard =
314         (uact == OB_USER_ACTION_KEYBOARD_KEY ||
315          uact == OB_USER_ACTION_MENU_SELECTION);
316 }
317
318 void setup_action_resize(ObAction **a, ObUserAction uact)
319 {
320     (*a)->data.moveresize.move = FALSE;
321     (*a)->data.moveresize.keyboard =
322         (uact == OB_USER_ACTION_KEYBOARD_KEY ||
323          uact == OB_USER_ACTION_MENU_SELECTION);
324 }
325
326 void setup_action_showmenu(ObAction **a, ObUserAction uact)
327 {
328     /* you cannot call ShowMenu from inside a menu, cuz the menu code makes
329        assumptions that there is only one menu (and submenus) open at
330        a time! */
331     if (uact == OB_USER_ACTION_MENU_SELECTION) {
332         action_free(*a);
333         a = NULL;
334     }
335 }
336
337 ActionString actionstrings[] =
338 {
339     {
340         "execute", 
341         action_execute, 
342         NULL
343     },
344     {
345         "directionalfocusnorth", 
346         action_directional_focus, 
347         setup_action_directional_focus_north
348     },
349     {
350         "directionalfocuseast", 
351         action_directional_focus, 
352         setup_action_directional_focus_east
353     },
354     {
355         "directionalfocussouth", 
356         action_directional_focus, 
357         setup_action_directional_focus_south
358     },
359     {
360         "directionalfocuswest",
361         action_directional_focus,
362         setup_action_directional_focus_west
363     },
364     {
365         "directionalfocusnortheast",
366         action_directional_focus,
367         setup_action_directional_focus_northeast
368     },
369     {
370         "directionalfocussoutheast",
371         action_directional_focus,
372         setup_action_directional_focus_southeast
373     },
374     {
375         "directionalfocussouthwest",
376         action_directional_focus,
377         setup_action_directional_focus_southwest
378     },
379     {
380         "directionalfocusnorthwest",
381         action_directional_focus,
382         setup_action_directional_focus_northwest
383     },
384     {
385         "activate",
386         action_activate,
387         NULL
388     },
389     {
390         "focus",
391         action_focus,
392         NULL
393     },
394     {
395         "unfocus",
396         action_unfocus,
397         NULL
398     },
399     {
400         "iconify",
401         action_iconify,
402         NULL
403     },
404     {
405         "raiselower",
406         action_raiselower,
407         NULL
408     },
409     {
410         "raise",
411         action_raise,
412         NULL
413     },
414     {
415         "lower",
416         action_lower,
417         NULL
418     },
419     {
420         "close",
421         action_close,
422         NULL
423     },
424     {
425         "kill",
426         action_kill,
427         NULL
428     },
429     {
430         "shadelower",
431         action_shadelower,
432         NULL
433     },
434     {
435         "unshaderaise",
436         action_unshaderaise,
437         NULL
438     },
439     {
440         "shade",
441         action_shade,
442         NULL
443     },
444     {
445         "unshade",
446         action_unshade,
447         NULL
448     },
449     {
450         "toggleshade",
451         action_toggle_shade,
452         NULL
453     },
454     {
455         "toggleomnipresent",
456         action_toggle_omnipresent,
457         NULL
458     },
459     {
460         "moverelativehorz",
461         action_move_relative_horz,
462         NULL
463     },
464     {
465         "moverelativevert",
466         action_move_relative_vert,
467         NULL
468     },
469     {
470         "resizerelativehorz",
471         action_resize_relative_horz,
472         NULL
473     },
474     {
475         "resizerelativevert",
476         action_resize_relative_vert,
477         NULL
478     },
479     {
480         "maximizefull",
481         action_maximize_full,
482         NULL
483     },
484     {
485         "unmaximizefull",
486         action_unmaximize_full,
487         NULL
488     },
489     {
490         "togglemaximizefull",
491         action_toggle_maximize_full,
492         NULL
493     },
494     {
495         "maximizehorz",
496         action_maximize_horz,
497         NULL
498     },
499     {
500         "unmaximizehorz",
501         action_unmaximize_horz,
502         NULL
503     },
504     {
505         "togglemaximizehorz",
506         action_toggle_maximize_horz,
507         NULL
508     },
509     {
510         "maximizevert",
511         action_maximize_vert,
512         NULL
513     },
514     {
515         "unmaximizevert",
516         action_unmaximize_vert,
517         NULL
518     },
519     {
520         "togglemaximizevert",
521         action_toggle_maximize_vert,
522         NULL
523     },
524     {
525         "sendtodesktop",
526         action_send_to_desktop,
527         setup_action_send_to_desktop
528     },
529     {
530         "sendtodesktopnext",
531         action_send_to_desktop_dir,
532         setup_action_send_to_desktop_next
533     },
534     {
535         "sendtodesktopprevious",
536         action_send_to_desktop_dir,
537         setup_action_send_to_desktop_prev
538     },
539     {
540         "sendtodesktopright",
541         action_send_to_desktop_dir,
542         setup_action_send_to_desktop_right
543     },
544     {
545         "sendtodesktopleft",
546         action_send_to_desktop_dir,
547         setup_action_send_to_desktop_left
548     },
549     {
550         "sendtodesktopup",
551         action_send_to_desktop_dir,
552         setup_action_send_to_desktop_up
553     },
554     {
555         "sendtodesktopdown",
556         action_send_to_desktop_dir,
557         setup_action_send_to_desktop_down
558     },
559     {
560         "desktop",
561         action_desktop,
562         NULL
563     },
564     {
565         "desktopnext",
566         action_desktop_dir,
567         setup_action_desktop_next
568     },
569     {
570         "desktopprevious",
571         action_desktop_dir,
572         setup_action_desktop_prev
573     },
574     {
575         "desktopright",
576         action_desktop_dir,
577         setup_action_desktop_right
578     },
579     {
580         "desktopleft",
581         action_desktop_dir,
582         setup_action_desktop_left
583     },
584     {
585         "desktopup",
586         action_desktop_dir,
587         setup_action_desktop_up
588     },
589     {
590         "desktopdown",
591         action_desktop_dir,
592         setup_action_desktop_down
593     },
594     {
595         "toggledecorations",
596         action_toggle_decorations,
597         NULL
598     },
599     {
600         "move",
601         action_moveresize,
602         setup_action_move
603     },
604     {
605         "resize",
606         action_moveresize,
607         setup_action_resize
608     },
609     {
610         "toggleshowdesktop",
611         action_toggle_show_desktop,
612         NULL
613     },
614     {
615         "showdesktop",
616         action_show_desktop,
617         NULL
618     },
619     {
620         "unshowdesktop",
621         action_unshow_desktop,
622         NULL
623     },
624     {
625         "desktoplast",
626         action_desktop_last,
627         NULL
628     },
629     {
630         "reconfigure",
631         action_reconfigure,
632         NULL
633     },
634     {
635         "restart",
636         action_restart,
637         NULL
638     },
639     {
640         "exit",
641         action_exit,
642         NULL
643     },
644     {
645         "showmenu",
646         action_showmenu,
647         setup_action_showmenu
648     },
649     {
650         "sendtotoplayer",
651         action_send_to_layer,
652         setup_action_top_layer
653     },
654     {
655         "togglealwaysontop",
656         action_toggle_layer,
657         setup_action_top_layer
658     },
659     {
660         "sendtonormallayer",
661         action_send_to_layer,
662         setup_action_normal_layer
663     },
664     {
665         "sendtobottomlayer",
666         action_send_to_layer,
667         setup_action_bottom_layer
668     },
669     {
670         "togglealwaysonbottom",
671         action_toggle_layer,
672         setup_action_bottom_layer
673     },
674     {
675         "nextwindow",
676         action_cycle_windows,
677         setup_action_cycle_windows_next
678     },
679     {
680         "previouswindow",
681         action_cycle_windows,
682         setup_action_cycle_windows_previous
683     },
684     {
685         "movetoedgenorth",
686         action_movetoedge,
687         setup_action_movetoedge_north
688     },
689     {
690         "movetoedgesouth",
691         action_movetoedge,
692         setup_action_movetoedge_south
693     },
694     {
695         "movetoedgewest",
696         action_movetoedge,
697         setup_action_movetoedge_west
698     },
699     {
700         "movetoedgeeast",
701         action_movetoedge,
702         setup_action_movetoedge_east
703     },
704     {
705         "growtoedgenorth",
706         action_growtoedge,
707         setup_action_growtoedge_north
708     },
709     {
710         "growtoedgesouth",
711         action_growtoedge,
712         setup_action_growtoedge_south
713     },
714     {
715         "growtoedgewest",
716         action_growtoedge,
717         setup_action_growtoedge_west
718     },
719     {
720         "growtoedgeeast",
721         action_growtoedge,
722         setup_action_growtoedge_east
723     },
724     {
725         NULL,
726         NULL,
727         NULL
728     }
729 };
730
731 ObAction *action_from_string(const gchar *name, ObUserAction uact)
732 {
733     ObAction *a = NULL;
734     gboolean exist = FALSE;
735     int i;
736
737     for (i = 0; actionstrings[i].name; i++)
738         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
739             exist = TRUE;
740             a = action_new(actionstrings[i].func, uact);
741             if (actionstrings[i].setup)
742                 actionstrings[i].setup(&a, uact);
743             /* only key bindings can be interactive. thus saith the xor. */
744             if (uact != OB_USER_ACTION_KEYBOARD_KEY)
745                 a->data.any.interactive = FALSE;
746             break;
747         }
748     if (!exist)
749         g_warning("Invalid action '%s' requested. No such action exists.",
750                   name);
751     if (!a)
752         g_warning("Invalid use of action '%s'. Action will be ignored.", name);
753     return a;
754 }
755
756 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
757                        ObUserAction uact)
758 {
759     char *actname;
760     ObAction *act = NULL;
761     xmlNodePtr n;
762
763     if (parse_attr_string("name", node, &actname)) {
764         if ((act = action_from_string(actname, uact))) {
765             if (act->func == action_execute || act->func == action_restart) {
766                 if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
767                     gchar *s = parse_string(doc, n);
768                     act->data.execute.path = parse_expand_tilde(s);
769                     g_free(s);
770                 }
771             } else if (act->func == action_showmenu) {
772                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
773                     act->data.showmenu.name = parse_string(doc, n);
774             } else if (act->func == action_move_relative_horz ||
775                        act->func == action_move_relative_vert ||
776                        act->func == action_resize_relative_horz ||
777                        act->func == action_resize_relative_vert) {
778                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
779                     act->data.relative.delta = parse_int(doc, n);
780             } else if (act->func == action_desktop) {
781                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
782                     act->data.desktop.desk = parse_int(doc, n);
783                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
784            } else if (act->func == action_send_to_desktop) {
785                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
786                     act->data.sendto.desk = parse_int(doc, n);
787                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
788                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
789                     act->data.sendto.follow = parse_bool(doc, n);
790             } else if (act->func == action_desktop_dir) {
791                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
792                     act->data.desktopdir.wrap = parse_bool(doc, n); 
793             } else if (act->func == action_send_to_desktop_dir) {
794                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
795                     act->data.sendtodir.wrap = parse_bool(doc, n);
796                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
797                     act->data.sendtodir.follow = parse_bool(doc, n);
798             } else if (act->func == action_activate) {
799                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
800                     act->data.activate.here = parse_bool(doc, n);
801             } else if (act->func == action_cycle_windows) {
802                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
803                     act->data.cycle.linear = parse_bool(doc, n);
804             }
805         }
806         g_free(actname);
807     }
808     return act;
809 }
810
811 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
812                      guint state, guint button, gint x, gint y,
813                      gboolean cancel, gboolean done)
814 {
815     GSList *it;
816     ObAction *a;
817     gboolean inter = FALSE;
818
819     if (!acts)
820         return;
821
822     if (x < 0 && y < 0)
823         screen_pointer_pos(&x, &y);
824
825     if (grab_on_keyboard())
826         inter = TRUE;
827     else
828         for (it = acts; it; it = g_slist_next(it)) {
829             a = it->data;
830             if (a->data.any.interactive) {
831                 inter = TRUE;
832                 break;
833             }
834         }
835
836     if (!inter) {
837         /* sometimes when we execute another app as an action,
838            it won't work right unless we XUngrabKeyboard first,
839            even though we grabbed the key/button Asychronously.
840            e.g. "gnome-panel-control --main-menu" */
841         XUngrabKeyboard(ob_display, event_lasttime);
842     }
843
844     for (it = acts; it; it = g_slist_next(it)) {
845         a = it->data;
846
847         a->data.any.c = c;
848         a->data.any.context = context;
849         a->data.any.x = x;
850         a->data.any.y = y;
851
852         a->data.any.button = button;
853
854         if (a->data.any.interactive) {
855             a->data.inter.cancel = cancel;
856             a->data.inter.final = done;
857             if (!(cancel || done))
858                 keyboard_interactive_grab(state, c, a);
859         }
860
861         a->func(&a->data);
862     }
863 }
864
865 void action_execute(union ActionData *data)
866 {
867     GError *e = NULL;
868     char *cmd;
869     if (data->execute.path) {
870         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
871         if (cmd) {
872             if (!g_spawn_command_line_async(cmd, &e)) {
873                 g_warning("failed to execute '%s': %s",
874                           cmd, e->message);
875             }
876             g_free(cmd);
877         } else {
878             g_warning("failed to convert '%s' from utf8", data->execute.path);
879         }
880     }
881 }
882
883 void action_activate(union ActionData *data)
884 {
885     if (data->activate.any.c)
886         client_activate(data->activate.any.c, data->activate.here);
887 }
888
889 void action_focus(union ActionData *data)
890 {
891     if (data->client.any.c)
892         client_focus(data->client.any.c);
893 }
894
895 void action_unfocus (union ActionData *data)
896 {
897     if (data->client.any.c)
898         client_unfocus(data->client.any.c);
899 }
900
901 void action_iconify(union ActionData *data)
902 {
903     if (data->client.any.c)
904         client_iconify(data->client.any.c, TRUE, TRUE);
905 }
906
907 void action_raiselower(union ActionData *data)
908 {
909     ObClient *c = data->client.any.c;
910     GList *it;
911     gboolean raise = FALSE;
912
913     if (!c) return;
914
915     for (it = stacking_list; it; it = g_list_next(it)) {
916         ObClient *cit = it->data;
917
918         if (cit == c) break;
919         if (client_normal(cit) == client_normal(c) &&
920             cit->layer == c->layer &&
921             cit->frame->visible)
922         {
923             if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
924                 raise = TRUE;
925                 break;
926             }
927         }
928     }
929
930     if (raise) {
931         client_action_start(data);
932         stacking_raise(CLIENT_AS_WINDOW(c));
933         client_action_end(data);
934     } else {
935         client_action_start(data);
936         stacking_lower(CLIENT_AS_WINDOW(c));
937         client_action_end(data);
938     }
939 }
940
941 void action_raise(union ActionData *data)
942 {
943     if (data->client.any.c) {
944         client_action_start(data);
945         stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
946         client_action_end(data);
947     }
948 }
949
950 void action_unshaderaise(union ActionData *data)
951 {
952     if (data->client.any.c) {
953         if (data->client.any.c->shaded) {
954             client_action_start(data);
955             client_shade(data->client.any.c, FALSE);
956             client_action_end(data);
957         } else {
958             client_action_start(data);
959             stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
960             client_action_end(data);
961         }
962     }
963 }
964
965 void action_shadelower(union ActionData *data)
966 {
967     if (data->client.any.c) {
968         if (data->client.any.c->shaded)
969             stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
970         else {
971             client_action_start(data);
972             client_shade(data->client.any.c, TRUE);
973             client_action_end(data);
974         }
975     }
976 }
977
978 void action_lower(union ActionData *data)
979 {
980     if (data->client.any.c) {
981         client_action_start(data);
982         stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
983         client_action_end(data);
984     }
985 }
986
987 void action_close(union ActionData *data)
988 {
989     if (data->client.any.c)
990         client_close(data->client.any.c);
991 }
992
993 void action_kill(union ActionData *data)
994 {
995     if (data->client.any.c)
996         client_kill(data->client.any.c);
997 }
998
999 void action_shade(union ActionData *data)
1000 {
1001     if (data->client.any.c) { 
1002         client_action_start(data);
1003         client_shade(data->client.any.c, TRUE);
1004         client_action_end(data);
1005     }
1006 }
1007
1008 void action_unshade(union ActionData *data)
1009 {
1010     if (data->client.any.c) {
1011         client_action_start(data);
1012         client_shade(data->client.any.c, FALSE);
1013         client_action_end(data);
1014     }
1015 }
1016
1017 void action_toggle_shade(union ActionData *data)
1018 {
1019     if (data->client.any.c) {
1020         client_action_start(data);
1021         client_shade(data->client.any.c, !data->client.any.c->shaded);
1022         client_action_end(data);
1023     }
1024 }
1025
1026 void action_toggle_omnipresent(union ActionData *data)
1027
1028     if (data->client.any.c)
1029         client_set_desktop(data->client.any.c,
1030                            data->client.any.c->desktop == DESKTOP_ALL ?
1031                            screen_desktop : DESKTOP_ALL, FALSE);
1032 }
1033
1034 void action_move_relative_horz(union ActionData *data)
1035 {
1036     ObClient *c = data->relative.any.c;
1037     if (c) {
1038         client_action_start(data);
1039         client_move(c, c->area.x + data->relative.delta, c->area.y);
1040         client_action_end(data);
1041     }
1042 }
1043
1044 void action_move_relative_vert(union ActionData *data)
1045 {
1046     ObClient *c = data->relative.any.c;
1047     if (c) {
1048         client_action_start(data);
1049         client_move(c, c->area.x, c->area.y + data->relative.delta);
1050         client_action_end(data);
1051     }
1052 }
1053
1054 void action_resize_relative_horz(union ActionData *data)
1055 {
1056     ObClient *c = data->relative.any.c;
1057     if (c) {
1058         client_action_start(data);
1059         client_resize(c,
1060                       c->area.width + data->relative.delta * c->size_inc.width,
1061                       c->area.height);
1062         client_action_end(data);
1063     }
1064 }
1065
1066 void action_resize_relative_vert(union ActionData *data)
1067 {
1068     ObClient *c = data->relative.any.c;
1069     if (c && !c->shaded) {
1070         client_action_start(data);
1071         client_resize(c, c->area.width, c->area.height +
1072                       data->relative.delta * c->size_inc.height);
1073         client_action_end(data);
1074     }
1075 }
1076
1077 void action_maximize_full(union ActionData *data)
1078 {
1079     if (data->client.any.c) {
1080         client_action_start(data);
1081         client_maximize(data->client.any.c, TRUE, 0, TRUE);
1082         client_action_end(data);
1083     }
1084 }
1085
1086 void action_unmaximize_full(union ActionData *data)
1087 {
1088     if (data->client.any.c) {
1089         client_action_start(data);
1090         client_maximize(data->client.any.c, FALSE, 0, TRUE);
1091         client_action_end(data);
1092     }
1093 }
1094
1095 void action_toggle_maximize_full(union ActionData *data)
1096 {
1097     if (data->client.any.c) {
1098         client_action_start(data);
1099         client_maximize(data->client.any.c,
1100                         !(data->client.any.c->max_horz ||
1101                           data->client.any.c->max_vert),
1102                         0, TRUE);
1103         client_action_end(data);
1104     }
1105 }
1106
1107 void action_maximize_horz(union ActionData *data)
1108 {
1109     if (data->client.any.c) {
1110         client_action_start(data);
1111         client_maximize(data->client.any.c, TRUE, 1, TRUE);
1112         client_action_end(data);
1113     }
1114 }
1115
1116 void action_unmaximize_horz(union ActionData *data)
1117 {
1118     if (data->client.any.c) {
1119         client_action_start(data);
1120         client_maximize(data->client.any.c, FALSE, 1, TRUE);
1121         client_action_end(data);
1122     }
1123 }
1124
1125 void action_toggle_maximize_horz(union ActionData *data)
1126 {
1127     if (data->client.any.c) {
1128         client_action_start(data);
1129         client_maximize(data->client.any.c,
1130                         !data->client.any.c->max_horz, 1, TRUE);
1131         client_action_end(data);
1132     }
1133 }
1134
1135 void action_maximize_vert(union ActionData *data)
1136 {
1137     if (data->client.any.c) {
1138         client_action_start(data);
1139         client_maximize(data->client.any.c, TRUE, 2, TRUE);
1140         client_action_end(data);
1141     }
1142 }
1143
1144 void action_unmaximize_vert(union ActionData *data)
1145 {
1146     if (data->client.any.c) {
1147         client_action_start(data);
1148         client_maximize(data->client.any.c, FALSE, 2, TRUE);
1149         client_action_end(data);
1150     }
1151 }
1152
1153 void action_toggle_maximize_vert(union ActionData *data)
1154 {
1155     if (data->client.any.c) {
1156         client_action_start(data);
1157         client_maximize(data->client.any.c,
1158                         !data->client.any.c->max_vert, 2, TRUE);
1159         client_action_end(data);
1160     }
1161 }
1162
1163 void action_send_to_desktop(union ActionData *data)
1164 {
1165     ObClient *c = data->sendto.any.c;
1166
1167     if (!c || !client_normal(c)) return;
1168
1169     if (data->sendto.desk < screen_num_desktops ||
1170         data->sendto.desk == DESKTOP_ALL) {
1171         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1172         if (data->sendto.follow)
1173             screen_set_desktop(data->sendto.desk);
1174     }
1175 }
1176
1177 void action_desktop(union ActionData *data)
1178 {
1179     if (data->desktop.desk < screen_num_desktops ||
1180         data->desktop.desk == DESKTOP_ALL)
1181         screen_set_desktop(data->desktop.desk);
1182 }
1183
1184 void action_desktop_dir(union ActionData *data)
1185 {
1186     guint d;
1187
1188     d = screen_cycle_desktop(data->desktopdir.dir,
1189                              data->desktopdir.wrap,
1190                              data->sendtodir.linear,
1191                              data->desktopdir.inter.any.interactive,
1192                              data->desktopdir.inter.final,
1193                              data->desktopdir.inter.cancel);
1194     screen_set_desktop(d);
1195 }
1196
1197 void action_send_to_desktop_dir(union ActionData *data)
1198 {
1199     ObClient *c = data->sendtodir.inter.any.c;
1200     guint d;
1201
1202     if (!c || !client_normal(c)) return;
1203
1204     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1205                              data->sendtodir.linear,
1206                              data->sendtodir.inter.any.interactive,
1207                              data->sendtodir.inter.final,
1208                              data->sendtodir.inter.cancel);
1209     client_set_desktop(c, d, data->sendtodir.follow);
1210     if (data->sendtodir.follow)
1211         screen_set_desktop(d);
1212 }
1213
1214 void action_desktop_last(union ActionData *data)
1215 {
1216     screen_set_desktop(screen_last_desktop);
1217 }
1218
1219 void action_toggle_decorations(union ActionData *data)
1220 {
1221     ObClient *c = data->client.any.c;
1222
1223     if (c) {
1224         client_action_start(data);
1225         c->decorate = !c->decorate;
1226         client_setup_decor_and_functions(c);
1227         client_action_end(data);
1228     }
1229 }
1230
1231 static guint32 pick_corner(int x, int y, int cx, int cy, int cw, int ch)
1232 {
1233     if (x - cx > cw / 2) {
1234         if (y - cy > ch / 2)
1235             return prop_atoms.net_wm_moveresize_size_bottomright;
1236         else
1237             return prop_atoms.net_wm_moveresize_size_topright;
1238     } else {
1239         if (y - cy > ch / 2)
1240             return prop_atoms.net_wm_moveresize_size_bottomleft;
1241         else
1242             return prop_atoms.net_wm_moveresize_size_topleft;
1243     }
1244 }
1245
1246 void action_moveresize(union ActionData *data)
1247 {
1248     ObClient *c = data->moveresize.any.c;
1249     guint32 corner;
1250
1251     if (!c || !client_normal(c)) return;
1252
1253     if (data->moveresize.keyboard) {
1254         corner = (data->moveresize.move ?
1255                   prop_atoms.net_wm_moveresize_move_keyboard :
1256                   prop_atoms.net_wm_moveresize_size_keyboard);
1257     } else {
1258         corner = (data->moveresize.move ?
1259                   prop_atoms.net_wm_moveresize_move :
1260                   pick_corner(data->any.x, data->any.y,
1261                               c->frame->area.x, c->frame->area.y,
1262                               /* use the client size because the frame
1263                                  can be differently sized (shaded
1264                                  windows) and we want this based on the
1265                                  clients size */
1266                               c->area.width + c->frame->size.left +
1267                               c->frame->size.right,
1268                               c->area.height + c->frame->size.top +
1269                               c->frame->size.bottom));
1270     }
1271
1272     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1273 }
1274
1275 void action_reconfigure(union ActionData *data)
1276 {
1277     ob_reconfigure();
1278 }
1279
1280 void action_restart(union ActionData *data)
1281 {
1282     ob_restart_other(data->execute.path);
1283 }
1284
1285 void action_exit(union ActionData *data)
1286 {
1287     ob_exit(0);
1288 }
1289
1290 void action_showmenu(union ActionData *data)
1291 {
1292     if (data->showmenu.name) {
1293         menu_show(data->showmenu.name, data->any.x, data->any.y,
1294                   data->showmenu.any.c);
1295     }
1296 }
1297
1298 void action_cycle_windows(union ActionData *data)
1299 {
1300     focus_cycle(data->cycle.forward, data->cycle.linear,
1301                 data->cycle.inter.any.interactive,
1302                 data->cycle.inter.final, data->cycle.inter.cancel);
1303 }
1304
1305 void action_directional_focus(union ActionData *data)
1306 {
1307     focus_directional_cycle(data->interdiraction.direction,
1308                             data->interdiraction.inter.any.interactive,
1309                             data->interdiraction.inter.final,
1310                             data->interdiraction.inter.cancel);
1311 }
1312
1313 void action_movetoedge(union ActionData *data)
1314 {
1315     int x, y;
1316     ObClient *c = data->diraction.any.c;
1317
1318     if (!c)
1319         return;
1320     x = c->frame->area.x;
1321     y = c->frame->area.y;
1322     
1323     switch(data->diraction.direction) {
1324     case OB_DIRECTION_NORTH:
1325         y = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1326         break;
1327     case OB_DIRECTION_WEST:
1328         x = client_directional_edge_search(c, OB_DIRECTION_WEST);
1329         break;
1330     case OB_DIRECTION_SOUTH:
1331         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH) -
1332             c->frame->area.height;
1333         break;
1334     case OB_DIRECTION_EAST:
1335         x = client_directional_edge_search(c, OB_DIRECTION_EAST) -
1336             c->frame->area.width;
1337         break;
1338     default:
1339         g_assert_not_reached();
1340     }
1341     frame_frame_gravity(c->frame, &x, &y);
1342     client_action_start(data);
1343     client_move(c, x, y);
1344     client_action_end(data);
1345
1346 }
1347
1348 void action_growtoedge(union ActionData *data)
1349 {
1350     int x, y, width, height, dest;
1351     ObClient *c = data->diraction.any.c;
1352     Rect *a;
1353
1354     if (!c)
1355         return;
1356     
1357     a = screen_area(c->desktop);
1358     x = c->frame->area.x;
1359     y = c->frame->area.y;
1360     width = c->frame->area.width;
1361     height = c->frame->area.height;
1362
1363     switch(data->diraction.direction) {
1364     case OB_DIRECTION_NORTH:
1365         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1366         if (a->y == y)
1367             height = c->frame->area.height / 2;
1368         else {
1369             height = c->frame->area.y + c->frame->area.height - dest;
1370             y = dest;
1371         }
1372         break;
1373     case OB_DIRECTION_WEST:
1374         dest = client_directional_edge_search(c, OB_DIRECTION_WEST);
1375         if (a->x == x)
1376             width = c->frame->area.width / 2;
1377         else {
1378             width = c->frame->area.x + c->frame->area.width - dest;
1379             x = dest;
1380         }
1381         break;
1382     case OB_DIRECTION_SOUTH:
1383         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH);
1384         if (a->y + a->height == y + c->frame->area.height) {
1385             height = c->frame->area.height / 2;
1386             y = a->y + a->height - height;
1387         } else
1388             height = dest - c->frame->area.y;
1389         y += (height - c->frame->area.height) % c->size_inc.height;
1390         height -= (height - c->frame->area.height) % c->size_inc.height;
1391         break;
1392     case OB_DIRECTION_EAST:
1393         dest = client_directional_edge_search(c, OB_DIRECTION_EAST);
1394         if (a->x + a->width == x + c->frame->area.width) {
1395             width = c->frame->area.width / 2;
1396             x = a->x + a->width - width;
1397         } else
1398             width = dest - c->frame->area.x;
1399         x += (width - c->frame->area.width) % c->size_inc.width;
1400         width -= (width - c->frame->area.width) % c->size_inc.width;
1401         break;
1402     default:
1403         g_assert_not_reached();
1404     }
1405     frame_frame_gravity(c->frame, &x, &y);
1406     width -= c->frame->size.left + c->frame->size.right;
1407     height -= c->frame->size.top + c->frame->size.bottom;
1408     client_action_start(data);
1409     client_move_resize(c, x, y, width, height);
1410     client_action_end(data);
1411 }
1412
1413 void action_send_to_layer(union ActionData *data)
1414 {
1415     if (data->layer.any.c)
1416         client_set_layer(data->layer.any.c, data->layer.layer);
1417 }
1418
1419 void action_toggle_layer(union ActionData *data)
1420 {
1421     ObClient *c = data->layer.any.c;
1422
1423     if (c) {
1424         client_action_start(data);
1425         if (data->layer.layer < 0)
1426             client_set_layer(c, c->below ? 0 : -1);
1427         else if (data->layer.layer > 0)
1428             client_set_layer(c, c->above ? 0 : 1);
1429         client_action_end(data);
1430     }
1431 }
1432
1433 void action_toggle_show_desktop(union ActionData *data)
1434 {
1435     screen_show_desktop(!screen_showing_desktop);
1436 }
1437
1438 void action_show_desktop(union ActionData *data)
1439 {
1440     screen_show_desktop(TRUE);
1441 }
1442
1443 void action_unshow_desktop(union ActionData *data)
1444 {
1445     screen_show_desktop(FALSE);
1446 }