add lower and raiselower actions
[mikachu/openbox.git] / openbox / action.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    action.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "client.h"
22 #include "focus.h"
23 #include "focus_cycle.h"
24 #include "moveresize.h"
25 #include "menu.h"
26 #include "prop.h"
27 #include "stacking.h"
28 #include "screen.h"
29 #include "action.h"
30 #include "openbox.h"
31 #include "grab.h"
32 #include "keyboard.h"
33 #include "event.h"
34 #include "dock.h"
35 #include "config.h"
36 #include "mainloop.h"
37 #include "startupnotify.h"
38 #include "gettext.h"
39
40 #include <glib.h>
41
42
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         "unfocus",
459         action_unfocus,
460         setup_client_action
461     },
462     {
463         "focustobottom",
464         action_focus_order_to_bottom,
465         setup_client_action
466     },
467     {
468         "kill",
469         action_kill,
470         setup_client_action
471     },
472     {
473         "shadelower",
474         action_shadelower,
475         setup_client_action
476     },
477     {
478         "unshaderaise",
479         action_unshaderaise,
480         setup_client_action
481     },
482     {
483         "shade",
484         action_shade,
485         setup_client_action
486     },
487     {
488         "unshade",
489         action_unshade,
490         setup_client_action
491     },
492     {
493         "toggleshade",
494         action_toggle_shade,
495         setup_client_action
496     },
497     {
498         "toggleomnipresent",
499         action_toggle_omnipresent,
500         setup_client_action
501     },
502     {
503         "moverelativehorz",
504         action_move_relative_horz,
505         setup_client_action
506     },
507     {
508         "moverelativevert",
509         action_move_relative_vert,
510         setup_client_action
511     },
512     {
513         "movetocenter",
514         action_move_to_center,
515         setup_client_action
516     },
517     {
518         "resizerelativehorz",
519         action_resize_relative_horz,
520         setup_client_action
521     },
522     {
523         "resizerelativevert",
524         action_resize_relative_vert,
525         setup_client_action
526     },
527     {
528         "moverelative",
529         action_move_relative,
530         setup_client_action
531     },
532     {
533         "resizerelative",
534         action_resize_relative,
535         setup_client_action
536     },
537     {
538         "maximizefull",
539         action_maximize_full,
540         setup_client_action
541     },
542     {
543         "unmaximizefull",
544         action_unmaximize_full,
545         setup_client_action
546     },
547     {
548         "togglemaximizefull",
549         action_toggle_maximize_full,
550         setup_client_action
551     },
552     {
553         "maximizehorz",
554         action_maximize_horz,
555         setup_client_action
556     },
557     {
558         "unmaximizehorz",
559         action_unmaximize_horz,
560         setup_client_action
561     },
562     {
563         "togglemaximizehorz",
564         action_toggle_maximize_horz,
565         setup_client_action
566     },
567     {
568         "maximizevert",
569         action_maximize_vert,
570         setup_client_action
571     },
572     {
573         "unmaximizevert",
574         action_unmaximize_vert,
575         setup_client_action
576     },
577     {
578         "togglemaximizevert",
579         action_toggle_maximize_vert,
580         setup_client_action
581     },
582     {
583         "togglefullscreen",
584         action_toggle_fullscreen,
585         setup_client_action
586     },
587     {
588         "sendtodesktop",
589         action_send_to_desktop,
590         setup_action_send_to_desktop
591     },
592     {
593         "sendtodesktopnext",
594         action_send_to_desktop_dir,
595         setup_action_send_to_desktop_next
596     },
597     {
598         "sendtodesktopprevious",
599         action_send_to_desktop_dir,
600         setup_action_send_to_desktop_prev
601     },
602     {
603         "sendtodesktopright",
604         action_send_to_desktop_dir,
605         setup_action_send_to_desktop_right
606     },
607     {
608         "sendtodesktopleft",
609         action_send_to_desktop_dir,
610         setup_action_send_to_desktop_left
611     },
612     {
613         "sendtodesktopup",
614         action_send_to_desktop_dir,
615         setup_action_send_to_desktop_up
616     },
617     {
618         "sendtodesktopdown",
619         action_send_to_desktop_dir,
620         setup_action_send_to_desktop_down
621     },
622     {
623         "desktop",
624         action_desktop,
625         setup_action_desktop
626     },
627     {
628         "desktopnext",
629         action_desktop_dir,
630         setup_action_desktop_next
631     },
632     {
633         "desktopprevious",
634         action_desktop_dir,
635         setup_action_desktop_prev
636     },
637     {
638         "desktopright",
639         action_desktop_dir,
640         setup_action_desktop_right
641     },
642     {
643         "desktopleft",
644         action_desktop_dir,
645         setup_action_desktop_left
646     },
647     {
648         "desktopup",
649         action_desktop_dir,
650         setup_action_desktop_up
651     },
652     {
653         "desktopdown",
654         action_desktop_dir,
655         setup_action_desktop_down
656     },
657     {
658         "toggledecorations",
659         action_toggle_decorations,
660         setup_client_action
661     },
662     {
663         "resize",
664         action_resize,
665         setup_action_resize
666     },
667     {
668         "toggledockautohide",
669         action_toggle_dockautohide,
670         NULL
671     },
672     {
673         "desktoplast",
674         action_desktop_last,
675         NULL
676     },
677     {
678         "sendtotoplayer",
679         action_send_to_layer,
680         setup_action_top_layer
681     },
682     {
683         "togglealwaysontop",
684         action_toggle_layer,
685         setup_action_top_layer
686     },
687     {
688         "sendtonormallayer",
689         action_send_to_layer,
690         setup_action_normal_layer
691     },
692     {
693         "sendtobottomlayer",
694         action_send_to_layer,
695         setup_action_bottom_layer
696     },
697     {
698         "togglealwaysonbottom",
699         action_toggle_layer,
700         setup_action_bottom_layer
701     },
702     {
703         "movefromedgenorth",
704         action_movetoedge,
705         setup_action_movefromedge_north
706     },
707     {
708         "movefromedgesouth",
709         action_movetoedge,
710         setup_action_movefromedge_south
711     },
712     {
713         "movefromedgewest",
714         action_movetoedge,
715         setup_action_movefromedge_west
716     },
717     {
718         "movefromedgeeast",
719         action_movetoedge,
720         setup_action_movefromedge_east
721     },
722     {
723         "movetoedgenorth",
724         action_movetoedge,
725         setup_action_movetoedge_north
726     },
727     {
728         "movetoedgesouth",
729         action_movetoedge,
730         setup_action_movetoedge_south
731     },
732     {
733         "movetoedgewest",
734         action_movetoedge,
735         setup_action_movetoedge_west
736     },
737     {
738         "movetoedgeeast",
739         action_movetoedge,
740         setup_action_movetoedge_east
741     },
742     {
743         "growtoedgenorth",
744         action_growtoedge,
745         setup_action_growtoedge_north
746     },
747     {
748         "growtoedgesouth",
749         action_growtoedge,
750         setup_action_growtoedge_south
751     },
752     {
753         "growtoedgewest",
754         action_growtoedge,
755         setup_action_growtoedge_west
756     },
757     {
758         "growtoedgeeast",
759         action_growtoedge,
760         setup_action_growtoedge_east
761     },
762     {
763         "adddesktoplast",
764         action_add_desktop,
765         setup_action_addremove_desktop_last
766     },
767     {
768         "removedesktoplast",
769         action_remove_desktop,
770         setup_action_addremove_desktop_last
771     },
772     {
773         "adddesktopcurrent",
774         action_add_desktop,
775         setup_action_addremove_desktop_current
776     },
777     {
778         "removedesktopcurrent",
779         action_remove_desktop,
780         setup_action_addremove_desktop_current
781     },
782     {
783         NULL,
784         NULL,
785         NULL
786     }
787 };
788
789 /* only key bindings can be interactive. thus saith the xor.
790    because of how the mouse is grabbed, mouse events dont even get
791    read during interactive events, so no dice! >:) */
792 #define INTERACTIVE_LIMIT(a, uact) \
793     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
794         a->data.any.interactive = FALSE;
795
796 ObAction *action_from_string(const gchar *name, ObUserAction uact)
797 {
798     ObAction *a = NULL;
799     gboolean exist = FALSE;
800     gint i;
801
802     for (i = 0; actionstrings[i].name; i++)
803         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
804             exist = TRUE;
805             a = action_new(actionstrings[i].func);
806             if (actionstrings[i].setup)
807                 actionstrings[i].setup(&a, uact);
808             if (a)
809                 INTERACTIVE_LIMIT(a, uact);
810             break;
811         }
812     if (!exist)
813         g_message(_("Invalid action '%s' requested. No such action exists."),
814                   name);
815     if (!a)
816         g_message(_("Invalid use of action '%s'. Action will be ignored."),
817                   name);
818     return a;
819 }
820
821 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
822                        ObUserAction uact)
823 {
824     gchar *actname;
825     ObAction *act = NULL;
826     xmlNodePtr n;
827
828     if (parse_attr_string("name", node, &actname)) {
829         if ((act = action_from_string(actname, uact))) {
830             } else if (act->func == action_move_relative_horz ||
831                        act->func == action_move_relative_vert ||
832                        act->func == action_resize_relative_horz ||
833                        act->func == action_resize_relative_vert) {
834                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
835                     act->data.relative.deltax = parse_int(doc, n);
836             } else if (act->func == action_move_relative) {
837                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
838                     act->data.relative.deltax = parse_int(doc, n);
839                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
840                     act->data.relative.deltay = parse_int(doc, n);
841             } else if (act->func == action_resize_relative) {
842                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
843                     act->data.relative.deltaxl = parse_int(doc, n);
844                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
845                     act->data.relative.deltayu = parse_int(doc, n);
846                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
847                     act->data.relative.deltax = parse_int(doc, n);
848                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
849                     act->data.relative.deltay = parse_int(doc, n);
850             } else if (act->func == action_desktop) {
851                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
852                     act->data.desktop.desk = parse_int(doc, n);
853                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
854 /*
855                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
856                     act->data.desktop.inter.any.interactive =
857                         parse_bool(doc, n);
858 */
859            } else if (act->func == action_send_to_desktop) {
860                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
861                     act->data.sendto.desk = parse_int(doc, n);
862                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
863                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
864                     act->data.sendto.follow = parse_bool(doc, n);
865             } else if (act->func == action_desktop_dir) {
866                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
867                     act->data.desktopdir.wrap = parse_bool(doc, n); 
868                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
869                     act->data.desktopdir.inter.any.interactive =
870                         parse_bool(doc, n);
871             } else if (act->func == action_send_to_desktop_dir) {
872                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
873                     act->data.sendtodir.wrap = parse_bool(doc, n);
874                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
875                     act->data.sendtodir.follow = parse_bool(doc, n);
876                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
877                     act->data.sendtodir.inter.any.interactive =
878                         parse_bool(doc, n);
879             } else if (act->func == action_directional_focus) {
880                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
881                     act->data.interdiraction.dialog = parse_bool(doc, n);
882                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
883                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
884                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
885                     act->data.interdiraction.desktop_windows =
886                         parse_bool(doc, n);
887             } else if (act->func == action_resize) {
888                 if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
889                     gchar *s = parse_string(doc, n);
890                     if (!g_ascii_strcasecmp(s, "top"))
891                         act->data.moveresize.corner =
892                             prop_atoms.net_wm_moveresize_size_top;
893                     else if (!g_ascii_strcasecmp(s, "bottom"))
894                         act->data.moveresize.corner =
895                             prop_atoms.net_wm_moveresize_size_bottom;
896                     else if (!g_ascii_strcasecmp(s, "left"))
897                         act->data.moveresize.corner =
898                             prop_atoms.net_wm_moveresize_size_left;
899                     else if (!g_ascii_strcasecmp(s, "right"))
900                         act->data.moveresize.corner =
901                             prop_atoms.net_wm_moveresize_size_right;
902                     else if (!g_ascii_strcasecmp(s, "topleft"))
903                         act->data.moveresize.corner =
904                             prop_atoms.net_wm_moveresize_size_topleft;
905                     else if (!g_ascii_strcasecmp(s, "topright"))
906                         act->data.moveresize.corner =
907                             prop_atoms.net_wm_moveresize_size_topright;
908                     else if (!g_ascii_strcasecmp(s, "bottomleft"))
909                         act->data.moveresize.corner =
910                             prop_atoms.net_wm_moveresize_size_bottomleft;
911                     else if (!g_ascii_strcasecmp(s, "bottomright"))
912                         act->data.moveresize.corner =
913                             prop_atoms.net_wm_moveresize_size_bottomright;
914                     g_free(s);
915                 }
916             INTERACTIVE_LIMIT(act, uact);
917         }
918         g_free(actname);
919     }
920     return act;
921 }
922
923 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
924                      guint state, guint button, gint x, gint y, Time time,
925                      gboolean cancel, gboolean done)
926 {
927     GSList *it;
928     ObAction *a;
929
930     if (!acts)
931         return;
932
933     if (x < 0 && y < 0)
934         screen_pointer_pos(&x, &y);
935
936     for (it = acts; it; it = g_slist_next(it)) {
937         a = it->data;
938
939         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
940             a->data.any.c = a->data.any.client_action ? c : NULL;
941             a->data.any.context = context;
942             a->data.any.x = x;
943             a->data.any.y = y;
944
945             a->data.any.button = button;
946
947             a->data.any.time = time;
948
949             if (a->data.any.interactive) {
950                 a->data.inter.cancel = cancel;
951                 a->data.inter.final = done;
952                 if (!(cancel || done))
953                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
954                         continue;
955             }
956
957             /* XXX UGLY HACK race with motion event starting a move and the
958                button release gettnig processed first. answer: don't queue
959                moveresize starts. UGLY HACK XXX
960
961                XXX ALSO don't queue showmenu events, because on button press
962                events we need to know if a mouse grab is going to take place,
963                and set the button to 0, so that later motion events don't think
964                that a drag is going on. since showmenu grabs the pointer..
965             */
966             if (a->data.any.interactive || a->func == action_move ||
967                 a->func == action_resize || a->func == action_showmenu)
968             {
969                 /* interactive actions are not queued */
970                 a->func(&a->data);
971             } else if (a->func == action_focus ||
972                        a->func == action_activate ||
973                        a->func == action_showmenu)
974             {
975                 /* XXX MORE UGLY HACK
976                    actions from clicks on client windows are NOT queued.
977                    this solves the mysterious click-and-drag-doesnt-work
978                    problem. it was because the window gets focused and stuff
979                    after the button event has already been passed through. i
980                    dont really know why it should care but it does and it makes
981                    a difference.
982
983                    however this very bogus ! !
984                    we want to send the button press to the window BEFORE
985                    we do the action because the action might move the windows
986                    (eg change desktops) and then the button press ends up on
987                    the completely wrong window !
988                    so, this is just for that bug, and it will only NOT queue it
989                    if it is a focusing action that can be used with the mouse
990                    pointer. ugh.
991
992                    also with the menus, there is a race going on. if the
993                    desktop wants to pop up a menu, and we do too, we send them
994                    the button before we pop up the menu, so they pop up their
995                    menu first. but not always. if we pop up our menu before
996                    sending them the button press, then the result is
997                    deterministic. yay.
998
999                    XXX further more. focus actions are not queued at all,
1000                    because if you bind focus->showmenu, the menu will get
1001                    hidden to do the focusing
1002                 */
1003                 a->func(&a->data);
1004             } else
1005                 ob_main_loop_queue_action(ob_main_loop, a);
1006         }
1007     }
1008 }
1009
1010 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1011 {
1012     ObAction *a;
1013     GSList *l;
1014
1015     a = action_from_string(name, OB_USER_ACTION_NONE);
1016     g_assert(a);
1017
1018     l = g_slist_append(NULL, a);
1019
1020     action_run(l, c, 0, time);
1021 }
1022
1023 void action_unfocus (union ActionData *data)
1024 {
1025     if (data->client.any.c == focus_client)
1026         focus_fallback(FALSE, FALSE, TRUE);
1027 }
1028
1029 void action_iconify(union ActionData *data)
1030 {
1031     client_action_start(data);
1032     client_iconify(data->client.any.c, TRUE, TRUE, FALSE);
1033     client_action_end(data, config_focus_under_mouse);
1034 }
1035
1036 void action_focus_order_to_bottom(union ActionData *data)
1037 {
1038     focus_order_to_bottom(data->client.any.c);
1039 }
1040
1041 void action_unshaderaise(union ActionData *data)
1042 {
1043     if (data->client.any.c->shaded)
1044         action_unshade(data);
1045     else
1046         action_raise(data);
1047 }
1048
1049 void action_shadelower(union ActionData *data)
1050 {
1051     if (data->client.any.c->shaded)
1052         action_lower(data);
1053     else
1054         action_shade(data);
1055 }
1056
1057 void action_kill(union ActionData *data)
1058 {
1059     client_kill(data->client.any.c);
1060 }
1061
1062 void action_shade(union ActionData *data)
1063 {
1064     client_action_start(data);
1065     client_shade(data->client.any.c, TRUE);
1066     client_action_end(data, config_focus_under_mouse);
1067 }
1068
1069 void action_unshade(union ActionData *data)
1070 {
1071     client_action_start(data);
1072     client_shade(data->client.any.c, FALSE);
1073     client_action_end(data, config_focus_under_mouse);
1074 }
1075
1076 void action_toggle_shade(union ActionData *data)
1077 {
1078     client_action_start(data);
1079     client_shade(data->client.any.c, !data->client.any.c->shaded);
1080     client_action_end(data, config_focus_under_mouse);
1081 }
1082
1083 void action_toggle_omnipresent(union ActionData *data)
1084
1085     client_set_desktop(data->client.any.c,
1086                        data->client.any.c->desktop == DESKTOP_ALL ?
1087                        screen_desktop : DESKTOP_ALL, FALSE, TRUE);
1088 }
1089
1090 void action_move_relative_horz(union ActionData *data)
1091 {
1092     ObClient *c = data->relative.any.c;
1093     client_action_start(data);
1094     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1095     client_action_end(data, FALSE);
1096 }
1097
1098 void action_move_relative_vert(union ActionData *data)
1099 {
1100     ObClient *c = data->relative.any.c;
1101     client_action_start(data);
1102     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1103     client_action_end(data, FALSE);
1104 }
1105
1106 void action_move_to_center(union ActionData *data)
1107 {
1108     ObClient *c = data->client.any.c;
1109     Rect *area;
1110     area = screen_area(c->desktop, client_monitor(c), NULL);
1111     client_action_start(data);
1112     client_move(c, area->x + area->width / 2 - c->area.width / 2,
1113                 area->y + area->height / 2 - c->area.height / 2);
1114     client_action_end(data, FALSE);
1115     g_free(area);
1116 }
1117
1118 void action_resize_relative_horz(union ActionData *data)
1119 {
1120     ObClient *c = data->relative.any.c;
1121     client_action_start(data);
1122     client_resize(c,
1123                   c->area.width + data->relative.deltax * c->size_inc.width,
1124                   c->area.height);
1125     client_action_end(data, FALSE);
1126 }
1127
1128 void action_resize_relative_vert(union ActionData *data)
1129 {
1130     ObClient *c = data->relative.any.c;
1131     if (!c->shaded) {
1132         client_action_start(data);
1133         client_resize(c, c->area.width, c->area.height +
1134                       data->relative.deltax * c->size_inc.height);
1135         client_action_end(data, FALSE);
1136     }
1137 }
1138
1139 void action_move_relative(union ActionData *data)
1140 {
1141     ObClient *c = data->relative.any.c;
1142     client_action_start(data);
1143     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1144                 data->relative.deltay);
1145     client_action_end(data, FALSE);
1146 }
1147
1148 void action_resize_relative(union ActionData *data)
1149 {
1150     ObClient *c = data->relative.any.c;
1151     gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
1152
1153     client_action_start(data);
1154
1155     x = c->area.x;
1156     y = c->area.y;
1157     ow = c->area.width;
1158     xoff = -data->relative.deltaxl * c->size_inc.width;
1159     nw = ow + data->relative.deltax * c->size_inc.width
1160         + data->relative.deltaxl * c->size_inc.width;
1161     oh = c->area.height;
1162     yoff = -data->relative.deltayu * c->size_inc.height;
1163     nh = oh + data->relative.deltay * c->size_inc.height
1164         + data->relative.deltayu * c->size_inc.height;
1165
1166     g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
1167             data->relative.deltax, 
1168             data->relative.deltaxl, 
1169             x, ow, xoff, nw);
1170     
1171     client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
1172     xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
1173     yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
1174     client_move_resize(c, x + xoff, y + yoff, nw, nh);
1175     client_action_end(data, FALSE);
1176 }
1177
1178 void action_maximize_full(union ActionData *data)
1179 {
1180     client_action_start(data);
1181     client_maximize(data->client.any.c, TRUE, 0);
1182     client_action_end(data, config_focus_under_mouse);
1183 }
1184
1185 void action_unmaximize_full(union ActionData *data)
1186 {
1187     client_action_start(data);
1188     client_maximize(data->client.any.c, FALSE, 0);
1189     client_action_end(data, config_focus_under_mouse);
1190 }
1191
1192 void action_toggle_maximize_full(union ActionData *data)
1193 {
1194     client_action_start(data);
1195     client_maximize(data->client.any.c,
1196                     !(data->client.any.c->max_horz ||
1197                       data->client.any.c->max_vert),
1198                     0);
1199     client_action_end(data, config_focus_under_mouse);
1200 }
1201
1202 void action_maximize_horz(union ActionData *data)
1203 {
1204     client_action_start(data);
1205     client_maximize(data->client.any.c, TRUE, 1);
1206     client_action_end(data, config_focus_under_mouse);
1207 }
1208
1209 void action_unmaximize_horz(union ActionData *data)
1210 {
1211     client_action_start(data);
1212     client_maximize(data->client.any.c, FALSE, 1);
1213     client_action_end(data, config_focus_under_mouse);
1214 }
1215
1216 void action_toggle_maximize_horz(union ActionData *data)
1217 {
1218     client_action_start(data);
1219     client_maximize(data->client.any.c,
1220                     !data->client.any.c->max_horz, 1);
1221     client_action_end(data, config_focus_under_mouse);
1222 }
1223
1224 void action_maximize_vert(union ActionData *data)
1225 {
1226     client_action_start(data);
1227     client_maximize(data->client.any.c, TRUE, 2);
1228     client_action_end(data, config_focus_under_mouse);
1229 }
1230
1231 void action_unmaximize_vert(union ActionData *data)
1232 {
1233     client_action_start(data);
1234     client_maximize(data->client.any.c, FALSE, 2);
1235     client_action_end(data, config_focus_under_mouse);
1236 }
1237
1238 void action_toggle_maximize_vert(union ActionData *data)
1239 {
1240     client_action_start(data);
1241     client_maximize(data->client.any.c,
1242                     !data->client.any.c->max_vert, 2);
1243     client_action_end(data, config_focus_under_mouse);
1244 }
1245
1246 void action_toggle_fullscreen(union ActionData *data)
1247 {
1248     client_action_start(data);
1249     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1250     client_action_end(data, config_focus_under_mouse);
1251 }
1252
1253 void action_send_to_desktop(union ActionData *data)
1254 {
1255     ObClient *c = data->sendto.any.c;
1256
1257     if (!client_normal(c)) return;
1258
1259     if (data->sendto.desk < screen_num_desktops ||
1260         data->sendto.desk == DESKTOP_ALL) {
1261         client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
1262         if (data->sendto.follow && data->sendto.desk != screen_desktop)
1263             screen_set_desktop(data->sendto.desk, TRUE);
1264     }
1265 }
1266
1267 void action_desktop(union ActionData *data)
1268 {
1269     /* XXX add the interactive/dialog option back again once the dialog
1270        has been made to not use grabs */
1271     if (data->desktop.desk < screen_num_desktops ||
1272         data->desktop.desk == DESKTOP_ALL)
1273     {
1274         screen_set_desktop(data->desktop.desk, TRUE);
1275         if (data->inter.any.interactive)
1276             screen_desktop_popup(data->desktop.desk, TRUE);
1277     }
1278 }
1279
1280 void action_desktop_dir(union ActionData *data)
1281 {
1282     guint d;
1283
1284     d = screen_cycle_desktop(data->desktopdir.dir,
1285                              data->desktopdir.wrap,
1286                              data->desktopdir.linear,
1287                              data->desktopdir.inter.any.interactive,
1288                              data->desktopdir.inter.final,
1289                              data->desktopdir.inter.cancel);
1290     /* only move the desktop when the action is complete. if we switch
1291        desktops during the interactive action, focus will move but with
1292        NotifyWhileGrabbed and applications don't like that. */
1293     if (!data->sendtodir.inter.any.interactive ||
1294         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1295     {
1296         if (d != screen_desktop)
1297             screen_set_desktop(d, TRUE);
1298     }
1299 }
1300
1301 void action_send_to_desktop_dir(union ActionData *data)
1302 {
1303     ObClient *c = data->sendtodir.inter.any.c;
1304     guint d;
1305
1306     if (!client_normal(c)) return;
1307
1308     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1309                              data->sendtodir.linear,
1310                              data->sendtodir.inter.any.interactive,
1311                              data->sendtodir.inter.final,
1312                              data->sendtodir.inter.cancel);
1313     /* only move the desktop when the action is complete. if we switch
1314        desktops during the interactive action, focus will move but with
1315        NotifyWhileGrabbed and applications don't like that. */
1316     if (!data->sendtodir.inter.any.interactive ||
1317         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1318     {
1319         client_set_desktop(c, d, data->sendtodir.follow, FALSE);
1320         if (data->sendtodir.follow && d != screen_desktop)
1321             screen_set_desktop(d, TRUE);
1322     }
1323 }
1324
1325 void action_desktop_last(union ActionData *data)
1326 {
1327     if (screen_last_desktop < screen_num_desktops)
1328         screen_set_desktop(screen_last_desktop, TRUE);
1329 }
1330
1331 void action_toggle_decorations(union ActionData *data)
1332 {
1333     ObClient *c = data->client.any.c;
1334
1335     client_action_start(data);
1336     client_set_undecorated(c, !c->undecorated);
1337     client_action_end(data, FALSE);
1338 }
1339
1340 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1341                            gboolean shaded)
1342 {
1343     /* let's make x and y client relative instead of screen relative */
1344     x = x - cx;
1345     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1346
1347 #define X x*ch/cw
1348 #define A -4*X + 7*ch/3
1349 #define B  4*X -15*ch/9
1350 #define C -X/4 + 2*ch/3
1351 #define D  X/4 + 5*ch/12
1352 #define E  X/4 +   ch/3
1353 #define F -X/4 + 7*ch/12
1354 #define G  4*X - 4*ch/3
1355 #define H -4*X + 8*ch/3
1356 #define a (y > 5*ch/9)
1357 #define b (x < 4*cw/9)
1358 #define c (x > 5*cw/9)
1359 #define d (y < 4*ch/9)
1360
1361     /*
1362       Each of these defines (except X which is just there for fun), represents
1363       the equation of a line. The lines they represent are shown in the diagram
1364       below. Checking y against these lines, we are able to choose a region
1365       of the window as shown.
1366
1367       +---------------------A-------|-------|-------B---------------------+
1368       |                     |A                     B|                     |
1369       |                     |A      |       |      B|                     |
1370       |                     | A                   B |                     |
1371       |                     | A     |       |     B |                     |
1372       |                     |  A                 B  |                     |
1373       |                     |  A    |       |    B  |                     |
1374       |        northwest    |   A     north     B   |   northeast         |
1375       |                     |   A   |       |   B   |                     |
1376       |                     |    A             B    |                     |
1377       C---------------------+----A--+-------+--B----+---------------------D
1378       |CCCCCCC              |     A           B     |              DDDDDDD|
1379       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1380       |               CCCCCCC      A         B      DDDDDDD               |
1381       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1382       |                     |       b       c       |                     | sh
1383       |             west    |       b  move c       |   east              | ad
1384       |                     |       b       c       |                     | ed
1385       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1386       |               EEEEEEE      G         H      FFFFFFF               |
1387       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1388       |EEEEEEE              |     G           H     |              FFFFFFF|
1389       E---------------------+----G--+-------+--H----+---------------------F
1390       |                     |    G             H    |                     |
1391       |                     |   G   |       |   H   |                     |
1392       |        southwest    |   G     south     H   |   southeast         |
1393       |                     |  G    |       |    H  |                     |
1394       |                     |  G                 H  |                     |
1395       |                     | G     |       |     H |                     |
1396       |                     | G                   H |                     |
1397       |                     |G      |       |      H|                     |
1398       |                     |G                     H|                     |
1399       +---------------------G-------|-------|-------H---------------------+
1400     */
1401
1402     if (shaded) {
1403         /* for shaded windows, you can only resize west/east and move */
1404         if (b)
1405             return prop_atoms.net_wm_moveresize_size_left;
1406         if (c)
1407             return prop_atoms.net_wm_moveresize_size_right;
1408         return prop_atoms.net_wm_moveresize_move;
1409     }
1410
1411     if (y < A && y >= C)
1412         return prop_atoms.net_wm_moveresize_size_topleft;
1413     else if (y >= A && y >= B && a)
1414         return prop_atoms.net_wm_moveresize_size_top;
1415     else if (y < B && y >= D)
1416         return prop_atoms.net_wm_moveresize_size_topright;
1417     else if (y < C && y >= E && b)
1418         return prop_atoms.net_wm_moveresize_size_left;
1419     else if (y < D && y >= F && c)
1420         return prop_atoms.net_wm_moveresize_size_right;
1421     else if (y < E && y >= G)
1422         return prop_atoms.net_wm_moveresize_size_bottomleft;
1423     else if (y < G && y < H && d)
1424         return prop_atoms.net_wm_moveresize_size_bottom;
1425     else if (y >= H && y < F)
1426         return prop_atoms.net_wm_moveresize_size_bottomright;
1427     else
1428         return prop_atoms.net_wm_moveresize_move;
1429
1430 #undef X
1431 #undef A
1432 #undef B
1433 #undef C
1434 #undef D
1435 #undef E
1436 #undef F
1437 #undef G
1438 #undef H
1439 #undef a
1440 #undef b
1441 #undef c
1442 #undef d
1443 }
1444
1445 void action_resize(union ActionData *data)
1446 {
1447     ObClient *c = data->moveresize.any.c;
1448     guint32 corner;
1449
1450     if (data->moveresize.keyboard)
1451         corner = prop_atoms.net_wm_moveresize_size_keyboard;
1452     else if (data->moveresize.corner)
1453         corner = data->moveresize.corner; /* it was specified in the binding */
1454     else
1455         corner = pick_corner(data->any.x, data->any.y,
1456                              c->frame->area.x, c->frame->area.y,
1457                              /* use the client size because the frame
1458                                 can be differently sized (shaded
1459                                 windows) and we want this based on the
1460                                 clients size */
1461                              c->area.width + c->frame->size.left +
1462                              c->frame->size.right,
1463                              c->area.height + c->frame->size.top +
1464                              c->frame->size.bottom, c->shaded);
1465
1466     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1467 }
1468
1469 void action_directional_focus(union ActionData *data)
1470 {
1471     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1472        on us */
1473     event_halt_focus_delay();
1474
1475     focus_directional_cycle(data->interdiraction.direction,
1476                             data->interdiraction.dock_windows,
1477                             data->interdiraction.desktop_windows,
1478                             data->any.interactive,
1479                             data->interdiraction.dialog,
1480                             data->interdiraction.inter.final,
1481                             data->interdiraction.inter.cancel);
1482 }
1483
1484 void action_movetoedge(union ActionData *data)
1485 {
1486     gint x, y;
1487     ObClient *c = data->diraction.any.c;
1488
1489     x = c->frame->area.x;
1490     y = c->frame->area.y;
1491     
1492     switch(data->diraction.direction) {
1493     case OB_DIRECTION_NORTH:
1494         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1495                                            data->diraction.hang)
1496             - (data->diraction.hang ? c->frame->area.height : 0);
1497         break;
1498     case OB_DIRECTION_WEST:
1499         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1500                                            data->diraction.hang)
1501             - (data->diraction.hang ? c->frame->area.width : 0);
1502         break;
1503     case OB_DIRECTION_SOUTH:
1504         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1505                                            data->diraction.hang)
1506             - (data->diraction.hang ? 0 : c->frame->area.height);
1507         break;
1508     case OB_DIRECTION_EAST:
1509         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1510                                            data->diraction.hang)
1511             - (data->diraction.hang ? 0 : c->frame->area.width);
1512         break;
1513     default:
1514         g_assert_not_reached();
1515     }
1516     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1517     client_action_start(data);
1518     client_move(c, x, y);
1519     client_action_end(data, FALSE);
1520 }
1521
1522 void action_growtoedge(union ActionData *data)
1523 {
1524     gint x, y, width, height, dest;
1525     ObClient *c = data->diraction.any.c;
1526     Rect *a;
1527
1528     a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
1529     x = c->frame->area.x;
1530     y = c->frame->area.y;
1531     /* get the unshaded frame's dimensions..if it is shaded */
1532     width = c->area.width + c->frame->size.left + c->frame->size.right;
1533     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1534
1535     switch(data->diraction.direction) {
1536     case OB_DIRECTION_NORTH:
1537         if (c->shaded) break; /* don't allow vertical resize if shaded */
1538
1539         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1540         if (a->y == y)
1541             height = height / 2;
1542         else {
1543             height = c->frame->area.y + height - dest;
1544             y = dest;
1545         }
1546         break;
1547     case OB_DIRECTION_WEST:
1548         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1549         if (a->x == x)
1550             width = width / 2;
1551         else {
1552             width = c->frame->area.x + width - dest;
1553             x = dest;
1554         }
1555         break;
1556     case OB_DIRECTION_SOUTH:
1557         if (c->shaded) break; /* don't allow vertical resize if shaded */
1558
1559         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1560         if (a->y + a->height == y + c->frame->area.height) {
1561             height = c->frame->area.height / 2;
1562             y = a->y + a->height - height;
1563         } else
1564             height = dest - c->frame->area.y;
1565         y += (height - c->frame->area.height) % c->size_inc.height;
1566         height -= (height - c->frame->area.height) % c->size_inc.height;
1567         break;
1568     case OB_DIRECTION_EAST:
1569         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1570         if (a->x + a->width == x + c->frame->area.width) {
1571             width = c->frame->area.width / 2;
1572             x = a->x + a->width - width;
1573         } else
1574             width = dest - c->frame->area.x;
1575         x += (width - c->frame->area.width) % c->size_inc.width;
1576         width -= (width - c->frame->area.width) % c->size_inc.width;
1577         break;
1578     default:
1579         g_assert_not_reached();
1580     }
1581     width -= c->frame->size.left + c->frame->size.right;
1582     height -= c->frame->size.top + c->frame->size.bottom;
1583     frame_frame_gravity(c->frame, &x, &y, width, height);
1584     client_action_start(data);
1585     client_move_resize(c, x, y, width, height);
1586     client_action_end(data, FALSE);
1587     g_free(a);
1588 }
1589
1590 void action_send_to_layer(union ActionData *data)
1591 {
1592     client_set_layer(data->layer.any.c, data->layer.layer);
1593 }
1594
1595 void action_toggle_layer(union ActionData *data)
1596 {
1597     ObClient *c = data->layer.any.c;
1598
1599     client_action_start(data);
1600     if (data->layer.layer < 0)
1601         client_set_layer(c, c->below ? 0 : -1);
1602     else if (data->layer.layer > 0)
1603         client_set_layer(c, c->above ? 0 : 1);
1604     client_action_end(data, config_focus_under_mouse);
1605 }
1606
1607 void action_toggle_dockautohide(union ActionData *data)
1608 {
1609     config_dock_hide = !config_dock_hide;
1610     dock_configure();
1611 }
1612
1613 void action_add_desktop(union ActionData *data)
1614 {
1615     client_action_start(data);
1616     screen_set_num_desktops(screen_num_desktops+1);
1617
1618     /* move all the clients over */
1619     if (data->addremovedesktop.current) {
1620         GList *it;
1621
1622         for (it = client_list; it; it = g_list_next(it)) {
1623             ObClient *c = it->data;
1624             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
1625                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
1626         }
1627     }
1628
1629     client_action_end(data, config_focus_under_mouse);
1630 }
1631
1632 void action_remove_desktop(union ActionData *data)
1633 {
1634     guint rmdesktop, movedesktop;
1635     GList *it, *stacking_copy;
1636
1637     if (screen_num_desktops < 2) return;
1638
1639     client_action_start(data);
1640
1641     /* what desktop are we removing and moving to? */
1642     if (data->addremovedesktop.current)
1643         rmdesktop = screen_desktop;
1644     else
1645         rmdesktop = screen_num_desktops - 1;
1646     if (rmdesktop < screen_num_desktops - 1)
1647         movedesktop = rmdesktop + 1;
1648     else
1649         movedesktop = rmdesktop;
1650
1651     /* make a copy of the list cuz we're changing it */
1652     stacking_copy = g_list_copy(stacking_list);
1653     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
1654         if (WINDOW_IS_CLIENT(it->data)) {
1655             ObClient *c = it->data;
1656             guint d = c->desktop;
1657             if (d != DESKTOP_ALL && d >= movedesktop) {
1658                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
1659                 ob_debug("moving window %s\n", c->title);
1660             }
1661             /* raise all the windows that are on the current desktop which
1662                is being merged */
1663             if ((screen_desktop == rmdesktop - 1 ||
1664                  screen_desktop == rmdesktop) &&
1665                 (d == DESKTOP_ALL || d == screen_desktop))
1666             {
1667                 stacking_raise(CLIENT_AS_WINDOW(c));
1668                 ob_debug("raising window %s\n", c->title);
1669             }
1670         }
1671     }
1672
1673     /* act like we're changing desktops */
1674     if (screen_desktop < screen_num_desktops - 1) {
1675         gint d = screen_desktop;
1676         screen_desktop = screen_last_desktop;
1677         screen_set_desktop(d, TRUE);
1678         ob_debug("fake desktop change\n");
1679     }
1680
1681     screen_set_num_desktops(screen_num_desktops-1);
1682
1683     client_action_end(data, config_focus_under_mouse);
1684 }