well.. it compiles..
[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 #include "screen.h"
23
24 static void     actions_definition_ref(ObActionsDefinition *def);
25 static void     actions_definition_unref(ObActionsDefinition *def);
26 static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state);
27 static void     actions_interactive_end_act();
28
29 static ObActionsAct *interactive_act = NULL;
30 static guint         interactive_initial_state = 0;
31
32 struct _ObActionsDefinition {
33     guint ref;
34
35     gchar *name;
36     ObActionsType type;
37
38     ObActionsDataSetupFunc setup;
39     ObActionsDataFreeFunc free;
40     ObActionsRunFunc run;
41     ObActionsInteractiveInputFunc i_input;
42     ObActionsInteractiveCancelFunc i_cancel;
43 };
44
45 struct _ObActionsAct {
46     guint ref;
47
48     ObActionsDefinition *def;
49     gpointer options;
50 };
51
52 static GSList *registered = NULL;
53
54
55 void actions_startup(gboolean reconfig)
56 {
57     if (reconfig) return;
58 }
59
60 void actions_shutdown(gboolean reconfig)
61 {
62     actions_interactive_cancel_act();
63
64     if (reconfig) return;
65
66     /* free all the registered actions */
67     while (registered) {
68         actions_definition_unref(registered->data);
69         registered = g_slist_delete_link(registered, registered);
70     }
71 }
72
73 gboolean actions_register(const gchar *name,
74                           ObActionsType type,
75                           ObActionsDataSetupFunc setup,
76                           ObActionsDataFreeFunc free,
77                           ObActionsRunFunc run,
78                           ObActionsInteractiveInputFunc i_input,
79                           ObActionsInteractiveCancelFunc i_cancel)
80 {
81     GSList *it;
82     ObActionsDefinition *def;
83
84     for (it = registered; it; it = g_slist_next(it)) {
85         def = it->data;
86         if (!g_ascii_strcasecmp(name, def->name)) /* already registered */
87             return FALSE;
88     }
89
90     g_assert((i_input == NULL) == (i_cancel == NULL));
91
92     def = g_new(ObActionsDefinition, 1);
93     def->ref = 1;
94     def->name = g_strdup(name);
95     def->type = type;
96     def->setup = setup;
97     def->free = free;
98     def->run = run;
99     def->i_input = i_input;
100     def->i_cancel = i_cancel;
101     return TRUE;
102 }
103
104 static void actions_definition_ref(ObActionsDefinition *def)
105 {
106     ++def->ref;
107 }
108
109 static void actions_definition_unref(ObActionsDefinition *def)
110 {
111     if (def && --def->ref == 0) {
112         g_free(def->name);
113         g_free(def);
114     }
115 }
116
117 ObActionsAct* actions_parse_string(const gchar *name)
118 {
119     GSList *it;
120     ObActionsDefinition *def;
121     ObActionsAct *act = NULL;
122
123     /* find the requested action */
124     for (it = registered; it; it = g_slist_next(it)) {
125         def = it->data;
126         if (!g_ascii_strcasecmp(name, def->name))
127             break;
128         def = NULL;
129     }
130
131     /* if we found the action */
132     if (def) {
133         act = g_new(ObActionsAct, 1);
134         act->ref = 1;
135         act->def = def;
136         actions_definition_ref(act->def);
137         act->options = NULL;
138     } else
139         g_message(_("Invalid action '%s' requested. No such action exists."),
140                   name);
141
142     return act;
143 }
144
145 ObActionsAct* actions_parse(ObParseInst *i,
146                             xmlDocPtr doc,
147                             xmlNodePtr node)
148 {
149     gchar *name;
150     ObActionsAct *act = NULL;
151
152     if (parse_attr_string("name", node, &name)) {
153         if ((act = actions_parse_string(name)))
154             /* there is more stuff to parse here */
155             act->options = act->def->setup(i, doc, node->children);
156
157         g_free(name);
158     }
159
160     return act;
161 }
162
163 gboolean actions_act_is_interactive(ObActionsAct *act)
164 {
165     return act->def->i_cancel != NULL;
166 }
167
168 void actions_act_ref(ObActionsAct *act)
169 {
170     ++act->ref;
171 }
172
173 void actions_act_unref(ObActionsAct *act)
174 {
175     if (act && --act->ref == 0) {
176         /* free the action specific options */
177         act->def->free(act->options);
178         /* unref the definition */
179         actions_definition_unref(act->def);
180         g_free(act);
181     }
182 }
183
184 static void actions_setup_data(ObActionsData *data,
185                                ObUserAction uact,
186                                Time time,
187                                guint state,
188                                gint x,
189                                gint y)
190 {
191     data->any.uact = uact;
192     data->any.time = time;
193     data->any.state = state;
194     data->any.x = x;
195     data->any.y = y;
196 }
197
198 void actions_run_acts(GSList *acts,
199                       ObUserAction uact,
200                       Time time,
201                       guint state,
202                       gint x,
203                       gint y,
204                       ObFrameContext con,
205                       struct _ObClient *client)
206 {
207     GSList *it;
208
209     /* Don't allow saving the initial state when running things from the
210        menu */
211     if (uact == OB_USER_ACTION_MENU_SELECTION)
212         state = 0;
213     /* If x and y are < 0 then use the current pointer position */
214     if (x < 0 && y < 0)
215         screen_pointer_pos(&x, &y);
216
217     for (it = acts; it; it = g_slist_next(it)) {
218         ObActionsData data;
219         ObActionsAct *act = it->data;
220         gboolean ok = TRUE;
221
222         data.type = act->def->type;
223         actions_setup_data(&data, uact, time, state, x, y);
224         switch (data.type) {
225         case OB_ACTION_TYPE_GLOBAL:
226             break;
227         case OB_ACTION_TYPE_CLIENT:
228             data.client.context = con;
229             data.client.c = client;
230             break;
231         default:
232             g_assert_not_reached();
233         }
234
235         if (actions_act_is_interactive(act) &&
236             (!interactive_act || interactive_act->def != act->def))
237         {
238             ok = actions_interactive_begin_act(act, state);
239         }
240
241         /* fire the action's run function with this data */
242         if (ok) {
243             if (!act->def->run(&data, act->options))
244                 actions_interactive_end_act();
245             else
246                 break; /* no actions are run after the interactive one */
247         }
248     }
249 }
250
251 gboolean actions_interactive_act_running()
252 {
253     return interactive_act != NULL;
254 }
255
256 void actions_interactive_cancel_act()
257 {
258     if (interactive_act) {
259         interactive_act->def->i_cancel(interactive_act->options);
260         actions_interactive_end_act();
261     }
262 }
263
264 static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state)
265 {
266     /* cancel the old one */
267     if (interactive_act)
268         actions_interactive_cancel_act();
269
270     if (grab_keyboard()) {
271         interactive_act = act;
272         actions_act_ref(interactive_act);
273
274         interactive_initial_state = state;
275         return TRUE;
276     }
277     else
278         return FALSE;
279 }
280
281 static void actions_interactive_end_act()
282 {
283     if (interactive_act) {
284         ungrab_keyboard();
285
286         actions_act_unref(interactive_act);
287         interactive_act = NULL;
288     }
289 }
290
291 gboolean actions_interactive_input_event(XEvent *e)
292 {
293     gboolean used = FALSE;
294     if (interactive_act) {
295         if (!interactive_act->def->i_input(interactive_initial_state, e,
296                                            interactive_act->options, &used))
297         {
298             used = TRUE; /* if it cancelled the action then it has to of
299                             been used */
300             actions_interactive_end_act();
301         }
302     }
303     return used;
304 }