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