this is a big one! im putting stats in here just cuz!
[dana/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         "toggleshowdesktop",
436         action_toggle_show_desktop,
437         NULL
438     },
439     {
440         "showdesktop",
441         action_show_desktop,
442         NULL
443     },
444     {
445         "unshowdesktop",
446         action_unshow_desktop,
447         NULL
448     },
449     {
450         "restart",
451         action_restart,
452         NULL
453     },
454     {
455         "exit",
456         action_exit,
457         NULL
458     },
459     {
460         "showmenu",
461         action_showmenu,
462         NULL
463     },
464     {
465         "sendtotoplayer",
466         action_send_to_layer,
467         setup_action_top_layer
468     },
469     {
470         "togglealwaysontop",
471         action_toggle_layer,
472         setup_action_top_layer
473     },
474     {
475         "sendtonormallayer",
476         action_send_to_layer,
477         setup_action_normal_layer
478     },
479     {
480         "sendtobottomlayer",
481         action_send_to_layer,
482         setup_action_bottom_layer
483     },
484     {
485         "togglealwaysonbottom",
486         action_toggle_layer,
487         setup_action_bottom_layer
488     },
489     {
490         "nextwindowlinear",
491         action_cycle_windows,
492         setup_action_cycle_windows_linear_next
493     },
494     {
495         "previouswindowlinear",
496         action_cycle_windows,
497         setup_action_cycle_windows_linear_previous
498     },
499     {
500         "nextwindow",
501         action_cycle_windows,
502         setup_action_cycle_windows_next
503     },
504     {
505         "previouswindow",
506         action_cycle_windows,
507         setup_action_cycle_windows_previous
508     },
509     {
510         "movetoedgenorth",
511         action_movetoedge,
512         setup_action_movetoedge_north
513     },
514     {
515         "movetoedgesouth",
516         action_movetoedge,
517         setup_action_movetoedge_south
518     },
519     {
520         "movetoedgewest",
521         action_movetoedge,
522         setup_action_movetoedge_west
523     },
524     {
525         "movetoedgeeast",
526         action_movetoedge,
527         setup_action_movetoedge_east
528     },
529     {
530         NULL,
531         NULL,
532         NULL
533     }
534 };
535
536 Action *action_from_string(char *name)
537 {
538     Action *a = NULL;
539     int i;
540
541     for (i = 0; actionstrings[i].name; i++)
542         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
543             a = action_new(actionstrings[i].func);
544             if (actionstrings[i].setup)
545                 actionstrings[i].setup(a);
546             break;
547         }
548     return a;
549 }
550
551 Action *action_parse(xmlDocPtr doc, xmlNodePtr node)
552 {
553     char *actname;
554     Action *act = NULL;
555     xmlNodePtr n;
556
557     if (parse_attr_string("name", node, &actname)) {
558         if ((act = action_from_string(actname))) {
559             if (act->func == action_execute || act->func == action_restart) {
560                 if ((n = parse_find_node("execute", node->xmlChildrenNode)))
561                     act->data.execute.path = parse_string(doc, n);
562             } else if (act->func == action_showmenu) {
563                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
564                     act->data.showmenu.name = parse_string(doc, n);
565             } else if (act->func == action_desktop) {
566                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
567                     act->data.desktop.desk = parse_int(doc, n);
568                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
569             } else if (act->func == action_send_to_desktop) {
570                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
571                     act->data.sendto.desk = parse_int(doc, n);
572                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
573             } else if (act->func == action_move_relative_horz ||
574                        act->func == action_move_relative_vert ||
575                        act->func == action_resize_relative_horz ||
576                        act->func == action_resize_relative_vert) {
577                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
578                     act->data.relative.delta = parse_int(doc, n);
579             } else if (act->func == action_desktop_right ||
580                        act->func == action_desktop_left ||
581                        act->func == action_desktop_up ||
582                        act->func == action_desktop_down) {
583                 if ((n = parse_find_node("wrap", node->xmlChildrenNode))) {
584                     g_message("WRAP %d", parse_bool(doc, n));
585                     act->data.desktopdir.wrap = parse_bool(doc, n);
586                 }
587             } else if (act->func == action_send_to_desktop_right ||
588                        act->func == action_send_to_desktop_left ||
589                        act->func == action_send_to_desktop_up ||
590                        act->func == action_send_to_desktop_down) {
591                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
592                     act->data.sendtodir.wrap = parse_bool(doc, n);
593                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
594                     act->data.sendtodir.follow = parse_bool(doc, n);
595             }
596         }
597     }
598     return act;
599 }
600
601 void action_execute(union ActionData *data)
602 {
603     GError *e = NULL;
604     char *cmd;
605     if (data->execute.path) {
606         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
607         if (cmd) {
608             if (!g_spawn_command_line_async(cmd, &e)) {
609                 g_warning("failed to execute '%s': %s",
610                           cmd, e->message);
611             }
612         } else {
613             g_warning("failed to convert '%s' from utf8", data->execute.path);
614         }
615     }
616 }
617
618 void action_focus(union ActionData *data)
619 {
620     if (data->client.c)
621         client_focus(data->client.c);
622 }
623
624 void action_unfocus (union ActionData *data)
625 {
626     if (data->client.c)
627         client_unfocus(data->client.c);
628 }
629
630 void action_iconify(union ActionData *data)
631 {
632     if (data->client.c)
633         client_iconify(data->client.c, TRUE, TRUE);
634 }
635
636 void action_focusraise(union ActionData *data)
637 {
638     if (data->client.c) {
639         client_focus(data->client.c);
640         stacking_raise(CLIENT_AS_WINDOW(data->client.c));
641     }
642 }
643
644 void action_raise(union ActionData *data)
645 {
646     if (data->client.c)
647         stacking_raise(CLIENT_AS_WINDOW(data->client.c));
648 }
649
650 void action_unshaderaise(union ActionData *data)
651 {
652     if (data->client.c) {
653         if (data->client.c->shaded)
654             client_shade(data->client.c, FALSE);
655         else
656             stacking_raise(CLIENT_AS_WINDOW(data->client.c));
657     }
658 }
659
660 void action_shadelower(union ActionData *data)
661 {
662     if (data->client.c) {
663         if (data->client.c->shaded)
664             stacking_lower(CLIENT_AS_WINDOW(data->client.c));
665         else
666             client_shade(data->client.c, TRUE);
667     }
668 }
669
670 void action_lower(union ActionData *data)
671 {
672     if (data->client.c)
673         stacking_lower(CLIENT_AS_WINDOW(data->client.c));
674 }
675
676 void action_close(union ActionData *data)
677 {
678     if (data->client.c)
679         client_close(data->client.c);
680 }
681
682 void action_kill(union ActionData *data)
683 {
684     if (data->client.c)
685         client_kill(data->client.c);
686 }
687
688 void action_shade(union ActionData *data)
689 {
690     if (data->client.c)
691         client_shade(data->client.c, TRUE);
692 }
693
694 void action_unshade(union ActionData *data)
695 {
696     if (data->client.c)
697         client_shade(data->client.c, FALSE);
698 }
699
700 void action_toggle_shade(union ActionData *data)
701 {
702     if (data->client.c)
703         client_shade(data->client.c, !data->client.c->shaded);
704 }
705
706 void action_toggle_omnipresent(union ActionData *data)
707
708     if (data->client.c)
709         client_set_desktop(data->client.c,
710                            data->client.c->desktop == DESKTOP_ALL ?
711                            screen_desktop : DESKTOP_ALL, FALSE);
712 }
713
714 void action_move_relative_horz(union ActionData *data)
715 {
716     Client *c = data->relative.c;
717     if (c)
718         client_configure(c, Corner_TopLeft,
719                          c->area.x + data->relative.delta, c->area.y,
720                          c->area.width, c->area.height, TRUE, TRUE);
721 }
722
723 void action_move_relative_vert(union ActionData *data)
724 {
725     Client *c = data->relative.c;
726     if (c)
727         client_configure(c, Corner_TopLeft,
728                          c->area.x, c->area.y + data->relative.delta,
729                          c->area.width, c->area.height, TRUE, TRUE);
730 }
731
732 void action_resize_relative_horz(union ActionData *data)
733 {
734     Client *c = data->relative.c;
735     if (c)
736         client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
737                          c->area.width +
738                          data->relative.delta * c->size_inc.width,
739                          c->area.height, TRUE, TRUE);
740 }
741
742 void action_resize_relative_vert(union ActionData *data)
743 {
744     Client *c = data->relative.c;
745     if (c && !c->shaded)
746         client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
747                          c->area.width, c->area.height +
748                          data->relative.delta * c->size_inc.height,
749                          TRUE, TRUE);
750 }
751
752 void action_maximize_full(union ActionData *data)
753 {
754     if (data->client.c)
755         client_maximize(data->client.c, TRUE, 0, TRUE);
756 }
757
758 void action_unmaximize_full(union ActionData *data)
759 {
760     if (data->client.c)
761         client_maximize(data->client.c, FALSE, 0, TRUE);
762 }
763
764 void action_toggle_maximize_full(union ActionData *data)
765 {
766     if (data->client.c)
767         client_maximize(data->client.c,
768                         !(data->client.c->max_horz ||
769                           data->client.c->max_vert),
770                         0, TRUE);
771 }
772
773 void action_maximize_horz(union ActionData *data)
774 {
775     if (data->client.c)
776         client_maximize(data->client.c, TRUE, 1, TRUE);
777 }
778
779 void action_unmaximize_horz(union ActionData *data)
780 {
781     if (data->client.c)
782         client_maximize(data->client.c, FALSE, 1, TRUE);
783 }
784
785 void action_toggle_maximize_horz(union ActionData *data)
786 {
787     if (data->client.c)
788         client_maximize(data->client.c, !data->client.c->max_horz, 1, TRUE);
789 }
790
791 void action_maximize_vert(union ActionData *data)
792 {
793     if (data->client.c)
794         client_maximize(data->client.c, TRUE, 2, TRUE);
795 }
796
797 void action_unmaximize_vert(union ActionData *data)
798 {
799     if (data->client.c)
800         client_maximize(data->client.c, FALSE, 2, TRUE);
801 }
802
803 void action_toggle_maximize_vert(union ActionData *data)
804 {
805     if (data->client.c)
806         client_maximize(data->client.c, !data->client.c->max_vert, 2, TRUE);
807 }
808
809 void action_send_to_desktop(union ActionData *data)
810 {
811     if (data->sendto.c) {
812         if (data->sendto.desk < screen_num_desktops ||
813             data->sendto.desk == DESKTOP_ALL) {
814             client_set_desktop(data->desktop.c,
815                                data->sendto.desk, data->sendto.follow);
816             if (data->sendto.follow) screen_set_desktop(data->sendto.desk);
817         }
818     }
819 }
820
821 void action_desktop(union ActionData *data)
822 {
823     if (data->desktop.desk < screen_num_desktops ||
824         data->desktop.desk == DESKTOP_ALL)
825         screen_set_desktop(data->desktop.desk);
826 }
827
828 static void cur_row_col(guint *r, guint *c)
829 {
830     switch (screen_desktop_layout.orientation) {
831     case Orientation_Horz:
832         switch (screen_desktop_layout.start_corner) {
833         case Corner_TopLeft:
834             *r = screen_desktop / screen_desktop_layout.columns;
835             *c = screen_desktop % screen_desktop_layout.columns;
836             break;
837         case Corner_BottomLeft:
838             *r = screen_desktop_layout.rows - 1 -
839                 screen_desktop / screen_desktop_layout.columns;
840             *c = screen_desktop % screen_desktop_layout.columns;
841             break;
842         case Corner_TopRight:
843             *r = screen_desktop / screen_desktop_layout.columns;
844             *c = screen_desktop_layout.columns - 1 -
845                 screen_desktop % screen_desktop_layout.columns;
846             break;
847         case Corner_BottomRight:
848             *r = screen_desktop_layout.rows - 1 -
849                 screen_desktop / screen_desktop_layout.columns;
850             *c = screen_desktop_layout.columns - 1 -
851                 screen_desktop % screen_desktop_layout.columns;
852             break;
853         }
854         break;
855     case Orientation_Vert:
856         switch (screen_desktop_layout.start_corner) {
857         case Corner_TopLeft:
858             *r = screen_desktop % screen_desktop_layout.rows;
859             *c = screen_desktop / screen_desktop_layout.rows;
860             break;
861         case Corner_BottomLeft:
862             *r = screen_desktop_layout.rows - 1 -
863                 screen_desktop % screen_desktop_layout.rows;
864             *c = screen_desktop / screen_desktop_layout.rows;
865             break;
866         case Corner_TopRight:
867             *r = screen_desktop % screen_desktop_layout.rows;
868             *c = screen_desktop_layout.columns - 1 -
869                 screen_desktop / screen_desktop_layout.rows;
870             break;
871         case Corner_BottomRight:
872             *r = screen_desktop_layout.rows - 1 -
873                 screen_desktop % screen_desktop_layout.rows;
874             *c = screen_desktop_layout.columns - 1 -
875                 screen_desktop / screen_desktop_layout.rows;
876             break;
877         }
878         break;
879     }
880 }
881
882 static guint translate_row_col(guint r, guint c)
883 {
884     switch (screen_desktop_layout.orientation) {
885     case Orientation_Horz:
886         switch (screen_desktop_layout.start_corner) {
887         case Corner_TopLeft:
888             return r % screen_desktop_layout.rows *
889                 screen_desktop_layout.columns +
890                 c % screen_desktop_layout.columns;
891         case Corner_BottomLeft:
892             return (screen_desktop_layout.rows - 1 -
893                     r % screen_desktop_layout.rows) *
894                 screen_desktop_layout.columns +
895                 c % screen_desktop_layout.columns;
896         case Corner_TopRight:
897             return r % screen_desktop_layout.rows *
898                 screen_desktop_layout.columns +
899                 (screen_desktop_layout.columns - 1 -
900                  c % screen_desktop_layout.columns);
901         case Corner_BottomRight:
902             return (screen_desktop_layout.rows - 1 -
903                     r % screen_desktop_layout.rows) *
904                 screen_desktop_layout.columns +
905                 (screen_desktop_layout.columns - 1 -
906                  c % screen_desktop_layout.columns);
907         }
908     case Orientation_Vert:
909         switch (screen_desktop_layout.start_corner) {
910         case Corner_TopLeft:
911             return c % screen_desktop_layout.columns *
912                 screen_desktop_layout.rows +
913                 r % screen_desktop_layout.rows;
914         case Corner_BottomLeft:
915             return c % screen_desktop_layout.columns *
916                 screen_desktop_layout.rows +
917                 (screen_desktop_layout.rows - 1 -
918                  r % screen_desktop_layout.rows);
919         case Corner_TopRight:
920             return (screen_desktop_layout.columns - 1 -
921                     c % screen_desktop_layout.columns) *
922                 screen_desktop_layout.rows +
923                 r % screen_desktop_layout.rows;
924         case Corner_BottomRight:
925             return (screen_desktop_layout.columns - 1 -
926                     c % screen_desktop_layout.columns) *
927                 screen_desktop_layout.rows +
928                 (screen_desktop_layout.rows - 1 -
929                  r % screen_desktop_layout.rows);
930         }
931     }
932     g_assert_not_reached();
933     return 0;
934 }
935
936 void action_desktop_right(union ActionData *data)
937 {
938     guint r, c, d;
939
940     cur_row_col(&r, &c);
941     ++c;
942     if (c >= screen_desktop_layout.columns) {
943         if (!data->desktopdir.wrap) return;
944         c = 0;
945     }
946     d = translate_row_col(r, c);
947     if (d >= screen_num_desktops) {
948         if (!data->desktopdir.wrap) return;
949         ++c;
950     }
951     d = translate_row_col(r, c);
952     if (d < screen_num_desktops)
953         screen_set_desktop(d);
954 }
955
956 void action_send_to_desktop_right(union ActionData *data)
957 {
958     guint r, c, d;
959
960     if (data->sendtodir.c) {
961         cur_row_col(&r, &c);
962         ++c;
963         if (c >= screen_desktop_layout.columns) {
964             if (!data->sendtodir.wrap) return;
965             c = 0;
966         }
967         d = translate_row_col(r, c);
968         if (d >= screen_num_desktops) {
969             if (!data->sendtodir.wrap) return;
970             ++c;
971         }
972         d = translate_row_col(r, c);
973         if (d < screen_num_desktops) {
974             client_set_desktop(data->sendtodir.c, d, data->sendtodir.follow);
975             if (data->sendtodir.follow) screen_set_desktop(d);
976         }
977     }
978 }
979
980 void action_desktop_left(union ActionData *data)
981 {
982     guint r, c, d;
983
984     cur_row_col(&r, &c);
985     --c;
986     if (c >= screen_desktop_layout.columns) {
987         if (!data->desktopdir.wrap) return;
988         c = screen_desktop_layout.columns - 1;
989     }
990     d = translate_row_col(r, c);
991     if (d >= screen_num_desktops) {
992         if (!data->desktopdir.wrap) return;
993         --c;
994     }
995     d = translate_row_col(r, c);
996     if (d < screen_num_desktops)
997         screen_set_desktop(d);
998 }
999
1000 void action_send_to_desktop_left(union ActionData *data)
1001 {
1002     guint r, c, d;
1003
1004     if (data->sendtodir.c) {
1005         cur_row_col(&r, &c);
1006         --c;
1007         if (c >= screen_desktop_layout.columns) {
1008             if (!data->sendtodir.wrap) return;
1009             c = screen_desktop_layout.columns - 1;
1010         }
1011         d = translate_row_col(r, c);
1012         if (d >= screen_num_desktops) {
1013             if (!data->sendtodir.wrap) return;
1014             --c;
1015         }
1016         d = translate_row_col(r, c);
1017         if (d < screen_num_desktops) {
1018             client_set_desktop(data->sendtodir.c, d, data->sendtodir.follow);
1019             if (data->sendtodir.follow) screen_set_desktop(d);
1020         }
1021     }
1022 }
1023
1024 void action_desktop_down(union ActionData *data)
1025 {
1026     guint r, c, d;
1027
1028     cur_row_col(&r, &c);
1029     ++r;
1030     if (r >= screen_desktop_layout.rows) {
1031         if (!data->desktopdir.wrap) return;
1032         r = 0;
1033     }
1034     d = translate_row_col(r, c);
1035     if (d >= screen_num_desktops) {
1036         if (!data->desktopdir.wrap) return;
1037         ++r;
1038     }
1039     d = translate_row_col(r, c);
1040     if (d < screen_num_desktops)
1041         screen_set_desktop(d);
1042 }
1043
1044 void action_send_to_desktop_down(union ActionData *data)
1045 {
1046     guint r, c, d;
1047
1048     if (data->sendtodir.c) {
1049         cur_row_col(&r, &c);
1050         ++r;
1051         if (r >= screen_desktop_layout.rows) {
1052             if (!data->sendtodir.wrap) return;
1053             r = 0;
1054         }
1055         d = translate_row_col(r, c);
1056         if (d >= screen_num_desktops) {
1057             if (!data->sendtodir.wrap) return;
1058             ++r;
1059         }
1060         d = translate_row_col(r, c);
1061         if (d < screen_num_desktops) {
1062             client_set_desktop(data->sendtodir.c, d, data->sendtodir.follow);
1063             if (data->sendtodir.follow) screen_set_desktop(d);
1064         }
1065     }
1066 }
1067
1068 void action_desktop_up(union ActionData *data)
1069 {
1070     guint r, c, d;
1071
1072     cur_row_col(&r, &c);
1073     --r;
1074     if (r >= screen_desktop_layout.rows) {
1075         if (!data->desktopdir.wrap) return;
1076         r = screen_desktop_layout.rows - 1;
1077     }
1078     d = translate_row_col(r, c);
1079     if (d >= screen_num_desktops) {
1080         if (!data->desktopdir.wrap) return;
1081         --r;
1082     }
1083     d = translate_row_col(r, c);
1084     if (d < screen_num_desktops)
1085         screen_set_desktop(d);
1086 }
1087
1088 void action_send_to_desktop_up(union ActionData *data)
1089 {
1090     guint r, c, d;
1091
1092     if (data->sendtodir.c) {
1093         cur_row_col(&r, &c);
1094         --r;
1095         if (r >= screen_desktop_layout.rows) {
1096             if (!data->sendtodir.wrap) return;
1097             r = screen_desktop_layout.rows - 1;
1098         }
1099         d = translate_row_col(r, c);
1100         if (d >= screen_num_desktops) {
1101             if (!data->sendtodir.wrap) return;
1102             --r;
1103         }
1104         d = translate_row_col(r, c);
1105         if (d < screen_num_desktops) {
1106             client_set_desktop(data->sendtodir.c, d, data->sendtodir.follow);
1107             if (data->sendtodir.follow) screen_set_desktop(d);
1108         }
1109     }
1110 }
1111
1112 void action_toggle_decorations(union ActionData *data)
1113 {
1114     Client *c = data->client.c;;
1115
1116     if (!c) return;
1117
1118     c->disabled_decorations = c->disabled_decorations ? 0 : ~0;
1119     client_setup_decor_and_functions(c);
1120 }
1121
1122 void action_moveresize(union ActionData *data)
1123 {
1124     Client *c = data->moveresize.c;
1125
1126     if (!c || !client_normal(c)) return;
1127
1128     moveresize_start(c, data->moveresize.x, data->moveresize.y,
1129                      data->moveresize.button, data->moveresize.corner);
1130 }
1131
1132 void action_restart(union ActionData *data)
1133 {
1134     ob_restart_path = data->execute.path;
1135     ob_shutdown = ob_restart = TRUE;
1136 }
1137
1138 void action_exit(union ActionData *data)
1139 {
1140     ob_shutdown = TRUE;
1141 }
1142
1143 void action_showmenu(union ActionData *data)
1144 {
1145     if (data->showmenu.name) {
1146         menu_show(data->showmenu.name, data->showmenu.x, data->showmenu.y,
1147                   data->showmenu.c);
1148     }
1149 }
1150
1151 void action_cycle_windows(union ActionData *data)
1152 {
1153     Client *c;
1154     
1155     c = focus_cycle(data->cycle.forward, data->cycle.linear, data->cycle.final,
1156                     data->cycle.cancel);
1157 }
1158
1159 void action_directional_focus(union ActionData *data)
1160 {
1161     Client *nf;
1162
1163     if (!data->diraction.c)
1164         return;
1165     if ((nf = client_find_directional(data->diraction.c,
1166                                       data->diraction.direction)))
1167         client_activate(nf);
1168 }
1169
1170 void action_movetoedge(union ActionData *data)
1171 {
1172     int x, y, h, w;
1173     Client *c = data->diraction.c;
1174
1175     if (!c)
1176         return;
1177     x = c->frame->area.x;
1178     y = c->frame->area.y;
1179     
1180     h = screen_area(c->desktop)->height;
1181     w = screen_area(c->desktop)->width;
1182     switch(data->diraction.direction) {
1183     case Direction_North:
1184         y = 0;
1185         break;
1186     case Direction_West:
1187         x = 0;
1188         break;
1189     case Direction_South:
1190         y = h - c->frame->area.height;
1191         break;
1192     case Direction_East:
1193         x = w - c->frame->area.width;
1194         break;
1195     }
1196     frame_frame_gravity(c->frame, &x, &y);
1197     client_configure(c, Corner_TopLeft,
1198                      x, y, c->area.width, c->area.height, TRUE, TRUE);
1199
1200 }
1201
1202 void action_send_to_layer(union ActionData *data)
1203 {
1204     if (data->layer.c)
1205         client_set_layer(data->layer.c, data->layer.layer);
1206 }
1207
1208 void action_toggle_layer(union ActionData *data)
1209 {
1210     Client *c = data->layer.c;
1211
1212     if (c) {
1213         if (data->layer.layer < 0)
1214             client_set_layer(c, c->below ? 0 : -1);
1215         else if (data->layer.layer > 0)
1216             client_set_layer(c, c->above ? 0 : 1);
1217     }
1218 }
1219
1220 void action_toggle_show_desktop(union ActionData *data)
1221 {
1222     screen_show_desktop(!screen_showing_desktop);
1223 }
1224
1225 void action_show_desktop(union ActionData *data)
1226 {
1227     screen_show_desktop(TRUE);
1228 }
1229
1230 void action_unshow_desktop(union ActionData *data)
1231 {
1232     screen_show_desktop(FALSE);
1233 }