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