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 */