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