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