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