add follow option back for sendto actions
[dana/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(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                     act->data.showmenu.name = parse_string(doc, n);
710             } else if (act->func == action_desktop) {
711                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
712                     act->data.desktop.desk = parse_int(doc, n);
713                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
714             } else if (act->func == action_send_to_desktop) {
715                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
716                     act->data.sendto.desk = parse_int(doc, n);
717                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
718             } else if (act->func == action_move_relative_horz ||
719                        act->func == action_move_relative_vert ||
720                        act->func == action_resize_relative_horz ||
721                        act->func == action_resize_relative_vert) {
722                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
723                     act->data.relative.delta = parse_int(doc, n);
724             } else if (act->func == action_desktop_dir) {
725                 if ((n = parse_find_node("wrap", node->xmlChildrenNode))) {
726                     act->data.desktopdir.wrap = parse_bool(doc, n);
727                 }
728             } else if (act->func == action_send_to_desktop) {
729                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
730                     act->data.sendto.follow = parse_bool(doc, n);
731             } else if (act->func == action_send_to_desktop_dir) {
732                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
733                     act->data.sendtodir.wrap = parse_bool(doc, n);
734                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
735                     act->data.sendtodir.follow = parse_bool(doc, n);
736             } else if (act->func == action_activate) {
737                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
738                     act->data.activate.here = parse_bool(doc, n);
739             }
740         }
741         g_free(actname);
742     }
743     return act;
744 }
745
746 void action_execute(union ActionData *data)
747 {
748     GError *e = NULL;
749     char *cmd;
750     if (data->execute.path) {
751         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
752         if (cmd) {
753             if (!g_spawn_command_line_async(cmd, &e)) {
754                 g_warning("failed to execute '%s': %s",
755                           cmd, e->message);
756             }
757         } else {
758             g_warning("failed to convert '%s' from utf8", data->execute.path);
759         }
760     }
761 }
762
763 void action_activate(union ActionData *data)
764 {
765     if (data->activate.c)
766         client_activate(data->activate.c, data->activate.here);
767 }
768
769 void action_focus(union ActionData *data)
770 {
771     if (data->client.c)
772         client_focus(data->client.c);
773 }
774
775 void action_unfocus (union ActionData *data)
776 {
777     if (data->client.c)
778         client_unfocus(data->client.c);
779 }
780
781 void action_iconify(union ActionData *data)
782 {
783     if (data->client.c)
784         client_iconify(data->client.c, TRUE, TRUE);
785 }
786
787 void action_raiselower(union ActionData *data)
788 {
789     ObClient *c = data->client.c;
790     GList *it;
791     gboolean raise = FALSE;
792
793     if (!c) return;
794
795     for (it = stacking_list; it; it = g_list_next(it)) {
796         ObClient *cit = it->data;
797
798         if (cit == c) break;
799         if (client_normal(cit) == client_normal(c) &&
800             cit->layer == c->layer &&
801             cit->frame->visible)
802         {
803             if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
804                 raise = TRUE;
805                 break;
806             }
807         }
808     }
809
810     if (raise)
811         stacking_raise(CLIENT_AS_WINDOW(c));
812     else
813         stacking_lower(CLIENT_AS_WINDOW(c));
814 }
815
816 void action_raise(union ActionData *data)
817 {
818     if (data->client.c)
819         stacking_raise(CLIENT_AS_WINDOW(data->client.c));
820 }
821
822 void action_unshaderaise(union ActionData *data)
823 {
824     if (data->client.c) {
825         if (data->client.c->shaded)
826             client_shade(data->client.c, FALSE);
827         else
828             stacking_raise(CLIENT_AS_WINDOW(data->client.c));
829     }
830 }
831
832 void action_shadelower(union ActionData *data)
833 {
834     if (data->client.c) {
835         if (data->client.c->shaded)
836             stacking_lower(CLIENT_AS_WINDOW(data->client.c));
837         else
838             client_shade(data->client.c, TRUE);
839     }
840 }
841
842 void action_lower(union ActionData *data)
843 {
844     if (data->client.c)
845         stacking_lower(CLIENT_AS_WINDOW(data->client.c));
846 }
847
848 void action_close(union ActionData *data)
849 {
850     if (data->client.c)
851         client_close(data->client.c);
852 }
853
854 void action_kill(union ActionData *data)
855 {
856     if (data->client.c)
857         client_kill(data->client.c);
858 }
859
860 void action_shade(union ActionData *data)
861 {
862     if (data->client.c)
863         client_shade(data->client.c, TRUE);
864 }
865
866 void action_unshade(union ActionData *data)
867 {
868     if (data->client.c)
869         client_shade(data->client.c, FALSE);
870 }
871
872 void action_toggle_shade(union ActionData *data)
873 {
874     if (data->client.c)
875         client_shade(data->client.c, !data->client.c->shaded);
876 }
877
878 void action_toggle_omnipresent(union ActionData *data)
879
880     if (data->client.c)
881         client_set_desktop(data->client.c,
882                            data->client.c->desktop == DESKTOP_ALL ?
883                            screen_desktop : DESKTOP_ALL, FALSE);
884 }
885
886 void action_move_relative_horz(union ActionData *data)
887 {
888     ObClient *c = data->relative.c;
889     if (c)
890         client_configure(c, OB_CORNER_TOPLEFT,
891                          c->area.x + data->relative.delta, c->area.y,
892                          c->area.width, c->area.height, TRUE, TRUE);
893 }
894
895 void action_move_relative_vert(union ActionData *data)
896 {
897     ObClient *c = data->relative.c;
898     if (c)
899         client_configure(c, OB_CORNER_TOPLEFT,
900                          c->area.x, c->area.y + data->relative.delta,
901                          c->area.width, c->area.height, TRUE, TRUE);
902 }
903
904 void action_resize_relative_horz(union ActionData *data)
905 {
906     ObClient *c = data->relative.c;
907     if (c)
908         client_configure(c, OB_CORNER_TOPLEFT, c->area.x, c->area.y,
909                          c->area.width +
910                          data->relative.delta * c->size_inc.width,
911                          c->area.height, TRUE, TRUE);
912 }
913
914 void action_resize_relative_vert(union ActionData *data)
915 {
916     ObClient *c = data->relative.c;
917     if (c && !c->shaded)
918         client_configure(c, OB_CORNER_TOPLEFT, c->area.x, c->area.y,
919                          c->area.width, c->area.height +
920                          data->relative.delta * c->size_inc.height,
921                          TRUE, TRUE);
922 }
923
924 void action_maximize_full(union ActionData *data)
925 {
926     if (data->client.c)
927         client_maximize(data->client.c, TRUE, 0, TRUE);
928 }
929
930 void action_unmaximize_full(union ActionData *data)
931 {
932     if (data->client.c)
933         client_maximize(data->client.c, FALSE, 0, TRUE);
934 }
935
936 void action_toggle_maximize_full(union ActionData *data)
937 {
938     if (data->client.c)
939         client_maximize(data->client.c,
940                         !(data->client.c->max_horz ||
941                           data->client.c->max_vert),
942                         0, TRUE);
943 }
944
945 void action_maximize_horz(union ActionData *data)
946 {
947     if (data->client.c)
948         client_maximize(data->client.c, TRUE, 1, TRUE);
949 }
950
951 void action_unmaximize_horz(union ActionData *data)
952 {
953     if (data->client.c)
954         client_maximize(data->client.c, FALSE, 1, TRUE);
955 }
956
957 void action_toggle_maximize_horz(union ActionData *data)
958 {
959     if (data->client.c)
960         client_maximize(data->client.c, !data->client.c->max_horz, 1, TRUE);
961 }
962
963 void action_maximize_vert(union ActionData *data)
964 {
965     if (data->client.c)
966         client_maximize(data->client.c, TRUE, 2, TRUE);
967 }
968
969 void action_unmaximize_vert(union ActionData *data)
970 {
971     if (data->client.c)
972         client_maximize(data->client.c, FALSE, 2, TRUE);
973 }
974
975 void action_toggle_maximize_vert(union ActionData *data)
976 {
977     if (data->client.c)
978         client_maximize(data->client.c, !data->client.c->max_vert, 2, TRUE);
979 }
980
981 void action_send_to_desktop(union ActionData *data)
982 {
983     ObClient *c = data->sendto.c;
984
985     if (!c || !client_normal(c)) return;
986
987     if (data->sendto.desk < screen_num_desktops ||
988         data->sendto.desk == DESKTOP_ALL) {
989         client_set_desktop(c, data->sendto.desk, data->sendtodir.follow);
990         if (data->sendto.follow)
991             screen_set_desktop(data->sendto.desk);
992     }
993 }
994
995 void action_desktop(union ActionData *data)
996 {
997     if (data->desktop.desk < screen_num_desktops ||
998         data->desktop.desk == DESKTOP_ALL)
999         screen_set_desktop(data->desktop.desk);
1000 }
1001
1002 void action_desktop_dir(union ActionData *data)
1003 {
1004     guint d;
1005
1006     d = screen_cycle_desktop(data->desktopdir.dir, data->desktopdir.wrap,
1007                              data->sendtodir.linear,
1008                              data->desktopdir.final, data->desktopdir.cancel);
1009     screen_set_desktop(d);
1010 }
1011
1012 void action_send_to_desktop_dir(union ActionData *data)
1013 {
1014     ObClient *c = data->sendtodir.c;
1015     guint d;
1016
1017     if (!c || !client_normal(c)) return;
1018
1019     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1020                              data->sendtodir.linear,
1021                              data->sendtodir.final, data->sendtodir.cancel);
1022     client_set_desktop(c, d, data->sendtodir.follow);
1023     if (data->sendtodir.follow)
1024         screen_set_desktop(d);
1025 }
1026
1027 #if 0
1028 void action_desktop_right(union ActionData *data)
1029 {
1030     guint r, c, d;
1031
1032     cur_row_col(&r, &c);
1033     ++c;
1034     if (c >= screen_desktop_layout.columns) {
1035         if (!data->desktopdir.wrap) return;
1036         c = 0;
1037     }
1038     d = translate_row_col(r, c);
1039     if (d >= screen_num_desktops) {
1040         if (!data->desktopdir.wrap) return;
1041         ++c;
1042     }
1043     d = translate_row_col(r, c);
1044     if (d < screen_num_desktops)
1045         screen_cycle_desktop(d, data->desktopdir.final,
1046                              data->desktopdir.cancel);
1047 }
1048
1049 void action_send_to_desktop_right(union ActionData *data)
1050 {
1051     ObClient *cl = data->sendto.c;
1052     guint r, c, d;
1053
1054     if (!cl || !client_normal(cl)) return;
1055
1056     cur_row_col(&r, &c);
1057     ++c;
1058     if (c >= screen_desktop_layout.columns) {
1059         if (!data->sendtodir.wrap) return;
1060         c = 0;
1061     }
1062     d = translate_row_col(r, c);
1063     if (d >= screen_num_desktops) {
1064         if (!data->sendtodir.wrap) return;
1065         ++c;
1066     }
1067     d = translate_row_col(r, c);
1068     if (d < screen_num_desktops) {
1069         client_set_desktop(cl, d, data->sendtodir.follow);
1070         if (data->sendtodir.follow)
1071             screen_cycle_desktop(d, data->desktopdir.final,
1072                                  data->desktopdir.cancel);
1073     }
1074 }
1075
1076 void action_desktop_left(union ActionData *data)
1077 {
1078     guint r, c, d;
1079
1080     cur_row_col(&r, &c);
1081     --c;
1082     if (c >= screen_desktop_layout.columns) {
1083         if (!data->desktopdir.wrap) return;
1084         c = screen_desktop_layout.columns - 1;
1085     }
1086     d = translate_row_col(r, c);
1087     if (d >= screen_num_desktops) {
1088         if (!data->desktopdir.wrap) return;
1089         --c;
1090     }
1091     d = translate_row_col(r, c);
1092     if (d < screen_num_desktops)
1093         screen_cycle_desktop(d, data->desktopdir.final,
1094                              data->desktopdir.cancel);
1095 }
1096
1097 void action_send_to_desktop_left(union ActionData *data)
1098 {
1099     ObClient *cl = data->sendto.c;
1100     guint r, c, d;
1101
1102     if (!cl || !client_normal(cl)) return;
1103
1104     cur_row_col(&r, &c);
1105     --c;
1106     if (c >= screen_desktop_layout.columns) {
1107         if (!data->sendtodir.wrap) return;
1108         c = screen_desktop_layout.columns - 1;
1109     }
1110     d = translate_row_col(r, c);
1111     if (d >= screen_num_desktops) {
1112         if (!data->sendtodir.wrap) return;
1113         --c;
1114     }
1115     d = translate_row_col(r, c);
1116     if (d < screen_num_desktops) {
1117         client_set_desktop(cl, d, data->sendtodir.follow);
1118         if (data->sendtodir.follow)
1119             screen_cycle_desktop(d, data->desktopdir.final,
1120                                  data->desktopdir.cancel);
1121     }
1122 }
1123
1124 void action_desktop_down(union ActionData *data)
1125 {
1126     guint r, c, d;
1127
1128     cur_row_col(&r, &c);
1129     ++r;
1130     if (r >= screen_desktop_layout.rows) {
1131         if (!data->desktopdir.wrap) return;
1132         r = 0;
1133     }
1134     d = translate_row_col(r, c);
1135     if (d >= screen_num_desktops) {
1136         if (!data->desktopdir.wrap) return;
1137         ++r;
1138     }
1139     d = translate_row_col(r, c);
1140     if (d < screen_num_desktops)
1141         screen_cycle_desktop(d, data->desktopdir.final,
1142                              data->desktopdir.cancel);
1143 }
1144
1145 void action_send_to_desktop_down(union ActionData *data)
1146 {
1147     guint r, c, d;
1148
1149     if (data->sendtodir.c) {
1150         cur_row_col(&r, &c);
1151         ++r;
1152         if (r >= screen_desktop_layout.rows) {
1153             if (!data->sendtodir.wrap) return;
1154             r = 0;
1155         }
1156         d = translate_row_col(r, c);
1157         if (d >= screen_num_desktops) {
1158             if (!data->sendtodir.wrap) return;
1159             ++r;
1160         }
1161         d = translate_row_col(r, c);
1162         if (d < screen_num_desktops) {
1163             client_set_desktop(data->sendtodir.c, d, data->sendtodir.follow);
1164             if (data->sendtodir.follow)
1165                 screen_cycle_desktop(d, data->desktopdir.final,
1166                                      data->desktopdir.cancel);
1167         }
1168     }
1169 }
1170
1171 void action_desktop_up(union ActionData *data)
1172 {
1173     guint r, c, d;
1174
1175     cur_row_col(&r, &c);
1176     --r;
1177     if (r >= screen_desktop_layout.rows) {
1178         if (!data->desktopdir.wrap) return;
1179         r = screen_desktop_layout.rows - 1;
1180     }
1181     d = translate_row_col(r, c);
1182     if (d >= screen_num_desktops) {
1183         if (!data->desktopdir.wrap) return;
1184         --r;
1185     }
1186     d = translate_row_col(r, c);
1187     if (d < screen_num_desktops)
1188         screen_cycle_desktop(d, data->desktopdir.final,
1189                              data->desktopdir.cancel);
1190 }
1191
1192 void action_send_to_desktop_up(union ActionData *data)
1193 {
1194     guint r, c, d;
1195
1196     if (data->sendtodir.c) {
1197         cur_row_col(&r, &c);
1198         --r;
1199         if (r >= screen_desktop_layout.rows) {
1200             if (!data->sendtodir.wrap) return;
1201             r = screen_desktop_layout.rows - 1;
1202         }
1203         d = translate_row_col(r, c);
1204         if (d >= screen_num_desktops) {
1205             if (!data->sendtodir.wrap) return;
1206             --r;
1207         }
1208         d = translate_row_col(r, c);
1209         if (d < screen_num_desktops) {
1210             client_set_desktop(data->sendtodir.c, d, data->sendtodir.follow);
1211             if (data->sendtodir.follow)
1212                 screen_cycle_desktop(d, data->desktopdir.final,
1213                                      data->desktopdir.cancel);
1214         }
1215     }
1216 }
1217 #endif
1218
1219 void action_toggle_decorations(union ActionData *data)
1220 {
1221     ObClient *c = data->client.c;;
1222
1223     if (!c) return;
1224
1225     c->decorate = !c->decorate;
1226     client_setup_decor_and_functions(c);
1227 }
1228
1229 void action_moveresize(union ActionData *data)
1230 {
1231     ObClient *c = data->moveresize.c;
1232
1233     if (!c || !client_normal(c)) return;
1234
1235     moveresize_start(c, data->moveresize.x, data->moveresize.y,
1236                      data->moveresize.button, data->moveresize.corner);
1237 }
1238
1239 void action_restart(union ActionData *data)
1240 {
1241     ob_restart_other(data->execute.path);
1242 }
1243
1244 void action_exit(union ActionData *data)
1245 {
1246     ob_exit();
1247 }
1248
1249 void action_showmenu(union ActionData *data)
1250 {
1251     if (data->showmenu.name) {
1252         menu_show(data->showmenu.name, data->showmenu.x, data->showmenu.y,
1253                   data->showmenu.c);
1254     }
1255 }
1256
1257 void action_cycle_windows(union ActionData *data)
1258 {
1259     ObClient *c;
1260     
1261     c = focus_cycle(data->cycle.forward, data->cycle.linear, data->cycle.final,
1262                     data->cycle.cancel);
1263 }
1264
1265 void action_directional_focus(union ActionData *data)
1266 {
1267     ObClient *nf;
1268
1269     if (!data->diraction.c)
1270         return;
1271     if ((nf = client_find_directional(data->diraction.c,
1272                                       data->diraction.direction)))
1273         client_activate(nf, FALSE);
1274 }
1275
1276 void action_movetoedge(union ActionData *data)
1277 {
1278     int x, y;
1279     ObClient *c = data->diraction.c;
1280
1281     if (!c)
1282         return;
1283     x = c->frame->area.x;
1284     y = c->frame->area.y;
1285     
1286     switch(data->diraction.direction) {
1287     case OB_DIRECTION_NORTH:
1288         y = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1289         break;
1290     case OB_DIRECTION_WEST:
1291         x = client_directional_edge_search(c, OB_DIRECTION_WEST);
1292         break;
1293     case OB_DIRECTION_SOUTH:
1294         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH) -
1295             c->frame->area.height;
1296         break;
1297     case OB_DIRECTION_EAST:
1298         x = client_directional_edge_search(c, OB_DIRECTION_EAST) -
1299             c->frame->area.width;
1300         break;
1301     default:
1302         g_assert_not_reached();
1303     }
1304     frame_frame_gravity(c->frame, &x, &y);
1305     client_configure(c, OB_CORNER_TOPLEFT,
1306                      x, y, c->area.width, c->area.height, TRUE, TRUE);
1307
1308 }
1309
1310 void action_growtoedge(union ActionData *data)
1311 {
1312     int x, y, width, height, dest;
1313     ObClient *c = data->diraction.c;
1314     Rect *a = screen_area(c->desktop);
1315
1316     if (!c)
1317         return;
1318     
1319     x = c->frame->area.x;
1320     y = c->frame->area.y;
1321     width = c->frame->area.width;
1322     height = c->frame->area.height;
1323
1324     switch(data->diraction.direction) {
1325     case OB_DIRECTION_NORTH:
1326         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH);
1327         if (a->y == y)
1328             height = c->frame->area.height / 2;
1329         else {
1330             height = c->frame->area.y + c->frame->area.height - dest;
1331             y = dest;
1332         }
1333         break;
1334     case OB_DIRECTION_WEST:
1335         dest = client_directional_edge_search(c, OB_DIRECTION_WEST);
1336         if (a->x == x)
1337             width = c->frame->area.width / 2;
1338         else {
1339             width = c->frame->area.x + c->frame->area.width - dest;
1340             x = dest;
1341         }
1342         break;
1343     case OB_DIRECTION_SOUTH:
1344         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH);
1345         if (a->y + a->height == y + c->frame->area.height) {
1346             height = c->frame->area.height / 2;
1347             y = a->y + a->height - height;
1348         } else
1349             height = dest - c->frame->area.y;
1350         y += (height - c->frame->area.height) % c->size_inc.height;
1351         height -= (height - c->frame->area.height) % c->size_inc.height;
1352         break;
1353     case OB_DIRECTION_EAST:
1354         dest = client_directional_edge_search(c, OB_DIRECTION_EAST);
1355         if (a->x + a->width == x + c->frame->area.width) {
1356             width = c->frame->area.width / 2;
1357             x = a->x + a->width - width;
1358         } else
1359             width = dest - c->frame->area.x;
1360         x += (width - c->frame->area.width) % c->size_inc.width;
1361         width -= (width - c->frame->area.width) % c->size_inc.width;
1362         break;
1363     default:
1364         g_assert_not_reached();
1365     }
1366     frame_frame_gravity(c->frame, &x, &y);
1367     width -= c->frame->size.left + c->frame->size.right;
1368     height -= c->frame->size.top + c->frame->size.bottom;
1369     client_configure(c, OB_CORNER_TOPLEFT, x, y, width, height, TRUE, TRUE);
1370 }
1371
1372 void action_send_to_layer(union ActionData *data)
1373 {
1374     if (data->layer.c)
1375         client_set_layer(data->layer.c, data->layer.layer);
1376 }
1377
1378 void action_toggle_layer(union ActionData *data)
1379 {
1380     ObClient *c = data->layer.c;
1381
1382     if (c) {
1383         if (data->layer.layer < 0)
1384             client_set_layer(c, c->below ? 0 : -1);
1385         else if (data->layer.layer > 0)
1386             client_set_layer(c, c->above ? 0 : 1);
1387     }
1388 }
1389
1390 void action_toggle_show_desktop(union ActionData *data)
1391 {
1392     screen_show_desktop(!screen_showing_desktop);
1393 }
1394
1395 void action_show_desktop(union ActionData *data)
1396 {
1397     screen_show_desktop(TRUE);
1398 }
1399
1400 void action_unshow_desktop(union ActionData *data)
1401 {
1402     screen_show_desktop(FALSE);
1403 }