1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 action.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
23 #include "focus_cycle.h"
24 #include "moveresize.h"
37 #include "startupnotify.h"
47 void (*func)(union ActionData *);
48 void (*setup)(ObAction **, ObUserAction uact);
51 static ObAction *action_new(void (*func)(union ActionData *data))
53 ObAction *a = g_new0(ObAction, 1);
60 void action_ref(ObAction *a)
65 void action_unref(ObAction *a)
67 if (a == NULL) return;
69 if (--a->ref > 0) return;
71 /* deal with pointers */
72 if (a->func == action_execute || a->func == action_restart)
73 g_free(a->data.execute.path);
74 else if (a->func == action_debug)
75 g_free(a->data.debug.string);
76 else if (a->func == action_showmenu)
77 g_free(a->data.showmenu.name);
82 ObAction* action_copy(const ObAction *src)
84 ObAction *a = action_new(src->func);
88 /* deal with pointers */
89 if (a->func == action_execute || a->func == action_restart)
90 a->data.execute.path = g_strdup(a->data.execute.path);
91 else if (a->func == action_debug)
92 a->data.debug.string = g_strdup(a->data.debug.string);
93 else if (a->func == action_showmenu)
94 a->data.showmenu.name = g_strdup(a->data.showmenu.name);
99 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
101 (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
102 (*a)->data.sendto.follow = TRUE;
105 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
107 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
108 (*a)->data.sendtodir.inter.any.interactive = TRUE;
109 (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
110 (*a)->data.sendtodir.linear = TRUE;
111 (*a)->data.sendtodir.wrap = TRUE;
112 (*a)->data.sendtodir.follow = TRUE;
115 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
117 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
118 (*a)->data.sendtodir.inter.any.interactive = TRUE;
119 (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
120 (*a)->data.sendtodir.linear = TRUE;
121 (*a)->data.sendtodir.wrap = TRUE;
122 (*a)->data.sendtodir.follow = TRUE;
125 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
127 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
128 (*a)->data.sendtodir.inter.any.interactive = TRUE;
129 (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
130 (*a)->data.sendtodir.linear = FALSE;
131 (*a)->data.sendtodir.wrap = TRUE;
132 (*a)->data.sendtodir.follow = TRUE;
135 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
137 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
138 (*a)->data.sendtodir.inter.any.interactive = TRUE;
139 (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
140 (*a)->data.sendtodir.linear = FALSE;
141 (*a)->data.sendtodir.wrap = TRUE;
142 (*a)->data.sendtodir.follow = TRUE;
145 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
147 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
148 (*a)->data.sendtodir.inter.any.interactive = TRUE;
149 (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
150 (*a)->data.sendtodir.linear = FALSE;
151 (*a)->data.sendtodir.wrap = TRUE;
152 (*a)->data.sendtodir.follow = TRUE;
155 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
157 (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
158 (*a)->data.sendtodir.inter.any.interactive = TRUE;
159 (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
160 (*a)->data.sendtodir.linear = FALSE;
161 (*a)->data.sendtodir.wrap = TRUE;
162 (*a)->data.sendtodir.follow = TRUE;
165 void setup_action_desktop(ObAction **a, ObUserAction uact)
168 (*a)->data.desktop.inter.any.interactive = FALSE;
172 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
174 (*a)->data.desktopdir.inter.any.interactive = TRUE;
175 (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
176 (*a)->data.desktopdir.linear = TRUE;
177 (*a)->data.desktopdir.wrap = TRUE;
180 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
182 (*a)->data.desktopdir.inter.any.interactive = TRUE;
183 (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
184 (*a)->data.desktopdir.linear = TRUE;
185 (*a)->data.desktopdir.wrap = TRUE;
188 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
190 (*a)->data.desktopdir.inter.any.interactive = TRUE;
191 (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
192 (*a)->data.desktopdir.linear = FALSE;
193 (*a)->data.desktopdir.wrap = TRUE;
196 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
198 (*a)->data.desktopdir.inter.any.interactive = TRUE;
199 (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
200 (*a)->data.desktopdir.linear = FALSE;
201 (*a)->data.desktopdir.wrap = TRUE;
204 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
206 (*a)->data.desktopdir.inter.any.interactive = TRUE;
207 (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
208 (*a)->data.desktopdir.linear = FALSE;
209 (*a)->data.desktopdir.wrap = TRUE;
212 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
214 (*a)->data.desktopdir.inter.any.interactive = TRUE;
215 (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
216 (*a)->data.desktopdir.linear = FALSE;
217 (*a)->data.desktopdir.wrap = TRUE;
220 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
222 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
223 (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
224 (*a)->data.diraction.hang = TRUE;
227 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
229 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
230 (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
231 (*a)->data.diraction.hang = TRUE;
234 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
236 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
237 (*a)->data.diraction.direction = OB_DIRECTION_EAST;
238 (*a)->data.diraction.hang = TRUE;
241 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
243 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
244 (*a)->data.diraction.direction = OB_DIRECTION_WEST;
245 (*a)->data.diraction.hang = TRUE;
248 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
250 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
251 (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
252 (*a)->data.diraction.hang = FALSE;
255 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
257 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
258 (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
259 (*a)->data.diraction.hang = FALSE;
262 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
264 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
265 (*a)->data.diraction.direction = OB_DIRECTION_EAST;
266 (*a)->data.diraction.hang = FALSE;
269 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
271 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
272 (*a)->data.diraction.direction = OB_DIRECTION_WEST;
273 (*a)->data.diraction.hang = FALSE;
276 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
278 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
279 (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
282 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
284 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
285 (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
288 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
290 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
291 (*a)->data.diraction.direction = OB_DIRECTION_EAST;
294 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
296 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
297 (*a)->data.diraction.direction = OB_DIRECTION_WEST;
300 void setup_action_top_layer(ObAction **a, ObUserAction uact)
302 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
303 (*a)->data.layer.layer = 1;
306 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
308 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
309 (*a)->data.layer.layer = 0;
312 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
314 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
315 (*a)->data.layer.layer = -1;
318 void setup_action_resize(ObAction **a, ObUserAction uact)
320 (*a)->data.moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS;
321 (*a)->data.moveresize.keyboard =
322 (uact == OB_USER_ACTION_NONE ||
323 uact == OB_USER_ACTION_KEYBOARD_KEY ||
324 uact == OB_USER_ACTION_MENU_SELECTION);
325 (*a)->data.moveresize.corner = 0;
328 void setup_action_addremove_desktop_current(ObAction **a, ObUserAction uact)
330 (*a)->data.addremovedesktop.current = TRUE;
333 void setup_action_addremove_desktop_last(ObAction **a, ObUserAction uact)
335 (*a)->data.addremovedesktop.current = FALSE;
338 void setup_client_action(ObAction **a, ObUserAction uact)
340 (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
343 ActionString actionstrings[] =
356 "resizerelativevert",
357 action_resize_relative_vert,
362 action_resize_relative,
367 action_send_to_desktop,
368 setup_action_send_to_desktop
372 action_send_to_desktop_dir,
373 setup_action_send_to_desktop_next
376 "sendtodesktopprevious",
377 action_send_to_desktop_dir,
378 setup_action_send_to_desktop_prev
381 "sendtodesktopright",
382 action_send_to_desktop_dir,
383 setup_action_send_to_desktop_right
387 action_send_to_desktop_dir,
388 setup_action_send_to_desktop_left
392 action_send_to_desktop_dir,
393 setup_action_send_to_desktop_up
397 action_send_to_desktop_dir,
398 setup_action_send_to_desktop_down
408 setup_action_desktop_next
413 setup_action_desktop_prev
418 setup_action_desktop_right
423 setup_action_desktop_left
428 setup_action_desktop_up
433 setup_action_desktop_down
437 action_toggle_decorations,
441 "toggledockautohide",
442 action_toggle_dockautohide,
452 action_send_to_layer,
453 setup_action_top_layer
458 setup_action_top_layer
462 action_send_to_layer,
463 setup_action_normal_layer
467 action_send_to_layer,
468 setup_action_bottom_layer
471 "togglealwaysonbottom",
473 setup_action_bottom_layer
478 setup_action_movefromedge_north
483 setup_action_movefromedge_south
488 setup_action_movefromedge_west
493 setup_action_movefromedge_east
498 setup_action_movetoedge_north
503 setup_action_movetoedge_south
508 setup_action_movetoedge_west
513 setup_action_movetoedge_east
518 setup_action_growtoedge_north
523 setup_action_growtoedge_south
528 setup_action_growtoedge_west
533 setup_action_growtoedge_east
538 setup_action_addremove_desktop_last
542 action_remove_desktop,
543 setup_action_addremove_desktop_last
548 setup_action_addremove_desktop_current
551 "removedesktopcurrent",
552 action_remove_desktop,
553 setup_action_addremove_desktop_current
562 /* only key bindings can be interactive. thus saith the xor.
563 because of how the mouse is grabbed, mouse events dont even get
564 read during interactive events, so no dice! >:) */
565 #define INTERACTIVE_LIMIT(a, uact) \
566 if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
567 a->data.any.interactive = FALSE;
569 ObAction *action_from_string(const gchar *name, ObUserAction uact)
572 gboolean exist = FALSE;
575 for (i = 0; actionstrings[i].name; i++)
576 if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
578 a = action_new(actionstrings[i].func);
579 if (actionstrings[i].setup)
580 actionstrings[i].setup(&a, uact);
582 INTERACTIVE_LIMIT(a, uact);
586 g_message(_("Invalid action '%s' requested. No such action exists."),
589 g_message(_("Invalid use of action '%s'. Action will be ignored."),
594 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
598 ObAction *act = NULL;
601 if (parse_attr_string("name", node, &actname)) {
602 if ((act = action_from_string(actname, uact))) {
603 } else if (act->func == action_resize_relative) {
604 if ((n = parse_find_node("left", node->xmlChildrenNode)))
605 act->data.relative.deltaxl = parse_int(doc, n);
606 if ((n = parse_find_node("up", node->xmlChildrenNode)))
607 act->data.relative.deltayu = parse_int(doc, n);
608 if ((n = parse_find_node("right", node->xmlChildrenNode)))
609 act->data.relative.deltax = parse_int(doc, n);
610 if ((n = parse_find_node("down", node->xmlChildrenNode)))
611 act->data.relative.deltay = parse_int(doc, n);
612 } else if (act->func == action_desktop) {
613 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
614 act->data.desktop.desk = parse_int(doc, n);
615 if (act->data.desktop.desk > 0) act->data.desktop.desk--;
617 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
618 act->data.desktop.inter.any.interactive =
621 } else if (act->func == action_send_to_desktop) {
622 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
623 act->data.sendto.desk = parse_int(doc, n);
624 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
625 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
626 act->data.sendto.follow = parse_bool(doc, n);
627 } else if (act->func == action_desktop_dir) {
628 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
629 act->data.desktopdir.wrap = parse_bool(doc, n);
630 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
631 act->data.desktopdir.inter.any.interactive =
633 } else if (act->func == action_send_to_desktop_dir) {
634 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
635 act->data.sendtodir.wrap = parse_bool(doc, n);
636 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
637 act->data.sendtodir.follow = parse_bool(doc, n);
638 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
639 act->data.sendtodir.inter.any.interactive =
641 INTERACTIVE_LIMIT(act, uact);
648 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
649 guint state, guint button, gint x, gint y, Time time,
650 gboolean cancel, gboolean done)
659 screen_pointer_pos(&x, &y);
661 for (it = acts; it; it = g_slist_next(it)) {
664 if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
665 a->data.any.c = a->data.any.client_action ? c : NULL;
666 a->data.any.context = context;
670 a->data.any.button = button;
672 a->data.any.time = time;
674 if (a->data.any.interactive) {
675 a->data.inter.cancel = cancel;
676 a->data.inter.final = done;
677 if (!(cancel || done))
678 if (!keyboard_interactive_grab(state, a->data.any.c, a))
682 /* XXX UGLY HACK race with motion event starting a move and the
683 button release gettnig processed first. answer: don't queue
684 moveresize starts. UGLY HACK XXX
686 XXX ALSO don't queue showmenu events, because on button press
687 events we need to know if a mouse grab is going to take place,
688 and set the button to 0, so that later motion events don't think
689 that a drag is going on. since showmenu grabs the pointer..
691 if (a->data.any.interactive || a->func == action_move ||
692 a->func == action_resize || a->func == action_showmenu)
694 /* interactive actions are not queued */
696 } else if (a->func == action_focus ||
697 a->func == action_activate ||
698 a->func == action_showmenu)
700 /* XXX MORE UGLY HACK
701 actions from clicks on client windows are NOT queued.
702 this solves the mysterious click-and-drag-doesnt-work
703 problem. it was because the window gets focused and stuff
704 after the button event has already been passed through. i
705 dont really know why it should care but it does and it makes
708 however this very bogus ! !
709 we want to send the button press to the window BEFORE
710 we do the action because the action might move the windows
711 (eg change desktops) and then the button press ends up on
712 the completely wrong window !
713 so, this is just for that bug, and it will only NOT queue it
714 if it is a focusing action that can be used with the mouse
717 also with the menus, there is a race going on. if the
718 desktop wants to pop up a menu, and we do too, we send them
719 the button before we pop up the menu, so they pop up their
720 menu first. but not always. if we pop up our menu before
721 sending them the button press, then the result is
724 XXX further more. focus actions are not queued at all,
725 because if you bind focus->showmenu, the menu will get
726 hidden to do the focusing
730 ob_main_loop_queue_action(ob_main_loop, a);
735 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
740 a = action_from_string(name, OB_USER_ACTION_NONE);
743 l = g_slist_append(NULL, a);
745 action_run(l, c, 0, time);
748 void action_unshaderaise(union ActionData *data)
750 if (data->client.any.c->shaded)
751 action_unshade(data);
756 void action_shadelower(union ActionData *data)
758 if (data->client.any.c->shaded)
764 void action_resize_relative_horz(union ActionData *data)
766 ObClient *c = data->relative.any.c;
767 client_action_start(data);
769 c->area.width + data->relative.deltax * c->size_inc.width,
771 client_action_end(data, FALSE);
774 void action_resize_relative_vert(union ActionData *data)
776 ObClient *c = data->relative.any.c;
778 client_action_start(data);
779 client_resize(c, c->area.width, c->area.height +
780 data->relative.deltax * c->size_inc.height);
781 client_action_end(data, FALSE);
785 void action_resize_relative(union ActionData *data)
787 ObClient *c = data->relative.any.c;
788 gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
790 client_action_start(data);
795 xoff = -data->relative.deltaxl * c->size_inc.width;
796 nw = ow + data->relative.deltax * c->size_inc.width
797 + data->relative.deltaxl * c->size_inc.width;
799 yoff = -data->relative.deltayu * c->size_inc.height;
800 nh = oh + data->relative.deltay * c->size_inc.height
801 + data->relative.deltayu * c->size_inc.height;
803 g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
804 data->relative.deltax,
805 data->relative.deltaxl,
808 client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
809 xoff = xoff == 0 ? 0 : (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
810 yoff = yoff == 0 ? 0 : (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
811 client_move_resize(c, x + xoff, y + yoff, nw, nh);
812 client_action_end(data, FALSE);
815 void action_send_to_desktop(union ActionData *data)
817 ObClient *c = data->sendto.any.c;
819 if (!client_normal(c)) return;
821 if (data->sendto.desk < screen_num_desktops ||
822 data->sendto.desk == DESKTOP_ALL) {
823 client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
824 if (data->sendto.follow && data->sendto.desk != screen_desktop)
825 screen_set_desktop(data->sendto.desk, TRUE);
829 void action_desktop(union ActionData *data)
831 /* XXX add the interactive/dialog option back again once the dialog
832 has been made to not use grabs */
833 if (data->desktop.desk < screen_num_desktops ||
834 data->desktop.desk == DESKTOP_ALL)
836 screen_set_desktop(data->desktop.desk, TRUE);
837 if (data->inter.any.interactive)
838 screen_desktop_popup(data->desktop.desk, TRUE);
842 void action_desktop_dir(union ActionData *data)
846 d = screen_cycle_desktop(data->desktopdir.dir,
847 data->desktopdir.wrap,
848 data->desktopdir.linear,
849 data->desktopdir.inter.any.interactive,
850 data->desktopdir.inter.final,
851 data->desktopdir.inter.cancel);
852 /* only move the desktop when the action is complete. if we switch
853 desktops during the interactive action, focus will move but with
854 NotifyWhileGrabbed and applications don't like that. */
855 if (!data->sendtodir.inter.any.interactive ||
856 (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
858 if (d != screen_desktop)
859 screen_set_desktop(d, TRUE);
863 void action_send_to_desktop_dir(union ActionData *data)
865 ObClient *c = data->sendtodir.inter.any.c;
868 if (!client_normal(c)) return;
870 d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
871 data->sendtodir.linear,
872 data->sendtodir.inter.any.interactive,
873 data->sendtodir.inter.final,
874 data->sendtodir.inter.cancel);
875 /* only move the desktop when the action is complete. if we switch
876 desktops during the interactive action, focus will move but with
877 NotifyWhileGrabbed and applications don't like that. */
878 if (!data->sendtodir.inter.any.interactive ||
879 (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
881 client_set_desktop(c, d, data->sendtodir.follow, FALSE);
882 if (data->sendtodir.follow && d != screen_desktop)
883 screen_set_desktop(d, TRUE);
887 void action_desktop_last(union ActionData *data)
889 if (screen_last_desktop < screen_num_desktops)
890 screen_set_desktop(screen_last_desktop, TRUE);
893 void action_toggle_decorations(union ActionData *data)
895 ObClient *c = data->client.any.c;
897 client_action_start(data);
898 client_set_undecorated(c, !c->undecorated);
899 client_action_end(data, FALSE);
903 void action_directional_focus(union ActionData *data)
905 /* if using focus_delay, stop the timer now so that focus doesn't go moving
907 event_halt_focus_delay();
909 focus_directional_cycle(data->interdiraction.direction,
910 data->interdiraction.dock_windows,
911 data->interdiraction.desktop_windows,
912 data->any.interactive,
913 data->interdiraction.dialog,
914 data->interdiraction.inter.final,
915 data->interdiraction.inter.cancel);
918 void action_movetoedge(union ActionData *data)
921 ObClient *c = data->diraction.any.c;
923 x = c->frame->area.x;
924 y = c->frame->area.y;
926 switch(data->diraction.direction) {
927 case OB_DIRECTION_NORTH:
928 y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
929 data->diraction.hang)
930 - (data->diraction.hang ? c->frame->area.height : 0);
932 case OB_DIRECTION_WEST:
933 x = client_directional_edge_search(c, OB_DIRECTION_WEST,
934 data->diraction.hang)
935 - (data->diraction.hang ? c->frame->area.width : 0);
937 case OB_DIRECTION_SOUTH:
938 y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
939 data->diraction.hang)
940 - (data->diraction.hang ? 0 : c->frame->area.height);
942 case OB_DIRECTION_EAST:
943 x = client_directional_edge_search(c, OB_DIRECTION_EAST,
944 data->diraction.hang)
945 - (data->diraction.hang ? 0 : c->frame->area.width);
948 g_assert_not_reached();
950 frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
951 client_action_start(data);
952 client_move(c, x, y);
953 client_action_end(data, FALSE);
956 void action_growtoedge(union ActionData *data)
958 gint x, y, width, height, dest;
959 ObClient *c = data->diraction.any.c;
962 a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
963 x = c->frame->area.x;
964 y = c->frame->area.y;
965 /* get the unshaded frame's dimensions..if it is shaded */
966 width = c->area.width + c->frame->size.left + c->frame->size.right;
967 height = c->area.height + c->frame->size.top + c->frame->size.bottom;
969 switch(data->diraction.direction) {
970 case OB_DIRECTION_NORTH:
971 if (c->shaded) break; /* don't allow vertical resize if shaded */
973 dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
977 height = c->frame->area.y + height - dest;
981 case OB_DIRECTION_WEST:
982 dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
986 width = c->frame->area.x + width - dest;
990 case OB_DIRECTION_SOUTH:
991 if (c->shaded) break; /* don't allow vertical resize if shaded */
993 dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
994 if (a->y + a->height == y + c->frame->area.height) {
995 height = c->frame->area.height / 2;
996 y = a->y + a->height - height;
998 height = dest - c->frame->area.y;
999 y += (height - c->frame->area.height) % c->size_inc.height;
1000 height -= (height - c->frame->area.height) % c->size_inc.height;
1002 case OB_DIRECTION_EAST:
1003 dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
1004 if (a->x + a->width == x + c->frame->area.width) {
1005 width = c->frame->area.width / 2;
1006 x = a->x + a->width - width;
1008 width = dest - c->frame->area.x;
1009 x += (width - c->frame->area.width) % c->size_inc.width;
1010 width -= (width - c->frame->area.width) % c->size_inc.width;
1013 g_assert_not_reached();
1015 width -= c->frame->size.left + c->frame->size.right;
1016 height -= c->frame->size.top + c->frame->size.bottom;
1017 frame_frame_gravity(c->frame, &x, &y, width, height);
1018 client_action_start(data);
1019 client_move_resize(c, x, y, width, height);
1020 client_action_end(data, FALSE);
1024 void action_send_to_layer(union ActionData *data)
1026 client_set_layer(data->layer.any.c, data->layer.layer);
1029 void action_toggle_layer(union ActionData *data)
1031 ObClient *c = data->layer.any.c;
1033 client_action_start(data);
1034 if (data->layer.layer < 0)
1035 client_set_layer(c, c->below ? 0 : -1);
1036 else if (data->layer.layer > 0)
1037 client_set_layer(c, c->above ? 0 : 1);
1038 client_action_end(data, config_focus_under_mouse);
1041 void action_toggle_dockautohide(union ActionData *data)
1043 config_dock_hide = !config_dock_hide;
1047 void action_add_desktop(union ActionData *data)
1049 client_action_start(data);
1050 screen_set_num_desktops(screen_num_desktops+1);
1052 /* move all the clients over */
1053 if (data->addremovedesktop.current) {
1056 for (it = client_list; it; it = g_list_next(it)) {
1057 ObClient *c = it->data;
1058 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
1059 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
1063 client_action_end(data, config_focus_under_mouse);
1066 void action_remove_desktop(union ActionData *data)
1068 guint rmdesktop, movedesktop;
1069 GList *it, *stacking_copy;
1071 if (screen_num_desktops < 2) return;
1073 client_action_start(data);
1075 /* what desktop are we removing and moving to? */
1076 if (data->addremovedesktop.current)
1077 rmdesktop = screen_desktop;
1079 rmdesktop = screen_num_desktops - 1;
1080 if (rmdesktop < screen_num_desktops - 1)
1081 movedesktop = rmdesktop + 1;
1083 movedesktop = rmdesktop;
1085 /* make a copy of the list cuz we're changing it */
1086 stacking_copy = g_list_copy(stacking_list);
1087 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
1088 if (WINDOW_IS_CLIENT(it->data)) {
1089 ObClient *c = it->data;
1090 guint d = c->desktop;
1091 if (d != DESKTOP_ALL && d >= movedesktop) {
1092 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
1093 ob_debug("moving window %s\n", c->title);
1095 /* raise all the windows that are on the current desktop which
1097 if ((screen_desktop == rmdesktop - 1 ||
1098 screen_desktop == rmdesktop) &&
1099 (d == DESKTOP_ALL || d == screen_desktop))
1101 stacking_raise(CLIENT_AS_WINDOW(c));
1102 ob_debug("raising window %s\n", c->title);
1107 /* act like we're changing desktops */
1108 if (screen_desktop < screen_num_desktops - 1) {
1109 gint d = screen_desktop;
1110 screen_desktop = screen_last_desktop;
1111 screen_set_desktop(d, TRUE);
1112 ob_debug("fake desktop change\n");
1115 screen_set_num_desktops(screen_num_desktops-1);
1117 client_action_end(data, config_focus_under_mouse);