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