Merge branch 'backport' into work
[mikachu/openbox.git] / openbox / actions / cyclewindows.c
1 #include "openbox/actions.h"
2 #include "openbox/stacking.h"
3 #include "openbox/window.h"
4 #include "openbox/event.h"
5 #include "openbox/focus_cycle.h"
6 #include "openbox/openbox.h"
7 #include "gettext.h"
8
9 typedef struct {
10     gboolean linear;
11     gboolean dialog;
12     gboolean dock_windows;
13     gboolean desktop_windows;
14     gboolean all_desktops;
15     gboolean forward;
16     gboolean bar;
17     gboolean raise;
18     GSList *actions;
19 } Options;
20
21 static gboolean cycling = FALSE;
22
23 static gpointer setup_func(xmlNodePtr node);
24 static gpointer setup_forward_func(xmlNodePtr node);
25 static gpointer setup_backward_func(xmlNodePtr node);
26 static void     free_func(gpointer options);
27 static gboolean run_func(ObActionsData *data, gpointer options);
28 static gboolean i_input_func(guint initial_state,
29                              XEvent *e,
30                              gpointer options,
31                              gboolean *used);
32 static void     i_cancel_func(gpointer options);
33
34 static void     end_cycle(gboolean cancel, guint state, Options *o);
35
36 void action_cyclewindows_startup(void)
37 {
38     actions_register("NextWindow", setup_forward_func, free_func,
39                      run_func, i_input_func, i_cancel_func);
40     actions_register("PreviousWindow", setup_backward_func, free_func,
41                      run_func, i_input_func, i_cancel_func);
42 }
43
44 static gpointer setup_func(xmlNodePtr node)
45 {
46     xmlNodePtr n;
47     Options *o;
48
49     o = g_new0(Options, 1);
50     o->dialog = TRUE;
51     o->bar = TRUE;
52
53     if ((n = obt_parse_find_node(node, "linear")))
54         o->linear = obt_parse_node_bool(n);
55     if ((n = obt_parse_find_node(node, "dialog")))
56         o->dialog = obt_parse_node_bool(n);
57     if ((n = obt_parse_find_node(node, "bar")))
58         o->bar = obt_parse_node_bool(n);
59     if ((n = obt_parse_find_node(node, "raise")))
60         o->raise = obt_parse_node_bool(n);
61     if ((n = obt_parse_find_node(node, "panels")))
62         o->dock_windows = obt_parse_node_bool(n);
63     if ((n = obt_parse_find_node(node, "desktop")))
64         o->desktop_windows = obt_parse_node_bool(n);
65     if ((n = obt_parse_find_node(node, "allDesktops")))
66         o->all_desktops = obt_parse_node_bool(n);
67
68     if ((n = obt_parse_find_node(node, "finalactions"))) {
69         xmlNodePtr m;
70
71         m = obt_parse_find_node(n->children, "action");
72         while (m) {
73             ObActionsAct *action = actions_parse(m);
74             if (action) o->actions = g_slist_prepend(o->actions, action);
75             m = obt_parse_find_node(m->next, "action");
76         }
77     }
78     else {
79         o->actions = g_slist_prepend(o->actions,
80                                      actions_parse_string("Focus"));
81         o->actions = g_slist_prepend(o->actions,
82                                      actions_parse_string("Raise"));
83         o->actions = g_slist_prepend(o->actions,
84                                      actions_parse_string("Unshade"));
85     }
86
87     return o;
88 }
89
90 static gpointer setup_forward_func(xmlNodePtr node)
91 {
92     Options *o = setup_func(node);
93     o->forward = TRUE;
94     return o;
95 }
96
97 static gpointer setup_backward_func(xmlNodePtr node)
98 {
99     Options *o = setup_func(node);
100     o->forward = FALSE;
101     return o;
102 }
103
104 static void free_func(gpointer options)
105 {
106     Options *o = options;
107
108     while (o->actions) {
109         actions_act_unref(o->actions->data);
110         o->actions = g_slist_delete_link(o->actions, o->actions);
111     }
112
113     g_free(o);
114 }
115
116 static gboolean run_func(ObActionsData *data, gpointer options)
117 {
118     Options *o = options;
119     struct _ObClient *ft;
120
121     ft = focus_cycle(o->forward,
122                      o->all_desktops,
123                      o->dock_windows,
124                      o->desktop_windows,
125                      o->linear,
126                      TRUE,
127                      o->bar,
128                      o->dialog,
129                      FALSE, FALSE);
130     cycling = TRUE;
131
132     stacking_restore();
133     if (o->raise) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
134
135     return TRUE;
136 }
137
138 static gboolean i_input_func(guint initial_state,
139                              XEvent *e,
140                              gpointer options,
141                              gboolean *used)
142 {
143     if (e->type == KeyPress) {
144         /* Escape cancels no matter what */
145         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
146             end_cycle(TRUE, e->xkey.state, options);
147             return FALSE;
148         }
149
150         /* There were no modifiers and they pressed enter */
151         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN) &&
152                  !initial_state)
153         {
154             end_cycle(FALSE, e->xkey.state, options);
155             return FALSE;
156         }
157     }
158     /* They released the modifiers */
159     else if (e->type == KeyRelease && initial_state &&
160              (e->xkey.state & initial_state) == 0)
161     {
162         end_cycle(FALSE, e->xkey.state, options);
163         return FALSE;
164     }
165
166     return TRUE;
167 }
168
169 static void i_cancel_func(gpointer options)
170 {
171     /* we get cancelled when we move focus, but we're not cycling anymore, so
172        just ignore that */
173     if (cycling)
174         end_cycle(TRUE, 0, options);
175 }
176
177 static void end_cycle(gboolean cancel, guint state, Options *o)
178 {
179     struct _ObClient *ft;
180
181     ft = focus_cycle(o->forward,
182                      o->all_desktops,
183                      o->dock_windows,
184                      o->desktop_windows,
185                      o->linear,
186                      TRUE,
187                      o->bar,
188                      o->dialog,
189                      TRUE, cancel);
190     cycling = FALSE;
191
192     if (ft)
193         actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
194                          state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft);
195
196     stacking_restore();
197 }