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