Fix a crash in user menu and a menu map bug.
[wmaker-crm.git] / src / usermenu.c
bloba4abedd188bf5ca65a0cb04a7807924167440c90
1 /* User defined menu is good, but beer's always better
2 * if someone wanna start hacking something, He heard...
3 * TODO
4 * - enhance commands. (eg, exit, hide, list all app's member
5 * window and etc)
6 * - cache menu... dunno.. if people really use this feature :P
7 * - Violins, senseless violins!
8 * that's all, right now :P
9 * - external! WINGs menu editor.
10 * TODONOT
11 * - allow applications to share their menu. ] think it
12 * looks wierd since there still are more than 1 appicon.
14 * Syntax...
15 * (
16 * "Program Name",
17 * ("Command 1", SHORTCUT, 1),
18 * ("Command 2", SHORTCUT, 2, ("Allowed_instant_1", "Allowed_instant_2")),
19 * ("Command 3", SHORTCUT, (3,4,5), ("Allowed_instant_1")),
20 * (
21 * "Submenu",
22 * ("Kill Command", KILL),
23 * ("Hide Command", HIDE),
24 * ("Hide Others Command", HIDE_OTHERS),
25 * ("Members", MEMBERS),
26 * ("Exit Command", EXIT)
27 * )
28 * )
30 * Tips:
31 * - If you don't want short cut keys to be listed
32 * in the right side of entries, you can just put them
33 * in array instead of using the string directly.
37 #include "wconfig.h"
39 #ifdef USER_MENU
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43 #include <X11/Xproto.h>
44 #include <X11/Xatom.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <stdio.h>
48 #include <unistd.h>
50 #include "WindowMaker.h"
51 #include "wcore.h"
52 #include "menu.h"
53 #include "actions.h"
54 #include "funcs.h"
55 #include "keybind.h"
57 #include "framewin.h"
60 extern proplist_t ReadProplistFromFile(char *file);
61 /*** var ***/
62 extern WPreferences wPreferences;
64 typedef struct {
65 WScreen *screen;
66 WShortKey *key;
67 int key_no;
68 } WUserMenuData;
71 static void
72 notifyClient(WMenu *menu, WMenuEntry *entry)
74 XEvent event;
75 WUserMenuData *data = entry->clientdata;
76 WScreen *scr = data->screen;
77 Window window;
78 int i;
80 window=scr->focused_window->client_win;
82 for(i=0;i<data->key_no;i++) {
83 event.xkey.type = KeyPress;
84 event.xkey.display = dpy;
85 event.xkey.window = window;
86 event.xkey.subwindow = 0x0;
87 event.xkey.x = 0x0;
88 event.xkey.y = 0x0;
89 event.xkey.x_root = 0x0;
90 event.xkey.y_root = 0x0;
91 event.xkey.keycode = data->key[i].keycode;
92 event.xkey.state = data->key[i].modifier;
93 event.xkey.same_screen = YES;
94 XSendEvent(dpy, window, False, NoEventMask, &event);
95 XFlush(dpy);
96 event.xkey.type = KeyRelease;
97 XSendEvent(dpy, window, False, NoEventMask, &event);
98 XFlush(dpy);
102 static void
103 removeUserMenudata(void *menudata)
105 WUserMenuData *data = menudata;
106 if(data->key) free(data->key);
107 free(data);
111 static WUserMenuData*
112 convertShortcuts(WScreen *scr, proplist_t shortcut)
114 WUserMenuData *data;
115 KeySym ksym;
116 char *k;
117 char *buffer;
118 char buf[128], *b;
119 int keycount,i,j,mod;
121 if (PLIsString(shortcut)) {
122 keycount = 1;
124 else if (PLIsArray(shortcut)) {
125 keycount = PLGetNumberOfElements(shortcut);
127 else return NULL;
128 /*for (i=0;i<keycount;i++){*/
130 data = wmalloc(sizeof(WUserMenuData));
131 if (!data) return NULL;
132 data->key = wmalloc(sizeof(WShortKey)*keycount);
133 if (!data->key) {
134 free(data);
135 return NULL;
139 for (i=0,j=0;i<keycount;i++) {
140 data->key[j].modifier = 0;
141 if (PLIsArray(shortcut)) {
142 strcpy(buf, PLGetString(PLGetArrayElement(shortcut, i)));
143 } else {
144 strcpy(buf, PLGetString(shortcut));
146 b = (char*)buf;
148 while ((k = strchr(b, '+'))!=NULL) {
149 *k = 0;
150 mod = wXModifierFromKey(b);
151 if (mod<0) {
152 break;
154 data->key[j].modifier |= mod;
155 b = k+1;
158 ksym = XStringToKeysym(b);
159 if (ksym==NoSymbol) {
160 continue;
163 data->key[j].keycode = XKeysymToKeycode(dpy, ksym);
164 if (data->key[j].keycode) {
165 j++;
169 keyover:
171 /* get key */
172 if (!j) {
173 puts("fatal j");
174 free(data->key);
175 free(data);
176 return NULL;
178 data->key_no = j;
179 data->screen = scr;
181 return data;
184 static WMenu*
185 configureUserMenu(WScreen *scr, proplist_t plum)
187 char *mtitle;
188 WMenu *menu=NULL;
189 proplist_t elem, title, command, params;
190 int count,i;
191 WUserMenuData *data;
193 if (!plum) return NULL;
194 if (!PLIsArray(plum)) {
195 return NULL;
198 count = PLGetNumberOfElements(plum);
199 if (!count) return NULL;
201 elem = PLGetArrayElement(plum, 0);
202 if (!PLIsString(elem)) {
203 return NULL;
206 mtitle = PLGetString(elem);
208 menu=wMenuCreateForApp(scr, mtitle, True);
210 for(i=1; i<count; i++) {
211 elem = PLGetArrayElement(plum,i);
212 if(PLIsArray(PLGetArrayElement(elem,1))) {
213 WMenu *submenu;
214 WMenuEntry *mentry;
216 submenu = configureUserMenu(scr,elem);
217 if (submenu)
218 mentry = wMenuAddCallback(menu, submenu->frame->title,
219 NULL, NULL);
220 wMenuEntrySetCascade(menu, mentry, submenu);
222 else {
223 int idx = 0;
224 proplist_t instances=0;
226 title = PLGetArrayElement(elem,idx++);
227 command = PLGetArrayElement(elem,idx++);
228 if (PLGetNumberOfElements(elem) >= 3)
229 params = PLGetArrayElement(elem,idx++);
231 if (!title || !command)
232 return menu;
234 if (!strcmp("SHORTCUT",PLGetString(command))) {
235 WMenuEntry *entry;
237 data = convertShortcuts(scr, params);
238 if (data){
239 entry = wMenuAddCallback(menu, PLGetString(title),
240 notifyClient, data);
242 if (entry) {
243 if (PLIsString(params)) {
244 entry->rtext = GetShortcutString(PLGetString(params));
246 entry->free_cdata = removeUserMenudata;
248 if (PLGetNumberOfElements(elem) >= 4) {
249 instances = PLGetArrayElement(elem,idx++);
250 if(PLIsArray(instances))
251 if (instances && PLGetNumberOfElements(instances)
252 && PLIsArray(instances)){
253 entry->instances = PLRetain(instances);
263 return menu;
266 void
267 wUserMenuRefreshInstances(WMenu *menu, WWindow *wwin)
269 WMenuEntry* entry;
270 int i,j,count,paintflag;
272 paintflag=0;
274 if(!menu) return;
276 for (i=0; i<menu->entry_no; i++) {
277 if (menu->entries[i]->instances){
278 proplist_t ins;
279 int oldflag;
280 count = PLGetNumberOfElements(menu->entries[i]->instances);
282 oldflag = menu->entries[i]->flags.enabled;
283 menu->entries[i]->flags.enabled = 0;
284 for (j=0; j<count;j++) {
285 ins = PLGetArrayElement(menu->entries[i]->instances,j);
286 if (!strcmp(wwin->wm_instance,PLGetString(ins))) {
287 menu->entries[i]->flags.enabled = 1;
288 break;
291 if (oldflag != menu->entries[i]->flags.enabled)
292 paintflag=1;
295 for (i=0; i < menu->cascade_no; i++) {
296 if (!menu->cascades[i]->flags.brother)
297 wUserMenuRefreshInstances(menu->cascades[i], wwin);
298 else
299 wUserMenuRefreshInstances(menu->cascades[i]->brother, wwin);
302 if (paintflag)
303 wMenuPaint(menu);
307 static WMenu*
308 readUserMenuFile(WScreen *scr, char *file_name)
310 WMenu *menu;
311 char *mtitle;
312 proplist_t plum, elem, title, command, params;
313 int count,i;
315 menu=NULL;
316 plum = ReadProplistFromFile(file_name);
317 /**/
319 if(plum){
320 menu = configureUserMenu(scr, plum);
321 PLRelease(plum);
323 return menu;
327 WMenu*
328 wUserMenuGet(WScreen *scr, WWindow *wwin)
330 WMenu *menu = NULL;
331 char buffer[100];
332 char *path = NULL;
333 char *tmp;
334 if ( wwin->wm_instance && wwin->wm_class ) {
335 tmp=wmalloc(strlen(wwin->wm_instance)+strlen(wwin->wm_class)+7);
336 sprintf(tmp,"%s.%s.menu",wwin->wm_instance,wwin->wm_class);
337 path = wfindfile(DEF_USER_MENU_PATHS,tmp);
338 free(tmp);
340 if (!path) return NULL;
342 if (wwin) {
343 menu = readUserMenuFile(scr, path);
346 free(path);
348 return menu;
351 #endif /* USER_MENU */