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