]> icculus.org git repositories - dana/openbox.git/blob - openbox/actions/desktop.c
Rename ObActionValue to ObConfigValue. This holds one of three types.
[dana/openbox.git] / openbox / actions / desktop.c
1 #include "openbox/action.h"
2 #include "openbox/action_list_run.h"
3 #include "openbox/config_value.h"
4 #include "openbox/screen.h"
5 #include "openbox/client.h"
6 #include "openbox/client_set.h"
7 #include "openbox/openbox.h"
8 #include "obt/keyboard.h"
9
10 typedef enum {
11     LAST,
12     CURRENT,
13     RELATIVE,
14     ABSOLUTE
15 } SwitchType;
16
17 typedef struct {
18     SwitchType type;
19     union {
20         struct {
21             guint desktop;
22         } abs;
23
24         struct {
25             gboolean linear;
26             gboolean wrap;
27             ObDirection dir;
28         } rel;
29     } u;
30     gboolean send;
31     gboolean follow;
32     gboolean interactive;
33
34     /* for the foreach function */
35     guint d;
36     GSList *moved;
37 } Options;
38
39 static gpointer setup_go_func(GHashTable *config,
40                               ObActionIPreFunc *pre,
41                               ObActionIInputFunc *input,
42                               ObActionICancelFunc *cancel,
43                               ObActionIPostFunc *post);
44 static gpointer setup_send_func(GHashTable *config,
45                                 ObActionIPreFunc *pre,
46                                 ObActionIInputFunc *input,
47                                 ObActionICancelFunc *cancel,
48                                 ObActionIPostFunc *post);
49 static void free_func(gpointer o);
50 static gboolean run_func(const ObClientSet *set,
51                          const ObActionListRun *data, gpointer options);
52
53 static gboolean i_pre_func(guint state, gpointer options);
54 static gboolean i_input_func(guint initial_state,
55                              XEvent *e,
56                              ObtIC *ic,
57                              gpointer options,
58                              gboolean *used);
59 static void i_post_func(gpointer options);
60
61
62 void action_desktop_startup(void)
63 {
64     action_register_i("GoToDesktop", OB_ACTION_DEFAULT_FILTER_EMPTY,
65                       setup_go_func, free_func, run_func);
66     action_register_i("SendToDesktop", OB_ACTION_DEFAULT_FILTER_SINGLE,
67                       setup_send_func, free_func, run_func);
68 }
69
70 static gpointer setup_func(GHashTable *config,
71                            ObActionIPreFunc *pre,
72                            ObActionIInputFunc *input,
73                            ObActionICancelFunc *cancel,
74                            ObActionIPostFunc *post)
75 {
76     ObConfigValue *v;
77     Options *o;
78
79     o = g_slice_new0(Options);
80     /* don't go anywhere if there are no options given */
81     o->type = ABSOLUTE;
82     o->u.abs.desktop = screen_desktop;
83     /* wrap by default - it's handy! */
84     o->u.rel.wrap = TRUE;
85
86     v = g_hash_table_lookup(config, "to");
87     if (v && config_value_is_string(v)) {
88         const gchar *s = config_value_string(v);
89         if (!g_ascii_strcasecmp(s, "last"))
90             o->type = LAST;
91         else if (!g_ascii_strcasecmp(s, "current"))
92             o->type = CURRENT;
93         else if (!g_ascii_strcasecmp(s, "next")) {
94             o->type = RELATIVE;
95             o->u.rel.linear = TRUE;
96             o->u.rel.dir = OB_DIRECTION_EAST;
97         }
98         else if (!g_ascii_strcasecmp(s, "previous")) {
99             o->type = RELATIVE;
100             o->u.rel.linear = TRUE;
101             o->u.rel.dir = OB_DIRECTION_WEST;
102         }
103         else if (!g_ascii_strcasecmp(s, "north") ||
104                  !g_ascii_strcasecmp(s, "up")) {
105             o->type = RELATIVE;
106             o->u.rel.dir = OB_DIRECTION_NORTH;
107         }
108         else if (!g_ascii_strcasecmp(s, "south") ||
109                  !g_ascii_strcasecmp(s, "down")) {
110             o->type = RELATIVE;
111             o->u.rel.dir = OB_DIRECTION_SOUTH;
112         }
113         else if (!g_ascii_strcasecmp(s, "west") ||
114                  !g_ascii_strcasecmp(s, "left")) {
115             o->type = RELATIVE;
116             o->u.rel.dir = OB_DIRECTION_WEST;
117         }
118         else if (!g_ascii_strcasecmp(s, "east") ||
119                  !g_ascii_strcasecmp(s, "right")) {
120             o->type = RELATIVE;
121             o->u.rel.dir = OB_DIRECTION_EAST;
122         }
123         else {
124             o->type = ABSOLUTE;
125             o->u.abs.desktop = atoi(s) - 1;
126         }
127     }
128
129     v = g_hash_table_lookup(config, "wrap");
130     if (v && config_value_is_string(v))
131         o->u.rel.wrap = config_value_bool(v);
132
133     return o;
134 }
135
136
137 static gpointer setup_go_func(GHashTable *config,
138                               ObActionIPreFunc *pre,
139                               ObActionIInputFunc *input,
140                               ObActionICancelFunc *cancel,
141                               ObActionIPostFunc *post)
142 {
143     Options *o;
144
145     o = setup_func(config, pre, input, cancel, post);
146     if (o->type == RELATIVE) {
147         o->interactive = TRUE;
148         *pre = i_pre_func;
149         *input = i_input_func;
150         *post = i_post_func;
151     }
152
153     return o;
154 }
155
156 static gpointer setup_send_func(GHashTable *config,
157                                 ObActionIPreFunc *pre,
158                                 ObActionIInputFunc *input,
159                                 ObActionICancelFunc *cancel,
160                                 ObActionIPostFunc *post)
161 {
162     ObConfigValue *v;
163     Options *o;
164
165     o = setup_func(config, pre, input, cancel, post);
166     o->send = TRUE;
167     o->follow = TRUE;
168
169     v = g_hash_table_lookup(config, "follow");
170     if (v && config_value_is_string(v))
171         o->follow = config_value_bool(v);
172
173     if (o->type == RELATIVE && o->follow) {
174         o->interactive = TRUE;
175         *pre = i_pre_func;
176         *input = i_input_func;
177         *post = i_post_func;
178     }
179
180     return o;
181 }
182
183 static void free_func(gpointer o)
184 {
185     g_slice_free(Options, o);
186 }
187
188 static gboolean each_send(ObClient *c, const ObActionListRun *data,
189                           gpointer options)
190 {
191     Options *o = options;
192     if (client_normal(c)) {
193         client_set_desktop(c, o->d, o->follow, FALSE);
194         o->moved = g_slist_prepend(o->moved, c);
195     }
196     return TRUE;
197 }
198
199 static gboolean run_func(const ObClientSet *set,
200                          const ObActionListRun *data, gpointer options)
201 {
202     Options *o = options;
203     guint d;
204
205     switch (o->type) {
206     case LAST:
207         d = screen_last_desktop;
208         break;
209     case CURRENT:
210         d = screen_desktop;
211         break;
212     case ABSOLUTE:
213         d = o->u.abs.desktop;
214         break;
215     case RELATIVE:
216         d = screen_find_desktop(screen_desktop,
217                                 o->u.rel.dir, o->u.rel.wrap, o->u.rel.linear);
218         break;
219     default:
220         g_assert_not_reached();
221     }
222
223     if (d < screen_num_desktops) {        
224         gboolean go;
225
226         go = TRUE;
227         o->d = d;
228         o->moved = NULL;
229
230         action_client_move(data, TRUE);
231         if (o->send) {
232             client_set_run(set, data, each_send, o);
233             go = o->follow;
234         }
235
236         if (go) {
237             GSList *it;
238
239             if (d != screen_desktop)
240                 screen_set_desktop(d, TRUE);
241             for (it = o->moved; it; it = g_slist_next(it))
242                 client_bring_helper_windows(it->data);
243         }
244         g_slist_free(o->moved);
245
246         action_client_move(data, FALSE);
247     }
248
249     return o->interactive;
250 }
251
252 static gboolean i_input_func(guint initial_state,
253                              XEvent *e,
254                              ObtIC *ic,
255                              gpointer options,
256                              gboolean *used)
257 {
258     guint mods, initial_mods;
259
260     initial_mods = obt_keyboard_only_modmasks(initial_state);
261     mods = obt_keyboard_only_modmasks(e->xkey.state);
262     if (e->type == KeyRelease) {
263         /* remove from the state the mask of the modifier key being
264            released, if it is a modifier key being released that is */
265         mods &= ~obt_keyboard_keyevent_to_modmask(e);
266     }
267
268     if (e->type == KeyPress) {
269         KeySym sym = obt_keyboard_keypress_to_keysym(e);
270
271         /* Escape cancels no matter what */
272         if (sym == XK_Escape)
273             return FALSE;
274
275         /* There were no modifiers and they pressed enter */
276         else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods)
277             return FALSE;
278     }
279     /* They released the modifiers */
280     else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods))
281     {
282         return FALSE;
283     }
284
285     return TRUE;
286 }
287
288 static gboolean i_pre_func(guint initial_state, gpointer options)
289 {
290     guint initial_mods = obt_keyboard_only_modmasks(initial_state);
291     if (!inital_mods) {
292         Options *o = options;
293         o->interactive = FALSE;
294         return FALSE;
295     }
296     else {
297         screen_show_desktop_popup(screen_desktop, TRUE);
298         return TRUE;
299     }
300 }
301
302 static void i_post_func(gpointer options)
303 {
304     screen_hide_desktop_popup();
305 }