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