add code for interactive actions
[dana/openbox.git] / openbox / actions.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    actions.h for the Openbox window manager
4    Copyright (c) 2007        Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "actions.h"
20 #include "gettext.h"
21 #include "grab.h"
22
23 static void     actions_definition_ref(ObActionsDefinition *def);
24 static void     actions_definition_unref(ObActionsDefinition *def);
25 static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state);
26 static void     actions_interactive_end_act();
27
28 static ObActionsAct *interactive_act = NULL;
29 static guint         interactive_initial_state = 0;
30
31 struct _ObActionsDefinition {
32     guint ref;
33
34     gchar *name;
35     ObActionsType type;
36
37     ObActionsDataSetupFunc setup;
38     ObActionsDataFreeFunc free;
39     ObActionsRunFunc run;
40     ObActionsInteractiveInputFunc i_input;
41     ObActionsInteractiveCancelFunc i_cancel;
42 };
43
44 struct _ObActionsAct {
45     guint ref;
46
47     ObActionsDefinition *def;
48     gpointer options;
49 };
50
51 static GSList *registered = NULL;
52
53
54 void actions_startup(gboolean reconfig)
55 {
56     if (reconfig) return;
57
58     
59 }
60
61 void actions_shutdown(gboolean reconfig)
62 {
63     if (reconfig) return;
64
65     /* free all the registered actions */
66     while (registered) {
67         actions_definition_unref(registered->data);
68         registered = g_slist_delete_link(registered, registered);
69     }
70 }
71
72 gboolean actions_register(const gchar *name,
73                           ObActionsType type,
74                           ObActionsDataSetupFunc setup,
75                           ObActionsDataFreeFunc free,
76                           ObActionsRunFunc run,
77                           ObActionsInteractiveInputFunc i_input,
78                           ObActionsInteractiveCancelFunc i_cancel)
79 {
80     GSList *it;
81     ObActionsDefinition *def;
82
83     for (it = registered; it; it = g_slist_next(it)) {
84         def = it->data;
85         if (!g_ascii_strcasecmp(name, def->name)) /* already registered */
86             return FALSE;
87     }
88
89     g_assert((i_input == NULL) == (i_cancel == NULL));
90
91     def = g_new(ObActionsDefinition, 1);
92     def->ref = 1;
93     def->name = g_strdup(name);
94     def->type = type;
95     def->setup = setup;
96     def->free = free;
97     def->run = run;
98     def->i_input = i_input;
99     def->i_cancel = i_cancel;
100     return TRUE;
101 }
102
103 static void actions_definition_ref(ObActionsDefinition *def)
104 {
105     ++def->ref;
106 }
107
108 static void actions_definition_unref(ObActionsDefinition *def)
109 {
110     if (def && --def->ref == 0) {
111         g_free(def->name);
112         g_free(def);
113     }
114 }
115
116 ObActionsAct* actions_parse_string(const gchar *name)
117 {
118     GSList *it;
119     ObActionsDefinition *def;
120     ObActionsAct *act = NULL;
121
122     /* find the requested action */
123     for (it = registered; it; it = g_slist_next(it)) {
124         def = it->data;
125         if (!g_ascii_strcasecmp(name, def->name))
126             break;
127         def = NULL;
128     }
129
130     /* if we found the action */
131     if (def) {
132         act = g_new(ObActionsAct, 1);
133         act->ref = 1;
134         act->def = def;
135         actions_definition_ref(act->def);
136         act->options = NULL;
137     } else
138         g_message(_("Invalid action '%s' requested. No such action exists."),
139                   name);
140
141     return act;
142 }
143
144 ObActionsAct* actions_parse(ObParseInst *i,
145                             xmlDocPtr doc,
146                             xmlNodePtr node)
147 {
148     gchar *name;
149     ObActionsAct *act = NULL;
150
151     if (parse_attr_string("name", node, &name)) {
152         if ((act = actions_parse_string(name)))
153             /* there is more stuff to parse here */
154             act->options = act->def->setup(i, doc, node->children);
155
156         g_free(name);
157     }
158
159     return act;
160 }
161
162 gboolean actions_act_is_interactive(ObActionsAct *act)
163 {
164     return act->def->i_cancel != NULL;
165 }
166
167 void actions_act_ref(ObActionsAct *act)
168 {
169     ++act->ref;
170 }
171
172 void actions_act_unref(ObActionsAct *act)
173 {
174     if (act && --act->ref == 0) {
175         /* free the action specific options */
176         act->def->free(act->options);
177         /* unref the definition */
178         actions_definition_unref(act->def);
179         g_free(act);
180     }
181 }
182
183 static void actions_setup_data(ObActionsData *data,
184                                ObUserAction uact,
185                                Time time,
186                                guint state,
187                                gint x,
188                                gint y)
189 {
190     data->any.uact = uact;
191     data->any.time = time;
192     data->any.state = state;
193     data->any.x = x;
194     data->any.y = y;
195 }
196
197 void actions_run_acts(GSList *acts,
198                       ObUserAction uact,
199                       Time time,
200                       guint state,
201                       gint x,
202                       gint y,
203                       ObFrameContext con,
204                       struct _ObClient *client)
205 {
206     GSList *it;
207
208     for (it = acts; it; it = g_slist_next(it)) {
209         ObActionsData data;
210         ObActionsAct *act = it->data;
211         gboolean ok = TRUE;
212
213         data.type = act->def->type;
214         actions_setup_data(&data, uact, time, state, x, y);
215         switch (data.type) {
216         case OB_ACTION_TYPE_GLOBAL:
217             break;
218         case OB_ACTION_TYPE_CLIENT:
219             data.client.context = con;
220             data.client.c = client;
221             break;
222         default:
223             g_assert_not_reached();
224         }
225
226         if (actions_act_is_interactive(act) &&
227             (!interactive_act || interactive_act->def != act->def))
228         {
229             ok = actions_interactive_begin_act(act, state);
230         }
231
232         /* fire the action's run function with this data */
233         if (ok) {
234             if (!act->def->run(&data, act->options))
235                 actions_interactive_end_act();
236             else
237                 break; /* no actions are run after the interactive one */
238         }
239     }
240 }
241
242 gboolean actions_interactive_act_running()
243 {
244     return interactive_act != NULL;
245 }
246
247 void actions_interactive_cancel_act()
248 {
249     if (interactive_act) {
250         interactive_act->def->i_cancel(interactive_act->options);
251         actions_interactive_end_act();
252     }
253 }
254
255 static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state)
256 {
257     /* cancel the old one */
258     if (interactive_act)
259         actions_interactive_cancel_act();
260
261     if (grab_keyboard()) {
262         interactive_act = act;
263         actions_act_ref(interactive_act);
264
265         interactive_initial_state = state;
266         return TRUE;
267     }
268     else
269         return FALSE;
270 }
271
272 static void actions_interactive_end_act()
273 {
274     if (interactive_act) {
275         ungrab_keyboard();
276
277         actions_act_unref(interactive_act);
278         interactive_act = NULL;
279     }
280 }
281
282 gboolean actions_interactive_input_event(XEvent *e)
283 {
284     gboolean used = FALSE;
285     if (interactive_act) {
286         if (!interactive_act->def->i_input(interactive_initial_state, e,
287                                            interactive_act->options, &used))
288         {
289             used = TRUE; /* if it cancelled the action then it has to of
290                             been used */
291             actions_interactive_end_act();
292         }
293     }
294     return used;
295 }