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