add maximize 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
43
44 typedef struct
45 {
46     const gchar *name;
47     void (*func)(union ActionData *);
48     void (*setup)(ObAction **, ObUserAction uact);
49 } ActionString;
50
51 static ObAction *action_new(void (*func)(union ActionData *data))
52 {
53     ObAction *a = g_new0(ObAction, 1);
54     a->ref = 1;
55     a->func = func;
56
57     return a;
58 }
59
60 void action_ref(ObAction *a)
61 {
62     ++a->ref;
63 }
64
65 void action_unref(ObAction *a)
66 {
67     if (a == NULL) return;
68
69     if (--a->ref > 0) return;
70
71     /* deal with pointers */
72     if (a->func == action_execute || a->func == action_restart)
73         g_free(a->data.execute.path);
74     else if (a->func == action_debug)
75         g_free(a->data.debug.string);
76     else if (a->func == action_showmenu)
77         g_free(a->data.showmenu.name);
78
79     g_free(a);
80 }
81
82 ObAction* action_copy(const ObAction *src)
83 {
84     ObAction *a = action_new(src->func);
85
86     a->data = src->data;
87
88     /* deal with pointers */
89     if (a->func == action_execute || a->func == action_restart)
90         a->data.execute.path = g_strdup(a->data.execute.path);
91     else if (a->func == action_debug)
92         a->data.debug.string = g_strdup(a->data.debug.string);
93     else if (a->func == action_showmenu)
94         a->data.showmenu.name = g_strdup(a->data.showmenu.name);
95
96     return a;
97 }
98
99 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
100 {
101     (*a)->data.interdiraction.inter.any.interactive = TRUE;
102     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
103     (*a)->data.interdiraction.dialog = TRUE;
104     (*a)->data.interdiraction.dock_windows = FALSE;
105     (*a)->data.interdiraction.desktop_windows = FALSE;
106 }
107
108 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
109 {
110     (*a)->data.interdiraction.inter.any.interactive = TRUE;
111     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
112     (*a)->data.interdiraction.dialog = TRUE;
113     (*a)->data.interdiraction.dock_windows = FALSE;
114     (*a)->data.interdiraction.desktop_windows = FALSE;
115 }
116
117 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
118 {
119     (*a)->data.interdiraction.inter.any.interactive = TRUE;
120     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
121     (*a)->data.interdiraction.dialog = TRUE;
122     (*a)->data.interdiraction.dock_windows = FALSE;
123     (*a)->data.interdiraction.desktop_windows = FALSE;
124 }
125
126 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
127 {
128     (*a)->data.interdiraction.inter.any.interactive = TRUE;
129     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
130     (*a)->data.interdiraction.dialog = TRUE;
131     (*a)->data.interdiraction.dock_windows = FALSE;
132     (*a)->data.interdiraction.desktop_windows = FALSE;
133 }
134
135 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
136 {
137     (*a)->data.interdiraction.inter.any.interactive = TRUE;
138     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
139     (*a)->data.interdiraction.dialog = TRUE;
140     (*a)->data.interdiraction.dock_windows = FALSE;
141     (*a)->data.interdiraction.desktop_windows = FALSE;
142 }
143
144 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
145 {
146     (*a)->data.interdiraction.inter.any.interactive = TRUE;
147     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
148     (*a)->data.interdiraction.dialog = TRUE;
149     (*a)->data.interdiraction.dock_windows = FALSE;
150     (*a)->data.interdiraction.desktop_windows = FALSE;
151 }
152
153 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
154 {
155     (*a)->data.interdiraction.inter.any.interactive = TRUE;
156     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
157     (*a)->data.interdiraction.dialog = TRUE;
158     (*a)->data.interdiraction.dock_windows = FALSE;
159     (*a)->data.interdiraction.desktop_windows = FALSE;
160 }
161
162 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
163 {
164     (*a)->data.interdiraction.inter.any.interactive = TRUE;
165     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
166     (*a)->data.interdiraction.dialog = TRUE;
167     (*a)->data.interdiraction.dock_windows = FALSE;
168     (*a)->data.interdiraction.desktop_windows = FALSE;
169 }
170
171 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
172 {
173     (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
174     (*a)->data.sendto.follow = TRUE;
175 }
176
177 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
178 {
179     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
180     (*a)->data.sendtodir.inter.any.interactive = TRUE;
181     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
182     (*a)->data.sendtodir.linear = TRUE;
183     (*a)->data.sendtodir.wrap = TRUE;
184     (*a)->data.sendtodir.follow = TRUE;
185 }
186
187 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
188 {
189     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
190     (*a)->data.sendtodir.inter.any.interactive = TRUE;
191     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
192     (*a)->data.sendtodir.linear = TRUE;
193     (*a)->data.sendtodir.wrap = TRUE;
194     (*a)->data.sendtodir.follow = TRUE;
195 }
196
197 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
198 {
199     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
200     (*a)->data.sendtodir.inter.any.interactive = TRUE;
201     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
202     (*a)->data.sendtodir.linear = FALSE;
203     (*a)->data.sendtodir.wrap = TRUE;
204     (*a)->data.sendtodir.follow = TRUE;
205 }
206
207 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
208 {
209     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
210     (*a)->data.sendtodir.inter.any.interactive = TRUE;
211     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
212     (*a)->data.sendtodir.linear = FALSE;
213     (*a)->data.sendtodir.wrap = TRUE;
214     (*a)->data.sendtodir.follow = TRUE;
215 }
216
217 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
218 {
219     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
220     (*a)->data.sendtodir.inter.any.interactive = TRUE;
221     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
222     (*a)->data.sendtodir.linear = FALSE;
223     (*a)->data.sendtodir.wrap = TRUE;
224     (*a)->data.sendtodir.follow = TRUE;
225 }
226
227 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
228 {
229     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
230     (*a)->data.sendtodir.inter.any.interactive = TRUE;
231     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
232     (*a)->data.sendtodir.linear = FALSE;
233     (*a)->data.sendtodir.wrap = TRUE;
234     (*a)->data.sendtodir.follow = TRUE;
235 }
236
237 void setup_action_desktop(ObAction **a, ObUserAction uact)
238 {
239 /*
240     (*a)->data.desktop.inter.any.interactive = FALSE;
241 */
242 }
243
244 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
245 {
246     (*a)->data.desktopdir.inter.any.interactive = TRUE;
247     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
248     (*a)->data.desktopdir.linear = TRUE;
249     (*a)->data.desktopdir.wrap = TRUE;
250 }
251
252 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
253 {
254     (*a)->data.desktopdir.inter.any.interactive = TRUE;
255     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
256     (*a)->data.desktopdir.linear = TRUE;
257     (*a)->data.desktopdir.wrap = TRUE;
258 }
259
260 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
261 {
262     (*a)->data.desktopdir.inter.any.interactive = TRUE;
263     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
264     (*a)->data.desktopdir.linear = FALSE;
265     (*a)->data.desktopdir.wrap = TRUE;
266 }
267
268 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
269 {
270     (*a)->data.desktopdir.inter.any.interactive = TRUE;
271     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
272     (*a)->data.desktopdir.linear = FALSE;
273     (*a)->data.desktopdir.wrap = TRUE;
274 }
275
276 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
277 {
278     (*a)->data.desktopdir.inter.any.interactive = TRUE;
279     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
280     (*a)->data.desktopdir.linear = FALSE;
281     (*a)->data.desktopdir.wrap = TRUE;
282 }
283
284 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
285 {
286     (*a)->data.desktopdir.inter.any.interactive = TRUE;
287     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
288     (*a)->data.desktopdir.linear = FALSE;
289     (*a)->data.desktopdir.wrap = TRUE;
290 }
291
292 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
293 {
294     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
295     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
296     (*a)->data.diraction.hang = TRUE;
297 }
298
299 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
300 {
301     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
302     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
303     (*a)->data.diraction.hang = TRUE;
304 }
305
306 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
307 {
308     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
309     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
310     (*a)->data.diraction.hang = TRUE;
311 }
312
313 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
314 {
315     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
316     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
317     (*a)->data.diraction.hang = TRUE;
318 }
319
320 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
321 {
322     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
323     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
324     (*a)->data.diraction.hang = FALSE;
325 }
326
327 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
328 {
329     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
330     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
331     (*a)->data.diraction.hang = FALSE;
332 }
333
334 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
335 {
336     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
337     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
338     (*a)->data.diraction.hang = FALSE;
339 }
340
341 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
342 {
343     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
344     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
345     (*a)->data.diraction.hang = FALSE;
346 }
347
348 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
349 {
350     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
351     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
352 }
353
354 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
355 {
356     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
357     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
358 }
359
360 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
361 {
362     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
363     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
364 }
365
366 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
367 {
368     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
369     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
370 }
371
372 void setup_action_top_layer(ObAction **a, ObUserAction uact)
373 {
374     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
375     (*a)->data.layer.layer = 1;
376 }
377
378 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
379 {
380     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
381     (*a)->data.layer.layer = 0;
382 }
383
384 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
385 {
386     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
387     (*a)->data.layer.layer = -1;
388 }
389
390 void setup_action_resize(ObAction **a, ObUserAction uact)
391 {
392     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
393     (*a)->data.moveresize.keyboard =
394         (uact == OB_USER_ACTION_NONE ||
395          uact == OB_USER_ACTION_KEYBOARD_KEY ||
396          uact == OB_USER_ACTION_MENU_SELECTION);
397     (*a)->data.moveresize.corner = 0;
398 }
399
400 void setup_action_addremove_desktop_current(ObAction **a, ObUserAction uact)
401 {
402     (*a)->data.addremovedesktop.current = TRUE;
403 }
404
405 void setup_action_addremove_desktop_last(ObAction **a, ObUserAction uact)
406 {
407     (*a)->data.addremovedesktop.current = FALSE;
408 }
409
410 void setup_client_action(ObAction **a, ObUserAction uact)
411 {
412     (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
413 }
414
415 ActionString actionstrings[] =
416 {
417     {
418         "directionalfocusnorth", 
419         action_directional_focus, 
420         setup_action_directional_focus_north
421     },
422     {
423         "directionalfocuseast", 
424         action_directional_focus, 
425         setup_action_directional_focus_east
426     },
427     {
428         "directionalfocussouth", 
429         action_directional_focus, 
430         setup_action_directional_focus_south
431     },
432     {
433         "directionalfocuswest",
434         action_directional_focus,
435         setup_action_directional_focus_west
436     },
437     {
438         "directionalfocusnortheast",
439         action_directional_focus,
440         setup_action_directional_focus_northeast
441     },
442     {
443         "directionalfocussoutheast",
444         action_directional_focus,
445         setup_action_directional_focus_southeast
446     },
447     {
448         "directionalfocussouthwest",
449         action_directional_focus,
450         setup_action_directional_focus_southwest
451     },
452     {
453         "directionalfocusnorthwest",
454         action_directional_focus,
455         setup_action_directional_focus_northwest
456     },
457     {
458         "kill",
459         action_kill,
460         setup_client_action
461     },
462     {
463         "shadelower",
464         action_shadelower,
465         setup_client_action
466     },
467     {
468         "unshaderaise",
469         action_unshaderaise,
470         setup_client_action
471     },
472     {
473         "shade",
474         action_shade,
475         setup_client_action
476     },
477     {
478         "unshade",
479         action_unshade,
480         setup_client_action
481     },
482     {
483         "toggleshade",
484         action_toggle_shade,
485         setup_client_action
486     },
487     {
488         "toggleomnipresent",
489         action_toggle_omnipresent,
490         setup_client_action
491     },
492     {
493         "moverelativehorz",
494         action_move_relative_horz,
495         setup_client_action
496     },
497     {
498         "moverelativevert",
499         action_move_relative_vert,
500         setup_client_action
501     },
502     {
503         "movetocenter",
504         action_move_to_center,
505         setup_client_action
506     },
507     {
508         "resizerelativehorz",
509         action_resize_relative_horz,
510         setup_client_action
511     },
512     {
513         "resizerelativevert",
514         action_resize_relative_vert,
515         setup_client_action
516     },
517     {
518         "moverelative",
519         action_move_relative,
520         setup_client_action
521     },
522     {
523         "resizerelative",
524         action_resize_relative,
525         setup_client_action
526     },
527     {
528         "maximizehorz",
529         action_maximize_horz,
530         setup_client_action
531     },
532     {
533         "unmaximizehorz",
534         action_unmaximize_horz,
535         setup_client_action
536     },
537     {
538         "togglemaximizehorz",
539         action_toggle_maximize_horz,
540         setup_client_action
541     },
542     {
543         "maximizevert",
544         action_maximize_vert,
545         setup_client_action
546     },
547     {
548         "unmaximizevert",
549         action_unmaximize_vert,
550         setup_client_action
551     },
552     {
553         "togglemaximizevert",
554         action_toggle_maximize_vert,
555         setup_client_action
556     },
557     {
558         "sendtodesktop",
559         action_send_to_desktop,
560         setup_action_send_to_desktop
561     },
562     {
563         "sendtodesktopnext",
564         action_send_to_desktop_dir,
565         setup_action_send_to_desktop_next
566     },
567     {
568         "sendtodesktopprevious",
569         action_send_to_desktop_dir,
570         setup_action_send_to_desktop_prev
571     },
572     {
573         "sendtodesktopright",
574         action_send_to_desktop_dir,
575         setup_action_send_to_desktop_right
576     },
577     {
578         "sendtodesktopleft",
579         action_send_to_desktop_dir,
580         setup_action_send_to_desktop_left
581     },
582     {
583         "sendtodesktopup",
584         action_send_to_desktop_dir,
585         setup_action_send_to_desktop_up
586     },
587     {
588         "sendtodesktopdown",
589         action_send_to_desktop_dir,
590         setup_action_send_to_desktop_down
591     },
592     {
593         "desktop",
594         action_desktop,
595         setup_action_desktop
596     },
597     {
598         "desktopnext",
599         action_desktop_dir,
600         setup_action_desktop_next
601     },
602     {
603         "desktopprevious",
604         action_desktop_dir,
605         setup_action_desktop_prev
606     },
607     {
608         "desktopright",
609         action_desktop_dir,
610         setup_action_desktop_right
611     },
612     {
613         "desktopleft",
614         action_desktop_dir,
615         setup_action_desktop_left
616     },
617     {
618         "desktopup",
619         action_desktop_dir,
620         setup_action_desktop_up
621     },
622     {
623         "desktopdown",
624         action_desktop_dir,
625         setup_action_desktop_down
626     },
627     {
628         "toggledecorations",
629         action_toggle_decorations,
630         setup_client_action
631     },
632     {
633         "resize",
634         action_resize,
635         setup_action_resize
636     },
637     {
638         "toggledockautohide",
639         action_toggle_dockautohide,
640         NULL
641     },
642     {
643         "desktoplast",
644         action_desktop_last,
645         NULL
646     },
647     {
648         "sendtotoplayer",
649         action_send_to_layer,
650         setup_action_top_layer
651     },
652     {
653         "togglealwaysontop",
654         action_toggle_layer,
655         setup_action_top_layer
656     },
657     {
658         "sendtonormallayer",
659         action_send_to_layer,
660         setup_action_normal_layer
661     },
662     {
663         "sendtobottomlayer",
664         action_send_to_layer,
665         setup_action_bottom_layer
666     },
667     {
668         "togglealwaysonbottom",
669         action_toggle_layer,
670         setup_action_bottom_layer
671     },
672     {
673         "movefromedgenorth",
674         action_movetoedge,
675         setup_action_movefromedge_north
676     },
677     {
678         "movefromedgesouth",
679         action_movetoedge,
680         setup_action_movefromedge_south
681     },
682     {
683         "movefromedgewest",
684         action_movetoedge,
685         setup_action_movefromedge_west
686     },
687     {
688         "movefromedgeeast",
689         action_movetoedge,
690         setup_action_movefromedge_east
691     },
692     {
693         "movetoedgenorth",
694         action_movetoedge,
695         setup_action_movetoedge_north
696     },
697     {
698         "movetoedgesouth",
699         action_movetoedge,
700         setup_action_movetoedge_south
701     },
702     {
703         "movetoedgewest",
704         action_movetoedge,
705         setup_action_movetoedge_west
706     },
707     {
708         "movetoedgeeast",
709         action_movetoedge,
710         setup_action_movetoedge_east
711     },
712     {
713         "growtoedgenorth",
714         action_growtoedge,
715         setup_action_growtoedge_north
716     },
717     {
718         "growtoedgesouth",
719         action_growtoedge,
720         setup_action_growtoedge_south
721     },
722     {
723         "growtoedgewest",
724         action_growtoedge,
725         setup_action_growtoedge_west
726     },
727     {
728         "growtoedgeeast",
729         action_growtoedge,
730         setup_action_growtoedge_east
731     },
732     {
733         "adddesktoplast",
734         action_add_desktop,
735         setup_action_addremove_desktop_last
736     },
737     {
738         "removedesktoplast",
739         action_remove_desktop,
740         setup_action_addremove_desktop_last
741     },
742     {
743         "adddesktopcurrent",
744         action_add_desktop,
745         setup_action_addremove_desktop_current
746     },
747     {
748         "removedesktopcurrent",
749         action_remove_desktop,
750         setup_action_addremove_desktop_current
751     },
752     {
753         NULL,
754         NULL,
755         NULL
756     }
757 };
758
759 /* only key bindings can be interactive. thus saith the xor.
760    because of how the mouse is grabbed, mouse events dont even get
761    read during interactive events, so no dice! >:) */
762 #define INTERACTIVE_LIMIT(a, uact) \
763     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
764         a->data.any.interactive = FALSE;
765
766 ObAction *action_from_string(const gchar *name, ObUserAction uact)
767 {
768     ObAction *a = NULL;
769     gboolean exist = FALSE;
770     gint i;
771
772     for (i = 0; actionstrings[i].name; i++)
773         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
774             exist = TRUE;
775             a = action_new(actionstrings[i].func);
776             if (actionstrings[i].setup)
777                 actionstrings[i].setup(&a, uact);
778             if (a)
779                 INTERACTIVE_LIMIT(a, uact);
780             break;
781         }
782     if (!exist)
783         g_message(_("Invalid action '%s' requested. No such action exists."),
784                   name);
785     if (!a)
786         g_message(_("Invalid use of action '%s'. Action will be ignored."),
787                   name);
788     return a;
789 }
790
791 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
792                        ObUserAction uact)
793 {
794     gchar *actname;
795     ObAction *act = NULL;
796     xmlNodePtr n;
797
798     if (parse_attr_string("name", node, &actname)) {
799         if ((act = action_from_string(actname, uact))) {
800             } else if (act->func == action_move_relative_horz ||
801                        act->func == action_move_relative_vert ||
802                        act->func == action_resize_relative_horz ||
803                        act->func == action_resize_relative_vert) {
804                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
805                     act->data.relative.deltax = parse_int(doc, n);
806             } else if (act->func == action_move_relative) {
807                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
808                     act->data.relative.deltax = parse_int(doc, n);
809                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
810                     act->data.relative.deltay = parse_int(doc, n);
811             } else if (act->func == action_resize_relative) {
812                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
813                     act->data.relative.deltaxl = parse_int(doc, n);
814                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
815                     act->data.relative.deltayu = parse_int(doc, n);
816                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
817                     act->data.relative.deltax = parse_int(doc, n);
818                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
819                     act->data.relative.deltay = parse_int(doc, n);
820             } else if (act->func == action_desktop) {
821                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
822                     act->data.desktop.desk = parse_int(doc, n);
823                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
824 /*
825                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
826                     act->data.desktop.inter.any.interactive =
827                         parse_bool(doc, n);
828 */
829            } else if (act->func == action_send_to_desktop) {
830                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
831                     act->data.sendto.desk = parse_int(doc, n);
832                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
833                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
834                     act->data.sendto.follow = parse_bool(doc, n);
835             } else if (act->func == action_desktop_dir) {
836                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
837                     act->data.desktopdir.wrap = parse_bool(doc, n); 
838                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
839                     act->data.desktopdir.inter.any.interactive =
840                         parse_bool(doc, n);
841             } else if (act->func == action_send_to_desktop_dir) {
842                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
843                     act->data.sendtodir.wrap = parse_bool(doc, n);
844                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
845                     act->data.sendtodir.follow = parse_bool(doc, n);
846                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
847                     act->data.sendtodir.inter.any.interactive =
848                         parse_bool(doc, n);
849             } else if (act->func == action_directional_focus) {
850                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
851                     act->data.interdiraction.dialog = parse_bool(doc, n);
852                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
853                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
854                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
855                     act->data.interdiraction.desktop_windows =
856                         parse_bool(doc, n);
857             } else if (act->func == action_resize) {
858                 if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
859                     gchar *s = parse_string(doc, n);
860                     if (!g_ascii_strcasecmp(s, "top"))
861                         act->data.moveresize.corner =
862                             prop_atoms.net_wm_moveresize_size_top;
863                     else if (!g_ascii_strcasecmp(s, "bottom"))
864                         act->data.moveresize.corner =
865                             prop_atoms.net_wm_moveresize_size_bottom;
866                     else if (!g_ascii_strcasecmp(s, "left"))
867                         act->data.moveresize.corner =
868                             prop_atoms.net_wm_moveresize_size_left;
869                     else if (!g_ascii_strcasecmp(s, "right"))
870                         act->data.moveresize.corner =
871                             prop_atoms.net_wm_moveresize_size_right;
872                     else if (!g_ascii_strcasecmp(s, "topleft"))
873                         act->data.moveresize.corner =
874                             prop_atoms.net_wm_moveresize_size_topleft;
875                     else if (!g_ascii_strcasecmp(s, "topright"))
876                         act->data.moveresize.corner =
877                             prop_atoms.net_wm_moveresize_size_topright;
878                     else if (!g_ascii_strcasecmp(s, "bottomleft"))
879                         act->data.moveresize.corner =
880                             prop_atoms.net_wm_moveresize_size_bottomleft;
881                     else if (!g_ascii_strcasecmp(s, "bottomright"))
882                         act->data.moveresize.corner =
883                             prop_atoms.net_wm_moveresize_size_bottomright;
884                     g_free(s);
885                 }
886             INTERACTIVE_LIMIT(act, uact);
887         }
888         g_free(actname);
889     }
890     return act;
891 }
892
893 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
894                      guint state, guint button, gint x, gint y, Time time,
895                      gboolean cancel, gboolean done)
896 {
897     GSList *it;
898     ObAction *a;
899
900     if (!acts)
901         return;
902
903     if (x < 0 && y < 0)
904         screen_pointer_pos(&x, &y);
905
906     for (it = acts; it; it = g_slist_next(it)) {
907         a = it->data;
908
909         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
910             a->data.any.c = a->data.any.client_action ? c : NULL;
911             a->data.any.context = context;
912             a->data.any.x = x;
913             a->data.any.y = y;
914
915             a->data.any.button = button;
916
917             a->data.any.time = time;
918
919             if (a->data.any.interactive) {
920                 a->data.inter.cancel = cancel;
921                 a->data.inter.final = done;
922                 if (!(cancel || done))
923                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
924                         continue;
925             }
926
927             /* XXX UGLY HACK race with motion event starting a move and the
928                button release gettnig processed first. answer: don't queue
929                moveresize starts. UGLY HACK XXX
930
931                XXX ALSO don't queue showmenu events, because on button press
932                events we need to know if a mouse grab is going to take place,
933                and set the button to 0, so that later motion events don't think
934                that a drag is going on. since showmenu grabs the pointer..
935             */
936             if (a->data.any.interactive || a->func == action_move ||
937                 a->func == action_resize || a->func == action_showmenu)
938             {
939                 /* interactive actions are not queued */
940                 a->func(&a->data);
941             } else if (a->func == action_focus ||
942                        a->func == action_activate ||
943                        a->func == action_showmenu)
944             {
945                 /* XXX MORE UGLY HACK
946                    actions from clicks on client windows are NOT queued.
947                    this solves the mysterious click-and-drag-doesnt-work
948                    problem. it was because the window gets focused and stuff
949                    after the button event has already been passed through. i
950                    dont really know why it should care but it does and it makes
951                    a difference.
952
953                    however this very bogus ! !
954                    we want to send the button press to the window BEFORE
955                    we do the action because the action might move the windows
956                    (eg change desktops) and then the button press ends up on
957                    the completely wrong window !
958                    so, this is just for that bug, and it will only NOT queue it
959                    if it is a focusing action that can be used with the mouse
960                    pointer. ugh.
961
962                    also with the menus, there is a race going on. if the
963                    desktop wants to pop up a menu, and we do too, we send them
964                    the button before we pop up the menu, so they pop up their
965                    menu first. but not always. if we pop up our menu before
966                    sending them the button press, then the result is
967                    deterministic. yay.
968
969                    XXX further more. focus actions are not queued at all,
970                    because if you bind focus->showmenu, the menu will get
971                    hidden to do the focusing
972                 */
973                 a->func(&a->data);
974             } else
975                 ob_main_loop_queue_action(ob_main_loop, a);
976         }
977     }
978 }
979
980 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
981 {
982     ObAction *a;
983     GSList *l;
984
985     a = action_from_string(name, OB_USER_ACTION_NONE);
986     g_assert(a);
987
988     l = g_slist_append(NULL, a);
989
990     action_run(l, c, 0, time);
991 }
992
993 void action_unshaderaise(union ActionData *data)
994 {
995     if (data->client.any.c->shaded)
996         action_unshade(data);
997     else
998         action_raise(data);
999 }
1000
1001 void action_shadelower(union ActionData *data)
1002 {
1003     if (data->client.any.c->shaded)
1004         action_lower(data);
1005     else
1006         action_shade(data);
1007 }
1008
1009 void action_kill(union ActionData *data)
1010 {
1011     client_kill(data->client.any.c);
1012 }
1013
1014 void action_shade(union ActionData *data)
1015 {
1016     client_action_start(data);
1017     client_shade(data->client.any.c, TRUE);
1018     client_action_end(data, config_focus_under_mouse);
1019 }
1020
1021 void action_unshade(union ActionData *data)
1022 {
1023     client_action_start(data);
1024     client_shade(data->client.any.c, FALSE);
1025     client_action_end(data, config_focus_under_mouse);
1026 }
1027
1028 void action_toggle_shade(union ActionData *data)
1029 {
1030     client_action_start(data);
1031     client_shade(data->client.any.c, !data->client.any.c->shaded);
1032     client_action_end(data, config_focus_under_mouse);
1033 }
1034
1035 void action_toggle_omnipresent(union ActionData *data)
1036
1037     client_set_desktop(data->client.any.c,
1038                        data->client.any.c->desktop == DESKTOP_ALL ?
1039                        screen_desktop : DESKTOP_ALL, FALSE, TRUE);
1040 }
1041
1042 void action_move_relative_horz(union ActionData *data)
1043 {
1044     ObClient *c = data->relative.any.c;
1045     client_action_start(data);
1046     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1047     client_action_end(data, FALSE);
1048 }
1049
1050 void action_move_relative_vert(union ActionData *data)
1051 {
1052     ObClient *c = data->relative.any.c;
1053     client_action_start(data);
1054     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1055     client_action_end(data, FALSE);
1056 }
1057
1058 void action_move_to_center(union ActionData *data)
1059 {
1060     ObClient *c = data->client.any.c;
1061     Rect *area;
1062     area = screen_area(c->desktop, client_monitor(c), NULL);
1063     client_action_start(data);
1064     client_move(c, area->x + area->width / 2 - c->area.width / 2,
1065                 area->y + area->height / 2 - c->area.height / 2);
1066     client_action_end(data, FALSE);
1067     g_free(area);
1068 }
1069
1070 void action_resize_relative_horz(union ActionData *data)
1071 {
1072     ObClient *c = data->relative.any.c;
1073     client_action_start(data);
1074     client_resize(c,
1075                   c->area.width + data->relative.deltax * c->size_inc.width,
1076                   c->area.height);
1077     client_action_end(data, FALSE);
1078 }
1079
1080 void action_resize_relative_vert(union ActionData *data)
1081 {
1082     ObClient *c = data->relative.any.c;
1083     if (!c->shaded) {
1084         client_action_start(data);
1085         client_resize(c, c->area.width, c->area.height +
1086                       data->relative.deltax * c->size_inc.height);
1087         client_action_end(data, FALSE);
1088     }
1089 }
1090
1091 void action_move_relative(union ActionData *data)
1092 {
1093     ObClient *c = data->relative.any.c;
1094     client_action_start(data);
1095     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1096                 data->relative.deltay);
1097     client_action_end(data, FALSE);
1098 }
1099
1100 void action_resize_relative(union ActionData *data)
1101 {
1102     ObClient *c = data->relative.any.c;
1103     gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
1104
1105     client_action_start(data);
1106
1107     x = c->area.x;
1108     y = c->area.y;
1109     ow = c->area.width;
1110     xoff = -data->relative.deltaxl * c->size_inc.width;
1111     nw = ow + data->relative.deltax * c->size_inc.width
1112         + data->relative.deltaxl * c->size_inc.width;
1113     oh = c->area.height;
1114     yoff = -data->relative.deltayu * c->size_inc.height;
1115     nh = oh + data->relative.deltay * c->size_inc.height
1116         + data->relative.deltayu * c->size_inc.height;
1117
1118     g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
1119             data->relative.deltax, 
1120             data->relative.deltaxl, 
1121             x, ow, xoff, nw);
1122     
1123     client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
1124     xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
1125     yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
1126     client_move_resize(c, x + xoff, y + yoff, nw, nh);
1127     client_action_end(data, FALSE);
1128 }
1129
1130 void action_maximize_horz(union ActionData *data)
1131 {
1132     client_action_start(data);
1133     client_maximize(data->client.any.c, TRUE, 1);
1134     client_action_end(data, config_focus_under_mouse);
1135 }
1136
1137 void action_unmaximize_horz(union ActionData *data)
1138 {
1139     client_action_start(data);
1140     client_maximize(data->client.any.c, FALSE, 1);
1141     client_action_end(data, config_focus_under_mouse);
1142 }
1143
1144 void action_toggle_maximize_horz(union ActionData *data)
1145 {
1146     client_action_start(data);
1147     client_maximize(data->client.any.c,
1148                     !data->client.any.c->max_horz, 1);
1149     client_action_end(data, config_focus_under_mouse);
1150 }
1151
1152 void action_maximize_vert(union ActionData *data)
1153 {
1154     client_action_start(data);
1155     client_maximize(data->client.any.c, TRUE, 2);
1156     client_action_end(data, config_focus_under_mouse);
1157 }
1158
1159 void action_unmaximize_vert(union ActionData *data)
1160 {
1161     client_action_start(data);
1162     client_maximize(data->client.any.c, FALSE, 2);
1163     client_action_end(data, config_focus_under_mouse);
1164 }
1165
1166 void action_toggle_maximize_vert(union ActionData *data)
1167 {
1168     client_action_start(data);
1169     client_maximize(data->client.any.c,
1170                     !data->client.any.c->max_vert, 2);
1171     client_action_end(data, config_focus_under_mouse);
1172 }
1173
1174 void action_send_to_desktop(union ActionData *data)
1175 {
1176     ObClient *c = data->sendto.any.c;
1177
1178     if (!client_normal(c)) return;
1179
1180     if (data->sendto.desk < screen_num_desktops ||
1181         data->sendto.desk == DESKTOP_ALL) {
1182         client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
1183         if (data->sendto.follow && data->sendto.desk != screen_desktop)
1184             screen_set_desktop(data->sendto.desk, TRUE);
1185     }
1186 }
1187
1188 void action_desktop(union ActionData *data)
1189 {
1190     /* XXX add the interactive/dialog option back again once the dialog
1191        has been made to not use grabs */
1192     if (data->desktop.desk < screen_num_desktops ||
1193         data->desktop.desk == DESKTOP_ALL)
1194     {
1195         screen_set_desktop(data->desktop.desk, TRUE);
1196         if (data->inter.any.interactive)
1197             screen_desktop_popup(data->desktop.desk, TRUE);
1198     }
1199 }
1200
1201 void action_desktop_dir(union ActionData *data)
1202 {
1203     guint d;
1204
1205     d = screen_cycle_desktop(data->desktopdir.dir,
1206                              data->desktopdir.wrap,
1207                              data->desktopdir.linear,
1208                              data->desktopdir.inter.any.interactive,
1209                              data->desktopdir.inter.final,
1210                              data->desktopdir.inter.cancel);
1211     /* only move the desktop when the action is complete. if we switch
1212        desktops during the interactive action, focus will move but with
1213        NotifyWhileGrabbed and applications don't like that. */
1214     if (!data->sendtodir.inter.any.interactive ||
1215         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1216     {
1217         if (d != screen_desktop)
1218             screen_set_desktop(d, TRUE);
1219     }
1220 }
1221
1222 void action_send_to_desktop_dir(union ActionData *data)
1223 {
1224     ObClient *c = data->sendtodir.inter.any.c;
1225     guint d;
1226
1227     if (!client_normal(c)) return;
1228
1229     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1230                              data->sendtodir.linear,
1231                              data->sendtodir.inter.any.interactive,
1232                              data->sendtodir.inter.final,
1233                              data->sendtodir.inter.cancel);
1234     /* only move the desktop when the action is complete. if we switch
1235        desktops during the interactive action, focus will move but with
1236        NotifyWhileGrabbed and applications don't like that. */
1237     if (!data->sendtodir.inter.any.interactive ||
1238         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1239     {
1240         client_set_desktop(c, d, data->sendtodir.follow, FALSE);
1241         if (data->sendtodir.follow && d != screen_desktop)
1242             screen_set_desktop(d, TRUE);
1243     }
1244 }
1245
1246 void action_desktop_last(union ActionData *data)
1247 {
1248     if (screen_last_desktop < screen_num_desktops)
1249         screen_set_desktop(screen_last_desktop, TRUE);
1250 }
1251
1252 void action_toggle_decorations(union ActionData *data)
1253 {
1254     ObClient *c = data->client.any.c;
1255
1256     client_action_start(data);
1257     client_set_undecorated(c, !c->undecorated);
1258     client_action_end(data, FALSE);
1259 }
1260
1261 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1262                            gboolean shaded)
1263 {
1264     /* let's make x and y client relative instead of screen relative */
1265     x = x - cx;
1266     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1267
1268 #define X x*ch/cw
1269 #define A -4*X + 7*ch/3
1270 #define B  4*X -15*ch/9
1271 #define C -X/4 + 2*ch/3
1272 #define D  X/4 + 5*ch/12
1273 #define E  X/4 +   ch/3
1274 #define F -X/4 + 7*ch/12
1275 #define G  4*X - 4*ch/3
1276 #define H -4*X + 8*ch/3
1277 #define a (y > 5*ch/9)
1278 #define b (x < 4*cw/9)
1279 #define c (x > 5*cw/9)
1280 #define d (y < 4*ch/9)
1281
1282     /*
1283       Each of these defines (except X which is just there for fun), represents
1284       the equation of a line. The lines they represent are shown in the diagram
1285       below. Checking y against these lines, we are able to choose a region
1286       of the window as shown.
1287
1288       +---------------------A-------|-------|-------B---------------------+
1289       |                     |A                     B|                     |
1290       |                     |A      |       |      B|                     |
1291       |                     | A                   B |                     |
1292       |                     | A     |       |     B |                     |
1293       |                     |  A                 B  |                     |
1294       |                     |  A    |       |    B  |                     |
1295       |        northwest    |   A     north     B   |   northeast         |
1296       |                     |   A   |       |   B   |                     |
1297       |                     |    A             B    |                     |
1298       C---------------------+----A--+-------+--B----+---------------------D
1299       |CCCCCCC              |     A           B     |              DDDDDDD|
1300       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1301       |               CCCCCCC      A         B      DDDDDDD               |
1302       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1303       |                     |       b       c       |                     | sh
1304       |             west    |       b  move c       |   east              | ad
1305       |                     |       b       c       |                     | ed
1306       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1307       |               EEEEEEE      G         H      FFFFFFF               |
1308       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1309       |EEEEEEE              |     G           H     |              FFFFFFF|
1310       E---------------------+----G--+-------+--H----+---------------------F
1311       |                     |    G             H    |                     |
1312       |                     |   G   |       |   H   |                     |
1313       |        southwest    |   G     south     H   |   southeast         |
1314       |                     |  G    |       |    H  |                     |
1315       |                     |  G                 H  |                     |
1316       |                     | G     |       |     H |                     |
1317       |                     | G                   H |                     |
1318       |                     |G      |       |      H|                     |
1319       |                     |G                     H|                     |
1320       +---------------------G-------|-------|-------H---------------------+
1321     */
1322
1323     if (shaded) {
1324         /* for shaded windows, you can only resize west/east and move */
1325         if (b)
1326             return prop_atoms.net_wm_moveresize_size_left;
1327         if (c)
1328             return prop_atoms.net_wm_moveresize_size_right;
1329         return prop_atoms.net_wm_moveresize_move;
1330     }
1331
1332     if (y < A && y >= C)
1333         return prop_atoms.net_wm_moveresize_size_topleft;
1334     else if (y >= A && y >= B && a)
1335         return prop_atoms.net_wm_moveresize_size_top;
1336     else if (y < B && y >= D)
1337         return prop_atoms.net_wm_moveresize_size_topright;
1338     else if (y < C && y >= E && b)
1339         return prop_atoms.net_wm_moveresize_size_left;
1340     else if (y < D && y >= F && c)
1341         return prop_atoms.net_wm_moveresize_size_right;
1342     else if (y < E && y >= G)
1343         return prop_atoms.net_wm_moveresize_size_bottomleft;
1344     else if (y < G && y < H && d)
1345         return prop_atoms.net_wm_moveresize_size_bottom;
1346     else if (y >= H && y < F)
1347         return prop_atoms.net_wm_moveresize_size_bottomright;
1348     else
1349         return prop_atoms.net_wm_moveresize_move;
1350
1351 #undef X
1352 #undef A
1353 #undef B
1354 #undef C
1355 #undef D
1356 #undef E
1357 #undef F
1358 #undef G
1359 #undef H
1360 #undef a
1361 #undef b
1362 #undef c
1363 #undef d
1364 }
1365
1366 void action_resize(union ActionData *data)
1367 {
1368     ObClient *c = data->moveresize.any.c;
1369     guint32 corner;
1370
1371     if (data->moveresize.keyboard)
1372         corner = prop_atoms.net_wm_moveresize_size_keyboard;
1373     else if (data->moveresize.corner)
1374         corner = data->moveresize.corner; /* it was specified in the binding */
1375     else
1376         corner = pick_corner(data->any.x, data->any.y,
1377                              c->frame->area.x, c->frame->area.y,
1378                              /* use the client size because the frame
1379                                 can be differently sized (shaded
1380                                 windows) and we want this based on the
1381                                 clients size */
1382                              c->area.width + c->frame->size.left +
1383                              c->frame->size.right,
1384                              c->area.height + c->frame->size.top +
1385                              c->frame->size.bottom, c->shaded);
1386
1387     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1388 }
1389
1390 void action_directional_focus(union ActionData *data)
1391 {
1392     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1393        on us */
1394     event_halt_focus_delay();
1395
1396     focus_directional_cycle(data->interdiraction.direction,
1397                             data->interdiraction.dock_windows,
1398                             data->interdiraction.desktop_windows,
1399                             data->any.interactive,
1400                             data->interdiraction.dialog,
1401                             data->interdiraction.inter.final,
1402                             data->interdiraction.inter.cancel);
1403 }
1404
1405 void action_movetoedge(union ActionData *data)
1406 {
1407     gint x, y;
1408     ObClient *c = data->diraction.any.c;
1409
1410     x = c->frame->area.x;
1411     y = c->frame->area.y;
1412     
1413     switch(data->diraction.direction) {
1414     case OB_DIRECTION_NORTH:
1415         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1416                                            data->diraction.hang)
1417             - (data->diraction.hang ? c->frame->area.height : 0);
1418         break;
1419     case OB_DIRECTION_WEST:
1420         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1421                                            data->diraction.hang)
1422             - (data->diraction.hang ? c->frame->area.width : 0);
1423         break;
1424     case OB_DIRECTION_SOUTH:
1425         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1426                                            data->diraction.hang)
1427             - (data->diraction.hang ? 0 : c->frame->area.height);
1428         break;
1429     case OB_DIRECTION_EAST:
1430         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1431                                            data->diraction.hang)
1432             - (data->diraction.hang ? 0 : c->frame->area.width);
1433         break;
1434     default:
1435         g_assert_not_reached();
1436     }
1437     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1438     client_action_start(data);
1439     client_move(c, x, y);
1440     client_action_end(data, FALSE);
1441 }
1442
1443 void action_growtoedge(union ActionData *data)
1444 {
1445     gint x, y, width, height, dest;
1446     ObClient *c = data->diraction.any.c;
1447     Rect *a;
1448
1449     a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
1450     x = c->frame->area.x;
1451     y = c->frame->area.y;
1452     /* get the unshaded frame's dimensions..if it is shaded */
1453     width = c->area.width + c->frame->size.left + c->frame->size.right;
1454     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1455
1456     switch(data->diraction.direction) {
1457     case OB_DIRECTION_NORTH:
1458         if (c->shaded) break; /* don't allow vertical resize if shaded */
1459
1460         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1461         if (a->y == y)
1462             height = height / 2;
1463         else {
1464             height = c->frame->area.y + height - dest;
1465             y = dest;
1466         }
1467         break;
1468     case OB_DIRECTION_WEST:
1469         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1470         if (a->x == x)
1471             width = width / 2;
1472         else {
1473             width = c->frame->area.x + width - dest;
1474             x = dest;
1475         }
1476         break;
1477     case OB_DIRECTION_SOUTH:
1478         if (c->shaded) break; /* don't allow vertical resize if shaded */
1479
1480         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1481         if (a->y + a->height == y + c->frame->area.height) {
1482             height = c->frame->area.height / 2;
1483             y = a->y + a->height - height;
1484         } else
1485             height = dest - c->frame->area.y;
1486         y += (height - c->frame->area.height) % c->size_inc.height;
1487         height -= (height - c->frame->area.height) % c->size_inc.height;
1488         break;
1489     case OB_DIRECTION_EAST:
1490         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1491         if (a->x + a->width == x + c->frame->area.width) {
1492             width = c->frame->area.width / 2;
1493             x = a->x + a->width - width;
1494         } else
1495             width = dest - c->frame->area.x;
1496         x += (width - c->frame->area.width) % c->size_inc.width;
1497         width -= (width - c->frame->area.width) % c->size_inc.width;
1498         break;
1499     default:
1500         g_assert_not_reached();
1501     }
1502     width -= c->frame->size.left + c->frame->size.right;
1503     height -= c->frame->size.top + c->frame->size.bottom;
1504     frame_frame_gravity(c->frame, &x, &y, width, height);
1505     client_action_start(data);
1506     client_move_resize(c, x, y, width, height);
1507     client_action_end(data, FALSE);
1508     g_free(a);
1509 }
1510
1511 void action_send_to_layer(union ActionData *data)
1512 {
1513     client_set_layer(data->layer.any.c, data->layer.layer);
1514 }
1515
1516 void action_toggle_layer(union ActionData *data)
1517 {
1518     ObClient *c = data->layer.any.c;
1519
1520     client_action_start(data);
1521     if (data->layer.layer < 0)
1522         client_set_layer(c, c->below ? 0 : -1);
1523     else if (data->layer.layer > 0)
1524         client_set_layer(c, c->above ? 0 : 1);
1525     client_action_end(data, config_focus_under_mouse);
1526 }
1527
1528 void action_toggle_dockautohide(union ActionData *data)
1529 {
1530     config_dock_hide = !config_dock_hide;
1531     dock_configure();
1532 }
1533
1534 void action_add_desktop(union ActionData *data)
1535 {
1536     client_action_start(data);
1537     screen_set_num_desktops(screen_num_desktops+1);
1538
1539     /* move all the clients over */
1540     if (data->addremovedesktop.current) {
1541         GList *it;
1542
1543         for (it = client_list; it; it = g_list_next(it)) {
1544             ObClient *c = it->data;
1545             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
1546                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
1547         }
1548     }
1549
1550     client_action_end(data, config_focus_under_mouse);
1551 }
1552
1553 void action_remove_desktop(union ActionData *data)
1554 {
1555     guint rmdesktop, movedesktop;
1556     GList *it, *stacking_copy;
1557
1558     if (screen_num_desktops < 2) return;
1559
1560     client_action_start(data);
1561
1562     /* what desktop are we removing and moving to? */
1563     if (data->addremovedesktop.current)
1564         rmdesktop = screen_desktop;
1565     else
1566         rmdesktop = screen_num_desktops - 1;
1567     if (rmdesktop < screen_num_desktops - 1)
1568         movedesktop = rmdesktop + 1;
1569     else
1570         movedesktop = rmdesktop;
1571
1572     /* make a copy of the list cuz we're changing it */
1573     stacking_copy = g_list_copy(stacking_list);
1574     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
1575         if (WINDOW_IS_CLIENT(it->data)) {
1576             ObClient *c = it->data;
1577             guint d = c->desktop;
1578             if (d != DESKTOP_ALL && d >= movedesktop) {
1579                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
1580                 ob_debug("moving window %s\n", c->title);
1581             }
1582             /* raise all the windows that are on the current desktop which
1583                is being merged */
1584             if ((screen_desktop == rmdesktop - 1 ||
1585                  screen_desktop == rmdesktop) &&
1586                 (d == DESKTOP_ALL || d == screen_desktop))
1587             {
1588                 stacking_raise(CLIENT_AS_WINDOW(c));
1589                 ob_debug("raising window %s\n", c->title);
1590             }
1591         }
1592     }
1593
1594     /* act like we're changing desktops */
1595     if (screen_desktop < screen_num_desktops - 1) {
1596         gint d = screen_desktop;
1597         screen_desktop = screen_last_desktop;
1598         screen_set_desktop(d, TRUE);
1599         ob_debug("fake desktop change\n");
1600     }
1601
1602     screen_set_num_desktops(screen_num_desktops-1);
1603
1604     client_action_end(data, config_focus_under_mouse);
1605 }