this is so bogus.. im going to break people's config files just now.
[dana/openbox-history.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) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "client.h"
22 #include "focus.h"
23 #include "moveresize.h"
24 #include "menu.h"
25 #include "prop.h"
26 #include "stacking.h"
27 #include "screen.h"
28 #include "action.h"
29 #include "openbox.h"
30 #include "grab.h"
31 #include "keyboard.h"
32 #include "event.h"
33 #include "dock.h"
34 #include "config.h"
35 #include "mainloop.h"
36 #include "startupnotify.h"
37 #include "gettext.h"
38
39 #include <glib.h>
40
41 inline void client_action_start(union ActionData *data)
42 {
43     if (config_focus_follow)
44         if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button)
45             grab_pointer(TRUE, FALSE, OB_CURSOR_NONE);
46 }
47
48 inline void client_action_end(union ActionData *data)
49 {
50     if (config_focus_follow)
51         if (data->any.context != OB_FRAME_CONTEXT_CLIENT) {
52             if (!data->any.button) {
53                 grab_pointer(FALSE, FALSE, OB_CURSOR_NONE);
54             } else {
55                 ObClient *c;
56
57                 /* usually this is sorta redundant, but with a press action
58                    the enter event will come as a GrabNotify which is
59                    ignored, so this will handle that case */
60                 if ((c = client_under_pointer()))
61                     event_enter_client(c);
62             }
63         }
64 }
65
66 typedef struct
67 {
68     const gchar *name;
69     void (*func)(union ActionData *);
70     void (*setup)(ObAction **, ObUserAction uact);
71 } ActionString;
72
73 static ObAction *action_new(void (*func)(union ActionData *data))
74 {
75     ObAction *a = g_new0(ObAction, 1);
76     a->ref = 1;
77     a->func = func;
78
79     return a;
80 }
81
82 void action_ref(ObAction *a)
83 {
84     ++a->ref;
85 }
86
87 void action_unref(ObAction *a)
88 {
89     if (a == NULL) return;
90
91     if (--a->ref > 0) return;
92
93     /* deal with pointers */
94     if (a->func == action_execute || a->func == action_restart)
95         g_free(a->data.execute.path);
96     else if (a->func == action_showmenu)
97         g_free(a->data.showmenu.name);
98
99     g_free(a);
100 }
101
102 ObAction* action_copy(const ObAction *src)
103 {
104     ObAction *a = action_new(src->func);
105
106     a->data = src->data;
107
108     /* deal with pointers */
109     if (a->func == action_execute || a->func == action_restart)
110         a->data.execute.path = g_strdup(a->data.execute.path);
111     else if (a->func == action_showmenu)
112         a->data.showmenu.name = g_strdup(a->data.showmenu.name);
113
114     return a;
115 }
116
117 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
118 {
119     (*a)->data.interdiraction.inter.any.interactive = TRUE;
120     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
121     (*a)->data.interdiraction.dialog = TRUE;
122     (*a)->data.interdiraction.dock_windows = FALSE;
123 }
124
125 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
126 {
127     (*a)->data.interdiraction.inter.any.interactive = TRUE;
128     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
129     (*a)->data.interdiraction.dialog = TRUE;
130     (*a)->data.interdiraction.dock_windows = FALSE;
131 }
132
133 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
134 {
135     (*a)->data.interdiraction.inter.any.interactive = TRUE;
136     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
137     (*a)->data.interdiraction.dialog = TRUE;
138     (*a)->data.interdiraction.dock_windows = FALSE;
139 }
140
141 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
142 {
143     (*a)->data.interdiraction.inter.any.interactive = TRUE;
144     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
145     (*a)->data.interdiraction.dialog = TRUE;
146     (*a)->data.interdiraction.dock_windows = FALSE;
147 }
148
149 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
150 {
151     (*a)->data.interdiraction.inter.any.interactive = TRUE;
152     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
153     (*a)->data.interdiraction.dialog = TRUE;
154     (*a)->data.interdiraction.dock_windows = FALSE;
155 }
156
157 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
158 {
159     (*a)->data.interdiraction.inter.any.interactive = TRUE;
160     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
161     (*a)->data.interdiraction.dialog = TRUE;
162     (*a)->data.interdiraction.dock_windows = FALSE;
163 }
164
165 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
166 {
167     (*a)->data.interdiraction.inter.any.interactive = TRUE;
168     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
169     (*a)->data.interdiraction.dialog = TRUE;
170     (*a)->data.interdiraction.dock_windows = FALSE;
171 }
172
173 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
174 {
175     (*a)->data.interdiraction.inter.any.interactive = TRUE;
176     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
177     (*a)->data.interdiraction.dialog = TRUE;
178     (*a)->data.interdiraction.dock_windows = FALSE;
179 }
180
181 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
182 {
183     (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
184     (*a)->data.sendto.follow = TRUE;
185 }
186
187 void setup_action_send_to_desktop_prev(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 = TRUE;
193     (*a)->data.sendtodir.wrap = TRUE;
194     (*a)->data.sendtodir.follow = TRUE;
195 }
196
197 void setup_action_send_to_desktop_next(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 = TRUE;
203     (*a)->data.sendtodir.wrap = TRUE;
204     (*a)->data.sendtodir.follow = TRUE;
205 }
206
207 void setup_action_send_to_desktop_left(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_WEST;
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_right(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_EAST;
222     (*a)->data.sendtodir.linear = FALSE;
223     (*a)->data.sendtodir.wrap = TRUE;
224     (*a)->data.sendtodir.follow = TRUE;
225 }
226
227 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
228 {
229     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
230     (*a)->data.sendtodir.inter.any.interactive = TRUE;
231     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
232     (*a)->data.sendtodir.linear = FALSE;
233     (*a)->data.sendtodir.wrap = TRUE;
234     (*a)->data.sendtodir.follow = TRUE;
235 }
236
237 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
238 {
239     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
240     (*a)->data.sendtodir.inter.any.interactive = TRUE;
241     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
242     (*a)->data.sendtodir.linear = FALSE;
243     (*a)->data.sendtodir.wrap = TRUE;
244     (*a)->data.sendtodir.follow = TRUE;
245 }
246
247 void setup_action_desktop(ObAction **a, ObUserAction uact)
248 {
249     (*a)->data.desktop.inter.any.interactive = FALSE;
250 }
251
252 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
253 {
254     (*a)->data.desktopdir.inter.any.interactive = TRUE;
255     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
256     (*a)->data.desktopdir.linear = TRUE;
257     (*a)->data.desktopdir.wrap = TRUE;
258 }
259
260 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
261 {
262     (*a)->data.desktopdir.inter.any.interactive = TRUE;
263     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
264     (*a)->data.desktopdir.linear = TRUE;
265     (*a)->data.desktopdir.wrap = TRUE;
266 }
267
268 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
269 {
270     (*a)->data.desktopdir.inter.any.interactive = TRUE;
271     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
272     (*a)->data.desktopdir.linear = FALSE;
273     (*a)->data.desktopdir.wrap = TRUE;
274 }
275
276 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
277 {
278     (*a)->data.desktopdir.inter.any.interactive = TRUE;
279     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
280     (*a)->data.desktopdir.linear = FALSE;
281     (*a)->data.desktopdir.wrap = TRUE;
282 }
283
284 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
285 {
286     (*a)->data.desktopdir.inter.any.interactive = TRUE;
287     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
288     (*a)->data.desktopdir.linear = FALSE;
289     (*a)->data.desktopdir.wrap = TRUE;
290 }
291
292 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
293 {
294     (*a)->data.desktopdir.inter.any.interactive = TRUE;
295     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
296     (*a)->data.desktopdir.linear = FALSE;
297     (*a)->data.desktopdir.wrap = TRUE;
298 }
299
300 void setup_action_cycle_windows_next(ObAction **a, ObUserAction uact)
301 {
302     (*a)->data.cycle.inter.any.interactive = TRUE;
303     (*a)->data.cycle.linear = FALSE;
304     (*a)->data.cycle.forward = TRUE;
305     (*a)->data.cycle.dialog = TRUE;
306     (*a)->data.cycle.dock_windows = FALSE;
307 }
308
309 void setup_action_cycle_windows_previous(ObAction **a, ObUserAction uact)
310 {
311     (*a)->data.cycle.inter.any.interactive = TRUE;
312     (*a)->data.cycle.linear = FALSE;
313     (*a)->data.cycle.forward = FALSE;
314     (*a)->data.cycle.dialog = TRUE;
315     (*a)->data.cycle.dock_windows = FALSE;
316 }
317
318 void setup_action_movefromedge_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     (*a)->data.diraction.hang = TRUE;
323 }
324
325 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
326 {
327     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
328     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
329     (*a)->data.diraction.hang = TRUE;
330 }
331
332 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
333 {
334     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
335     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
336     (*a)->data.diraction.hang = TRUE;
337 }
338
339 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
340 {
341     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
342     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
343     (*a)->data.diraction.hang = TRUE;
344 }
345
346 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
347 {
348     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
349     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
350     (*a)->data.diraction.hang = FALSE;
351 }
352
353 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
354 {
355     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
356     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
357     (*a)->data.diraction.hang = FALSE;
358 }
359
360 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
361 {
362     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
363     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
364     (*a)->data.diraction.hang = FALSE;
365 }
366
367 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
368 {
369     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
370     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
371     (*a)->data.diraction.hang = FALSE;
372 }
373
374 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
375 {
376     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
377     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
378 }
379
380 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
381 {
382     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
383     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
384 }
385
386 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
387 {
388     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
389     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
390 }
391
392 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
393 {
394     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
395     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
396 }
397
398 void setup_action_top_layer(ObAction **a, ObUserAction uact)
399 {
400     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
401     (*a)->data.layer.layer = 1;
402 }
403
404 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
405 {
406     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
407     (*a)->data.layer.layer = 0;
408 }
409
410 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
411 {
412     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
413     (*a)->data.layer.layer = -1;
414 }
415
416 void setup_action_move(ObAction **a, ObUserAction uact)
417 {
418     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
419     (*a)->data.moveresize.move = TRUE;
420     (*a)->data.moveresize.keyboard =
421         (uact == OB_USER_ACTION_NONE ||
422          uact == OB_USER_ACTION_KEYBOARD_KEY ||
423          uact == OB_USER_ACTION_MENU_SELECTION);
424 }
425
426 void setup_action_resize(ObAction **a, ObUserAction uact)
427 {
428     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
429     (*a)->data.moveresize.move = FALSE;
430     (*a)->data.moveresize.keyboard =
431         (uact == OB_USER_ACTION_NONE ||
432          uact == OB_USER_ACTION_KEYBOARD_KEY ||
433          uact == OB_USER_ACTION_MENU_SELECTION);
434 }
435
436 void setup_action_showmenu(ObAction **a, ObUserAction uact)
437 {
438     (*a)->data.showmenu.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
439     /* you cannot call ShowMenu from inside a menu, cuz the menu code makes
440        assumptions that there is only one menu (and submenus) open at
441        a time! */
442     if (uact == OB_USER_ACTION_MENU_SELECTION) {
443         action_unref(*a);
444         *a = NULL;
445     }
446 }
447
448 void setup_action_focus(ObAction **a, ObUserAction uact)
449 {
450     (*a)->data.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
451 }
452
453 void setup_client_action(ObAction **a, ObUserAction uact)
454 {
455     (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
456 }
457
458 ActionString actionstrings[] =
459 {
460     {
461         "execute", 
462         action_execute, 
463         NULL
464     },
465     {
466         "directionalfocusnorth", 
467         action_directional_focus, 
468         setup_action_directional_focus_north
469     },
470     {
471         "directionalfocuseast", 
472         action_directional_focus, 
473         setup_action_directional_focus_east
474     },
475     {
476         "directionalfocussouth", 
477         action_directional_focus, 
478         setup_action_directional_focus_south
479     },
480     {
481         "directionalfocuswest",
482         action_directional_focus,
483         setup_action_directional_focus_west
484     },
485     {
486         "directionalfocusnortheast",
487         action_directional_focus,
488         setup_action_directional_focus_northeast
489     },
490     {
491         "directionalfocussoutheast",
492         action_directional_focus,
493         setup_action_directional_focus_southeast
494     },
495     {
496         "directionalfocussouthwest",
497         action_directional_focus,
498         setup_action_directional_focus_southwest
499     },
500     {
501         "directionalfocusnorthwest",
502         action_directional_focus,
503         setup_action_directional_focus_northwest
504     },
505     {
506         "activate",
507         action_activate,
508         setup_action_focus
509     },
510     {
511         "focus",
512         action_focus,
513         setup_action_focus
514     },
515     {
516         "unfocus",
517         action_unfocus,
518         setup_client_action
519     },
520     {
521         "iconify",
522         action_iconify,
523         setup_client_action
524     },
525     {
526         "focustobottom",
527         action_focus_order_to_bottom,
528         setup_client_action
529     },
530     {
531         "raiselower",
532         action_raiselower,
533         setup_client_action
534     },
535     {
536         "raise",
537         action_raise,
538         setup_client_action
539     },
540     {
541         "lower",
542         action_lower,
543         setup_client_action
544     },
545     {
546         "close",
547         action_close,
548         setup_client_action
549     },
550     {
551         "kill",
552         action_kill,
553         setup_client_action
554     },
555     {
556         "shadelower",
557         action_shadelower,
558         setup_client_action
559     },
560     {
561         "unshaderaise",
562         action_unshaderaise,
563         setup_client_action
564     },
565     {
566         "shade",
567         action_shade,
568         setup_client_action
569     },
570     {
571         "unshade",
572         action_unshade,
573         setup_client_action
574     },
575     {
576         "toggleshade",
577         action_toggle_shade,
578         setup_client_action
579     },
580     {
581         "toggleomnipresent",
582         action_toggle_omnipresent,
583         setup_client_action
584     },
585     {
586         "moverelativehorz",
587         action_move_relative_horz,
588         setup_client_action
589     },
590     {
591         "moverelativevert",
592         action_move_relative_vert,
593         setup_client_action
594     },
595     {
596         "movetocenter",
597         action_move_to_center,
598         setup_client_action
599     },
600     {
601         "resizerelativehorz",
602         action_resize_relative_horz,
603         setup_client_action
604     },
605     {
606         "resizerelativevert",
607         action_resize_relative_vert,
608         setup_client_action
609     },
610     {
611         "moverelative",
612         action_move_relative,
613         setup_client_action
614     },
615     {
616         "resizerelative",
617         action_resize_relative,
618         setup_client_action
619     },
620     {
621         "maximizefull",
622         action_maximize_full,
623         setup_client_action
624     },
625     {
626         "unmaximizefull",
627         action_unmaximize_full,
628         setup_client_action
629     },
630     {
631         "togglemaximizefull",
632         action_toggle_maximize_full,
633         setup_client_action
634     },
635     {
636         "maximizehorz",
637         action_maximize_horz,
638         setup_client_action
639     },
640     {
641         "unmaximizehorz",
642         action_unmaximize_horz,
643         setup_client_action
644     },
645     {
646         "togglemaximizehorz",
647         action_toggle_maximize_horz,
648         setup_client_action
649     },
650     {
651         "maximizevert",
652         action_maximize_vert,
653         setup_client_action
654     },
655     {
656         "unmaximizevert",
657         action_unmaximize_vert,
658         setup_client_action
659     },
660     {
661         "togglemaximizevert",
662         action_toggle_maximize_vert,
663         setup_client_action
664     },
665     {
666         "togglefullscreen",
667         action_toggle_fullscreen,
668         setup_client_action
669     },
670     {
671         "sendtodesktop",
672         action_send_to_desktop,
673         setup_action_send_to_desktop
674     },
675     {
676         "sendtodesktopnext",
677         action_send_to_desktop_dir,
678         setup_action_send_to_desktop_next
679     },
680     {
681         "sendtodesktopprevious",
682         action_send_to_desktop_dir,
683         setup_action_send_to_desktop_prev
684     },
685     {
686         "sendtodesktopright",
687         action_send_to_desktop_dir,
688         setup_action_send_to_desktop_right
689     },
690     {
691         "sendtodesktopleft",
692         action_send_to_desktop_dir,
693         setup_action_send_to_desktop_left
694     },
695     {
696         "sendtodesktopup",
697         action_send_to_desktop_dir,
698         setup_action_send_to_desktop_up
699     },
700     {
701         "sendtodesktopdown",
702         action_send_to_desktop_dir,
703         setup_action_send_to_desktop_down
704     },
705     {
706         "desktop",
707         action_desktop,
708         setup_action_desktop
709     },
710     {
711         "desktopnext",
712         action_desktop_dir,
713         setup_action_desktop_next
714     },
715     {
716         "desktopprevious",
717         action_desktop_dir,
718         setup_action_desktop_prev
719     },
720     {
721         "desktopright",
722         action_desktop_dir,
723         setup_action_desktop_right
724     },
725     {
726         "desktopleft",
727         action_desktop_dir,
728         setup_action_desktop_left
729     },
730     {
731         "desktopup",
732         action_desktop_dir,
733         setup_action_desktop_up
734     },
735     {
736         "desktopdown",
737         action_desktop_dir,
738         setup_action_desktop_down
739     },
740     {
741         "toggledecorations",
742         action_toggle_decorations,
743         setup_client_action
744     },
745     {
746         "move",
747         action_moveresize,
748         setup_action_move
749     },
750     {
751         "resize",
752         action_moveresize,
753         setup_action_resize
754     },
755     {
756         "toggledockautohide",
757         action_toggle_dockautohide,
758         NULL
759     },
760     {
761         "toggleshowdesktop",
762         action_toggle_show_desktop,
763         NULL
764     },
765     {
766         "showdesktop",
767         action_show_desktop,
768         NULL
769     },
770     {
771         "unshowdesktop",
772         action_unshow_desktop,
773         NULL
774     },
775     {
776         "desktoplast",
777         action_desktop_last,
778         NULL
779     },
780     {
781         "reconfigure",
782         action_reconfigure,
783         NULL
784     },
785     {
786         "restart",
787         action_restart,
788         NULL
789     },
790     {
791         "exit",
792         action_exit,
793         NULL
794     },
795     {
796         "showmenu",
797         action_showmenu,
798         setup_action_showmenu
799     },
800     {
801         "sendtotoplayer",
802         action_send_to_layer,
803         setup_action_top_layer
804     },
805     {
806         "togglealwaysontop",
807         action_toggle_layer,
808         setup_action_top_layer
809     },
810     {
811         "sendtonormallayer",
812         action_send_to_layer,
813         setup_action_normal_layer
814     },
815     {
816         "sendtobottomlayer",
817         action_send_to_layer,
818         setup_action_bottom_layer
819     },
820     {
821         "togglealwaysonbottom",
822         action_toggle_layer,
823         setup_action_bottom_layer
824     },
825     {
826         "nextwindow",
827         action_cycle_windows,
828         setup_action_cycle_windows_next
829     },
830     {
831         "previouswindow",
832         action_cycle_windows,
833         setup_action_cycle_windows_previous
834     },
835     {
836         "movefromedgenorth",
837         action_movetoedge,
838         setup_action_movefromedge_north
839     },
840     {
841         "movefromedgesouth",
842         action_movetoedge,
843         setup_action_movefromedge_south
844     },
845     {
846         "movefromedgewest",
847         action_movetoedge,
848         setup_action_movefromedge_west
849     },
850     {
851         "movefromedgeeast",
852         action_movetoedge,
853         setup_action_movefromedge_east
854     },
855     {
856         "movetoedgenorth",
857         action_movetoedge,
858         setup_action_movetoedge_north
859     },
860     {
861         "movetoedgesouth",
862         action_movetoedge,
863         setup_action_movetoedge_south
864     },
865     {
866         "movetoedgewest",
867         action_movetoedge,
868         setup_action_movetoedge_west
869     },
870     {
871         "movetoedgeeast",
872         action_movetoedge,
873         setup_action_movetoedge_east
874     },
875     {
876         "growtoedgenorth",
877         action_growtoedge,
878         setup_action_growtoedge_north
879     },
880     {
881         "growtoedgesouth",
882         action_growtoedge,
883         setup_action_growtoedge_south
884     },
885     {
886         "growtoedgewest",
887         action_growtoedge,
888         setup_action_growtoedge_west
889     },
890     {
891         "growtoedgeeast",
892         action_growtoedge,
893         setup_action_growtoedge_east
894     },
895     {
896         "breakchroot",
897         action_break_chroot,
898         NULL
899     },
900     {
901         NULL,
902         NULL,
903         NULL
904     }
905 };
906
907 /* only key bindings can be interactive. thus saith the xor.
908    because of how the mouse is grabbed, mouse events dont even get
909    read during interactive events, so no dice! >:) */
910 #define INTERACTIVE_LIMIT(a, uact) \
911     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
912         a->data.any.interactive = FALSE;
913
914 ObAction *action_from_string(const gchar *name, ObUserAction uact)
915 {
916     ObAction *a = NULL;
917     gboolean exist = FALSE;
918     gint i;
919
920     for (i = 0; actionstrings[i].name; i++)
921         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
922             exist = TRUE;
923             a = action_new(actionstrings[i].func);
924             if (actionstrings[i].setup)
925                 actionstrings[i].setup(&a, uact);
926             if (a)
927                 INTERACTIVE_LIMIT(a, uact);
928             break;
929         }
930     if (!exist)
931         g_message(_("Invalid action '%s' requested. No such action exists."),
932                   name);
933     if (!a)
934         g_message(_("Invalid use of action '%s'. Action will be ignored."),
935                   name);
936     return a;
937 }
938
939 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
940                        ObUserAction uact)
941 {
942     gchar *actname;
943     ObAction *act = NULL;
944     xmlNodePtr n;
945
946     if (parse_attr_string("name", node, &actname)) {
947         if ((act = action_from_string(actname, uact))) {
948             if (act->func == action_execute || act->func == action_restart) {
949                 if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
950                     gchar *s = parse_string(doc, n);
951                     act->data.execute.path = parse_expand_tilde(s);
952                     g_free(s);
953                 }
954                 if ((n = parse_find_node("startupnotify", node->xmlChildrenNode))) {
955                     xmlNodePtr m;
956                     if ((m = parse_find_node("enabled", n->xmlChildrenNode)))
957                         act->data.execute.startupnotify = parse_bool(doc, m);
958                     if ((m = parse_find_node("name", n->xmlChildrenNode)))
959                         act->data.execute.name = parse_string(doc, m);
960                     if ((m = parse_find_node("icon", n->xmlChildrenNode)))
961                         act->data.execute.icon_name = parse_string(doc, m);
962                 }
963             } else if (act->func == action_showmenu) {
964                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
965                     act->data.showmenu.name = parse_string(doc, n);
966             } else if (act->func == action_move_relative_horz ||
967                        act->func == action_move_relative_vert ||
968                        act->func == action_resize_relative_horz ||
969                        act->func == action_resize_relative_vert) {
970                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
971                     act->data.relative.deltax = parse_int(doc, n);
972             } else if (act->func == action_move_relative) {
973                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
974                     act->data.relative.deltax = parse_int(doc, n);
975                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
976                     act->data.relative.deltay = parse_int(doc, n);
977             } else if (act->func == action_resize_relative) {
978                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
979                     act->data.relative.deltaxl = parse_int(doc, n);
980                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
981                     act->data.relative.deltayu = parse_int(doc, n);
982                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
983                     act->data.relative.deltax = parse_int(doc, n);
984                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
985                     act->data.relative.deltay = parse_int(doc, n);
986             } else if (act->func == action_desktop) {
987                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
988                     act->data.desktop.desk = parse_int(doc, n);
989                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
990                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
991                     act->data.desktop.inter.any.interactive =
992                         parse_bool(doc, n);
993            } else if (act->func == action_send_to_desktop) {
994                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
995                     act->data.sendto.desk = parse_int(doc, n);
996                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
997                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
998                     act->data.sendto.follow = parse_bool(doc, n);
999             } else if (act->func == action_desktop_dir) {
1000                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1001                     act->data.desktopdir.wrap = parse_bool(doc, n); 
1002                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1003                     act->data.desktopdir.inter.any.interactive =
1004                         parse_bool(doc, n);
1005             } else if (act->func == action_send_to_desktop_dir) {
1006                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1007                     act->data.sendtodir.wrap = parse_bool(doc, n);
1008                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1009                     act->data.sendtodir.follow = parse_bool(doc, n);
1010                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1011                     act->data.sendtodir.inter.any.interactive =
1012                         parse_bool(doc, n);
1013             } else if (act->func == action_activate) {
1014                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
1015                     act->data.activate.here = parse_bool(doc, n);
1016             } else if (act->func == action_cycle_windows) {
1017                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
1018                     act->data.cycle.linear = parse_bool(doc, n);
1019                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1020                     act->data.cycle.dialog = parse_bool(doc, n);
1021                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1022                     act->data.cycle.dock_windows = parse_bool(doc, n);
1023             } else if (act->func == action_directional_focus) {
1024                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1025                     act->data.interdiraction.dialog = parse_bool(doc, n);
1026                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1027                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
1028             } else if (act->func == action_raise ||
1029                        act->func == action_lower ||
1030                        act->func == action_raiselower ||
1031                        act->func == action_shadelower ||
1032                        act->func == action_unshaderaise) {
1033             }
1034             INTERACTIVE_LIMIT(act, uact);
1035         }
1036         g_free(actname);
1037     }
1038     return act;
1039 }
1040
1041 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
1042                      guint state, guint button, gint x, gint y, Time time,
1043                      gboolean cancel, gboolean done)
1044 {
1045     GSList *it;
1046     ObAction *a;
1047     gboolean inter = FALSE;
1048
1049     if (!acts)
1050         return;
1051
1052     if (x < 0 && y < 0)
1053         screen_pointer_pos(&x, &y);
1054
1055     if (grab_on_keyboard())
1056         inter = TRUE;
1057     else
1058         for (it = acts; it; it = g_slist_next(it)) {
1059             a = it->data;
1060             if (a->data.any.interactive) {
1061                 inter = TRUE;
1062                 break;
1063             }
1064         }
1065
1066     if (!inter) {
1067         /* sometimes when we execute another app as an action,
1068            it won't work right unless we XUngrabKeyboard first,
1069            even though we grabbed the key/button Asychronously.
1070            e.g. "gnome-panel-control --main-menu" */
1071         grab_keyboard(FALSE);
1072     }
1073
1074     for (it = acts; it; it = g_slist_next(it)) {
1075         a = it->data;
1076
1077         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
1078             a->data.any.c = a->data.any.client_action ? c : NULL;
1079             a->data.any.context = context;
1080             a->data.any.x = x;
1081             a->data.any.y = y;
1082
1083             a->data.any.button = button;
1084
1085             a->data.any.time = time;
1086
1087             if (a->data.any.interactive) {
1088                 a->data.inter.cancel = cancel;
1089                 a->data.inter.final = done;
1090                 if (!(cancel || done))
1091                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
1092                         continue;
1093             }
1094
1095             /* XXX UGLY HACK race with motion event starting a move and the
1096                button release gettnig processed first. answer: don't queue
1097                moveresize starts. UGLY HACK XXX */
1098             if (a->data.any.interactive || a->func == action_moveresize) {
1099                 /* interactive actions are not queued */
1100                 a->func(&a->data);
1101             } else if ((context == OB_FRAME_CONTEXT_CLIENT ||
1102                         (c && c->type == OB_CLIENT_TYPE_DESKTOP &&
1103                          context == OB_FRAME_CONTEXT_DESKTOP)) &&
1104                        (a->func == action_focus ||
1105                         a->func == action_activate))
1106             {
1107                 /* XXX MORE UGLY HACK
1108                    actions from clicks on client windows are NOT queued.
1109                    this solves the mysterious click-and-drag-doesnt-work
1110                    problem. it was because the window gets focused and stuff
1111                    after the button event has already been passed through. i
1112                    dont really know why it should care but it does and it makes
1113                    a difference.
1114
1115                    however this very bogus ! !
1116                    we want to send the button press to the window BEFORE
1117                    we do the action because the action might move the windows
1118                    (eg change desktops) and then the button press ends up on
1119                    the completely wrong window !
1120                    so, this is just for that bug, and it will only NOT queue it
1121                    if it is a focusing action that can be used with the mouse
1122                    pointer. ugh.
1123                 */
1124                 a->func(&a->data);
1125             } else
1126                 ob_main_loop_queue_action(ob_main_loop, a);
1127         }
1128     }
1129 }
1130
1131 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1132 {
1133     ObAction *a;
1134     GSList *l;
1135
1136     a = action_from_string(name, OB_USER_ACTION_NONE);
1137     g_assert(a);
1138
1139     l = g_slist_append(NULL, a);
1140
1141     action_run(l, c, 0, time);
1142 }
1143
1144 void action_execute(union ActionData *data)
1145 {
1146     GError *e = NULL;
1147     gchar *cmd, **argv = 0;
1148     if (data->execute.path) {
1149         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
1150         if (cmd) {
1151             if (!g_shell_parse_argv (cmd, NULL, &argv, &e)) {
1152                 g_message(_("Failed to execute '%s': %s"),
1153                           cmd, e->message);
1154                 g_error_free(e);
1155             } else if (data->execute.startupnotify) {
1156                 gchar *program;
1157                 
1158                 program = g_path_get_basename(argv[0]);
1159                 /* sets up the environment */
1160                 sn_setup_spawn_environment(program,
1161                                            data->execute.name,
1162                                            data->execute.icon_name,
1163                                            /* launch it on the current
1164                                               desktop */
1165                                            screen_desktop,
1166                                            data->execute.any.time);
1167                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1168                                    G_SPAWN_DO_NOT_REAP_CHILD,
1169                                    NULL, NULL, NULL, &e)) {
1170                     g_message(_("Failed to execute '%s': %s"),
1171                               cmd, e->message);
1172                     g_error_free(e);
1173                     sn_spawn_cancel();
1174                 }
1175                 unsetenv("DESKTOP_STARTUP_ID");
1176                 g_free(program);
1177                 g_strfreev(argv);
1178             } else {
1179                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1180                                    G_SPAWN_DO_NOT_REAP_CHILD,
1181                                    NULL, NULL, NULL, &e))
1182                 {
1183                     g_message(_("Failed to execute '%s': %s"),
1184                               cmd, e->message);
1185                     g_error_free(e);
1186                 }
1187                 g_strfreev(argv);
1188             }
1189             g_free(cmd);
1190         } else {
1191             g_message(_("Failed to convert the path '%s' from utf8"),
1192                       data->execute.path);
1193         }
1194     }
1195 }
1196
1197 void action_activate(union ActionData *data)
1198 {
1199     if (data->client.any.c) {
1200         /* similar to the openbox dock for dockapps, don't let user actions
1201            give focus to 3rd-party docks (panels) either (unless they ask for
1202            it themselves). */
1203         if (data->client.any.c->type != OB_CLIENT_TYPE_DOCK) {
1204             /* if using focus_delay, stop the timer now so that focus doesn't
1205                go moving on us */
1206             event_halt_focus_delay();
1207
1208             client_activate(data->activate.any.c, data->activate.here, TRUE);
1209         }
1210     } else {
1211         /* focus action on something other than a client, make keybindings
1212            work for this openbox instance, but don't focus any specific client
1213         */
1214         focus_nothing();
1215     }
1216 }
1217
1218 void action_focus(union ActionData *data)
1219 {
1220     if (data->client.any.c) {
1221         /* similar to the openbox dock for dockapps, don't let user actions
1222            give focus to 3rd-party docks (panels) either (unless they ask for
1223            it themselves). */
1224         if (data->client.any.c->type != OB_CLIENT_TYPE_DOCK) {
1225             /* if using focus_delay, stop the timer now so that focus doesn't
1226                go moving on us */
1227             event_halt_focus_delay();
1228
1229             client_focus(data->client.any.c);
1230         }
1231     } else {
1232         /* focus action on something other than a client, make keybindings
1233            work for this openbox instance, but don't focus any specific client
1234         */
1235         focus_nothing();
1236     }
1237 }
1238
1239 void action_unfocus (union ActionData *data)
1240 {
1241     if (data->client.any.c == focus_client)
1242         focus_fallback(FALSE);
1243 }
1244
1245 void action_iconify(union ActionData *data)
1246 {
1247     client_action_start(data);
1248     client_iconify(data->client.any.c, TRUE, TRUE);
1249     client_action_end(data);
1250 }
1251
1252 void action_focus_order_to_bottom(union ActionData *data)
1253 {
1254     focus_order_to_bottom(data->client.any.c);
1255 }
1256
1257 void action_raiselower(union ActionData *data)
1258 {
1259     ObClient *c = data->client.any.c;
1260     GList *it;
1261     gboolean raise = FALSE;
1262
1263     for (it = stacking_list; it; it = g_list_next(it)) {
1264         if (WINDOW_IS_CLIENT(it->data)) {
1265             ObClient *cit = it->data;
1266
1267             if (cit == c) break;
1268             if (client_normal(cit) == client_normal(c) &&
1269                     cit->layer == c->layer &&
1270                     cit->frame->visible &&
1271                     !client_search_transient(c, cit))
1272             {
1273                 if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
1274                     raise = TRUE;
1275                     break;
1276                 }
1277             }
1278         }
1279     }
1280
1281     if (raise)
1282         action_raise(data);
1283     else
1284         action_lower(data);
1285 }
1286
1287 void action_raise(union ActionData *data)
1288 {
1289     client_action_start(data);
1290     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1291     client_action_end(data);
1292 }
1293
1294 void action_unshaderaise(union ActionData *data)
1295 {
1296     if (data->client.any.c->shaded)
1297         action_unshade(data);
1298     else
1299         action_raise(data);
1300 }
1301
1302 void action_shadelower(union ActionData *data)
1303 {
1304     if (data->client.any.c->shaded)
1305         action_lower(data);
1306     else
1307         action_shade(data);
1308 }
1309
1310 void action_lower(union ActionData *data)
1311 {
1312     client_action_start(data);
1313     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1314     client_action_end(data);
1315 }
1316
1317 void action_close(union ActionData *data)
1318 {
1319     client_close(data->client.any.c);
1320 }
1321
1322 void action_kill(union ActionData *data)
1323 {
1324     client_kill(data->client.any.c);
1325 }
1326
1327 void action_shade(union ActionData *data)
1328 {
1329     client_action_start(data);
1330     client_shade(data->client.any.c, TRUE);
1331     client_action_end(data);
1332 }
1333
1334 void action_unshade(union ActionData *data)
1335 {
1336     client_action_start(data);
1337     client_shade(data->client.any.c, FALSE);
1338     client_action_end(data);
1339 }
1340
1341 void action_toggle_shade(union ActionData *data)
1342 {
1343     client_action_start(data);
1344     client_shade(data->client.any.c, !data->client.any.c->shaded);
1345     client_action_end(data);
1346 }
1347
1348 void action_toggle_omnipresent(union ActionData *data)
1349
1350     client_set_desktop(data->client.any.c,
1351                        data->client.any.c->desktop == DESKTOP_ALL ?
1352                        screen_desktop : DESKTOP_ALL, FALSE);
1353 }
1354
1355 void action_move_relative_horz(union ActionData *data)
1356 {
1357     ObClient *c = data->relative.any.c;
1358     client_action_start(data);
1359     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1360     client_action_end(data);
1361 }
1362
1363 void action_move_relative_vert(union ActionData *data)
1364 {
1365     ObClient *c = data->relative.any.c;
1366     client_action_start(data);
1367     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1368     client_action_end(data);
1369 }
1370
1371 void action_move_to_center(union ActionData *data)
1372 {
1373     ObClient *c = data->client.any.c;
1374     Rect *area;
1375     area = screen_area_monitor(c->desktop, 0);
1376     client_action_start(data);
1377     client_move(c, area->width / 2 - c->area.width / 2,
1378                 area->height / 2 - c->area.height / 2);
1379     client_action_end(data);
1380 }
1381
1382 void action_resize_relative_horz(union ActionData *data)
1383 {
1384     ObClient *c = data->relative.any.c;
1385     client_action_start(data);
1386     client_resize(c,
1387                   c->area.width + data->relative.deltax * c->size_inc.width,
1388                   c->area.height);
1389     client_action_end(data);
1390 }
1391
1392 void action_resize_relative_vert(union ActionData *data)
1393 {
1394     ObClient *c = data->relative.any.c;
1395     if (!c->shaded) {
1396         client_action_start(data);
1397         client_resize(c, c->area.width, c->area.height +
1398                       data->relative.deltax * c->size_inc.height);
1399         client_action_end(data);
1400     }
1401 }
1402
1403 void action_move_relative(union ActionData *data)
1404 {
1405     ObClient *c = data->relative.any.c;
1406     client_action_start(data);
1407     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1408                 data->relative.deltay);
1409     client_action_end(data);
1410 }
1411
1412 void action_resize_relative(union ActionData *data)
1413 {
1414     ObClient *c = data->relative.any.c;
1415     gint x, y, ow, w, oh, h, lw, lh;
1416
1417     client_action_start(data);
1418
1419     x = c->area.x;
1420     y = c->area.y;
1421     ow = c->area.width;
1422     w = ow + data->relative.deltax * c->size_inc.width
1423         + data->relative.deltaxl * c->size_inc.width;
1424     oh = c->area.height;
1425     h = oh + data->relative.deltay * c->size_inc.height
1426         + data->relative.deltayu * c->size_inc.height;
1427     
1428     client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
1429     client_move_resize(c, x + (ow - w), y + (oh - h), w, h);
1430     client_action_end(data);
1431 }
1432
1433 void action_maximize_full(union ActionData *data)
1434 {
1435     client_action_start(data);
1436     client_maximize(data->client.any.c, TRUE, 0);
1437     client_action_end(data);
1438 }
1439
1440 void action_unmaximize_full(union ActionData *data)
1441 {
1442     client_action_start(data);
1443     client_maximize(data->client.any.c, FALSE, 0);
1444     client_action_end(data);
1445 }
1446
1447 void action_toggle_maximize_full(union ActionData *data)
1448 {
1449     client_action_start(data);
1450     client_maximize(data->client.any.c,
1451                     !(data->client.any.c->max_horz ||
1452                       data->client.any.c->max_vert),
1453                     0);
1454     client_action_end(data);
1455 }
1456
1457 void action_maximize_horz(union ActionData *data)
1458 {
1459     client_action_start(data);
1460     client_maximize(data->client.any.c, TRUE, 1);
1461     client_action_end(data);
1462 }
1463
1464 void action_unmaximize_horz(union ActionData *data)
1465 {
1466     client_action_start(data);
1467     client_maximize(data->client.any.c, FALSE, 1);
1468     client_action_end(data);
1469 }
1470
1471 void action_toggle_maximize_horz(union ActionData *data)
1472 {
1473     client_action_start(data);
1474     client_maximize(data->client.any.c,
1475                     !data->client.any.c->max_horz, 1);
1476     client_action_end(data);
1477 }
1478
1479 void action_maximize_vert(union ActionData *data)
1480 {
1481     client_action_start(data);
1482     client_maximize(data->client.any.c, TRUE, 2);
1483     client_action_end(data);
1484 }
1485
1486 void action_unmaximize_vert(union ActionData *data)
1487 {
1488     client_action_start(data);
1489     client_maximize(data->client.any.c, FALSE, 2);
1490     client_action_end(data);
1491 }
1492
1493 void action_toggle_maximize_vert(union ActionData *data)
1494 {
1495     client_action_start(data);
1496     client_maximize(data->client.any.c,
1497                     !data->client.any.c->max_vert, 2);
1498     client_action_end(data);
1499 }
1500
1501 void action_toggle_fullscreen(union ActionData *data)
1502 {
1503     client_action_start(data);
1504     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1505     client_action_end(data);
1506 }
1507
1508 void action_send_to_desktop(union ActionData *data)
1509 {
1510     ObClient *c = data->sendto.any.c;
1511
1512     if (!client_normal(c)) return;
1513
1514     if (data->sendto.desk < screen_num_desktops ||
1515         data->sendto.desk == DESKTOP_ALL) {
1516         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1517         if (data->sendto.follow)
1518             screen_set_desktop(data->sendto.desk);
1519     }
1520 }
1521
1522 void action_desktop(union ActionData *data)
1523 {
1524     static guint first = (unsigned) -1;
1525
1526     if (data->inter.any.interactive && first == (unsigned) -1)
1527         first = screen_desktop;
1528
1529     if (!data->inter.any.interactive ||
1530         (!data->inter.cancel && !data->inter.final))
1531     {
1532         if (data->desktop.desk < screen_num_desktops ||
1533             data->desktop.desk == DESKTOP_ALL)
1534         {
1535             screen_set_desktop(data->desktop.desk);
1536             if (data->inter.any.interactive)
1537                 screen_desktop_popup(data->desktop.desk, TRUE);
1538         }
1539     } else if (data->inter.cancel) {
1540         screen_set_desktop(first);
1541     }
1542
1543     if (!data->inter.any.interactive || data->inter.final) {
1544         screen_desktop_popup(0, FALSE);
1545         first = (unsigned) -1;
1546     }
1547 }
1548
1549 void action_desktop_dir(union ActionData *data)
1550 {
1551     guint d;
1552
1553     d = screen_cycle_desktop(data->desktopdir.dir,
1554                              data->desktopdir.wrap,
1555                              data->desktopdir.linear,
1556                              data->desktopdir.inter.any.interactive,
1557                              data->desktopdir.inter.final,
1558                              data->desktopdir.inter.cancel);
1559     if (!data->sendtodir.inter.any.interactive ||
1560         !data->sendtodir.inter.final ||
1561         data->sendtodir.inter.cancel)
1562     {
1563         screen_set_desktop(d);
1564     }
1565 }
1566
1567 void action_send_to_desktop_dir(union ActionData *data)
1568 {
1569     ObClient *c = data->sendtodir.inter.any.c;
1570     guint d;
1571
1572     if (!client_normal(c)) return;
1573
1574     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1575                              data->sendtodir.linear,
1576                              data->sendtodir.inter.any.interactive,
1577                              data->sendtodir.inter.final,
1578                              data->sendtodir.inter.cancel);
1579     if (!data->sendtodir.inter.any.interactive ||
1580         !data->sendtodir.inter.final ||
1581         data->sendtodir.inter.cancel)
1582     {
1583         client_set_desktop(c, d, data->sendtodir.follow);
1584         if (data->sendtodir.follow)
1585             screen_set_desktop(d);
1586     }
1587 }
1588
1589 void action_desktop_last(union ActionData *data)
1590 {
1591     screen_set_desktop(screen_last_desktop);
1592 }
1593
1594 void action_toggle_decorations(union ActionData *data)
1595 {
1596     ObClient *c = data->client.any.c;
1597
1598     client_action_start(data);
1599     client_set_undecorated(c, !c->undecorated);
1600     client_action_end(data);
1601 }
1602
1603 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch)
1604 {
1605     /* let's make x and y client relative instead of screen relative */
1606     x = x - cx;
1607     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1608
1609 #define X x*ch/cw
1610 #define A -4*X + 7*ch/3
1611 #define B  4*X -15*ch/9
1612 #define C -X/4 + 2*ch/3
1613 #define D  X/4 + 5*ch/12
1614 #define E  X/4 +   ch/3
1615 #define F -X/4 + 7*ch/12
1616 #define G  4*X - 4*ch/3
1617 #define H -4*X + 8*ch/3
1618 #define a (y > 5*ch/9)
1619 #define b (x < 4*cw/9)
1620 #define c (x > 5*cw/9)
1621 #define d (y < 4*ch/9)
1622
1623     /*
1624       Each of these defines (except X which is just there for fun), represents
1625       the equation of a line. The lines they represent are shown in the diagram
1626       below. Checking y against these lines, we are able to choose a region
1627       of the window as shown.
1628
1629       +---------------------A-------|-------|-------B---------------------+
1630       |                     |A                     B|                     |
1631       |                     |A      |       |      B|                     |
1632       |                     | A                   B |                     |
1633       |                     | A     |       |     B |                     |
1634       |                     |  A                 B  |                     |
1635       |                     |  A    |       |    B  |                     |
1636       |        northwest    |   A     north     B   |   northeast         |
1637       |                     |   A   |       |   B   |                     |
1638       |                     |    A             B    |                     |
1639       C---------------------+----A--+-------+--B----+---------------------D
1640       |CCCCCCC              |     A           B     |              DDDDDDD|
1641       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1642       |               CCCCCCC      A         B      DDDDDDD               |
1643       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - -
1644       |                     |       b       c       |                     |
1645       |             west    |       b  move c       |   east              |
1646       |                     |       b       c       |                     |
1647       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - 
1648       |               EEEEEEE      G         H      FFFFFFF               |
1649       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1650       |EEEEEEE              |     G           H     |              FFFFFFF|
1651       E---------------------+----G--+-------+--H----+---------------------F
1652       |                     |    G             H    |                     |
1653       |                     |   G   |       |   H   |                     |
1654       |        southwest    |   G     south     H   |   southeast         |
1655       |                     |  G    |       |    H  |                     |
1656       |                     |  G                 H  |                     |
1657       |                     | G     |       |     H |                     |
1658       |                     | G                   H |                     |
1659       |                     |G      |       |      H|                     |
1660       |                     |G                     H|                     |
1661       +---------------------G-------|-------|-------H---------------------+
1662     */
1663
1664     if (y < A && y >= C)
1665         return prop_atoms.net_wm_moveresize_size_topleft;
1666     else if (y >= A && y >= B && a)
1667         return prop_atoms.net_wm_moveresize_size_top;
1668     else if (y < B && y >= D)
1669         return prop_atoms.net_wm_moveresize_size_topright;
1670     else if (y < C && y >= E && b)
1671         return prop_atoms.net_wm_moveresize_size_left;
1672     else if (y < D && y >= F && c)
1673         return prop_atoms.net_wm_moveresize_size_right;
1674     else if (y < E && y >= G)
1675         return prop_atoms.net_wm_moveresize_size_bottomleft;
1676     else if (y < G && y < H && d)
1677         return prop_atoms.net_wm_moveresize_size_bottom;
1678     else if (y >= H && y < F)
1679         return prop_atoms.net_wm_moveresize_size_bottomright;
1680     else
1681         return prop_atoms.net_wm_moveresize_move;
1682
1683 #undef X
1684 #undef A
1685 #undef B
1686 #undef C
1687 #undef D
1688 #undef E
1689 #undef F
1690 #undef G
1691 #undef H
1692 #undef a
1693 #undef b
1694 #undef c
1695 #undef d
1696 }
1697
1698 void action_moveresize(union ActionData *data)
1699 {
1700     ObClient *c = data->moveresize.any.c;
1701     guint32 corner;
1702
1703     if (!client_normal(c)) return;
1704
1705     if (data->moveresize.keyboard) {
1706         corner = (data->moveresize.move ?
1707                   prop_atoms.net_wm_moveresize_move_keyboard :
1708                   prop_atoms.net_wm_moveresize_size_keyboard);
1709     } else {
1710         corner = (data->moveresize.move ?
1711                   prop_atoms.net_wm_moveresize_move :
1712                   pick_corner(data->any.x, data->any.y,
1713                               c->frame->area.x, c->frame->area.y,
1714                               /* use the client size because the frame
1715                                  can be differently sized (shaded
1716                                  windows) and we want this based on the
1717                                  clients size */
1718                               c->area.width + c->frame->size.left +
1719                               c->frame->size.right,
1720                               c->area.height + c->frame->size.top +
1721                               c->frame->size.bottom));
1722         const gchar *c;
1723         if (corner == prop_atoms.net_wm_moveresize_size_topright)
1724             c = "topright";
1725         else if (corner == prop_atoms.net_wm_moveresize_size_top)
1726             c = "top";
1727         else if (corner == prop_atoms.net_wm_moveresize_size_topleft)
1728             c = "topleft";
1729         else if (corner == prop_atoms.net_wm_moveresize_size_right)
1730             c = "right";
1731         else if (corner == prop_atoms.net_wm_moveresize_move)
1732             c = "middle";
1733         else if (corner == prop_atoms.net_wm_moveresize_size_left)
1734             c = "left";
1735         else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
1736             c = "bottomright";
1737         else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
1738             c = "bottom";
1739         else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
1740             c = "bottomleft";
1741         ob_debug("corner: %s\n", c);
1742     }
1743
1744     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1745 }
1746
1747 void action_reconfigure(union ActionData *data)
1748 {
1749     ob_reconfigure();
1750 }
1751
1752 void action_restart(union ActionData *data)
1753 {
1754     ob_restart_other(data->execute.path);
1755 }
1756
1757 void action_exit(union ActionData *data)
1758 {
1759     ob_exit(0);
1760 }
1761
1762 void action_showmenu(union ActionData *data)
1763 {
1764     if (data->showmenu.name) {
1765         menu_show(data->showmenu.name, data->any.x, data->any.y,
1766                   data->any.button, data->showmenu.any.c);
1767     }
1768 }
1769
1770 void action_cycle_windows(union ActionData *data)
1771 {
1772     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1773        on us */
1774     event_halt_focus_delay();
1775
1776     focus_cycle(data->cycle.forward,
1777                 data->cycle.dock_windows,
1778                 data->cycle.linear, data->any.interactive,
1779                 data->cycle.dialog,
1780                 data->cycle.inter.final, data->cycle.inter.cancel);
1781 }
1782
1783 void action_directional_focus(union ActionData *data)
1784 {
1785     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1786        on us */
1787     event_halt_focus_delay();
1788
1789     focus_directional_cycle(data->interdiraction.direction,
1790                             data->interdiraction.dock_windows,
1791                             data->any.interactive,
1792                             data->interdiraction.dialog,
1793                             data->interdiraction.inter.final,
1794                             data->interdiraction.inter.cancel);
1795 }
1796
1797 void action_movetoedge(union ActionData *data)
1798 {
1799     gint x, y;
1800     ObClient *c = data->diraction.any.c;
1801
1802     x = c->frame->area.x;
1803     y = c->frame->area.y;
1804     
1805     switch(data->diraction.direction) {
1806     case OB_DIRECTION_NORTH:
1807         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1808                                            data->diraction.hang)
1809             - (data->diraction.hang ? c->frame->area.height : 0);
1810         break;
1811     case OB_DIRECTION_WEST:
1812         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1813                                            data->diraction.hang)
1814             - (data->diraction.hang ? c->frame->area.width : 0);
1815         break;
1816     case OB_DIRECTION_SOUTH:
1817         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1818                                            data->diraction.hang)
1819             - (data->diraction.hang ? 0 : c->frame->area.height);
1820         break;
1821     case OB_DIRECTION_EAST:
1822         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1823                                            data->diraction.hang)
1824             - (data->diraction.hang ? 0 : c->frame->area.width);
1825         break;
1826     default:
1827         g_assert_not_reached();
1828     }
1829     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1830     client_action_start(data);
1831     client_move(c, x, y);
1832     client_action_end(data);
1833 }
1834
1835 void action_growtoedge(union ActionData *data)
1836 {
1837     gint x, y, width, height, dest;
1838     ObClient *c = data->diraction.any.c;
1839     Rect *a;
1840
1841     a = screen_area(c->desktop);
1842     x = c->frame->area.x;
1843     y = c->frame->area.y;
1844     /* get the unshaded frame's dimensions..if it is shaded */
1845     width = c->area.width + c->frame->size.left + c->frame->size.right;
1846     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1847
1848     switch(data->diraction.direction) {
1849     case OB_DIRECTION_NORTH:
1850         if (c->shaded) break; /* don't allow vertical resize if shaded */
1851
1852         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1853         if (a->y == y)
1854             height = height / 2;
1855         else {
1856             height = c->frame->area.y + height - dest;
1857             y = dest;
1858         }
1859         break;
1860     case OB_DIRECTION_WEST:
1861         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1862         if (a->x == x)
1863             width = width / 2;
1864         else {
1865             width = c->frame->area.x + width - dest;
1866             x = dest;
1867         }
1868         break;
1869     case OB_DIRECTION_SOUTH:
1870         if (c->shaded) break; /* don't allow vertical resize if shaded */
1871
1872         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1873         if (a->y + a->height == y + c->frame->area.height) {
1874             height = c->frame->area.height / 2;
1875             y = a->y + a->height - height;
1876         } else
1877             height = dest - c->frame->area.y;
1878         y += (height - c->frame->area.height) % c->size_inc.height;
1879         height -= (height - c->frame->area.height) % c->size_inc.height;
1880         break;
1881     case OB_DIRECTION_EAST:
1882         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1883         if (a->x + a->width == x + c->frame->area.width) {
1884             width = c->frame->area.width / 2;
1885             x = a->x + a->width - width;
1886         } else
1887             width = dest - c->frame->area.x;
1888         x += (width - c->frame->area.width) % c->size_inc.width;
1889         width -= (width - c->frame->area.width) % c->size_inc.width;
1890         break;
1891     default:
1892         g_assert_not_reached();
1893     }
1894     width -= c->frame->size.left + c->frame->size.right;
1895     height -= c->frame->size.top + c->frame->size.bottom;
1896     frame_frame_gravity(c->frame, &x, &y, width, height);
1897     client_action_start(data);
1898     client_move_resize(c, x, y, width, height);
1899     client_action_end(data);
1900 }
1901
1902 void action_send_to_layer(union ActionData *data)
1903 {
1904     client_set_layer(data->layer.any.c, data->layer.layer);
1905 }
1906
1907 void action_toggle_layer(union ActionData *data)
1908 {
1909     ObClient *c = data->layer.any.c;
1910
1911     client_action_start(data);
1912     if (data->layer.layer < 0)
1913         client_set_layer(c, c->below ? 0 : -1);
1914     else if (data->layer.layer > 0)
1915         client_set_layer(c, c->above ? 0 : 1);
1916     client_action_end(data);
1917 }
1918
1919 void action_toggle_dockautohide(union ActionData *data)
1920 {
1921     config_dock_hide = !config_dock_hide;
1922     dock_configure();
1923 }
1924
1925 void action_toggle_show_desktop(union ActionData *data)
1926 {
1927     screen_show_desktop(!screen_showing_desktop);
1928 }
1929
1930 void action_show_desktop(union ActionData *data)
1931 {
1932     screen_show_desktop(TRUE);
1933 }
1934
1935 void action_unshow_desktop(union ActionData *data)
1936 {
1937     screen_show_desktop(FALSE);
1938 }
1939
1940 void action_break_chroot(union ActionData *data)
1941 {
1942     /* break out of one chroot */
1943     keyboard_reset_chains(1);
1944 }