Added the crash dialog panel
[wmaker-crm.git] / src / rootmenu.c
blob86a1feb4ffd7c1fd4c94ffaa5383fbc5e6fe9d4b
1 /* rootmenu.c- user defined menu
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
6 * Copyright (c) 1998 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.
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.
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.
24 #include "wconfig.h"
26 #ifndef LITE
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34 #include <sys/types.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <time.h>
38 #include <dirent.h>
40 #include <X11/Xlib.h>
41 #include <X11/Xutil.h>
42 #include <X11/Xatom.h>
44 #include "WindowMaker.h"
45 #include "actions.h"
46 #include "menu.h"
47 #include "funcs.h"
48 #include "dialog.h"
49 #include "keybind.h"
50 #include "stacking.h"
51 #include "workspace.h"
52 #include "defaults.h"
53 #include "framewin.h"
54 #include "session.h"
55 #include "xmodifier.h"
56 #include <proplist.h>
58 #include "list.h"
61 extern char *Locale;
63 extern WDDomain *WDRootMenu;
65 extern Cursor wCursor[WCUR_LAST];
67 extern Time LastTimestamp;
69 extern WPreferences wPreferences;
71 extern int wScreenCount;
73 static WMenu *readMenuPipe(WScreen *scr, char **file_name);
74 static WMenu *readMenuFile(WScreen *scr, char *file_name);
75 static WMenu *readMenuDirectory(WScreen *scr, char *title, char **file_name,
76 char *command);
79 typedef struct Shortcut {
80 struct Shortcut *next;
82 int modifier;
83 KeyCode keycode;
84 WMenuEntry *entry;
85 WMenu *menu;
86 } Shortcut;
90 static Shortcut *shortcutList = NULL;
94 * Syntax:
95 * # main menu
96 * "Menu Name" MENU
97 * "Title" EXEC command_to_exec -params
98 * "Submenu" MENU
99 * "Title" EXEC command_to_exec -params
100 * "Submenu" END
101 * "Workspaces" WORKSPACE_MENU
102 * "Title" built_in_command
103 * "Quit" EXIT
104 * "Quick Quit" EXIT QUICK
105 * "Menu Name" END
107 * Commands may be preceded by SHORTCUT key
109 * Built-in commands:
111 * INFO_PANEL - shows the Info Panel
112 * LEGAL_PANEL - shows the Legal info panel
113 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
114 * REFRESH - forces the desktop to be repainted
115 * EXIT [QUICK] - exit the window manager [without confirmation]
116 * EXEC <program> - execute an external program
117 * WORKSPACE_MENU - places the workspace submenu
118 * ARRANGE_ICONS
119 * RESTART [<window manager>] - restarts the window manager
120 * SHOW_ALL - unhide all windows on workspace
121 * HIDE_OTHERS - hides all windows excep the focused one
122 * OPEN_MENU file - read menu data from file which must be a valid menu file.
123 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
124 * - read menu data from directory(ies) and
125 * eventually precede each with a command.
126 * OPEN_MENU | command
127 * - opens command and uses its stdout to construct and insert
128 * the resulting menu in current position. The output of
129 * command must be a valid menu description.
130 * The space between '|' and command is optional.
131 * SAVE_SESSION - saves the current state of the desktop, which include
132 * all running applications, all their hints (geometry,
133 * position on screen, workspace they live on, the dock
134 * or clip from where they were launched, and
135 * if minimized, shaded or hidden. Also saves the current
136 * workspace the user is on. All will be restored on every
137 * start of windowmaker until another SAVE_SESSION or
138 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
139 * WindowMaker domain file, then saving is automatically
140 * done on every windowmaker exit, overwriting any
141 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
142 * dock state now.
143 * CLEAR_SESSION - clears any previous saved session. This will not have
144 * any effect if SaveSessionOnExit is True.
148 #define MAX(a,b) ((a)>(b) ? (a) : (b))
151 #define M_QUICK 1
153 /* menu commands */
155 static void
156 execCommand(WMenu *menu, WMenuEntry *entry)
158 char *cmdline;
160 cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata);
162 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
163 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT],
164 CurrentTime);
165 XSync(dpy, 0);
167 if (cmdline) {
168 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
169 free(cmdline);
171 XUngrabPointer(dpy, CurrentTime);
172 XSync(dpy, 0);
176 static void
177 exitCommand(WMenu *menu, WMenuEntry *entry)
179 static int inside = 0;
181 /* prevent reentrant calls */
182 if (inside)
183 return;
184 inside = 1;
186 if ((long)entry->clientdata==M_QUICK
187 || wMessageDialog(menu->frame->screen_ptr, _("Exit"),
188 _("Exit window manager?"),
189 _("Exit"), _("Cancel"), NULL)==WAPRDefault) {
190 #ifdef DEBUG
191 printf("Exiting WindowMaker.\n");
192 #endif
193 Shutdown(WSExitMode);
195 inside = 0;
199 static void
200 shutdownCommand(WMenu *menu, WMenuEntry *entry)
202 static int inside = 0;
203 int result;
205 /* prevent reentrant calls */
206 if (inside)
207 return;
208 inside = 1;
210 #define R_CANCEL 0
211 #define R_CLOSE 1
212 #define R_KILL 2
215 result = R_CANCEL;
216 if ((long)entry->clientdata==M_QUICK)
217 result = R_CLOSE;
218 else {
219 #ifdef XSMP_ENABLED
220 if (wSessionIsManaged()) {
221 int r;
223 r = wMessageDialog(menu->frame->screen_ptr,
224 _("Close X session"),
225 _("Close Window System session?\n"
226 "Kill might close applications with unsaved data."),
227 _("Close"), _("Kill"), _("Cancel"));
228 if (r==WAPRDefault)
229 result = R_CLOSE;
230 else if (r==WAPRAlternate)
231 result = R_KILL;
232 } else
233 #endif
235 int r;
237 r = wMessageDialog(menu->frame->screen_ptr,
238 _("Kill X session"),
239 _("Kill Window System session?\n"
240 "(all applications will be closed)"),
241 _("Kill"), _("Cancel"), NULL);
242 if (r==WAPRDefault)
243 result = R_KILL;
247 if (result!=R_CANCEL) {
248 #ifdef XSMP_ENABLED
249 if (result == R_CLOSE) {
250 Shutdown(WSLogoutMode);
251 } else
252 #endif /* XSMP_ENABLED */
254 Shutdown(WSKillMode);
257 #undef R_CLOSE
258 #undef R_CANCEL
259 #undef R_KILL
260 inside = 0;
264 static void
265 restartCommand(WMenu *menu, WMenuEntry *entry)
267 Shutdown(WSRestartPreparationMode);
268 Restart((char*)entry->clientdata);
272 static void
273 refreshCommand(WMenu *menu, WMenuEntry *entry)
275 wRefreshDesktop(menu->frame->screen_ptr);
279 static void
280 arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
282 wArrangeIcons(menu->frame->screen_ptr, True);
285 static void
286 showAllCommand(WMenu *menu, WMenuEntry *entry)
288 wShowAllWindows(menu->frame->screen_ptr);
291 static void
292 hideOthersCommand(WMenu *menu, WMenuEntry *entry)
294 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
298 static void
299 saveSessionCommand(WMenu *menu, WMenuEntry *entry)
301 if (!wPreferences.save_session_on_exit)
302 wSessionSaveState(menu->frame->screen_ptr);
304 wScreenSaveState(menu->frame->screen_ptr);
308 static void
309 clearSessionCommand(WMenu *menu, WMenuEntry *entry)
311 wSessionClearState(menu->frame->screen_ptr);
315 static void
316 infoPanelCommand(WMenu *menu, WMenuEntry *entry)
318 wShowInfoPanel(menu->frame->screen_ptr);
322 static void
323 legalPanelCommand(WMenu *menu, WMenuEntry *entry)
325 wShowLegalPanel(menu->frame->screen_ptr);
329 /********************************************************************/
332 static char*
333 getLocalizedMenuFile(char *menu)
335 char *buffer;
336 char *ptr;
338 if (!Locale)
339 return NULL;
341 buffer = wmalloc(strlen(menu)+32);
343 /* try menu.locale_name */
344 sprintf(buffer, "%s.%s", menu, Locale);
345 if (access(buffer, F_OK)==0) {
346 return buffer;
348 /* check if it is in the form aa_bb.encoding and check for aa_bb */
349 ptr = strchr(Locale, '.');
350 if (ptr) {
351 *ptr = 0;
352 if (access(buffer, F_OK)==0) {
353 return buffer;
356 /* now check for aa */
357 ptr = strchr(buffer, '_');
358 if (ptr) {
359 *ptr = 0;
360 if (access(buffer, F_OK)==0) {
361 return buffer;
365 return NULL;
369 static void
370 raiseMenus(WMenu *menu)
372 int i;
374 if (menu->flags.mapped) {
375 wRaiseFrame(menu->frame->core);
377 for (i=0; i<menu->cascade_no; i++) {
378 if (menu->cascades[i])
379 raiseMenus(menu->cascades[i]);
385 Bool
386 wRootMenuPerformShortcut(XEvent *event)
388 Shortcut *ptr;
389 int modifiers;
390 int done = 0;
392 /* ignore CapsLock */
393 modifiers = event->xkey.state & ValidModMask;
395 for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
396 if (ptr->keycode==0)
397 continue;
399 if (ptr->keycode==event->xkey.keycode && (ptr->modifier==modifiers)) {
400 (*ptr->entry->callback)(ptr->menu, ptr->entry);
401 done = True;
404 return done;
408 void
409 wRootMenuBindShortcuts(Window window)
411 Shortcut *ptr;
413 ptr = shortcutList;
414 while (ptr) {
415 if (ptr->modifier!=AnyModifier) {
416 XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
417 window, True, GrabModeAsync, GrabModeAsync);
418 #ifdef NUMLOCK_HACK
419 wHackedGrabKey(ptr->keycode, ptr->modifier,
420 window, True, GrabModeAsync, GrabModeAsync);
421 #endif
423 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
424 GrabModeAsync, GrabModeAsync);
425 ptr = ptr->next;
430 static void
431 rebindKeygrabs(WScreen *scr)
433 WWindow *wwin;
435 wwin = scr->focused_window;
437 while (wwin!=NULL) {
438 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
440 if (!WFLAGP(wwin, no_bind_keys)) {
441 wWindowSetKeyGrabs(wwin);
443 wwin = wwin->prev;
448 static void
449 removeShortcutsForMenu(WMenu *menu)
451 Shortcut *ptr, *tmp;
452 Shortcut *newList = NULL;
454 ptr = shortcutList;
455 while (ptr!=NULL) {
456 tmp = ptr->next;
457 if (ptr->menu == menu) {
458 free(ptr);
459 } else {
460 ptr->next = newList;
461 newList = ptr;
463 ptr = tmp;
465 shortcutList = newList;
466 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
470 static Bool
471 addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
472 WMenuEntry *entry)
474 Shortcut *ptr;
475 KeySym ksym;
476 char *k;
477 char buf[128], *b;
479 ptr = wmalloc(sizeof(Shortcut));
481 strcpy(buf, shortcutDefinition);
482 b = (char*)buf;
484 /* get modifiers */
485 ptr->modifier = 0;
486 while ((k = strchr(b, '+'))!=NULL) {
487 int mod;
489 *k = 0;
490 mod = wXModifierFromKey(b);
491 if (mod<0) {
492 wwarning(_("%s:invalid key modifier \"%s\""), file, b);
493 free(ptr);
494 return False;
496 ptr->modifier |= mod;
498 b = k+1;
501 /* get key */
502 ksym = XStringToKeysym(b);
504 if (ksym==NoSymbol) {
505 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
506 file, shortcutDefinition, entry->text);
507 free(ptr);
508 return False;
511 ptr->keycode = XKeysymToKeycode(dpy, ksym);
512 if (ptr->keycode==0) {
513 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
514 shortcutDefinition, entry->text);
515 free(ptr);
516 return False;
519 ptr->menu = menu;
520 ptr->entry = entry;
522 ptr->next = shortcutList;
523 shortcutList = ptr;
525 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
527 return True;
531 /*******************************/
533 static char*
534 cropline(char *line)
536 char *end;
538 if (strlen(line)==0)
539 return line;
541 end = &(line[strlen(line)])-1;
542 while (isspace(*line) && *line!=0) line++;
543 while (end>line && isspace(*end)) {
544 *end=0;
545 end--;
547 return line;
551 static char*
552 next_token(char *line, char **next)
554 char *tmp, c;
555 char *ret;
557 *next = NULL;
558 while (*line==' ' || *line=='\t') line++;
560 tmp = line;
562 if (*tmp=='"') {
563 tmp++; line++;
564 while (*tmp!=0 && *tmp!='"') tmp++;
565 if (*tmp!='"') {
566 wwarning(_("%s: unmatched '\"' in menu file"), line);
567 return NULL;
569 } else {
570 do {
571 if (*tmp=='\\')
572 tmp++;
574 if (*tmp!=0)
575 tmp++;
577 } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
580 c = *tmp;
581 *tmp = 0;
582 ret = wstrdup(line);
583 *tmp = c;
585 if (c==0)
586 return ret;
587 else
588 tmp++;
590 /* skip blanks */
591 while (*tmp==' ' || *tmp=='\t') tmp++;
593 if (*tmp!=0)
594 *next = tmp;
596 return ret;
600 static void
601 separateCommand(char *line, char ***file, char **command)
603 char *token, *tmp = line;
604 LinkedList *list = NULL;
605 int count, i;
607 *file = NULL;
608 *command = NULL;
609 do {
610 token = next_token(tmp, &tmp);
611 if (token) {
612 if (strcmp(token, "WITH")==0) {
613 if (tmp!=NULL && *tmp!=0)
614 *command = wstrdup(tmp);
615 else
616 wwarning(_("%s: missing command"), line);
617 break;
619 list = list_cons(token, list);
621 } while (token!=NULL && tmp!=NULL);
623 count = list_length(list);
624 if (count>0) {
625 *file = wmalloc(sizeof(char*)*(count+1));
626 i = count;
627 (*file)[count] = NULL;
628 while (list!=NULL) {
629 (*file)[--i] = list->head;
630 list_remove_head(&list);
636 static void
637 constructMenu(WMenu *menu, WMenuEntry *entry)
639 WMenu *submenu;
640 struct stat stat_buf;
641 char **path;
642 char *cmd;
643 char *lpath = NULL;
644 int i, first=-1;
645 time_t last=0;
647 separateCommand((char*)entry->clientdata, &path, &cmd);
648 if (!path || *path==NULL || **path==0) {
649 wwarning(_("invalid OPEN_MENU specification: %s"),
650 (char*)entry->clientdata);
651 return;
654 if (path[0][0]=='|') {
655 /* pipe menu */
657 if (!menu->cascades[entry->cascade] ||
658 menu->cascades[entry->cascade]->timestamp == 0) {
659 /* parse pipe */
661 submenu = readMenuPipe(menu->frame->screen_ptr, path);
663 /* there's no automatic reloading */
664 if(submenu != NULL)
665 submenu->timestamp = 1;
666 } else {
667 submenu = NULL;
670 } else {
671 i=0;
672 while(path[i] != NULL) {
673 char *tmp;
675 if (strcmp(path[i], "-noext")==0) {
676 i++;
677 continue;
680 tmp = wexpandpath(path[i]);
681 free(path[i]);
682 lpath = getLocalizedMenuFile(tmp);
683 if (lpath) {
684 free(tmp);
685 path[i] = lpath;
686 lpath = NULL;
687 } else {
688 path[i] = tmp;
691 if (stat(path[i], &stat_buf)==0) {
692 if (last < stat_buf.st_mtime)
693 last = stat_buf.st_mtime;
694 if (first<0)
695 first=i;
696 } else {
697 wsyserror(_("%s:could not stat menu"), path[i]);
698 /*goto finish;*/
701 i++;
704 if (first < 0) {
705 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
706 (char*)entry->clientdata);
707 goto finish;
709 stat(path[first], &stat_buf);
710 if (!menu->cascades[entry->cascade]
711 || menu->cascades[entry->cascade]->timestamp < last) {
713 if (S_ISDIR(stat_buf.st_mode)) {
714 /* menu directory */
715 submenu = readMenuDirectory(menu->frame->screen_ptr,
716 entry->text, path, cmd);
717 if (submenu)
718 submenu->timestamp = last;
719 } else if (S_ISREG(stat_buf.st_mode)) {
720 /* menu file */
722 if (cmd || path[1])
723 wwarning(_("too many parameters in OPEN_MENU: %s"),
724 (char*)entry->clientdata);
726 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
727 if (submenu)
728 submenu->timestamp = stat_buf.st_mtime;
729 } else {
730 submenu = NULL;
732 } else {
733 submenu = NULL;
737 if (submenu) {
738 wMenuEntryRemoveCascade(menu, entry);
739 wMenuEntrySetCascade(menu, entry, submenu);
742 finish:
743 i = 0;
744 while (path[i]!=NULL)
745 free(path[i++]);
746 free(path);
747 if (cmd)
748 free(cmd);
752 static WMenuEntry*
753 addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
755 WMenu *wsmenu;
756 WMenuEntry *entry;
758 if (scr->flags.added_workspace_menu) {
759 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
760 return NULL;
761 } else {
762 scr->flags.added_workspace_menu = 1;
764 wsmenu = wWorkspaceMenuMake(scr, True);
765 scr->workspace_menu = wsmenu;
766 entry = wMenuAddCallback(menu, title, NULL, NULL);
767 wMenuEntrySetCascade(menu, entry, wsmenu);
769 wWorkspaceMenuUpdate(scr, wsmenu);
771 return entry;
775 static WMenuEntry*
776 addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
777 char *params, char *file_name)
779 WScreen *scr;
780 WMenuEntry *entry = NULL;
781 Bool shortcutOk = False;
783 if (!menu)
784 return NULL;
785 scr = menu->frame->screen_ptr;
786 if (strcmp(command, "OPEN_MENU")==0) {
787 if (!params) {
788 wwarning(_("%s:missing parameter for menu command \"%s\""),
789 file_name, command);
790 } else {
791 WMenu *dummy;
792 char *path;
794 path = wfindfile(DEF_CONFIG_PATHS, params);
795 if (!path) {
796 path = wstrdup(params);
798 dummy = wMenuCreate(scr, title, False);
799 dummy->on_destroy = removeShortcutsForMenu;
800 entry = wMenuAddCallback(menu, title, constructMenu, path);
801 entry->free_cdata = free;
802 wMenuEntrySetCascade(menu, entry, dummy);
804 } else if (strcmp(command, "EXEC")==0) {
805 if (!params)
806 wwarning(_("%s:missing parameter for menu command \"%s\""),
807 file_name, command);
808 else {
809 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
810 entry->free_cdata = free;
811 shortcutOk = True;
813 } else if (strcmp(command, "EXIT")==0) {
815 if (params && strcmp(params, "QUICK")==0)
816 entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
817 else
818 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
820 shortcutOk = True;
821 } else if (strcmp(command, "SHUTDOWN")==0) {
823 if (params && strcmp(params, "QUICK")==0)
824 entry = wMenuAddCallback(menu, title, shutdownCommand,
825 (void*)M_QUICK);
826 else
827 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
829 shortcutOk = True;
830 } else if (strcmp(command, "REFRESH")==0) {
831 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
833 shortcutOk = True;
834 } else if (strcmp(command, "WORKSPACE_MENU")==0) {
835 entry = addWorkspaceMenu(scr, menu, title);
837 shortcutOk = True;
838 } else if (strcmp(command, "ARRANGE_ICONS")==0) {
839 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
841 shortcutOk = True;
842 } else if (strcmp(command, "HIDE_OTHERS")==0) {
843 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
845 shortcutOk = True;
846 } else if (strcmp(command, "SHOW_ALL")==0) {
847 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
849 shortcutOk = True;
850 } else if (strcmp(command, "RESTART")==0) {
851 entry = wMenuAddCallback(menu, title, restartCommand,
852 params ? wstrdup(params) : NULL);
853 entry->free_cdata = free;
854 shortcutOk = True;
855 } else if (strcmp(command, "SAVE_SESSION")==0) {
856 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
858 shortcutOk = True;
859 } else if (strcmp(command, "CLEAR_SESSION")==0) {
860 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
861 shortcutOk = True;
862 } else if (strcmp(command, "INFO_PANEL")==0) {
863 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
864 shortcutOk = True;
865 } else if (strcmp(command, "LEGAL_PANEL")==0) {
866 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
867 shortcutOk = True;
868 } else {
869 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
870 command);
872 return NULL;
875 if (shortcut && entry) {
876 if (!shortcutOk) {
877 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
878 title);
879 } else {
880 if (addShortcut(file_name, shortcut, menu, entry)) {
882 entry->rtext = GetShortcutString(shortcut);
884 entry->rtext = wstrdup(shortcut);
890 return entry;
895 /******************* Menu Configuration From File *******************/
897 static void
898 separateline(char *line, char *title, char *command, char *parameter,
899 char *shortcut)
901 int l, i;
903 l = strlen(line);
905 *title = 0;
906 *command = 0;
907 *parameter = 0;
908 *shortcut = 0;
909 /* get the title */
910 while (isspace(*line) && (*line!=0)) line++;
911 if (*line=='"') {
912 line++;
913 i=0;
914 while (line[i]!='"' && (line[i]!=0)) i++;
915 if (line[i]!='"')
916 return;
917 } else {
918 i=0;
919 while (!isspace(line[i]) && (line[i]!=0)) i++;
921 strncpy(title, line, i);
922 title[i++]=0;
923 line+=i;
925 /* get the command or shortcut keyword */
926 while (isspace(*line) && (*line!=0)) line++;
927 if (*line==0)
928 return;
929 i=0;
930 while (!isspace(line[i]) && (line[i]!=0)) i++;
931 strncpy(command, line, i);
932 command[i++]=0;
933 line+=i;
935 if (strcmp(command, "SHORTCUT")==0) {
936 /* get the shortcut key */
937 while (isspace(*line) && (*line!=0)) line++;
938 if (*line=='"') {
939 line++;
940 i=0;
941 while (line[i]!='"' && (line[i]!=0)) i++;
942 if (line[i]!='"')
943 return;
944 } else {
945 i=0;
946 while (!isspace(line[i]) && (line[i]!=0)) i++;
948 strncpy(shortcut, line, i);
949 shortcut[i++]=0;
950 line+=i;
952 *command=0;
954 /* get the command */
955 while (isspace(*line) && (*line!=0)) line++;
956 if (*line==0)
957 return;
958 i=0;
959 while (!isspace(line[i]) && (line[i]!=0)) i++;
960 strncpy(command, line, i);
961 command[i++]=0;
962 line+=i;
965 /* get the parameters */
966 while (isspace(*line) && (*line!=0)) line++;
967 if (*line==0)
968 return;
970 if (*line=='"') {
971 line++;
972 l = 0;
973 while (line[l]!=0 && line[l]!='"') {
974 parameter[l] = line[l];
975 l++;
977 parameter[l] = 0;
978 return;
981 l = strlen(line);
982 while (isspace(line[l]) && (l>0)) l--;
983 strncpy(parameter, line, l);
984 parameter[l]=0;
988 static WMenu*
989 parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
991 char linebuf[MAXLINE];
992 char elinebuf[MAXLINE];
993 char title[MAXLINE];
994 char command[MAXLINE];
995 char shortcut[MAXLINE];
996 char params[MAXLINE];
997 char *line;
999 while (!IsEof(file)) {
1000 int lsize, ok;
1002 ok = 0;
1003 fgets(linebuf, MAXLINE, file);
1004 line = cropline(linebuf);
1005 lsize = strlen(line);
1006 do {
1007 if (line[lsize-1]=='\\') {
1008 char *line2;
1009 int lsize2;
1010 fgets(elinebuf, MAXLINE, file);
1011 line2=cropline(elinebuf);
1012 lsize2=strlen(line2);
1013 if (lsize2+lsize>MAXLINE) {
1014 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1015 file_name, line);
1016 ok=2;
1017 } else {
1018 line[lsize-1]=0;
1019 lsize+=lsize2-1;
1020 strcat(line, line2);
1022 } else {
1023 ok=1;
1025 } while (!ok && !IsEof(file));
1026 if (ok==2)
1027 continue;
1029 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1030 continue;
1033 separateline(line, title, command, params, shortcut);
1035 if (!command[0]) {
1036 wwarning(_("%s:missing command in menu config: %s"), file_name,
1037 line);
1038 goto error;
1041 if (strcasecmp(command, "MENU")==0) {
1042 WMenu *cascade;
1044 /* start submenu */
1046 cascade = wMenuCreate(scr, title, False);
1047 cascade->on_destroy = removeShortcutsForMenu;
1048 if (parseCascade(scr, cascade, file, file_name)==NULL) {
1049 wMenuDestroy(cascade, True);
1050 } else {
1051 wMenuEntrySetCascade(menu,
1052 wMenuAddCallback(menu, title, NULL, NULL),
1053 cascade);
1055 } else if (strcasecmp(command, "END")==0) {
1056 /* end of menu */
1057 return menu;
1059 } else {
1060 /* normal items */
1061 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1062 params[0] ? params : NULL, file_name);
1066 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1067 file_name);
1068 return menu;
1070 error:
1071 return menu;
1075 static WMenu*
1076 readMenuFile(WScreen *scr, char *file_name)
1078 WMenu *menu=NULL;
1079 FILE *file = NULL;
1080 char linebuf[MAXLINE];
1081 char title[MAXLINE];
1082 char shortcut[MAXLINE];
1083 char command[MAXLINE];
1084 char params[MAXLINE];
1085 char *line;
1086 #ifdef USECPP
1087 char *args;
1088 int cpp = 0;
1089 #endif
1091 #ifdef USECPP
1092 if (!wPreferences.flags.nocpp) {
1093 args = MakeCPPArgs(file_name);
1094 if (!args) {
1095 wwarning(_("could not make arguments for menu file preprocessor"));
1096 } else {
1097 sprintf(command, "%s %s %s", CPP_PATH, args, file_name);
1098 free(args);
1099 file = popen(command, "r");
1100 if (!file) {
1101 wsyserror(_("%s:could not open/preprocess menu file"),
1102 file_name);
1103 } else {
1104 cpp = 1;
1108 #endif /* USECPP */
1110 if (!file) {
1111 file = fopen(file_name, "r");
1112 if (!file) {
1113 wsyserror(_("%s:could not open menu file"), file_name);
1114 return NULL;
1118 while (!IsEof(file)) {
1119 if (!fgets(linebuf, MAXLINE, file))
1120 break;
1121 line = cropline(linebuf);
1122 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1123 continue;
1125 separateline(line, title, command, params, shortcut);
1127 if (!command[0]) {
1128 wwarning(_("%s:missing command in menu config: %s"), file_name,
1129 line);
1130 break;
1132 if (strcasecmp(command, "MENU")==0) {
1133 menu = wMenuCreate(scr, title, True);
1134 menu->on_destroy = removeShortcutsForMenu;
1135 if (!parseCascade(scr, menu, file, file_name)) {
1136 wMenuDestroy(menu, True);
1138 break;
1139 } else {
1140 wwarning(_("%s:invalid menu file. MENU command is missing"),
1141 file_name);
1142 break;
1146 #ifdef CPP
1147 if (cpp) {
1148 if (pclose(file)==-1) {
1149 wsyserror(_("error reading preprocessed menu data"));
1151 } else {
1152 fclose(file);
1154 #else
1155 fclose(file);
1156 #endif
1158 return menu;
1161 /************ Menu Configuration From Pipe *************/
1163 static WMenu*
1164 readMenuPipe(WScreen *scr, char **file_name)
1166 WMenu *menu=NULL;
1167 FILE *file = NULL;
1168 char linebuf[MAXLINE];
1169 char title[MAXLINE];
1170 char command[MAXLINE];
1171 char params[MAXLINE];
1172 char shortcut[MAXLINE];
1173 char *line;
1174 char * filename;
1175 char flat_file[MAXLINE];
1176 int i;
1177 #ifdef USECPP
1178 char *args;
1179 int cpp = 0;
1180 #endif
1182 flat_file[0] = '\0';
1184 for(i = 0 ; file_name[i] != NULL ; i++) {
1185 strcat(flat_file, file_name[i]);
1186 strcat(flat_file, " ");
1188 filename = flat_file+1;
1190 #ifdef USECPP
1191 if (!wPreferences.flags.nocpp) {
1192 args = MakeCPPArgs(filename);
1193 if (!args) {
1194 wwarning(_("could not make arguments for menu file preprocessor"));
1195 } else {
1196 sprintf(command, "%s | %s %s", filename, CPP_PATH, args);
1198 free(args);
1199 file = popen(command, "r");
1200 if (!file) {
1201 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1202 } else {
1203 cpp = 1;
1208 #endif /* USECPP */
1210 if (!file) {
1211 file = popen(filename, "r");
1213 if (!file) {
1214 wsyserror(_("%s:could not open menu file"), filename);
1215 return NULL;
1219 while (!IsEof(file)) {
1220 if (!fgets(linebuf, MAXLINE, file))
1221 break;
1222 line = cropline(linebuf);
1223 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1224 continue;
1226 separateline(line, title, command, params, shortcut);
1228 if (!command[0]) {
1229 wwarning(_("%s:missing command in menu config: %s"), file_name,
1230 line);
1231 break;
1233 if (strcasecmp(command, "MENU")==0) {
1234 menu = wMenuCreate(scr, title, True);
1235 menu->on_destroy = removeShortcutsForMenu;
1236 if (!parseCascade(scr, menu, file, filename)) {
1237 wMenuDestroy(menu, True);
1239 break;
1240 } else {
1241 wwarning(_("%s:no title given for the root menu"), filename);
1242 break;
1246 pclose(file);
1248 return menu;
1253 typedef struct {
1254 char *name;
1255 int index;
1256 } dir_data;
1259 static int
1260 myCompare(dir_data *d1, dir_data *d2)
1262 return strcmp(d1->name, d2->name);
1266 /************ Menu Configuration From Directory *************/
1268 static Bool
1269 isFilePackage(char *file)
1271 int l;
1273 /* check if the extension indicates this file is a
1274 * file package. For now, only recognize .themed */
1276 l = strlen(file);
1278 if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
1279 return True;
1280 } else {
1281 return False;
1286 static WMenu*
1287 readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
1289 DIR *dir;
1290 struct dirent *dentry;
1291 struct stat stat_buf;
1292 WMenu *menu=NULL;
1293 char *buffer;
1294 LinkedList *dirs = NULL, *files = NULL;
1295 int length, i, have_space=0;
1296 dir_data *data;
1297 int stripExtension = 0;
1299 i=0;
1300 while (path[i]!=NULL) {
1301 if (strcmp(path[i], "-noext")==0) {
1302 stripExtension = 1;
1303 i++;
1304 continue;
1307 dir = opendir(path[i]);
1308 if (!dir) {
1309 i++;
1310 continue;
1313 while ((dentry = readdir(dir))) {
1315 if (strcmp(dentry->d_name, ".")==0 ||
1316 strcmp(dentry->d_name, "..")==0)
1317 continue;
1319 if (dentry->d_name[0] == '.')
1320 continue;
1322 buffer = wmalloc(strlen(path[i])+strlen(dentry->d_name)+4);
1323 if (!buffer) {
1324 wsyserror(_("out of memory while constructing directory menu %s"),
1325 path[i]);
1326 break;
1329 strcpy(buffer, path[i]);
1330 strcat(buffer, "/");
1331 strcat(buffer, dentry->d_name);
1333 if (stat(buffer, &stat_buf)!=0) {
1334 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1335 path[i], dentry->d_name);
1336 } else {
1337 Bool isFilePack = False;
1339 data = NULL;
1340 if (S_ISDIR(stat_buf.st_mode)
1341 && !(isFilePack = isFilePackage(dentry->d_name))) {
1343 /* access always returns success for user root */
1344 if (access(buffer, X_OK)==0) {
1345 /* Directory is accesible. Add to directory list */
1347 data = (dir_data*) wmalloc(sizeof(dir_data));
1348 data->name = wstrdup(dentry->d_name);
1349 data->index = i;
1351 list_insert_sorted(data, &dirs, (int(*)())myCompare);
1353 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1354 /* Hack because access always returns X_OK success for user root */
1355 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1356 if ((command!=NULL && access(buffer, R_OK)==0) ||
1357 (command==NULL && access(buffer, X_OK)==0 &&
1358 (stat_buf.st_mode & S_IXANY))) {
1360 data = (dir_data*) wmalloc(sizeof(dir_data));
1361 data->name = wstrdup(dentry->d_name);
1362 data->index = i;
1364 list_insert_sorted(data, &files, (int(*)())myCompare);
1368 free(buffer);
1371 closedir(dir);
1372 i++;
1375 if (!dirs && !files)
1376 return NULL;
1378 menu = wMenuCreate(scr, title, False);
1379 menu->on_destroy = removeShortcutsForMenu;
1381 while (dirs != NULL) {
1382 /* New directory. Use same OPEN_MENU command that was used
1383 * for the current directory. */
1384 dir_data *d = (dir_data*)dirs->head;
1386 length = strlen(path[d->index])+strlen(d->name)+6;
1387 if (command)
1388 length += strlen(command) + 6;
1389 buffer = wmalloc(length);
1390 if (!buffer) {
1391 wsyserror(_("out of memory while constructing directory menu %s"),
1392 path[d->index]);
1393 break;
1396 have_space = strchr(path[d->index], ' ')!=NULL ||
1397 strchr(d->name, ' ')!=NULL;
1398 if (have_space) {
1399 buffer[0] = '"';
1400 buffer[1] = 0;
1401 strcat(buffer, path[d->index]);
1402 } else {
1403 strcpy(buffer, path[d->index]);
1405 strcat(buffer, "/");
1406 strcat(buffer, d->name);
1407 if (have_space)
1408 strcat(buffer, "\"");
1409 if (command) {
1410 strcat(buffer, " WITH ");
1411 strcat(buffer, command);
1414 addMenuEntry(menu, d->name, NULL, "OPEN_MENU", buffer, path[d->index]);
1416 free(buffer);
1417 if (dirs->head) {
1418 if (d->name)
1419 free(d->name);
1420 free(dirs->head);
1422 list_remove_head(&dirs);
1425 while (files != NULL) {
1426 /* executable: add as entry */
1427 dir_data *f = (dir_data*) files->head;;
1429 length = strlen(path[f->index])+strlen(f->name)+6;
1430 if (command)
1431 length += strlen(command);
1433 buffer = wmalloc(length);
1434 if (!buffer) {
1435 wsyserror(_("out of memory while constructing directory menu %s"),
1436 path[f->index]);
1437 break;
1440 have_space = strchr(path[f->index], ' ')!=NULL ||
1441 strchr(f->name, ' ')!=NULL;
1442 if (command!=NULL) {
1443 strcpy(buffer, command);
1444 strcat(buffer, " ");
1445 if (have_space)
1446 strcat(buffer, "\"");
1447 strcat(buffer, path[f->index]);
1448 } else {
1449 if (have_space) {
1450 buffer[0] = '"';
1451 buffer[1] = 0;
1452 strcat(buffer, path[f->index]);
1453 } else {
1454 strcpy(buffer, path[f->index]);
1457 strcat(buffer, "/");
1458 strcat(buffer, f->name);
1459 if (have_space)
1460 strcat(buffer, "\"");
1462 if (stripExtension) {
1463 char *ptr = strrchr(f->name, '.');
1464 if (ptr && ptr!=f->name)
1465 *ptr = 0;
1467 addMenuEntry(menu, f->name, NULL, "EXEC", buffer, path[f->index]);
1469 free(buffer);
1470 if (files->head) {
1471 if (f->name)
1472 free(f->name);
1473 free(files->head);
1475 list_remove_head(&files);
1478 return menu;
1482 /************ Menu Configuration From WMRootMenu *************/
1484 static WMenu*
1485 makeDefaultMenu(WScreen *scr)
1487 WMenu *menu=NULL;
1489 menu = wMenuCreate(scr, _("Commands"), True);
1490 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1491 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1492 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1493 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1494 return menu;
1502 *----------------------------------------------------------------------
1503 * configureMenu--
1504 * Reads root menu configuration from defaults database.
1506 *----------------------------------------------------------------------
1508 static WMenu*
1509 configureMenu(WScreen *scr, proplist_t definition)
1511 WMenu *menu = NULL;
1512 proplist_t elem;
1513 int i, count;
1514 proplist_t title, command, params;
1515 char *tmp, *mtitle;
1518 if (PLIsString(definition)) {
1519 struct stat stat_buf;
1520 char *path = NULL;
1521 Bool menu_is_default = False;
1523 /* menu definition is a string. Probably a path, so parse the file */
1525 tmp = wexpandpath(PLGetString(definition));
1527 path = getLocalizedMenuFile(tmp);
1529 if (!path)
1530 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1532 if (!path) {
1533 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1534 menu_is_default = True;
1537 if (!path) {
1538 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1539 tmp);
1540 free(tmp);
1541 return NULL;
1544 if (stat(path, &stat_buf)<0) {
1545 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1546 free(path);
1547 free(tmp);
1548 return NULL;
1551 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1552 /* if the pointer in WMRootMenu has changed */
1553 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1555 if (menu_is_default) {
1556 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1557 path);
1560 menu = readMenuFile(scr, path);
1561 if (menu)
1562 menu->timestamp = MAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1563 } else {
1564 menu = NULL;
1566 free(path);
1567 free(tmp);
1569 return menu;
1572 count = PLGetNumberOfElements(definition);
1573 if (count==0)
1574 return NULL;
1576 elem = PLGetArrayElement(definition, 0);
1577 if (!PLIsString(elem)) {
1578 tmp = PLGetDescription(elem);
1579 wwarning(_("%s:format error in root menu configuration \"%s\""),
1580 "WMRootMenu", tmp);
1581 free(tmp);
1582 return NULL;
1584 mtitle = PLGetString(elem);
1586 menu = wMenuCreate(scr, mtitle, False);
1587 menu->on_destroy = removeShortcutsForMenu;
1589 #ifdef GLOBAL_SUBMENU_FILE
1591 WMenu *submenu;
1592 WMenuEntry *mentry;
1594 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1596 if (submenu) {
1597 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1598 wMenuEntrySetCascade(menu, mentry, submenu);
1601 #endif
1603 for (i=1; i<count; i++) {
1604 elem = PLGetArrayElement(definition, i);
1605 #if 0
1606 if (PLIsString(elem)) {
1607 char *file;
1609 file = PLGetString(elem);
1612 #endif
1613 if (!PLIsArray(elem) || PLGetNumberOfElements(elem) < 2)
1614 goto error;
1616 if (PLIsArray(PLGetArrayElement(elem,1))) {
1617 WMenu *submenu;
1618 WMenuEntry *mentry;
1620 /* submenu */
1621 submenu = configureMenu(scr, elem);
1622 if (submenu) {
1623 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
1624 NULL);
1625 wMenuEntrySetCascade(menu, mentry, submenu);
1627 } else {
1628 int idx = 0;
1629 char *shortcut;
1630 /* normal entry */
1632 title = PLGetArrayElement(elem, idx++);
1633 shortcut = PLGetArrayElement(elem, idx++);
1634 if (strcmp(PLGetString(shortcut), "SHORTCUT")==0) {
1635 shortcut = PLGetArrayElement(elem, idx++);
1636 command = PLGetArrayElement(elem, idx++);
1637 } else {
1638 command = shortcut;
1639 shortcut = NULL;
1641 params = PLGetArrayElement(elem, idx++);
1643 if (!title || !command)
1644 goto error;
1646 addMenuEntry(menu, PLGetString(title),
1647 shortcut ? PLGetString(shortcut) : NULL,
1648 PLGetString(command),
1649 params ? PLGetString(params) : NULL, "WMRootMenu");
1651 continue;
1653 error:
1654 tmp = PLGetDescription(elem);
1655 wwarning(_("%s:format error in root menu configuration \"%s\""),
1656 "WMRootMenu", tmp);
1657 free(tmp);
1660 return menu;
1671 *----------------------------------------------------------------------
1672 * OpenRootMenu--
1673 * Opens the root menu, parsing the menu configuration from the
1674 * defaults database.
1675 * If the menu is already mapped and is not sticked to the
1676 * root window, it will be unmapped.
1678 * Side effects:
1679 * The menu may be remade.
1681 * Notes:
1682 * Construction of OPEN_MENU entries are delayed to the moment the
1683 * user map's them.
1684 *----------------------------------------------------------------------
1686 void
1687 OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
1689 WMenu *menu=NULL;
1690 proplist_t definition;
1692 static proplist_t domain=NULL;
1694 if (!domain) {
1695 domain = PLMakeString("WMRootMenu");
1699 scr->flags.root_menu_changed_shortcuts = 0;
1700 scr->flags.added_workspace_menu = 0;
1702 if (scr->root_menu && scr->root_menu->flags.mapped) {
1703 menu = scr->root_menu;
1704 if (!menu->flags.buttoned) {
1705 wMenuUnmap(menu);
1706 } else {
1707 wRaiseFrame(menu->frame->core);
1709 if (keyboard)
1710 wMenuMapAt(menu, 0, 0, True);
1711 else
1712 wMenuMapCopyAt(menu, x-menu->frame->core->width/2,
1713 y-menu->frame->top_width/2);
1715 return;
1719 definition = WDRootMenu->dictionary;
1722 definition = PLGetDomain(domain);
1724 if (definition) {
1725 if (PLIsArray(definition)) {
1726 if (!scr->root_menu
1727 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1728 menu = configureMenu(scr, definition);
1729 if (menu)
1730 menu->timestamp = WDRootMenu->timestamp;
1732 } else
1733 menu = NULL;
1734 } else {
1735 menu = configureMenu(scr, definition);
1739 if (!menu) {
1740 /* menu hasn't changed or could not be read */
1741 if (!scr->root_menu) {
1742 wMessageDialog(scr, _("Error"),
1743 _("The applications menu could not be loaded."
1744 "Look at the console output for a detailed"
1745 "description of the errors"),
1746 _("OK"), NULL, NULL);
1748 menu = makeDefaultMenu(scr);
1749 scr->root_menu = menu;
1751 menu = scr->root_menu;
1752 } else {
1753 /* new root menu */
1754 if (scr->root_menu)
1755 wMenuDestroy(scr->root_menu, True);
1756 scr->root_menu = menu;
1758 if (menu) {
1759 wMenuMapAt(menu, x-menu->frame->core->width/2, y-menu->frame->top_width/2,
1760 keyboard);
1763 if (scr->flags.root_menu_changed_shortcuts)
1764 rebindKeygrabs(scr);
1767 #endif /* !LITE */