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