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