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