]> icculus.org git repositories - dana/openbox.git/blob - openbox/action.c
add the kill/close/cyclewindows actions
[dana/openbox.git] / openbox / action.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    action.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "client.h"
22 #include "focus.h"
23 #include "focus_cycle.h"
24 #include "moveresize.h"
25 #include "menu.h"
26 #include "prop.h"
27 #include "stacking.h"
28 #include "screen.h"
29 #include "action.h"
30 #include "openbox.h"
31 #include "grab.h"
32 #include "keyboard.h"
33 #include "event.h"
34 #include "dock.h"
35 #include "config.h"
36 #include "mainloop.h"
37 #include "startupnotify.h"
38 #include "gettext.h"
39
40 #include <glib.h>
41
42
43
44 typedef struct
45 {
46     const gchar *name;
47     void (*func)(union ActionData *);
48     void (*setup)(ObAction **, ObUserAction uact);
49 } ActionString;
50
51 static ObAction *action_new(void (*func)(union ActionData *data))
52 {
53     ObAction *a = g_new0(ObAction, 1);
54     a->ref = 1;
55     a->func = func;
56
57     return a;
58 }
59
60 void action_ref(ObAction *a)
61 {
62     ++a->ref;
63 }
64
65 void action_unref(ObAction *a)
66 {
67     if (a == NULL) return;
68
69     if (--a->ref > 0) return;
70
71     /* deal with pointers */
72     if (a->func == action_execute || a->func == action_restart)
73         g_free(a->data.execute.path);
74     else if (a->func == action_debug)
75         g_free(a->data.debug.string);
76     else if (a->func == action_showmenu)
77         g_free(a->data.showmenu.name);
78
79     g_free(a);
80 }
81
82 ObAction* action_copy(const ObAction *src)
83 {
84     ObAction *a = action_new(src->func);
85
86     a->data = src->data;
87
88     /* deal with pointers */
89     if (a->func == action_execute || a->func == action_restart)
90         a->data.execute.path = g_strdup(a->data.execute.path);
91     else if (a->func == action_debug)
92         a->data.debug.string = g_strdup(a->data.debug.string);
93     else if (a->func == action_showmenu)
94         a->data.showmenu.name = g_strdup(a->data.showmenu.name);
95
96     return a;
97 }
98
99 void setup_action_directional_focus_north(ObAction **a, ObUserAction uact)
100 {
101     (*a)->data.interdiraction.inter.any.interactive = TRUE;
102     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTH;
103     (*a)->data.interdiraction.dialog = TRUE;
104     (*a)->data.interdiraction.dock_windows = FALSE;
105     (*a)->data.interdiraction.desktop_windows = FALSE;
106 }
107
108 void setup_action_directional_focus_east(ObAction **a, ObUserAction uact)
109 {
110     (*a)->data.interdiraction.inter.any.interactive = TRUE;
111     (*a)->data.interdiraction.direction = OB_DIRECTION_EAST;
112     (*a)->data.interdiraction.dialog = TRUE;
113     (*a)->data.interdiraction.dock_windows = FALSE;
114     (*a)->data.interdiraction.desktop_windows = FALSE;
115 }
116
117 void setup_action_directional_focus_south(ObAction **a, ObUserAction uact)
118 {
119     (*a)->data.interdiraction.inter.any.interactive = TRUE;
120     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTH;
121     (*a)->data.interdiraction.dialog = TRUE;
122     (*a)->data.interdiraction.dock_windows = FALSE;
123     (*a)->data.interdiraction.desktop_windows = FALSE;
124 }
125
126 void setup_action_directional_focus_west(ObAction **a, ObUserAction uact)
127 {
128     (*a)->data.interdiraction.inter.any.interactive = TRUE;
129     (*a)->data.interdiraction.direction = OB_DIRECTION_WEST;
130     (*a)->data.interdiraction.dialog = TRUE;
131     (*a)->data.interdiraction.dock_windows = FALSE;
132     (*a)->data.interdiraction.desktop_windows = FALSE;
133 }
134
135 void setup_action_directional_focus_northeast(ObAction **a, ObUserAction uact)
136 {
137     (*a)->data.interdiraction.inter.any.interactive = TRUE;
138     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHEAST;
139     (*a)->data.interdiraction.dialog = TRUE;
140     (*a)->data.interdiraction.dock_windows = FALSE;
141     (*a)->data.interdiraction.desktop_windows = FALSE;
142 }
143
144 void setup_action_directional_focus_southeast(ObAction **a, ObUserAction uact)
145 {
146     (*a)->data.interdiraction.inter.any.interactive = TRUE;
147     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHEAST;
148     (*a)->data.interdiraction.dialog = TRUE;
149     (*a)->data.interdiraction.dock_windows = FALSE;
150     (*a)->data.interdiraction.desktop_windows = FALSE;
151 }
152
153 void setup_action_directional_focus_southwest(ObAction **a, ObUserAction uact)
154 {
155     (*a)->data.interdiraction.inter.any.interactive = TRUE;
156     (*a)->data.interdiraction.direction = OB_DIRECTION_SOUTHWEST;
157     (*a)->data.interdiraction.dialog = TRUE;
158     (*a)->data.interdiraction.dock_windows = FALSE;
159     (*a)->data.interdiraction.desktop_windows = FALSE;
160 }
161
162 void setup_action_directional_focus_northwest(ObAction **a, ObUserAction uact)
163 {
164     (*a)->data.interdiraction.inter.any.interactive = TRUE;
165     (*a)->data.interdiraction.direction = OB_DIRECTION_NORTHWEST;
166     (*a)->data.interdiraction.dialog = TRUE;
167     (*a)->data.interdiraction.dock_windows = FALSE;
168     (*a)->data.interdiraction.desktop_windows = FALSE;
169 }
170
171 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
172 {
173     (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
174     (*a)->data.sendto.follow = TRUE;
175 }
176
177 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
178 {
179     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
180     (*a)->data.sendtodir.inter.any.interactive = TRUE;
181     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
182     (*a)->data.sendtodir.linear = TRUE;
183     (*a)->data.sendtodir.wrap = TRUE;
184     (*a)->data.sendtodir.follow = TRUE;
185 }
186
187 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
188 {
189     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
190     (*a)->data.sendtodir.inter.any.interactive = TRUE;
191     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
192     (*a)->data.sendtodir.linear = TRUE;
193     (*a)->data.sendtodir.wrap = TRUE;
194     (*a)->data.sendtodir.follow = TRUE;
195 }
196
197 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
198 {
199     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
200     (*a)->data.sendtodir.inter.any.interactive = TRUE;
201     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
202     (*a)->data.sendtodir.linear = FALSE;
203     (*a)->data.sendtodir.wrap = TRUE;
204     (*a)->data.sendtodir.follow = TRUE;
205 }
206
207 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
208 {
209     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
210     (*a)->data.sendtodir.inter.any.interactive = TRUE;
211     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
212     (*a)->data.sendtodir.linear = FALSE;
213     (*a)->data.sendtodir.wrap = TRUE;
214     (*a)->data.sendtodir.follow = TRUE;
215 }
216
217 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
218 {
219     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
220     (*a)->data.sendtodir.inter.any.interactive = TRUE;
221     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
222     (*a)->data.sendtodir.linear = FALSE;
223     (*a)->data.sendtodir.wrap = TRUE;
224     (*a)->data.sendtodir.follow = TRUE;
225 }
226
227 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
228 {
229     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
230     (*a)->data.sendtodir.inter.any.interactive = TRUE;
231     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
232     (*a)->data.sendtodir.linear = FALSE;
233     (*a)->data.sendtodir.wrap = TRUE;
234     (*a)->data.sendtodir.follow = TRUE;
235 }
236
237 void setup_action_desktop(ObAction **a, ObUserAction uact)
238 {
239 /*
240     (*a)->data.desktop.inter.any.interactive = FALSE;
241 */
242 }
243
244 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
245 {
246     (*a)->data.desktopdir.inter.any.interactive = TRUE;
247     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
248     (*a)->data.desktopdir.linear = TRUE;
249     (*a)->data.desktopdir.wrap = TRUE;
250 }
251
252 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
253 {
254     (*a)->data.desktopdir.inter.any.interactive = TRUE;
255     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
256     (*a)->data.desktopdir.linear = TRUE;
257     (*a)->data.desktopdir.wrap = TRUE;
258 }
259
260 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
261 {
262     (*a)->data.desktopdir.inter.any.interactive = TRUE;
263     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
264     (*a)->data.desktopdir.linear = FALSE;
265     (*a)->data.desktopdir.wrap = TRUE;
266 }
267
268 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
269 {
270     (*a)->data.desktopdir.inter.any.interactive = TRUE;
271     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
272     (*a)->data.desktopdir.linear = FALSE;
273     (*a)->data.desktopdir.wrap = TRUE;
274 }
275
276 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
277 {
278     (*a)->data.desktopdir.inter.any.interactive = TRUE;
279     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
280     (*a)->data.desktopdir.linear = FALSE;
281     (*a)->data.desktopdir.wrap = TRUE;
282 }
283
284 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
285 {
286     (*a)->data.desktopdir.inter.any.interactive = TRUE;
287     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
288     (*a)->data.desktopdir.linear = FALSE;
289     (*a)->data.desktopdir.wrap = TRUE;
290 }
291
292 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
293 {
294     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
295     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
296     (*a)->data.diraction.hang = TRUE;
297 }
298
299 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
300 {
301     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
302     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
303     (*a)->data.diraction.hang = TRUE;
304 }
305
306 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
307 {
308     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
309     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
310     (*a)->data.diraction.hang = TRUE;
311 }
312
313 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
314 {
315     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
316     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
317     (*a)->data.diraction.hang = TRUE;
318 }
319
320 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
321 {
322     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
323     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
324     (*a)->data.diraction.hang = FALSE;
325 }
326
327 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
328 {
329     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
330     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
331     (*a)->data.diraction.hang = FALSE;
332 }
333
334 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
335 {
336     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
337     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
338     (*a)->data.diraction.hang = FALSE;
339 }
340
341 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
342 {
343     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
344     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
345     (*a)->data.diraction.hang = FALSE;
346 }
347
348 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
349 {
350     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
351     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
352 }
353
354 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
355 {
356     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
357     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
358 }
359
360 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
361 {
362     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
363     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
364 }
365
366 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
367 {
368     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
369     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
370 }
371
372 void setup_action_top_layer(ObAction **a, ObUserAction uact)
373 {
374     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
375     (*a)->data.layer.layer = 1;
376 }
377
378 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
379 {
380     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
381     (*a)->data.layer.layer = 0;
382 }
383
384 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
385 {
386     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
387     (*a)->data.layer.layer = -1;
388 }
389
390 void setup_action_resize(ObAction **a, ObUserAction uact)
391 {
392     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
393     (*a)->data.moveresize.keyboard =
394         (uact == OB_USER_ACTION_NONE ||
395          uact == OB_USER_ACTION_KEYBOARD_KEY ||
396          uact == OB_USER_ACTION_MENU_SELECTION);
397     (*a)->data.moveresize.corner = 0;
398 }
399
400 void setup_action_addremove_desktop_current(ObAction **a, ObUserAction uact)
401 {
402     (*a)->data.addremovedesktop.current = TRUE;
403 }
404
405 void setup_action_addremove_desktop_last(ObAction **a, ObUserAction uact)
406 {
407     (*a)->data.addremovedesktop.current = FALSE;
408 }
409
410 void setup_client_action(ObAction **a, ObUserAction uact)
411 {
412     (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
413 }
414
415 ActionString actionstrings[] =
416 {
417     {
418         "directionalfocusnorth", 
419         action_directional_focus, 
420         setup_action_directional_focus_north
421     },
422     {
423         "directionalfocuseast", 
424         action_directional_focus, 
425         setup_action_directional_focus_east
426     },
427     {
428         "directionalfocussouth", 
429         action_directional_focus, 
430         setup_action_directional_focus_south
431     },
432     {
433         "directionalfocuswest",
434         action_directional_focus,
435         setup_action_directional_focus_west
436     },
437     {
438         "directionalfocusnortheast",
439         action_directional_focus,
440         setup_action_directional_focus_northeast
441     },
442     {
443         "directionalfocussoutheast",
444         action_directional_focus,
445         setup_action_directional_focus_southeast
446     },
447     {
448         "directionalfocussouthwest",
449         action_directional_focus,
450         setup_action_directional_focus_southwest
451     },
452     {
453         "directionalfocusnorthwest",
454         action_directional_focus,
455         setup_action_directional_focus_northwest
456     },
457     {
458         "shadelower",
459         action_shadelower,
460         setup_client_action
461     },
462     {
463         "unshaderaise",
464         action_unshaderaise,
465         setup_client_action
466     },
467     {
468         "toggleomnipresent",
469         action_toggle_omnipresent,
470         setup_client_action
471     },
472     {
473         "resizerelativevert",
474         action_resize_relative_vert,
475         setup_client_action
476     },
477     {
478         "resizerelative",
479         action_resize_relative,
480         setup_client_action
481     },
482     {
483         "sendtodesktop",
484         action_send_to_desktop,
485         setup_action_send_to_desktop
486     },
487     {
488         "sendtodesktopnext",
489         action_send_to_desktop_dir,
490         setup_action_send_to_desktop_next
491     },
492     {
493         "sendtodesktopprevious",
494         action_send_to_desktop_dir,
495         setup_action_send_to_desktop_prev
496     },
497     {
498         "sendtodesktopright",
499         action_send_to_desktop_dir,
500         setup_action_send_to_desktop_right
501     },
502     {
503         "sendtodesktopleft",
504         action_send_to_desktop_dir,
505         setup_action_send_to_desktop_left
506     },
507     {
508         "sendtodesktopup",
509         action_send_to_desktop_dir,
510         setup_action_send_to_desktop_up
511     },
512     {
513         "sendtodesktopdown",
514         action_send_to_desktop_dir,
515         setup_action_send_to_desktop_down
516     },
517     {
518         "desktop",
519         action_desktop,
520         setup_action_desktop
521     },
522     {
523         "desktopnext",
524         action_desktop_dir,
525         setup_action_desktop_next
526     },
527     {
528         "desktopprevious",
529         action_desktop_dir,
530         setup_action_desktop_prev
531     },
532     {
533         "desktopright",
534         action_desktop_dir,
535         setup_action_desktop_right
536     },
537     {
538         "desktopleft",
539         action_desktop_dir,
540         setup_action_desktop_left
541     },
542     {
543         "desktopup",
544         action_desktop_dir,
545         setup_action_desktop_up
546     },
547     {
548         "desktopdown",
549         action_desktop_dir,
550         setup_action_desktop_down
551     },
552     {
553         "toggledecorations",
554         action_toggle_decorations,
555         setup_client_action
556     },
557     {
558         "resize",
559         action_resize,
560         setup_action_resize
561     },
562     {
563         "toggledockautohide",
564         action_toggle_dockautohide,
565         NULL
566     },
567     {
568         "desktoplast",
569         action_desktop_last,
570         NULL
571     },
572     {
573         "sendtotoplayer",
574         action_send_to_layer,
575         setup_action_top_layer
576     },
577     {
578         "togglealwaysontop",
579         action_toggle_layer,
580         setup_action_top_layer
581     },
582     {
583         "sendtonormallayer",
584         action_send_to_layer,
585         setup_action_normal_layer
586     },
587     {
588         "sendtobottomlayer",
589         action_send_to_layer,
590         setup_action_bottom_layer
591     },
592     {
593         "togglealwaysonbottom",
594         action_toggle_layer,
595         setup_action_bottom_layer
596     },
597     {
598         "movefromedgenorth",
599         action_movetoedge,
600         setup_action_movefromedge_north
601     },
602     {
603         "movefromedgesouth",
604         action_movetoedge,
605         setup_action_movefromedge_south
606     },
607     {
608         "movefromedgewest",
609         action_movetoedge,
610         setup_action_movefromedge_west
611     },
612     {
613         "movefromedgeeast",
614         action_movetoedge,
615         setup_action_movefromedge_east
616     },
617     {
618         "movetoedgenorth",
619         action_movetoedge,
620         setup_action_movetoedge_north
621     },
622     {
623         "movetoedgesouth",
624         action_movetoedge,
625         setup_action_movetoedge_south
626     },
627     {
628         "movetoedgewest",
629         action_movetoedge,
630         setup_action_movetoedge_west
631     },
632     {
633         "movetoedgeeast",
634         action_movetoedge,
635         setup_action_movetoedge_east
636     },
637     {
638         "growtoedgenorth",
639         action_growtoedge,
640         setup_action_growtoedge_north
641     },
642     {
643         "growtoedgesouth",
644         action_growtoedge,
645         setup_action_growtoedge_south
646     },
647     {
648         "growtoedgewest",
649         action_growtoedge,
650         setup_action_growtoedge_west
651     },
652     {
653         "growtoedgeeast",
654         action_growtoedge,
655         setup_action_growtoedge_east
656     },
657     {
658         "adddesktoplast",
659         action_add_desktop,
660         setup_action_addremove_desktop_last
661     },
662     {
663         "removedesktoplast",
664         action_remove_desktop,
665         setup_action_addremove_desktop_last
666     },
667     {
668         "adddesktopcurrent",
669         action_add_desktop,
670         setup_action_addremove_desktop_current
671     },
672     {
673         "removedesktopcurrent",
674         action_remove_desktop,
675         setup_action_addremove_desktop_current
676     },
677     {
678         NULL,
679         NULL,
680         NULL
681     }
682 };
683
684 /* only key bindings can be interactive. thus saith the xor.
685    because of how the mouse is grabbed, mouse events dont even get
686    read during interactive events, so no dice! >:) */
687 #define INTERACTIVE_LIMIT(a, uact) \
688     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
689         a->data.any.interactive = FALSE;
690
691 ObAction *action_from_string(const gchar *name, ObUserAction uact)
692 {
693     ObAction *a = NULL;
694     gboolean exist = FALSE;
695     gint i;
696
697     for (i = 0; actionstrings[i].name; i++)
698         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
699             exist = TRUE;
700             a = action_new(actionstrings[i].func);
701             if (actionstrings[i].setup)
702                 actionstrings[i].setup(&a, uact);
703             if (a)
704                 INTERACTIVE_LIMIT(a, uact);
705             break;
706         }
707     if (!exist)
708         g_message(_("Invalid action '%s' requested. No such action exists."),
709                   name);
710     if (!a)
711         g_message(_("Invalid use of action '%s'. Action will be ignored."),
712                   name);
713     return a;
714 }
715
716 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
717                        ObUserAction uact)
718 {
719     gchar *actname;
720     ObAction *act = NULL;
721     xmlNodePtr n;
722
723     if (parse_attr_string("name", node, &actname)) {
724         if ((act = action_from_string(actname, uact))) {
725             } else if (act->func == action_resize_relative) {
726                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
727                     act->data.relative.deltaxl = parse_int(doc, n);
728                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
729                     act->data.relative.deltayu = parse_int(doc, n);
730                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
731                     act->data.relative.deltax = parse_int(doc, n);
732                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
733                     act->data.relative.deltay = parse_int(doc, n);
734             } else if (act->func == action_desktop) {
735                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
736                     act->data.desktop.desk = parse_int(doc, n);
737                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
738 /*
739                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
740                     act->data.desktop.inter.any.interactive =
741                         parse_bool(doc, n);
742 */
743            } else if (act->func == action_send_to_desktop) {
744                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
745                     act->data.sendto.desk = parse_int(doc, n);
746                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
747                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
748                     act->data.sendto.follow = parse_bool(doc, n);
749             } else if (act->func == action_desktop_dir) {
750                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
751                     act->data.desktopdir.wrap = parse_bool(doc, n); 
752                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
753                     act->data.desktopdir.inter.any.interactive =
754                         parse_bool(doc, n);
755             } else if (act->func == action_send_to_desktop_dir) {
756                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
757                     act->data.sendtodir.wrap = parse_bool(doc, n);
758                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
759                     act->data.sendtodir.follow = parse_bool(doc, n);
760                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
761                     act->data.sendtodir.inter.any.interactive =
762                         parse_bool(doc, n);
763             } else if (act->func == action_directional_focus) {
764                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
765                     act->data.interdiraction.dialog = parse_bool(doc, n);
766                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
767                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
768                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
769                     act->data.interdiraction.desktop_windows =
770                         parse_bool(doc, n);
771             } else if (act->func == action_resize) {
772                 if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
773                     gchar *s = parse_string(doc, n);
774                     if (!g_ascii_strcasecmp(s, "top"))
775                         act->data.moveresize.corner =
776                             prop_atoms.net_wm_moveresize_size_top;
777                     else if (!g_ascii_strcasecmp(s, "bottom"))
778                         act->data.moveresize.corner =
779                             prop_atoms.net_wm_moveresize_size_bottom;
780                     else if (!g_ascii_strcasecmp(s, "left"))
781                         act->data.moveresize.corner =
782                             prop_atoms.net_wm_moveresize_size_left;
783                     else if (!g_ascii_strcasecmp(s, "right"))
784                         act->data.moveresize.corner =
785                             prop_atoms.net_wm_moveresize_size_right;
786                     else if (!g_ascii_strcasecmp(s, "topleft"))
787                         act->data.moveresize.corner =
788                             prop_atoms.net_wm_moveresize_size_topleft;
789                     else if (!g_ascii_strcasecmp(s, "topright"))
790                         act->data.moveresize.corner =
791                             prop_atoms.net_wm_moveresize_size_topright;
792                     else if (!g_ascii_strcasecmp(s, "bottomleft"))
793                         act->data.moveresize.corner =
794                             prop_atoms.net_wm_moveresize_size_bottomleft;
795                     else if (!g_ascii_strcasecmp(s, "bottomright"))
796                         act->data.moveresize.corner =
797                             prop_atoms.net_wm_moveresize_size_bottomright;
798                     g_free(s);
799                 }
800             INTERACTIVE_LIMIT(act, uact);
801         }
802         g_free(actname);
803     }
804     return act;
805 }
806
807 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
808                      guint state, guint button, gint x, gint y, Time time,
809                      gboolean cancel, gboolean done)
810 {
811     GSList *it;
812     ObAction *a;
813
814     if (!acts)
815         return;
816
817     if (x < 0 && y < 0)
818         screen_pointer_pos(&x, &y);
819
820     for (it = acts; it; it = g_slist_next(it)) {
821         a = it->data;
822
823         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
824             a->data.any.c = a->data.any.client_action ? c : NULL;
825             a->data.any.context = context;
826             a->data.any.x = x;
827             a->data.any.y = y;
828
829             a->data.any.button = button;
830
831             a->data.any.time = time;
832
833             if (a->data.any.interactive) {
834                 a->data.inter.cancel = cancel;
835                 a->data.inter.final = done;
836                 if (!(cancel || done))
837                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
838                         continue;
839             }
840
841             /* XXX UGLY HACK race with motion event starting a move and the
842                button release gettnig processed first. answer: don't queue
843                moveresize starts. UGLY HACK XXX
844
845                XXX ALSO don't queue showmenu events, because on button press
846                events we need to know if a mouse grab is going to take place,
847                and set the button to 0, so that later motion events don't think
848                that a drag is going on. since showmenu grabs the pointer..
849             */
850             if (a->data.any.interactive || a->func == action_move ||
851                 a->func == action_resize || a->func == action_showmenu)
852             {
853                 /* interactive actions are not queued */
854                 a->func(&a->data);
855             } else if (a->func == action_focus ||
856                        a->func == action_activate ||
857                        a->func == action_showmenu)
858             {
859                 /* XXX MORE UGLY HACK
860                    actions from clicks on client windows are NOT queued.
861                    this solves the mysterious click-and-drag-doesnt-work
862                    problem. it was because the window gets focused and stuff
863                    after the button event has already been passed through. i
864                    dont really know why it should care but it does and it makes
865                    a difference.
866
867                    however this very bogus ! !
868                    we want to send the button press to the window BEFORE
869                    we do the action because the action might move the windows
870                    (eg change desktops) and then the button press ends up on
871                    the completely wrong window !
872                    so, this is just for that bug, and it will only NOT queue it
873                    if it is a focusing action that can be used with the mouse
874                    pointer. ugh.
875
876                    also with the menus, there is a race going on. if the
877                    desktop wants to pop up a menu, and we do too, we send them
878                    the button before we pop up the menu, so they pop up their
879                    menu first. but not always. if we pop up our menu before
880                    sending them the button press, then the result is
881                    deterministic. yay.
882
883                    XXX further more. focus actions are not queued at all,
884                    because if you bind focus->showmenu, the menu will get
885                    hidden to do the focusing
886                 */
887                 a->func(&a->data);
888             } else
889                 ob_main_loop_queue_action(ob_main_loop, a);
890         }
891     }
892 }
893
894 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
895 {
896     ObAction *a;
897     GSList *l;
898
899     a = action_from_string(name, OB_USER_ACTION_NONE);
900     g_assert(a);
901
902     l = g_slist_append(NULL, a);
903
904     action_run(l, c, 0, time);
905 }
906
907 void action_unshaderaise(union ActionData *data)
908 {
909     if (data->client.any.c->shaded)
910         action_unshade(data);
911     else
912         action_raise(data);
913 }
914
915 void action_shadelower(union ActionData *data)
916 {
917     if (data->client.any.c->shaded)
918         action_lower(data);
919     else
920         action_shade(data);
921 }
922
923 void action_toggle_omnipresent(union ActionData *data)
924
925     client_set_desktop(data->client.any.c,
926                        data->client.any.c->desktop == DESKTOP_ALL ?
927                        screen_desktop : DESKTOP_ALL, FALSE, TRUE);
928 }
929
930 void action_resize_relative_horz(union ActionData *data)
931 {
932     ObClient *c = data->relative.any.c;
933     client_action_start(data);
934     client_resize(c,
935                   c->area.width + data->relative.deltax * c->size_inc.width,
936                   c->area.height);
937     client_action_end(data, FALSE);
938 }
939
940 void action_resize_relative_vert(union ActionData *data)
941 {
942     ObClient *c = data->relative.any.c;
943     if (!c->shaded) {
944         client_action_start(data);
945         client_resize(c, c->area.width, c->area.height +
946                       data->relative.deltax * c->size_inc.height);
947         client_action_end(data, FALSE);
948     }
949 }
950
951 void action_resize_relative(union ActionData *data)
952 {
953     ObClient *c = data->relative.any.c;
954     gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
955
956     client_action_start(data);
957
958     x = c->area.x;
959     y = c->area.y;
960     ow = c->area.width;
961     xoff = -data->relative.deltaxl * c->size_inc.width;
962     nw = ow + data->relative.deltax * c->size_inc.width
963         + data->relative.deltaxl * c->size_inc.width;
964     oh = c->area.height;
965     yoff = -data->relative.deltayu * c->size_inc.height;
966     nh = oh + data->relative.deltay * c->size_inc.height
967         + data->relative.deltayu * c->size_inc.height;
968
969     g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
970             data->relative.deltax, 
971             data->relative.deltaxl, 
972             x, ow, xoff, nw);
973     
974     client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
975     xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
976     yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
977     client_move_resize(c, x + xoff, y + yoff, nw, nh);
978     client_action_end(data, FALSE);
979 }
980
981 void action_send_to_desktop(union ActionData *data)
982 {
983     ObClient *c = data->sendto.any.c;
984
985     if (!client_normal(c)) return;
986
987     if (data->sendto.desk < screen_num_desktops ||
988         data->sendto.desk == DESKTOP_ALL) {
989         client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
990         if (data->sendto.follow && data->sendto.desk != screen_desktop)
991             screen_set_desktop(data->sendto.desk, TRUE);
992     }
993 }
994
995 void action_desktop(union ActionData *data)
996 {
997     /* XXX add the interactive/dialog option back again once the dialog
998        has been made to not use grabs */
999     if (data->desktop.desk < screen_num_desktops ||
1000         data->desktop.desk == DESKTOP_ALL)
1001     {
1002         screen_set_desktop(data->desktop.desk, TRUE);
1003         if (data->inter.any.interactive)
1004             screen_desktop_popup(data->desktop.desk, TRUE);
1005     }
1006 }
1007
1008 void action_desktop_dir(union ActionData *data)
1009 {
1010     guint d;
1011
1012     d = screen_cycle_desktop(data->desktopdir.dir,
1013                              data->desktopdir.wrap,
1014                              data->desktopdir.linear,
1015                              data->desktopdir.inter.any.interactive,
1016                              data->desktopdir.inter.final,
1017                              data->desktopdir.inter.cancel);
1018     /* only move the desktop when the action is complete. if we switch
1019        desktops during the interactive action, focus will move but with
1020        NotifyWhileGrabbed and applications don't like that. */
1021     if (!data->sendtodir.inter.any.interactive ||
1022         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1023     {
1024         if (d != screen_desktop)
1025             screen_set_desktop(d, TRUE);
1026     }
1027 }
1028
1029 void action_send_to_desktop_dir(union ActionData *data)
1030 {
1031     ObClient *c = data->sendtodir.inter.any.c;
1032     guint d;
1033
1034     if (!client_normal(c)) return;
1035
1036     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1037                              data->sendtodir.linear,
1038                              data->sendtodir.inter.any.interactive,
1039                              data->sendtodir.inter.final,
1040                              data->sendtodir.inter.cancel);
1041     /* only move the desktop when the action is complete. if we switch
1042        desktops during the interactive action, focus will move but with
1043        NotifyWhileGrabbed and applications don't like that. */
1044     if (!data->sendtodir.inter.any.interactive ||
1045         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1046     {
1047         client_set_desktop(c, d, data->sendtodir.follow, FALSE);
1048         if (data->sendtodir.follow && d != screen_desktop)
1049             screen_set_desktop(d, TRUE);
1050     }
1051 }
1052
1053 void action_desktop_last(union ActionData *data)
1054 {
1055     if (screen_last_desktop < screen_num_desktops)
1056         screen_set_desktop(screen_last_desktop, TRUE);
1057 }
1058
1059 void action_toggle_decorations(union ActionData *data)
1060 {
1061     ObClient *c = data->client.any.c;
1062
1063     client_action_start(data);
1064     client_set_undecorated(c, !c->undecorated);
1065     client_action_end(data, FALSE);
1066 }
1067
1068 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1069                            gboolean shaded)
1070 {
1071     /* let's make x and y client relative instead of screen relative */
1072     x = x - cx;
1073     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1074
1075 #define X x*ch/cw
1076 #define A -4*X + 7*ch/3
1077 #define B  4*X -15*ch/9
1078 #define C -X/4 + 2*ch/3
1079 #define D  X/4 + 5*ch/12
1080 #define E  X/4 +   ch/3
1081 #define F -X/4 + 7*ch/12
1082 #define G  4*X - 4*ch/3
1083 #define H -4*X + 8*ch/3
1084 #define a (y > 5*ch/9)
1085 #define b (x < 4*cw/9)
1086 #define c (x > 5*cw/9)
1087 #define d (y < 4*ch/9)
1088
1089     /*
1090       Each of these defines (except X which is just there for fun), represents
1091       the equation of a line. The lines they represent are shown in the diagram
1092       below. Checking y against these lines, we are able to choose a region
1093       of the window as shown.
1094
1095       +---------------------A-------|-------|-------B---------------------+
1096       |                     |A                     B|                     |
1097       |                     |A      |       |      B|                     |
1098       |                     | A                   B |                     |
1099       |                     | A     |       |     B |                     |
1100       |                     |  A                 B  |                     |
1101       |                     |  A    |       |    B  |                     |
1102       |        northwest    |   A     north     B   |   northeast         |
1103       |                     |   A   |       |   B   |                     |
1104       |                     |    A             B    |                     |
1105       C---------------------+----A--+-------+--B----+---------------------D
1106       |CCCCCCC              |     A           B     |              DDDDDDD|
1107       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1108       |               CCCCCCC      A         B      DDDDDDD               |
1109       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1110       |                     |       b       c       |                     | sh
1111       |             west    |       b  move c       |   east              | ad
1112       |                     |       b       c       |                     | ed
1113       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1114       |               EEEEEEE      G         H      FFFFFFF               |
1115       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1116       |EEEEEEE              |     G           H     |              FFFFFFF|
1117       E---------------------+----G--+-------+--H----+---------------------F
1118       |                     |    G             H    |                     |
1119       |                     |   G   |       |   H   |                     |
1120       |        southwest    |   G     south     H   |   southeast         |
1121       |                     |  G    |       |    H  |                     |
1122       |                     |  G                 H  |                     |
1123       |                     | G     |       |     H |                     |
1124       |                     | G                   H |                     |
1125       |                     |G      |       |      H|                     |
1126       |                     |G                     H|                     |
1127       +---------------------G-------|-------|-------H---------------------+
1128     */
1129
1130     if (shaded) {
1131         /* for shaded windows, you can only resize west/east and move */
1132         if (b)
1133             return prop_atoms.net_wm_moveresize_size_left;
1134         if (c)
1135             return prop_atoms.net_wm_moveresize_size_right;
1136         return prop_atoms.net_wm_moveresize_move;
1137     }
1138
1139     if (y < A && y >= C)
1140         return prop_atoms.net_wm_moveresize_size_topleft;
1141     else if (y >= A && y >= B && a)
1142         return prop_atoms.net_wm_moveresize_size_top;
1143     else if (y < B && y >= D)
1144         return prop_atoms.net_wm_moveresize_size_topright;
1145     else if (y < C && y >= E && b)
1146         return prop_atoms.net_wm_moveresize_size_left;
1147     else if (y < D && y >= F && c)
1148         return prop_atoms.net_wm_moveresize_size_right;
1149     else if (y < E && y >= G)
1150         return prop_atoms.net_wm_moveresize_size_bottomleft;
1151     else if (y < G && y < H && d)
1152         return prop_atoms.net_wm_moveresize_size_bottom;
1153     else if (y >= H && y < F)
1154         return prop_atoms.net_wm_moveresize_size_bottomright;
1155     else
1156         return prop_atoms.net_wm_moveresize_move;
1157
1158 #undef X
1159 #undef A
1160 #undef B
1161 #undef C
1162 #undef D
1163 #undef E
1164 #undef F
1165 #undef G
1166 #undef H
1167 #undef a
1168 #undef b
1169 #undef c
1170 #undef d
1171 }
1172
1173 void action_resize(union ActionData *data)
1174 {
1175     ObClient *c = data->moveresize.any.c;
1176     guint32 corner;
1177
1178     if (data->moveresize.keyboard)
1179         corner = prop_atoms.net_wm_moveresize_size_keyboard;
1180     else if (data->moveresize.corner)
1181         corner = data->moveresize.corner; /* it was specified in the binding */
1182     else
1183         corner = pick_corner(data->any.x, data->any.y,
1184                              c->frame->area.x, c->frame->area.y,
1185                              /* use the client size because the frame
1186                                 can be differently sized (shaded
1187                                 windows) and we want this based on the
1188                                 clients size */
1189                              c->area.width + c->frame->size.left +
1190                              c->frame->size.right,
1191                              c->area.height + c->frame->size.top +
1192                              c->frame->size.bottom, c->shaded);
1193
1194     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1195 }
1196
1197 void action_directional_focus(union ActionData *data)
1198 {
1199     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1200        on us */
1201     event_halt_focus_delay();
1202
1203     focus_directional_cycle(data->interdiraction.direction,
1204                             data->interdiraction.dock_windows,
1205                             data->interdiraction.desktop_windows,
1206                             data->any.interactive,
1207                             data->interdiraction.dialog,
1208                             data->interdiraction.inter.final,
1209                             data->interdiraction.inter.cancel);
1210 }
1211
1212 void action_movetoedge(union ActionData *data)
1213 {
1214     gint x, y;
1215     ObClient *c = data->diraction.any.c;
1216
1217     x = c->frame->area.x;
1218     y = c->frame->area.y;
1219     
1220     switch(data->diraction.direction) {
1221     case OB_DIRECTION_NORTH:
1222         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1223                                            data->diraction.hang)
1224             - (data->diraction.hang ? c->frame->area.height : 0);
1225         break;
1226     case OB_DIRECTION_WEST:
1227         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1228                                            data->diraction.hang)
1229             - (data->diraction.hang ? c->frame->area.width : 0);
1230         break;
1231     case OB_DIRECTION_SOUTH:
1232         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1233                                            data->diraction.hang)
1234             - (data->diraction.hang ? 0 : c->frame->area.height);
1235         break;
1236     case OB_DIRECTION_EAST:
1237         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1238                                            data->diraction.hang)
1239             - (data->diraction.hang ? 0 : c->frame->area.width);
1240         break;
1241     default:
1242         g_assert_not_reached();
1243     }
1244     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1245     client_action_start(data);
1246     client_move(c, x, y);
1247     client_action_end(data, FALSE);
1248 }
1249
1250 void action_growtoedge(union ActionData *data)
1251 {
1252     gint x, y, width, height, dest;
1253     ObClient *c = data->diraction.any.c;
1254     Rect *a;
1255
1256     a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
1257     x = c->frame->area.x;
1258     y = c->frame->area.y;
1259     /* get the unshaded frame's dimensions..if it is shaded */
1260     width = c->area.width + c->frame->size.left + c->frame->size.right;
1261     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1262
1263     switch(data->diraction.direction) {
1264     case OB_DIRECTION_NORTH:
1265         if (c->shaded) break; /* don't allow vertical resize if shaded */
1266
1267         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1268         if (a->y == y)
1269             height = height / 2;
1270         else {
1271             height = c->frame->area.y + height - dest;
1272             y = dest;
1273         }
1274         break;
1275     case OB_DIRECTION_WEST:
1276         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1277         if (a->x == x)
1278             width = width / 2;
1279         else {
1280             width = c->frame->area.x + width - dest;
1281             x = dest;
1282         }
1283         break;
1284     case OB_DIRECTION_SOUTH:
1285         if (c->shaded) break; /* don't allow vertical resize if shaded */
1286
1287         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1288         if (a->y + a->height == y + c->frame->area.height) {
1289             height = c->frame->area.height / 2;
1290             y = a->y + a->height - height;
1291         } else
1292             height = dest - c->frame->area.y;
1293         y += (height - c->frame->area.height) % c->size_inc.height;
1294         height -= (height - c->frame->area.height) % c->size_inc.height;
1295         break;
1296     case OB_DIRECTION_EAST:
1297         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1298         if (a->x + a->width == x + c->frame->area.width) {
1299             width = c->frame->area.width / 2;
1300             x = a->x + a->width - width;
1301         } else
1302             width = dest - c->frame->area.x;
1303         x += (width - c->frame->area.width) % c->size_inc.width;
1304         width -= (width - c->frame->area.width) % c->size_inc.width;
1305         break;
1306     default:
1307         g_assert_not_reached();
1308     }
1309     width -= c->frame->size.left + c->frame->size.right;
1310     height -= c->frame->size.top + c->frame->size.bottom;
1311     frame_frame_gravity(c->frame, &x, &y, width, height);
1312     client_action_start(data);
1313     client_move_resize(c, x, y, width, height);
1314     client_action_end(data, FALSE);
1315     g_free(a);
1316 }
1317
1318 void action_send_to_layer(union ActionData *data)
1319 {
1320     client_set_layer(data->layer.any.c, data->layer.layer);
1321 }
1322
1323 void action_toggle_layer(union ActionData *data)
1324 {
1325     ObClient *c = data->layer.any.c;
1326
1327     client_action_start(data);
1328     if (data->layer.layer < 0)
1329         client_set_layer(c, c->below ? 0 : -1);
1330     else if (data->layer.layer > 0)
1331         client_set_layer(c, c->above ? 0 : 1);
1332     client_action_end(data, config_focus_under_mouse);
1333 }
1334
1335 void action_toggle_dockautohide(union ActionData *data)
1336 {
1337     config_dock_hide = !config_dock_hide;
1338     dock_configure();
1339 }
1340
1341 void action_add_desktop(union ActionData *data)
1342 {
1343     client_action_start(data);
1344     screen_set_num_desktops(screen_num_desktops+1);
1345
1346     /* move all the clients over */
1347     if (data->addremovedesktop.current) {
1348         GList *it;
1349
1350         for (it = client_list; it; it = g_list_next(it)) {
1351             ObClient *c = it->data;
1352             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
1353                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
1354         }
1355     }
1356
1357     client_action_end(data, config_focus_under_mouse);
1358 }
1359
1360 void action_remove_desktop(union ActionData *data)
1361 {
1362     guint rmdesktop, movedesktop;
1363     GList *it, *stacking_copy;
1364
1365     if (screen_num_desktops < 2) return;
1366
1367     client_action_start(data);
1368
1369     /* what desktop are we removing and moving to? */
1370     if (data->addremovedesktop.current)
1371         rmdesktop = screen_desktop;
1372     else
1373         rmdesktop = screen_num_desktops - 1;
1374     if (rmdesktop < screen_num_desktops - 1)
1375         movedesktop = rmdesktop + 1;
1376     else
1377         movedesktop = rmdesktop;
1378
1379     /* make a copy of the list cuz we're changing it */
1380     stacking_copy = g_list_copy(stacking_list);
1381     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
1382         if (WINDOW_IS_CLIENT(it->data)) {
1383             ObClient *c = it->data;
1384             guint d = c->desktop;
1385             if (d != DESKTOP_ALL && d >= movedesktop) {
1386                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
1387                 ob_debug("moving window %s\n", c->title);
1388             }
1389             /* raise all the windows that are on the current desktop which
1390                is being merged */
1391             if ((screen_desktop == rmdesktop - 1 ||
1392                  screen_desktop == rmdesktop) &&
1393                 (d == DESKTOP_ALL || d == screen_desktop))
1394             {
1395                 stacking_raise(CLIENT_AS_WINDOW(c));
1396                 ob_debug("raising window %s\n", c->title);
1397             }
1398         }
1399     }
1400
1401     /* act like we're changing desktops */
1402     if (screen_desktop < screen_num_desktops - 1) {
1403         gint d = screen_desktop;
1404         screen_desktop = screen_last_desktop;
1405         screen_set_desktop(d, TRUE);
1406         ob_debug("fake desktop change\n");
1407     }
1408
1409     screen_set_num_desktops(screen_num_desktops-1);
1410
1411     client_action_end(data, config_focus_under_mouse);
1412 }