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