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