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