]> Dogcows Code - chaz/openbox/blob - openbox/action.c
add resizerelative action
[chaz/openbox] / 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
43
44 typedef struct
45 {
46 const gchar *name;
47 void (*func)(union ActionData *);
48 void (*setup)(ObAction **, ObUserAction uact);
49 } ActionString;
50
51 static ObAction *action_new(void (*func)(union ActionData *data))
52 {
53 ObAction *a = g_new0(ObAction, 1);
54 a->ref = 1;
55 a->func = func;
56
57 return a;
58 }
59
60 void action_ref(ObAction *a)
61 {
62 ++a->ref;
63 }
64
65 void action_unref(ObAction *a)
66 {
67 if (a == NULL) return;
68
69 if (--a->ref > 0) return;
70
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);
78
79 g_free(a);
80 }
81
82 ObAction* action_copy(const ObAction *src)
83 {
84 ObAction *a = action_new(src->func);
85
86 a->data = src->data;
87
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);
95
96 return a;
97 }
98
99 void setup_action_send_to_desktop(ObAction **a, ObUserAction uact)
100 {
101 (*a)->data.sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS;
102 (*a)->data.sendto.follow = TRUE;
103 }
104
105 void setup_action_send_to_desktop_prev(ObAction **a, ObUserAction uact)
106 {
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;
113 }
114
115 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
116 {
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;
123 }
124
125 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
126 {
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;
133 }
134
135 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
136 {
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;
143 }
144
145 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
146 {
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;
153 }
154
155 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
156 {
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;
163 }
164
165 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
166 {
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;
171 }
172
173 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
174 {
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;
179 }
180
181 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
182 {
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;
187 }
188
189 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
190 {
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;
195 }
196
197 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
198 {
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;
203 }
204
205 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
206 {
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;
211 }
212
213 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
214 {
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;
218 }
219
220 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
221 {
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;
225 }
226
227 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
228 {
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;
232 }
233
234 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
235 {
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;
239 }
240
241 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
242 {
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;
246 }
247
248 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
249 {
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;
253 }
254
255 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
256 {
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;
260 }
261
262 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
263 {
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;
267 }
268
269 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
270 {
271 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
272 (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
273 }
274
275 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
276 {
277 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
278 (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
279 }
280
281 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
282 {
283 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
284 (*a)->data.diraction.direction = OB_DIRECTION_EAST;
285 }
286
287 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
288 {
289 (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
290 (*a)->data.diraction.direction = OB_DIRECTION_WEST;
291 }
292
293 void setup_action_top_layer(ObAction **a, ObUserAction uact)
294 {
295 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
296 (*a)->data.layer.layer = 1;
297 }
298
299 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
300 {
301 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
302 (*a)->data.layer.layer = 0;
303 }
304
305 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
306 {
307 (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
308 (*a)->data.layer.layer = -1;
309 }
310
311 void setup_action_resize(ObAction **a, ObUserAction uact)
312 {
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;
319 }
320
321 void setup_action_addremove_desktop_current(ObAction **a, ObUserAction uact)
322 {
323 (*a)->data.addremovedesktop.current = TRUE;
324 }
325
326 void setup_action_addremove_desktop_last(ObAction **a, ObUserAction uact)
327 {
328 (*a)->data.addremovedesktop.current = FALSE;
329 }
330
331 void setup_client_action(ObAction **a, ObUserAction uact)
332 {
333 (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
334 }
335
336 ActionString actionstrings[] =
337 {
338 {
339 "shadelower",
340 action_shadelower,
341 setup_client_action
342 },
343 {
344 "unshaderaise",
345 action_unshaderaise,
346 setup_client_action
347 },
348 {
349 "sendtodesktop",
350 action_send_to_desktop,
351 setup_action_send_to_desktop
352 },
353 {
354 "sendtodesktopnext",
355 action_send_to_desktop_dir,
356 setup_action_send_to_desktop_next
357 },
358 {
359 "sendtodesktopprevious",
360 action_send_to_desktop_dir,
361 setup_action_send_to_desktop_prev
362 },
363 {
364 "sendtodesktopright",
365 action_send_to_desktop_dir,
366 setup_action_send_to_desktop_right
367 },
368 {
369 "sendtodesktopleft",
370 action_send_to_desktop_dir,
371 setup_action_send_to_desktop_left
372 },
373 {
374 "sendtodesktopup",
375 action_send_to_desktop_dir,
376 setup_action_send_to_desktop_up
377 },
378 {
379 "sendtodesktopdown",
380 action_send_to_desktop_dir,
381 setup_action_send_to_desktop_down
382 },
383 {
384 "toggledockautohide",
385 action_toggle_dockautohide,
386 NULL
387 },
388 {
389 "sendtotoplayer",
390 action_send_to_layer,
391 setup_action_top_layer
392 },
393 {
394 "togglealwaysontop",
395 action_toggle_layer,
396 setup_action_top_layer
397 },
398 {
399 "sendtonormallayer",
400 action_send_to_layer,
401 setup_action_normal_layer
402 },
403 {
404 "sendtobottomlayer",
405 action_send_to_layer,
406 setup_action_bottom_layer
407 },
408 {
409 "togglealwaysonbottom",
410 action_toggle_layer,
411 setup_action_bottom_layer
412 },
413 {
414 "movefromedgenorth",
415 action_movetoedge,
416 setup_action_movefromedge_north
417 },
418 {
419 "movefromedgesouth",
420 action_movetoedge,
421 setup_action_movefromedge_south
422 },
423 {
424 "movefromedgewest",
425 action_movetoedge,
426 setup_action_movefromedge_west
427 },
428 {
429 "movefromedgeeast",
430 action_movetoedge,
431 setup_action_movefromedge_east
432 },
433 {
434 "movetoedgenorth",
435 action_movetoedge,
436 setup_action_movetoedge_north
437 },
438 {
439 "movetoedgesouth",
440 action_movetoedge,
441 setup_action_movetoedge_south
442 },
443 {
444 "movetoedgewest",
445 action_movetoedge,
446 setup_action_movetoedge_west
447 },
448 {
449 "movetoedgeeast",
450 action_movetoedge,
451 setup_action_movetoedge_east
452 },
453 {
454 "growtoedgenorth",
455 action_growtoedge,
456 setup_action_growtoedge_north
457 },
458 {
459 "growtoedgesouth",
460 action_growtoedge,
461 setup_action_growtoedge_south
462 },
463 {
464 "growtoedgewest",
465 action_growtoedge,
466 setup_action_growtoedge_west
467 },
468 {
469 "growtoedgeeast",
470 action_growtoedge,
471 setup_action_growtoedge_east
472 },
473 {
474 "adddesktoplast",
475 action_add_desktop,
476 setup_action_addremove_desktop_last
477 },
478 {
479 "removedesktoplast",
480 action_remove_desktop,
481 setup_action_addremove_desktop_last
482 },
483 {
484 "adddesktopcurrent",
485 action_add_desktop,
486 setup_action_addremove_desktop_current
487 },
488 {
489 "removedesktopcurrent",
490 action_remove_desktop,
491 setup_action_addremove_desktop_current
492 },
493 {
494 NULL,
495 NULL,
496 NULL
497 }
498 };
499
500 /* only key bindings can be interactive. thus saith the xor.
501 because of how the mouse is grabbed, mouse events dont even get
502 read during interactive events, so no dice! >:) */
503 #define INTERACTIVE_LIMIT(a, uact) \
504 if (uact != OB_USER_ACTION_KEYBOARD_KEY) \
505 a->data.any.interactive = FALSE;
506
507 ObAction *action_from_string(const gchar *name, ObUserAction uact)
508 {
509 ObAction *a = NULL;
510 gboolean exist = FALSE;
511 gint i;
512
513 for (i = 0; actionstrings[i].name; i++)
514 if (!g_ascii_strcasecmp(name, actionstrings[i].name)) {
515 exist = TRUE;
516 a = action_new(actionstrings[i].func);
517 if (actionstrings[i].setup)
518 actionstrings[i].setup(&a, uact);
519 if (a)
520 INTERACTIVE_LIMIT(a, uact);
521 break;
522 }
523 if (!exist)
524 g_message(_("Invalid action '%s' requested. No such action exists."),
525 name);
526 if (!a)
527 g_message(_("Invalid use of action '%s'. Action will be ignored."),
528 name);
529 return a;
530 }
531
532 ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
533 ObUserAction uact)
534 {
535 gchar *actname;
536 ObAction *act = NULL;
537 xmlNodePtr n;
538
539 if (parse_attr_string("name", node, &actname)) {
540 if ((act = action_from_string(actname, uact))) {
541 } else if (act->func == action_desktop) {
542 } else if (act->func == action_send_to_desktop) {
543 if ((n = parse_find_node("desktop", node->xmlChildrenNode)))
544 act->data.sendto.desk = parse_int(doc, n);
545 if (act->data.sendto.desk > 0) act->data.sendto.desk--;
546 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
547 act->data.sendto.follow = parse_bool(doc, n);
548 } else if (act->func == action_send_to_desktop_dir) {
549 if ((n = parse_find_node("wrap", node->xmlChildrenNode)))
550 act->data.sendtodir.wrap = parse_bool(doc, n);
551 if ((n = parse_find_node("follow", node->xmlChildrenNode)))
552 act->data.sendtodir.follow = parse_bool(doc, n);
553 if ((n = parse_find_node("dialog", node->xmlChildrenNode)))
554 act->data.sendtodir.inter.any.interactive =
555 parse_bool(doc, n);
556 INTERACTIVE_LIMIT(act, uact);
557 }
558 g_free(actname);
559 }
560 return act;
561 }
562
563 void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
564 guint state, guint button, gint x, gint y, Time time,
565 gboolean cancel, gboolean done)
566 {
567 GSList *it;
568 ObAction *a;
569
570 if (!acts)
571 return;
572
573 if (x < 0 && y < 0)
574 screen_pointer_pos(&x, &y);
575
576 for (it = acts; it; it = g_slist_next(it)) {
577 a = it->data;
578
579 if (!(a->data.any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) {
580 a->data.any.c = a->data.any.client_action ? c : NULL;
581 a->data.any.context = context;
582 a->data.any.x = x;
583 a->data.any.y = y;
584
585 a->data.any.button = button;
586
587 a->data.any.time = time;
588
589 if (a->data.any.interactive) {
590 a->data.inter.cancel = cancel;
591 a->data.inter.final = done;
592 if (!(cancel || done))
593 if (!keyboard_interactive_grab(state, a->data.any.c, a))
594 continue;
595 }
596
597 /* XXX UGLY HACK race with motion event starting a move and the
598 button release gettnig processed first. answer: don't queue
599 moveresize starts. UGLY HACK XXX
600
601 XXX ALSO don't queue showmenu events, because on button press
602 events we need to know if a mouse grab is going to take place,
603 and set the button to 0, so that later motion events don't think
604 that a drag is going on. since showmenu grabs the pointer..
605 */
606 if (a->data.any.interactive || a->func == action_move ||
607 a->func == action_resize || a->func == action_showmenu)
608 {
609 /* interactive actions are not queued */
610 a->func(&a->data);
611 } else if (a->func == action_focus ||
612 a->func == action_activate ||
613 a->func == action_showmenu)
614 {
615 /* XXX MORE UGLY HACK
616 actions from clicks on client windows are NOT queued.
617 this solves the mysterious click-and-drag-doesnt-work
618 problem. it was because the window gets focused and stuff
619 after the button event has already been passed through. i
620 dont really know why it should care but it does and it makes
621 a difference.
622
623 however this very bogus ! !
624 we want to send the button press to the window BEFORE
625 we do the action because the action might move the windows
626 (eg change desktops) and then the button press ends up on
627 the completely wrong window !
628 so, this is just for that bug, and it will only NOT queue it
629 if it is a focusing action that can be used with the mouse
630 pointer. ugh.
631
632 also with the menus, there is a race going on. if the
633 desktop wants to pop up a menu, and we do too, we send them
634 the button before we pop up the menu, so they pop up their
635 menu first. but not always. if we pop up our menu before
636 sending them the button press, then the result is
637 deterministic. yay.
638
639 XXX further more. focus actions are not queued at all,
640 because if you bind focus->showmenu, the menu will get
641 hidden to do the focusing
642 */
643 a->func(&a->data);
644 } else
645 ob_main_loop_queue_action(ob_main_loop, a);
646 }
647 }
648 }
649
650 void action_run_string(const gchar *name, struct _ObClient *c, Time time)
651 {
652 ObAction *a;
653 GSList *l;
654
655 a = action_from_string(name, OB_USER_ACTION_NONE);
656 g_assert(a);
657
658 l = g_slist_append(NULL, a);
659
660 action_run(l, c, 0, time);
661 }
662
663 void action_unshaderaise(union ActionData *data)
664 {
665 if (data->client.any.c->shaded)
666 action_unshade(data);
667 else
668 action_raise(data);
669 }
670
671 void action_shadelower(union ActionData *data)
672 {
673 if (data->client.any.c->shaded)
674 action_lower(data);
675 else
676 action_shade(data);
677 }
678
679 void action_resize_relative(union ActionData *data)
680 {
681 }
682
683 void action_send_to_desktop(union ActionData *data)
684 {
685 ObClient *c = data->sendto.any.c;
686
687 if (!client_normal(c)) return;
688
689 if (data->sendto.desk < screen_num_desktops ||
690 data->sendto.desk == DESKTOP_ALL) {
691 client_set_desktop(c, data->sendto.desk, data->sendto.follow, FALSE);
692 if (data->sendto.follow && data->sendto.desk != screen_desktop)
693 screen_set_desktop(data->sendto.desk, TRUE);
694 }
695 }
696
697 void action_send_to_desktop_dir(union ActionData *data)
698 {
699 ObClient *c = data->sendtodir.inter.any.c;
700 guint d;
701
702 if (!client_normal(c)) return;
703
704 d = screen_cycle_desktop(data->sendtodir.dir, data->sendtodir.wrap,
705 data->sendtodir.linear,
706 data->sendtodir.inter.any.interactive,
707 data->sendtodir.inter.final,
708 data->sendtodir.inter.cancel);
709 /* only move the desktop when the action is complete. if we switch
710 desktops during the interactive action, focus will move but with
711 NotifyWhileGrabbed and applications don't like that. */
712 if (!data->sendtodir.inter.any.interactive ||
713 (data->sendtodir.inter.final && !data->sendtodir.inter.cancel))
714 {
715 client_set_desktop(c, d, data->sendtodir.follow, FALSE);
716 if (data->sendtodir.follow && d != screen_desktop)
717 screen_set_desktop(d, TRUE);
718 }
719 }
720
721 void action_directional_focus(union ActionData *data)
722 {
723 /* if using focus_delay, stop the timer now so that focus doesn't go moving
724 on us */
725 event_halt_focus_delay();
726
727 focus_directional_cycle(data->interdiraction.direction,
728 data->interdiraction.dock_windows,
729 data->interdiraction.desktop_windows,
730 data->any.interactive,
731 data->interdiraction.dialog,
732 data->interdiraction.inter.final,
733 data->interdiraction.inter.cancel);
734 }
735
736 void action_movetoedge(union ActionData *data)
737 {
738 gint x, y;
739 ObClient *c = data->diraction.any.c;
740
741 x = c->frame->area.x;
742 y = c->frame->area.y;
743
744 switch(data->diraction.direction) {
745 case OB_DIRECTION_NORTH:
746 y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
747 data->diraction.hang)
748 - (data->diraction.hang ? c->frame->area.height : 0);
749 break;
750 case OB_DIRECTION_WEST:
751 x = client_directional_edge_search(c, OB_DIRECTION_WEST,
752 data->diraction.hang)
753 - (data->diraction.hang ? c->frame->area.width : 0);
754 break;
755 case OB_DIRECTION_SOUTH:
756 y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
757 data->diraction.hang)
758 - (data->diraction.hang ? 0 : c->frame->area.height);
759 break;
760 case OB_DIRECTION_EAST:
761 x = client_directional_edge_search(c, OB_DIRECTION_EAST,
762 data->diraction.hang)
763 - (data->diraction.hang ? 0 : c->frame->area.width);
764 break;
765 default:
766 g_assert_not_reached();
767 }
768 frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
769 client_action_start(data);
770 client_move(c, x, y);
771 client_action_end(data, FALSE);
772 }
773
774 void action_growtoedge(union ActionData *data)
775 {
776 gint x, y, width, height, dest;
777 ObClient *c = data->diraction.any.c;
778 Rect *a;
779
780 a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
781 x = c->frame->area.x;
782 y = c->frame->area.y;
783 /* get the unshaded frame's dimensions..if it is shaded */
784 width = c->area.width + c->frame->size.left + c->frame->size.right;
785 height = c->area.height + c->frame->size.top + c->frame->size.bottom;
786
787 switch(data->diraction.direction) {
788 case OB_DIRECTION_NORTH:
789 if (c->shaded) break; /* don't allow vertical resize if shaded */
790
791 dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
792 if (a->y == y)
793 height = height / 2;
794 else {
795 height = c->frame->area.y + height - dest;
796 y = dest;
797 }
798 break;
799 case OB_DIRECTION_WEST:
800 dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
801 if (a->x == x)
802 width = width / 2;
803 else {
804 width = c->frame->area.x + width - dest;
805 x = dest;
806 }
807 break;
808 case OB_DIRECTION_SOUTH:
809 if (c->shaded) break; /* don't allow vertical resize if shaded */
810
811 dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
812 if (a->y + a->height == y + c->frame->area.height) {
813 height = c->frame->area.height / 2;
814 y = a->y + a->height - height;
815 } else
816 height = dest - c->frame->area.y;
817 y += (height - c->frame->area.height) % c->size_inc.height;
818 height -= (height - c->frame->area.height) % c->size_inc.height;
819 break;
820 case OB_DIRECTION_EAST:
821 dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
822 if (a->x + a->width == x + c->frame->area.width) {
823 width = c->frame->area.width / 2;
824 x = a->x + a->width - width;
825 } else
826 width = dest - c->frame->area.x;
827 x += (width - c->frame->area.width) % c->size_inc.width;
828 width -= (width - c->frame->area.width) % c->size_inc.width;
829 break;
830 default:
831 g_assert_not_reached();
832 }
833 width -= c->frame->size.left + c->frame->size.right;
834 height -= c->frame->size.top + c->frame->size.bottom;
835 frame_frame_gravity(c->frame, &x, &y, width, height);
836 client_action_start(data);
837 client_move_resize(c, x, y, width, height);
838 client_action_end(data, FALSE);
839 g_free(a);
840 }
841
842 void action_send_to_layer(union ActionData *data)
843 {
844 client_set_layer(data->layer.any.c, data->layer.layer);
845 }
846
847 void action_toggle_layer(union ActionData *data)
848 {
849 ObClient *c = data->layer.any.c;
850
851 client_action_start(data);
852 if (data->layer.layer < 0)
853 client_set_layer(c, c->below ? 0 : -1);
854 else if (data->layer.layer > 0)
855 client_set_layer(c, c->above ? 0 : 1);
856 client_action_end(data, config_focus_under_mouse);
857 }
858
859 void action_toggle_dockautohide(union ActionData *data)
860 {
861 config_dock_hide = !config_dock_hide;
862 dock_configure();
863 }
864
865 void action_add_desktop(union ActionData *data)
866 {
867 client_action_start(data);
868 screen_set_num_desktops(screen_num_desktops+1);
869
870 /* move all the clients over */
871 if (data->addremovedesktop.current) {
872 GList *it;
873
874 for (it = client_list; it; it = g_list_next(it)) {
875 ObClient *c = it->data;
876 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop)
877 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
878 }
879 }
880
881 client_action_end(data, config_focus_under_mouse);
882 }
883
884 void action_remove_desktop(union ActionData *data)
885 {
886 guint rmdesktop, movedesktop;
887 GList *it, *stacking_copy;
888
889 if (screen_num_desktops < 2) return;
890
891 client_action_start(data);
892
893 /* what desktop are we removing and moving to? */
894 if (data->addremovedesktop.current)
895 rmdesktop = screen_desktop;
896 else
897 rmdesktop = screen_num_desktops - 1;
898 if (rmdesktop < screen_num_desktops - 1)
899 movedesktop = rmdesktop + 1;
900 else
901 movedesktop = rmdesktop;
902
903 /* make a copy of the list cuz we're changing it */
904 stacking_copy = g_list_copy(stacking_list);
905 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
906 if (WINDOW_IS_CLIENT(it->data)) {
907 ObClient *c = it->data;
908 guint d = c->desktop;
909 if (d != DESKTOP_ALL && d >= movedesktop) {
910 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
911 ob_debug("moving window %s\n", c->title);
912 }
913 /* raise all the windows that are on the current desktop which
914 is being merged */
915 if ((screen_desktop == rmdesktop - 1 ||
916 screen_desktop == rmdesktop) &&
917 (d == DESKTOP_ALL || d == screen_desktop))
918 {
919 stacking_raise(CLIENT_AS_WINDOW(c));
920 ob_debug("raising window %s\n", c->title);
921 }
922 }
923 }
924
925 /* act like we're changing desktops */
926 if (screen_desktop < screen_num_desktops - 1) {
927 gint d = screen_desktop;
928 screen_desktop = screen_last_desktop;
929 screen_set_desktop(d, TRUE);
930 ob_debug("fake desktop change\n");
931 }
932
933 screen_set_num_desktops(screen_num_desktops-1);
934
935 client_action_end(data, config_focus_under_mouse);
936 }
This page took 0.080881 seconds and 4 git commands to generate.