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