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