74a15620e06f8ef933e1a5a17f6af06671b8af32
[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         "reconfigure",
785         action_reconfigure,
786         NULL
787     },
788     {
789         "restart",
790         action_restart,
791         NULL
792     },
793     {
794         "exit",
795         action_exit,
796         NULL
797     },
798     {
799         "sendtotoplayer",
800         action_send_to_layer,
801         setup_action_top_layer
802     },
803     {
804         "togglealwaysontop",
805         action_toggle_layer,
806         setup_action_top_layer
807     },
808     {
809         "sendtonormallayer",
810         action_send_to_layer,
811         setup_action_normal_layer
812     },
813     {
814         "sendtobottomlayer",
815         action_send_to_layer,
816         setup_action_bottom_layer
817     },
818     {
819         "togglealwaysonbottom",
820         action_toggle_layer,
821         setup_action_bottom_layer
822     },
823     {
824         "nextwindow",
825         action_cycle_windows,
826         setup_action_cycle_windows_next
827     },
828     {
829         "previouswindow",
830         action_cycle_windows,
831         setup_action_cycle_windows_previous
832     },
833     {
834         "movefromedgenorth",
835         action_movetoedge,
836         setup_action_movefromedge_north
837     },
838     {
839         "movefromedgesouth",
840         action_movetoedge,
841         setup_action_movefromedge_south
842     },
843     {
844         "movefromedgewest",
845         action_movetoedge,
846         setup_action_movefromedge_west
847     },
848     {
849         "movefromedgeeast",
850         action_movetoedge,
851         setup_action_movefromedge_east
852     },
853     {
854         "movetoedgenorth",
855         action_movetoedge,
856         setup_action_movetoedge_north
857     },
858     {
859         "movetoedgesouth",
860         action_movetoedge,
861         setup_action_movetoedge_south
862     },
863     {
864         "movetoedgewest",
865         action_movetoedge,
866         setup_action_movetoedge_west
867     },
868     {
869         "movetoedgeeast",
870         action_movetoedge,
871         setup_action_movetoedge_east
872     },
873     {
874         "growtoedgenorth",
875         action_growtoedge,
876         setup_action_growtoedge_north
877     },
878     {
879         "growtoedgesouth",
880         action_growtoedge,
881         setup_action_growtoedge_south
882     },
883     {
884         "growtoedgewest",
885         action_growtoedge,
886         setup_action_growtoedge_west
887     },
888     {
889         "growtoedgeeast",
890         action_growtoedge,
891         setup_action_growtoedge_east
892     },
893     {
894         "breakchroot",
895         action_break_chroot,
896         NULL
897     },
898     {
899         "adddesktoplast",
900         action_add_desktop,
901         setup_action_addremove_desktop_last
902     },
903     {
904         "removedesktoplast",
905         action_remove_desktop,
906         setup_action_addremove_desktop_last
907     },
908     {
909         "adddesktopcurrent",
910         action_add_desktop,
911         setup_action_addremove_desktop_current
912     },
913     {
914         "removedesktopcurrent",
915         action_remove_desktop,
916         setup_action_addremove_desktop_current
917     },
918     {
919         NULL,
920         NULL,
921         NULL
922     }
923 };
924
925 /* only key bindings can be interactive. thus saith the xor.
926    because of how the mouse is grabbed, mouse events dont even get
927    read during interactive events, so no dice! >:) */
928 #define INTERACTIVE_LIMIT(a, uact) \
929     if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
930         a->data.any.interactive = FALSE;
931
932 ObAction *action_from_string(const gchar *name, ObUserAction uact)
933 {
934     ObAction *a = NULL;
935     gboolean exist = FALSE;
936     gint i;
937
938     for (i = 0; actionstrings[i].name; i++)
939         if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
940             exist = TRUE;
941             a = action_new(actionstrings[i].func);
942             if (actionstrings[i].setup)
943                 actionstrings[i].setup(&a, uact);
944             if (a)
945                 INTERACTIVE_LIMIT(a, uact);
946             break;
947         }
948     if (!exist)
949         g_message(_("Invalid action '%s' requested. No such action exists."),
950                   name);
951     if (!a)
952         g_message(_("Invalid use of action '%s'. Action will be ignored."),
953                   name);
954     return a;
955 }
956
957 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
958                        ObUserAction uact)
959 {
960     gchar *actname;
961     ObAction *act = NULL;
962     xmlNodePtr n;
963
964     if (parse_attr_string("name", node, &actname)) {
965         if ((act = action_from_string(actname, uact))) {
966             } else if (act->func == action_move_relative_horz ||
967                        act->func == action_move_relative_vert ||
968                        act->func == action_resize_relative_horz ||
969                        act->func == action_resize_relative_vert) {
970                 if ((n = parse_find_node("delta", node->xmlChildrenNode)))
971                     act->data.relative.deltax = parse_int(doc, n);
972             } else if (act->func == action_move_relative) {
973                 if ((n = parse_find_node("x", node->xmlChildrenNode)))
974                     act->data.relative.deltax = parse_int(doc, n);
975                 if ((n = parse_find_node("y", node->xmlChildrenNode)))
976                     act->data.relative.deltay = parse_int(doc, n);
977             } else if (act->func == action_resize_relative) {
978                 if ((n = parse_find_node("left", node->xmlChildrenNode)))
979                     act->data.relative.deltaxl = parse_int(doc, n);
980                 if ((n = parse_find_node("up", node->xmlChildrenNode)))
981                     act->data.relative.deltayu = parse_int(doc, n);
982                 if ((n = parse_find_node("right", node->xmlChildrenNode)))
983                     act->data.relative.deltax = parse_int(doc, n);
984                 if ((n = parse_find_node("down", node->xmlChildrenNode)))
985                     act->data.relative.deltay = parse_int(doc, n);
986             } else if (act->func == action_desktop) {
987                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
988                     act->data.desktop.desk = parse_int(doc, n);
989                 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
990 /*
991                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
992                     act->data.desktop.inter.any.interactive =
993                         parse_bool(doc, n);
994 */
995            } else if (act->func == action_send_to_desktop) {
996                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
997                     act->data.sendto.desk = parse_int(doc, n);
998                 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
999                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1000                     act->data.sendto.follow = parse_bool(doc, n);
1001             } else if (act->func == action_desktop_dir) {
1002                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1003                     act->data.desktopdir.wrap = parse_bool(doc, n); 
1004                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1005                     act->data.desktopdir.inter.any.interactive =
1006                         parse_bool(doc, n);
1007             } else if (act->func == action_send_to_desktop_dir) {
1008                 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
1009                     act->data.sendtodir.wrap = parse_bool(doc, n);
1010                 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
1011                     act->data.sendtodir.follow = parse_bool(doc, n);
1012                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1013                     act->data.sendtodir.inter.any.interactive =
1014                         parse_bool(doc, n);
1015             } else if (act->func == action_activate) {
1016                 if ((n = parse_find_node("here", node->xmlChildrenNode)))
1017                     act->data.activate.here = parse_bool(doc, n);
1018             } else if (act->func == action_cycle_windows) {
1019                 if ((n = parse_find_node("linear", node->xmlChildrenNode)))
1020                     act->data.cycle.linear = parse_bool(doc, n);
1021                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1022                     act->data.cycle.dialog = parse_bool(doc, n);
1023                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1024                     act->data.cycle.dock_windows = parse_bool(doc, n);
1025                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1026                     act->data.cycle.desktop_windows = parse_bool(doc, n);
1027                 if ((n = parse_find_node("allDesktops",
1028                                          node->xmlChildrenNode)))
1029                     act->data.cycle.all_desktops = parse_bool(doc, n);
1030             } else if (act->func == action_directional_focus) {
1031                 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
1032                     act->data.interdiraction.dialog = parse_bool(doc, n);
1033                 if ((n = parse_find_node("panels", node->xmlChildrenNode)))
1034                     act->data.interdiraction.dock_windows = parse_bool(doc, n);
1035                 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
1036                     act->data.interdiraction.desktop_windows =
1037                         parse_bool(doc, n);
1038             } else if (act->func == action_resize) {
1039                 if ((n = parse_find_node("edge", node->xmlChildrenNode))) {
1040                     gchar *s = parse_string(doc, n);
1041                     if (!g_ascii_strcasecmp(s, "top"))
1042                         act->data.moveresize.corner =
1043                             prop_atoms.net_wm_moveresize_size_top;
1044                     else if (!g_ascii_strcasecmp(s, "bottom"))
1045                         act->data.moveresize.corner =
1046                             prop_atoms.net_wm_moveresize_size_bottom;
1047                     else if (!g_ascii_strcasecmp(s, "left"))
1048                         act->data.moveresize.corner =
1049                             prop_atoms.net_wm_moveresize_size_left;
1050                     else if (!g_ascii_strcasecmp(s, "right"))
1051                         act->data.moveresize.corner =
1052                             prop_atoms.net_wm_moveresize_size_right;
1053                     else if (!g_ascii_strcasecmp(s, "topleft"))
1054                         act->data.moveresize.corner =
1055                             prop_atoms.net_wm_moveresize_size_topleft;
1056                     else if (!g_ascii_strcasecmp(s, "topright"))
1057                         act->data.moveresize.corner =
1058                             prop_atoms.net_wm_moveresize_size_topright;
1059                     else if (!g_ascii_strcasecmp(s, "bottomleft"))
1060                         act->data.moveresize.corner =
1061                             prop_atoms.net_wm_moveresize_size_bottomleft;
1062                     else if (!g_ascii_strcasecmp(s, "bottomright"))
1063                         act->data.moveresize.corner =
1064                             prop_atoms.net_wm_moveresize_size_bottomright;
1065                     g_free(s);
1066                 }
1067             } else if (act->func == action_raise ||
1068                        act->func == action_lower ||
1069                        act->func == action_raiselower ||
1070                        act->func == action_shadelower ||
1071                        act->func == action_unshaderaise) {
1072             }
1073             INTERACTIVE_LIMIT(act, uact);
1074         }
1075         g_free(actname);
1076     }
1077     return act;
1078 }
1079
1080 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
1081                      guint state, guint button, gint x, gint y, Time time,
1082                      gboolean cancel, gboolean done)
1083 {
1084     GSList *it;
1085     ObAction *a;
1086
1087     if (!acts)
1088         return;
1089
1090     if (x < 0 && y < 0)
1091         screen_pointer_pos(&x, &y);
1092
1093     for (it = acts; it; it = g_slist_next(it)) {
1094         a = it->data;
1095
1096         if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
1097             a->data.any.c = a->data.any.client_action ? c : NULL;
1098             a->data.any.context = context;
1099             a->data.any.x = x;
1100             a->data.any.y = y;
1101
1102             a->data.any.button = button;
1103
1104             a->data.any.time = time;
1105
1106             if (a->data.any.interactive) {
1107                 a->data.inter.cancel = cancel;
1108                 a->data.inter.final = done;
1109                 if (!(cancel || done))
1110                     if (!keyboard_interactive_grab(state, a->data.any.c, a))
1111                         continue;
1112             }
1113
1114             /* XXX UGLY HACK race with motion event starting a move and the
1115                button release gettnig processed first. answer: don't queue
1116                moveresize starts. UGLY HACK XXX
1117
1118                XXX ALSO don't queue showmenu events, because on button press
1119                events we need to know if a mouse grab is going to take place,
1120                and set the button to 0, so that later motion events don't think
1121                that a drag is going on. since showmenu grabs the pointer..
1122             */
1123             if (a->data.any.interactive || a->func == action_move ||
1124                 a->func == action_resize || a->func == action_showmenu)
1125             {
1126                 /* interactive actions are not queued */
1127                 a->func(&a->data);
1128             } else if (a->func == action_focus ||
1129                        a->func == action_activate ||
1130                        a->func == action_showmenu)
1131             {
1132                 /* XXX MORE UGLY HACK
1133                    actions from clicks on client windows are NOT queued.
1134                    this solves the mysterious click-and-drag-doesnt-work
1135                    problem. it was because the window gets focused and stuff
1136                    after the button event has already been passed through. i
1137                    dont really know why it should care but it does and it makes
1138                    a difference.
1139
1140                    however this very bogus ! !
1141                    we want to send the button press to the window BEFORE
1142                    we do the action because the action might move the windows
1143                    (eg change desktops) and then the button press ends up on
1144                    the completely wrong window !
1145                    so, this is just for that bug, and it will only NOT queue it
1146                    if it is a focusing action that can be used with the mouse
1147                    pointer. ugh.
1148
1149                    also with the menus, there is a race going on. if the
1150                    desktop wants to pop up a menu, and we do too, we send them
1151                    the button before we pop up the menu, so they pop up their
1152                    menu first. but not always. if we pop up our menu before
1153                    sending them the button press, then the result is
1154                    deterministic. yay.
1155
1156                    XXX further more. focus actions are not queued at all,
1157                    because if you bind focus->showmenu, the menu will get
1158                    hidden to do the focusing
1159                 */
1160                 a->func(&a->data);
1161             } else
1162                 ob_main_loop_queue_action(ob_main_loop, a);
1163         }
1164     }
1165 }
1166
1167 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
1168 {
1169     ObAction *a;
1170     GSList *l;
1171
1172     a = action_from_string(name, OB_USER_ACTION_NONE);
1173     g_assert(a);
1174
1175     l = g_slist_append(NULL, a);
1176
1177     action_run(l, c, 0, time);
1178 }
1179
1180 void action_activate(union ActionData *data)
1181 {
1182     if (data->client.any.c) {
1183         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1184             (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
1185              data->any.context != OB_FRAME_CONTEXT_FRAME))
1186         {
1187             /* if using focus_delay, stop the timer now so that focus doesn't
1188                go moving on us */
1189             event_halt_focus_delay();
1190
1191             client_activate(data->activate.any.c, data->activate.here, TRUE);
1192         }
1193     } else {
1194         /* focus action on something other than a client, make keybindings
1195            work for this openbox instance, but don't focus any specific client
1196         */
1197         focus_nothing();
1198     }
1199 }
1200
1201 void action_focus(union ActionData *data)
1202 {
1203     if (data->client.any.c) {
1204         if (!data->any.button || client_mouse_focusable(data->client.any.c) ||
1205             (data->any.context != OB_FRAME_CONTEXT_CLIENT &&
1206              data->any.context != OB_FRAME_CONTEXT_FRAME))
1207         {
1208             /* if using focus_delay, stop the timer now so that focus doesn't
1209                go moving on us */
1210             event_halt_focus_delay();
1211
1212             client_focus(data->client.any.c);
1213         }
1214     } else {
1215         /* focus action on something other than a client, make keybindings
1216            work for this openbox instance, but don't focus any specific client
1217         */
1218         focus_nothing();
1219     }
1220 }
1221
1222 void action_unfocus (union ActionData *data)
1223 {
1224     if (data->client.any.c == focus_client)
1225         focus_fallback(FALSE, FALSE, TRUE);
1226 }
1227
1228 void action_iconify(union ActionData *data)
1229 {
1230     client_action_start(data);
1231     client_iconify(data->client.any.c, TRUE, TRUE, FALSE);
1232     client_action_end(data, config_focus_under_mouse);
1233 }
1234
1235 void action_focus_order_to_bottom(union ActionData *data)
1236 {
1237     focus_order_to_bottom(data->client.any.c);
1238 }
1239
1240 void action_raiselower(union ActionData *data)
1241 {
1242     ObClient *c = data->client.any.c;
1243
1244     client_action_start(data);
1245     stacking_restack_request(c, NULL, Opposite);
1246     client_action_end(data, config_focus_under_mouse);
1247 }
1248
1249 void action_raise(union ActionData *data)
1250 {
1251     client_action_start(data);
1252     stacking_raise(CLIENT_AS_WINDOW(data->client.any.c));
1253     client_action_end(data, config_focus_under_mouse);
1254 }
1255
1256 void action_unshaderaise(union ActionData *data)
1257 {
1258     if (data->client.any.c->shaded)
1259         action_unshade(data);
1260     else
1261         action_raise(data);
1262 }
1263
1264 void action_shadelower(union ActionData *data)
1265 {
1266     if (data->client.any.c->shaded)
1267         action_lower(data);
1268     else
1269         action_shade(data);
1270 }
1271
1272 void action_lower(union ActionData *data)
1273 {
1274     client_action_start(data);
1275     stacking_lower(CLIENT_AS_WINDOW(data->client.any.c));
1276     client_action_end(data, config_focus_under_mouse);
1277 }
1278
1279 void action_close(union ActionData *data)
1280 {
1281     client_close(data->client.any.c);
1282 }
1283
1284 void action_kill(union ActionData *data)
1285 {
1286     client_kill(data->client.any.c);
1287 }
1288
1289 void action_shade(union ActionData *data)
1290 {
1291     client_action_start(data);
1292     client_shade(data->client.any.c, TRUE);
1293     client_action_end(data, config_focus_under_mouse);
1294 }
1295
1296 void action_unshade(union ActionData *data)
1297 {
1298     client_action_start(data);
1299     client_shade(data->client.any.c, FALSE);
1300     client_action_end(data, config_focus_under_mouse);
1301 }
1302
1303 void action_toggle_shade(union ActionData *data)
1304 {
1305     client_action_start(data);
1306     client_shade(data->client.any.c, !data->client.any.c->shaded);
1307     client_action_end(data, config_focus_under_mouse);
1308 }
1309
1310 void action_toggle_omnipresent(union ActionData *data)
1311
1312     client_set_desktop(data->client.any.c,
1313                        data->client.any.c->desktop == DESKTOP_ALL ?
1314                        screen_desktop : DESKTOP_ALL, FALSE, TRUE);
1315 }
1316
1317 void action_move_relative_horz(union ActionData *data)
1318 {
1319     ObClient *c = data->relative.any.c;
1320     client_action_start(data);
1321     client_move(c, c->area.x + data->relative.deltax, c->area.y);
1322     client_action_end(data, FALSE);
1323 }
1324
1325 void action_move_relative_vert(union ActionData *data)
1326 {
1327     ObClient *c = data->relative.any.c;
1328     client_action_start(data);
1329     client_move(c, c->area.x, c->area.y + data->relative.deltax);
1330     client_action_end(data, FALSE);
1331 }
1332
1333 void action_move_to_center(union ActionData *data)
1334 {
1335     ObClient *c = data->client.any.c;
1336     Rect *area;
1337     area = screen_area(c->desktop, client_monitor(c), NULL);
1338     client_action_start(data);
1339     client_move(c, area->x + area->width / 2 - c->area.width / 2,
1340                 area->y + area->height / 2 - c->area.height / 2);
1341     client_action_end(data, FALSE);
1342     g_free(area);
1343 }
1344
1345 void action_resize_relative_horz(union ActionData *data)
1346 {
1347     ObClient *c = data->relative.any.c;
1348     client_action_start(data);
1349     client_resize(c,
1350                   c->area.width + data->relative.deltax * c->size_inc.width,
1351                   c->area.height);
1352     client_action_end(data, FALSE);
1353 }
1354
1355 void action_resize_relative_vert(union ActionData *data)
1356 {
1357     ObClient *c = data->relative.any.c;
1358     if (!c->shaded) {
1359         client_action_start(data);
1360         client_resize(c, c->area.width, c->area.height +
1361                       data->relative.deltax * c->size_inc.height);
1362         client_action_end(data, FALSE);
1363     }
1364 }
1365
1366 void action_move_relative(union ActionData *data)
1367 {
1368     ObClient *c = data->relative.any.c;
1369     client_action_start(data);
1370     client_move(c, c->area.x + data->relative.deltax, c->area.y +
1371                 data->relative.deltay);
1372     client_action_end(data, FALSE);
1373 }
1374
1375 void action_resize_relative(union ActionData *data)
1376 {
1377     ObClient *c = data->relative.any.c;
1378     gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
1379
1380     client_action_start(data);
1381
1382     x = c->area.x;
1383     y = c->area.y;
1384     ow = c->area.width;
1385     xoff = -data->relative.deltaxl * c->size_inc.width;
1386     nw = ow + data->relative.deltax * c->size_inc.width
1387         + data->relative.deltaxl * c->size_inc.width;
1388     oh = c->area.height;
1389     yoff = -data->relative.deltayu * c->size_inc.height;
1390     nh = oh + data->relative.deltay * c->size_inc.height
1391         + data->relative.deltayu * c->size_inc.height;
1392
1393     g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
1394             data->relative.deltax, 
1395             data->relative.deltaxl, 
1396             x, ow, xoff, nw);
1397     
1398     client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
1399     xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
1400     yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
1401     client_move_resize(c, x + xoff, y + yoff, nw, nh);
1402     client_action_end(data, FALSE);
1403 }
1404
1405 void action_maximize_full(union ActionData *data)
1406 {
1407     client_action_start(data);
1408     client_maximize(data->client.any.c, TRUE, 0);
1409     client_action_end(data, config_focus_under_mouse);
1410 }
1411
1412 void action_unmaximize_full(union ActionData *data)
1413 {
1414     client_action_start(data);
1415     client_maximize(data->client.any.c, FALSE, 0);
1416     client_action_end(data, config_focus_under_mouse);
1417 }
1418
1419 void action_toggle_maximize_full(union ActionData *data)
1420 {
1421     client_action_start(data);
1422     client_maximize(data->client.any.c,
1423                     !(data->client.any.c->max_horz ||
1424                       data->client.any.c->max_vert),
1425                     0);
1426     client_action_end(data, config_focus_under_mouse);
1427 }
1428
1429 void action_maximize_horz(union ActionData *data)
1430 {
1431     client_action_start(data);
1432     client_maximize(data->client.any.c, TRUE, 1);
1433     client_action_end(data, config_focus_under_mouse);
1434 }
1435
1436 void action_unmaximize_horz(union ActionData *data)
1437 {
1438     client_action_start(data);
1439     client_maximize(data->client.any.c, FALSE, 1);
1440     client_action_end(data, config_focus_under_mouse);
1441 }
1442
1443 void action_toggle_maximize_horz(union ActionData *data)
1444 {
1445     client_action_start(data);
1446     client_maximize(data->client.any.c,
1447                     !data->client.any.c->max_horz, 1);
1448     client_action_end(data, config_focus_under_mouse);
1449 }
1450
1451 void action_maximize_vert(union ActionData *data)
1452 {
1453     client_action_start(data);
1454     client_maximize(data->client.any.c, TRUE, 2);
1455     client_action_end(data, config_focus_under_mouse);
1456 }
1457
1458 void action_unmaximize_vert(union ActionData *data)
1459 {
1460     client_action_start(data);
1461     client_maximize(data->client.any.c, FALSE, 2);
1462     client_action_end(data, config_focus_under_mouse);
1463 }
1464
1465 void action_toggle_maximize_vert(union ActionData *data)
1466 {
1467     client_action_start(data);
1468     client_maximize(data->client.any.c,
1469                     !data->client.any.c->max_vert, 2);
1470     client_action_end(data, config_focus_under_mouse);
1471 }
1472
1473 void action_toggle_fullscreen(union ActionData *data)
1474 {
1475     client_action_start(data);
1476     client_fullscreen(data->client.any.c, !(data->client.any.c->fullscreen));
1477     client_action_end(data, config_focus_under_mouse);
1478 }
1479
1480 void action_send_to_desktop(union ActionData *data)
1481 {
1482     ObClient *c = data->sendto.any.c;
1483
1484     if (!client_normal(c)) return;
1485
1486     if (data->sendto.desk < screen_num_desktops ||
1487         data->sendto.desk == DESKTOP_ALL) {
1488         client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
1489         if (data->sendto.follow && data->sendto.desk != screen_desktop)
1490             screen_set_desktop(data->sendto.desk, TRUE);
1491     }
1492 }
1493
1494 void action_desktop(union ActionData *data)
1495 {
1496     /* XXX add the interactive/dialog option back again once the dialog
1497        has been made to not use grabs */
1498     if (data->desktop.desk < screen_num_desktops ||
1499         data->desktop.desk == DESKTOP_ALL)
1500     {
1501         screen_set_desktop(data->desktop.desk, TRUE);
1502         if (data->inter.any.interactive)
1503             screen_desktop_popup(data->desktop.desk, TRUE);
1504     }
1505 }
1506
1507 void action_desktop_dir(union ActionData *data)
1508 {
1509     guint d;
1510
1511     d = screen_cycle_desktop(data->desktopdir.dir,
1512                              data->desktopdir.wrap,
1513                              data->desktopdir.linear,
1514                              data->desktopdir.inter.any.interactive,
1515                              data->desktopdir.inter.final,
1516                              data->desktopdir.inter.cancel);
1517     /* only move the desktop when the action is complete. if we switch
1518        desktops during the interactive action, focus will move but with
1519        NotifyWhileGrabbed and applications don't like that. */
1520     if (!data->sendtodir.inter.any.interactive ||
1521         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1522     {
1523         if (d != screen_desktop)
1524             screen_set_desktop(d, TRUE);
1525     }
1526 }
1527
1528 void action_send_to_desktop_dir(union ActionData *data)
1529 {
1530     ObClient *c = data->sendtodir.inter.any.c;
1531     guint d;
1532
1533     if (!client_normal(c)) return;
1534
1535     d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
1536                              data->sendtodir.linear,
1537                              data->sendtodir.inter.any.interactive,
1538                              data->sendtodir.inter.final,
1539                              data->sendtodir.inter.cancel);
1540     /* only move the desktop when the action is complete. if we switch
1541        desktops during the interactive action, focus will move but with
1542        NotifyWhileGrabbed and applications don't like that. */
1543     if (!data->sendtodir.inter.any.interactive ||
1544         (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
1545     {
1546         client_set_desktop(c, d, data->sendtodir.follow, FALSE);
1547         if (data->sendtodir.follow && d != screen_desktop)
1548             screen_set_desktop(d, TRUE);
1549     }
1550 }
1551
1552 void action_desktop_last(union ActionData *data)
1553 {
1554     if (screen_last_desktop < screen_num_desktops)
1555         screen_set_desktop(screen_last_desktop, TRUE);
1556 }
1557
1558 void action_toggle_decorations(union ActionData *data)
1559 {
1560     ObClient *c = data->client.any.c;
1561
1562     client_action_start(data);
1563     client_set_undecorated(c, !c->undecorated);
1564     client_action_end(data, FALSE);
1565 }
1566
1567 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
1568                            gboolean shaded)
1569 {
1570     /* let's make x and y client relative instead of screen relative */
1571     x = x - cx;
1572     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
1573
1574 #define X x*ch/cw
1575 #define A -4*X + 7*ch/3
1576 #define B  4*X -15*ch/9
1577 #define C -X/4 + 2*ch/3
1578 #define D  X/4 + 5*ch/12
1579 #define E  X/4 +   ch/3
1580 #define F -X/4 + 7*ch/12
1581 #define G  4*X - 4*ch/3
1582 #define H -4*X + 8*ch/3
1583 #define a (y > 5*ch/9)
1584 #define b (x < 4*cw/9)
1585 #define c (x > 5*cw/9)
1586 #define d (y < 4*ch/9)
1587
1588     /*
1589       Each of these defines (except X which is just there for fun), represents
1590       the equation of a line. The lines they represent are shown in the diagram
1591       below. Checking y against these lines, we are able to choose a region
1592       of the window as shown.
1593
1594       +---------------------A-------|-------|-------B---------------------+
1595       |                     |A                     B|                     |
1596       |                     |A      |       |      B|                     |
1597       |                     | A                   B |                     |
1598       |                     | A     |       |     B |                     |
1599       |                     |  A                 B  |                     |
1600       |                     |  A    |       |    B  |                     |
1601       |        northwest    |   A     north     B   |   northeast         |
1602       |                     |   A   |       |   B   |                     |
1603       |                     |    A             B    |                     |
1604       C---------------------+----A--+-------+--B----+---------------------D
1605       |CCCCCCC              |     A           B     |              DDDDDDD|
1606       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
1607       |               CCCCCCC      A         B      DDDDDDD               |
1608       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
1609       |                     |       b       c       |                     | sh
1610       |             west    |       b  move c       |   east              | ad
1611       |                     |       b       c       |                     | ed
1612       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
1613       |               EEEEEEE      G         H      FFFFFFF               |
1614       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
1615       |EEEEEEE              |     G           H     |              FFFFFFF|
1616       E---------------------+----G--+-------+--H----+---------------------F
1617       |                     |    G             H    |                     |
1618       |                     |   G   |       |   H   |                     |
1619       |        southwest    |   G     south     H   |   southeast         |
1620       |                     |  G    |       |    H  |                     |
1621       |                     |  G                 H  |                     |
1622       |                     | G     |       |     H |                     |
1623       |                     | G                   H |                     |
1624       |                     |G      |       |      H|                     |
1625       |                     |G                     H|                     |
1626       +---------------------G-------|-------|-------H---------------------+
1627     */
1628
1629     if (shaded) {
1630         /* for shaded windows, you can only resize west/east and move */
1631         if (b)
1632             return prop_atoms.net_wm_moveresize_size_left;
1633         if (c)
1634             return prop_atoms.net_wm_moveresize_size_right;
1635         return prop_atoms.net_wm_moveresize_move;
1636     }
1637
1638     if (y < A && y >= C)
1639         return prop_atoms.net_wm_moveresize_size_topleft;
1640     else if (y >= A && y >= B && a)
1641         return prop_atoms.net_wm_moveresize_size_top;
1642     else if (y < B && y >= D)
1643         return prop_atoms.net_wm_moveresize_size_topright;
1644     else if (y < C && y >= E && b)
1645         return prop_atoms.net_wm_moveresize_size_left;
1646     else if (y < D && y >= F && c)
1647         return prop_atoms.net_wm_moveresize_size_right;
1648     else if (y < E && y >= G)
1649         return prop_atoms.net_wm_moveresize_size_bottomleft;
1650     else if (y < G && y < H && d)
1651         return prop_atoms.net_wm_moveresize_size_bottom;
1652     else if (y >= H && y < F)
1653         return prop_atoms.net_wm_moveresize_size_bottomright;
1654     else
1655         return prop_atoms.net_wm_moveresize_move;
1656
1657 #undef X
1658 #undef A
1659 #undef B
1660 #undef C
1661 #undef D
1662 #undef E
1663 #undef F
1664 #undef G
1665 #undef H
1666 #undef a
1667 #undef b
1668 #undef c
1669 #undef d
1670 }
1671
1672 void action_move(union ActionData *data)
1673 {
1674     ObClient *c = data->moveresize.any.c;
1675     guint32 corner;
1676
1677     if (data->moveresize.keyboard)
1678         corner = prop_atoms.net_wm_moveresize_move_keyboard;
1679     else
1680         corner = prop_atoms.net_wm_moveresize_move;
1681
1682     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1683 }
1684
1685 void action_resize(union ActionData *data)
1686 {
1687     ObClient *c = data->moveresize.any.c;
1688     guint32 corner;
1689
1690     if (data->moveresize.keyboard)
1691         corner = prop_atoms.net_wm_moveresize_size_keyboard;
1692     else if (data->moveresize.corner)
1693         corner = data->moveresize.corner; /* it was specified in the binding */
1694     else
1695         corner = pick_corner(data->any.x, data->any.y,
1696                              c->frame->area.x, c->frame->area.y,
1697                              /* use the client size because the frame
1698                                 can be differently sized (shaded
1699                                 windows) and we want this based on the
1700                                 clients size */
1701                              c->area.width + c->frame->size.left +
1702                              c->frame->size.right,
1703                              c->area.height + c->frame->size.top +
1704                              c->frame->size.bottom, c->shaded);
1705
1706     moveresize_start(c, data->any.x, data->any.y, data->any.button, corner);
1707 }
1708
1709 void action_reconfigure(union ActionData *data)
1710 {
1711     ob_reconfigure();
1712 }
1713
1714 void action_restart(union ActionData *data)
1715 {
1716     ob_restart_other(data->execute.path);
1717 }
1718
1719 void action_exit(union ActionData *data)
1720 {
1721     ob_exit(0);
1722 }
1723
1724 void action_cycle_windows(union ActionData *data)
1725 {
1726     /* if using focus_delay, stop the timer now so that focus doesn't go moving
1727        on us */
1728     event_halt_focus_delay();
1729
1730     focus_cycle(data->cycle.forward,
1731                 data->cycle.all_desktops,
1732                 data->cycle.dock_windows,
1733                 data->cycle.desktop_windows,
1734                 data->cycle.linear, data->any.interactive,
1735                 data->cycle.dialog,
1736                 data->cycle.inter.final, data->cycle.inter.cancel);
1737 }
1738
1739 void action_directional_focus(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_directional_cycle(data->interdiraction.direction,
1746                             data->interdiraction.dock_windows,
1747                             data->interdiraction.desktop_windows,
1748                             data->any.interactive,
1749                             data->interdiraction.dialog,
1750                             data->interdiraction.inter.final,
1751                             data->interdiraction.inter.cancel);
1752 }
1753
1754 void action_movetoedge(union ActionData *data)
1755 {
1756     gint x, y;
1757     ObClient *c = data->diraction.any.c;
1758
1759     x = c->frame->area.x;
1760     y = c->frame->area.y;
1761     
1762     switch(data->diraction.direction) {
1763     case OB_DIRECTION_NORTH:
1764         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
1765                                            data->diraction.hang)
1766             - (data->diraction.hang ? c->frame->area.height : 0);
1767         break;
1768     case OB_DIRECTION_WEST:
1769         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
1770                                            data->diraction.hang)
1771             - (data->diraction.hang ? c->frame->area.width : 0);
1772         break;
1773     case OB_DIRECTION_SOUTH:
1774         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
1775                                            data->diraction.hang)
1776             - (data->diraction.hang ? 0 : c->frame->area.height);
1777         break;
1778     case OB_DIRECTION_EAST:
1779         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
1780                                            data->diraction.hang)
1781             - (data->diraction.hang ? 0 : c->frame->area.width);
1782         break;
1783     default:
1784         g_assert_not_reached();
1785     }
1786     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
1787     client_action_start(data);
1788     client_move(c, x, y);
1789     client_action_end(data, FALSE);
1790 }
1791
1792 void action_growtoedge(union ActionData *data)
1793 {
1794     gint x, y, width, height, dest;
1795     ObClient *c = data->diraction.any.c;
1796     Rect *a;
1797
1798     a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
1799     x = c->frame->area.x;
1800     y = c->frame->area.y;
1801     /* get the unshaded frame's dimensions..if it is shaded */
1802     width = c->area.width + c->frame->size.left + c->frame->size.right;
1803     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
1804
1805     switch(data->diraction.direction) {
1806     case OB_DIRECTION_NORTH:
1807         if (c->shaded) break; /* don't allow vertical resize if shaded */
1808
1809         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
1810         if (a->y == y)
1811             height = height / 2;
1812         else {
1813             height = c->frame->area.y + height - dest;
1814             y = dest;
1815         }
1816         break;
1817     case OB_DIRECTION_WEST:
1818         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
1819         if (a->x == x)
1820             width = width / 2;
1821         else {
1822             width = c->frame->area.x + width - dest;
1823             x = dest;
1824         }
1825         break;
1826     case OB_DIRECTION_SOUTH:
1827         if (c->shaded) break; /* don't allow vertical resize if shaded */
1828
1829         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
1830         if (a->y + a->height == y + c->frame->area.height) {
1831             height = c->frame->area.height / 2;
1832             y = a->y + a->height - height;
1833         } else
1834             height = dest - c->frame->area.y;
1835         y += (height - c->frame->area.height) % c->size_inc.height;
1836         height -= (height - c->frame->area.height) % c->size_inc.height;
1837         break;
1838     case OB_DIRECTION_EAST:
1839         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1840         if (a->x + a->width == x + c->frame->area.width) {
1841             width = c->frame->area.width / 2;
1842             x = a->x + a->width - width;
1843         } else
1844             width = dest - c->frame->area.x;
1845         x += (width - c->frame->area.width) % c->size_inc.width;
1846         width -= (width - c->frame->area.width) % c->size_inc.width;
1847         break;
1848     default:
1849         g_assert_not_reached();
1850     }
1851     width -= c->frame->size.left + c->frame->size.right;
1852     height -= c->frame->size.top + c->frame->size.bottom;
1853     frame_frame_gravity(c->frame, &x, &y, width, height);
1854     client_action_start(data);
1855     client_move_resize(c, x, y, width, height);
1856     client_action_end(data, FALSE);
1857     g_free(a);
1858 }
1859
1860 void action_send_to_layer(union ActionData *data)
1861 {
1862     client_set_layer(data->layer.any.c, data->layer.layer);
1863 }
1864
1865 void action_toggle_layer(union ActionData *data)
1866 {
1867     ObClient *c = data->layer.any.c;
1868
1869     client_action_start(data);
1870     if (data->layer.layer < 0)
1871         client_set_layer(c, c->below ? 0 : -1);
1872     else if (data->layer.layer > 0)
1873         client_set_layer(c, c->above ? 0 : 1);
1874     client_action_end(data, config_focus_under_mouse);
1875 }
1876
1877 void action_toggle_dockautohide(union ActionData *data)
1878 {
1879     config_dock_hide = !config_dock_hide;
1880     dock_configure();
1881 }
1882
1883 void action_break_chroot(union ActionData *data)
1884 {
1885     /* break out of one chroot */
1886     keyboard_reset_chains(1);
1887 }
1888
1889 void action_add_desktop(union ActionData *data)
1890 {
1891     client_action_start(data);
1892     screen_set_num_desktops(screen_num_desktops+1);
1893
1894     /* move all the clients over */
1895     if (data->addremovedesktop.current) {
1896         GList *it;
1897
1898         for (it = client_list; it; it = g_list_next(it)) {
1899             ObClient *c = it->data;
1900             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
1901                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
1902         }
1903     }
1904
1905     client_action_end(data, config_focus_under_mouse);
1906 }
1907
1908 void action_remove_desktop(union ActionData *data)
1909 {
1910     guint rmdesktop, movedesktop;
1911     GList *it, *stacking_copy;
1912
1913     if (screen_num_desktops < 2) return;
1914
1915     client_action_start(data);
1916
1917     /* what desktop are we removing and moving to? */
1918     if (data->addremovedesktop.current)
1919         rmdesktop = screen_desktop;
1920     else
1921         rmdesktop = screen_num_desktops - 1;
1922     if (rmdesktop < screen_num_desktops - 1)
1923         movedesktop = rmdesktop + 1;
1924     else
1925         movedesktop = rmdesktop;
1926
1927     /* make a copy of the list cuz we're changing it */
1928     stacking_copy = g_list_copy(stacking_list);
1929     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
1930         if (WINDOW_IS_CLIENT(it->data)) {
1931             ObClient *c = it->data;
1932             guint d = c->desktop;
1933             if (d != DESKTOP_ALL && d >= movedesktop) {
1934                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
1935                 ob_debug("moving window %s\n", c->title);
1936             }
1937             /* raise all the windows that are on the current desktop which
1938                is being merged */
1939             if ((screen_desktop == rmdesktop - 1 ||
1940                  screen_desktop == rmdesktop) &&
1941                 (d == DESKTOP_ALL || d == screen_desktop))
1942             {
1943                 stacking_raise(CLIENT_AS_WINDOW(c));
1944                 ob_debug("raising window %s\n", c->title);
1945             }
1946         }
1947     }
1948
1949     /* act like we're changing desktops */
1950     if (screen_desktop < screen_num_desktops - 1) {
1951         gint d = screen_desktop;
1952         screen_desktop = screen_last_desktop;
1953         screen_set_desktop(d, TRUE);
1954         ob_debug("fake desktop change\n");
1955     }
1956
1957     screen_set_num_desktops(screen_num_desktops-1);
1958
1959     client_action_end(data, config_focus_under_mouse);
1960 }