]> icculus.org git repositories - dana/openbox.git/blob - openbox/action.c
improve the add/remove desktop actions a bunch.
[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 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_cycle_windows_next(ObAction **a, ObUserAction uact)
322 {
323     (*a)->data.cycle.inter.any.interactive = TRUE;
324     (*a)->data.cycle.linear = FALSE;
325     (*a)->data.cycle.forward = TRUE;
326     (*a)->data.cycle.dialog = TRUE;
327     (*a)->data.cycle.dock_windows = FALSE;
328     (*a)->data.cycle.desktop_windows = FALSE;
329     (*a)->data.cycle.all_desktops = FALSE;
330 }
331
332 void setup_action_cycle_windows_previous(ObAction **a, ObUserAction uact)
333 {
334     (*a)->data.cycle.inter.any.interactive = TRUE;
335     (*a)->data.cycle.linear = FALSE;
336     (*a)->data.cycle.forward = FALSE;
337     (*a)->data.cycle.dialog = TRUE;
338     (*a)->data.cycle.dock_windows = FALSE;
339     (*a)->data.cycle.desktop_windows = FALSE;
340     (*a)->data.cycle.all_desktops = FALSE;
341 }
342
343 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
344 {
345     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
346     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
347     (*a)->data.diraction.hang = TRUE;
348 }
349
350 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
351 {
352     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
353     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
354     (*a)->data.diraction.hang = TRUE;
355 }
356
357 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
358 {
359     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
360     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
361     (*a)->data.diraction.hang = TRUE;
362 }
363
364 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
365 {
366     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
367     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
368     (*a)->data.diraction.hang = TRUE;
369 }
370
371 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
372 {
373     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
374     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
375     (*a)->data.diraction.hang = FALSE;
376 }
377
378 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
379 {
380     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
381     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
382     (*a)->data.diraction.hang = FALSE;
383 }
384
385 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
386 {
387     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
388     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
389     (*a)->data.diraction.hang = FALSE;
390 }
391
392 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
393 {
394     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
395     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
396     (*a)->data.diraction.hang = FALSE;
397 }
398
399 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
400 {
401     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
402     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
403 }
404
405 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
406 {
407     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
408     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
409 }
410
411 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
412 {
413     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
414     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
415 }
416
417 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
418 {
419     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
420     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
421 }
422
423 void setup_action_top_layer(ObAction **a, ObUserAction uact)
424 {
425     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
426     (*a)->data.layer.layer = 1;
427 }
428
429 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
430 {
431     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
432     (*a)->data.layer.layer = 0;
433 }
434
435 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
436 {
437     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
438     (*a)->data.layer.layer = -1;
439 }
440
441 void setup_action_move(ObAction **a, ObUserAction uact)
442 {
443     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
444     (*a)->data.moveresize.keyboard =
445         (uact == OB_USER_ACTION_NONE ||
446          uact == OB_USER_ACTION_KEYBOARD_KEY ||
447          uact == OB_USER_ACTION_MENU_SELECTION);
448     (*a)->data.moveresize.corner = 0;
449 }
450
451 void setup_action_resize(ObAction **a, ObUserAction uact)
452 {
453     (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
454     (*a)->data.moveresize.keyboard =
455         (uact == OB_USER_ACTION_NONE ||
456          uact == OB_USER_ACTION_KEYBOARD_KEY ||
457          uact == OB_USER_ACTION_MENU_SELECTION);
458     (*a)->data.moveresize.corner = 0;
459 }
460
461 void setup_action_showmenu(ObAction **a, ObUserAction uact)
462 {
463     (*a)->data.showmenu.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
464     /* you cannot call ShowMenu from inside a menu, cuz the menu code makes
465        assumptions that there is only one menu (and submenus) open at
466        a time! */
467     if (uact == OB_USER_ACTION_MENU_SELECTION) {
468         action_unref(*a);
469         *a = NULL;
470     }
471 }
472
473 void setup_action_addremove_desktop_current(ObAction **a, ObUserAction uact)
474 {
475     (*a)->data.addremovedesktop.current = TRUE;
476 }
477
478 void setup_action_addremove_desktop_last(ObAction **a, ObUserAction uact)
479 {
480     (*a)->data.addremovedesktop.current = FALSE;
481 }
482
483 void setup_action_focus(ObAction **a, ObUserAction uact)
484 {
485     (*a)->data.any.client_action = OB_CLIENT_ACTION_OPTIONAL;
486 }
487
488 void setup_client_action(ObAction **a, ObUserAction uact)
489 {
490     (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
491 }
492
493 ActionString actionstrings[] =
494 {
495     {
496         "debug", 
497         action_debug,
498         NULL
499     },
500     {
501         "execute", 
502         action_execute,
503         NULL
504     },
505     {
506         "directionalfocusnorth", 
507         action_directional_focus, 
508         setup_action_directional_focus_north
509     },
510     {
511         "directionalfocuseast", 
512         action_directional_focus, 
513         setup_action_directional_focus_east
514     },
515     {
516         "directionalfocussouth", 
517         action_directional_focus, 
518         setup_action_directional_focus_south
519     },
520     {
521         "directionalfocuswest",
522         action_directional_focus,
523         setup_action_directional_focus_west
524     },
525     {
526         "directionalfocusnortheast",
527         action_directional_focus,
528         setup_action_directional_focus_northeast
529     },
530     {
531         "directionalfocussoutheast",
532         action_directional_focus,
533         setup_action_directional_focus_southeast
534     },
535     {
536         "directionalfocussouthwest",
537         action_directional_focus,
538         setup_action_directional_focus_southwest
539     },
540     {
541         "directionalfocusnorthwest",
542         action_directional_focus,
543         setup_action_directional_focus_northwest
544     },
545     {
546         "activate",
547         action_activate,
548         setup_action_focus
549     },
550     {
551         "focus",
552         action_focus,
553         setup_action_focus
554     },
555     {
556         "unfocus",
557         action_unfocus,
558         setup_client_action
559     },
560     {
561         "iconify",
562         action_iconify,
563         setup_client_action
564     },
565     {
566         "focustobottom",
567         action_focus_order_to_bottom,
568         setup_client_action
569     },
570     {
571         "raiselower",
572         action_raiselower,
573         setup_client_action
574     },
575     {
576         "raise",
577         action_raise,
578         setup_client_action
579     },
580     {
581         "lower",
582         action_lower,
583         setup_client_action
584     },
585     {
586         "close",
587         action_close,
588         setup_client_action
589     },
590     {
591         "kill",
592         action_kill,
593         setup_client_action
594     },
595     {
596         "shadelower",
597         action_shadelower,
598         setup_client_action
599     },
600     {
601         "unshaderaise",
602         action_unshaderaise,
603         setup_client_action
604     },
605     {
606         "shade",
607         action_shade,
608         setup_client_action
609     },
610     {
611         "unshade",
612         action_unshade,
613         setup_client_action
614     },
615     {
616         "toggleshade",
617         action_toggle_shade,
618         setup_client_action
619     },
620     {
621         "toggleomnipresent",
622         action_toggle_omnipresent,
623         setup_client_action
624     },
625     {
626         "moverelativehorz",
627         action_move_relative_horz,
628         setup_client_action
629     },
630     {
631         "moverelativevert",
632         action_move_relative_vert,
633         setup_client_action
634     },
635     {
636         "movetocenter",
637         action_move_to_center,
638         setup_client_action
639     },
640     {
641         "resizerelativehorz",
642         action_resize_relative_horz,
643         setup_client_action
644     },
645     {
646         "resizerelativevert",
647         action_resize_relative_vert,
648         setup_client_action
649     },
650     {
651         "moverelative",
652         action_move_relative,
653         setup_client_action
654     },
655     {
656         "resizerelative",
657         action_resize_relative,
658         setup_client_action
659     },
660     {
661         "maximizefull",
662         action_maximize_full,
663         setup_client_action
664     },
665     {
666         "unmaximizefull",
667         action_unmaximize_full,
668         setup_client_action
669     },
670     {
671         "togglemaximizefull",
672         action_toggle_maximize_full,
673         setup_client_action
674     },
675     {
676         "maximizehorz",
677         action_maximize_horz,
678         setup_client_action
679     },
680     {
681         "unmaximizehorz",
682         action_unmaximize_horz,
683         setup_client_action
684     },
685     {
686         "togglemaximizehorz",
687         action_toggle_maximize_horz,
688         setup_client_action
689     },
690     {
691         "maximizevert",
692         action_maximize_vert,
693         setup_client_action
694     },
695     {
696         "unmaximizevert",
697         action_unmaximize_vert,
698         setup_client_action
699     },
700     {
701         "togglemaximizevert",
702         action_toggle_maximize_vert,
703         setup_client_action
704     },
705     {
706         "togglefullscreen",
707         action_toggle_fullscreen,
708         setup_client_action
709     },
710     {
711         "sendtodesktop",
712         action_send_to_desktop,
713         setup_action_send_to_desktop
714     },
715     {
716         "sendtodesktopnext",
717         action_send_to_desktop_dir,
718         setup_action_send_to_desktop_next
719     },
720     {
721         "sendtodesktopprevious",
722         action_send_to_desktop_dir,
723         setup_action_send_to_desktop_prev
724     },
725     {
726         "sendtodesktopright",
727         action_send_to_desktop_dir,
728         setup_action_send_to_desktop_right
729     },
730     {
731         "sendtodesktopleft",
732         action_send_to_desktop_dir,
733         setup_action_send_to_desktop_left
734     },
735     {
736         "sendtodesktopup",
737         action_send_to_desktop_dir,
738         setup_action_send_to_desktop_up
739     },
740     {
741         "sendtodesktopdown",
742         action_send_to_desktop_dir,
743         setup_action_send_to_desktop_down
744     },
745     {
746         "desktop",
747         action_desktop,
748         setup_action_desktop
749     },
750     {
751         "desktopnext",
752         action_desktop_dir,
753         setup_action_desktop_next
754     },
755     {
756         "desktopprevious",
757         action_desktop_dir,
758         setup_action_desktop_prev
759     },
760     {
761         "desktopright",
762         action_desktop_dir,
763         setup_action_desktop_right
764     },
765     {
766         "desktopleft",
767         action_desktop_dir,
768         setup_action_desktop_left
769     },
770     {
771         "desktopup",
772         action_desktop_dir,
773         setup_action_desktop_up
774     },
775     {
776         "desktopdown",
777         action_desktop_dir,
778         setup_action_desktop_down
779     },
780     {
781         "toggledecorations",
782         action_toggle_decorations,
783         setup_client_action
784     },
785     {
786         "move",
787         action_move,
788         setup_action_move
789     },
790     {
791         "resize",
792         action_resize,
793         setup_action_resize
794     },
795     {
796         "toggledockautohide",
797         action_toggle_dockautohide,
798         NULL
799     },
800     {
801         "toggleshowdesktop",
802         action_toggle_show_desktop,
803         NULL
804     },
805     {
806         "showdesktop",
807         action_show_desktop,
808         NULL
809     },
810     {
811         "unshowdesktop",
812         action_unshow_desktop,
813         NULL
814     },
815     {
816         "desktoplast",
817         action_desktop_last,
818         NULL
819     },
820     {
821         "reconfigure",
822         action_reconfigure,
823         NULL
824     },
825     {
826         "restart",
827         action_restart,
828         NULL
829     },
830     {
831         "exit",
832         action_exit,
833         NULL
834     },
835     {
836         "showmenu",
837         action_showmenu,
838         setup_action_showmenu
839     },
840     {
841         "sendtotoplayer",
842         action_send_to_layer,
843         setup_action_top_layer
844     },
845     {
846         "togglealwaysontop",
847         action_toggle_layer,
848         setup_action_top_layer
849     },
850     {
851         "sendtonormallayer",
852         action_send_to_layer,
853         setup_action_normal_layer
854     },
855     {
856         "sendtobottomlayer",
857         action_send_to_layer,
858         setup_action_bottom_layer
859     },
860     {
861         "togglealwaysonbottom",
862         action_toggle_layer,
863         setup_action_bottom_layer
864     },
865     {
866         "nextwindow",
867         action_cycle_windows,
868         setup_action_cycle_windows_next
869     },
870     {
871         "previouswindow",
872         action_cycle_windows,
873         setup_action_cycle_windows_previous
874     },
875     {
876         "movefromedgenorth",
877         action_movetoedge,
878         setup_action_movefromedge_north
879     },
880     {
881         "movefromedgesouth",
882         action_movetoedge,
883         setup_action_movefromedge_south
884     },
885     {
886         "movefromedgewest",
887         action_movetoedge,
888         setup_action_movefromedge_west
889     },
890     {
891         "movefromedgeeast",
892         action_movetoedge,
893         setup_action_movefromedge_east
894     },
895     {
896         "movetoedgenorth",
897         action_movetoedge,
898         setup_action_movetoedge_north
899     },
900     {
901         "movetoedgesouth",
902         action_movetoedge,
903         setup_action_movetoedge_south
904     },
905     {
906         "movetoedgewest",
907         action_movetoedge,
908         setup_action_movetoedge_west
909     },
910     {
911         "movetoedgeeast",
912         action_movetoedge,
913         setup_action_movetoedge_east
914     },
915     {
916         "growtoedgenorth",
917         action_growtoedge,
918         setup_action_growtoedge_north
919     },
920     {
921         "growtoedgesouth",
922         action_growtoedge,
923         setup_action_growtoedge_south
924     },
925     {
926         "growtoedgewest",
927         action_growtoedge,
928         setup_action_growtoedge_west
929     },
930     {
931         "growtoedgeeast",
932         action_growtoedge,
933         setup_action_growtoedge_east
934     },
935     {
936         "breakchroot",
937         action_break_chroot,
938         NULL
939     },
940     {
941         "adddesktoplast",
942         action_add_desktop,
943         setup_action_addremove_desktop_last
944     },
945     {
946         "removedesktoplast",
947         action_remove_desktop,
948         setup_action_addremove_desktop_last
949     },
950     {
951         "adddesktopcurrent",
952         action_add_desktop,
953         setup_action_addremove_desktop_current
954     },
955     {
956         "removedesktopcurrent",
957         action_remove_desktop,
958         setup_action_addremove_desktop_current
959     },
960     {
961         NULL,
962         NULL,
963         NULL
964     }
965 };
966
967 /* only key bindings can be interactive. thus saith the xor.
968    because of how the mouse is grabbed, mouse events dont even get
969    read during interactive events, so no dice! >:) */
970 #define INTERACTIVE_LIMIT(a, uact) \
971     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
972         a->data.any.interactive = FALSE;
973
974 ObAction *action_from_string(const gchar *name, ObUserAction uact)
975 {
976     ObAction *a = NULL;
977     gboolean exist = FALSE;
978     gint i;
979
980     for (i = 0; actionstrings[i].name; i++)
981         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
982             exist = TRUE;
983             a = action_new(actionstrings[i].func);
984             if (actionstrings[i].setup)
985                 actionstrings[i].setup(&a, uact);
986             if (a)
987                 INTERACTIVE_LIMIT(a, uact);
988             break;
989         }
990     if (!exist)
991         g_message(_("Invalid action '%s' requested. No such action exists."),
992                   name);
993     if (!a)
994         g_message(_("Invalid use of action '%s'. Action will be ignored."),
995                   name);
996     return a;
997 }
998
999 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
1000                        ObUserAction uact)
1001 {
1002     gchar *actname;
1003     ObAction *act = NULL;
1004     xmlNodePtr n;
1005
1006     if (parse_attr_string("name", node, &actname)) {
1007         if ((act = action_from_string(actname, uact))) {
1008             if (act->func == action_execute || act->func == action_restart) {
1009                 if ((n = parse_find_node("execute", node->xmlChildrenNode))) {
1010                     gchar *s = parse_string(doc, n);
1011                     act->data.execute.path = parse_expand_tilde(s);
1012                     g_free(s);
1013                 }
1014                 if ((n = parse_find_node("startupnotify", node->xmlChildrenNode))) {
1015                     xmlNodePtr m;
1016                     if ((m = parse_find_node("enabled", n->xmlChildrenNode)))
1017                         act->data.execute.startupnotify = parse_bool(doc, m);
1018                     if ((m = parse_find_node("name", n->xmlChildrenNode)))
1019                         act->data.execute.name = parse_string(doc, m);
1020                     if ((m = parse_find_node("icon", n->xmlChildrenNode)))
1021                         act->data.execute.icon_name = parse_string(doc, m);
1022                 }
1023             } else if (act->func == action_debug) {
1024                 if ((n = parse_find_node("string", node->xmlChildrenNode)))
1025                     act->data.debug.string = parse_string(doc, n);
1026             } else if (act->func == action_showmenu) {
1027                 if ((n = parse_find_node("menu", node->xmlChildrenNode)))
1028                     act->data.showmenu.name = parse_string(doc, n);
1029             } else if (act->func == action_move_relative_horz ||
1030                        act->func == action_move_relative_vert ||
1031                        act->func == action_resize_relative_horz ||
1032                        act->func == action_resize_relative_vert) {
1033                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
1034                     act->data.relative.deltax = parse_int(doc, n);
1035             } else if (act->func == action_move_relative) {
1036                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
1037                     act->data.relative.deltax = parse_int(doc, n);
1038                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
1039                     act->data.relative.deltay = parse_int(doc, n);
1040             } else if (act->func == action_resize_relative) {
1041                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
1042                     act->data.relative.deltaxl = parse_int(doc, n);
1043                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
1044                     act->data.relative.deltayu = parse_int(doc, n);
1045                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
1046                     act->data.relative.deltax = parse_int(doc, n);
1047                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
1048                     act->data.relative.deltay = parse_int(doc, n);
1049             } else if (act->func == action_desktop) {
1050                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1051                     act->data.desktop.desk = parse_int(doc, n);
1052                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
1053 /*
1054                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1055                     act->data.desktop.inter.any.interactive =
1056                         parse_bool(doc, n);
1057 */
1058            } else if (act->func == action_send_to_desktop) {
1059                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1060                     act->data.sendto.desk = parse_int(doc, n);
1061                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
1062                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1063                     act->data.sendto.follow = parse_bool(doc, n);
1064             } else if (act->func == action_desktop_dir) {
1065                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1066                     act->data.desktopdir.wrap = parse_bool(doc, n); 
1067                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1068                     act->data.desktopdir.inter.any.interactive =
1069                         parse_bool(doc, n);
1070             } else if (act->func == action_send_to_desktop_dir) {
1071                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1072                     act->data.sendtodir.wrap = parse_bool(doc, n);
1073                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1074                     act->data.sendtodir.follow = parse_bool(doc, n);
1075                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1076                     act->data.sendtodir.inter.any.interactive =
1077                         parse_bool(doc, n);
1078             } else if (act->func == action_activate) {
1079                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
1080                     act->data.activate.here = parse_bool(doc, n);
1081             } else if (act->func == action_cycle_windows) {
1082                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
1083                     act->data.cycle.linear = parse_bool(doc, n);
1084                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1085                     act->data.cycle.dialog = parse_bool(doc, n);
1086                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1087                     act->data.cycle.dock_windows = parse_bool(doc, n);
1088                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1089                     act->data.cycle.desktop_windows = parse_bool(doc, n);
1090                 if ((n = parse_find_node("allDesktops",
1091                                          node->xmlChildrenNode)))
1092                     act->data.cycle.all_desktops = parse_bool(doc, n);
1093             } else if (act->func == action_directional_focus) {
1094                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1095                     act->data.interdiraction.dialog = parse_bool(doc, n);
1096                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1097                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
1098                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1099                     act->data.interdiraction.desktop_windows =
1100                         parse_bool(doc, n);
1101             } else if (act->func == action_resize) {
1102                 if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
1103                     gchar *s = parse_string(doc, n);
1104                     if (!g_ascii_strcasecmp(s, "top"))
1105                         act->data.moveresize.corner =
1106                             prop_atoms.net_wm_moveresize_size_top;
1107                     else if (!g_ascii_strcasecmp(s, "bottom"))
1108                         act->data.moveresize.corner =
1109                             prop_atoms.net_wm_moveresize_size_bottom;
1110                     else if (!g_ascii_strcasecmp(s, "left"))
1111                         act->data.moveresize.corner =
1112                             prop_atoms.net_wm_moveresize_size_left;
1113                     else if (!g_ascii_strcasecmp(s, "right"))
1114                         act->data.moveresize.corner =
1115                             prop_atoms.net_wm_moveresize_size_right;
1116                     else if (!g_ascii_strcasecmp(s, "topleft"))
1117                         act->data.moveresize.corner =
1118                             prop_atoms.net_wm_moveresize_size_topleft;
1119                     else if (!g_ascii_strcasecmp(s, "topright"))
1120                         act->data.moveresize.corner =
1121                             prop_atoms.net_wm_moveresize_size_topright;
1122                     else if (!g_ascii_strcasecmp(s, "bottomleft"))
1123                         act->data.moveresize.corner =
1124                             prop_atoms.net_wm_moveresize_size_bottomleft;
1125                     else if (!g_ascii_strcasecmp(s, "bottomright"))
1126                         act->data.moveresize.corner =
1127                             prop_atoms.net_wm_moveresize_size_bottomright;
1128                     g_free(s);
1129                 }
1130             } else if (act->func == action_raise ||
1131                        act->func == action_lower ||
1132                        act->func == action_raiselower ||
1133                        act->func == action_shadelower ||
1134                        act->func == action_unshaderaise) {
1135             }
1136             INTERACTIVE_LIMIT(act, uact);
1137         }
1138         g_free(actname);
1139     }
1140     return act;
1141 }
1142
1143 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
1144                      guint state, guint button, gint x, gint y, Time time,
1145                      gboolean cancel, gboolean done)
1146 {
1147     GSList *it;
1148     ObAction *a;
1149
1150     if (!acts)
1151         return;
1152
1153     if (x < 0 && y < 0)
1154         screen_pointer_pos(&x, &y);
1155
1156     for (it = acts; it; it = g_slist_next(it)) {
1157         a = it->data;
1158
1159         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
1160             a->data.any.c = a->data.any.client_action ? c : NULL;
1161             a->data.any.context = context;
1162             a->data.any.x = x;
1163             a->data.any.y = y;
1164
1165             a->data.any.button = button;
1166
1167             a->data.any.time = time;
1168
1169             if (a->data.any.interactive) {
1170                 a->data.inter.cancel = cancel;
1171                 a->data.inter.final = done;
1172                 if (!(cancel || done))
1173                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
1174                         continue;
1175             }
1176
1177             /* XXX UGLY HACK race with motion event starting a move and the
1178                button release gettnig processed first. answer: don't queue
1179                moveresize starts. UGLY HACK XXX
1180
1181                XXX ALSO don't queue showmenu events, because on button press
1182                events we need to know if a mouse grab is going to take place,
1183                and set the button to 0, so that later motion events don't think
1184                that a drag is going on. since showmenu grabs the pointer..
1185             */
1186             if (a->data.any.interactive || a->func == action_move ||
1187                 a->func == action_resize || a->func == action_showmenu)
1188             {
1189                 /* interactive actions are not queued */
1190                 a->func(&a->data);
1191             } else if (a->func == action_focus ||
1192                        a->func == action_activate ||
1193                        a->func == action_showmenu)
1194             {
1195                 /* XXX MORE UGLY HACK
1196                    actions from clicks on client windows are NOT queued.
1197                    this solves the mysterious click-and-drag-doesnt-work
1198                    problem. it was because the window gets focused and stuff
1199                    after the button event has already been passed through. i
1200                    dont really know why it should care but it does and it makes
1201                    a difference.
1202
1203                    however this very bogus ! !
1204                    we want to send the button press to the window BEFORE
1205                    we do the action because the action might move the windows
1206                    (eg change desktops) and then the button press ends up on
1207                    the completely wrong window !
1208                    so, this is just for that bug, and it will only NOT queue it
1209                    if it is a focusing action that can be used with the mouse
1210                    pointer. ugh.
1211
1212                    also with the menus, there is a race going on. if the
1213                    desktop wants to pop up a menu, and we do too, we send them
1214                    the button before we pop up the menu, so they pop up their
1215                    menu first. but not always. if we pop up our menu before
1216                    sending them the button press, then the result is
1217                    deterministic. yay.
1218
1219                    XXX further more. focus actions are not queued at all,
1220                    because if you bind focus->showmenu, the menu will get
1221                    hidden to do the focusing
1222                 */
1223                 a->func(&a->data);
1224             } else
1225                 ob_main_loop_queue_action(ob_main_loop, a);
1226         }
1227     }
1228 }
1229
1230 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1231 {
1232     ObAction *a;
1233     GSList *l;
1234
1235     a = action_from_string(name, OB_USER_ACTION_NONE);
1236     g_assert(a);
1237
1238     l = g_slist_append(NULL, a);
1239
1240     action_run(l, c, 0, time);
1241 }
1242
1243 void action_debug(union ActionData *data)
1244 {
1245     if (data->debug.string)
1246         g_print("%s\n", data->debug.string);
1247 }
1248
1249 void action_execute(union ActionData *data)
1250 {
1251     GError *e = NULL;
1252     gchar *cmd, **argv = 0;
1253     if (data->execute.path) {
1254         cmd = g_filename_from_utf8(data->execute.path, -1, NULL, NULL, NULL);
1255         if (cmd) {
1256             /* If there is a keyboard grab going on then we need to cancel
1257                it so the application can grab things */
1258             event_cancel_all_key_grabs();
1259
1260             if (!g_shell_parse_argv (cmd, NULL, &argv, &e)) {
1261                 g_message(_("Failed to execute '%s': %s"),
1262                           cmd, e->message);
1263                 g_error_free(e);
1264             } else if (data->execute.startupnotify) {
1265                 gchar *program;
1266                 
1267                 program = g_path_get_basename(argv[0]);
1268                 /* sets up the environment */
1269                 sn_setup_spawn_environment(program,
1270                                            data->execute.name,
1271                                            data->execute.icon_name,
1272                                            /* launch it on the current
1273                                               desktop */
1274                                            screen_desktop,
1275                                            data->execute.any.time);
1276                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1277                                    G_SPAWN_DO_NOT_REAP_CHILD,
1278                                    NULL, NULL, NULL, &e)) {
1279                     g_message(_("Failed to execute '%s': %s"),
1280                               cmd, e->message);
1281                     g_error_free(e);
1282                     sn_spawn_cancel();
1283                 }
1284                 unsetenv("DESKTOP_STARTUP_ID");
1285                 g_free(program);
1286                 g_strfreev(argv);
1287             } else {
1288                 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH |
1289                                    G_SPAWN_DO_NOT_REAP_CHILD,
1290                                    NULL, NULL, NULL, &e))
1291                 {
1292                     g_message(_("Failed to execute '%s': %s"),
1293                               cmd, e->message);
1294                     g_error_free(e);
1295                 }
1296                 g_strfreev(argv);
1297             }
1298             g_free(cmd);
1299         } else {
1300             g_message(_("Failed to convert the path '%s' from utf8"),
1301                       data->execute.path);
1302         }
1303     }
1304 }
1305
1306 void action_activate(union ActionData *data)
1307 {
1308     if (data->client.any.c) {
1309         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1310             (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
1311              data->any.context != OB_FRAME_CONTEXT_FRAME))
1312         {
1313             /* if using focus_delay, stop the timer now so that focus doesn't
1314                go moving on us */
1315             event_halt_focus_delay();
1316
1317             client_activate(data->activate.any.c, data->activate.here, TRUE);
1318         }
1319     } else {
1320         /* focus action on something other than a client, make keybindings
1321            work for this openbox instance, but don't focus any specific client
1322         */
1323         focus_nothing();
1324     }
1325 }
1326
1327 void action_focus(union ActionData *data)
1328 {
1329     if (data->client.any.c) {
1330         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1331             (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
1332              data->any.context != OB_FRAME_CONTEXT_FRAME))
1333         {
1334             /* if using focus_delay, stop the timer now so that focus doesn't
1335                go moving on us */
1336             event_halt_focus_delay();
1337
1338             client_focus(data->client.any.c);
1339         }
1340     } else {
1341         /* focus action on something other than a client, make keybindings
1342            work for this openbox instance, but don't focus any specific client
1343         */
1344         focus_nothing();
1345     }
1346 }
1347
1348 void action_unfocus (union ActionData *data)
1349 {
1350     if (data->client.any.c == focus_client)
1351         focus_fallback(FALSE, FALSE, TRUE);
1352 }
1353
1354 void action_iconify(union ActionData *data)
1355 {
1356     client_action_start(data);
1357     client_iconify(data->client.any.c, TRUE, TRUE, FALSE);
1358     client_action_end(data, config_focus_under_mouse);
1359 }
1360
1361 void action_focus_order_to_bottom(union ActionData *data)
1362 {
1363     focus_order_to_bottom(data->client.any.c);
1364 }
1365
1366 void action_raiselower(union ActionData *data)
1367 {
1368     ObClient *c = data->client.any.c;
1369
1370     client_action_start(data);
1371     stacking_restack_request(c, NULL, Opposite, FALSE);
1372     client_action_end(data, config_focus_under_mouse);
1373 }
1374
1375 void action_raise(union ActionData *data)
1376 {
1377     client_action_start(data);
1378     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1379     client_action_end(data, config_focus_under_mouse);
1380 }
1381
1382 void action_unshaderaise(union ActionData *data)
1383 {
1384     if (data->client.any.c->shaded)
1385         action_unshade(data);
1386     else
1387         action_raise(data);
1388 }
1389
1390 void action_shadelower(union ActionData *data)
1391 {
1392     if (data->client.any.c->shaded)
1393         action_lower(data);
1394     else
1395         action_shade(data);
1396 }
1397
1398 void action_lower(union ActionData *data)
1399 {
1400     client_action_start(data);
1401     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1402     client_action_end(data, config_focus_under_mouse);
1403 }
1404
1405 void action_close(union ActionData *data)
1406 {
1407     client_close(data->client.any.c);
1408 }
1409
1410 void action_kill(union ActionData *data)
1411 {
1412     client_kill(data->client.any.c);
1413 }
1414
1415 void action_shade(union ActionData *data)
1416 {
1417     client_action_start(data);
1418     client_shade(data->client.any.c, TRUE);
1419     client_action_end(data, config_focus_under_mouse);
1420 }
1421
1422 void action_unshade(union ActionData *data)
1423 {
1424     client_action_start(data);
1425     client_shade(data->client.any.c, FALSE);
1426     client_action_end(data, config_focus_under_mouse);
1427 }
1428
1429 void action_toggle_shade(union ActionData *data)
1430 {
1431     client_action_start(data);
1432     client_shade(data->client.any.c, !data->client.any.c->shaded);
1433     client_action_end(data, config_focus_under_mouse);
1434 }
1435
1436 void action_toggle_omnipresent(union ActionData *data)
1437
1438     client_set_desktop(data->client.any.c,
1439                        data->client.any.c->desktop == DESKTOP_ALL ?
1440                        screen_desktop : DESKTOP_ALL, FALSE, TRUE);
1441 }
1442
1443 void action_move_relative_horz(union ActionData *data)
1444 {
1445     ObClient *c = data->relative.any.c;
1446     client_action_start(data);
1447     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1448     client_action_end(data, FALSE);
1449 }
1450
1451 void action_move_relative_vert(union ActionData *data)
1452 {
1453     ObClient *c = data->relative.any.c;
1454     client_action_start(data);
1455     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1456     client_action_end(data, FALSE);
1457 }
1458
1459 void action_move_to_center(union ActionData *data)
1460 {
1461     ObClient *c = data->client.any.c;
1462     Rect *area;
1463     area = screen_area(c->desktop, client_monitor(c), NULL);
1464     client_action_start(data);
1465     client_move(c, area->width / 2 - c->area.width / 2,
1466                 area->height / 2 - c->area.height / 2);
1467     client_action_end(data, FALSE);
1468     g_free(area);
1469 }
1470
1471 void action_resize_relative_horz(union ActionData *data)
1472 {
1473     ObClient *c = data->relative.any.c;
1474     client_action_start(data);
1475     client_resize(c,
1476                   c->area.width + data->relative.deltax * c->size_inc.width,
1477                   c->area.height);
1478     client_action_end(data, FALSE);
1479 }
1480
1481 void action_resize_relative_vert(union ActionData *data)
1482 {
1483     ObClient *c = data->relative.any.c;
1484     if (!c->shaded) {
1485         client_action_start(data);
1486         client_resize(c, c->area.width, c->area.height +
1487                       data->relative.deltax * c->size_inc.height);
1488         client_action_end(data, FALSE);
1489     }
1490 }
1491
1492 void action_move_relative(union ActionData *data)
1493 {
1494     ObClient *c = data->relative.any.c;
1495     client_action_start(data);
1496     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1497                 data->relative.deltay);
1498     client_action_end(data, FALSE);
1499 }
1500
1501 void action_resize_relative(union ActionData *data)
1502 {
1503     ObClient *c = data->relative.any.c;
1504     gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
1505
1506     client_action_start(data);
1507
1508     x = c->area.x;
1509     y = c->area.y;
1510     ow = c->area.width;
1511     xoff = -data->relative.deltaxl * c->size_inc.width;
1512     nw = ow + data->relative.deltax * c->size_inc.width
1513         + data->relative.deltaxl * c->size_inc.width;
1514     oh = c->area.height;
1515     yoff = -data->relative.deltayu * c->size_inc.height;
1516     nh = oh + data->relative.deltay * c->size_inc.height
1517         + data->relative.deltayu * c->size_inc.height;
1518
1519     g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
1520             data->relative.deltax, 
1521             data->relative.deltaxl, 
1522             x, ow, xoff, nw);
1523     
1524     client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
1525     xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
1526     yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
1527     client_move_resize(c, x + xoff, y + yoff, nw, nh);
1528     client_action_end(data, FALSE);
1529 }
1530
1531 void action_maximize_full(union ActionData *data)
1532 {
1533     client_action_start(data);
1534     client_maximize(data->client.any.c, TRUE, 0);
1535     client_action_end(data, config_focus_under_mouse);
1536 }
1537
1538 void action_unmaximize_full(union ActionData *data)
1539 {
1540     client_action_start(data);
1541     client_maximize(data->client.any.c, FALSE, 0);
1542     client_action_end(data, config_focus_under_mouse);
1543 }
1544
1545 void action_toggle_maximize_full(union ActionData *data)
1546 {
1547     client_action_start(data);
1548     client_maximize(data->client.any.c,
1549                     !(data->client.any.c->max_horz ||
1550                       data->client.any.c->max_vert),
1551                     0);
1552     client_action_end(data, config_focus_under_mouse);
1553 }
1554
1555 void action_maximize_horz(union ActionData *data)
1556 {
1557     client_action_start(data);
1558     client_maximize(data->client.any.c, TRUE, 1);
1559     client_action_end(data, config_focus_under_mouse);
1560 }
1561
1562 void action_unmaximize_horz(union ActionData *data)
1563 {
1564     client_action_start(data);
1565     client_maximize(data->client.any.c, FALSE, 1);
1566     client_action_end(data, config_focus_under_mouse);
1567 }
1568
1569 void action_toggle_maximize_horz(union ActionData *data)
1570 {
1571     client_action_start(data);
1572     client_maximize(data->client.any.c,
1573                     !data->client.any.c->max_horz, 1);
1574     client_action_end(data, config_focus_under_mouse);
1575 }
1576
1577 void action_maximize_vert(union ActionData *data)
1578 {
1579     client_action_start(data);
1580     client_maximize(data->client.any.c, TRUE, 2);
1581     client_action_end(data, config_focus_under_mouse);
1582 }
1583
1584 void action_unmaximize_vert(union ActionData *data)
1585 {
1586     client_action_start(data);
1587     client_maximize(data->client.any.c, FALSE, 2);
1588     client_action_end(data, config_focus_under_mouse);
1589 }
1590
1591 void action_toggle_maximize_vert(union ActionData *data)
1592 {
1593     client_action_start(data);
1594     client_maximize(data->client.any.c,
1595                     !data->client.any.c->max_vert, 2);
1596     client_action_end(data, config_focus_under_mouse);
1597 }
1598
1599 void action_toggle_fullscreen(union ActionData *data)
1600 {
1601     client_action_start(data);
1602     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1603     client_action_end(data, config_focus_under_mouse);
1604 }
1605
1606 void action_send_to_desktop(union ActionData *data)
1607 {
1608     ObClient *c = data->sendto.any.c;
1609
1610     if (!client_normal(c)) return;
1611
1612     if (data->sendto.desk < screen_num_desktops ||
1613         data->sendto.desk == DESKTOP_ALL) {
1614         client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
1615         if (data->sendto.follow && data->sendto.desk != screen_desktop)
1616             screen_set_desktop(data->sendto.desk, TRUE);
1617     }
1618 }
1619
1620 void action_desktop(union ActionData *data)
1621 {
1622     /* XXX add the interactive/dialog option back again once the dialog
1623        has been made to not use grabs */
1624     if (data->desktop.desk < screen_num_desktops ||
1625         data->desktop.desk == DESKTOP_ALL)
1626     {
1627         screen_set_desktop(data->desktop.desk, TRUE);
1628         if (data->inter.any.interactive)
1629             screen_desktop_popup(data->desktop.desk, TRUE);
1630     }
1631 }
1632
1633 void action_desktop_dir(union ActionData *data)
1634 {
1635     guint d;
1636
1637     d = screen_cycle_desktop(data->desktopdir.dir,
1638                              data->desktopdir.wrap,
1639                              data->desktopdir.linear,
1640                              data->desktopdir.inter.any.interactive,
1641                              data->desktopdir.inter.final,
1642                              data->desktopdir.inter.cancel);
1643     /* only move the desktop when the action is complete. if we switch
1644        desktops during the interactive action, focus will move but with
1645        NotifyWhileGrabbed and applications don't like that. */
1646     if (!data->sendtodir.inter.any.interactive ||
1647         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1648     {
1649         if (d != screen_desktop)
1650             screen_set_desktop(d, TRUE);
1651     }
1652 }
1653
1654 void action_send_to_desktop_dir(union ActionData *data)
1655 {
1656     ObClient *c = data->sendtodir.inter.any.c;
1657     guint d;
1658
1659     if (!client_normal(c)) return;
1660
1661     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1662                              data->sendtodir.linear,
1663                              data->sendtodir.inter.any.interactive,
1664                              data->sendtodir.inter.final,
1665                              data->sendtodir.inter.cancel);
1666     /* only move the desktop when the action is complete. if we switch
1667        desktops during the interactive action, focus will move but with
1668        NotifyWhileGrabbed and applications don't like that. */
1669     if (!data->sendtodir.inter.any.interactive ||
1670         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1671     {
1672         client_set_desktop(c, d, data->sendtodir.follow, FALSE);
1673         if (data->sendtodir.follow && d != screen_desktop)
1674             screen_set_desktop(d, TRUE);
1675     }
1676 }
1677
1678 void action_desktop_last(union ActionData *data)
1679 {
1680     if (screen_last_desktop < screen_num_desktops)
1681         screen_set_desktop(screen_last_desktop, TRUE);
1682 }
1683
1684 void action_toggle_decorations(union ActionData *data)
1685 {
1686     ObClient *c = data->client.any.c;
1687
1688     client_action_start(data);
1689     client_set_undecorated(c, !c->undecorated);
1690     client_action_end(data, FALSE);
1691 }
1692
1693 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1694                            gboolean shaded)
1695 {
1696     /* let's make x and y client relative instead of screen relative */
1697     x = x - cx;
1698     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1699
1700 #define X x*ch/cw
1701 #define A -4*X + 7*ch/3
1702 #define B  4*X -15*ch/9
1703 #define C -X/4 + 2*ch/3
1704 #define D  X/4 + 5*ch/12
1705 #define E  X/4 +   ch/3
1706 #define F -X/4 + 7*ch/12
1707 #define G  4*X - 4*ch/3
1708 #define H -4*X + 8*ch/3
1709 #define a (y > 5*ch/9)
1710 #define b (x < 4*cw/9)
1711 #define c (x > 5*cw/9)
1712 #define d (y < 4*ch/9)
1713
1714     /*
1715       Each of these defines (except X which is just there for fun), represents
1716       the equation of a line. The lines they represent are shown in the diagram
1717       below. Checking y against these lines, we are able to choose a region
1718       of the window as shown.
1719
1720       +---------------------A-------|-------|-------B---------------------+
1721       |                     |A                     B|                     |
1722       |                     |A      |       |      B|                     |
1723       |                     | A                   B |                     |
1724       |                     | A     |       |     B |                     |
1725       |                     |  A                 B  |                     |
1726       |                     |  A    |       |    B  |                     |
1727       |        northwest    |   A     north     B   |   northeast         |
1728       |                     |   A   |       |   B   |                     |
1729       |                     |    A             B    |                     |
1730       C---------------------+----A--+-------+--B----+---------------------D
1731       |CCCCCCC              |     A           B     |              DDDDDDD|
1732       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1733       |               CCCCCCC      A         B      DDDDDDD               |
1734       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1735       |                     |       b       c       |                     | sh
1736       |             west    |       b  move c       |   east              | ad
1737       |                     |       b       c       |                     | ed
1738       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1739       |               EEEEEEE      G         H      FFFFFFF               |
1740       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1741       |EEEEEEE              |     G           H     |              FFFFFFF|
1742       E---------------------+----G--+-------+--H----+---------------------F
1743       |                     |    G             H    |                     |
1744       |                     |   G   |       |   H   |                     |
1745       |        southwest    |   G     south     H   |   southeast         |
1746       |                     |  G    |       |    H  |                     |
1747       |                     |  G                 H  |                     |
1748       |                     | G     |       |     H |                     |
1749       |                     | G                   H |                     |
1750       |                     |G      |       |      H|                     |
1751       |                     |G                     H|                     |
1752       +---------------------G-------|-------|-------H---------------------+
1753     */
1754
1755     if (shaded) {
1756         /* for shaded windows, you can only resize west/east and move */
1757         if (b)
1758             return prop_atoms.net_wm_moveresize_size_left;
1759         if (c)
1760             return prop_atoms.net_wm_moveresize_size_right;
1761         return prop_atoms.net_wm_moveresize_move;
1762     }
1763
1764     if (y < A && y >= C)
1765         return prop_atoms.net_wm_moveresize_size_topleft;
1766     else if (y >= A && y >= B && a)
1767         return prop_atoms.net_wm_moveresize_size_top;
1768     else if (y < B && y >= D)
1769         return prop_atoms.net_wm_moveresize_size_topright;
1770     else if (y < C && y >= E && b)
1771         return prop_atoms.net_wm_moveresize_size_left;
1772     else if (y < D && y >= F && c)
1773         return prop_atoms.net_wm_moveresize_size_right;
1774     else if (y < E && y >= G)
1775         return prop_atoms.net_wm_moveresize_size_bottomleft;
1776     else if (y < G && y < H && d)
1777         return prop_atoms.net_wm_moveresize_size_bottom;
1778     else if (y >= H && y < F)
1779         return prop_atoms.net_wm_moveresize_size_bottomright;
1780     else
1781         return prop_atoms.net_wm_moveresize_move;
1782
1783 #undef X
1784 #undef A
1785 #undef B
1786 #undef C
1787 #undef D
1788 #undef E
1789 #undef F
1790 #undef G
1791 #undef H
1792 #undef a
1793 #undef b
1794 #undef c
1795 #undef d
1796 }
1797
1798 void action_move(union ActionData *data)
1799 {
1800     ObClient *c = data->moveresize.any.c;
1801     guint32 corner;
1802
1803     if (data->moveresize.keyboard)
1804         corner = prop_atoms.net_wm_moveresize_move_keyboard;
1805     else
1806         corner = prop_atoms.net_wm_moveresize_move;
1807
1808     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1809 }
1810
1811 void action_resize(union ActionData *data)
1812 {
1813     ObClient *c = data->moveresize.any.c;
1814     guint32 corner;
1815
1816     if (data->moveresize.keyboard)
1817         corner = prop_atoms.net_wm_moveresize_size_keyboard;
1818     else if (data->moveresize.corner)
1819         corner = data->moveresize.corner; /* it was specified in the binding */
1820     else
1821         corner = pick_corner(data->any.x, data->any.y,
1822                              c->frame->area.x, c->frame->area.y,
1823                              /* use the client size because the frame
1824                                 can be differently sized (shaded
1825                                 windows) and we want this based on the
1826                                 clients size */
1827                              c->area.width + c->frame->size.left +
1828                              c->frame->size.right,
1829                              c->area.height + c->frame->size.top +
1830                              c->frame->size.bottom, c->shaded);
1831
1832     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1833 }
1834
1835 void action_reconfigure(union ActionData *data)
1836 {
1837     ob_reconfigure();
1838 }
1839
1840 void action_restart(union ActionData *data)
1841 {
1842     ob_restart_other(data->execute.path);
1843 }
1844
1845 void action_exit(union ActionData *data)
1846 {
1847     ob_exit(0);
1848 }
1849
1850 void action_showmenu(union ActionData *data)
1851 {
1852     if (data->showmenu.name) {
1853         menu_show(data->showmenu.name, data->any.x, data->any.y,
1854                   data->any.button, data->showmenu.any.c);
1855     }
1856 }
1857
1858 void action_cycle_windows(union ActionData *data)
1859 {
1860     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1861        on us */
1862     event_halt_focus_delay();
1863
1864     focus_cycle(data->cycle.forward,
1865                 data->cycle.all_desktops,
1866                 data->cycle.dock_windows,
1867                 data->cycle.desktop_windows,
1868                 data->cycle.linear, data->any.interactive,
1869                 data->cycle.dialog,
1870                 data->cycle.inter.final, data->cycle.inter.cancel);
1871 }
1872
1873 void action_directional_focus(union ActionData *data)
1874 {
1875     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1876        on us */
1877     event_halt_focus_delay();
1878
1879     focus_directional_cycle(data->interdiraction.direction,
1880                             data->interdiraction.dock_windows,
1881                             data->interdiraction.desktop_windows,
1882                             data->any.interactive,
1883                             data->interdiraction.dialog,
1884                             data->interdiraction.inter.final,
1885                             data->interdiraction.inter.cancel);
1886 }
1887
1888 void action_movetoedge(union ActionData *data)
1889 {
1890     gint x, y;
1891     ObClient *c = data->diraction.any.c;
1892
1893     x = c->frame->area.x;
1894     y = c->frame->area.y;
1895     
1896     switch(data->diraction.direction) {
1897     case OB_DIRECTION_NORTH:
1898         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1899                                            data->diraction.hang)
1900             - (data->diraction.hang ? c->frame->area.height : 0);
1901         break;
1902     case OB_DIRECTION_WEST:
1903         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1904                                            data->diraction.hang)
1905             - (data->diraction.hang ? c->frame->area.width : 0);
1906         break;
1907     case OB_DIRECTION_SOUTH:
1908         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1909                                            data->diraction.hang)
1910             - (data->diraction.hang ? 0 : c->frame->area.height);
1911         break;
1912     case OB_DIRECTION_EAST:
1913         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1914                                            data->diraction.hang)
1915             - (data->diraction.hang ? 0 : c->frame->area.width);
1916         break;
1917     default:
1918         g_assert_not_reached();
1919     }
1920     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1921     client_action_start(data);
1922     client_move(c, x, y);
1923     client_action_end(data, FALSE);
1924 }
1925
1926 void action_growtoedge(union ActionData *data)
1927 {
1928     gint x, y, width, height, dest;
1929     ObClient *c = data->diraction.any.c;
1930     Rect *a;
1931
1932     a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
1933     x = c->frame->area.x;
1934     y = c->frame->area.y;
1935     /* get the unshaded frame's dimensions..if it is shaded */
1936     width = c->area.width + c->frame->size.left + c->frame->size.right;
1937     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1938
1939     switch(data->diraction.direction) {
1940     case OB_DIRECTION_NORTH:
1941         if (c->shaded) break; /* don't allow vertical resize if shaded */
1942
1943         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1944         if (a->y == y)
1945             height = height / 2;
1946         else {
1947             height = c->frame->area.y + height - dest;
1948             y = dest;
1949         }
1950         break;
1951     case OB_DIRECTION_WEST:
1952         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1953         if (a->x == x)
1954             width = width / 2;
1955         else {
1956             width = c->frame->area.x + width - dest;
1957             x = dest;
1958         }
1959         break;
1960     case OB_DIRECTION_SOUTH:
1961         if (c->shaded) break; /* don't allow vertical resize if shaded */
1962
1963         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1964         if (a->y + a->height == y + c->frame->area.height) {
1965             height = c->frame->area.height / 2;
1966             y = a->y + a->height - height;
1967         } else
1968             height = dest - c->frame->area.y;
1969         y += (height - c->frame->area.height) % c->size_inc.height;
1970         height -= (height - c->frame->area.height) % c->size_inc.height;
1971         break;
1972     case OB_DIRECTION_EAST:
1973         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1974         if (a->x + a->width == x + c->frame->area.width) {
1975             width = c->frame->area.width / 2;
1976             x = a->x + a->width - width;
1977         } else
1978             width = dest - c->frame->area.x;
1979         x += (width - c->frame->area.width) % c->size_inc.width;
1980         width -= (width - c->frame->area.width) % c->size_inc.width;
1981         break;
1982     default:
1983         g_assert_not_reached();
1984     }
1985     width -= c->frame->size.left + c->frame->size.right;
1986     height -= c->frame->size.top + c->frame->size.bottom;
1987     frame_frame_gravity(c->frame, &x, &y, width, height);
1988     client_action_start(data);
1989     client_move_resize(c, x, y, width, height);
1990     client_action_end(data, FALSE);
1991     g_free(a);
1992 }
1993
1994 void action_send_to_layer(union ActionData *data)
1995 {
1996     client_set_layer(data->layer.any.c, data->layer.layer);
1997 }
1998
1999 void action_toggle_layer(union ActionData *data)
2000 {
2001     ObClient *c = data->layer.any.c;
2002
2003     client_action_start(data);
2004     if (data->layer.layer < 0)
2005         client_set_layer(c, c->below ? 0 : -1);
2006     else if (data->layer.layer > 0)
2007         client_set_layer(c, c->above ? 0 : 1);
2008     client_action_end(data, config_focus_under_mouse);
2009 }
2010
2011 void action_toggle_dockautohide(union ActionData *data)
2012 {
2013     config_dock_hide = !config_dock_hide;
2014     dock_configure();
2015 }
2016
2017 void action_toggle_show_desktop(union ActionData *data)
2018 {
2019     screen_show_desktop(!screen_showing_desktop, NULL);
2020 }
2021
2022 void action_show_desktop(union ActionData *data)
2023 {
2024     screen_show_desktop(TRUE, NULL);
2025 }
2026
2027 void action_unshow_desktop(union ActionData *data)
2028 {
2029     screen_show_desktop(FALSE, NULL);
2030 }
2031
2032 void action_break_chroot(union ActionData *data)
2033 {
2034     /* break out of one chroot */
2035     keyboard_reset_chains(1);
2036 }
2037
2038 void action_add_desktop(union ActionData *data)
2039 {
2040     client_action_start(data);
2041     screen_set_num_desktops(screen_num_desktops+1);
2042
2043     /* move all the clients over */
2044     if (data->addremovedesktop.current) {
2045         GList *it;
2046
2047         for (it = client_list; it; it = g_list_next(it)) {
2048             ObClient *c = it->data;
2049             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
2050                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
2051         }
2052     }
2053
2054     client_action_end(data, config_focus_under_mouse);
2055 }
2056
2057 void action_remove_desktop(union ActionData *data)
2058 {
2059     if (screen_num_desktops < 2) return;
2060
2061     client_action_start(data);
2062
2063     /* move all the clients over */
2064     if (data->addremovedesktop.current) {
2065         GList *it, *stacking_copy;
2066
2067         /* make a copy of the list cuz we're changing it */
2068         stacking_copy = g_list_copy(stacking_list);
2069         for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
2070             if (WINDOW_IS_CLIENT(it->data)) {
2071                 ObClient *c = it->data;
2072                 if (c->desktop != DESKTOP_ALL && c->desktop > screen_desktop)
2073                     client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
2074                 /* raise all the windows that are on the current desktop which
2075                    is being merged */
2076                 else if (c->desktop == DESKTOP_ALL ||
2077                          c->desktop == screen_desktop)
2078                     stacking_raise(CLIENT_AS_WINDOW(c));
2079             }
2080         }
2081     }
2082
2083     /* act like we're changing desktops */
2084     if (screen_desktop < screen_num_desktops - 1) {
2085         gint d = screen_desktop;
2086         screen_desktop = screen_last_desktop;
2087         screen_set_desktop(d, TRUE);
2088         ob_debug("fake desktop change\n");
2089     }
2090
2091     screen_set_num_desktops(screen_num_desktops-1);
2092
2093     client_action_end(data, config_focus_under_mouse);
2094 }