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