Change to the linux kernel coding style
[wmaker-crm.git] / src / usermenu.c
1 /* usermenu.c- user defined menu
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) hmmm... Should I put everybody's name here?
6 * Where's my lawyer?? -- ]d :D
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
22 *
23 * * * * * * * * *
24 * User defined menu is good, but beer's always better
25 * if someone wanna start hacking something, He heard...
26 * TODO
27 * - enhance commands. (eg, exit, hide, list all app's member
28 * window and etc)
29 * - cache menu... dunno.. if people really use this feature :P
30 * - Violins, senseless violins!
31 * that's all, right now :P
32 * - external! WINGs menu editor.
33 * TODONOT
34 * - allow applications to share their menu. ] think it
35 * looks wierd since there still are more than 1 appicon.
36 *
37 * Syntax...
38 * (
39 * "Program Name",
40 * ("Command 1", SHORTCUT, 1),
41 * ("Command 2", SHORTCUT, 2, ("Allowed_instant_1", "Allowed_instant_2")),
42 * ("Command 3", SHORTCUT, (3,4,5), ("Allowed_instant_1")),
43 * (
44 * "Submenu",
45 * ("Kill Command", KILL),
46 * ("Hide Command", HIDE),
47 * ("Hide Others Command", HIDE_OTHERS),
48 * ("Members", MEMBERS),
49 * ("Exit Command", EXIT)
50 * )
51 * )
52 *
53 * Tips:
54 * - If you don't want short cut keys to be listed
55 * in the right side of entries, you can just put them
56 * in array instead of using the string directly.
57 *
58 */
59
60 #include "wconfig.h"
61
62 #ifdef USER_MENU
63
64 #include <X11/Xlib.h>
65 #include <X11/Xutil.h>
66 #include <X11/Xproto.h>
67 #include <X11/Xatom.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <stdio.h>
71 #include <unistd.h>
72
73 #include "WindowMaker.h"
74 #include "wcore.h"
75 #include "menu.h"
76 #include "actions.h"
77 #include "funcs.h"
78 #include "keybind.h"
79
80 #include "framewin.h"
81
82 #define MAX_SHORTCUT_LENGTH 32
83
84 /*** var ***/
85 extern WPreferences wPreferences;
86
87 typedef struct {
88 WScreen *screen;
89 WShortKey *key;
90 int key_no;
91 } WUserMenuData;
92
93 static void notifyClient(WMenu * menu, WMenuEntry * entry)
94 {
95 XEvent event;
96 WUserMenuData *data = entry->clientdata;
97 WScreen *scr = data->screen;
98 Window window;
99 int i;
100
101 window = scr->focused_window->client_win;
102
103 for (i = 0; i < data->key_no; i++) {
104 event.xkey.type = KeyPress;
105 event.xkey.display = dpy;
106 event.xkey.window = window;
107 event.xkey.root = DefaultRootWindow(dpy);
108 event.xkey.subwindow = (Window) None;
109 event.xkey.x = 0x0;
110 event.xkey.y = 0x0;
111 event.xkey.x_root = 0x0;
112 event.xkey.y_root = 0x0;
113 event.xkey.keycode = data->key[i].keycode;
114 event.xkey.state = data->key[i].modifier;
115 event.xkey.same_screen = True;
116 event.xkey.time = CurrentTime;
117 if (XSendEvent(dpy, window, False, KeyPressMask, &event)) {
118 event.xkey.type = KeyRelease;
119 event.xkey.time = CurrentTime;
120 XSendEvent(dpy, window, True, KeyReleaseMask, &event);
121 }
122 }
123 }
124
125 static void removeUserMenudata(void *menudata)
126 {
127 WUserMenuData *data = menudata;
128 if (data->key)
129 wfree(data->key);
130 wfree(data);
131 }
132
133 static WUserMenuData *convertShortcuts(WScreen * scr, WMPropList * shortcut)
134 {
135 WUserMenuData *data;
136 KeySym ksym;
137 char *k;
138 char *buffer;
139 char buf[MAX_SHORTCUT_LENGTH], *b;
140 int keycount, i, j, mod;
141
142 if (WMIsPLString(shortcut)) {
143 keycount = 1;
144 } else if (WMIsPLArray(shortcut)) {
145 keycount = WMGetPropListItemCount(shortcut);
146 } else
147 return NULL;
148 /*for (i=0;i<keycount;i++){ */
149
150 data = wmalloc(sizeof(WUserMenuData));
151 if (!data)
152 return NULL;
153 data->key = wmalloc(sizeof(WShortKey) * keycount);
154 if (!data->key) {
155 wfree(data);
156 return NULL;
157 }
158
159 for (i = 0, j = 0; i < keycount; i++) {
160 data->key[j].modifier = 0;
161 if (WMIsPLArray(shortcut)) {
162 strncpy(buf, WMGetFromPLString(WMGetFromPLArray(shortcut, i)), MAX_SHORTCUT_LENGTH);
163 } else {
164 strncpy(buf, WMGetFromPLString(shortcut), MAX_SHORTCUT_LENGTH);
165 }
166 b = (char *)buf;
167
168 while ((k = strchr(b, '+')) != NULL) {
169 *k = 0;
170 mod = wXModifierFromKey(b);
171 if (mod < 0) {
172 break;
173 }
174 data->key[j].modifier |= mod;
175 b = k + 1;
176 }
177
178 ksym = XStringToKeysym(b);
179 if (ksym == NoSymbol) {
180 continue;
181 }
182
183 data->key[j].keycode = XKeysymToKeycode(dpy, ksym);
184 if (data->key[j].keycode) {
185 j++;
186 }
187 }
188
189 keyover:
190
191 /* get key */
192 if (!j) {
193 puts("fatal j");
194 wfree(data->key);
195 wfree(data);
196 return NULL;
197 }
198 data->key_no = j;
199 data->screen = scr;
200
201 return data;
202 }
203
204 static WMenu *configureUserMenu(WScreen * scr, WMPropList * plum)
205 {
206 char *mtitle;
207 WMenu *menu = NULL;
208 WMPropList *elem, *title, *command, *params;
209 int count, i;
210 WUserMenuData *data;
211
212 if (!plum)
213 return NULL;
214 if (!WMIsPLArray(plum)) {
215 return NULL;
216 }
217
218 count = WMGetPropListItemCount(plum);
219 if (!count)
220 return NULL;
221
222 elem = WMGetFromPLArray(plum, 0);
223 if (!WMIsPLString(elem)) {
224 return NULL;
225 }
226
227 mtitle = WMGetFromPLString(elem);
228
229 menu = wMenuCreateForApp(scr, mtitle, True);
230
231 for (i = 1; i < count; i++) {
232 elem = WMGetFromPLArray(plum, i);
233 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
234 WMenu *submenu;
235 WMenuEntry *mentry;
236
237 submenu = configureUserMenu(scr, elem);
238 if (submenu)
239 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
240 wMenuEntrySetCascade(menu, mentry, submenu);
241 } else {
242 int idx = 0;
243 WMPropList *instances = 0;
244
245 title = WMGetFromPLArray(elem, idx++);
246 command = WMGetFromPLArray(elem, idx++);
247 if (WMGetPropListItemCount(elem) >= 3)
248 params = WMGetFromPLArray(elem, idx++);
249
250 if (!title || !command)
251 return menu;
252
253 if (!strcmp("SHORTCUT", WMGetFromPLString(command))) {
254 WMenuEntry *entry;
255
256 data = convertShortcuts(scr, params);
257 if (data) {
258 entry = wMenuAddCallback(menu, WMGetFromPLString(title),
259 notifyClient, data);
260
261 if (entry) {
262 if (WMIsPLString(params)) {
263 entry->rtext =
264 GetShortcutString(WMGetFromPLString(params));
265 }
266 entry->free_cdata = removeUserMenudata;
267
268 if (WMGetPropListItemCount(elem) >= 4) {
269 instances = WMGetFromPLArray(elem, idx++);
270 if (WMIsPLArray(instances))
271 if (instances && WMGetPropListItemCount(instances)
272 && WMIsPLArray(instances)) {
273 entry->instances =
274 WMRetainPropList(instances);
275 }
276 }
277 }
278 }
279 }
280
281 }
282 }
283 return menu;
284 }
285
286 void wUserMenuRefreshInstances(WMenu * menu, WWindow * wwin)
287 {
288 WMenuEntry *entry;
289 int i, j, count, paintflag;
290
291 paintflag = 0;
292
293 if (!menu)
294 return;
295
296 for (i = 0; i < menu->entry_no; i++) {
297 if (menu->entries[i]->instances) {
298 WMPropList *ins;
299 int oldflag;
300 count = WMGetPropListItemCount(menu->entries[i]->instances);
301
302 oldflag = menu->entries[i]->flags.enabled;
303 menu->entries[i]->flags.enabled = 0;
304 for (j = 0; j < count; j++) {
305 ins = WMGetFromPLArray(menu->entries[i]->instances, j);
306 if (!strcmp(wwin->wm_instance, WMGetFromPLString(ins))) {
307 menu->entries[i]->flags.enabled = 1;
308 break;
309 }
310 }
311 if (oldflag != menu->entries[i]->flags.enabled)
312 paintflag = 1;
313 }
314 }
315 for (i = 0; i < menu->cascade_no; i++) {
316 if (!menu->cascades[i]->flags.brother)
317 wUserMenuRefreshInstances(menu->cascades[i], wwin);
318 else
319 wUserMenuRefreshInstances(menu->cascades[i]->brother, wwin);
320 }
321
322 if (paintflag)
323 wMenuPaint(menu);
324 }
325
326 static WMenu *readUserMenuFile(WScreen * scr, char *file_name)
327 {
328 WMenu *menu;
329 char *mtitle;
330 WMPropList *plum, *elem, *title, *command, *params;
331 int count, i;
332
333 menu = NULL;
334 plum = WMReadPropListFromFile(file_name);
335 /**/ if (plum) {
336 menu = configureUserMenu(scr, plum);
337 WMReleasePropList(plum);
338 }
339 return menu;
340 }
341
342 WMenu *wUserMenuGet(WScreen * scr, WWindow * wwin)
343 {
344 WMenu *menu = NULL;
345 char buffer[100];
346 char *path = NULL;
347 char *tmp;
348 if (wwin->wm_instance && wwin->wm_class) {
349 int len = strlen(wwin->wm_instance) + strlen(wwin->wm_class) + 7;
350 tmp = wmalloc(len);
351 snprintf(tmp, len, "%s.%s.menu", wwin->wm_instance, wwin->wm_class);
352 path = wfindfile(DEF_USER_MENU_PATHS, tmp);
353 wfree(tmp);
354
355 if (!path)
356 return NULL;
357
358 if (wwin) {
359 menu = readUserMenuFile(scr, path);
360 }
361
362 wfree(path);
363 }
364 return menu;
365 }
366
367 #endif /* USER_MENU */