]> icculus.org git repositories - manmower/openbox.git/blob - openbox/action.c
bring back action_run_string,
[manmower/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 void action_run_string(const gchar *name, struct _ObClient *c)
930 {
931     ObAction *a;
932     GSList *l;
933
934     a = action_from_string(name, OB_USER_ACTION_NONE);
935     g_assert(a);
936
937     l = g_slist_append(NULL, a);
938
939     action_run(l, c, 0);
940 }
941
942 void action_execute(union ActionData *data)
943 {
944     GError *e = NULL;
945     char *cmd;
946     if (data->execute.path) {
947         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
948         if (cmd) {
949             if (!g_spawn_command_line_async(cmd, &e)) {
950                 g_warning("failed to execute '%s': %s",
951                           cmd, e->message);
952             }
953             g_free(cmd);
954         } else {
955             g_warning("failed to convert '%s' from utf8", data->execute.path);
956         }
957     }
958 }
959
960 void action_activate(union ActionData *data)
961 {
962     client_activate(data->activate.any.c, data->activate.here);
963 }
964
965 void action_focus(union ActionData *data)
966 {
967     client_focus(data->client.any.c);
968 }
969
970 void action_unfocus (union ActionData *data)
971 {
972     client_unfocus(data->client.any.c);
973 }
974
975 void action_iconify(union ActionData *data)
976 {
977     client_iconify(data->client.any.c, TRUE, TRUE);
978 }
979
980 void action_raiselower(union ActionData *data)
981 {
982     ObClient *c = data->client.any.c;
983     GList *it;
984     gboolean raise = FALSE;
985
986     for (it = stacking_list; it; it = g_list_next(it)) {
987         ObClient *cit = it->data;
988
989         if (cit == c) break;
990         if (client_normal(cit) == client_normal(c) &&
991             cit->layer == c->layer &&
992             cit->frame->visible &&
993             !client_search_transient(c, cit))
994         {
995             if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
996                 raise = TRUE;
997                 break;
998             }
999         }
1000     }
1001
1002     action_run_string((raise ? "Raise" : "Lower"), c);
1003 }
1004
1005 void action_raise(union ActionData *data)
1006 {
1007     client_action_start(data);
1008     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1009     client_action_end(data);
1010 }
1011
1012 void action_unshaderaise(union ActionData *data)
1013 {
1014     if (data->client.any.c->shaded)
1015         action_run_string("Unshade", data->client.any.c);
1016     else
1017         action_run_string("Raise", data->client.any.c);
1018 }
1019
1020 void action_shadelower(union ActionData *data)
1021 {
1022     if (data->client.any.c->shaded)
1023         action_run_string("Lower", data->client.any.c);
1024     else
1025         action_run_string("Shade", data->client.any.c);
1026 }
1027
1028 void action_lower(union ActionData *data)
1029 {
1030     client_action_start(data);
1031     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1032     client_action_end(data);
1033 }
1034
1035 void action_close(union ActionData *data)
1036 {
1037     client_close(data->client.any.c);
1038 }
1039
1040 void action_kill(union ActionData *data)
1041 {
1042     client_kill(data->client.any.c);
1043 }
1044
1045 void action_shade(union ActionData *data)
1046 {
1047     client_action_start(data);
1048     client_shade(data->client.any.c, TRUE);
1049     client_action_end(data);
1050 }
1051
1052 void action_unshade(union ActionData *data)
1053 {
1054     client_action_start(data);
1055     client_shade(data->client.any.c, FALSE);
1056     client_action_end(data);
1057 }
1058
1059 void action_toggle_shade(union ActionData *data)
1060 {
1061     client_action_start(data);
1062     client_shade(data->client.any.c, !data->client.any.c->shaded);
1063     client_action_end(data);
1064 }
1065
1066 void action_toggle_omnipresent(union ActionData *data)
1067
1068     client_set_desktop(data->client.any.c,
1069                        data->client.any.c->desktop == DESKTOP_ALL ?
1070                        screen_desktop : DESKTOP_ALL, FALSE);
1071 }
1072
1073 void action_move_relative_horz(union ActionData *data)
1074 {
1075     ObClient *c = data->relative.any.c;
1076     client_action_start(data);
1077     client_move(c, c->area.x + data->relative.delta, c->area.y);
1078     client_action_end(data);
1079 }
1080
1081 void action_move_relative_vert(union ActionData *data)
1082 {
1083     ObClient *c = data->relative.any.c;
1084     client_action_start(data);
1085     client_move(c, c->area.x, c->area.y + data->relative.delta);
1086     client_action_end(data);
1087 }
1088
1089 void action_resize_relative_horz(union ActionData *data)
1090 {
1091     ObClient *c = data->relative.any.c;
1092     client_action_start(data);
1093     client_resize(c,
1094                   c->area.width + data->relative.delta * c->size_inc.width,
1095                   c->area.height);
1096     client_action_end(data);
1097 }
1098
1099 void action_resize_relative_vert(union ActionData *data)
1100 {
1101     ObClient *c = data->relative.any.c;
1102     if (!c->shaded) {
1103         client_action_start(data);
1104         client_resize(c, c->area.width, c->area.height +
1105                       data->relative.delta * c->size_inc.height);
1106         client_action_end(data);
1107     }
1108 }
1109
1110 void action_maximize_full(union ActionData *data)
1111 {
1112     client_action_start(data);
1113     client_maximize(data->client.any.c, TRUE, 0, TRUE);
1114     client_action_end(data);
1115 }
1116
1117 void action_unmaximize_full(union ActionData *data)
1118 {
1119     client_action_start(data);
1120     client_maximize(data->client.any.c, FALSE, 0, TRUE);
1121     client_action_end(data);
1122 }
1123
1124 void action_toggle_maximize_full(union ActionData *data)
1125 {
1126     client_action_start(data);
1127     client_maximize(data->client.any.c,
1128                     !(data->client.any.c->max_horz ||
1129                       data->client.any.c->max_vert),
1130                     0, TRUE);
1131     client_action_end(data);
1132 }
1133
1134 void action_maximize_horz(union ActionData *data)
1135 {
1136     client_action_start(data);
1137     client_maximize(data->client.any.c, TRUE, 1, TRUE);
1138     client_action_end(data);
1139 }
1140
1141 void action_unmaximize_horz(union ActionData *data)
1142 {
1143     client_action_start(data);
1144     client_maximize(data->client.any.c, FALSE, 1, TRUE);
1145     client_action_end(data);
1146 }
1147
1148 void action_toggle_maximize_horz(union ActionData *data)
1149 {
1150     client_action_start(data);
1151     client_maximize(data->client.any.c,
1152                     !data->client.any.c->max_horz, 1, TRUE);
1153     client_action_end(data);
1154 }
1155
1156 void action_maximize_vert(union ActionData *data)
1157 {
1158     client_action_start(data);
1159     client_maximize(data->client.any.c, TRUE, 2, TRUE);
1160     client_action_end(data);
1161 }
1162
1163 void action_unmaximize_vert(union ActionData *data)
1164 {
1165     client_action_start(data);
1166     client_maximize(data->client.any.c, FALSE, 2, TRUE);
1167     client_action_end(data);
1168 }
1169
1170 void action_toggle_maximize_vert(union ActionData *data)
1171 {
1172     client_action_start(data);
1173     client_maximize(data->client.any.c,
1174                     !data->client.any.c->max_vert, 2, TRUE);
1175     client_action_end(data);
1176 }
1177
1178 void action_send_to_desktop(union ActionData *data)
1179 {
1180     ObClient *c = data->sendto.any.c;
1181
1182     if (!client_normal(c)) return;
1183
1184     if (data->sendto.desk < screen_num_desktops ||
1185         data->sendto.desk == DESKTOP_ALL) {
1186         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1187         if (data->sendto.follow)
1188             screen_set_desktop(data->sendto.desk);
1189     }
1190 }
1191
1192 void action_desktop(union ActionData *data)
1193 {
1194     static guint first = (unsigned) -1;
1195
1196     if (data->inter.any.interactive && first == (unsigned) -1)
1197         first = screen_desktop;
1198
1199     if (!data->inter.any.interactive ||
1200         (!data->inter.cancel && !data->inter.final))
1201     {
1202         if (data->desktop.desk < screen_num_desktops ||
1203             data->desktop.desk == DESKTOP_ALL)
1204         {
1205             screen_set_desktop(data->desktop.desk);
1206             if (data->inter.any.interactive)
1207                 screen_desktop_popup(data->desktop.desk, TRUE);
1208         }
1209     } else if (data->inter.cancel) {
1210         screen_set_desktop(first);
1211     }
1212
1213     if (data->inter.any.interactive && data->inter.final) {
1214         screen_desktop_popup(0, FALSE);
1215         first = (unsigned) -1;
1216     }
1217 }
1218
1219 void action_desktop_dir(union ActionData *data)
1220 {
1221     guint d;
1222
1223     d = screen_cycle_desktop(data->desktopdir.dir,
1224                              data->desktopdir.wrap,
1225                              data->sendtodir.linear,
1226                              data->desktopdir.inter.any.interactive,
1227                              data->desktopdir.inter.final,
1228                              data->desktopdir.inter.cancel);
1229     if (!data->sendtodir.inter.any.interactive ||
1230         !data->sendtodir.inter.final)
1231     {
1232         screen_set_desktop(d);
1233     }
1234 }
1235
1236 void action_send_to_desktop_dir(union ActionData *data)
1237 {
1238     ObClient *c = data->sendtodir.inter.any.c;
1239     guint d;
1240
1241     if (!client_normal(c)) return;
1242
1243     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1244                              data->sendtodir.linear,
1245                              data->sendtodir.inter.any.interactive,
1246                              data->sendtodir.inter.final,
1247                              data->sendtodir.inter.cancel);
1248     if (!data->sendtodir.inter.any.interactive ||
1249         !data->sendtodir.inter.final)
1250     {
1251         client_set_desktop(c, d, data->sendtodir.follow);
1252         if (data->sendtodir.follow)
1253             screen_set_desktop(d);
1254     }
1255 }
1256
1257 void action_desktop_last(union ActionData *data)
1258 {
1259     screen_set_desktop(screen_last_desktop);
1260 }
1261
1262 void action_toggle_decorations(union ActionData *data)
1263 {
1264     ObClient *c = data->client.any.c;
1265
1266     client_action_start(data);
1267     client_set_undecorated(c, !c->undecorated);
1268     client_action_end(data);
1269 }
1270
1271 static guint32 pick_corner(int x, int y, int cx, int cy, int cw, int ch)
1272 {
1273     if (x - cx > cw / 2) {
1274         if (y - cy > ch / 2)
1275             return prop_atoms.net_wm_moveresize_size_bottomright;
1276         else
1277             return prop_atoms.net_wm_moveresize_size_topright;
1278     } else {
1279         if (y - cy > ch / 2)
1280             return prop_atoms.net_wm_moveresize_size_bottomleft;
1281         else
1282             return prop_atoms.net_wm_moveresize_size_topleft;
1283     }
1284 }
1285
1286 void action_moveresize(union ActionData *data)
1287 {
1288     ObClient *c = data->moveresize.any.c;
1289     guint32 corner;
1290
1291     if (!client_normal(c)) return;
1292
1293     if (data->moveresize.keyboard) {
1294         corner = (data->moveresize.move ?
1295                   prop_atoms.net_wm_moveresize_move_keyboard :
1296                   prop_atoms.net_wm_moveresize_size_keyboard);
1297     } else {
1298         corner = (data->moveresize.move ?
1299                   prop_atoms.net_wm_moveresize_move :
1300                   pick_corner(data->any.x, data->any.y,
1301                               c->frame->area.x, c->frame->area.y,
1302                               /* use the client size because the frame
1303                                  can be differently sized (shaded
1304                                  windows) and we want this based on the
1305                                  clients size */
1306                               c->area.width + c->frame->size.left +
1307                               c->frame->size.right,
1308                               c->area.height + c->frame->size.top +
1309                               c->frame->size.bottom));
1310     }
1311
1312     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1313 }
1314
1315 void action_reconfigure(union ActionData *data)
1316 {
1317     ob_reconfigure();
1318 }
1319
1320 void action_restart(union ActionData *data)
1321 {
1322     ob_restart_other(data->execute.path);
1323 }
1324
1325 void action_exit(union ActionData *data)
1326 {
1327     ob_exit(0);
1328 }
1329
1330 void action_showmenu(union ActionData *data)
1331 {
1332     if (data->showmenu.name) {
1333         menu_show(data->showmenu.name, data->any.x, data->any.y,
1334                   data->showmenu.any.c);
1335     }
1336 }
1337
1338 void action_cycle_windows(union ActionData *data)
1339 {
1340     focus_cycle(data->cycle.forward, data->cycle.linear,
1341                 data->cycle.inter.any.interactive,
1342                 data->cycle.inter.final, data->cycle.inter.cancel);
1343 }
1344
1345 void action_directional_focus(union ActionData *data)
1346 {
1347     focus_directional_cycle(data->interdiraction.direction,
1348                             data->interdiraction.inter.any.interactive,
1349                             data->interdiraction.inter.final,
1350                             data->interdiraction.inter.cancel);
1351 }
1352
1353 void action_movetoedge(union ActionData *data)
1354 {
1355     int x, y;
1356     ObClient *c = data->diraction.any.c;
1357
1358     x = c->frame->area.x;
1359     y = c->frame->area.y;
1360     
1361     switch(data->diraction.direction) {
1362     case OB_DIRECTION_NORTH:
1363         y = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1364         break;
1365     case OB_DIRECTION_WEST:
1366         x = client_directional_edge_search(c, OB_DIRECTION_WEST);
1367         break;
1368     case OB_DIRECTION_SOUTH:
1369         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH) -
1370             c->frame->area.height;
1371         break;
1372     case OB_DIRECTION_EAST:
1373         x = client_directional_edge_search(c, OB_DIRECTION_EAST) -
1374             c->frame->area.width;
1375         break;
1376     default:
1377         g_assert_not_reached();
1378     }
1379     frame_frame_gravity(c->frame, &x, &y);
1380     client_action_start(data);
1381     client_move(c, x, y);
1382     client_action_end(data);
1383 }
1384
1385 void action_growtoedge(union ActionData *data)
1386 {
1387     int x, y, width, height, dest;
1388     ObClient *c = data->diraction.any.c;
1389     Rect *a;
1390
1391     a = screen_area(c->desktop);
1392     x = c->frame->area.x;
1393     y = c->frame->area.y;
1394     width = c->frame->area.width;
1395     height = c->frame->area.height;
1396
1397     switch(data->diraction.direction) {
1398     case OB_DIRECTION_NORTH:
1399         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1400         if (a->y == y)
1401             height = c->frame->area.height / 2;
1402         else {
1403             height = c->frame->area.y + c->frame->area.height - dest;
1404             y = dest;
1405         }
1406         break;
1407     case OB_DIRECTION_WEST:
1408         dest = client_directional_edge_search(c, OB_DIRECTION_WEST);
1409         if (a->x == x)
1410             width = c->frame->area.width / 2;
1411         else {
1412             width = c->frame->area.x + c->frame->area.width - dest;
1413             x = dest;
1414         }
1415         break;
1416     case OB_DIRECTION_SOUTH:
1417         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH);
1418         if (a->y + a->height == y + c->frame->area.height) {
1419             height = c->frame->area.height / 2;
1420             y = a->y + a->height - height;
1421         } else
1422             height = dest - c->frame->area.y;
1423         y += (height - c->frame->area.height) % c->size_inc.height;
1424         height -= (height - c->frame->area.height) % c->size_inc.height;
1425         break;
1426     case OB_DIRECTION_EAST:
1427         dest = client_directional_edge_search(c, OB_DIRECTION_EAST);
1428         if (a->x + a->width == x + c->frame->area.width) {
1429             width = c->frame->area.width / 2;
1430             x = a->x + a->width - width;
1431         } else
1432             width = dest - c->frame->area.x;
1433         x += (width - c->frame->area.width) % c->size_inc.width;
1434         width -= (width - c->frame->area.width) % c->size_inc.width;
1435         break;
1436     default:
1437         g_assert_not_reached();
1438     }
1439     frame_frame_gravity(c->frame, &x, &y);
1440     width -= c->frame->size.left + c->frame->size.right;
1441     height -= c->frame->size.top + c->frame->size.bottom;
1442     client_action_start(data);
1443     client_move_resize(c, x, y, width, height);
1444     client_action_end(data);
1445 }
1446
1447 void action_send_to_layer(union ActionData *data)
1448 {
1449     client_set_layer(data->layer.any.c, data->layer.layer);
1450 }
1451
1452 void action_toggle_layer(union ActionData *data)
1453 {
1454     ObClient *c = data->layer.any.c;
1455
1456     client_action_start(data);
1457     if (data->layer.layer < 0)
1458         client_set_layer(c, c->below ? 0 : -1);
1459     else if (data->layer.layer > 0)
1460         client_set_layer(c, c->above ? 0 : 1);
1461     client_action_end(data);
1462 }
1463
1464 void action_toggle_show_desktop(union ActionData *data)
1465 {
1466     screen_show_desktop(!screen_showing_desktop);
1467 }
1468
1469 void action_show_desktop(union ActionData *data)
1470 {
1471     screen_show_desktop(TRUE);
1472 }
1473
1474 void action_unshow_desktop(union ActionData *data)
1475 {
1476     screen_show_desktop(FALSE);
1477 }