]> icculus.org git repositories - dana/openbox.git/blob - openbox/actions/directionalwindows.c
Use ObConfigValue to parse gravity coords, remove parse special functions from config...
[dana/openbox.git] / openbox / actions / directionalwindows.c
1 #include "openbox/action.h"
2 #include "openbox/action_list.h"
3 #include "openbox/action_list_run.h"
4 #include "openbox/action_parser.h"
5 #include "openbox/config_value.h"
6 #include "openbox/client_set.h"
7 #include "openbox/event.h"
8 #include "openbox/stacking.h"
9 #include "openbox/window.h"
10 #include "openbox/focus_cycle.h"
11 #include "openbox/openbox.h"
12 #include "openbox/misc.h"
13 #include "gettext.h"
14 #include "obt/keyboard.h"
15
16 typedef struct {
17     gboolean interactive;
18     gboolean dialog;
19     gboolean dock_windows;
20     gboolean desktop_windows;
21     ObDirection direction;
22     gboolean bar;
23     gboolean raise;
24     ObActionList *actions;
25 } Options;
26
27 static gboolean cycling = FALSE;
28
29 static gpointer setup_func(GHashTable *config);
30 static gpointer setup_cycle_func(GHashTable *config,
31                                  ObActionIPreFunc *pre,
32                                  ObActionIInputFunc *input,
33                                  ObActionICancelFunc *cancel,
34                                  ObActionIPostFunc *post);
35 static gpointer setup_target_func(GHashTable *config);
36 static void     free_func(gpointer options);
37 static gboolean run_func(const ObClientSet *set,
38                          const ObActionListRun *data, gpointer options);
39 static gboolean i_input_func(guint initial_state,
40                              XEvent *e,
41                              ObtIC *ic,
42                              gpointer options,
43                              gboolean *used);
44 static void     i_cancel_func(gpointer options);
45
46 static void     end_cycle(gboolean cancel, guint state, Options *o);
47
48 void action_directionalwindows_startup(void)
49 {
50     action_register_i("DirectionalCycleWindows", OB_ACTION_DEFAULT_FILTER_ALL,
51                       setup_cycle_func, free_func, run_func);
52     action_register("DirectionalTargetWindow", OB_ACTION_DEFAULT_FILTER_ALL,
53                     setup_target_func, free_func, run_func);
54 }
55
56 static gpointer setup_func(GHashTable *config)
57 {
58     ObConfigValue *v;
59     Options *o;
60
61     o = g_slice_new0(Options);
62     o->dialog = TRUE;
63     o->bar = TRUE;
64
65     v = g_hash_table_lookup(config, "dialog");
66     if (v && config_value_is_string(v))
67         o->dialog = config_value_bool(v);
68     v = g_hash_table_lookup(config, "bar");
69     if (v && config_value_is_string(v))
70         o->bar = config_value_bool(v);
71     v = g_hash_table_lookup(config, "raise");
72     if (v && config_value_is_string(v))
73         o->raise = config_value_bool(v);
74     v = g_hash_table_lookup(config, "panels");
75     if (v && config_value_is_string(v))
76         o->dock_windows = config_value_bool(v);
77     v = g_hash_table_lookup(config, "desktop");
78     if (v && config_value_is_string(v))
79         o->desktop_windows = config_value_bool(v);
80     v = g_hash_table_lookup(config, "direction");
81     if (v && config_value_is_string(v)) {
82         const gchar *s = config_value_string(v);
83         if (!g_ascii_strcasecmp(s, "north") ||
84             !g_ascii_strcasecmp(s, "up"))
85             o->direction = OB_DIRECTION_NORTH;
86         else if (!g_ascii_strcasecmp(s, "northwest"))
87             o->direction = OB_DIRECTION_NORTHWEST;
88         else if (!g_ascii_strcasecmp(s, "northeast"))
89             o->direction = OB_DIRECTION_NORTHEAST;
90         else if (!g_ascii_strcasecmp(s, "west") ||
91                  !g_ascii_strcasecmp(s, "left"))
92             o->direction = OB_DIRECTION_WEST;
93         else if (!g_ascii_strcasecmp(s, "east") ||
94                  !g_ascii_strcasecmp(s, "right"))
95             o->direction = OB_DIRECTION_EAST;
96         else if (!g_ascii_strcasecmp(s, "south") ||
97                  !g_ascii_strcasecmp(s, "down"))
98             o->direction = OB_DIRECTION_SOUTH;
99         else if (!g_ascii_strcasecmp(s, "southwest"))
100             o->direction = OB_DIRECTION_SOUTHWEST;
101         else if (!g_ascii_strcasecmp(s, "southeast"))
102             o->direction = OB_DIRECTION_SOUTHEAST;
103     }
104
105     v = g_hash_table_lookup(config, "finalactions");
106     if (v && config_value_is_action_list(v)) {
107         o->actions = config_value_action_list(v);
108         action_list_ref(o->actions);
109     }
110     else {
111         ObActionParser *p = action_parser_new();
112         o->actions = action_parser_read_string(p,
113                                                 "focus\n"
114                                                 "raise\n"
115                                                 "unshade\n");
116         action_parser_unref(p);
117     }
118
119     return o;
120 }
121
122 static gpointer setup_cycle_func(GHashTable *config,
123                                  ObActionIPreFunc *pre,
124                                  ObActionIInputFunc *input,
125                                  ObActionICancelFunc *cancel,
126                                  ObActionIPostFunc *post)
127 {
128     Options *o = setup_func(config);
129     o->interactive = TRUE;
130     *input = i_input_func;
131     *cancel = i_cancel_func;
132     return o;
133 }
134
135 static gpointer setup_target_func(GHashTable *config)
136 {
137     Options *o = setup_func(config);
138     o->interactive = FALSE;
139     return o;
140 }
141
142 static void free_func(gpointer options)
143 {
144     Options *o = options;
145
146     action_list_unref(o->actions);
147     g_slice_free(Options, o);
148 }
149
150 static gboolean run_func(const ObClientSet *set,
151                          const ObActionListRun *data, gpointer options)
152 {
153     Options *o = options;
154
155     if (client_set_is_empty(set)) return FALSE;
156
157     if (!o->interactive)
158         end_cycle(FALSE, data->mod_state, o);
159     else {
160         struct _ObClient *ft;
161
162         ft = focus_directional_cycle(o->direction,
163                                      set,
164                                      o->dock_windows,
165                                      o->desktop_windows,
166                                      TRUE,
167                                      o->bar,
168                                      o->dialog,
169                                      FALSE, FALSE);
170         cycling = TRUE;
171
172         stacking_restore();
173         if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
174     }
175
176     return o->interactive;
177 }
178
179 static gboolean i_input_func(guint initial_state,
180                              XEvent *e,
181                              ObtIC *ic,
182                              gpointer options,
183                              gboolean *used)
184 {
185     guint mods, initial_mods;
186
187     initial_mods = obt_keyboard_only_modmasks(initial_state);
188     mods = obt_keyboard_only_modmasks(e->xkey.state);
189     if (e->type == KeyRelease) {
190         /* remove from the state the mask of the modifier key being
191            released, if it is a modifier key being released that is */
192         mods &= ~obt_keyboard_keyevent_to_modmask(e);
193     }
194
195     if (e->type == KeyPress) {
196         KeySym sym = obt_keyboard_keypress_to_keysym(e);
197
198         /* Escape cancels no matter what */
199         if (sym == XK_Escape) {
200             end_cycle(TRUE, e->xkey.state, options);
201             return FALSE;
202         }
203
204         /* There were no modifiers and they pressed enter */
205         else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods) {
206             end_cycle(FALSE, e->xkey.state, options);
207             return FALSE;
208         }
209     }
210     /* They released the modifiers */
211     else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods)) {
212         end_cycle(FALSE, e->xkey.state, options);
213         return FALSE;
214     }
215
216     return TRUE;
217 }
218
219 static void i_cancel_func(gpointer options)
220 {
221     /* we get cancelled when we move focus, but we're not cycling anymore, so
222        just ignore that */
223     if (cycling)
224         end_cycle(TRUE, 0, options);
225 }
226
227 static void end_cycle(gboolean cancel, guint state, Options *o)
228 {
229     struct _ObClient *ft;
230
231     ft = focus_directional_cycle(o->direction,
232                                  NULL,
233                                  o->dock_windows,
234                                  o->desktop_windows,
235                                  o->interactive,
236                                  o->bar,
237                                  o->dialog,
238                                  TRUE, cancel);
239     cycling = FALSE;
240
241     if (ft)
242         action_list_run(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
243                         state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft);
244
245     stacking_restore();
246 }