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