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