whitespace
[dana/openbox.git] / openbox / action.c
1 #include "client.h"
2 #include "stacking.h"
3 #include "frame.h"
4 #include "screen.h"
5 #include "action.h"
6 #include "dispatch.h"
7 #include "openbox.h"
8
9 #include <glib.h>
10
11 Action *action_new(void (*func)(union ActionData *data))
12 {
13     Action *a = g_new0(Action, 1);
14     a->func = func;
15
16     /* deal with pointers */
17     if (func == action_execute)
18         a->data.execute.path = NULL;
19
20     return a;
21 }
22
23 void action_free(Action *a)
24 {
25     if (a == NULL) return;
26
27     /* deal with pointers */
28     if (a->func == action_execute || a->func == action_restart)
29         g_free(a->data.execute.path);
30
31     g_free(a);
32 }
33
34 Action *action_from_string(char *name)
35 {
36     Action *a = NULL;
37     if (!g_ascii_strcasecmp(name, "execute")) {
38         a = action_new(action_execute);
39     } else if (!g_ascii_strcasecmp(name, "focus")) {
40         a = action_new(action_focus);
41     } else if (!g_ascii_strcasecmp(name, "unfocus")) {
42         a = action_new(action_unfocus);
43     } else if (!g_ascii_strcasecmp(name, "iconify")) {
44         a = action_new(action_iconify);
45     } else if (!g_ascii_strcasecmp(name, "raise")) {
46         a = action_new(action_raise);
47     } else if (!g_ascii_strcasecmp(name, "lower")) {
48         a = action_new(action_lower);
49     } else if (!g_ascii_strcasecmp(name, "focusraise")) {
50         a = action_new(action_focusraise);
51     } else if (!g_ascii_strcasecmp(name, "close")) {
52         a = action_new(action_close);
53     } else if (!g_ascii_strcasecmp(name, "kill")) {
54         a = action_new(action_kill);
55     } else if (!g_ascii_strcasecmp(name, "shadelower")) {
56         a = action_new(action_shadelower);
57     } else if (!g_ascii_strcasecmp(name, "unshaderaise")) {
58         a = action_new(action_unshaderaise);
59     } else if (!g_ascii_strcasecmp(name, "shade")) {
60         a = action_new(action_shade);
61     } else if (!g_ascii_strcasecmp(name, "unshade")) {
62         a = action_new(action_unshade);
63     } else if (!g_ascii_strcasecmp(name, "toggleshade")) {
64         a = action_new(action_toggle_shade);
65     } else if (!g_ascii_strcasecmp(name, "toggleomnipresent")) {
66         a = action_new(action_toggle_omnipresent);
67     } else if (!g_ascii_strcasecmp(name, "moverelativehorz")) {
68         a = action_new(action_move_relative_horz);
69     } else if (!g_ascii_strcasecmp(name, "moverelativevert")) {
70         a = action_new(action_move_relative_vert);
71     } else if (!g_ascii_strcasecmp(name, "resizerelativehorz")) {
72         a = action_new(action_resize_relative_horz);
73     } else if (!g_ascii_strcasecmp(name, "resizerelativevert")) {
74         a = action_new(action_resize_relative_vert);
75     } else if (!g_ascii_strcasecmp(name, "maximizefull")) {
76         a = action_new(action_maximize_full);
77     } else if (!g_ascii_strcasecmp(name, "unmaximizefull")) {
78         a = action_new(action_unmaximize_full);
79     } else if (!g_ascii_strcasecmp(name, "togglemaximizefull")) {
80         a = action_new(action_toggle_maximize_full);
81     } else if (!g_ascii_strcasecmp(name, "maximizehorz")) {
82         a = action_new(action_maximize_horz);
83     } else if (!g_ascii_strcasecmp(name, "unmaximizehorz")) {
84         a = action_new(action_unmaximize_horz);
85     } else if (!g_ascii_strcasecmp(name, "togglemaximizehorz")) {
86         a = action_new(action_toggle_maximize_horz);
87     } else if (!g_ascii_strcasecmp(name, "maximizevert")) {
88         a = action_new(action_maximize_vert);
89     } else if (!g_ascii_strcasecmp(name, "unmaximizevert")) {
90         a = action_new(action_unmaximize_vert);
91     } else if (!g_ascii_strcasecmp(name, "togglemaximizevert")) {
92         a = action_new(action_toggle_maximize_vert);
93     } else if (!g_ascii_strcasecmp(name, "sendtodesktop")) {
94         a = action_new(action_send_to_desktop);
95         a->data.sendto.follow = TRUE;
96     } else if (!g_ascii_strcasecmp(name, "sendtonextdesktop")) {
97         a = action_new(action_send_to_next_desktop);
98         a->data.sendtonextprev.wrap = FALSE;
99         a->data.sendtonextprev.follow = TRUE;
100     } else if (!g_ascii_strcasecmp(name, "sendtonextdesktopwrap")) {
101         a = action_new(action_send_to_next_desktop);
102         a->data.sendtonextprev.wrap = TRUE;
103         a->data.sendtonextprev.follow = TRUE;
104     } else if (!g_ascii_strcasecmp(name, "sendtopreviousdesktop")) {
105         a = action_new(action_send_to_previous_desktop);
106         a->data.sendtonextprev.wrap = FALSE;
107         a->data.sendtonextprev.follow = TRUE;
108     } else if (!g_ascii_strcasecmp(name, "sendtopreviousdesktopwrap")) {
109         a = action_new(action_send_to_previous_desktop);
110         a->data.sendtonextprev.wrap = TRUE;
111         a->data.sendtonextprev.follow = TRUE;
112     } else if (!g_ascii_strcasecmp(name, "desktop")) {
113         a = action_new(action_desktop);
114     } else if (!g_ascii_strcasecmp(name, "nextdesktop")) {
115         a = action_new(action_next_desktop);
116         a->data.nextprevdesktop.wrap = FALSE;
117     } else if (!g_ascii_strcasecmp(name, "nextdesktopwrap")) {
118         a = action_new(action_next_desktop);
119         a->data.nextprevdesktop.wrap = TRUE;
120     } else if (!g_ascii_strcasecmp(name, "previousdesktop")) {
121         a = action_new(action_previous_desktop);
122         a->data.nextprevdesktop.wrap = FALSE;
123     } else if (!g_ascii_strcasecmp(name, "previousdesktopwrap")) {
124         a = action_new(action_previous_desktop);
125         a->data.nextprevdesktop.wrap = TRUE;
126     } else if (!g_ascii_strcasecmp(name, "nextdesktopcolumn")) {
127         a = action_new(action_next_desktop_column);
128         a->data.nextprevdesktop.wrap = FALSE;
129     } else if (!g_ascii_strcasecmp(name, "nextdesktopcolumnwrap")) {
130         a = action_new(action_next_desktop_column);
131         a->data.nextprevdesktop.wrap = TRUE;
132     } else if (!g_ascii_strcasecmp(name, "previousdesktopcolumn")) {
133         a = action_new(action_previous_desktop_column);
134         a->data.nextprevdesktop.wrap = FALSE;
135     } else if (!g_ascii_strcasecmp(name, "previousdesktopcolumnwrap")) {
136         a = action_new(action_previous_desktop_column);
137         a->data.nextprevdesktop.wrap = TRUE;
138     } else if (!g_ascii_strcasecmp(name, "nextdesktoprow")) {
139         a = action_new(action_next_desktop_row);
140         a->data.nextprevdesktop.wrap = FALSE;
141     } else if (!g_ascii_strcasecmp(name, "nextdesktoprowwrap")) {
142         a = action_new(action_next_desktop_row);
143         a->data.nextprevdesktop.wrap = TRUE;
144     } else if (!g_ascii_strcasecmp(name, "previousdesktoprow")) {
145         a = action_new(action_previous_desktop_row);
146         a->data.nextprevdesktop.wrap = FALSE;
147     } else if (!g_ascii_strcasecmp(name, "previousdesktoprowwrap")) {
148         a = action_new(action_previous_desktop_row);
149         a->data.nextprevdesktop.wrap = TRUE;
150     } else if (!g_ascii_strcasecmp(name, "toggledecorations")) {
151         a = action_new(action_toggle_decorations);
152     } else if (!g_ascii_strcasecmp(name, "move")) {
153         a = action_new(action_move);
154     } else if (!g_ascii_strcasecmp(name, "resize")) {
155         a = action_new(action_resize);
156     } else if (!g_ascii_strcasecmp(name, "restart")) {
157         a = action_new(action_restart);
158     } else if (!g_ascii_strcasecmp(name, "exit")) {
159         a = action_new(action_exit);
160     }
161     else if (!g_ascii_strcasecmp(name, "showmenu")) {
162         a = action_new(action_showmenu);
163     }
164     
165     return a;
166 }
167
168 void action_execute(union ActionData *data)
169 {
170     GError *e = NULL;
171     if (data->execute.path)
172         if (!g_spawn_command_line_async(data->execute.path, &e)) {
173             g_warning("failed to execute '%s': %s",
174                       data->execute.path, e->message);
175         }
176 }
177
178 void action_focus(union ActionData *data)
179 {
180     if (data->client.c)
181         client_focus(data->client.c);
182 }
183
184 void action_unfocus (union ActionData *data)
185 {
186     if (data->client.c)
187         client_unfocus(data->client.c);
188 }
189
190 void action_iconify(union ActionData *data)
191 {
192     if (data->client.c)
193         client_iconify(data->client.c, TRUE, TRUE);
194 }
195
196 void action_focusraise(union ActionData *data)
197 {
198     if (data->client.c) {
199         client_focus(data->client.c);
200         stacking_raise(data->client.c);
201     }
202 }
203
204 void action_raise(union ActionData *data)
205 {
206     if (data->client.c)
207         stacking_raise(data->client.c);
208 }
209
210 void action_unshaderaise(union ActionData *data)
211 {
212     if (data->client.c) {
213         if (data->client.c->shaded)
214             client_shade(data->client.c, FALSE);
215         else
216             stacking_raise(data->client.c);
217     }
218 }
219
220 void action_shadelower(union ActionData *data)
221 {
222     if (data->client.c) {
223         if (data->client.c->shaded)
224             stacking_lower(data->client.c);
225         else
226             client_shade(data->client.c, TRUE);
227     }
228 }
229
230 void action_lower(union ActionData *data)
231 {
232     if (data->client.c)
233         stacking_lower(data->client.c);
234 }
235
236 void action_close(union ActionData *data)
237 {
238     if (data->client.c)
239         client_close(data->client.c);
240 }
241
242 void action_kill(union ActionData *data)
243 {
244     if (data->client.c)
245         client_kill(data->client.c);
246 }
247
248 void action_shade(union ActionData *data)
249 {
250     if (data->client.c)
251         client_shade(data->client.c, TRUE);
252 }
253
254 void action_unshade(union ActionData *data)
255 {
256     if (data->client.c)
257         client_shade(data->client.c, FALSE);
258 }
259
260 void action_toggle_shade(union ActionData *data)
261 {
262     if (data->client.c)
263         client_shade(data->client.c, !data->client.c->shaded);
264 }
265
266 void action_toggle_omnipresent(union ActionData *data)
267
268     if (data->client.c)
269         client_set_desktop(data->client.c,
270                            data->client.c->desktop == DESKTOP_ALL ?
271                            screen_desktop : DESKTOP_ALL, FALSE);
272 }
273
274 void action_move_relative_horz(union ActionData *data)
275 {
276     Client *c = data->relative.c;
277     if (c)
278         client_configure(c, Corner_TopLeft,
279                          c->area.x + data->relative.delta, c->area.y,
280                          c->area.width, c->area.height, TRUE, TRUE);
281 }
282
283 void action_move_relative_vert(union ActionData *data)
284 {
285     Client *c = data->relative.c;
286     if (c)
287         client_configure(c, Corner_TopLeft,
288                          c->area.x, c->area.y + data->relative.delta,
289                          c->area.width, c->area.height, TRUE, TRUE);
290 }
291
292 void action_resize_relative_horz(union ActionData *data)
293 {
294     Client *c = data->relative.c;
295     if (c)
296         client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
297                          c->area.width + data->relative.delta,
298                          c->area.height, TRUE, TRUE);
299 }
300
301 void action_resize_relative_vert(union ActionData *data)
302 {
303     Client *c = data->relative.c;
304     if (c && !c->shaded)
305         client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
306                          c->area.width, c->area.height + data->relative.delta,
307                          TRUE, TRUE);
308 }
309
310 void action_maximize_full(union ActionData *data)
311 {
312     if (data->client.c)
313         client_maximize(data->client.c, TRUE, 0, TRUE);
314 }
315
316 void action_unmaximize_full(union ActionData *data)
317 {
318     if (data->client.c)
319         client_maximize(data->client.c, FALSE, 0, TRUE);
320 }
321
322 void action_toggle_maximize_full(union ActionData *data)
323 {
324     if (data->client.c)
325         client_maximize(data->client.c,
326                         !(data->client.c->max_horz ||
327                           data->client.c->max_vert),
328                         0, TRUE);
329 }
330
331 void action_maximize_horz(union ActionData *data)
332 {
333     if (data->client.c)
334         client_maximize(data->client.c, TRUE, 1, TRUE);
335 }
336
337 void action_unmaximize_horz(union ActionData *data)
338 {
339     if (data->client.c)
340         client_maximize(data->client.c, FALSE, 1, TRUE);
341 }
342
343 void action_toggle_maximize_horz(union ActionData *data)
344 {
345     if (data->client.c)
346         client_maximize(data->client.c, !data->client.c->max_horz, 1, TRUE);
347 }
348
349 void action_maximize_vert(union ActionData *data)
350 {
351     if (data->client.c)
352         client_maximize(data->client.c, TRUE, 2, TRUE);
353 }
354
355 void action_unmaximize_vert(union ActionData *data)
356 {
357     if (data->client.c)
358         client_maximize(data->client.c, FALSE, 2, TRUE);
359 }
360
361 void action_toggle_maximize_vert(union ActionData *data)
362 {
363     if (data->client.c)
364         client_maximize(data->client.c, !data->client.c->max_vert, 2, TRUE);
365 }
366
367 void action_send_to_desktop(union ActionData *data)
368 {
369     if (data->sendto.c) {
370         if (data->sendto.desk < screen_num_desktops ||
371             data->sendto.desk == DESKTOP_ALL) {
372             client_set_desktop(data->desktop.c,
373                                data->sendto.desk, data->sendto.follow);
374             if (data->sendto.follow) screen_set_desktop(data->sendto.desk);
375         }
376     }
377 }
378
379 void action_send_to_next_desktop(union ActionData *data)
380 {
381     guint d;
382
383     if (!data->sendtonextprev.c) return;
384
385     d = screen_desktop + 1;
386     if (d >= screen_num_desktops) {
387         if (!data->sendtonextprev.wrap) return;
388         d = 0;
389     }
390     client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
391     if (data->sendtonextprev.follow) screen_set_desktop(d);
392 }
393
394 void action_send_to_previous_desktop(union ActionData *data)
395 {
396     guint d;
397
398     if (!data->sendtonextprev.c) return;
399
400     d = screen_desktop - 1;
401     if (d >= screen_num_desktops) {
402         if (!data->sendtonextprev.wrap) return;
403         d = screen_num_desktops - 1;
404     }
405     client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
406     if (data->sendtonextprev.follow) screen_set_desktop(d);
407 }
408
409 void action_desktop(union ActionData *data)
410 {
411     if (data->desktop.desk < screen_num_desktops ||
412         data->desktop.desk == DESKTOP_ALL)
413         screen_set_desktop(data->desktop.desk);
414 }
415
416 void action_next_desktop(union ActionData *data)
417 {
418     guint d;
419
420     d = screen_desktop + 1;
421     if (d >= screen_num_desktops) {
422         if (!data->nextprevdesktop.wrap) return;
423         d = 0;
424     }
425     screen_set_desktop(d);
426 }
427
428 void action_previous_desktop(union ActionData *data)
429 {
430     guint d;
431
432     d = screen_desktop - 1;
433     if (d >= screen_num_desktops) {
434         if (!data->nextprevdesktop.wrap) return;
435         d = screen_num_desktops - 1;
436     }
437     screen_set_desktop(d);
438 }
439
440 static void cur_row_col(guint *r, guint *c)
441 {
442     switch (screen_desktop_layout.orientation) {
443     case Orientation_Horz:
444         switch (screen_desktop_layout.start_corner) {
445         case Corner_TopLeft:
446             *r = screen_desktop / screen_desktop_layout.columns;
447             *c = screen_desktop % screen_desktop_layout.columns;
448             break;
449         case Corner_BottomLeft:
450             *r = screen_desktop_layout.rows - 1 -
451                 screen_desktop / screen_desktop_layout.columns;
452             *c = screen_desktop % screen_desktop_layout.columns;
453             break;
454         break;
455         case Corner_TopRight:
456             *r = screen_desktop / screen_desktop_layout.columns;
457             *c = screen_desktop_layout.columns - 1 -
458                 screen_desktop % screen_desktop_layout.columns;
459             break;
460         case Corner_BottomRight:
461             *r = screen_desktop_layout.rows - 1 -
462                 screen_desktop / screen_desktop_layout.columns;
463             *c = screen_desktop_layout.columns - 1 -
464                 screen_desktop % screen_desktop_layout.columns;
465             break;
466         break;
467         }
468     case Orientation_Vert:
469         switch (screen_desktop_layout.start_corner) {
470         case Corner_TopLeft:
471             *r = screen_desktop % screen_desktop_layout.rows;
472             *c = screen_desktop / screen_desktop_layout.rows;
473             break;
474         case Corner_BottomLeft:
475             *r = screen_desktop_layout.rows - 1 -
476                 screen_desktop % screen_desktop_layout.rows;
477             *c = screen_desktop / screen_desktop_layout.rows;
478             break;
479         break;
480         case Corner_TopRight:
481             *r = screen_desktop % screen_desktop_layout.rows;
482             *c = screen_desktop_layout.columns - 1 -
483                 screen_desktop / screen_desktop_layout.rows;
484             break;
485         case Corner_BottomRight:
486             *r = screen_desktop_layout.rows - 1 -
487                 screen_desktop % screen_desktop_layout.rows;
488             *c = screen_desktop_layout.columns - 1 -
489                 screen_desktop / screen_desktop_layout.rows;
490             break;
491         break;
492         }
493         break;
494     }
495 }
496
497 static guint translate_row_col(guint r, guint c)
498 {
499     switch (screen_desktop_layout.orientation) {
500     case Orientation_Horz:
501         switch (screen_desktop_layout.start_corner) {
502         case Corner_TopLeft:
503             return r * screen_desktop_layout.columns + c;
504         case Corner_BottomLeft:
505             return (screen_desktop_layout.rows - 1 - r) *
506                 screen_desktop_layout.columns + c;
507         case Corner_TopRight:
508             return r * screen_desktop_layout.columns +
509                 (screen_desktop_layout.columns - 1 - c);
510         case Corner_BottomRight:
511             return (screen_desktop_layout.rows - 1 - r) *
512                 screen_desktop_layout.columns +
513                 (screen_desktop_layout.columns - 1 - c);
514         }
515     case Orientation_Vert:
516         switch (screen_desktop_layout.start_corner) {
517         case Corner_TopLeft:
518             return c * screen_desktop_layout.rows + r;
519         case Corner_BottomLeft:
520             return c * screen_desktop_layout.rows +
521                 (screen_desktop_layout.rows - 1 - r);
522         case Corner_TopRight:
523             return (screen_desktop_layout.columns - 1 - c) *
524                 screen_desktop_layout.rows + r;
525         case Corner_BottomRight:
526             return (screen_desktop_layout.columns - 1 - c) *
527                 screen_desktop_layout.rows +
528                 (screen_desktop_layout.rows - 1 - r);
529         }
530     }
531     g_assert_not_reached();
532     return 0;
533 }
534
535 void action_next_desktop_column(union ActionData *data)
536 {
537     guint r, c, d;
538
539     cur_row_col(&r, &c);
540     ++c;
541     d = translate_row_col(r, c);
542     if (d >= screen_num_desktops) {
543         if (!data->nextprevdesktop.wrap) return;
544         c = 0;
545     }
546     if (d >= screen_num_desktops)
547         ++c;
548     d = translate_row_col(r, c);
549     if (d < screen_num_desktops)
550         screen_set_desktop(d);
551 }
552
553 void action_previous_desktop_column(union ActionData *data)
554 {
555     guint r, c, d;
556
557     cur_row_col(&r, &c);
558     --c;
559     d = translate_row_col(r, c);
560     if (d >= screen_num_desktops) {
561         if (!data->nextprevdesktop.wrap) return;
562         c = screen_desktop_layout.columns - 1;
563     }
564     if (d >= screen_num_desktops)
565         --c;
566     d = translate_row_col(r, c);
567     if (d < screen_num_desktops)
568         screen_set_desktop(d);
569 }
570
571 void action_next_desktop_row(union ActionData *data)
572 {
573     guint r, c, d;
574
575     cur_row_col(&r, &c);
576     ++r;
577     d = translate_row_col(r, c);
578     if (d >= screen_num_desktops) {
579         if (!data->nextprevdesktop.wrap) return;
580         r = 0;
581     }
582     if (d >= screen_num_desktops)
583         ++r;
584     d = translate_row_col(r, c);
585     if (d < screen_num_desktops)
586         screen_set_desktop(d);
587 }
588
589 void action_previous_desktop_row(union ActionData *data)
590 {
591     guint r, c, d;
592
593     cur_row_col(&r, &c);
594     --r;
595     d = translate_row_col(r, c);
596     if (d >= screen_num_desktops) {
597         if (!data->nextprevdesktop.wrap) return;
598         c = screen_desktop_layout.rows - 1;
599     }
600     if (d >= screen_num_desktops)
601         --r;
602     d = translate_row_col(r, c);
603     if (d < screen_num_desktops)
604         screen_set_desktop(d);
605 }
606
607 void action_toggle_decorations(union ActionData *data)
608 {
609     Client *c = data->client.c;
610     c->disabled_decorations = c->disabled_decorations ? 0 : ~0;
611     client_setup_decor_and_functions(c);
612 }
613
614 void action_move(union ActionData *data)
615 {
616     Client *c = data->move.c;
617     int x = data->move.x;
618     int y = data->move.y;
619
620     if (!c || !client_normal(c)) return;
621
622     dispatch_move(c, &x, &y);
623
624     frame_frame_gravity(c->frame, &x, &y); /* get where the client should be */
625     client_configure(c, Corner_TopLeft, x, y, c->area.width, c->area.height,
626                      TRUE, data->move.final);
627 }
628
629 void action_resize(union ActionData *data)
630 {
631     Client *c = data->resize.c;
632     int w = data->resize.x;
633     int h = data->resize.y;
634  
635     if (!c || c->shaded || !client_normal(c)) return;
636
637     dispatch_resize(c, &w, &h, data->resize.corner);
638     
639     w -= c->frame->size.left + c->frame->size.right;
640     h -= c->frame->size.top + c->frame->size.bottom;
641     
642     client_configure(c, data->resize.corner, c->area.x, c->area.y, w, h,
643                      TRUE, data->resize.final);
644 }
645
646 void action_restart(union ActionData *data)
647 {
648     ob_restart_path = data->execute.path;
649     ob_shutdown = ob_restart = TRUE;
650 }
651
652 void action_exit(union ActionData *data)
653 {
654     ob_shutdown = TRUE;
655 }
656
657 void action_showmenu(union ActionData *data)
658 {
659     g_message(__FUNCTION__);
660 }