add the adddesktop and removedesktop actions
[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
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_prev(ObAction **a, ObUserAction uact)
100 {
101     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
102     (*a)->data.sendtodir.inter.any.interactive = TRUE;
103     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
104     (*a)->data.sendtodir.linear = TRUE;
105     (*a)->data.sendtodir.wrap = TRUE;
106     (*a)->data.sendtodir.follow = TRUE;
107 }
108
109 void setup_action_send_to_desktop_next(ObAction **a, ObUserAction uact)
110 {
111     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
112     (*a)->data.sendtodir.inter.any.interactive = TRUE;
113     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
114     (*a)->data.sendtodir.linear = TRUE;
115     (*a)->data.sendtodir.wrap = TRUE;
116     (*a)->data.sendtodir.follow = TRUE;
117 }
118
119 void setup_action_send_to_desktop_left(ObAction **a, ObUserAction uact)
120 {
121     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
122     (*a)->data.sendtodir.inter.any.interactive = TRUE;
123     (*a)->data.sendtodir.dir = OB_DIRECTION_WEST;
124     (*a)->data.sendtodir.linear = FALSE;
125     (*a)->data.sendtodir.wrap = TRUE;
126     (*a)->data.sendtodir.follow = TRUE;
127 }
128
129 void setup_action_send_to_desktop_right(ObAction **a, ObUserAction uact)
130 {
131     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
132     (*a)->data.sendtodir.inter.any.interactive = TRUE;
133     (*a)->data.sendtodir.dir = OB_DIRECTION_EAST;
134     (*a)->data.sendtodir.linear = FALSE;
135     (*a)->data.sendtodir.wrap = TRUE;
136     (*a)->data.sendtodir.follow = TRUE;
137 }
138
139 void setup_action_send_to_desktop_up(ObAction **a, ObUserAction uact)
140 {
141     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
142     (*a)->data.sendtodir.inter.any.interactive = TRUE;
143     (*a)->data.sendtodir.dir = OB_DIRECTION_NORTH;
144     (*a)->data.sendtodir.linear = FALSE;
145     (*a)->data.sendtodir.wrap = TRUE;
146     (*a)->data.sendtodir.follow = TRUE;
147 }
148
149 void setup_action_send_to_desktop_down(ObAction **a, ObUserAction uact)
150 {
151     (*a)->data.sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS;
152     (*a)->data.sendtodir.inter.any.interactive = TRUE;
153     (*a)->data.sendtodir.dir = OB_DIRECTION_SOUTH;
154     (*a)->data.sendtodir.linear = FALSE;
155     (*a)->data.sendtodir.wrap = TRUE;
156     (*a)->data.sendtodir.follow = TRUE;
157 }
158
159 void setup_action_desktop_prev(ObAction **a, ObUserAction uact)
160 {
161     (*a)->data.desktopdir.inter.any.interactive = TRUE;
162     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
163     (*a)->data.desktopdir.linear = TRUE;
164     (*a)->data.desktopdir.wrap = TRUE;
165 }
166
167 void setup_action_desktop_next(ObAction **a, ObUserAction uact)
168 {
169     (*a)->data.desktopdir.inter.any.interactive = TRUE;
170     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
171     (*a)->data.desktopdir.linear = TRUE;
172     (*a)->data.desktopdir.wrap = TRUE;
173 }
174
175 void setup_action_desktop_left(ObAction **a, ObUserAction uact)
176 {
177     (*a)->data.desktopdir.inter.any.interactive = TRUE;
178     (*a)->data.desktopdir.dir = OB_DIRECTION_WEST;
179     (*a)->data.desktopdir.linear = FALSE;
180     (*a)->data.desktopdir.wrap = TRUE;
181 }
182
183 void setup_action_desktop_right(ObAction **a, ObUserAction uact)
184 {
185     (*a)->data.desktopdir.inter.any.interactive = TRUE;
186     (*a)->data.desktopdir.dir = OB_DIRECTION_EAST;
187     (*a)->data.desktopdir.linear = FALSE;
188     (*a)->data.desktopdir.wrap = TRUE;
189 }
190
191 void setup_action_desktop_up(ObAction **a, ObUserAction uact)
192 {
193     (*a)->data.desktopdir.inter.any.interactive = TRUE;
194     (*a)->data.desktopdir.dir = OB_DIRECTION_NORTH;
195     (*a)->data.desktopdir.linear = FALSE;
196     (*a)->data.desktopdir.wrap = TRUE;
197 }
198
199 void setup_action_desktop_down(ObAction **a, ObUserAction uact)
200 {
201     (*a)->data.desktopdir.inter.any.interactive = TRUE;
202     (*a)->data.desktopdir.dir = OB_DIRECTION_SOUTH;
203     (*a)->data.desktopdir.linear = FALSE;
204     (*a)->data.desktopdir.wrap = TRUE;
205 }
206
207 void setup_action_movefromedge_north(ObAction **a, ObUserAction uact)
208 {
209     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
210     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
211     (*a)->data.diraction.hang = TRUE;
212 }
213
214 void setup_action_movefromedge_south(ObAction **a, ObUserAction uact)
215 {
216     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
217     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
218     (*a)->data.diraction.hang = TRUE;
219 }
220
221 void setup_action_movefromedge_east(ObAction **a, ObUserAction uact)
222 {
223     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
224     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
225     (*a)->data.diraction.hang = TRUE;
226 }
227
228 void setup_action_movefromedge_west(ObAction **a, ObUserAction uact)
229 {
230     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
231     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
232     (*a)->data.diraction.hang = TRUE;
233 }
234
235 void setup_action_movetoedge_north(ObAction **a, ObUserAction uact)
236 {
237     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
238     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
239     (*a)->data.diraction.hang = FALSE;
240 }
241
242 void setup_action_movetoedge_south(ObAction **a, ObUserAction uact)
243 {
244     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
245     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
246     (*a)->data.diraction.hang = FALSE;
247 }
248
249 void setup_action_movetoedge_east(ObAction **a, ObUserAction uact)
250 {
251     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
252     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
253     (*a)->data.diraction.hang = FALSE;
254 }
255
256 void setup_action_movetoedge_west(ObAction **a, ObUserAction uact)
257 {
258     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
259     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
260     (*a)->data.diraction.hang = FALSE;
261 }
262
263 void setup_action_growtoedge_north(ObAction **a, ObUserAction uact)
264 {
265     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
266     (*a)->data.diraction.direction = OB_DIRECTION_NORTH;
267 }
268
269 void setup_action_growtoedge_south(ObAction **a, ObUserAction uact)
270 {
271     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
272     (*a)->data.diraction.direction = OB_DIRECTION_SOUTH;
273 }
274
275 void setup_action_growtoedge_east(ObAction **a, ObUserAction uact)
276 {
277     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
278     (*a)->data.diraction.direction = OB_DIRECTION_EAST;
279 }
280
281 void setup_action_growtoedge_west(ObAction **a, ObUserAction uact)
282 {
283     (*a)->data.diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS;
284     (*a)->data.diraction.direction = OB_DIRECTION_WEST;
285 }
286
287 void setup_action_top_layer(ObAction **a, ObUserAction uact)
288 {
289     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
290     (*a)->data.layer.layer = 1;
291 }
292
293 void setup_action_normal_layer(ObAction **a, ObUserAction uact)
294 {
295     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
296     (*a)->data.layer.layer = 0;
297 }
298
299 void setup_action_bottom_layer(ObAction **a, ObUserAction uact)
300 {
301     (*a)->data.layer.any.client_action = OB_CLIENT_ACTION_ALWAYS;
302     (*a)->data.layer.layer = -1;
303 }
304
305 void setup_client_action(ObAction **a, ObUserAction uact)
306 {
307     (*a)->data.any.client_action = OB_CLIENT_ACTION_ALWAYS;
308 }
309
310 ActionString actionstrings[] =
311 {
312     {
313         "shadelower",
314         action_shadelower,
315         setup_client_action
316     },
317     {
318         "unshaderaise",
319         action_unshaderaise,
320         setup_client_action
321     },
322     {
323         "sendtodesktopnext",
324         action_send_to_desktop_dir,
325         setup_action_send_to_desktop_next
326     },
327     {
328         "sendtodesktopprevious",
329         action_send_to_desktop_dir,
330         setup_action_send_to_desktop_prev
331     },
332     {
333         "sendtodesktopright",
334         action_send_to_desktop_dir,
335         setup_action_send_to_desktop_right
336     },
337     {
338         "sendtodesktopleft",
339         action_send_to_desktop_dir,
340         setup_action_send_to_desktop_left
341     },
342     {
343         "sendtodesktopup",
344         action_send_to_desktop_dir,
345         setup_action_send_to_desktop_up
346     },
347     {
348         "sendtodesktopdown",
349         action_send_to_desktop_dir,
350         setup_action_send_to_desktop_down
351     },
352     {
353         "toggledockautohide",
354         action_toggle_dockautohide,
355         NULL
356     },
357     {
358         "sendtotoplayer",
359         action_send_to_layer,
360         setup_action_top_layer
361     },
362     {
363         "togglealwaysontop",
364         action_toggle_layer,
365         setup_action_top_layer
366     },
367     {
368         "sendtonormallayer",
369         action_send_to_layer,
370         setup_action_normal_layer
371     },
372     {
373         "sendtobottomlayer",
374         action_send_to_layer,
375         setup_action_bottom_layer
376     },
377     {
378         "togglealwaysonbottom",
379         action_toggle_layer,
380         setup_action_bottom_layer
381     },
382     {
383         "movefromedgenorth",
384         action_movetoedge,
385         setup_action_movefromedge_north
386     },
387     {
388         "movefromedgesouth",
389         action_movetoedge,
390         setup_action_movefromedge_south
391     },
392     {
393         "movefromedgewest",
394         action_movetoedge,
395         setup_action_movefromedge_west
396     },
397     {
398         "movefromedgeeast",
399         action_movetoedge,
400         setup_action_movefromedge_east
401     },
402     {
403         "movetoedgenorth",
404         action_movetoedge,
405         setup_action_movetoedge_north
406     },
407     {
408         "movetoedgesouth",
409         action_movetoedge,
410         setup_action_movetoedge_south
411     },
412     {
413         "movetoedgewest",
414         action_movetoedge,
415         setup_action_movetoedge_west
416     },
417     {
418         "movetoedgeeast",
419         action_movetoedge,
420         setup_action_movetoedge_east
421     },
422     {
423         "growtoedgenorth",
424         action_growtoedge,
425         setup_action_growtoedge_north
426     },
427     {
428         "growtoedgesouth",
429         action_growtoedge,
430         setup_action_growtoedge_south
431     },
432     {
433         "growtoedgewest",
434         action_growtoedge,
435         setup_action_growtoedge_west
436     },
437     {
438         "growtoedgeeast",
439         action_growtoedge,
440         setup_action_growtoedge_east
441     },
442     {
443         NULL,
444         NULL,
445         NULL
446     }
447 };
448
449 void action_unshaderaise(union ActionData *data)
450 {
451     if (data->client.any.c->shaded)
452         action_unshade(data);
453     else
454         action_raise(data);
455 }
456
457 void action_shadelower(union ActionData *data)
458 {
459     if (data->client.any.c->shaded)
460         action_lower(data);
461     else
462         action_shade(data);
463 }
464
465 void action_movetoedge(union ActionData *data)
466 {
467     gint x, y;
468     ObClient *c = data->diraction.any.c;
469
470     x = c->frame->area.x;
471     y = c->frame->area.y;
472     
473     switch(data->diraction.direction) {
474     case OB_DIRECTION_NORTH:
475         y = client_directional_edge_search(c, OB_DIRECTION_NORTH,
476                                            data->diraction.hang)
477             - (data->diraction.hang ? c->frame->area.height : 0);
478         break;
479     case OB_DIRECTION_WEST:
480         x = client_directional_edge_search(c, OB_DIRECTION_WEST,
481                                            data->diraction.hang)
482             - (data->diraction.hang ? c->frame->area.width : 0);
483         break;
484     case OB_DIRECTION_SOUTH:
485         y = client_directional_edge_search(c, OB_DIRECTION_SOUTH,
486                                            data->diraction.hang)
487             - (data->diraction.hang ? 0 : c->frame->area.height);
488         break;
489     case OB_DIRECTION_EAST:
490         x = client_directional_edge_search(c, OB_DIRECTION_EAST,
491                                            data->diraction.hang)
492             - (data->diraction.hang ? 0 : c->frame->area.width);
493         break;
494     default:
495         g_assert_not_reached();
496     }
497     frame_frame_gravity(c->frame, &x, &y, c->area.width, c->area.height);
498     client_action_start(data);
499     client_move(c, x, y);
500     client_action_end(data, FALSE);
501 }
502
503 void action_growtoedge(union ActionData *data)
504 {
505     gint x, y, width, height, dest;
506     ObClient *c = data->diraction.any.c;
507     Rect *a;
508
509     a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area);
510     x = c->frame->area.x;
511     y = c->frame->area.y;
512     /* get the unshaded frame's dimensions..if it is shaded */
513     width = c->area.width + c->frame->size.left + c->frame->size.right;
514     height = c->area.height + c->frame->size.top + c->frame->size.bottom;
515
516     switch(data->diraction.direction) {
517     case OB_DIRECTION_NORTH:
518         if (c->shaded) break; /* don't allow vertical resize if shaded */
519
520         dest = client_directional_edge_search(c, OB_DIRECTION_NORTH, FALSE);
521         if (a->y == y)
522             height = height / 2;
523         else {
524             height = c->frame->area.y + height - dest;
525             y = dest;
526         }
527         break;
528     case OB_DIRECTION_WEST:
529         dest = client_directional_edge_search(c, OB_DIRECTION_WEST, FALSE);
530         if (a->x == x)
531             width = width / 2;
532         else {
533             width = c->frame->area.x + width - dest;
534             x = dest;
535         }
536         break;
537     case OB_DIRECTION_SOUTH:
538         if (c->shaded) break; /* don't allow vertical resize if shaded */
539
540         dest = client_directional_edge_search(c, OB_DIRECTION_SOUTH, FALSE);
541         if (a->y + a->height == y + c->frame->area.height) {
542             height = c->frame->area.height / 2;
543             y = a->y + a->height - height;
544         } else
545             height = dest - c->frame->area.y;
546         y += (height - c->frame->area.height) % c->size_inc.height;
547         height -= (height - c->frame->area.height) % c->size_inc.height;
548         break;
549     case OB_DIRECTION_EAST:
550         dest = client_directional_edge_search(c, OB_DIRECTION_EAST, FALSE);
551         if (a->x + a->width == x + c->frame->area.width) {
552             width = c->frame->area.width / 2;
553             x = a->x + a->width - width;
554         } else
555             width = dest - c->frame->area.x;
556         x += (width - c->frame->area.width) % c->size_inc.width;
557         width -= (width - c->frame->area.width) % c->size_inc.width;
558         break;
559     default:
560         g_assert_not_reached();
561     }
562     width -= c->frame->size.left + c->frame->size.right;
563     height -= c->frame->size.top + c->frame->size.bottom;
564     frame_frame_gravity(c->frame, &x, &y, width, height);
565     client_action_start(data);
566     client_move_resize(c, x, y, width, height);
567     client_action_end(data, FALSE);
568     g_free(a);
569 }
570
571 void action_send_to_layer(union ActionData *data)
572 {
573     client_set_layer(data->layer.any.c, data->layer.layer);
574 }
575
576 void action_toggle_layer(union ActionData *data)
577 {
578     ObClient *c = data->layer.any.c;
579
580     client_action_start(data);
581     if (data->layer.layer < 0)
582         client_set_layer(c, c->below ? 0 : -1);
583     else if (data->layer.layer > 0)
584         client_set_layer(c, c->above ? 0 : 1);
585     client_action_end(data, config_focus_under_mouse);
586 }
587
588 void action_toggle_dockautohide(union ActionData *data)
589 {
590     config_dock_hide = !config_dock_hide;
591     dock_configure();
592 }
593
594 void action_remove_desktop(union ActionData *data)
595 {
596 }