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