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