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_prev(ObAction
**a
, ObUserAction uact
)
167 (*a
)->data
.desktopdir
.inter
.any
.interactive
= TRUE
;
168 (*a
)->data
.desktopdir
.dir
= OB_DIRECTION_WEST
;
169 (*a
)->data
.desktopdir
.linear
= TRUE
;
170 (*a
)->data
.desktopdir
.wrap
= TRUE
;
173 void setup_action_desktop_next(ObAction
**a
, ObUserAction uact
)
175 (*a
)->data
.desktopdir
.inter
.any
.interactive
= TRUE
;
176 (*a
)->data
.desktopdir
.dir
= OB_DIRECTION_EAST
;
177 (*a
)->data
.desktopdir
.linear
= TRUE
;
178 (*a
)->data
.desktopdir
.wrap
= TRUE
;
181 void setup_action_desktop_left(ObAction
**a
, ObUserAction uact
)
183 (*a
)->data
.desktopdir
.inter
.any
.interactive
= TRUE
;
184 (*a
)->data
.desktopdir
.dir
= OB_DIRECTION_WEST
;
185 (*a
)->data
.desktopdir
.linear
= FALSE
;
186 (*a
)->data
.desktopdir
.wrap
= TRUE
;
189 void setup_action_desktop_right(ObAction
**a
, ObUserAction uact
)
191 (*a
)->data
.desktopdir
.inter
.any
.interactive
= TRUE
;
192 (*a
)->data
.desktopdir
.dir
= OB_DIRECTION_EAST
;
193 (*a
)->data
.desktopdir
.linear
= FALSE
;
194 (*a
)->data
.desktopdir
.wrap
= TRUE
;
197 void setup_action_desktop_up(ObAction
**a
, ObUserAction uact
)
199 (*a
)->data
.desktopdir
.inter
.any
.interactive
= TRUE
;
200 (*a
)->data
.desktopdir
.dir
= OB_DIRECTION_NORTH
;
201 (*a
)->data
.desktopdir
.linear
= FALSE
;
202 (*a
)->data
.desktopdir
.wrap
= TRUE
;
205 void setup_action_desktop_down(ObAction
**a
, ObUserAction uact
)
207 (*a
)->data
.desktopdir
.inter
.any
.interactive
= TRUE
;
208 (*a
)->data
.desktopdir
.dir
= OB_DIRECTION_SOUTH
;
209 (*a
)->data
.desktopdir
.linear
= FALSE
;
210 (*a
)->data
.desktopdir
.wrap
= TRUE
;
213 void setup_action_movefromedge_north(ObAction
**a
, ObUserAction uact
)
215 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
216 (*a
)->data
.diraction
.direction
= OB_DIRECTION_NORTH
;
217 (*a
)->data
.diraction
.hang
= TRUE
;
220 void setup_action_movefromedge_south(ObAction
**a
, ObUserAction uact
)
222 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
223 (*a
)->data
.diraction
.direction
= OB_DIRECTION_SOUTH
;
224 (*a
)->data
.diraction
.hang
= TRUE
;
227 void setup_action_movefromedge_east(ObAction
**a
, ObUserAction uact
)
229 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
230 (*a
)->data
.diraction
.direction
= OB_DIRECTION_EAST
;
231 (*a
)->data
.diraction
.hang
= TRUE
;
234 void setup_action_movefromedge_west(ObAction
**a
, ObUserAction uact
)
236 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
237 (*a
)->data
.diraction
.direction
= OB_DIRECTION_WEST
;
238 (*a
)->data
.diraction
.hang
= TRUE
;
241 void setup_action_movetoedge_north(ObAction
**a
, ObUserAction uact
)
243 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
244 (*a
)->data
.diraction
.direction
= OB_DIRECTION_NORTH
;
245 (*a
)->data
.diraction
.hang
= FALSE
;
248 void setup_action_movetoedge_south(ObAction
**a
, ObUserAction uact
)
250 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
251 (*a
)->data
.diraction
.direction
= OB_DIRECTION_SOUTH
;
252 (*a
)->data
.diraction
.hang
= FALSE
;
255 void setup_action_movetoedge_east(ObAction
**a
, ObUserAction uact
)
257 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
258 (*a
)->data
.diraction
.direction
= OB_DIRECTION_EAST
;
259 (*a
)->data
.diraction
.hang
= FALSE
;
262 void setup_action_movetoedge_west(ObAction
**a
, ObUserAction uact
)
264 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
265 (*a
)->data
.diraction
.direction
= OB_DIRECTION_WEST
;
266 (*a
)->data
.diraction
.hang
= FALSE
;
269 void setup_action_growtoedge_north(ObAction
**a
, ObUserAction uact
)
271 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
272 (*a
)->data
.diraction
.direction
= OB_DIRECTION_NORTH
;
275 void setup_action_growtoedge_south(ObAction
**a
, ObUserAction uact
)
277 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
278 (*a
)->data
.diraction
.direction
= OB_DIRECTION_SOUTH
;
281 void setup_action_growtoedge_east(ObAction
**a
, ObUserAction uact
)
283 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
284 (*a
)->data
.diraction
.direction
= OB_DIRECTION_EAST
;
287 void setup_action_growtoedge_west(ObAction
**a
, ObUserAction uact
)
289 (*a
)->data
.diraction
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
290 (*a
)->data
.diraction
.direction
= OB_DIRECTION_WEST
;
293 void setup_action_top_layer(ObAction
**a
, ObUserAction uact
)
295 (*a
)->data
.layer
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
296 (*a
)->data
.layer
.layer
= 1;
299 void setup_action_normal_layer(ObAction
**a
, ObUserAction uact
)
301 (*a
)->data
.layer
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
302 (*a
)->data
.layer
.layer
= 0;
305 void setup_action_bottom_layer(ObAction
**a
, ObUserAction uact
)
307 (*a
)->data
.layer
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
308 (*a
)->data
.layer
.layer
= -1;
311 void setup_action_resize(ObAction
**a
, ObUserAction uact
)
313 (*a
)->data
.moveresize
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
314 (*a
)->data
.moveresize
.keyboard
=
315 (uact
== OB_USER_ACTION_NONE
||
316 uact
== OB_USER_ACTION_KEYBOARD_KEY
||
317 uact
== OB_USER_ACTION_MENU_SELECTION
);
318 (*a
)->data
.moveresize
.corner
= 0;
321 void setup_action_addremove_desktop_current(ObAction
**a
, ObUserAction uact
)
323 (*a
)->data
.addremovedesktop
.current
= TRUE
;
326 void setup_action_addremove_desktop_last(ObAction
**a
, ObUserAction uact
)
328 (*a
)->data
.addremovedesktop
.current
= FALSE
;
331 void setup_client_action(ObAction
**a
, ObUserAction uact
)
333 (*a
)->data
.any
.client_action
= OB_CLIENT_ACTION_ALWAYS
;
336 ActionString actionstrings
[] =
349 "resizerelativevert",
350 action_resize_relative_vert
,
355 action_resize_relative
,
360 action_send_to_desktop
,
361 setup_action_send_to_desktop
365 action_send_to_desktop_dir
,
366 setup_action_send_to_desktop_next
369 "sendtodesktopprevious",
370 action_send_to_desktop_dir
,
371 setup_action_send_to_desktop_prev
374 "sendtodesktopright",
375 action_send_to_desktop_dir
,
376 setup_action_send_to_desktop_right
380 action_send_to_desktop_dir
,
381 setup_action_send_to_desktop_left
385 action_send_to_desktop_dir
,
386 setup_action_send_to_desktop_up
390 action_send_to_desktop_dir
,
391 setup_action_send_to_desktop_down
394 "toggledockautohide",
395 action_toggle_dockautohide
,
405 action_send_to_layer
,
406 setup_action_top_layer
411 setup_action_top_layer
415 action_send_to_layer
,
416 setup_action_normal_layer
420 action_send_to_layer
,
421 setup_action_bottom_layer
424 "togglealwaysonbottom",
426 setup_action_bottom_layer
431 setup_action_movefromedge_north
436 setup_action_movefromedge_south
441 setup_action_movefromedge_west
446 setup_action_movefromedge_east
451 setup_action_movetoedge_north
456 setup_action_movetoedge_south
461 setup_action_movetoedge_west
466 setup_action_movetoedge_east
471 setup_action_growtoedge_north
476 setup_action_growtoedge_south
481 setup_action_growtoedge_west
486 setup_action_growtoedge_east
491 setup_action_addremove_desktop_last
495 action_remove_desktop
,
496 setup_action_addremove_desktop_last
501 setup_action_addremove_desktop_current
504 "removedesktopcurrent",
505 action_remove_desktop
,
506 setup_action_addremove_desktop_current
515 /* only key bindings can be interactive. thus saith the xor.
516 because of how the mouse is grabbed, mouse events dont even get
517 read during interactive events, so no dice! >:) */
518 #define INTERACTIVE_LIMIT(a, uact) \
519 if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
520 a->data.any.interactive = FALSE;
522 ObAction
*action_from_string(const gchar
*name
, ObUserAction uact
)
525 gboolean exist
= FALSE
;
528 for (i
= 0; actionstrings
[i
].name
; i
++)
529 if (!g_ascii_strcasecmp(name
, actionstrings
[i
].name
)) {
531 a
= action_new(actionstrings
[i
].func
);
532 if (actionstrings
[i
].setup
)
533 actionstrings
[i
].setup(&a
, uact
);
535 INTERACTIVE_LIMIT(a
, uact
);
539 g_message(_("Invalid action '%s' requested. No such action exists."),
542 g_message(_("Invalid use of action '%s'. Action will be ignored."),
547 ObAction
*action_parse(ObParseInst
*i
, xmlDocPtr doc
, xmlNodePtr node
,
551 ObAction
*act
= NULL
;
554 if (parse_attr_string("name", node
, &actname
)) {
555 if ((act
= action_from_string(actname
, uact
))) {
556 } else if (act
->func
== action_resize_relative
) {
557 if ((n
= parse_find_node("left", node
->xmlChildrenNode
)))
558 act
->data
.relative
.deltaxl
= parse_int(doc
, n
);
559 if ((n
= parse_find_node("up", node
->xmlChildrenNode
)))
560 act
->data
.relative
.deltayu
= parse_int(doc
, n
);
561 if ((n
= parse_find_node("right", node
->xmlChildrenNode
)))
562 act
->data
.relative
.deltax
= parse_int(doc
, n
);
563 if ((n
= parse_find_node("down", node
->xmlChildrenNode
)))
564 act
->data
.relative
.deltay
= parse_int(doc
, n
);
565 } else if (act
->func
== action_desktop
) {
566 if ((n
= parse_find_node("desktop", node
->xmlChildrenNode
)))
567 act
->data
.desktop
.desk
= parse_int(doc
, n
);
568 if (act
->data
.desktop
.desk
> 0) act
->data
.desktop
.desk
--;
570 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
571 act->data.desktop.inter.any.interactive =
574 } else if (act
->func
== action_send_to_desktop
) {
575 if ((n
= parse_find_node("desktop", node
->xmlChildrenNode
)))
576 act
->data
.sendto
.desk
= parse_int(doc
, n
);
577 if (act
->data
.sendto
.desk
> 0) act
->data
.sendto
.desk
--;
578 if ((n
= parse_find_node("follow", node
->xmlChildrenNode
)))
579 act
->data
.sendto
.follow
= parse_bool(doc
, n
);
580 } else if (act
->func
== action_send_to_desktop_dir
) {
581 if ((n
= parse_find_node("wrap", node
->xmlChildrenNode
)))
582 act
->data
.sendtodir
.wrap
= parse_bool(doc
, n
);
583 if ((n
= parse_find_node("follow", node
->xmlChildrenNode
)))
584 act
->data
.sendtodir
.follow
= parse_bool(doc
, n
);
585 if ((n
= parse_find_node("dialog", node
->xmlChildrenNode
)))
586 act
->data
.sendtodir
.inter
.any
.interactive
=
588 INTERACTIVE_LIMIT(act
, uact
);
595 void action_run_list(GSList
*acts
, ObClient
*c
, ObFrameContext context
,
596 guint state
, guint button
, gint x
, gint y
, Time time
,
597 gboolean cancel
, gboolean done
)
606 screen_pointer_pos(&x
, &y
);
608 for (it
= acts
; it
; it
= g_slist_next(it
)) {
611 if (!(a
->data
.any
.client_action
== OB_CLIENT_ACTION_ALWAYS
&& !c
)) {
612 a
->data
.any
.c
= a
->data
.any
.client_action
? c
: NULL
;
613 a
->data
.any
.context
= context
;
617 a
->data
.any
.button
= button
;
619 a
->data
.any
.time
= time
;
621 if (a
->data
.any
.interactive
) {
622 a
->data
.inter
.cancel
= cancel
;
623 a
->data
.inter
.final
= done
;
624 if (!(cancel
|| done
))
625 if (!keyboard_interactive_grab(state
, a
->data
.any
.c
, a
))
629 /* XXX UGLY HACK race with motion event starting a move and the
630 button release gettnig processed first. answer: don't queue
631 moveresize starts. UGLY HACK XXX
633 XXX ALSO don't queue showmenu events, because on button press
634 events we need to know if a mouse grab is going to take place,
635 and set the button to 0, so that later motion events don't think
636 that a drag is going on. since showmenu grabs the pointer..
638 if (a
->data
.any
.interactive
|| a
->func
== action_move
||
639 a
->func
== action_resize
|| a
->func
== action_showmenu
)
641 /* interactive actions are not queued */
643 } else if (a
->func
== action_focus
||
644 a
->func
== action_activate
||
645 a
->func
== action_showmenu
)
647 /* XXX MORE UGLY HACK
648 actions from clicks on client windows are NOT queued.
649 this solves the mysterious click-and-drag-doesnt-work
650 problem. it was because the window gets focused and stuff
651 after the button event has already been passed through. i
652 dont really know why it should care but it does and it makes
655 however this very bogus ! !
656 we want to send the button press to the window BEFORE
657 we do the action because the action might move the windows
658 (eg change desktops) and then the button press ends up on
659 the completely wrong window !
660 so, this is just for that bug, and it will only NOT queue it
661 if it is a focusing action that can be used with the mouse
664 also with the menus, there is a race going on. if the
665 desktop wants to pop up a menu, and we do too, we send them
666 the button before we pop up the menu, so they pop up their
667 menu first. but not always. if we pop up our menu before
668 sending them the button press, then the result is
671 XXX further more. focus actions are not queued at all,
672 because if you bind focus->showmenu, the menu will get
673 hidden to do the focusing
677 ob_main_loop_queue_action(ob_main_loop
, a
);
682 void action_run_string(const gchar
*name
, struct _ObClient
*c
, Time time
)
687 a
= action_from_string(name
, OB_USER_ACTION_NONE
);
690 l
= g_slist_append(NULL
, a
);
692 action_run(l
, c
, 0, time
);
695 void action_unshaderaise(union ActionData
*data
)
697 if (data
->client
.any
.c
->shaded
)
698 action_unshade(data
);
703 void action_shadelower(union ActionData
*data
)
705 if (data
->client
.any
.c
->shaded
)
711 void action_resize_relative_horz(union ActionData
*data
)
713 ObClient
*c
= data
->relative
.any
.c
;
714 client_action_start(data
);
716 c
->area
.width
+ data
->relative
.deltax
* c
->size_inc
.width
,
718 client_action_end(data
, FALSE
);
721 void action_resize_relative_vert(union ActionData
*data
)
723 ObClient
*c
= data
->relative
.any
.c
;
725 client_action_start(data
);
726 client_resize(c
, c
->area
.width
, c
->area
.height
+
727 data
->relative
.deltax
* c
->size_inc
.height
);
728 client_action_end(data
, FALSE
);
732 void action_resize_relative(union ActionData
*data
)
734 ObClient
*c
= data
->relative
.any
.c
;
735 gint x
, y
, ow
, xoff
, nw
, oh
, yoff
, nh
, lw
, lh
;
737 client_action_start(data
);
742 xoff
= -data
->relative
.deltaxl
* c
->size_inc
.width
;
743 nw
= ow
+ data
->relative
.deltax
* c
->size_inc
.width
744 + data
->relative
.deltaxl
* c
->size_inc
.width
;
746 yoff
= -data
->relative
.deltayu
* c
->size_inc
.height
;
747 nh
= oh
+ data
->relative
.deltay
* c
->size_inc
.height
748 + data
->relative
.deltayu
* c
->size_inc
.height
;
750 g_print("deltax %d %d x %d ow %d xoff %d nw %d\n",
751 data
->relative
.deltax
,
752 data
->relative
.deltaxl
,
755 client_try_configure(c
, &x
, &y
, &nw
, &nh
, &lw
, &lh
, TRUE
);
756 xoff
= xoff
== 0 ? 0 : (xoff
< 0 ? MAX(xoff
, ow
-nw
) : MIN(xoff
, ow
-nw
));
757 yoff
= yoff
== 0 ? 0 : (yoff
< 0 ? MAX(yoff
, oh
-nh
) : MIN(yoff
, oh
-nh
));
758 client_move_resize(c
, x
+ xoff
, y
+ yoff
, nw
, nh
);
759 client_action_end(data
, FALSE
);
762 void action_send_to_desktop(union ActionData
*data
)
764 ObClient
*c
= data
->sendto
.any
.c
;
766 if (!client_normal(c
)) return;
768 if (data
->sendto
.desk
< screen_num_desktops
||
769 data
->sendto
.desk
== DESKTOP_ALL
) {
770 client_set_desktop(c
, data
->sendto
.desk
, data
->sendto
.follow
, FALSE
);
771 if (data
->sendto
.follow
&& data
->sendto
.desk
!= screen_desktop
)
772 screen_set_desktop(data
->sendto
.desk
, TRUE
);
776 void action_send_to_desktop_dir(union ActionData
*data
)
778 ObClient
*c
= data
->sendtodir
.inter
.any
.c
;
781 if (!client_normal(c
)) return;
783 d
= screen_cycle_desktop(data
->sendtodir
.dir
, data
->sendtodir
.wrap
,
784 data
->sendtodir
.linear
,
785 data
->sendtodir
.inter
.any
.interactive
,
786 data
->sendtodir
.inter
.final
,
787 data
->sendtodir
.inter
.cancel
);
788 /* only move the desktop when the action is complete. if we switch
789 desktops during the interactive action, focus will move but with
790 NotifyWhileGrabbed and applications don't like that. */
791 if (!data
->sendtodir
.inter
.any
.interactive
||
792 (data
->sendtodir
.inter
.final
&& !data
->sendtodir
.inter
.cancel
))
794 client_set_desktop(c
, d
, data
->sendtodir
.follow
, FALSE
);
795 if (data
->sendtodir
.follow
&& d
!= screen_desktop
)
796 screen_set_desktop(d
, TRUE
);
800 void action_desktop_last(union ActionData
*data
)
802 if (screen_last_desktop
< screen_num_desktops
)
803 screen_set_desktop(screen_last_desktop
, TRUE
);
806 void action_directional_focus(union ActionData
*data
)
808 /* if using focus_delay, stop the timer now so that focus doesn't go moving
810 event_halt_focus_delay();
812 focus_directional_cycle(data
->interdiraction
.direction
,
813 data
->interdiraction
.dock_windows
,
814 data
->interdiraction
.desktop_windows
,
815 data
->any
.interactive
,
816 data
->interdiraction
.dialog
,
817 data
->interdiraction
.inter
.final
,
818 data
->interdiraction
.inter
.cancel
);
821 void action_movetoedge(union ActionData
*data
)
824 ObClient
*c
= data
->diraction
.any
.c
;
826 x
= c
->frame
->area
.x
;
827 y
= c
->frame
->area
.y
;
829 switch(data
->diraction
.direction
) {
830 case OB_DIRECTION_NORTH
:
831 y
= client_directional_edge_search(c
, OB_DIRECTION_NORTH
,
832 data
->diraction
.hang
)
833 - (data
->diraction
.hang
? c
->frame
->area
.height
: 0);
835 case OB_DIRECTION_WEST
:
836 x
= client_directional_edge_search(c
, OB_DIRECTION_WEST
,
837 data
->diraction
.hang
)
838 - (data
->diraction
.hang
? c
->frame
->area
.width
: 0);
840 case OB_DIRECTION_SOUTH
:
841 y
= client_directional_edge_search(c
, OB_DIRECTION_SOUTH
,
842 data
->diraction
.hang
)
843 - (data
->diraction
.hang
? 0 : c
->frame
->area
.height
);
845 case OB_DIRECTION_EAST
:
846 x
= client_directional_edge_search(c
, OB_DIRECTION_EAST
,
847 data
->diraction
.hang
)
848 - (data
->diraction
.hang
? 0 : c
->frame
->area
.width
);
851 g_assert_not_reached();
853 frame_frame_gravity(c
->frame
, &x
, &y
, c
->area
.width
, c
->area
.height
);
854 client_action_start(data
);
855 client_move(c
, x
, y
);
856 client_action_end(data
, FALSE
);
859 void action_growtoedge(union ActionData
*data
)
861 gint x
, y
, width
, height
, dest
;
862 ObClient
*c
= data
->diraction
.any
.c
;
865 a
= screen_area(c
->desktop
, SCREEN_AREA_ALL_MONITORS
, &c
->frame
->area
);
866 x
= c
->frame
->area
.x
;
867 y
= c
->frame
->area
.y
;
868 /* get the unshaded frame's dimensions..if it is shaded */
869 width
= c
->area
.width
+ c
->frame
->size
.left
+ c
->frame
->size
.right
;
870 height
= c
->area
.height
+ c
->frame
->size
.top
+ c
->frame
->size
.bottom
;
872 switch(data
->diraction
.direction
) {
873 case OB_DIRECTION_NORTH
:
874 if (c
->shaded
) break; /* don't allow vertical resize if shaded */
876 dest
= client_directional_edge_search(c
, OB_DIRECTION_NORTH
, FALSE
);
880 height
= c
->frame
->area
.y
+ height
- dest
;
884 case OB_DIRECTION_WEST
:
885 dest
= client_directional_edge_search(c
, OB_DIRECTION_WEST
, FALSE
);
889 width
= c
->frame
->area
.x
+ width
- dest
;
893 case OB_DIRECTION_SOUTH
:
894 if (c
->shaded
) break; /* don't allow vertical resize if shaded */
896 dest
= client_directional_edge_search(c
, OB_DIRECTION_SOUTH
, FALSE
);
897 if (a
->y
+ a
->height
== y
+ c
->frame
->area
.height
) {
898 height
= c
->frame
->area
.height
/ 2;
899 y
= a
->y
+ a
->height
- height
;
901 height
= dest
- c
->frame
->area
.y
;
902 y
+= (height
- c
->frame
->area
.height
) % c
->size_inc
.height
;
903 height
-= (height
- c
->frame
->area
.height
) % c
->size_inc
.height
;
905 case OB_DIRECTION_EAST
:
906 dest
= client_directional_edge_search(c
, OB_DIRECTION_EAST
, FALSE
);
907 if (a
->x
+ a
->width
== x
+ c
->frame
->area
.width
) {
908 width
= c
->frame
->area
.width
/ 2;
909 x
= a
->x
+ a
->width
- width
;
911 width
= dest
- c
->frame
->area
.x
;
912 x
+= (width
- c
->frame
->area
.width
) % c
->size_inc
.width
;
913 width
-= (width
- c
->frame
->area
.width
) % c
->size_inc
.width
;
916 g_assert_not_reached();
918 width
-= c
->frame
->size
.left
+ c
->frame
->size
.right
;
919 height
-= c
->frame
->size
.top
+ c
->frame
->size
.bottom
;
920 frame_frame_gravity(c
->frame
, &x
, &y
, width
, height
);
921 client_action_start(data
);
922 client_move_resize(c
, x
, y
, width
, height
);
923 client_action_end(data
, FALSE
);
927 void action_send_to_layer(union ActionData
*data
)
929 client_set_layer(data
->layer
.any
.c
, data
->layer
.layer
);
932 void action_toggle_layer(union ActionData
*data
)
934 ObClient
*c
= data
->layer
.any
.c
;
936 client_action_start(data
);
937 if (data
->layer
.layer
< 0)
938 client_set_layer(c
, c
->below
? 0 : -1);
939 else if (data
->layer
.layer
> 0)
940 client_set_layer(c
, c
->above
? 0 : 1);
941 client_action_end(data
, config_focus_under_mouse
);
944 void action_toggle_dockautohide(union ActionData
*data
)
946 config_dock_hide
= !config_dock_hide
;
950 void action_add_desktop(union ActionData
*data
)
952 client_action_start(data
);
953 screen_set_num_desktops(screen_num_desktops
+1);
955 /* move all the clients over */
956 if (data
->addremovedesktop
.current
) {
959 for (it
= client_list
; it
; it
= g_list_next(it
)) {
960 ObClient
*c
= it
->data
;
961 if (c
->desktop
!= DESKTOP_ALL
&& c
->desktop
>= screen_desktop
)
962 client_set_desktop(c
, c
->desktop
+1, FALSE
, TRUE
);
966 client_action_end(data
, config_focus_under_mouse
);
969 void action_remove_desktop(union ActionData
*data
)
971 guint rmdesktop
, movedesktop
;
972 GList
*it
, *stacking_copy
;
974 if (screen_num_desktops
< 2) return;
976 client_action_start(data
);
978 /* what desktop are we removing and moving to? */
979 if (data
->addremovedesktop
.current
)
980 rmdesktop
= screen_desktop
;
982 rmdesktop
= screen_num_desktops
- 1;
983 if (rmdesktop
< screen_num_desktops
- 1)
984 movedesktop
= rmdesktop
+ 1;
986 movedesktop
= rmdesktop
;
988 /* make a copy of the list cuz we're changing it */
989 stacking_copy
= g_list_copy(stacking_list
);
990 for (it
= g_list_last(stacking_copy
); it
; it
= g_list_previous(it
)) {
991 if (WINDOW_IS_CLIENT(it
->data
)) {
992 ObClient
*c
= it
->data
;
993 guint d
= c
->desktop
;
994 if (d
!= DESKTOP_ALL
&& d
>= movedesktop
) {
995 client_set_desktop(c
, c
->desktop
- 1, TRUE
, TRUE
);
996 ob_debug("moving window %s\n", c
->title
);
998 /* raise all the windows that are on the current desktop which
1000 if ((screen_desktop
== rmdesktop
- 1 ||
1001 screen_desktop
== rmdesktop
) &&
1002 (d
== DESKTOP_ALL
|| d
== screen_desktop
))
1004 stacking_raise(CLIENT_AS_WINDOW(c
));
1005 ob_debug("raising window %s\n", c
->title
);
1010 /* act like we're changing desktops */
1011 if (screen_desktop
< screen_num_desktops
- 1) {
1012 gint d
= screen_desktop
;
1013 screen_desktop
= screen_last_desktop
;
1014 screen_set_desktop(d
, TRUE
);
1015 ob_debug("fake desktop change\n");
1018 screen_set_num_desktops(screen_num_desktops
-1);
1020 client_action_end(data
, config_focus_under_mouse
);