]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/action.c
make interactive actions a type and not special cases.
[mikachu/openbox.git] / openbox / action.c
1 #include "debug.h"
2 #include "client.h"
3 #include "focus.h"
4 #include "moveresize.h"
5 #include "menu.h"
6 #include "prop.h"
7 #include "stacking.h"
8 #include "frame.h"
9 #include "screen.h"
10 #include "action.h"
11 #include "openbox.h"
12
13 #include <glib.h>
14
15 typedef struct ActionString {
16     char *name;
17     void (*func)(union ActionData *);
18     void (*setup)(ObAction *);
19 } ActionString;
20
21 ObAction *action_new(void (*func)(union ActionData *data))
22 {
23     ObAction *a = g_new0(ObAction, 1);
24     a->func = func;
25
26     return a;
27 }
28
29 void action_free(ObAction *a)
30 {
31     if (a == NULL) return;
32
33     /* deal with pointers */
34     if (a->func == action_execute || a->func == action_restart)
35         g_free(a->data.execute.path);
36     else if (a->func == action_showmenu)
37         g_free(a->data.showmenu.name);
38
39     g_free(a);
40 }
41
42 void setup_action_directional_focus_north(ObAction *a)
43 {
44     a->data.diraction.direction = OB_DIRECTION_NORTH;
45 }
46
47 void setup_action_directional_focus_east(ObAction *a)
48 {
49     a->data.diraction.direction = OB_DIRECTION_EAST;
50 }
51
52 void setup_action_directional_focus_south(ObAction *a)
53 {
54     a->data.diraction.direction = OB_DIRECTION_SOUTH;
55 }
56
57 void setup_action_directional_focus_west(ObAction *a)
58 {
59     a->data.diraction.direction = OB_DIRECTION_WEST;
60 }
61
62 void setup_action_directional_focus_northeast(ObAction *a)
63 {
64     a->data.diraction.direction = OB_DIRECTION_NORTHEAST;
65 }
66
67 void setup_action_directional_focus_southeast(ObAction *a)
68 {
69     a->data.diraction.direction = OB_DIRECTION_SOUTHEAST;
70 }
71
72 void setup_action_directional_focus_southwest(ObAction *a)
73 {
74     a->data.diraction.direction = OB_DIRECTION_SOUTHWEST;
75 }
76
77 void setup_action_directional_focus_northwest(ObAction *a)
78 {
79     a->data.diraction.direction = OB_DIRECTION_NORTHWEST;
80 }
81
82 void setup_action_send_to_desktop(ObAction *a)
83 {
84     a->data.sendto.follow = TRUE;
85 }
86
87 void setup_action_send_to_desktop_prev(ObAction *a)
88 {
89     a->data.sendtodir.inter.any.interactive = TRUE;
90     a->data.sendtodir.dir = OB_DIRECTION_WEST;
91     a->data.sendtodir.linear = TRUE;
92     a->data.sendtodir.wrap = TRUE;
93     a->data.sendtodir.follow = TRUE;
94 }
95
96 void setup_action_send_to_desktop_next(ObAction *a)
97 {
98     a->data.sendtodir.inter.any.interactive = TRUE;
99     a->data.sendtodir.dir = OB_DIRECTION_EAST;
100     a->data.sendtodir.linear = TRUE;
101     a->data.sendtodir.wrap = TRUE;
102     a->data.sendtodir.follow = TRUE;
103 }
104
105 void setup_action_send_to_desktop_left(ObAction *a)
106 {
107     a->data.sendtodir.inter.any.interactive = TRUE;
108     a->data.sendtodir.dir = OB_DIRECTION_WEST;
109     a->data.sendtodir.linear = FALSE;
110     a->data.sendtodir.wrap = TRUE;
111     a->data.sendtodir.follow = TRUE;
112 }
113
114 void setup_action_send_to_desktop_right(ObAction *a)
115 {
116     a->data.sendtodir.inter.any.interactive = TRUE;
117     a->data.sendtodir.dir = OB_DIRECTION_EAST;
118     a->data.sendtodir.linear = FALSE;
119     a->data.sendtodir.wrap = TRUE;
120     a->data.sendtodir.follow = TRUE;
121 }
122
123 void setup_action_send_to_desktop_up(ObAction *a)
124 {
125     a->data.sendtodir.inter.any.interactive = TRUE;
126     a->data.sendtodir.dir = OB_DIRECTION_NORTH;
127     a->data.sendtodir.linear = FALSE;
128     a->data.sendtodir.wrap = TRUE;
129     a->data.sendtodir.follow = TRUE;
130 }
131
132 void setup_action_send_to_desktop_down(ObAction *a)
133 {
134     a->data.sendtodir.inter.any.interactive = TRUE;
135     a->data.sendtodir.dir = OB_DIRECTION_SOUTH;
136     a->data.sendtodir.linear = FALSE;
137     a->data.sendtodir.wrap = TRUE;
138     a->data.sendtodir.follow = TRUE;
139 }
140
141 void setup_action_desktop_prev(ObAction *a)
142 {
143     a->data.desktopdir.inter.any.interactive = TRUE;
144     a->data.desktopdir.dir = OB_DIRECTION_WEST;
145     a->data.desktopdir.linear = TRUE;
146     a->data.desktopdir.wrap = TRUE;
147 }
148
149 void setup_action_desktop_next(ObAction *a)
150 {
151     a->data.desktopdir.inter.any.interactive = TRUE;
152     a->data.desktopdir.dir = OB_DIRECTION_EAST;
153     a->data.desktopdir.linear = TRUE;
154     a->data.desktopdir.wrap = TRUE;
155 }
156
157 void setup_action_desktop_left(ObAction *a)
158 {
159     a->data.desktopdir.inter.any.interactive = TRUE;
160     a->data.desktopdir.dir = OB_DIRECTION_WEST;
161     a->data.desktopdir.linear = FALSE;
162     a->data.desktopdir.wrap = TRUE;
163 }
164
165 void setup_action_desktop_right(ObAction *a)
166 {
167     a->data.desktopdir.inter.any.interactive = TRUE;
168     a->data.desktopdir.dir = OB_DIRECTION_EAST;
169     a->data.desktopdir.linear = FALSE;
170     a->data.desktopdir.wrap = TRUE;
171 }
172
173 void setup_action_desktop_up(ObAction *a)
174 {
175     a->data.desktopdir.inter.any.interactive = TRUE;
176     a->data.desktopdir.dir = OB_DIRECTION_NORTH;
177     a->data.desktopdir.linear = FALSE;
178     a->data.desktopdir.wrap = TRUE;
179 }
180
181 void setup_action_desktop_down(ObAction *a)
182 {
183     a->data.desktopdir.inter.any.interactive = TRUE;
184     a->data.desktopdir.dir = OB_DIRECTION_SOUTH;
185     a->data.desktopdir.linear = FALSE;
186     a->data.desktopdir.wrap = TRUE;
187 }
188
189 void setup_action_move_keyboard(ObAction *a)
190 {
191     a->data.moveresize.corner = prop_atoms.net_wm_moveresize_move_keyboard;
192 }
193
194 void setup_action_move(ObAction *a)
195 {
196     a->data.moveresize.corner = prop_atoms.net_wm_moveresize_move;
197 }
198
199 void setup_action_resize(ObAction *a)
200 {
201     a->data.moveresize.corner = prop_atoms.net_wm_moveresize_size_topleft;
202 }
203
204 void setup_action_resize_keyboard(ObAction *a)
205 {
206     a->data.moveresize.corner = prop_atoms.net_wm_moveresize_size_keyboard;
207 }
208
209 void setup_action_cycle_windows_linear_next(ObAction *a)
210 {
211     a->data.cycle.inter.any.interactive = TRUE;
212     a->data.cycle.linear = TRUE;
213     a->data.cycle.forward = TRUE;
214 }
215
216 void setup_action_cycle_windows_linear_previous(ObAction *a)
217 {
218     a->data.cycle.inter.any.interactive = TRUE;
219     a->data.cycle.linear = TRUE;
220     a->data.cycle.forward = FALSE;
221 }
222
223 void setup_action_cycle_windows_next(ObAction *a)
224 {
225     a->data.cycle.inter.any.interactive = TRUE;
226     a->data.cycle.linear = FALSE;
227     a->data.cycle.forward = TRUE;
228 }
229
230 void setup_action_cycle_windows_previous(ObAction *a)
231 {
232     a->data.cycle.inter.any.interactive = TRUE;
233     a->data.cycle.linear = FALSE;
234     a->data.cycle.forward = FALSE;
235 }
236
237 void setup_action_movetoedge_north(ObAction *a)
238 {
239     a->data.diraction.direction = OB_DIRECTION_NORTH;
240 }
241
242 void setup_action_movetoedge_south(ObAction *a)
243 {
244     a->data.diraction.direction = OB_DIRECTION_SOUTH;
245 }
246
247 void setup_action_movetoedge_east(ObAction *a)
248 {
249     a->data.diraction.direction = OB_DIRECTION_EAST;
250 }
251
252 void setup_action_movetoedge_west(ObAction *a)
253 {
254     a->data.diraction.direction = OB_DIRECTION_WEST;
255 }
256
257 void setup_action_growtoedge_north(ObAction *a)
258 {
259     a->data.diraction.direction = OB_DIRECTION_NORTH;
260 }
261
262 void setup_action_growtoedge_south(ObAction *a)
263 {
264     a->data.diraction.direction = OB_DIRECTION_SOUTH;
265 }
266
267 void setup_action_growtoedge_east(ObAction *a)
268 {
269     a->data.diraction.direction = OB_DIRECTION_EAST;
270 }
271
272 void setup_action_growtoedge_west(ObAction *a)
273 {
274     a->data.diraction.direction = OB_DIRECTION_WEST;
275 }
276
277 void setup_action_top_layer(ObAction *a)
278 {
279     a->data.layer.layer = 1;
280 }
281
282 void setup_action_normal_layer(ObAction *a)
283 {
284     a->data.layer.layer = 0;
285 }
286
287 void setup_action_bottom_layer(ObAction *a)
288 {
289     a->data.layer.layer = -1;
290 }
291
292 ActionString actionstrings[] =
293 {
294     {
295         "execute", 
296         action_execute, 
297         NULL
298     },
299     {
300         "directionalfocusnorth", 
301         action_directional_focus, 
302         setup_action_directional_focus_north
303     },
304     {
305         "directionalfocuseast", 
306         action_directional_focus, 
307         setup_action_directional_focus_east
308     },
309     {
310         "directionalfocussouth", 
311         action_directional_focus, 
312         setup_action_directional_focus_south
313     },
314     {
315         "directionalfocuswest",
316         action_directional_focus,
317         setup_action_directional_focus_west
318     },
319     {
320         "directionalfocusnortheast",
321         action_directional_focus,
322         setup_action_directional_focus_northeast
323     },
324     {
325         "directionalfocussoutheast",
326         action_directional_focus,
327         setup_action_directional_focus_southeast
328     },
329     {
330         "directionalfocussouthwest",
331         action_directional_focus,
332         setup_action_directional_focus_southwest
333     },
334     {
335         "directionalfocusnorthwest",
336         action_directional_focus,
337         setup_action_directional_focus_northwest
338     },
339     {
340         "activate",
341         action_activate,
342         NULL,
343     },
344     {
345         "focus",
346         action_focus,
347         NULL,
348     },
349     {
350         "unfocus",
351         action_unfocus,
352         NULL
353     },
354     {
355         "iconify",
356         action_iconify,
357         NULL
358     },
359     {
360         "raiselower",
361         action_raiselower,
362         NULL
363     },
364     {
365         "raise",
366         action_raise,
367         NULL
368     },
369     {
370         "lower",
371         action_lower,
372         NULL
373     },
374     {
375         "close",
376         action_close,
377         NULL
378     },
379     {
380         "kill",
381         action_kill,
382         NULL
383     },
384     {
385         "shadelower",
386         action_shadelower,
387         NULL
388     },
389     {
390         "unshaderaise",
391         action_unshaderaise,
392         NULL
393     },
394     {
395         "shade",
396         action_shade,
397         NULL
398     },
399     {
400         "unshade",
401         action_unshade,
402         NULL
403     },
404     {
405         "toggleshade",
406         action_toggle_shade,
407         NULL
408     },
409     {
410         "toggleomnipresent",
411         action_toggle_omnipresent,
412         NULL
413     },
414     {
415         "moverelativehorz",
416         action_move_relative_horz,
417         NULL
418     },
419     {
420         "moverelativevert",
421         action_move_relative_vert,
422         NULL
423     },
424     {
425         "resizerelativehorz",
426         action_resize_relative_horz,
427         NULL
428     },
429     {
430         "resizerelativevert",
431         action_resize_relative_vert,
432         NULL
433     },
434     {
435         "maximizefull",
436         action_maximize_full,
437         NULL
438     },
439     {
440         "unmaximizefull",
441         action_unmaximize_full,
442         NULL
443     },
444     {
445         "togglemaximizefull",
446         action_toggle_maximize_full,
447         NULL
448     },
449     {
450         "maximizehorz",
451         action_maximize_horz,
452         NULL
453     },
454     {
455         "unmaximizehorz",
456         action_unmaximize_horz,
457         NULL
458     },
459     {
460         "togglemaximizehorz",
461         action_toggle_maximize_horz,
462         NULL
463     },
464     {
465         "maximizevert",
466         action_maximize_vert,
467         NULL
468     },
469     {
470         "unmaximizevert",
471         action_unmaximize_vert,
472         NULL
473     },
474     {
475         "togglemaximizevert",
476         action_toggle_maximize_vert,
477         NULL
478     },
479     {
480         "sendtodesktop",
481         action_send_to_desktop,
482         setup_action_send_to_desktop
483     },
484     {
485         "sendtodesktopnext",
486         action_send_to_desktop_dir,
487         setup_action_send_to_desktop_next
488     },
489     {
490         "sendtodesktopprevious",
491         action_send_to_desktop_dir,
492         setup_action_send_to_desktop_prev
493     },
494     {
495         "sendtodesktopright",
496         action_send_to_desktop_dir,
497         setup_action_send_to_desktop_right
498     },
499     {
500         "sendtodesktopleft",
501         action_send_to_desktop_dir,
502         setup_action_send_to_desktop_left
503     },
504     {
505         "sendtodesktopup",
506         action_send_to_desktop_dir,
507         setup_action_send_to_desktop_up
508     },
509     {
510         "sendtodesktopdown",
511         action_send_to_desktop_dir,
512         setup_action_send_to_desktop_down
513     },
514     {
515         "desktop",
516         action_desktop,
517         NULL
518     },
519     {
520         "desktopnext",
521         action_desktop_dir,
522         setup_action_desktop_next
523     },
524     {
525         "desktopprevious",
526         action_desktop_dir,
527         setup_action_desktop_prev
528     },
529     {
530         "desktopright",
531         action_desktop_dir,
532         setup_action_desktop_right
533     },
534     {
535         "desktopleft",
536         action_desktop_dir,
537         setup_action_desktop_left
538     },
539     {
540         "desktopup",
541         action_desktop_dir,
542         setup_action_desktop_up
543     },
544     {
545         "desktopdown",
546         action_desktop_dir,
547         setup_action_desktop_down
548     },
549     {
550         "toggledecorations",
551         action_toggle_decorations,
552         NULL
553     },
554     {
555         "keyboardmove", 
556         action_moveresize,
557         setup_action_move_keyboard
558     },
559     {
560         "move",
561         action_moveresize,
562         setup_action_move
563     },
564     {
565         "resize",
566         action_moveresize,
567         setup_action_resize
568     },
569     {
570         "keyboardresize",
571         action_moveresize,
572         setup_action_resize_keyboard
573     },
574     {
575         "toggleshowdesktop",
576         action_toggle_show_desktop,
577         NULL
578     },
579     {
580         "showdesktop",
581         action_show_desktop,
582         NULL
583     },
584     {
585         "unshowdesktop",
586         action_unshow_desktop,
587         NULL
588     },
589     {
590         "restart",
591         action_restart,
592         NULL
593     },
594     {
595         "exit",
596         action_exit,
597         NULL
598     },
599     {
600         "showmenu",
601         action_showmenu,
602         NULL
603     },
604     {
605         "sendtotoplayer",
606         action_send_to_layer,
607         setup_action_top_layer
608     },
609     {
610         "togglealwaysontop",
611         action_toggle_layer,
612         setup_action_top_layer
613     },
614     {
615         "sendtonormallayer",
616         action_send_to_layer,
617         setup_action_normal_layer
618     },
619     {
620         "sendtobottomlayer",
621         action_send_to_layer,
622         setup_action_bottom_layer
623     },
624     {
625         "togglealwaysonbottom",
626         action_toggle_layer,
627         setup_action_bottom_layer
628     },
629     {
630         "nextwindowlinear",
631         action_cycle_windows,
632         setup_action_cycle_windows_linear_next
633     },
634     {
635         "previouswindowlinear",
636         action_cycle_windows,
637         setup_action_cycle_windows_linear_previous
638     },
639     {
640         "nextwindow",
641         action_cycle_windows,
642         setup_action_cycle_windows_next
643     },
644     {
645         "previouswindow",
646         action_cycle_windows,
647         setup_action_cycle_windows_previous
648     },
649     {
650         "movetoedgenorth",
651         action_movetoedge,
652         setup_action_movetoedge_north
653     },
654     {
655         "movetoedgesouth",
656         action_movetoedge,
657         setup_action_movetoedge_south
658     },
659     {
660         "movetoedgewest",
661         action_movetoedge,
662         setup_action_movetoedge_west
663     },
664     {
665         "movetoedgeeast",
666         action_movetoedge,
667         setup_action_movetoedge_east
668     },
669     {
670         "growtoedgenorth",
671         action_growtoedge,
672         setup_action_growtoedge_north
673     },
674     {
675         "growtoedgesouth",
676         action_growtoedge,
677         setup_action_growtoedge_south
678     },
679     {
680         "growtoedgewest",
681         action_growtoedge,
682         setup_action_growtoedge_west
683     },
684     {
685         "growtoedgeeast",
686         action_growtoedge,
687         setup_action_growtoedge_east
688     },
689     {
690         NULL,
691         NULL,
692         NULL
693     }
694 };
695
696 ObAction *action_from_string(char *name)
697 {
698     ObAction *a = NULL;
699     int i;
700
701     for (i = 0; actionstrings[i].name; i++)
702         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
703             a = action_new(actionstrings[i].func);
704             if (actionstrings[i].setup)
705                 actionstrings[i].setup(a);
706             break;
707         }
708     return a;
709 }
710
711 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node)
712 {
713     char *actname;
714     ObAction *act = NULL;
715     xmlNodePtr n;
716
717     if (parse_attr_string("name", node, &actname)) {
718         if ((act = action_from_string(actname))) {
719             if (act->func == action_execute || act->func == action_restart) {
720                 if ((n = parse_find_node("execute", node->xmlChildrenNode)))
721                     act->data.execute.path = parse_string(doc, n);
722             } else if (act->func == action_showmenu) {
723                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
724                     act->data.showmenu.name = parse_string(doc, n);
725             } else if (act->func == action_desktop) {
726                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
727                     act->data.desktop.desk = parse_int(doc, n);
728                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
729             } else if (act->func == action_send_to_desktop) {
730                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
731                     act->data.sendto.desk = parse_int(doc, n);
732                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
733             } else if (act->func == action_move_relative_horz ||
734                        act->func == action_move_relative_vert ||
735                        act->func == action_resize_relative_horz ||
736                        act->func == action_resize_relative_vert) {
737                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
738                     act->data.relative.delta = parse_int(doc, n);
739             } else if (act->func == action_desktop_dir) {
740                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
741                     act->data.desktopdir.wrap = parse_bool(doc, n);
742             } else if (act->func == action_send_to_desktop) {
743                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
744                     act->data.sendto.follow = parse_bool(doc, n);
745             } else if (act->func == action_send_to_desktop_dir) {
746                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
747                     act->data.sendtodir.wrap = parse_bool(doc, n);
748                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
749                     act->data.sendtodir.follow = parse_bool(doc, n);
750             } else if (act->func == action_activate) {
751                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
752                     act->data.activate.here = parse_bool(doc, n);
753             }
754
755             if (act->data.any.interactive) {
756                 if ((n = parse_find_node("interactive",node->xmlChildrenNode)))
757                     act->data.any.interactive = parse_bool(doc, n);
758             }
759         }
760         g_free(actname);
761     }
762     return act;
763 }
764
765 void action_execute(union ActionData *data)
766 {
767     GError *e = NULL;
768     char *cmd;
769     if (data->execute.path) {
770         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
771         if (cmd) {
772             if (!g_spawn_command_line_async(cmd, &e)) {
773                 g_warning("failed to execute '%s': %s",
774                           cmd, e->message);
775             }
776         } else {
777             g_warning("failed to convert '%s' from utf8", data->execute.path);
778         }
779     }
780 }
781
782 void action_activate(union ActionData *data)
783 {
784     if (data->activate.any.c)
785         client_activate(data->activate.any.c, data->activate.here);
786 }
787
788 void action_focus(union ActionData *data)
789 {
790     if (data->client.any.c)
791         client_focus(data->client.any.c);
792 }
793
794 void action_unfocus (union ActionData *data)
795 {
796     if (data->client.any.c)
797         client_unfocus(data->client.any.c);
798 }
799
800 void action_iconify(union ActionData *data)
801 {
802     if (data->client.any.c)
803         client_iconify(data->client.any.c, TRUE, TRUE);
804 }
805
806 void action_raiselower(union ActionData *data)
807 {
808     ObClient *c = data->client.any.c;
809     GList *it;
810     gboolean raise = FALSE;
811
812     if (!c) return;
813
814     for (it = stacking_list; it; it = g_list_next(it)) {
815         ObClient *cit = it->data;
816
817         if (cit == c) break;
818         if (client_normal(cit) == client_normal(c) &&
819             cit->layer == c->layer &&
820             cit->frame->visible)
821         {
822             if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
823                 raise = TRUE;
824                 break;
825             }
826         }
827     }
828
829     if (raise)
830         stacking_raise(CLIENT_AS_WINDOW(c));
831     else
832         stacking_lower(CLIENT_AS_WINDOW(c));
833 }
834
835 void action_raise(union ActionData *data)
836 {
837     if (data->client.any.c)
838         stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
839 }
840
841 void action_unshaderaise(union ActionData *data)
842 {
843     if (data->client.any.c) {
844         if (data->client.any.c->shaded)
845             client_shade(data->client.any.c, FALSE);
846         else
847             stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
848     }
849 }
850
851 void action_shadelower(union ActionData *data)
852 {
853     if (data->client.any.c) {
854         if (data->client.any.c->shaded)
855             stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
856         else
857             client_shade(data->client.any.c, TRUE);
858     }
859 }
860
861 void action_lower(union ActionData *data)
862 {
863     if (data->client.any.c)
864         stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
865 }
866
867 void action_close(union ActionData *data)
868 {
869     if (data->client.any.c)
870         client_close(data->client.any.c);
871 }
872
873 void action_kill(union ActionData *data)
874 {
875     if (data->client.any.c)
876         client_kill(data->client.any.c);
877 }
878
879 void action_shade(union ActionData *data)
880 {
881     if (data->client.any.c)
882         client_shade(data->client.any.c, TRUE);
883 }
884
885 void action_unshade(union ActionData *data)
886 {
887     if (data->client.any.c)
888         client_shade(data->client.any.c, FALSE);
889 }
890
891 void action_toggle_shade(union ActionData *data)
892 {
893     if (data->client.any.c)
894         client_shade(data->client.any.c, !data->client.any.c->shaded);
895 }
896
897 void action_toggle_omnipresent(union ActionData *data)
898
899     if (data->client.any.c)
900         client_set_desktop(data->client.any.c,
901                            data->client.any.c->desktop == DESKTOP_ALL ?
902                            screen_desktop : DESKTOP_ALL, FALSE);
903 }
904
905 void action_move_relative_horz(union ActionData *data)
906 {
907     ObClient *c = data->relative.any.c;
908     if (c)
909         client_configure(c, OB_CORNER_TOPLEFT,
910                          c->area.x + data->relative.delta, c->area.y,
911                          c->area.width, c->area.height, TRUE, TRUE);
912 }
913
914 void action_move_relative_vert(union ActionData *data)
915 {
916     ObClient *c = data->relative.any.c;
917     if (c)
918         client_configure(c, OB_CORNER_TOPLEFT,
919                          c->area.x, c->area.y + data->relative.delta,
920                          c->area.width, c->area.height, TRUE, TRUE);
921 }
922
923 void action_resize_relative_horz(union ActionData *data)
924 {
925     ObClient *c = data->relative.any.c;
926     if (c)
927         client_configure(c, OB_CORNER_TOPLEFT, c->area.x, c->area.y,
928                          c->area.width +
929                          data->relative.delta * c->size_inc.width,
930                          c->area.height, TRUE, TRUE);
931 }
932
933 void action_resize_relative_vert(union ActionData *data)
934 {
935     ObClient *c = data->relative.any.c;
936     if (c && !c->shaded)
937         client_configure(c, OB_CORNER_TOPLEFT, c->area.x, c->area.y,
938                          c->area.width, c->area.height +
939                          data->relative.delta * c->size_inc.height,
940                          TRUE, TRUE);
941 }
942
943 void action_maximize_full(union ActionData *data)
944 {
945     if (data->client.any.c)
946         client_maximize(data->client.any.c, TRUE, 0, TRUE);
947 }
948
949 void action_unmaximize_full(union ActionData *data)
950 {
951     if (data->client.any.c)
952         client_maximize(data->client.any.c, FALSE, 0, TRUE);
953 }
954
955 void action_toggle_maximize_full(union ActionData *data)
956 {
957     if (data->client.any.c)
958         client_maximize(data->client.any.c,
959                         !(data->client.any.c->max_horz ||
960                           data->client.any.c->max_vert),
961                         0, TRUE);
962 }
963
964 void action_maximize_horz(union ActionData *data)
965 {
966     if (data->client.any.c)
967         client_maximize(data->client.any.c, TRUE, 1, TRUE);
968 }
969
970 void action_unmaximize_horz(union ActionData *data)
971 {
972     if (data->client.any.c)
973         client_maximize(data->client.any.c, FALSE, 1, TRUE);
974 }
975
976 void action_toggle_maximize_horz(union ActionData *data)
977 {
978     if (data->client.any.c)
979         client_maximize(data->client.any.c,
980                         !data->client.any.c->max_horz, 1, TRUE);
981 }
982
983 void action_maximize_vert(union ActionData *data)
984 {
985     if (data->client.any.c)
986         client_maximize(data->client.any.c, TRUE, 2, TRUE);
987 }
988
989 void action_unmaximize_vert(union ActionData *data)
990 {
991     if (data->client.any.c)
992         client_maximize(data->client.any.c, FALSE, 2, TRUE);
993 }
994
995 void action_toggle_maximize_vert(union ActionData *data)
996 {
997     if (data->client.any.c)
998         client_maximize(data->client.any.c, !data->client.any.c->max_vert, 2, TRUE);
999 }
1000
1001 void action_send_to_desktop(union ActionData *data)
1002 {
1003     ObClient *c = data->sendto.any.c;
1004
1005     if (!c || !client_normal(c)) return;
1006
1007     if (data->sendto.desk < screen_num_desktops ||
1008         data->sendto.desk == DESKTOP_ALL) {
1009         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1010         if (data->sendto.follow)
1011             screen_set_desktop(data->sendto.desk);
1012     }
1013 }
1014
1015 void action_desktop(union ActionData *data)
1016 {
1017     if (data->desktop.desk < screen_num_desktops ||
1018         data->desktop.desk == DESKTOP_ALL)
1019         screen_set_desktop(data->desktop.desk);
1020 }
1021
1022 void action_desktop_dir(union ActionData *data)
1023 {
1024     guint d;
1025
1026     d = screen_cycle_desktop(data->desktopdir.dir,
1027                              data->desktopdir.wrap,
1028                              data->sendtodir.linear,
1029                              data->desktopdir.inter.any.interactive,
1030                              data->desktopdir.inter.final,
1031                              data->desktopdir.inter.cancel);
1032     screen_set_desktop(d);
1033 }
1034
1035 void action_send_to_desktop_dir(union ActionData *data)
1036 {
1037     ObClient *c = data->sendtodir.inter.any.c;
1038     guint d;
1039
1040     if (!c || !client_normal(c)) return;
1041
1042     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1043                              data->sendtodir.linear,
1044                              data->sendtodir.inter.any.interactive,
1045                              data->sendtodir.inter.final,
1046                              data->sendtodir.inter.cancel);
1047     client_set_desktop(c, d, data->sendtodir.follow);
1048     if (data->sendtodir.follow)
1049         screen_set_desktop(d);
1050 }
1051
1052 void action_toggle_decorations(union ActionData *data)
1053 {
1054     ObClient *c = data->client.any.c;
1055
1056     if (!c) return;
1057
1058     c->decorate = !c->decorate;
1059     client_setup_decor_and_functions(c);
1060 }
1061
1062 void action_moveresize(union ActionData *data)
1063 {
1064     ObClient *c = data->moveresize.any.c;
1065
1066     if (!c || !client_normal(c)) return;
1067
1068     moveresize_start(c, data->moveresize.x, data->moveresize.y,
1069                      data->moveresize.button, data->moveresize.corner);
1070 }
1071
1072 void action_restart(union ActionData *data)
1073 {
1074     ob_restart_other(data->execute.path);
1075 }
1076
1077 void action_exit(union ActionData *data)
1078 {
1079     ob_exit();
1080 }
1081
1082 void action_showmenu(union ActionData *data)
1083 {
1084     if (data->showmenu.name) {
1085         menu_show(data->showmenu.name, data->showmenu.x, data->showmenu.y,
1086                   data->showmenu.any.c);
1087     }
1088 }
1089
1090 void action_cycle_windows(union ActionData *data)
1091 {
1092     ObClient *c;
1093     
1094     c = focus_cycle(data->cycle.forward, data->cycle.linear,
1095                     data->cycle.inter.any.interactive,
1096                     data->cycle.inter.final, data->cycle.inter.cancel);
1097 }
1098
1099 void action_directional_focus(union ActionData *data)
1100 {
1101     ObClient *nf;
1102
1103     if (!data->diraction.any.c)
1104         return;
1105     if ((nf = client_find_directional(data->diraction.any.c,
1106                                       data->diraction.direction)))
1107         client_activate(nf, FALSE);
1108 }
1109
1110 void action_movetoedge(union ActionData *data)
1111 {
1112     int x, y;
1113     ObClient *c = data->diraction.any.c;
1114
1115     if (!c)
1116         return;
1117     x = c->frame->area.x;
1118     y = c->frame->area.y;
1119     
1120     switch(data->diraction.direction) {
1121     case OB_DIRECTION_NORTH:
1122         y = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1123         break;
1124     case OB_DIRECTION_WEST:
1125         x = client_directional_edge_search(c, OB_DIRECTION_WEST);
1126         break;
1127     case OB_DIRECTION_SOUTH:
1128         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH) -
1129             c->frame->area.height;
1130         break;
1131     case OB_DIRECTION_EAST:
1132         x = client_directional_edge_search(c, OB_DIRECTION_EAST) -
1133             c->frame->area.width;
1134         break;
1135     default:
1136         g_assert_not_reached();
1137     }
1138     frame_frame_gravity(c->frame, &x, &y);
1139     client_configure(c, OB_CORNER_TOPLEFT,
1140                      x, y, c->area.width, c->area.height, TRUE, TRUE);
1141
1142 }
1143
1144 void action_growtoedge(union ActionData *data)
1145 {
1146     int x, y, width, height, dest;
1147     ObClient *c = data->diraction.any.c;
1148     Rect *a = screen_area(c->desktop);
1149
1150     if (!c)
1151         return;
1152     
1153     x = c->frame->area.x;
1154     y = c->frame->area.y;
1155     width = c->frame->area.width;
1156     height = c->frame->area.height;
1157
1158     switch(data->diraction.direction) {
1159     case OB_DIRECTION_NORTH:
1160         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1161         if (a->y == y)
1162             height = c->frame->area.height / 2;
1163         else {
1164             height = c->frame->area.y + c->frame->area.height - dest;
1165             y = dest;
1166         }
1167         break;
1168     case OB_DIRECTION_WEST:
1169         dest = client_directional_edge_search(c, OB_DIRECTION_WEST);
1170         if (a->x == x)
1171             width = c->frame->area.width / 2;
1172         else {
1173             width = c->frame->area.x + c->frame->area.width - dest;
1174             x = dest;
1175         }
1176         break;
1177     case OB_DIRECTION_SOUTH:
1178         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH);
1179         if (a->y + a->height == y + c->frame->area.height) {
1180             height = c->frame->area.height / 2;
1181             y = a->y + a->height - height;
1182         } else
1183             height = dest - c->frame->area.y;
1184         y += (height - c->frame->area.height) % c->size_inc.height;
1185         height -= (height - c->frame->area.height) % c->size_inc.height;
1186         break;
1187     case OB_DIRECTION_EAST:
1188         dest = client_directional_edge_search(c, OB_DIRECTION_EAST);
1189         if (a->x + a->width == x + c->frame->area.width) {
1190             width = c->frame->area.width / 2;
1191             x = a->x + a->width - width;
1192         } else
1193             width = dest - c->frame->area.x;
1194         x += (width - c->frame->area.width) % c->size_inc.width;
1195         width -= (width - c->frame->area.width) % c->size_inc.width;
1196         break;
1197     default:
1198         g_assert_not_reached();
1199     }
1200     frame_frame_gravity(c->frame, &x, &y);
1201     width -= c->frame->size.left + c->frame->size.right;
1202     height -= c->frame->size.top + c->frame->size.bottom;
1203     client_configure(c, OB_CORNER_TOPLEFT, x, y, width, height, TRUE, TRUE);
1204 }
1205
1206 void action_send_to_layer(union ActionData *data)
1207 {
1208     if (data->layer.any.c)
1209         client_set_layer(data->layer.any.c, data->layer.layer);
1210 }
1211
1212 void action_toggle_layer(union ActionData *data)
1213 {
1214     ObClient *c = data->layer.any.c;
1215
1216     if (c) {
1217         if (data->layer.layer < 0)
1218             client_set_layer(c, c->below ? 0 : -1);
1219         else if (data->layer.layer > 0)
1220             client_set_layer(c, c->above ? 0 : 1);
1221     }
1222 }
1223
1224 void action_toggle_show_desktop(union ActionData *data)
1225 {
1226     screen_show_desktop(!screen_showing_desktop);
1227 }
1228
1229 void action_show_desktop(union ActionData *data)
1230 {
1231     screen_show_desktop(TRUE);
1232 }
1233
1234 void action_unshow_desktop(union ActionData *data)
1235 {
1236     screen_show_desktop(FALSE);
1237 }