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