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