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