]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/action.c
add the unfocus action
[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         "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_iconify(union ActionData *data)
1014 {
1015     client_action_start(data);
1016     client_iconify(data->client.any.c, TRUE, TRUE, FALSE);
1017     client_action_end(data, config_focus_under_mouse);
1018 }
1019
1020 void action_unshaderaise(union ActionData *data)
1021 {
1022     if (data->client.any.c->shaded)
1023         action_unshade(data);
1024     else
1025         action_raise(data);
1026 }
1027
1028 void action_shadelower(union ActionData *data)
1029 {
1030     if (data->client.any.c->shaded)
1031         action_lower(data);
1032     else
1033         action_shade(data);
1034 }
1035
1036 void action_kill(union ActionData *data)
1037 {
1038     client_kill(data->client.any.c);
1039 }
1040
1041 void action_shade(union ActionData *data)
1042 {
1043     client_action_start(data);
1044     client_shade(data->client.any.c, TRUE);
1045     client_action_end(data, config_focus_under_mouse);
1046 }
1047
1048 void action_unshade(union ActionData *data)
1049 {
1050     client_action_start(data);
1051     client_shade(data->client.any.c, FALSE);
1052     client_action_end(data, config_focus_under_mouse);
1053 }
1054
1055 void action_toggle_shade(union ActionData *data)
1056 {
1057     client_action_start(data);
1058     client_shade(data->client.any.c, !data->client.any.c->shaded);
1059     client_action_end(data, config_focus_under_mouse);
1060 }
1061
1062 void action_toggle_omnipresent(union ActionData *data)
1063
1064     client_set_desktop(data->client.any.c,
1065                        data->client.any.c->desktop == DESKTOP_ALL ?
1066                        screen_desktop : DESKTOP_ALL, FALSE, TRUE);
1067 }
1068
1069 void action_move_relative_horz(union ActionData *data)
1070 {
1071     ObClient *c = data->relative.any.c;
1072     client_action_start(data);
1073     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1074     client_action_end(data, FALSE);
1075 }
1076
1077 void action_move_relative_vert(union ActionData *data)
1078 {
1079     ObClient *c = data->relative.any.c;
1080     client_action_start(data);
1081     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1082     client_action_end(data, FALSE);
1083 }
1084
1085 void action_move_to_center(union ActionData *data)
1086 {
1087     ObClient *c = data->client.any.c;
1088     Rect *area;
1089     area = screen_area(c->desktop, client_monitor(c), NULL);
1090     client_action_start(data);
1091     client_move(c, area->x + area->width / 2 - c->area.width / 2,
1092                 area->y + area->height / 2 - c->area.height / 2);
1093     client_action_end(data, FALSE);
1094     g_free(area);
1095 }
1096
1097 void action_resize_relative_horz(union ActionData *data)
1098 {
1099     ObClient *c = data->relative.any.c;
1100     client_action_start(data);
1101     client_resize(c,
1102                   c->area.width + data->relative.deltax * c->size_inc.width,
1103                   c->area.height);
1104     client_action_end(data, FALSE);
1105 }
1106
1107 void action_resize_relative_vert(union ActionData *data)
1108 {
1109     ObClient *c = data->relative.any.c;
1110     if (!c->shaded) {
1111         client_action_start(data);
1112         client_resize(c, c->area.width, c->area.height +
1113                       data->relative.deltax * c->size_inc.height);
1114         client_action_end(data, FALSE);
1115     }
1116 }
1117
1118 void action_move_relative(union ActionData *data)
1119 {
1120     ObClient *c = data->relative.any.c;
1121     client_action_start(data);
1122     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1123                 data->relative.deltay);
1124     client_action_end(data, FALSE);
1125 }
1126
1127 void action_resize_relative(union ActionData *data)
1128 {
1129     ObClient *c = data->relative.any.c;
1130     gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
1131
1132     client_action_start(data);
1133
1134     x = c->area.x;
1135     y = c->area.y;
1136     ow = c->area.width;
1137     xoff = -data->relative.deltaxl * c->size_inc.width;
1138     nw = ow + data->relative.deltax * c->size_inc.width
1139         + data->relative.deltaxl * c->size_inc.width;
1140     oh = c->area.height;
1141     yoff = -data->relative.deltayu * c->size_inc.height;
1142     nh = oh + data->relative.deltay * c->size_inc.height
1143         + data->relative.deltayu * c->size_inc.height;
1144
1145     g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
1146             data->relative.deltax, 
1147             data->relative.deltaxl, 
1148             x, ow, xoff, nw);
1149     
1150     client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
1151     xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
1152     yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
1153     client_move_resize(c, x + xoff, y + yoff, nw, nh);
1154     client_action_end(data, FALSE);
1155 }
1156
1157 void action_maximize_full(union ActionData *data)
1158 {
1159     client_action_start(data);
1160     client_maximize(data->client.any.c, TRUE, 0);
1161     client_action_end(data, config_focus_under_mouse);
1162 }
1163
1164 void action_unmaximize_full(union ActionData *data)
1165 {
1166     client_action_start(data);
1167     client_maximize(data->client.any.c, FALSE, 0);
1168     client_action_end(data, config_focus_under_mouse);
1169 }
1170
1171 void action_toggle_maximize_full(union ActionData *data)
1172 {
1173     client_action_start(data);
1174     client_maximize(data->client.any.c,
1175                     !(data->client.any.c->max_horz ||
1176                       data->client.any.c->max_vert),
1177                     0);
1178     client_action_end(data, config_focus_under_mouse);
1179 }
1180
1181 void action_maximize_horz(union ActionData *data)
1182 {
1183     client_action_start(data);
1184     client_maximize(data->client.any.c, TRUE, 1);
1185     client_action_end(data, config_focus_under_mouse);
1186 }
1187
1188 void action_unmaximize_horz(union ActionData *data)
1189 {
1190     client_action_start(data);
1191     client_maximize(data->client.any.c, FALSE, 1);
1192     client_action_end(data, config_focus_under_mouse);
1193 }
1194
1195 void action_toggle_maximize_horz(union ActionData *data)
1196 {
1197     client_action_start(data);
1198     client_maximize(data->client.any.c,
1199                     !data->client.any.c->max_horz, 1);
1200     client_action_end(data, config_focus_under_mouse);
1201 }
1202
1203 void action_maximize_vert(union ActionData *data)
1204 {
1205     client_action_start(data);
1206     client_maximize(data->client.any.c, TRUE, 2);
1207     client_action_end(data, config_focus_under_mouse);
1208 }
1209
1210 void action_unmaximize_vert(union ActionData *data)
1211 {
1212     client_action_start(data);
1213     client_maximize(data->client.any.c, FALSE, 2);
1214     client_action_end(data, config_focus_under_mouse);
1215 }
1216
1217 void action_toggle_maximize_vert(union ActionData *data)
1218 {
1219     client_action_start(data);
1220     client_maximize(data->client.any.c,
1221                     !data->client.any.c->max_vert, 2);
1222     client_action_end(data, config_focus_under_mouse);
1223 }
1224
1225 void action_toggle_fullscreen(union ActionData *data)
1226 {
1227     client_action_start(data);
1228     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1229     client_action_end(data, config_focus_under_mouse);
1230 }
1231
1232 void action_send_to_desktop(union ActionData *data)
1233 {
1234     ObClient *c = data->sendto.any.c;
1235
1236     if (!client_normal(c)) return;
1237
1238     if (data->sendto.desk < screen_num_desktops ||
1239         data->sendto.desk == DESKTOP_ALL) {
1240         client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
1241         if (data->sendto.follow && data->sendto.desk != screen_desktop)
1242             screen_set_desktop(data->sendto.desk, TRUE);
1243     }
1244 }
1245
1246 void action_desktop(union ActionData *data)
1247 {
1248     /* XXX add the interactive/dialog option back again once the dialog
1249        has been made to not use grabs */
1250     if (data->desktop.desk < screen_num_desktops ||
1251         data->desktop.desk == DESKTOP_ALL)
1252     {
1253         screen_set_desktop(data->desktop.desk, TRUE);
1254         if (data->inter.any.interactive)
1255             screen_desktop_popup(data->desktop.desk, TRUE);
1256     }
1257 }
1258
1259 void action_desktop_dir(union ActionData *data)
1260 {
1261     guint d;
1262
1263     d = screen_cycle_desktop(data->desktopdir.dir,
1264                              data->desktopdir.wrap,
1265                              data->desktopdir.linear,
1266                              data->desktopdir.inter.any.interactive,
1267                              data->desktopdir.inter.final,
1268                              data->desktopdir.inter.cancel);
1269     /* only move the desktop when the action is complete. if we switch
1270        desktops during the interactive action, focus will move but with
1271        NotifyWhileGrabbed and applications don't like that. */
1272     if (!data->sendtodir.inter.any.interactive ||
1273         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1274     {
1275         if (d != screen_desktop)
1276             screen_set_desktop(d, TRUE);
1277     }
1278 }
1279
1280 void action_send_to_desktop_dir(union ActionData *data)
1281 {
1282     ObClient *c = data->sendtodir.inter.any.c;
1283     guint d;
1284
1285     if (!client_normal(c)) return;
1286
1287     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1288                              data->sendtodir.linear,
1289                              data->sendtodir.inter.any.interactive,
1290                              data->sendtodir.inter.final,
1291                              data->sendtodir.inter.cancel);
1292     /* only move the desktop when the action is complete. if we switch
1293        desktops during the interactive action, focus will move but with
1294        NotifyWhileGrabbed and applications don't like that. */
1295     if (!data->sendtodir.inter.any.interactive ||
1296         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1297     {
1298         client_set_desktop(c, d, data->sendtodir.follow, FALSE);
1299         if (data->sendtodir.follow && d != screen_desktop)
1300             screen_set_desktop(d, TRUE);
1301     }
1302 }
1303
1304 void action_desktop_last(union ActionData *data)
1305 {
1306     if (screen_last_desktop < screen_num_desktops)
1307         screen_set_desktop(screen_last_desktop, TRUE);
1308 }
1309
1310 void action_toggle_decorations(union ActionData *data)
1311 {
1312     ObClient *c = data->client.any.c;
1313
1314     client_action_start(data);
1315     client_set_undecorated(c, !c->undecorated);
1316     client_action_end(data, FALSE);
1317 }
1318
1319 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1320                            gboolean shaded)
1321 {
1322     /* let's make x and y client relative instead of screen relative */
1323     x = x - cx;
1324     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1325
1326 #define X x*ch/cw
1327 #define A -4*X + 7*ch/3
1328 #define B  4*X -15*ch/9
1329 #define C -X/4 + 2*ch/3
1330 #define D  X/4 + 5*ch/12
1331 #define E  X/4 +   ch/3
1332 #define F -X/4 + 7*ch/12
1333 #define G  4*X - 4*ch/3
1334 #define H -4*X + 8*ch/3
1335 #define a (y > 5*ch/9)
1336 #define b (x < 4*cw/9)
1337 #define c (x > 5*cw/9)
1338 #define d (y < 4*ch/9)
1339
1340     /*
1341       Each of these defines (except X which is just there for fun), represents
1342       the equation of a line. The lines they represent are shown in the diagram
1343       below. Checking y against these lines, we are able to choose a region
1344       of the window as shown.
1345
1346       +---------------------A-------|-------|-------B---------------------+
1347       |                     |A                     B|                     |
1348       |                     |A      |       |      B|                     |
1349       |                     | A                   B |                     |
1350       |                     | A     |       |     B |                     |
1351       |                     |  A                 B  |                     |
1352       |                     |  A    |       |    B  |                     |
1353       |        northwest    |   A     north     B   |   northeast         |
1354       |                     |   A   |       |   B   |                     |
1355       |                     |    A             B    |                     |
1356       C---------------------+----A--+-------+--B----+---------------------D
1357       |CCCCCCC              |     A           B     |              DDDDDDD|
1358       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1359       |               CCCCCCC      A         B      DDDDDDD               |
1360       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1361       |                     |       b       c       |                     | sh
1362       |             west    |       b  move c       |   east              | ad
1363       |                     |       b       c       |                     | ed
1364       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1365       |               EEEEEEE      G         H      FFFFFFF               |
1366       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1367       |EEEEEEE              |     G           H     |              FFFFFFF|
1368       E---------------------+----G--+-------+--H----+---------------------F
1369       |                     |    G             H    |                     |
1370       |                     |   G   |       |   H   |                     |
1371       |        southwest    |   G     south     H   |   southeast         |
1372       |                     |  G    |       |    H  |                     |
1373       |                     |  G                 H  |                     |
1374       |                     | G     |       |     H |                     |
1375       |                     | G                   H |                     |
1376       |                     |G      |       |      H|                     |
1377       |                     |G                     H|                     |
1378       +---------------------G-------|-------|-------H---------------------+
1379     */
1380
1381     if (shaded) {
1382         /* for shaded windows, you can only resize west/east and move */
1383         if (b)
1384             return prop_atoms.net_wm_moveresize_size_left;
1385         if (c)
1386             return prop_atoms.net_wm_moveresize_size_right;
1387         return prop_atoms.net_wm_moveresize_move;
1388     }
1389
1390     if (y < A && y >= C)
1391         return prop_atoms.net_wm_moveresize_size_topleft;
1392     else if (y >= A && y >= B && a)
1393         return prop_atoms.net_wm_moveresize_size_top;
1394     else if (y < B && y >= D)
1395         return prop_atoms.net_wm_moveresize_size_topright;
1396     else if (y < C && y >= E && b)
1397         return prop_atoms.net_wm_moveresize_size_left;
1398     else if (y < D && y >= F && c)
1399         return prop_atoms.net_wm_moveresize_size_right;
1400     else if (y < E && y >= G)
1401         return prop_atoms.net_wm_moveresize_size_bottomleft;
1402     else if (y < G && y < H && d)
1403         return prop_atoms.net_wm_moveresize_size_bottom;
1404     else if (y >= H && y < F)
1405         return prop_atoms.net_wm_moveresize_size_bottomright;
1406     else
1407         return prop_atoms.net_wm_moveresize_move;
1408
1409 #undef X
1410 #undef A
1411 #undef B
1412 #undef C
1413 #undef D
1414 #undef E
1415 #undef F
1416 #undef G
1417 #undef H
1418 #undef a
1419 #undef b
1420 #undef c
1421 #undef d
1422 }
1423
1424 void action_resize(union ActionData *data)
1425 {
1426     ObClient *c = data->moveresize.any.c;
1427     guint32 corner;
1428
1429     if (data->moveresize.keyboard)
1430         corner = prop_atoms.net_wm_moveresize_size_keyboard;
1431     else if (data->moveresize.corner)
1432         corner = data->moveresize.corner; /* it was specified in the binding */
1433     else
1434         corner = pick_corner(data->any.x, data->any.y,
1435                              c->frame->area.x, c->frame->area.y,
1436                              /* use the client size because the frame
1437                                 can be differently sized (shaded
1438                                 windows) and we want this based on the
1439                                 clients size */
1440                              c->area.width + c->frame->size.left +
1441                              c->frame->size.right,
1442                              c->area.height + c->frame->size.top +
1443                              c->frame->size.bottom, c->shaded);
1444
1445     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1446 }
1447
1448 void action_directional_focus(union ActionData *data)
1449 {
1450     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1451        on us */
1452     event_halt_focus_delay();
1453
1454     focus_directional_cycle(data->interdiraction.direction,
1455                             data->interdiraction.dock_windows,
1456                             data->interdiraction.desktop_windows,
1457                             data->any.interactive,
1458                             data->interdiraction.dialog,
1459                             data->interdiraction.inter.final,
1460                             data->interdiraction.inter.cancel);
1461 }
1462
1463 void action_movetoedge(union ActionData *data)
1464 {
1465     gint x, y;
1466     ObClient *c = data->diraction.any.c;
1467
1468     x = c->frame->area.x;
1469     y = c->frame->area.y;
1470     
1471     switch(data->diraction.direction) {
1472     case OB_DIRECTION_NORTH:
1473         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1474                                            data->diraction.hang)
1475             - (data->diraction.hang ? c->frame->area.height : 0);
1476         break;
1477     case OB_DIRECTION_WEST:
1478         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1479                                            data->diraction.hang)
1480             - (data->diraction.hang ? c->frame->area.width : 0);
1481         break;
1482     case OB_DIRECTION_SOUTH:
1483         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1484                                            data->diraction.hang)
1485             - (data->diraction.hang ? 0 : c->frame->area.height);
1486         break;
1487     case OB_DIRECTION_EAST:
1488         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1489                                            data->diraction.hang)
1490             - (data->diraction.hang ? 0 : c->frame->area.width);
1491         break;
1492     default:
1493         g_assert_not_reached();
1494     }
1495     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1496     client_action_start(data);
1497     client_move(c, x, y);
1498     client_action_end(data, FALSE);
1499 }
1500
1501 void action_growtoedge(union ActionData *data)
1502 {
1503     gint x, y, width, height, dest;
1504     ObClient *c = data->diraction.any.c;
1505     Rect *a;
1506
1507     a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
1508     x = c->frame->area.x;
1509     y = c->frame->area.y;
1510     /* get the unshaded frame's dimensions..if it is shaded */
1511     width = c->area.width + c->frame->size.left + c->frame->size.right;
1512     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1513
1514     switch(data->diraction.direction) {
1515     case OB_DIRECTION_NORTH:
1516         if (c->shaded) break; /* don't allow vertical resize if shaded */
1517
1518         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1519         if (a->y == y)
1520             height = height / 2;
1521         else {
1522             height = c->frame->area.y + height - dest;
1523             y = dest;
1524         }
1525         break;
1526     case OB_DIRECTION_WEST:
1527         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1528         if (a->x == x)
1529             width = width / 2;
1530         else {
1531             width = c->frame->area.x + width - dest;
1532             x = dest;
1533         }
1534         break;
1535     case OB_DIRECTION_SOUTH:
1536         if (c->shaded) break; /* don't allow vertical resize if shaded */
1537
1538         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1539         if (a->y + a->height == y + c->frame->area.height) {
1540             height = c->frame->area.height / 2;
1541             y = a->y + a->height - height;
1542         } else
1543             height = dest - c->frame->area.y;
1544         y += (height - c->frame->area.height) % c->size_inc.height;
1545         height -= (height - c->frame->area.height) % c->size_inc.height;
1546         break;
1547     case OB_DIRECTION_EAST:
1548         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1549         if (a->x + a->width == x + c->frame->area.width) {
1550             width = c->frame->area.width / 2;
1551             x = a->x + a->width - width;
1552         } else
1553             width = dest - c->frame->area.x;
1554         x += (width - c->frame->area.width) % c->size_inc.width;
1555         width -= (width - c->frame->area.width) % c->size_inc.width;
1556         break;
1557     default:
1558         g_assert_not_reached();
1559     }
1560     width -= c->frame->size.left + c->frame->size.right;
1561     height -= c->frame->size.top + c->frame->size.bottom;
1562     frame_frame_gravity(c->frame, &x, &y, width, height);
1563     client_action_start(data);
1564     client_move_resize(c, x, y, width, height);
1565     client_action_end(data, FALSE);
1566     g_free(a);
1567 }
1568
1569 void action_send_to_layer(union ActionData *data)
1570 {
1571     client_set_layer(data->layer.any.c, data->layer.layer);
1572 }
1573
1574 void action_toggle_layer(union ActionData *data)
1575 {
1576     ObClient *c = data->layer.any.c;
1577
1578     client_action_start(data);
1579     if (data->layer.layer < 0)
1580         client_set_layer(c, c->below ? 0 : -1);
1581     else if (data->layer.layer > 0)
1582         client_set_layer(c, c->above ? 0 : 1);
1583     client_action_end(data, config_focus_under_mouse);
1584 }
1585
1586 void action_toggle_dockautohide(union ActionData *data)
1587 {
1588     config_dock_hide = !config_dock_hide;
1589     dock_configure();
1590 }
1591
1592 void action_add_desktop(union ActionData *data)
1593 {
1594     client_action_start(data);
1595     screen_set_num_desktops(screen_num_desktops+1);
1596
1597     /* move all the clients over */
1598     if (data->addremovedesktop.current) {
1599         GList *it;
1600
1601         for (it = client_list; it; it = g_list_next(it)) {
1602             ObClient *c = it->data;
1603             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
1604                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
1605         }
1606     }
1607
1608     client_action_end(data, config_focus_under_mouse);
1609 }
1610
1611 void action_remove_desktop(union ActionData *data)
1612 {
1613     guint rmdesktop, movedesktop;
1614     GList *it, *stacking_copy;
1615
1616     if (screen_num_desktops < 2) return;
1617
1618     client_action_start(data);
1619
1620     /* what desktop are we removing and moving to? */
1621     if (data->addremovedesktop.current)
1622         rmdesktop = screen_desktop;
1623     else
1624         rmdesktop = screen_num_desktops - 1;
1625     if (rmdesktop < screen_num_desktops - 1)
1626         movedesktop = rmdesktop + 1;
1627     else
1628         movedesktop = rmdesktop;
1629
1630     /* make a copy of the list cuz we're changing it */
1631     stacking_copy = g_list_copy(stacking_list);
1632     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
1633         if (WINDOW_IS_CLIENT(it->data)) {
1634             ObClient *c = it->data;
1635             guint d = c->desktop;
1636             if (d != DESKTOP_ALL && d >= movedesktop) {
1637                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
1638                 ob_debug("moving window %s\n", c->title);
1639             }
1640             /* raise all the windows that are on the current desktop which
1641                is being merged */
1642             if ((screen_desktop == rmdesktop - 1 ||
1643                  screen_desktop == rmdesktop) &&
1644                 (d == DESKTOP_ALL || d == screen_desktop))
1645             {
1646                 stacking_raise(CLIENT_AS_WINDOW(c));
1647                 ob_debug("raising window %s\n", c->title);
1648             }
1649         }
1650     }
1651
1652     /* act like we're changing desktops */
1653     if (screen_desktop < screen_num_desktops - 1) {
1654         gint d = screen_desktop;
1655         screen_desktop = screen_last_desktop;
1656         screen_set_desktop(d, TRUE);
1657         ob_debug("fake desktop change\n");
1658     }
1659
1660     screen_set_num_desktops(screen_num_desktops-1);
1661
1662     client_action_end(data, config_focus_under_mouse);
1663 }