]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/action.c
fixes for new keybindings stuff/chroot/parsing
[mikachu/openbox.git] / openbox / action.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    action.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "client.h"
22 #include "focus.h"
23 #include "moveresize.h"
24 #include "menu.h"
25 #include "prop.h"
26 #include "stacking.h"
27 #include "screen.h"
28 #include "action.h"
29 #include "openbox.h"
30 #include "grab.h"
31 #include "keyboard.h"
32 #include "event.h"
33 #include "dock.h"
34 #include "config.h"
35 #include "mainloop.h"
36 #include "startupnotify.h"
37 #include "gettext.h"
38
39 #include <glib.h>
40
41 inline void client_action_start(union ActionData *data)
42 {
43     if (config_focus_follow)
44         if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button)
45             grab_pointer(TRUE, FALSE, OB_CURSOR_NONE);
46 }
47
48 inline void client_action_end(union ActionData *data)
49 {
50     if (config_focus_follow)
51         if (data->any.context != OB_FRAME_CONTEXT_CLIENT) {
52             if (!data->any.button) {
53                 grab_pointer(FALSE, FALSE, OB_CURSOR_NONE);
54             } else {
55                 ObClient *c;
56
57                 /* usually this is sorta redundant, but with a press action
58                    the enter event will come as a GrabNotify which is
59                    ignored, so this will handle that case */
60                 if ((c = client_under_pointer()))
61                     event_enter_client(c);
62             }
63         }
64 }
65
66 typedef struct
67 {
68     const gchar *name;
69     void (*func)(union ActionData *);
70     void (*setup)(ObAction **, ObUserAction uact);
71 } ActionString;
72
73 static ObAction *action_new(void (*func)(union ActionData *data))
74 {
75     ObAction *a = g_new0(ObAction, 1);
76     a->ref = 1;
77     a->func = func;
78
79     return a;
80 }
81
82 void action_ref(ObAction *a)
83 {
84     ++a->ref;
85 }
86
87 void action_unref(ObAction *a)
88 {
89     if (a == NULL) return;
90
91     if (--a->ref > 0) return;
92
93     /* deal with pointers */
94     if (a->func == action_execute || a->func == action_restart)
95         g_free(a->data.execute.path);
96     else if (a->func == action_showmenu)
97         g_free(a->data.showmenu.name);
98
99     g_free(a);
100 }
101
102 ObAction* action_copy(const ObAction *src)
103 {
104     ObAction *a = action_new(src->func);
105
106     a->data = src->data;
107
108     /* deal with pointers */
109     if (a->func == action_execute || a->func == action_restart)
110         a->data.execute.path = g_strdup(a->data.execute.path);
111     else if (a->func == action_showmenu)
112         a->data.showmenu.name = g_strdup(a->data.showmenu.name);
113
114     return a;
115 }
116
117 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
118 {
119     (*a)->data.interdiraction.inter.any.interactive = TRUE;
120     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
121     (*a)->data.interdiraction.dialog = TRUE;
122     (*a)->data.interdiraction.dock_windows = FALSE;
123 }
124
125 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
126 {
127     (*a)->data.interdiraction.inter.any.interactive = TRUE;
128     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
129     (*a)->data.interdiraction.dialog = TRUE;
130     (*a)->data.interdiraction.dock_windows = FALSE;
131 }
132
133 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
134 {
135     (*a)->data.interdiraction.inter.any.interactive = TRUE;
136     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
137     (*a)->data.interdiraction.dialog = TRUE;
138     (*a)->data.interdiraction.dock_windows = FALSE;
139 }
140
141 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
142 {
143     (*a)->data.interdiraction.inter.any.interactive = TRUE;
144     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
145     (*a)->data.interdiraction.dialog = TRUE;
146     (*a)->data.interdiraction.dock_windows = FALSE;
147 }
148
149 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
150 {
151     (*a)->data.interdiraction.inter.any.interactive = TRUE;
152     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
153     (*a)->data.interdiraction.dialog = TRUE;
154     (*a)->data.interdiraction.dock_windows = FALSE;
155 }
156
157 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
158 {
159     (*a)->data.interdiraction.inter.any.interactive = TRUE;
160     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
161     (*a)->data.interdiraction.dialog = TRUE;
162     (*a)->data.interdiraction.dock_windows = FALSE;
163 }
164
165 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
166 {
167     (*a)->data.interdiraction.inter.any.interactive = TRUE;
168     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
169     (*a)->data.interdiraction.dialog = TRUE;
170     (*a)->data.interdiraction.dock_windows = FALSE;
171 }
172
173 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
174 {
175     (*a)->data.interdiraction.inter.any.interactive = TRUE;
176     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
177     (*a)->data.interdiraction.dialog = TRUE;
178     (*a)->data.interdiraction.dock_windows = FALSE;
179 }
180
181 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
182 {
183     (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
184     (*a)->data.sendto.follow = TRUE;
185 }
186
187 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
188 {
189     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
190     (*a)->data.sendtodir.inter.any.interactive = TRUE;
191     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
192     (*a)->data.sendtodir.linear = TRUE;
193     (*a)->data.sendtodir.wrap = TRUE;
194     (*a)->data.sendtodir.follow = TRUE;
195 }
196
197 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
198 {
199     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
200     (*a)->data.sendtodir.inter.any.interactive = TRUE;
201     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
202     (*a)->data.sendtodir.linear = TRUE;
203     (*a)->data.sendtodir.wrap = TRUE;
204     (*a)->data.sendtodir.follow = TRUE;
205 }
206
207 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
208 {
209     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
210     (*a)->data.sendtodir.inter.any.interactive = TRUE;
211     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
212     (*a)->data.sendtodir.linear = FALSE;
213     (*a)->data.sendtodir.wrap = TRUE;
214     (*a)->data.sendtodir.follow = TRUE;
215 }
216
217 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
218 {
219     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
220     (*a)->data.sendtodir.inter.any.interactive = TRUE;
221     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
222     (*a)->data.sendtodir.linear = FALSE;
223     (*a)->data.sendtodir.wrap = TRUE;
224     (*a)->data.sendtodir.follow = TRUE;
225 }
226
227 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
228 {
229     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
230     (*a)->data.sendtodir.inter.any.interactive = TRUE;
231     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
232     (*a)->data.sendtodir.linear = FALSE;
233     (*a)->data.sendtodir.wrap = TRUE;
234     (*a)->data.sendtodir.follow = TRUE;
235 }
236
237 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
238 {
239     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
240     (*a)->data.sendtodir.inter.any.interactive = TRUE;
241     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
242     (*a)->data.sendtodir.linear = FALSE;
243     (*a)->data.sendtodir.wrap = TRUE;
244     (*a)->data.sendtodir.follow = TRUE;
245 }
246
247 void setup_action_desktop(ObAction **a, ObUserAction uact)
248 {
249     (*a)->data.desktop.inter.any.interactive = FALSE;
250 }
251
252 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
253 {
254     (*a)->data.desktopdir.inter.any.interactive = TRUE;
255     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
256     (*a)->data.desktopdir.linear = TRUE;
257     (*a)->data.desktopdir.wrap = TRUE;
258 }
259
260 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
261 {
262     (*a)->data.desktopdir.inter.any.interactive = TRUE;
263     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
264     (*a)->data.desktopdir.linear = TRUE;
265     (*a)->data.desktopdir.wrap = TRUE;
266 }
267
268 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
269 {
270     (*a)->data.desktopdir.inter.any.interactive = TRUE;
271     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
272     (*a)->data.desktopdir.linear = FALSE;
273     (*a)->data.desktopdir.wrap = TRUE;
274 }
275
276 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
277 {
278     (*a)->data.desktopdir.inter.any.interactive = TRUE;
279     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
280     (*a)->data.desktopdir.linear = FALSE;
281     (*a)->data.desktopdir.wrap = TRUE;
282 }
283
284 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
285 {
286     (*a)->data.desktopdir.inter.any.interactive = TRUE;
287     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
288     (*a)->data.desktopdir.linear = FALSE;
289     (*a)->data.desktopdir.wrap = TRUE;
290 }
291
292 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
293 {
294     (*a)->data.desktopdir.inter.any.interactive = TRUE;
295     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
296     (*a)->data.desktopdir.linear = FALSE;
297     (*a)->data.desktopdir.wrap = TRUE;
298 }
299
300 void setup_action_cycle_windows_next(ObAction **a, ObUserAction uact)
301 {
302     (*a)->data.cycle.inter.any.interactive = TRUE;
303     (*a)->data.cycle.linear = FALSE;
304     (*a)->data.cycle.forward = TRUE;
305     (*a)->data.cycle.dialog = TRUE;
306     (*a)->data.cycle.dock_windows = FALSE;
307 }
308
309 void setup_action_cycle_windows_previous(ObAction **a, ObUserAction uact)
310 {
311     (*a)->data.cycle.inter.any.interactive = TRUE;
312     (*a)->data.cycle.linear = FALSE;
313     (*a)->data.cycle.forward = FALSE;
314     (*a)->data.cycle.dialog = TRUE;
315     (*a)->data.cycle.dock_windows = FALSE;
316 }
317
318 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
319 {
320     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
321     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
322     (*a)->data.diraction.hang = TRUE;
323 }
324
325 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
326 {
327     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
328     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
329     (*a)->data.diraction.hang = TRUE;
330 }
331
332 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
333 {
334     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
335     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
336     (*a)->data.diraction.hang = TRUE;
337 }
338
339 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
340 {
341     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
342     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
343     (*a)->data.diraction.hang = TRUE;
344 }
345
346 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
347 {
348     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
349     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
350     (*a)->data.diraction.hang = FALSE;
351 }
352
353 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
354 {
355     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
356     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
357     (*a)->data.diraction.hang = FALSE;
358 }
359
360 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
361 {
362     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
363     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
364     (*a)->data.diraction.hang = FALSE;
365 }
366
367 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
368 {
369     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
370     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
371     (*a)->data.diraction.hang = FALSE;
372 }
373
374 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
375 {
376     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
377     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
378 }
379
380 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
381 {
382     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
383     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
384 }
385
386 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
387 {
388     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
389     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
390 }
391
392 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
393 {
394     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
395     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
396 }
397
398 void setup_action_top_layer(ObAction **a, ObUserAction uact)
399 {
400     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
401     (*a)->data.layer.layer = 1;
402 }
403
404 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
405 {
406     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
407     (*a)->data.layer.layer = 0;
408 }
409
410 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
411 {
412     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
413     (*a)->data.layer.layer = -1;
414 }
415
416 void setup_action_move(ObAction **a, ObUserAction uact)
417 {
418     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
419     (*a)->data.moveresize.move = TRUE;
420     (*a)->data.moveresize.keyboard =
421         (uact == OB_USER_ACTION_NONE ||
422          uact == OB_USER_ACTION_KEYBOARD_KEY ||
423          uact == OB_USER_ACTION_MENU_SELECTION);
424 }
425
426 void setup_action_resize(ObAction **a, ObUserAction uact)
427 {
428     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
429     (*a)->data.moveresize.move = FALSE;
430     (*a)->data.moveresize.keyboard =
431         (uact == OB_USER_ACTION_NONE ||
432          uact == OB_USER_ACTION_KEYBOARD_KEY ||
433          uact == OB_USER_ACTION_MENU_SELECTION);
434 }
435
436 void setup_action_showmenu(ObAction **a, ObUserAction uact)
437 {
438     (*a)->data.showmenu.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
439     /* you cannot call ShowMenu from inside a menu, cuz the menu code makes
440        assumptions that there is only one menu (and submenus) open at
441        a time! */
442     if (uact == OB_USER_ACTION_MENU_SELECTION) {
443         action_unref(*a);
444         *a = NULL;
445     }
446 }
447
448 void setup_action_focus(ObAction **a, ObUserAction uact)
449 {
450     (*a)->data.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
451 }
452
453 void setup_client_action(ObAction **a, ObUserAction uact)
454 {
455     (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
456 }
457
458 ActionString actionstrings[] =
459 {
460     {
461         "execute", 
462         action_execute, 
463         NULL
464     },
465     {
466         "directionalfocusnorth", 
467         action_directional_focus, 
468         setup_action_directional_focus_north
469     },
470     {
471         "directionalfocuseast", 
472         action_directional_focus, 
473         setup_action_directional_focus_east
474     },
475     {
476         "directionalfocussouth", 
477         action_directional_focus, 
478         setup_action_directional_focus_south
479     },
480     {
481         "directionalfocuswest",
482         action_directional_focus,
483         setup_action_directional_focus_west
484     },
485     {
486         "directionalfocusnortheast",
487         action_directional_focus,
488         setup_action_directional_focus_northeast
489     },
490     {
491         "directionalfocussoutheast",
492         action_directional_focus,
493         setup_action_directional_focus_southeast
494     },
495     {
496         "directionalfocussouthwest",
497         action_directional_focus,
498         setup_action_directional_focus_southwest
499     },
500     {
501         "directionalfocusnorthwest",
502         action_directional_focus,
503         setup_action_directional_focus_northwest
504     },
505     {
506         "activate",
507         action_activate,
508         setup_client_action
509     },
510     {
511         "focus",
512         action_focus,
513         setup_action_focus
514     },
515     {
516         "unfocus",
517         action_unfocus,
518         setup_client_action
519     },
520     {
521         "iconify",
522         action_iconify,
523         setup_client_action
524     },
525     {
526         "focustobottom",
527         action_focus_order_to_bottom,
528         setup_client_action
529     },
530     {
531         "raiselower",
532         action_raiselower,
533         setup_client_action
534     },
535     {
536         "raise",
537         action_raise,
538         setup_client_action
539     },
540     {
541         "lower",
542         action_lower,
543         setup_client_action
544     },
545     {
546         "close",
547         action_close,
548         setup_client_action
549     },
550     {
551         "kill",
552         action_kill,
553         setup_client_action
554     },
555     {
556         "shadelower",
557         action_shadelower,
558         setup_client_action
559     },
560     {
561         "unshaderaise",
562         action_unshaderaise,
563         setup_client_action
564     },
565     {
566         "shade",
567         action_shade,
568         setup_client_action
569     },
570     {
571         "unshade",
572         action_unshade,
573         setup_client_action
574     },
575     {
576         "toggleshade",
577         action_toggle_shade,
578         setup_client_action
579     },
580     {
581         "toggleomnipresent",
582         action_toggle_omnipresent,
583         setup_client_action
584     },
585     {
586         "moverelativehorz",
587         action_move_relative_horz,
588         setup_client_action
589     },
590     {
591         "moverelativevert",
592         action_move_relative_vert,
593         setup_client_action
594     },
595     {
596         "movetocenter",
597         action_move_to_center,
598         setup_client_action
599     },
600     {
601         "resizerelativehorz",
602         action_resize_relative_horz,
603         setup_client_action
604     },
605     {
606         "resizerelativevert",
607         action_resize_relative_vert,
608         setup_client_action
609     },
610     {
611         "moverelative",
612         action_move_relative,
613         setup_client_action
614     },
615     {
616         "resizerelative",
617         action_resize_relative,
618         setup_client_action
619     },
620     {
621         "maximizefull",
622         action_maximize_full,
623         setup_client_action
624     },
625     {
626         "unmaximizefull",
627         action_unmaximize_full,
628         setup_client_action
629     },
630     {
631         "togglemaximizefull",
632         action_toggle_maximize_full,
633         setup_client_action
634     },
635     {
636         "maximizehorz",
637         action_maximize_horz,
638         setup_client_action
639     },
640     {
641         "unmaximizehorz",
642         action_unmaximize_horz,
643         setup_client_action
644     },
645     {
646         "togglemaximizehorz",
647         action_toggle_maximize_horz,
648         setup_client_action
649     },
650     {
651         "maximizevert",
652         action_maximize_vert,
653         setup_client_action
654     },
655     {
656         "unmaximizevert",
657         action_unmaximize_vert,
658         setup_client_action
659     },
660     {
661         "togglemaximizevert",
662         action_toggle_maximize_vert,
663         setup_client_action
664     },
665     {
666         "togglefullscreen",
667         action_toggle_fullscreen,
668         setup_client_action
669     },
670     {
671         "sendtodesktop",
672         action_send_to_desktop,
673         setup_action_send_to_desktop
674     },
675     {
676         "sendtodesktopnext",
677         action_send_to_desktop_dir,
678         setup_action_send_to_desktop_next
679     },
680     {
681         "sendtodesktopprevious",
682         action_send_to_desktop_dir,
683         setup_action_send_to_desktop_prev
684     },
685     {
686         "sendtodesktopright",
687         action_send_to_desktop_dir,
688         setup_action_send_to_desktop_right
689     },
690     {
691         "sendtodesktopleft",
692         action_send_to_desktop_dir,
693         setup_action_send_to_desktop_left
694     },
695     {
696         "sendtodesktopup",
697         action_send_to_desktop_dir,
698         setup_action_send_to_desktop_up
699     },
700     {
701         "sendtodesktopdown",
702         action_send_to_desktop_dir,
703         setup_action_send_to_desktop_down
704     },
705     {
706         "desktop",
707         action_desktop,
708         setup_action_desktop
709     },
710     {
711         "desktopnext",
712         action_desktop_dir,
713         setup_action_desktop_next
714     },
715     {
716         "desktopprevious",
717         action_desktop_dir,
718         setup_action_desktop_prev
719     },
720     {
721         "desktopright",
722         action_desktop_dir,
723         setup_action_desktop_right
724     },
725     {
726         "desktopleft",
727         action_desktop_dir,
728         setup_action_desktop_left
729     },
730     {
731         "desktopup",
732         action_desktop_dir,
733         setup_action_desktop_up
734     },
735     {
736         "desktopdown",
737         action_desktop_dir,
738         setup_action_desktop_down
739     },
740     {
741         "toggledecorations",
742         action_toggle_decorations,
743         setup_client_action
744     },
745     {
746         "move",
747         action_moveresize,
748         setup_action_move
749     },
750     {
751         "resize",
752         action_moveresize,
753         setup_action_resize
754     },
755     {
756         "toggledockautohide",
757         action_toggle_dockautohide,
758         NULL
759     },
760     {
761         "toggleshowdesktop",
762         action_toggle_show_desktop,
763         NULL
764     },
765     {
766         "showdesktop",
767         action_show_desktop,
768         NULL
769     },
770     {
771         "unshowdesktop",
772         action_unshow_desktop,
773         NULL
774     },
775     {
776         "desktoplast",
777         action_desktop_last,
778         NULL
779     },
780     {
781         "reconfigure",
782         action_reconfigure,
783         NULL
784     },
785     {
786         "restart",
787         action_restart,
788         NULL
789     },
790     {
791         "exit",
792         action_exit,
793         NULL
794     },
795     {
796         "showmenu",
797         action_showmenu,
798         setup_action_showmenu
799     },
800     {
801         "sendtotoplayer",
802         action_send_to_layer,
803         setup_action_top_layer
804     },
805     {
806         "togglealwaysontop",
807         action_toggle_layer,
808         setup_action_top_layer
809     },
810     {
811         "sendtonormallayer",
812         action_send_to_layer,
813         setup_action_normal_layer
814     },
815     {
816         "sendtobottomlayer",
817         action_send_to_layer,
818         setup_action_bottom_layer
819     },
820     {
821         "togglealwaysonbottom",
822         action_toggle_layer,
823         setup_action_bottom_layer
824     },
825     {
826         "nextwindow",
827         action_cycle_windows,
828         setup_action_cycle_windows_next
829     },
830     {
831         "previouswindow",
832         action_cycle_windows,
833         setup_action_cycle_windows_previous
834     },
835     {
836         "movefromedgenorth",
837         action_movetoedge,
838         setup_action_movefromedge_north
839     },
840     {
841         "movefromedgesouth",
842         action_movetoedge,
843         setup_action_movefromedge_south
844     },
845     {
846         "movefromedgewest",
847         action_movetoedge,
848         setup_action_movefromedge_west
849     },
850     {
851         "movefromedgeeast",
852         action_movetoedge,
853         setup_action_movefromedge_east
854     },
855     {
856         "movetoedgenorth",
857         action_movetoedge,
858         setup_action_movetoedge_north
859     },
860     {
861         "movetoedgesouth",
862         action_movetoedge,
863         setup_action_movetoedge_south
864     },
865     {
866         "movetoedgewest",
867         action_movetoedge,
868         setup_action_movetoedge_west
869     },
870     {
871         "movetoedgeeast",
872         action_movetoedge,
873         setup_action_movetoedge_east
874     },
875     {
876         "growtoedgenorth",
877         action_growtoedge,
878         setup_action_growtoedge_north
879     },
880     {
881         "growtoedgesouth",
882         action_growtoedge,
883         setup_action_growtoedge_south
884     },
885     {
886         "growtoedgewest",
887         action_growtoedge,
888         setup_action_growtoedge_west
889     },
890     {
891         "growtoedgeeast",
892         action_growtoedge,
893         setup_action_growtoedge_east
894     },
895     {
896         "breakchroot",
897         action_break_chroot,
898         NULL
899     },
900     {
901         NULL,
902         NULL,
903         NULL
904     }
905 };
906
907 /* only key bindings can be interactive. thus saith the xor.
908    because of how the mouse is grabbed, mouse events dont even get
909    read during interactive events, so no dice! >:) */
910 #define INTERACTIVE_LIMIT(a, uact) \
911     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
912         a->data.any.interactive = FALSE;
913
914 ObAction *action_from_string(const gchar *name, ObUserAction uact)
915 {
916     ObAction *a = NULL;
917     gboolean exist = FALSE;
918     gint i;
919
920     for (i = 0; actionstrings[i].name; i++)
921         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
922             exist = TRUE;
923             a = action_new(actionstrings[i].func);
924             if (actionstrings[i].setup)
925                 actionstrings[i].setup(&a, uact);
926             if (a)
927                 INTERACTIVE_LIMIT(a, uact);
928             break;
929         }
930     if (!exist)
931         g_message(_("Invalid action '%s' requested. No such action exists."),
932                   name);
933     if (!a)
934         g_message(_("Invalid use of action '%s'. Action will be ignored."),
935                   name);
936     return a;
937 }
938
939 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
940                        ObUserAction uact)
941 {
942     gchar *actname;
943     ObAction *act = NULL;
944     xmlNodePtr n;
945
946     if (parse_attr_string("name", node, &actname)) {
947         if ((act = action_from_string(actname, uact))) {
948             if (act->func == action_execute || act->func == action_restart) {
949                 if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
950                     gchar *s = parse_string(doc, n);
951                     act->data.execute.path = parse_expand_tilde(s);
952                     g_free(s);
953                 }
954                 if ((n = parse_find_node("startupnotify", node->xmlChildrenNode))) {
955                     xmlNodePtr m;
956                     if ((m = parse_find_node("enabled", n->xmlChildrenNode)))
957                         act->data.execute.startupnotify = parse_bool(doc, m);
958                     if ((m = parse_find_node("name", n->xmlChildrenNode)))
959                         act->data.execute.name = parse_string(doc, m);
960                     if ((m = parse_find_node("icon", n->xmlChildrenNode)))
961                         act->data.execute.icon_name = parse_string(doc, m);
962                 }
963             } else if (act->func == action_showmenu) {
964                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
965                     act->data.showmenu.name = parse_string(doc, n);
966             } else if (act->func == action_move_relative_horz ||
967                        act->func == action_move_relative_vert ||
968                        act->func == action_resize_relative_horz ||
969                        act->func == action_resize_relative_vert) {
970                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
971                     act->data.relative.deltax = parse_int(doc, n);
972             } else if (act->func == action_move_relative) {
973                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
974                     act->data.relative.deltax = parse_int(doc, n);
975                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
976                     act->data.relative.deltay = parse_int(doc, n);
977             } else if (act->func == action_resize_relative) {
978                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
979                     act->data.relative.deltaxl = parse_int(doc, n);
980                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
981                     act->data.relative.deltayu = parse_int(doc, n);
982                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
983                     act->data.relative.deltax = parse_int(doc, n);
984                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
985                     act->data.relative.deltay = parse_int(doc, n);
986             } else if (act->func == action_desktop) {
987                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
988                     act->data.desktop.desk = parse_int(doc, n);
989                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
990                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
991                     act->data.desktop.inter.any.interactive =
992                         parse_bool(doc, n);
993            } else if (act->func == action_send_to_desktop) {
994                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
995                     act->data.sendto.desk = parse_int(doc, n);
996                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
997                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
998                     act->data.sendto.follow = parse_bool(doc, n);
999             } else if (act->func == action_desktop_dir) {
1000                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1001                     act->data.desktopdir.wrap = parse_bool(doc, n); 
1002                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1003                     act->data.desktopdir.inter.any.interactive =
1004                         parse_bool(doc, n);
1005             } else if (act->func == action_send_to_desktop_dir) {
1006                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1007                     act->data.sendtodir.wrap = parse_bool(doc, n);
1008                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1009                     act->data.sendtodir.follow = parse_bool(doc, n);
1010                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1011                     act->data.sendtodir.inter.any.interactive =
1012                         parse_bool(doc, n);
1013             } else if (act->func == action_activate) {
1014                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
1015                     act->data.activate.here = parse_bool(doc, n);
1016             } else if (act->func == action_cycle_windows) {
1017                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
1018                     act->data.cycle.linear = parse_bool(doc, n);
1019                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1020                     act->data.cycle.dialog = parse_bool(doc, n);
1021                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1022                     act->data.cycle.dock_windows = parse_bool(doc, n);
1023             } else if (act->func == action_directional_focus) {
1024                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1025                     act->data.interdiraction.dialog = parse_bool(doc, n);
1026                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1027                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
1028             } else if (act->func == action_raise ||
1029                        act->func == action_lower ||
1030                        act->func == action_raiselower ||
1031                        act->func == action_shadelower ||
1032                        act->func == action_unshaderaise) {
1033             }
1034             INTERACTIVE_LIMIT(act, uact);
1035         }
1036         g_free(actname);
1037     }
1038     return act;
1039 }
1040
1041 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
1042                      guint state, guint button, gint x, gint y, Time time,
1043                      gboolean cancel, gboolean done)
1044 {
1045     GSList *it;
1046     ObAction *a;
1047     gboolean inter = FALSE;
1048
1049     if (!acts)
1050         return;
1051
1052     if (x < 0 && y < 0)
1053         screen_pointer_pos(&x, &y);
1054
1055     if (grab_on_keyboard())
1056         inter = TRUE;
1057     else
1058         for (it = acts; it; it = g_slist_next(it)) {
1059             a = it->data;
1060             if (a->data.any.interactive) {
1061                 inter = TRUE;
1062                 break;
1063             }
1064         }
1065
1066     if (!inter) {
1067         /* sometimes when we execute another app as an action,
1068            it won't work right unless we XUngrabKeyboard first,
1069            even though we grabbed the key/button Asychronously.
1070            e.g. "gnome-panel-control --main-menu" */
1071         grab_keyboard(FALSE);
1072     }
1073
1074     for (it = acts; it; it = g_slist_next(it)) {
1075         a = it->data;
1076
1077         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
1078             a->data.any.c = a->data.any.client_action ? c : NULL;
1079             a->data.any.context = context;
1080             a->data.any.x = x;
1081             a->data.any.y = y;
1082
1083             a->data.any.button = button;
1084
1085             a->data.any.time = time;
1086
1087             if (a->data.any.interactive) {
1088                 a->data.inter.cancel = cancel;
1089                 a->data.inter.final = done;
1090                 if (!(cancel || done))
1091                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
1092                         continue;
1093             }
1094
1095             /* XXX UGLY HACK race with motion event starting a move and the
1096                button release gettnig processed first. answer: don't queue
1097                moveresize starts. UGLY HACK XXX */
1098             if (a->data.any.interactive || a->func == action_moveresize) {
1099                 /* interactive actions are not queued */
1100                 a->func(&a->data);
1101             } else
1102                 ob_main_loop_queue_action(ob_main_loop, a);
1103         }
1104     }
1105 }
1106
1107 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1108 {
1109     ObAction *a;
1110     GSList *l;
1111
1112     a = action_from_string(name, OB_USER_ACTION_NONE);
1113     g_assert(a);
1114
1115     l = g_slist_append(NULL, a);
1116
1117     action_run(l, c, 0, time);
1118 }
1119
1120 void action_execute(union ActionData *data)
1121 {
1122     GError *e = NULL;
1123     gchar *cmd, **argv = 0;
1124     if (data->execute.path) {
1125         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
1126         if (cmd) {
1127             if (!g_shell_parse_argv (cmd, NULL, &argv, &e)) {
1128                 g_message(_("Failed to execute '%s': %s"),
1129                           cmd, e->message);
1130                 g_error_free(e);
1131             } else if (data->execute.startupnotify) {
1132                 gchar *program;
1133                 
1134                 program = g_path_get_basename(argv[0]);
1135                 /* sets up the environment */
1136                 sn_setup_spawn_environment(program,
1137                                            data->execute.name,
1138                                            data->execute.icon_name,
1139                                            /* launch it on the current
1140                                               desktop */
1141                                            screen_desktop,
1142                                            data->execute.any.time);
1143                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1144                                    G_SPAWN_DO_NOT_REAP_CHILD,
1145                                    NULL, NULL, NULL, &e)) {
1146                     g_message(_("Failed to execute '%s': %s"),
1147                               cmd, e->message);
1148                     g_error_free(e);
1149                     sn_spawn_cancel();
1150                 }
1151                 unsetenv("DESKTOP_STARTUP_ID");
1152                 g_free(program);
1153                 g_strfreev(argv);
1154             } else {
1155                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1156                                    G_SPAWN_DO_NOT_REAP_CHILD,
1157                                    NULL, NULL, NULL, &e))
1158                 {
1159                     g_message(_("Failed to execute '%s': %s"),
1160                               cmd, e->message);
1161                     g_error_free(e);
1162                 }
1163                 g_strfreev(argv);
1164             }
1165             g_free(cmd);
1166         } else {
1167             g_message(_("Failed to convert the path '%s' from utf8"),
1168                       data->execute.path);
1169         }
1170     }
1171 }
1172
1173 void action_activate(union ActionData *data)
1174 {
1175     /* similar to the openbox dock for dockapps, don't let user actions give
1176        focus to 3rd-party docks (panels) either (unless they ask for it
1177        themselves). */
1178     if (data->client.any.c->type != OB_CLIENT_TYPE_DOCK) {
1179         /* if using focus_delay, stop the timer now so that focus doesn't go
1180            moving on us */
1181         event_halt_focus_delay();
1182
1183         client_activate(data->activate.any.c, data->activate.here, TRUE);
1184     }
1185 }
1186
1187 void action_focus(union ActionData *data)
1188 {
1189     if (data->client.any.c) {
1190         /* similar to the openbox dock for dockapps, don't let user actions
1191            give focus to 3rd-party docks (panels) either (unless they ask for
1192            it themselves). */
1193         if (data->client.any.c->type != OB_CLIENT_TYPE_DOCK) {
1194             /* if using focus_delay, stop the timer now so that focus doesn't
1195                go moving on us */
1196             event_halt_focus_delay();
1197
1198             client_focus(data->client.any.c);
1199         }
1200     } else {
1201         /* focus action on something other than a client, make keybindings
1202            work for this openbox instance, but don't focus any specific client
1203         */
1204         focus_nothing();
1205     }
1206 }
1207
1208 void action_unfocus (union ActionData *data)
1209 {
1210     if (data->client.any.c == focus_client)
1211         focus_fallback(FALSE);
1212 }
1213
1214 void action_iconify(union ActionData *data)
1215 {
1216     client_action_start(data);
1217     client_iconify(data->client.any.c, TRUE, TRUE);
1218     client_action_end(data);
1219 }
1220
1221 void action_focus_order_to_bottom(union ActionData *data)
1222 {
1223     focus_order_to_bottom(data->client.any.c);
1224 }
1225
1226 void action_raiselower(union ActionData *data)
1227 {
1228     ObClient *c = data->client.any.c;
1229     GList *it;
1230     gboolean raise = FALSE;
1231
1232     for (it = stacking_list; it; it = g_list_next(it)) {
1233         if (WINDOW_IS_CLIENT(it->data)) {
1234             ObClient *cit = it->data;
1235
1236             if (cit == c) break;
1237             if (client_normal(cit) == client_normal(c) &&
1238                     cit->layer == c->layer &&
1239                     cit->frame->visible &&
1240                     !client_search_transient(c, cit))
1241             {
1242                 if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) {
1243                     raise = TRUE;
1244                     break;
1245                 }
1246             }
1247         }
1248     }
1249
1250     if (raise)
1251         action_raise(data);
1252     else
1253         action_lower(data);
1254 }
1255
1256 void action_raise(union ActionData *data)
1257 {
1258     client_action_start(data);
1259     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1260     client_action_end(data);
1261 }
1262
1263 void action_unshaderaise(union ActionData *data)
1264 {
1265     if (data->client.any.c->shaded)
1266         action_unshade(data);
1267     else
1268         action_raise(data);
1269 }
1270
1271 void action_shadelower(union ActionData *data)
1272 {
1273     if (data->client.any.c->shaded)
1274         action_lower(data);
1275     else
1276         action_shade(data);
1277 }
1278
1279 void action_lower(union ActionData *data)
1280 {
1281     client_action_start(data);
1282     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1283     client_action_end(data);
1284 }
1285
1286 void action_close(union ActionData *data)
1287 {
1288     client_close(data->client.any.c);
1289 }
1290
1291 void action_kill(union ActionData *data)
1292 {
1293     client_kill(data->client.any.c);
1294 }
1295
1296 void action_shade(union ActionData *data)
1297 {
1298     client_action_start(data);
1299     client_shade(data->client.any.c, TRUE);
1300     client_action_end(data);
1301 }
1302
1303 void action_unshade(union ActionData *data)
1304 {
1305     client_action_start(data);
1306     client_shade(data->client.any.c, FALSE);
1307     client_action_end(data);
1308 }
1309
1310 void action_toggle_shade(union ActionData *data)
1311 {
1312     client_action_start(data);
1313     client_shade(data->client.any.c, !data->client.any.c->shaded);
1314     client_action_end(data);
1315 }
1316
1317 void action_toggle_omnipresent(union ActionData *data)
1318
1319     client_set_desktop(data->client.any.c,
1320                        data->client.any.c->desktop == DESKTOP_ALL ?
1321                        screen_desktop : DESKTOP_ALL, FALSE);
1322 }
1323
1324 void action_move_relative_horz(union ActionData *data)
1325 {
1326     ObClient *c = data->relative.any.c;
1327     client_action_start(data);
1328     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1329     client_action_end(data);
1330 }
1331
1332 void action_move_relative_vert(union ActionData *data)
1333 {
1334     ObClient *c = data->relative.any.c;
1335     client_action_start(data);
1336     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1337     client_action_end(data);
1338 }
1339
1340 void action_move_to_center(union ActionData *data)
1341 {
1342     ObClient *c = data->client.any.c;
1343     Rect *area;
1344     area = screen_area_monitor(c->desktop, 0);
1345     client_action_start(data);
1346     client_move(c, area->width / 2 - c->area.width / 2,
1347                 area->height / 2 - c->area.height / 2);
1348     client_action_end(data);
1349 }
1350
1351 void action_resize_relative_horz(union ActionData *data)
1352 {
1353     ObClient *c = data->relative.any.c;
1354     client_action_start(data);
1355     client_resize(c,
1356                   c->area.width + data->relative.deltax * c->size_inc.width,
1357                   c->area.height);
1358     client_action_end(data);
1359 }
1360
1361 void action_resize_relative_vert(union ActionData *data)
1362 {
1363     ObClient *c = data->relative.any.c;
1364     if (!c->shaded) {
1365         client_action_start(data);
1366         client_resize(c, c->area.width, c->area.height +
1367                       data->relative.deltax * c->size_inc.height);
1368         client_action_end(data);
1369     }
1370 }
1371
1372 void action_move_relative(union ActionData *data)
1373 {
1374     ObClient *c = data->relative.any.c;
1375     client_action_start(data);
1376     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1377                 data->relative.deltay);
1378     client_action_end(data);
1379 }
1380
1381 void action_resize_relative(union ActionData *data)
1382 {
1383     ObClient *c = data->relative.any.c;
1384     gint x, y, ow, w, oh, h, lw, lh;
1385
1386     client_action_start(data);
1387
1388     x = c->area.x;
1389     y = c->area.y;
1390     ow = c->area.width;
1391     w = ow + data->relative.deltax * c->size_inc.width
1392         + data->relative.deltaxl * c->size_inc.width;
1393     oh = c->area.height;
1394     h = oh + data->relative.deltay * c->size_inc.height
1395         + data->relative.deltayu * c->size_inc.height;
1396     
1397     client_try_configure(c, OB_CORNER_TOPLEFT, &x, &y, &w, &h, &lw, &lh, TRUE);
1398     client_move_resize(c, x + (ow - w), y + (oh - h), w, h);
1399     client_action_end(data);
1400 }
1401
1402 void action_maximize_full(union ActionData *data)
1403 {
1404     client_action_start(data);
1405     client_maximize(data->client.any.c, TRUE, 0);
1406     client_action_end(data);
1407 }
1408
1409 void action_unmaximize_full(union ActionData *data)
1410 {
1411     client_action_start(data);
1412     client_maximize(data->client.any.c, FALSE, 0);
1413     client_action_end(data);
1414 }
1415
1416 void action_toggle_maximize_full(union ActionData *data)
1417 {
1418     client_action_start(data);
1419     client_maximize(data->client.any.c,
1420                     !(data->client.any.c->max_horz ||
1421                       data->client.any.c->max_vert),
1422                     0);
1423     client_action_end(data);
1424 }
1425
1426 void action_maximize_horz(union ActionData *data)
1427 {
1428     client_action_start(data);
1429     client_maximize(data->client.any.c, TRUE, 1);
1430     client_action_end(data);
1431 }
1432
1433 void action_unmaximize_horz(union ActionData *data)
1434 {
1435     client_action_start(data);
1436     client_maximize(data->client.any.c, FALSE, 1);
1437     client_action_end(data);
1438 }
1439
1440 void action_toggle_maximize_horz(union ActionData *data)
1441 {
1442     client_action_start(data);
1443     client_maximize(data->client.any.c,
1444                     !data->client.any.c->max_horz, 1);
1445     client_action_end(data);
1446 }
1447
1448 void action_maximize_vert(union ActionData *data)
1449 {
1450     client_action_start(data);
1451     client_maximize(data->client.any.c, TRUE, 2);
1452     client_action_end(data);
1453 }
1454
1455 void action_unmaximize_vert(union ActionData *data)
1456 {
1457     client_action_start(data);
1458     client_maximize(data->client.any.c, FALSE, 2);
1459     client_action_end(data);
1460 }
1461
1462 void action_toggle_maximize_vert(union ActionData *data)
1463 {
1464     client_action_start(data);
1465     client_maximize(data->client.any.c,
1466                     !data->client.any.c->max_vert, 2);
1467     client_action_end(data);
1468 }
1469
1470 void action_toggle_fullscreen(union ActionData *data)
1471 {
1472     client_action_start(data);
1473     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1474     client_action_end(data);
1475 }
1476
1477 void action_send_to_desktop(union ActionData *data)
1478 {
1479     ObClient *c = data->sendto.any.c;
1480
1481     if (!client_normal(c)) return;
1482
1483     if (data->sendto.desk < screen_num_desktops ||
1484         data->sendto.desk == DESKTOP_ALL) {
1485         client_set_desktop(c, data->sendto.desk, data->sendto.follow);
1486         if (data->sendto.follow)
1487             screen_set_desktop(data->sendto.desk);
1488     }
1489 }
1490
1491 void action_desktop(union ActionData *data)
1492 {
1493     static guint first = (unsigned) -1;
1494
1495     if (data->inter.any.interactive && first == (unsigned) -1)
1496         first = screen_desktop;
1497
1498     if (!data->inter.any.interactive ||
1499         (!data->inter.cancel && !data->inter.final))
1500     {
1501         if (data->desktop.desk < screen_num_desktops ||
1502             data->desktop.desk == DESKTOP_ALL)
1503         {
1504             screen_set_desktop(data->desktop.desk);
1505             if (data->inter.any.interactive)
1506                 screen_desktop_popup(data->desktop.desk, TRUE);
1507         }
1508     } else if (data->inter.cancel) {
1509         screen_set_desktop(first);
1510     }
1511
1512     if (!data->inter.any.interactive || data->inter.final) {
1513         screen_desktop_popup(0, FALSE);
1514         first = (unsigned) -1;
1515     }
1516 }
1517
1518 void action_desktop_dir(union ActionData *data)
1519 {
1520     guint d;
1521
1522     d = screen_cycle_desktop(data->desktopdir.dir,
1523                              data->desktopdir.wrap,
1524                              data->desktopdir.linear,
1525                              data->desktopdir.inter.any.interactive,
1526                              data->desktopdir.inter.final,
1527                              data->desktopdir.inter.cancel);
1528     if (!data->sendtodir.inter.any.interactive ||
1529         !data->sendtodir.inter.final ||
1530         data->sendtodir.inter.cancel)
1531     {
1532         screen_set_desktop(d);
1533     }
1534 }
1535
1536 void action_send_to_desktop_dir(union ActionData *data)
1537 {
1538     ObClient *c = data->sendtodir.inter.any.c;
1539     guint d;
1540
1541     if (!client_normal(c)) return;
1542
1543     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1544                              data->sendtodir.linear,
1545                              data->sendtodir.inter.any.interactive,
1546                              data->sendtodir.inter.final,
1547                              data->sendtodir.inter.cancel);
1548     if (!data->sendtodir.inter.any.interactive ||
1549         !data->sendtodir.inter.final ||
1550         data->sendtodir.inter.cancel)
1551     {
1552         client_set_desktop(c, d, data->sendtodir.follow);
1553         if (data->sendtodir.follow)
1554             screen_set_desktop(d);
1555     }
1556 }
1557
1558 void action_desktop_last(union ActionData *data)
1559 {
1560     screen_set_desktop(screen_last_desktop);
1561 }
1562
1563 void action_toggle_decorations(union ActionData *data)
1564 {
1565     ObClient *c = data->client.any.c;
1566
1567     client_action_start(data);
1568     client_set_undecorated(c, !c->undecorated);
1569     client_action_end(data);
1570 }
1571
1572 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch)
1573 {
1574     /* let's make x and y client relative instead of screen relative */
1575     x = x - cx;
1576     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1577
1578 #define X x*ch/cw
1579 #define A -4*X + 7*ch/3
1580 #define B  4*X -15*ch/9
1581 #define C -X/4 + 2*ch/3
1582 #define D  X/4 + 5*ch/12
1583 #define E  X/4 +   ch/3
1584 #define F -X/4 + 7*ch/12
1585 #define G  4*X - 4*ch/3
1586 #define H -4*X + 8*ch/3
1587 #define a (y > 5*ch/9)
1588 #define b (x < 4*cw/9)
1589 #define c (x > 5*cw/9)
1590 #define d (y < 4*ch/9)
1591
1592     /*
1593       Each of these defines (except X which is just there for fun), represents
1594       the equation of a line. The lines they represent are shown in the diagram
1595       below. Checking y against these lines, we are able to choose a region
1596       of the window as shown.
1597
1598       +---------------------A-------|-------|-------B---------------------+
1599       |                     |A                     B|                     |
1600       |                     |A      |       |      B|                     |
1601       |                     | A                   B |                     |
1602       |                     | A     |       |     B |                     |
1603       |                     |  A                 B  |                     |
1604       |                     |  A    |       |    B  |                     |
1605       |        northwest    |   A     north     B   |   northeast         |
1606       |                     |   A   |       |   B   |                     |
1607       |                     |    A             B    |                     |
1608       C---------------------+----A--+-------+--B----+---------------------D
1609       |CCCCCCC              |     A           B     |              DDDDDDD|
1610       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1611       |               CCCCCCC      A         B      DDDDDDD               |
1612       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - -
1613       |                     |       b       c       |                     |
1614       |             west    |       b  move c       |   east              |
1615       |                     |       b       c       |                     |
1616       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - 
1617       |               EEEEEEE      G         H      FFFFFFF               |
1618       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1619       |EEEEEEE              |     G           H     |              FFFFFFF|
1620       E---------------------+----G--+-------+--H----+---------------------F
1621       |                     |    G             H    |                     |
1622       |                     |   G   |       |   H   |                     |
1623       |        southwest    |   G     south     H   |   southeast         |
1624       |                     |  G    |       |    H  |                     |
1625       |                     |  G                 H  |                     |
1626       |                     | G     |       |     H |                     |
1627       |                     | G                   H |                     |
1628       |                     |G      |       |      H|                     |
1629       |                     |G                     H|                     |
1630       +---------------------G-------|-------|-------H---------------------+
1631     */
1632
1633     if (y < A && y >= C)
1634         return prop_atoms.net_wm_moveresize_size_topleft;
1635     else if (y >= A && y >= B && a)
1636         return prop_atoms.net_wm_moveresize_size_top;
1637     else if (y < B && y >= D)
1638         return prop_atoms.net_wm_moveresize_size_topright;
1639     else if (y < C && y >= E && b)
1640         return prop_atoms.net_wm_moveresize_size_left;
1641     else if (y < D && y >= F && c)
1642         return prop_atoms.net_wm_moveresize_size_right;
1643     else if (y < E && y >= G)
1644         return prop_atoms.net_wm_moveresize_size_bottomleft;
1645     else if (y < G && y < H && d)
1646         return prop_atoms.net_wm_moveresize_size_bottom;
1647     else if (y >= H && y < F)
1648         return prop_atoms.net_wm_moveresize_size_bottomright;
1649     else
1650         return prop_atoms.net_wm_moveresize_move;
1651
1652 #undef X
1653 #undef A
1654 #undef B
1655 #undef C
1656 #undef D
1657 #undef E
1658 #undef F
1659 #undef G
1660 #undef H
1661 #undef a
1662 #undef b
1663 #undef c
1664 #undef d
1665 }
1666
1667 void action_moveresize(union ActionData *data)
1668 {
1669     ObClient *c = data->moveresize.any.c;
1670     guint32 corner;
1671
1672     if (!client_normal(c)) return;
1673
1674     if (data->moveresize.keyboard) {
1675         corner = (data->moveresize.move ?
1676                   prop_atoms.net_wm_moveresize_move_keyboard :
1677                   prop_atoms.net_wm_moveresize_size_keyboard);
1678     } else {
1679         corner = (data->moveresize.move ?
1680                   prop_atoms.net_wm_moveresize_move :
1681                   pick_corner(data->any.x, data->any.y,
1682                               c->frame->area.x, c->frame->area.y,
1683                               /* use the client size because the frame
1684                                  can be differently sized (shaded
1685                                  windows) and we want this based on the
1686                                  clients size */
1687                               c->area.width + c->frame->size.left +
1688                               c->frame->size.right,
1689                               c->area.height + c->frame->size.top +
1690                               c->frame->size.bottom));
1691         const gchar *c;
1692         if (corner == prop_atoms.net_wm_moveresize_size_topright)
1693             c = "topright";
1694         else if (corner == prop_atoms.net_wm_moveresize_size_top)
1695             c = "top";
1696         else if (corner == prop_atoms.net_wm_moveresize_size_topleft)
1697             c = "topleft";
1698         else if (corner == prop_atoms.net_wm_moveresize_size_right)
1699             c = "right";
1700         else if (corner == prop_atoms.net_wm_moveresize_move)
1701             c = "middle";
1702         else if (corner == prop_atoms.net_wm_moveresize_size_left)
1703             c = "left";
1704         else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
1705             c = "bottomright";
1706         else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
1707             c = "bottom";
1708         else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
1709             c = "bottomleft";
1710         ob_debug("corner: %s\n", c);
1711     }
1712
1713     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1714 }
1715
1716 void action_reconfigure(union ActionData *data)
1717 {
1718     ob_reconfigure();
1719 }
1720
1721 void action_restart(union ActionData *data)
1722 {
1723     ob_restart_other(data->execute.path);
1724 }
1725
1726 void action_exit(union ActionData *data)
1727 {
1728     ob_exit(0);
1729 }
1730
1731 void action_showmenu(union ActionData *data)
1732 {
1733     if (data->showmenu.name) {
1734         menu_show(data->showmenu.name, data->any.x, data->any.y,
1735                   data->showmenu.any.c);
1736     }
1737 }
1738
1739 void action_cycle_windows(union ActionData *data)
1740 {
1741     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1742        on us */
1743     event_halt_focus_delay();
1744
1745     focus_cycle(data->cycle.forward,
1746                 data->cycle.dock_windows,
1747                 data->cycle.linear, data->any.interactive,
1748                 data->cycle.dialog,
1749                 data->cycle.inter.final, data->cycle.inter.cancel);
1750 }
1751
1752 void action_directional_focus(union ActionData *data)
1753 {
1754     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1755        on us */
1756     event_halt_focus_delay();
1757
1758     focus_directional_cycle(data->interdiraction.direction,
1759                             data->interdiraction.dock_windows,
1760                             data->any.interactive,
1761                             data->interdiraction.dialog,
1762                             data->interdiraction.inter.final,
1763                             data->interdiraction.inter.cancel);
1764 }
1765
1766 void action_movetoedge(union ActionData *data)
1767 {
1768     gint x, y;
1769     ObClient *c = data->diraction.any.c;
1770
1771     x = c->frame->area.x;
1772     y = c->frame->area.y;
1773     
1774     switch(data->diraction.direction) {
1775     case OB_DIRECTION_NORTH:
1776         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1777                                            data->diraction.hang)
1778             - (data->diraction.hang ? c->frame->area.height : 0);
1779         break;
1780     case OB_DIRECTION_WEST:
1781         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1782                                            data->diraction.hang)
1783             - (data->diraction.hang ? c->frame->area.width : 0);
1784         break;
1785     case OB_DIRECTION_SOUTH:
1786         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1787                                            data->diraction.hang)
1788             - (data->diraction.hang ? 0 : c->frame->area.height);
1789         break;
1790     case OB_DIRECTION_EAST:
1791         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1792                                            data->diraction.hang)
1793             - (data->diraction.hang ? 0 : c->frame->area.width);
1794         break;
1795     default:
1796         g_assert_not_reached();
1797     }
1798     frame_frame_gravity(c->frame, &x, &y);
1799     client_action_start(data);
1800     client_move(c, x, y);
1801     client_action_end(data);
1802 }
1803
1804 void action_growtoedge(union ActionData *data)
1805 {
1806     gint x, y, width, height, dest;
1807     ObClient *c = data->diraction.any.c;
1808     Rect *a;
1809
1810     //FIXME growtoedge resizes shaded windows to 0 height
1811     if (c->shaded)
1812         return;
1813
1814     a = screen_area(c->desktop);
1815     x = c->frame->area.x;
1816     y = c->frame->area.y;
1817     width = c->frame->area.width;
1818     height = c->frame->area.height;
1819
1820     switch(data->diraction.direction) {
1821     case OB_DIRECTION_NORTH:
1822         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1823         if (a->y == y)
1824             height = c->frame->area.height / 2;
1825         else {
1826             height = c->frame->area.y + c->frame->area.height - dest;
1827             y = dest;
1828         }
1829         break;
1830     case OB_DIRECTION_WEST:
1831         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1832         if (a->x == x)
1833             width = c->frame->area.width / 2;
1834         else {
1835             width = c->frame->area.x + c->frame->area.width - dest;
1836             x = dest;
1837         }
1838         break;
1839     case OB_DIRECTION_SOUTH:
1840         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1841         if (a->y + a->height == y + c->frame->area.height) {
1842             height = c->frame->area.height / 2;
1843             y = a->y + a->height - height;
1844         } else
1845             height = dest - c->frame->area.y;
1846         y += (height - c->frame->area.height) % c->size_inc.height;
1847         height -= (height - c->frame->area.height) % c->size_inc.height;
1848         break;
1849     case OB_DIRECTION_EAST:
1850         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1851         if (a->x + a->width == x + c->frame->area.width) {
1852             width = c->frame->area.width / 2;
1853             x = a->x + a->width - width;
1854         } else
1855             width = dest - c->frame->area.x;
1856         x += (width - c->frame->area.width) % c->size_inc.width;
1857         width -= (width - c->frame->area.width) % c->size_inc.width;
1858         break;
1859     default:
1860         g_assert_not_reached();
1861     }
1862     frame_frame_gravity(c->frame, &x, &y);
1863     width -= c->frame->size.left + c->frame->size.right;
1864     height -= c->frame->size.top + c->frame->size.bottom;
1865     client_action_start(data);
1866     client_move_resize(c, x, y, width, height);
1867     client_action_end(data);
1868 }
1869
1870 void action_send_to_layer(union ActionData *data)
1871 {
1872     client_set_layer(data->layer.any.c, data->layer.layer);
1873 }
1874
1875 void action_toggle_layer(union ActionData *data)
1876 {
1877     ObClient *c = data->layer.any.c;
1878
1879     client_action_start(data);
1880     if (data->layer.layer < 0)
1881         client_set_layer(c, c->below ? 0 : -1);
1882     else if (data->layer.layer > 0)
1883         client_set_layer(c, c->above ? 0 : 1);
1884     client_action_end(data);
1885 }
1886
1887 void action_toggle_dockautohide(union ActionData *data)
1888 {
1889     config_dock_hide = !config_dock_hide;
1890     dock_configure();
1891 }
1892
1893 void action_toggle_show_desktop(union ActionData *data)
1894 {
1895     screen_show_desktop(!screen_showing_desktop);
1896 }
1897
1898 void action_show_desktop(union ActionData *data)
1899 {
1900     screen_show_desktop(TRUE);
1901 }
1902
1903 void action_unshow_desktop(union ActionData *data)
1904 {
1905     screen_show_desktop(FALSE);
1906 }
1907
1908 void action_break_chroot(union ActionData *data)
1909 {
1910     /* break out of one chroot */
1911     keyboard_reset_chains(1);
1912 }