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