Add OPEN_PLMENU option to parse command generated proplist style menus
[wmaker-crm.git] / src / rootmenu.c
1 /* rootmenu.c- user defined menu
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
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 along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "wconfig.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <sys/types.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <ctype.h>
34 #include <time.h>
35 #include <dirent.h>
36
37 #include <X11/Xlib.h>
38 #include <X11/Xutil.h>
39 #include <X11/Xatom.h>
40
41 #include "WindowMaker.h"
42 #include "actions.h"
43 #include "menu.h"
44 #include "funcs.h"
45 #include "main.h"
46 #include "dialog.h"
47 #include "keybind.h"
48 #include "stacking.h"
49 #include "workspace.h"
50 #include "defaults.h"
51 #include "framewin.h"
52 #include "session.h"
53 #include "shutdown.h"
54 #include "xmodifier.h"
55
56 #include <WINGs/WUtil.h>
57
58 #define MAX_SHORTCUT_LENGTH 32
59
60 extern char *Locale;
61 extern WDDomain *WDRootMenu;
62 extern Cursor wCursor[WCUR_LAST];
63 extern WPreferences wPreferences;
64
65 static WMenu *readMenuPipe(WScreen * scr, char **file_name);
66 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name);
67 static WMenu *readMenuFile(WScreen * scr, char *file_name);
68 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
69 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
70 static void menu_parser_register_macros(WMenuParser parser);
71
72 typedef struct Shortcut {
73 struct Shortcut *next;
74
75 int modifier;
76 KeyCode keycode;
77 WMenuEntry *entry;
78 WMenu *menu;
79 } Shortcut;
80
81 static Shortcut *shortcutList = NULL;
82
83 /*
84 * Syntax:
85 * # main menu
86 * "Menu Name" MENU
87 * "Title" EXEC command_to_exec -params
88 * "Submenu" MENU
89 * "Title" EXEC command_to_exec -params
90 * "Submenu" END
91 * "Workspaces" WORKSPACE_MENU
92 * "Title" built_in_command
93 * "Quit" EXIT
94 * "Quick Quit" EXIT QUICK
95 * "Menu Name" END
96 *
97 * Commands may be preceded by SHORTCUT key
98 *
99 * Built-in commands:
100 *
101 * INFO_PANEL - shows the Info Panel
102 * LEGAL_PANEL - shows the Legal info panel
103 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
104 * REFRESH - forces the desktop to be repainted
105 * EXIT [QUICK] - exit the window manager [without confirmation]
106 * EXEC <program> - execute an external program
107 * SHEXEC <command> - execute a shell command
108 * WORKSPACE_MENU - places the workspace submenu
109 * ARRANGE_ICONS
110 * RESTART [<window manager>] - restarts the window manager
111 * SHOW_ALL - unhide all windows on workspace
112 * HIDE_OTHERS - hides all windows excep the focused one
113 * OPEN_MENU file - read menu data from file which must be a valid menu file.
114 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
115 * - read menu data from directory(ies) and
116 * eventually precede each with a command.
117 * OPEN_MENU | command
118 * - opens command and uses its stdout to construct and insert
119 * the resulting menu in current position. The output of
120 * command must be a valid menu description.
121 * The space between '|' and command is optional.
122 * || will do the same, but will not cache the contents.
123 * OPEN_PLMENU | command
124 * - opens command and uses its stdout which must be in proplist
125 * fromat to construct and insert the resulting menu in current
126 * position.
127 * The space between '|' and command is optional.
128 * || will do the same, but will not cache the contents.
129 * SAVE_SESSION - saves the current state of the desktop, which include
130 * all running applications, all their hints (geometry,
131 * position on screen, workspace they live on, the dock
132 * or clip from where they were launched, and
133 * if minimized, shaded or hidden. Also saves the current
134 * workspace the user is on. All will be restored on every
135 * start of windowmaker until another SAVE_SESSION or
136 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
137 * WindowMaker domain file, then saving is automatically
138 * done on every windowmaker exit, overwriting any
139 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
140 * dock state now.
141 * CLEAR_SESSION - clears any previous saved session. This will not have
142 * any effect if SaveSessionOnExit is True.
143 *
144 */
145
146 #define M_QUICK 1
147
148 /* menu commands */
149
150 static void execCommand(WMenu * menu, WMenuEntry * entry)
151 {
152 char *cmdline;
153
154 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
155
156 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
157 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
158 XSync(dpy, 0);
159
160 if (cmdline) {
161 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
162 wfree(cmdline);
163 }
164 XUngrabPointer(dpy, CurrentTime);
165 XSync(dpy, 0);
166 }
167
168 static void exitCommand(WMenu * menu, WMenuEntry * entry)
169 {
170 static int inside = 0;
171 int result;
172
173 /* prevent reentrant calls */
174 if (inside)
175 return;
176 inside = 1;
177
178 #define R_CANCEL 0
179 #define R_EXIT 1
180
181 result = R_CANCEL;
182
183 if ((long)entry->clientdata == M_QUICK) {
184 result = R_EXIT;
185 } else {
186 int r, oldSaveSessionFlag;
187
188 oldSaveSessionFlag = wPreferences.save_session_on_exit;
189 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
190 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
191
192 if (r == WAPRDefault) {
193 result = R_EXIT;
194 } else if (r == WAPRAlternate) {
195 /* Don't modify the "save session on exit" flag if the
196 * user canceled the operation. */
197 wPreferences.save_session_on_exit = oldSaveSessionFlag;
198 }
199 }
200 if (result == R_EXIT)
201 Shutdown(WSExitMode);
202
203 #undef R_EXIT
204 #undef R_CANCEL
205 inside = 0;
206 }
207
208 static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
209 {
210 static int inside = 0;
211 int result;
212
213 /* prevent reentrant calls */
214 if (inside)
215 return;
216 inside = 1;
217
218 #define R_CANCEL 0
219 #define R_CLOSE 1
220 #define R_KILL 2
221
222 result = R_CANCEL;
223 if ((long)entry->clientdata == M_QUICK)
224 result = R_CLOSE;
225 else {
226 int r, oldSaveSessionFlag;
227
228 oldSaveSessionFlag = wPreferences.save_session_on_exit;
229
230 r = wExitDialog(menu->frame->screen_ptr,
231 _("Kill X session"),
232 _("Kill Window System session?\n"
233 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
234 if (r == WAPRDefault) {
235 result = R_KILL;
236 } else if (r == WAPRAlternate) {
237 /* Don't modify the "save session on exit" flag if the
238 * user canceled the operation. */
239 wPreferences.save_session_on_exit = oldSaveSessionFlag;
240 }
241 }
242
243 if (result != R_CANCEL) {
244 Shutdown(WSKillMode);
245 }
246 #undef R_CLOSE
247 #undef R_CANCEL
248 #undef R_KILL
249 inside = 0;
250 }
251
252 static void restartCommand(WMenu * menu, WMenuEntry * entry)
253 {
254 Shutdown(WSRestartPreparationMode);
255 Restart((char *)entry->clientdata, False);
256 Restart(NULL, True);
257 }
258
259 static void refreshCommand(WMenu * menu, WMenuEntry * entry)
260 {
261 wRefreshDesktop(menu->frame->screen_ptr);
262 }
263
264 static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
265 {
266 wArrangeIcons(menu->frame->screen_ptr, True);
267 }
268
269 static void showAllCommand(WMenu * menu, WMenuEntry * entry)
270 {
271 wShowAllWindows(menu->frame->screen_ptr);
272 }
273
274 static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
275 {
276 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
277 }
278
279 static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
280 {
281 if (!wPreferences.save_session_on_exit)
282 wSessionSaveState(menu->frame->screen_ptr);
283
284 wScreenSaveState(menu->frame->screen_ptr);
285 }
286
287 static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
288 {
289 wSessionClearState(menu->frame->screen_ptr);
290 wScreenSaveState(menu->frame->screen_ptr);
291 }
292
293 static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
294 {
295 wShowInfoPanel(menu->frame->screen_ptr);
296 }
297
298 static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
299 {
300 wShowLegalPanel(menu->frame->screen_ptr);
301 }
302
303 /********************************************************************/
304
305 static char * getLocalizedMenuFile(char *menu)
306 {
307 char *buffer, *ptr, *locale;
308 int len;
309
310 if (!Locale)
311 return NULL;
312
313 len = strlen(menu) + strlen(Locale) + 8;
314 buffer = wmalloc(len);
315
316 /* try menu.locale_name */
317 snprintf(buffer, len, "%s.%s", menu, Locale);
318 if (access(buffer, F_OK) == 0)
319 return buffer;
320
321 /* position of locale in our buffer */
322 locale = buffer + strlen(menu) + 1;
323
324 /* check if it is in the form aa_bb.encoding and check for aa_bb */
325 ptr = strchr(locale, '.');
326 if (ptr) {
327 *ptr = 0;
328 if (access(buffer, F_OK) == 0)
329 return buffer;
330 }
331
332 /* now check for aa */
333 ptr = strchr(locale, '_');
334 if (ptr) {
335 *ptr = 0;
336 if (access(buffer, F_OK) == 0)
337 return buffer;
338 }
339
340 wfree(buffer);
341
342 return NULL;
343 }
344
345 Bool wRootMenuPerformShortcut(XEvent * event)
346 {
347 WScreen *scr = wScreenForRootWindow(event->xkey.root);
348 Shortcut *ptr;
349 int modifiers;
350 int done = 0;
351
352 /* ignore CapsLock */
353 modifiers = event->xkey.state & ValidModMask;
354
355 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
356 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
357 continue;
358
359 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
360 (*ptr->entry->callback) (ptr->menu, ptr->entry);
361 done = True;
362 }
363 }
364
365 return done;
366 }
367
368 void wRootMenuBindShortcuts(Window window)
369 {
370 Shortcut *ptr;
371
372 ptr = shortcutList;
373 while (ptr) {
374 if (ptr->modifier != AnyModifier) {
375 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
376 window, True, GrabModeAsync, GrabModeAsync);
377 #ifdef NUMLOCK_HACK
378 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
379 #endif
380 }
381 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
382 ptr = ptr->next;
383 }
384 }
385
386 static void rebindKeygrabs(WScreen * scr)
387 {
388 WWindow *wwin;
389
390 wwin = scr->focused_window;
391
392 while (wwin != NULL) {
393 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
394
395 if (!WFLAGP(wwin, no_bind_keys)) {
396 wWindowSetKeyGrabs(wwin);
397 }
398 wwin = wwin->prev;
399 }
400 }
401
402 static void removeShortcutsForMenu(WMenu * menu)
403 {
404 Shortcut *ptr, *tmp;
405 Shortcut *newList = NULL;
406
407 ptr = shortcutList;
408 while (ptr != NULL) {
409 tmp = ptr->next;
410 if (ptr->menu == menu) {
411 wfree(ptr);
412 } else {
413 ptr->next = newList;
414 newList = ptr;
415 }
416 ptr = tmp;
417 }
418 shortcutList = newList;
419 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
420 }
421
422 static Bool addShortcut(const char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
423 {
424 Shortcut *ptr;
425 KeySym ksym;
426 char *k;
427 char buf[MAX_SHORTCUT_LENGTH], *b;
428
429 ptr = wmalloc(sizeof(Shortcut));
430
431 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
432 b = (char *)buf;
433
434 /* get modifiers */
435 ptr->modifier = 0;
436 while ((k = strchr(b, '+')) != NULL) {
437 int mod;
438
439 *k = 0;
440 mod = wXModifierFromKey(b);
441 if (mod < 0) {
442 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
443 wfree(ptr);
444 return False;
445 }
446 ptr->modifier |= mod;
447
448 b = k + 1;
449 }
450
451 /* get key */
452 ksym = XStringToKeysym(b);
453
454 if (ksym == NoSymbol) {
455 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
456 file, shortcutDefinition, entry->text);
457 wfree(ptr);
458 return False;
459 }
460
461 ptr->keycode = XKeysymToKeycode(dpy, ksym);
462 if (ptr->keycode == 0) {
463 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
464 shortcutDefinition, entry->text);
465 wfree(ptr);
466 return False;
467 }
468
469 ptr->menu = menu;
470 ptr->entry = entry;
471
472 ptr->next = shortcutList;
473 shortcutList = ptr;
474
475 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
476
477 return True;
478 }
479
480 static char *next_token(char *line, char **next)
481 {
482 char *tmp, c;
483 char *ret;
484
485 *next = NULL;
486 while (*line == ' ' || *line == '\t')
487 line++;
488
489 tmp = line;
490
491 if (*tmp == '"') {
492 tmp++;
493 line++;
494 while (*tmp != 0 && *tmp != '"')
495 tmp++;
496 if (*tmp != '"') {
497 wwarning(_("%s: unmatched '\"' in menu file"), line);
498 return NULL;
499 }
500 } else {
501 do {
502 if (*tmp == '\\')
503 tmp++;
504
505 if (*tmp != 0)
506 tmp++;
507
508 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
509 }
510
511 c = *tmp;
512 *tmp = 0;
513 ret = wstrdup(line);
514 *tmp = c;
515
516 if (c == 0)
517 return ret;
518 else
519 tmp++;
520
521 /* skip blanks */
522 while (*tmp == ' ' || *tmp == '\t')
523 tmp++;
524
525 if (*tmp != 0)
526 *next = tmp;
527
528 return ret;
529 }
530
531 static void separateCommand(char *line, char ***file, char **command)
532 {
533 char *token, *tmp = line;
534 WMArray *array = WMCreateArray(4);
535 int count, i;
536
537 *file = NULL;
538 *command = NULL;
539 do {
540 token = next_token(tmp, &tmp);
541 if (token) {
542 if (strcmp(token, "WITH") == 0) {
543 if (tmp != NULL && *tmp != 0)
544 *command = wstrdup(tmp);
545 else
546 wwarning(_("%s: missing command"), line);
547 break;
548 }
549 WMAddToArray(array, token);
550 }
551 } while (token != NULL && tmp != NULL);
552
553 count = WMGetArrayItemCount(array);
554 if (count > 0) {
555 *file = wmalloc(sizeof(char *) * (count + 1));
556 (*file)[count] = NULL;
557 for (i = 0; i < count; i++) {
558 (*file)[i] = WMGetFromArray(array, i);
559 }
560 }
561 WMFreeArray(array);
562 }
563
564 static WMenu *constructPLMenu(WScreen *screen, char *path)
565 {
566 WMPropList *pl = NULL;
567 WMenu *menu = NULL;
568
569 if (!path)
570 return NULL;
571
572 pl = WMReadPropListFromFile(path);
573 if (!pl)
574 return NULL;
575
576 menu = configureMenu(screen, pl, False);
577 if (!menu)
578 return NULL;
579
580 menu->on_destroy = removeShortcutsForMenu;
581 return menu;
582 }
583
584
585
586 static void constructMenu(WMenu * menu, WMenuEntry * entry)
587 {
588 WMenu *submenu;
589 struct stat stat_buf;
590 char **path;
591 char *cmd;
592 char *lpath = NULL;
593 int i, first = -1;
594 time_t last = 0;
595
596 separateCommand((char *)entry->clientdata, &path, &cmd);
597 if (path == NULL || *path == NULL || **path == 0) {
598 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
599 if (cmd)
600 wfree(cmd);
601 return;
602 }
603
604 if (path[0][0] == '|') {
605 /* pipe menu */
606
607 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
608 /* parse pipe */
609
610 submenu = readMenuPipe(menu->frame->screen_ptr, path);
611
612 if (submenu != NULL) {
613 if (path[0][1] == '|')
614 submenu->timestamp = 0;
615 else
616 submenu->timestamp = 1; /* there's no automatic reloading */
617 }
618 } else {
619 submenu = NULL;
620 }
621
622 } else {
623
624 /* try interpreting path as a proplist file */
625 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
626 /* if unsuccessful, try it as an old-style file */
627 if (!submenu) {
628
629 i = 0;
630 while (path[i] != NULL) {
631 char *tmp;
632
633 if (strcmp(path[i], "-noext") == 0) {
634 i++;
635 continue;
636 }
637
638 tmp = wexpandpath(path[i]);
639 wfree(path[i]);
640 lpath = getLocalizedMenuFile(tmp);
641 if (lpath) {
642 wfree(tmp);
643 path[i] = lpath;
644 lpath = NULL;
645 } else {
646 path[i] = tmp;
647 }
648
649 if (stat(path[i], &stat_buf) == 0) {
650 if (last < stat_buf.st_mtime)
651 last = stat_buf.st_mtime;
652 if (first < 0)
653 first = i;
654 } else {
655 werror(_("%s:could not stat menu"), path[i]);
656 /*goto finish; */
657 }
658
659 i++;
660 }
661
662 if (first < 0) {
663 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
664 goto finish;
665 }
666 stat(path[first], &stat_buf);
667 if (!menu->cascades[entry->cascade]
668 || menu->cascades[entry->cascade]->timestamp < last) {
669
670 if (S_ISDIR(stat_buf.st_mode)) {
671 /* menu directory */
672 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
673 if (submenu)
674 submenu->timestamp = last;
675 } else if (S_ISREG(stat_buf.st_mode)) {
676 /* menu file */
677
678 if (cmd || path[1])
679 wwarning(_("too many parameters in OPEN_MENU: %s"),
680 (char *)entry->clientdata);
681
682 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
683 if (submenu)
684 submenu->timestamp = stat_buf.st_mtime;
685 } else {
686 submenu = NULL;
687 }
688 } else {
689 submenu = NULL;
690 }
691 }
692 }
693
694 if (submenu) {
695 wMenuEntryRemoveCascade(menu, entry);
696 wMenuEntrySetCascade(menu, entry, submenu);
697 }
698
699 finish:
700 i = 0;
701 while (path[i] != NULL)
702 wfree(path[i++]);
703 wfree(path);
704 if (cmd)
705 wfree(cmd);
706 }
707
708 static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
709 {
710 WMenu *submenu = NULL;
711 char **path;
712 char *cmd;
713 int i;
714
715 separateCommand((char *)entry->clientdata, &path, &cmd);
716 if (path == NULL || *path == NULL || **path == 0) {
717 wwarning(_("invalid OPEN_PLMENU specification: %s"),
718 (char *)entry->clientdata);
719 if (cmd)
720 wfree(cmd);
721 return;
722 }
723
724 if (path[0][0] == '|') {
725 /* pipe menu */
726
727 if (!menu->cascades[entry->cascade]
728 || menu->cascades[entry->cascade]->timestamp == 0) {
729 /* parse pipe */
730 submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
731
732 if (submenu != NULL) {
733 if (path[0][1] == '|')
734 submenu->timestamp = 0;
735 else
736 submenu->timestamp = 1; /* there's no automatic reloading */
737 }
738 }
739 }
740
741 if (submenu) {
742 wMenuEntryRemoveCascade(menu, entry);
743 wMenuEntrySetCascade(menu, entry, submenu);
744 }
745
746 i = 0;
747 while (path[i] != NULL)
748 wfree(path[i++]);
749
750 wfree(path);
751 if (cmd)
752 wfree(cmd);
753
754 }
755 static void cleanupWorkspaceMenu(WMenu * menu)
756 {
757 if (menu->frame->screen_ptr->workspace_menu == menu)
758 menu->frame->screen_ptr->workspace_menu = NULL;
759 }
760
761 static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
762 {
763 WMenu *wsmenu;
764 WMenuEntry *entry;
765
766 if (scr->flags.added_workspace_menu) {
767 wwarning(_
768 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
769 return NULL;
770 } else {
771 scr->flags.added_workspace_menu = 1;
772
773 wsmenu = wWorkspaceMenuMake(scr, True);
774 wsmenu->on_destroy = cleanupWorkspaceMenu;
775
776 scr->workspace_menu = wsmenu;
777 entry = wMenuAddCallback(menu, title, NULL, NULL);
778 wMenuEntrySetCascade(menu, entry, wsmenu);
779
780 wWorkspaceMenuUpdate(scr, wsmenu);
781 }
782 return entry;
783 }
784
785 static void cleanupWindowsMenu(WMenu * menu)
786 {
787 if (menu->frame->screen_ptr->switch_menu == menu)
788 menu->frame->screen_ptr->switch_menu = NULL;
789 }
790
791 static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
792 {
793 WMenu *wwmenu;
794 WWindow *wwin;
795 WMenuEntry *entry;
796
797 if (scr->flags.added_windows_menu) {
798 wwarning(_
799 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
800 return NULL;
801 } else {
802 scr->flags.added_windows_menu = 1;
803
804 wwmenu = wMenuCreate(scr, _("Window List"), False);
805 wwmenu->on_destroy = cleanupWindowsMenu;
806 scr->switch_menu = wwmenu;
807 wwin = scr->focused_window;
808 while (wwin) {
809 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
810
811 wwin = wwin->prev;
812 }
813 entry = wMenuAddCallback(menu, title, NULL, NULL);
814 wMenuEntrySetCascade(menu, entry, wwmenu);
815 }
816 return entry;
817 }
818
819 static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
820 char *params, const char *file_name)
821 {
822 WScreen *scr;
823 WMenuEntry *entry = NULL;
824 Bool shortcutOk = False;
825
826 if (!menu)
827 return NULL;
828 scr = menu->frame->screen_ptr;
829 if (strcmp(command, "OPEN_MENU") == 0) {
830 if (!params) {
831 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
832 } else {
833 WMenu *dummy;
834 char *path;
835
836 path = wfindfile(DEF_CONFIG_PATHS, params);
837 if (!path) {
838 path = wstrdup(params);
839 }
840 dummy = wMenuCreate(scr, title, False);
841 dummy->on_destroy = removeShortcutsForMenu;
842 entry = wMenuAddCallback(menu, title, constructMenu, path);
843 entry->free_cdata = wfree;
844 wMenuEntrySetCascade(menu, entry, dummy);
845 }
846 } else if (strcmp(command, "OPEN_PLMENU") == 0) {
847 if (!params) {
848 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
849 } else {
850 WMenu *dummy;
851 char *path;
852
853 path = wfindfile(DEF_CONFIG_PATHS, params);
854 if (!path)
855 path = wstrdup(params);
856
857 dummy = wMenuCreate(scr, title, False);
858 dummy->on_destroy = removeShortcutsForMenu;
859 entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
860 entry->free_cdata = wfree;
861 wMenuEntrySetCascade(menu, entry, dummy);
862 }
863 } else if (strcmp(command, "EXEC") == 0) {
864 if (!params)
865 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
866 else {
867 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
868 entry->free_cdata = wfree;
869 shortcutOk = True;
870 }
871 } else if (strcmp(command, "SHEXEC") == 0) {
872 if (!params)
873 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
874 else {
875 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
876 entry->free_cdata = wfree;
877 shortcutOk = True;
878 }
879 } else if (strcmp(command, "EXIT") == 0) {
880
881 if (params && strcmp(params, "QUICK") == 0)
882 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
883 else
884 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
885
886 shortcutOk = True;
887 } else if (strcmp(command, "SHUTDOWN") == 0) {
888
889 if (params && strcmp(params, "QUICK") == 0)
890 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
891 else
892 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
893
894 shortcutOk = True;
895 } else if (strcmp(command, "REFRESH") == 0) {
896 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
897
898 shortcutOk = True;
899 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
900 entry = addWorkspaceMenu(scr, menu, title);
901
902 shortcutOk = True;
903 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
904 entry = addWindowsMenu(scr, menu, title);
905
906 shortcutOk = True;
907 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
908 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
909
910 shortcutOk = True;
911 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
912 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
913
914 shortcutOk = True;
915 } else if (strcmp(command, "SHOW_ALL") == 0) {
916 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
917
918 shortcutOk = True;
919 } else if (strcmp(command, "RESTART") == 0) {
920 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
921 entry->free_cdata = wfree;
922 shortcutOk = True;
923 } else if (strcmp(command, "SAVE_SESSION") == 0) {
924 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
925
926 shortcutOk = True;
927 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
928 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
929 shortcutOk = True;
930 } else if (strcmp(command, "INFO_PANEL") == 0) {
931 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
932 shortcutOk = True;
933 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
934 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
935 shortcutOk = True;
936 } else {
937 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
938
939 return NULL;
940 }
941
942 if (shortcut && entry) {
943 if (!shortcutOk) {
944 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
945 } else {
946 if (addShortcut(file_name, shortcut, menu, entry)) {
947
948 entry->rtext = GetShortcutString(shortcut);
949 /*
950 entry->rtext = wstrdup(shortcut);
951 */
952 }
953 }
954 }
955
956 return entry;
957 }
958
959 /******************* Menu Configuration From File *******************/
960
961 static void freeline(char *title, char *command, char *parameter, char *shortcut)
962 {
963 wfree(title);
964 wfree(command);
965 wfree(parameter);
966 wfree(shortcut);
967 }
968
969 static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
970 {
971 char *command, *params, *shortcut, *title;
972
973 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
974
975 if (command == NULL || !command[0]) {
976 WMenuParserError(parser, _("missing command in menu config") );
977 freeline(title, command, params, shortcut);
978 goto error;
979 }
980
981 if (strcasecmp(command, "MENU") == 0) {
982 WMenu *cascade;
983
984 /* start submenu */
985
986 cascade = wMenuCreate(scr, M_(title), False);
987 cascade->on_destroy = removeShortcutsForMenu;
988 if (!parseCascade(scr, cascade, parser)) {
989 wMenuDestroy(cascade, True);
990 } else {
991 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
992 }
993 } else if (strcasecmp(command, "END") == 0) {
994 /* end of menu */
995 freeline(title, command, params, shortcut);
996 return menu;
997 } else {
998 /* normal items */
999 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
1000 }
1001 freeline(title, command, params, shortcut);
1002 }
1003
1004 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
1005
1006 error:
1007 return NULL;
1008 }
1009
1010 static WMenu *readMenuFile(WScreen * scr, char *file_name)
1011 {
1012 WMenu *menu = NULL;
1013 FILE *file = NULL;
1014 WMenuParser parser;
1015 char *command, *params, *shortcut, *title;
1016
1017 file = fopen(file_name, "rb");
1018 if (!file) {
1019 werror(_("%s:could not open menu file"), file_name);
1020 return NULL;
1021 }
1022 parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
1023 menu_parser_register_macros(parser);
1024
1025 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1026
1027 if (command == NULL || !command[0]) {
1028 WMenuParserError(parser, _("missing command in menu config") );
1029 freeline(title, command, params, shortcut);
1030 break;
1031 }
1032 if (strcasecmp(command, "MENU") == 0) {
1033 menu = wMenuCreate(scr, M_(title), True);
1034 menu->on_destroy = removeShortcutsForMenu;
1035 if (!parseCascade(scr, menu, parser)) {
1036 wMenuDestroy(menu, True);
1037 menu = NULL;
1038 }
1039 freeline(title, command, params, shortcut);
1040 break;
1041 } else {
1042 WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
1043 freeline(title, command, params, shortcut);
1044 break;
1045 }
1046 freeline(title, command, params, shortcut);
1047 }
1048
1049 WMenuParserDelete(parser);
1050 fclose(file);
1051
1052 return menu;
1053 }
1054
1055 /************ Menu Configuration From Pipe *************/
1056 static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1057 {
1058 WMPropList *plist = NULL;
1059 WMenu *menu = NULL;
1060 char *filename;
1061 char flat_file[MAXLINE];
1062 int i;
1063
1064 flat_file[0] = '\0';
1065
1066 for (i = 0; file_name[i] != NULL; i++) {
1067 strcat(flat_file, file_name[i]);
1068 strcat(flat_file, " ");
1069 }
1070 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1071
1072 plist = WMReadPropListFromPipe(filename);
1073
1074 if (!plist)
1075 return NULL;
1076
1077 menu = configureMenu(scr, plist, False);
1078 if (!menu)
1079 return NULL;
1080
1081 menu->on_destroy = removeShortcutsForMenu;
1082 return menu;
1083 }
1084
1085 static WMenu *readMenuPipe(WScreen * scr, char **file_name)
1086 {
1087 WMenu *menu = NULL;
1088 FILE *file = NULL;
1089 WMenuParser parser;
1090 char *command, *params, *shortcut, *title;
1091 char *filename;
1092 char flat_file[MAXLINE];
1093 int i;
1094
1095 flat_file[0] = '\0';
1096
1097 for (i = 0; file_name[i] != NULL; i++) {
1098 strcat(flat_file, file_name[i]);
1099 strcat(flat_file, " ");
1100 }
1101 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1102
1103 file = popen(filename, "r");
1104 if (!file) {
1105 werror(_("%s:could not open menu file"), filename);
1106 return NULL;
1107 }
1108 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
1109 menu_parser_register_macros(parser);
1110
1111 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
1112
1113 if (command == NULL || !command[0]) {
1114 WMenuParserError(parser, _("missing command in menu config") );
1115 freeline(title, command, params, shortcut);
1116 break;
1117 }
1118 if (strcasecmp(command, "MENU") == 0) {
1119 menu = wMenuCreate(scr, M_(title), True);
1120 menu->on_destroy = removeShortcutsForMenu;
1121 if (!parseCascade(scr, menu, parser)) {
1122 wMenuDestroy(menu, True);
1123 menu = NULL;
1124 }
1125 freeline(title, command, params, shortcut);
1126 break;
1127 } else {
1128 WMenuParserError(parser, _("no title given for the root menu") );
1129 freeline(title, command, params, shortcut);
1130 break;
1131 }
1132
1133 freeline(title, command, params, shortcut);
1134 }
1135
1136 WMenuParserDelete(parser);
1137 pclose(file);
1138
1139 return menu;
1140 }
1141
1142 typedef struct {
1143 char *name;
1144 int index;
1145 } dir_data;
1146
1147 static int myCompare(const void *d1, const void *d2)
1148 {
1149 dir_data *p1 = *(dir_data **) d1;
1150 dir_data *p2 = *(dir_data **) d2;
1151
1152 return strcmp(p1->name, p2->name);
1153 }
1154
1155 /***** Preset some macro for file parser *****/
1156 static void menu_parser_register_macros(WMenuParser parser)
1157 {
1158 Visual *visual;
1159 char buf[32];
1160
1161 // Used to return CPP verion, now returns wmaker's version
1162 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1163
1164 // All macros below were historically defined by WindowMaker
1165 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1166 snprintf(buf, sizeof(buf), "%d", visual->class);
1167 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1168
1169 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1170 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1171
1172 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1173 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1174
1175 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1176 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1177
1178 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1179
1180 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1181 }
1182
1183 /************ Menu Configuration From Directory *************/
1184
1185 static Bool isFilePackage(char *file)
1186 {
1187 int l;
1188
1189 /* check if the extension indicates this file is a
1190 * file package. For now, only recognize .themed */
1191
1192 l = strlen(file);
1193
1194 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1195 return True;
1196 } else {
1197 return False;
1198 }
1199 }
1200
1201 static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
1202 {
1203 DIR *dir;
1204 struct dirent *dentry;
1205 struct stat stat_buf;
1206 WMenu *menu = NULL;
1207 char *buffer;
1208 WMArray *dirs = NULL, *files = NULL;
1209 WMArrayIterator iter;
1210 int length, i, have_space = 0;
1211 dir_data *data;
1212 int stripExtension = 0;
1213
1214 dirs = WMCreateArray(16);
1215 files = WMCreateArray(16);
1216
1217 i = 0;
1218 while (path[i] != NULL) {
1219 if (strcmp(path[i], "-noext") == 0) {
1220 stripExtension = 1;
1221 i++;
1222 continue;
1223 }
1224
1225 dir = opendir(path[i]);
1226 if (!dir) {
1227 i++;
1228 continue;
1229 }
1230
1231 while ((dentry = readdir(dir))) {
1232
1233 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1234 continue;
1235
1236 if (dentry->d_name[0] == '.')
1237 continue;
1238
1239 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1240 if (!buffer) {
1241 werror(_("out of memory while constructing directory menu %s"), path[i]);
1242 break;
1243 }
1244
1245 strcpy(buffer, path[i]);
1246 strcat(buffer, "/");
1247 strcat(buffer, dentry->d_name);
1248
1249 if (stat(buffer, &stat_buf) != 0) {
1250 werror(_("%s:could not stat file \"%s\" in menu directory"),
1251 path[i], dentry->d_name);
1252 } else {
1253 Bool isFilePack = False;
1254
1255 data = NULL;
1256 if (S_ISDIR(stat_buf.st_mode)
1257 && !(isFilePack = isFilePackage(dentry->d_name))) {
1258
1259 /* access always returns success for user root */
1260 if (access(buffer, X_OK) == 0) {
1261 /* Directory is accesible. Add to directory list */
1262
1263 data = (dir_data *) wmalloc(sizeof(dir_data));
1264 data->name = wstrdup(dentry->d_name);
1265 data->index = i;
1266
1267 WMAddToArray(dirs, data);
1268 }
1269 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1270 /* Hack because access always returns X_OK success for user root */
1271 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1272 if ((command != NULL && access(buffer, R_OK) == 0) ||
1273 (command == NULL && access(buffer, X_OK) == 0 &&
1274 (stat_buf.st_mode & S_IXANY))) {
1275
1276 data = (dir_data *) wmalloc(sizeof(dir_data));
1277 data->name = wstrdup(dentry->d_name);
1278 data->index = i;
1279
1280 WMAddToArray(files, data);
1281 }
1282 }
1283 }
1284 free(buffer);
1285 }
1286
1287 closedir(dir);
1288 i++;
1289 }
1290
1291 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1292 WMFreeArray(dirs);
1293 WMFreeArray(files);
1294 return NULL;
1295 }
1296
1297 WMSortArray(dirs, myCompare);
1298 WMSortArray(files, myCompare);
1299
1300 menu = wMenuCreate(scr, M_(title), False);
1301 menu->on_destroy = removeShortcutsForMenu;
1302
1303 WM_ITERATE_ARRAY(dirs, data, iter) {
1304 /* New directory. Use same OPEN_MENU command that was used
1305 * for the current directory. */
1306 length = strlen(path[data->index]) + strlen(data->name) + 6;
1307 if (stripExtension)
1308 length += 7;
1309 if (command)
1310 length += strlen(command) + 6;
1311 buffer = malloc(length);
1312 if (!buffer) {
1313 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1314 break;
1315 }
1316
1317 buffer[0] = '\0';
1318 if (stripExtension)
1319 strcat(buffer, "-noext ");
1320
1321 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1322
1323 if (have_space)
1324 strcat(buffer, "\"");
1325 strcat(buffer, path[data->index]);
1326
1327 strcat(buffer, "/");
1328 strcat(buffer, data->name);
1329 if (have_space)
1330 strcat(buffer, "\"");
1331 if (command) {
1332 strcat(buffer, " WITH ");
1333 strcat(buffer, command);
1334 }
1335
1336 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
1337
1338 wfree(buffer);
1339 if (data->name)
1340 wfree(data->name);
1341 wfree(data);
1342 }
1343
1344 WM_ITERATE_ARRAY(files, data, iter) {
1345 /* executable: add as entry */
1346 length = strlen(path[data->index]) + strlen(data->name) + 6;
1347 if (command)
1348 length += strlen(command);
1349
1350 buffer = malloc(length);
1351 if (!buffer) {
1352 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
1353 break;
1354 }
1355
1356 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1357 if (command != NULL) {
1358 strcpy(buffer, command);
1359 strcat(buffer, " ");
1360 if (have_space)
1361 strcat(buffer, "\"");
1362 strcat(buffer, path[data->index]);
1363 } else {
1364 if (have_space) {
1365 buffer[0] = '"';
1366 buffer[1] = 0;
1367 strcat(buffer, path[data->index]);
1368 } else {
1369 strcpy(buffer, path[data->index]);
1370 }
1371 }
1372 strcat(buffer, "/");
1373 strcat(buffer, data->name);
1374 if (have_space)
1375 strcat(buffer, "\"");
1376
1377 if (stripExtension) {
1378 char *ptr = strrchr(data->name, '.');
1379 if (ptr && ptr != data->name)
1380 *ptr = 0;
1381 }
1382 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
1383
1384 wfree(buffer);
1385 if (data->name)
1386 wfree(data->name);
1387 wfree(data);
1388 }
1389
1390 WMFreeArray(files);
1391 WMFreeArray(dirs);
1392
1393 return menu;
1394 }
1395
1396 /************ Menu Configuration From WMRootMenu *************/
1397
1398 static WMenu *makeDefaultMenu(WScreen * scr)
1399 {
1400 WMenu *menu = NULL;
1401
1402 menu = wMenuCreate(scr, _("Commands"), True);
1403 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1404 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
1405 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1406 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1407 return menu;
1408 }
1409
1410 /*
1411 *----------------------------------------------------------------------
1412 * configureMenu--
1413 * Reads root menu configuration from defaults database.
1414 *
1415 *----------------------------------------------------------------------
1416 */
1417 static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
1418 {
1419 WMenu *menu = NULL;
1420 WMPropList *elem;
1421 int i, count;
1422 WMPropList *title, *command, *params;
1423 char *tmp, *mtitle;
1424
1425 if (WMIsPLString(definition)) {
1426 struct stat stat_buf;
1427 char *path = NULL;
1428 Bool menu_is_default = False;
1429
1430 /* menu definition is a string. Probably a path, so parse the file */
1431
1432 tmp = wexpandpath(WMGetFromPLString(definition));
1433
1434 path = getLocalizedMenuFile(tmp);
1435
1436 if (!path)
1437 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1438
1439 if (!path) {
1440 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1441 menu_is_default = True;
1442 }
1443
1444 if (!path) {
1445 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
1446 wfree(tmp);
1447 return NULL;
1448 }
1449
1450 if (stat(path, &stat_buf) < 0) {
1451 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1452 wfree(path);
1453 wfree(tmp);
1454 return NULL;
1455 }
1456
1457 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1458 /* if the pointer in WMRootMenu has changed */
1459 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1460
1461 if (menu_is_default) {
1462 wwarning(_
1463 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1464 path);
1465 }
1466
1467 menu = readMenuFile(scr, path);
1468 if (menu)
1469 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1470 } else {
1471 menu = NULL;
1472 }
1473 wfree(path);
1474 wfree(tmp);
1475
1476 return menu;
1477 }
1478
1479 count = WMGetPropListItemCount(definition);
1480 if (count == 0)
1481 return NULL;
1482
1483 elem = WMGetFromPLArray(definition, 0);
1484 if (!WMIsPLString(elem)) {
1485 tmp = WMGetPropListDescription(elem, False);
1486 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1487 wfree(tmp);
1488 return NULL;
1489 }
1490 mtitle = WMGetFromPLString(elem);
1491
1492 menu = wMenuCreate(scr, M_(mtitle), False);
1493 menu->on_destroy = removeShortcutsForMenu;
1494
1495 #ifdef GLOBAL_SUBMENU_FILE
1496 if (includeGlobals) {
1497 WMenu *submenu;
1498 WMenuEntry *mentry;
1499
1500 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1501
1502 if (submenu) {
1503 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1504 wMenuEntrySetCascade(menu, mentry, submenu);
1505 }
1506 }
1507 #endif
1508
1509 for (i = 1; i < count; i++) {
1510 elem = WMGetFromPLArray(definition, i);
1511 #if 0
1512 if (WMIsPLString(elem)) {
1513 char *file;
1514
1515 file = WMGetFromPLString(elem);
1516
1517 }
1518 #endif
1519 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1520 goto error;
1521
1522 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1523 WMenu *submenu;
1524 WMenuEntry *mentry;
1525
1526 /* submenu */
1527 submenu = configureMenu(scr, elem, True);
1528 if (submenu) {
1529 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1530 wMenuEntrySetCascade(menu, mentry, submenu);
1531 }
1532 } else {
1533 int idx = 0;
1534 WMPropList *shortcut;
1535 /* normal entry */
1536
1537 title = WMGetFromPLArray(elem, idx++);
1538 shortcut = WMGetFromPLArray(elem, idx++);
1539 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1540 shortcut = WMGetFromPLArray(elem, idx++);
1541 command = WMGetFromPLArray(elem, idx++);
1542 } else {
1543 command = shortcut;
1544 shortcut = NULL;
1545 }
1546 params = WMGetFromPLArray(elem, idx++);
1547
1548 if (!title || !command)
1549 goto error;
1550
1551 addMenuEntry(menu, M_(WMGetFromPLString(title)),
1552 shortcut ? WMGetFromPLString(shortcut) : NULL,
1553 WMGetFromPLString(command),
1554 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1555 }
1556 continue;
1557
1558 error:
1559 tmp = WMGetPropListDescription(elem, False);
1560 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1561 wfree(tmp);
1562 }
1563
1564 return menu;
1565 }
1566
1567 /*
1568 *----------------------------------------------------------------------
1569 * OpenRootMenu--
1570 * Opens the root menu, parsing the menu configuration from the
1571 * defaults database.
1572 * If the menu is already mapped and is not sticked to the
1573 * root window, it will be unmapped.
1574 *
1575 * Side effects:
1576 * The menu may be remade.
1577 *
1578 * Notes:
1579 * Construction of OPEN_MENU entries are delayed to the moment the
1580 * user map's them.
1581 *----------------------------------------------------------------------
1582 */
1583 void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
1584 {
1585 WMenu *menu = NULL;
1586 WMPropList *definition;
1587 /*
1588 static WMPropList *domain=NULL;
1589
1590 if (!domain) {
1591 domain = WMCreatePLString("WMRootMenu");
1592 }
1593 */
1594
1595 scr->flags.root_menu_changed_shortcuts = 0;
1596 scr->flags.added_workspace_menu = 0;
1597 scr->flags.added_windows_menu = 0;
1598
1599 if (scr->root_menu && scr->root_menu->flags.mapped) {
1600 menu = scr->root_menu;
1601 if (!menu->flags.buttoned) {
1602 wMenuUnmap(menu);
1603 } else {
1604 wRaiseFrame(menu->frame->core);
1605
1606 if (keyboard)
1607 wMenuMapAt(menu, 0, 0, True);
1608 else
1609 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1610 }
1611 return;
1612 }
1613
1614 definition = WDRootMenu->dictionary;
1615
1616 /*
1617 definition = PLGetDomain(domain);
1618 */
1619 if (definition) {
1620 if (WMIsPLArray(definition)) {
1621 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1622 menu = configureMenu(scr, definition, True);
1623 if (menu)
1624 menu->timestamp = WDRootMenu->timestamp;
1625
1626 } else
1627 menu = NULL;
1628 } else {
1629 menu = configureMenu(scr, definition, True);
1630 }
1631 }
1632
1633 if (!menu) {
1634 /* menu hasn't changed or could not be read */
1635 if (!scr->root_menu) {
1636 wMessageDialog(scr, _("Error"),
1637 _("The applications menu could not be loaded. "
1638 "Look at the console output for a detailed "
1639 "description of the errors."), _("OK"), NULL, NULL);
1640
1641 menu = makeDefaultMenu(scr);
1642 scr->root_menu = menu;
1643 }
1644 menu = scr->root_menu;
1645 } else {
1646 /* new root menu */
1647 if (scr->root_menu) {
1648 wMenuDestroy(scr->root_menu, True);
1649 }
1650 scr->root_menu = menu;
1651 }
1652 if (menu) {
1653 int newx, newy;
1654
1655 if (keyboard && x == 0 && y == 0) {
1656 newx = newy = 0;
1657 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1658 newx = x - menu->frame->core->width / 2;
1659 newy = y - menu->frame->core->height / 2;
1660 } else {
1661 newx = x - menu->frame->core->width / 2;
1662 newy = y;
1663 }
1664 wMenuMapAt(menu, newx, newy, keyboard);
1665 }
1666
1667 if (scr->flags.root_menu_changed_shortcuts)
1668 rebindKeygrabs(scr);
1669 }