*** empty log message ***
[wmaker-crm.git] / src / rootmenu.c
blob8fd8c21d00b645d01e79b823ff275ccdbe3b0520
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, False);
269 Restart(NULL, True);
273 static void
274 refreshCommand(WMenu *menu, WMenuEntry *entry)
276 wRefreshDesktop(menu->frame->screen_ptr);
280 static void
281 arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
283 wArrangeIcons(menu->frame->screen_ptr, True);
286 static void
287 showAllCommand(WMenu *menu, WMenuEntry *entry)
289 wShowAllWindows(menu->frame->screen_ptr);
292 static void
293 hideOthersCommand(WMenu *menu, WMenuEntry *entry)
295 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
299 static void
300 saveSessionCommand(WMenu *menu, WMenuEntry *entry)
302 if (!wPreferences.save_session_on_exit)
303 wSessionSaveState(menu->frame->screen_ptr);
305 wScreenSaveState(menu->frame->screen_ptr);
309 static void
310 clearSessionCommand(WMenu *menu, WMenuEntry *entry)
312 wSessionClearState(menu->frame->screen_ptr);
316 static void
317 infoPanelCommand(WMenu *menu, WMenuEntry *entry)
319 wShowInfoPanel(menu->frame->screen_ptr);
323 static void
324 legalPanelCommand(WMenu *menu, WMenuEntry *entry)
326 wShowLegalPanel(menu->frame->screen_ptr);
330 /********************************************************************/
333 static char*
334 getLocalizedMenuFile(char *menu)
336 char *buffer;
337 char *ptr;
339 if (!Locale)
340 return NULL;
342 buffer = wmalloc(strlen(menu)+32);
344 /* try menu.locale_name */
345 sprintf(buffer, "%s.%s", menu, Locale);
346 if (access(buffer, F_OK)==0) {
347 return buffer;
349 /* check if it is in the form aa_bb.encoding and check for aa_bb */
350 ptr = strchr(Locale, '.');
351 if (ptr) {
352 *ptr = 0;
353 if (access(buffer, F_OK)==0) {
354 return buffer;
357 /* now check for aa */
358 ptr = strchr(buffer, '_');
359 if (ptr) {
360 *ptr = 0;
361 if (access(buffer, F_OK)==0) {
362 return buffer;
366 return NULL;
370 static void
371 raiseMenus(WMenu *menu)
373 int i;
375 if (menu->flags.mapped) {
376 wRaiseFrame(menu->frame->core);
378 for (i=0; i<menu->cascade_no; i++) {
379 if (menu->cascades[i])
380 raiseMenus(menu->cascades[i]);
386 Bool
387 wRootMenuPerformShortcut(XEvent *event)
389 Shortcut *ptr;
390 int modifiers;
391 int done = 0;
393 /* ignore CapsLock */
394 modifiers = event->xkey.state & ValidModMask;
396 for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
397 if (ptr->keycode==0)
398 continue;
400 if (ptr->keycode==event->xkey.keycode && (ptr->modifier==modifiers)) {
401 (*ptr->entry->callback)(ptr->menu, ptr->entry);
402 done = True;
405 return done;
409 void
410 wRootMenuBindShortcuts(Window window)
412 Shortcut *ptr;
414 ptr = shortcutList;
415 while (ptr) {
416 if (ptr->modifier!=AnyModifier) {
417 XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
418 window, True, GrabModeAsync, GrabModeAsync);
419 #ifdef NUMLOCK_HACK
420 wHackedGrabKey(ptr->keycode, ptr->modifier,
421 window, True, GrabModeAsync, GrabModeAsync);
422 #endif
424 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
425 GrabModeAsync, GrabModeAsync);
426 ptr = ptr->next;
431 static void
432 rebindKeygrabs(WScreen *scr)
434 WWindow *wwin;
436 wwin = scr->focused_window;
438 while (wwin!=NULL) {
439 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
441 if (!WFLAGP(wwin, no_bind_keys)) {
442 wWindowSetKeyGrabs(wwin);
444 wwin = wwin->prev;
449 static void
450 removeShortcutsForMenu(WMenu *menu)
452 Shortcut *ptr, *tmp;
453 Shortcut *newList = NULL;
455 ptr = shortcutList;
456 while (ptr!=NULL) {
457 tmp = ptr->next;
458 if (ptr->menu == menu) {
459 free(ptr);
460 } else {
461 ptr->next = newList;
462 newList = ptr;
464 ptr = tmp;
466 shortcutList = newList;
467 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
471 static Bool
472 addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
473 WMenuEntry *entry)
475 Shortcut *ptr;
476 KeySym ksym;
477 char *k;
478 char buf[128], *b;
480 ptr = wmalloc(sizeof(Shortcut));
482 strcpy(buf, shortcutDefinition);
483 b = (char*)buf;
485 /* get modifiers */
486 ptr->modifier = 0;
487 while ((k = strchr(b, '+'))!=NULL) {
488 int mod;
490 *k = 0;
491 mod = wXModifierFromKey(b);
492 if (mod<0) {
493 wwarning(_("%s:invalid key modifier \"%s\""), file, b);
494 free(ptr);
495 return False;
497 ptr->modifier |= mod;
499 b = k+1;
502 /* get key */
503 ksym = XStringToKeysym(b);
505 if (ksym==NoSymbol) {
506 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
507 file, shortcutDefinition, entry->text);
508 free(ptr);
509 return False;
512 ptr->keycode = XKeysymToKeycode(dpy, ksym);
513 if (ptr->keycode==0) {
514 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
515 shortcutDefinition, entry->text);
516 free(ptr);
517 return False;
520 ptr->menu = menu;
521 ptr->entry = entry;
523 ptr->next = shortcutList;
524 shortcutList = ptr;
526 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
528 return True;
532 /*******************************/
534 static char*
535 cropline(char *line)
537 char *end;
539 if (strlen(line)==0)
540 return line;
542 end = &(line[strlen(line)])-1;
543 while (isspace(*line) && *line!=0) line++;
544 while (end>line && isspace(*end)) {
545 *end=0;
546 end--;
548 return line;
552 static char*
553 next_token(char *line, char **next)
555 char *tmp, c;
556 char *ret;
558 *next = NULL;
559 while (*line==' ' || *line=='\t') line++;
561 tmp = line;
563 if (*tmp=='"') {
564 tmp++; line++;
565 while (*tmp!=0 && *tmp!='"') tmp++;
566 if (*tmp!='"') {
567 wwarning(_("%s: unmatched '\"' in menu file"), line);
568 return NULL;
570 } else {
571 do {
572 if (*tmp=='\\')
573 tmp++;
575 if (*tmp!=0)
576 tmp++;
578 } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
581 c = *tmp;
582 *tmp = 0;
583 ret = wstrdup(line);
584 *tmp = c;
586 if (c==0)
587 return ret;
588 else
589 tmp++;
591 /* skip blanks */
592 while (*tmp==' ' || *tmp=='\t') tmp++;
594 if (*tmp!=0)
595 *next = tmp;
597 return ret;
601 static void
602 separateCommand(char *line, char ***file, char **command)
604 char *token, *tmp = line;
605 LinkedList *list = NULL;
606 int count, i;
608 *file = NULL;
609 *command = NULL;
610 do {
611 token = next_token(tmp, &tmp);
612 if (token) {
613 if (strcmp(token, "WITH")==0) {
614 if (tmp!=NULL && *tmp!=0)
615 *command = wstrdup(tmp);
616 else
617 wwarning(_("%s: missing command"), line);
618 break;
620 list = list_cons(token, list);
622 } while (token!=NULL && tmp!=NULL);
624 count = list_length(list);
625 if (count>0) {
626 *file = wmalloc(sizeof(char*)*(count+1));
627 i = count;
628 (*file)[count] = NULL;
629 while (list!=NULL) {
630 (*file)[--i] = list->head;
631 list_remove_head(&list);
637 static void
638 constructMenu(WMenu *menu, WMenuEntry *entry)
640 WMenu *submenu;
641 struct stat stat_buf;
642 char **path;
643 char *cmd;
644 char *lpath = NULL;
645 int i, first=-1;
646 time_t last=0;
648 separateCommand((char*)entry->clientdata, &path, &cmd);
649 if (!path || *path==NULL || **path==0) {
650 wwarning(_("invalid OPEN_MENU specification: %s"),
651 (char*)entry->clientdata);
652 return;
655 if (path[0][0]=='|') {
656 /* pipe menu */
658 if (!menu->cascades[entry->cascade] ||
659 menu->cascades[entry->cascade]->timestamp == 0) {
660 /* parse pipe */
662 submenu = readMenuPipe(menu->frame->screen_ptr, path);
664 /* there's no automatic reloading */
665 if(submenu != NULL)
666 submenu->timestamp = 1;
667 } else {
668 submenu = NULL;
671 } else {
672 i=0;
673 while(path[i] != NULL) {
674 char *tmp;
676 if (strcmp(path[i], "-noext")==0) {
677 i++;
678 continue;
681 tmp = wexpandpath(path[i]);
682 free(path[i]);
683 lpath = getLocalizedMenuFile(tmp);
684 if (lpath) {
685 free(tmp);
686 path[i] = lpath;
687 lpath = NULL;
688 } else {
689 path[i] = tmp;
692 if (stat(path[i], &stat_buf)==0) {
693 if (last < stat_buf.st_mtime)
694 last = stat_buf.st_mtime;
695 if (first<0)
696 first=i;
697 } else {
698 wsyserror(_("%s:could not stat menu"), path[i]);
699 /*goto finish;*/
702 i++;
705 if (first < 0) {
706 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
707 (char*)entry->clientdata);
708 goto finish;
710 stat(path[first], &stat_buf);
711 if (!menu->cascades[entry->cascade]
712 || menu->cascades[entry->cascade]->timestamp < last) {
714 if (S_ISDIR(stat_buf.st_mode)) {
715 /* menu directory */
716 submenu = readMenuDirectory(menu->frame->screen_ptr,
717 entry->text, path, cmd);
718 if (submenu)
719 submenu->timestamp = last;
720 } else if (S_ISREG(stat_buf.st_mode)) {
721 /* menu file */
723 if (cmd || path[1])
724 wwarning(_("too many parameters in OPEN_MENU: %s"),
725 (char*)entry->clientdata);
727 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
728 if (submenu)
729 submenu->timestamp = stat_buf.st_mtime;
730 } else {
731 submenu = NULL;
733 } else {
734 submenu = NULL;
738 if (submenu) {
739 wMenuEntryRemoveCascade(menu, entry);
740 wMenuEntrySetCascade(menu, entry, submenu);
743 finish:
744 i = 0;
745 while (path[i]!=NULL)
746 free(path[i++]);
747 free(path);
748 if (cmd)
749 free(cmd);
753 static WMenuEntry*
754 addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
756 WMenu *wsmenu;
757 WMenuEntry *entry;
759 if (scr->flags.added_workspace_menu) {
760 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
761 return NULL;
762 } else {
763 scr->flags.added_workspace_menu = 1;
765 wsmenu = wWorkspaceMenuMake(scr, True);
766 scr->workspace_menu = wsmenu;
767 entry = wMenuAddCallback(menu, title, NULL, NULL);
768 wMenuEntrySetCascade(menu, entry, wsmenu);
770 wWorkspaceMenuUpdate(scr, wsmenu);
772 return entry;
776 static WMenuEntry*
777 addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
778 char *params, char *file_name)
780 WScreen *scr;
781 WMenuEntry *entry = NULL;
782 Bool shortcutOk = False;
784 if (!menu)
785 return NULL;
786 scr = menu->frame->screen_ptr;
787 if (strcmp(command, "OPEN_MENU")==0) {
788 if (!params) {
789 wwarning(_("%s:missing parameter for menu command \"%s\""),
790 file_name, command);
791 } else {
792 WMenu *dummy;
793 char *path;
795 path = wfindfile(DEF_CONFIG_PATHS, params);
796 if (!path) {
797 path = wstrdup(params);
799 dummy = wMenuCreate(scr, title, False);
800 dummy->on_destroy = removeShortcutsForMenu;
801 entry = wMenuAddCallback(menu, title, constructMenu, path);
802 entry->free_cdata = free;
803 wMenuEntrySetCascade(menu, entry, dummy);
805 } else if (strcmp(command, "EXEC")==0) {
806 if (!params)
807 wwarning(_("%s:missing parameter for menu command \"%s\""),
808 file_name, command);
809 else {
810 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
811 entry->free_cdata = free;
812 shortcutOk = True;
814 } else if (strcmp(command, "EXIT")==0) {
816 if (params && strcmp(params, "QUICK")==0)
817 entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
818 else
819 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
821 shortcutOk = True;
822 } else if (strcmp(command, "SHUTDOWN")==0) {
824 if (params && strcmp(params, "QUICK")==0)
825 entry = wMenuAddCallback(menu, title, shutdownCommand,
826 (void*)M_QUICK);
827 else
828 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
830 shortcutOk = True;
831 } else if (strcmp(command, "REFRESH")==0) {
832 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
834 shortcutOk = True;
835 } else if (strcmp(command, "WORKSPACE_MENU")==0) {
836 entry = addWorkspaceMenu(scr, menu, title);
838 shortcutOk = True;
839 } else if (strcmp(command, "ARRANGE_ICONS")==0) {
840 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
842 shortcutOk = True;
843 } else if (strcmp(command, "HIDE_OTHERS")==0) {
844 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
846 shortcutOk = True;
847 } else if (strcmp(command, "SHOW_ALL")==0) {
848 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
850 shortcutOk = True;
851 } else if (strcmp(command, "RESTART")==0) {
852 entry = wMenuAddCallback(menu, title, restartCommand,
853 params ? wstrdup(params) : NULL);
854 entry->free_cdata = free;
855 shortcutOk = True;
856 } else if (strcmp(command, "SAVE_SESSION")==0) {
857 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
859 shortcutOk = True;
860 } else if (strcmp(command, "CLEAR_SESSION")==0) {
861 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
862 shortcutOk = True;
863 } else if (strcmp(command, "INFO_PANEL")==0) {
864 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
865 shortcutOk = True;
866 } else if (strcmp(command, "LEGAL_PANEL")==0) {
867 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
868 shortcutOk = True;
869 } else {
870 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
871 command);
873 return NULL;
876 if (shortcut && entry) {
877 if (!shortcutOk) {
878 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
879 title);
880 } else {
881 if (addShortcut(file_name, shortcut, menu, entry)) {
883 entry->rtext = GetShortcutString(shortcut);
885 entry->rtext = wstrdup(shortcut);
891 return entry;
896 /******************* Menu Configuration From File *******************/
898 static void
899 separateline(char *line, char *title, char *command, char *parameter,
900 char *shortcut)
902 int l, i;
904 l = strlen(line);
906 *title = 0;
907 *command = 0;
908 *parameter = 0;
909 *shortcut = 0;
910 /* get the title */
911 while (isspace(*line) && (*line!=0)) line++;
912 if (*line=='"') {
913 line++;
914 i=0;
915 while (line[i]!='"' && (line[i]!=0)) i++;
916 if (line[i]!='"')
917 return;
918 } else {
919 i=0;
920 while (!isspace(line[i]) && (line[i]!=0)) i++;
922 strncpy(title, line, i);
923 title[i++]=0;
924 line+=i;
926 /* get the command or shortcut keyword */
927 while (isspace(*line) && (*line!=0)) line++;
928 if (*line==0)
929 return;
930 i=0;
931 while (!isspace(line[i]) && (line[i]!=0)) i++;
932 strncpy(command, line, i);
933 command[i++]=0;
934 line+=i;
936 if (strcmp(command, "SHORTCUT")==0) {
937 /* get the shortcut key */
938 while (isspace(*line) && (*line!=0)) line++;
939 if (*line=='"') {
940 line++;
941 i=0;
942 while (line[i]!='"' && (line[i]!=0)) i++;
943 if (line[i]!='"')
944 return;
945 } else {
946 i=0;
947 while (!isspace(line[i]) && (line[i]!=0)) i++;
949 strncpy(shortcut, line, i);
950 shortcut[i++]=0;
951 line+=i;
953 *command=0;
955 /* get the command */
956 while (isspace(*line) && (*line!=0)) line++;
957 if (*line==0)
958 return;
959 i=0;
960 while (!isspace(line[i]) && (line[i]!=0)) i++;
961 strncpy(command, line, i);
962 command[i++]=0;
963 line+=i;
966 /* get the parameters */
967 while (isspace(*line) && (*line!=0)) line++;
968 if (*line==0)
969 return;
971 if (*line=='"') {
972 line++;
973 l = 0;
974 while (line[l]!=0 && line[l]!='"') {
975 parameter[l] = line[l];
976 l++;
978 parameter[l] = 0;
979 return;
982 l = strlen(line);
983 while (isspace(line[l]) && (l>0)) l--;
984 strncpy(parameter, line, l);
985 parameter[l]=0;
989 static WMenu*
990 parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
992 char linebuf[MAXLINE];
993 char elinebuf[MAXLINE];
994 char title[MAXLINE];
995 char command[MAXLINE];
996 char shortcut[MAXLINE];
997 char params[MAXLINE];
998 char *line;
1000 while (!IsEof(file)) {
1001 int lsize, ok;
1003 ok = 0;
1004 fgets(linebuf, MAXLINE, file);
1005 line = cropline(linebuf);
1006 lsize = strlen(line);
1007 do {
1008 if (line[lsize-1]=='\\') {
1009 char *line2;
1010 int lsize2;
1011 fgets(elinebuf, MAXLINE, file);
1012 line2=cropline(elinebuf);
1013 lsize2=strlen(line2);
1014 if (lsize2+lsize>MAXLINE) {
1015 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1016 file_name, line);
1017 ok=2;
1018 } else {
1019 line[lsize-1]=0;
1020 lsize+=lsize2-1;
1021 strcat(line, line2);
1023 } else {
1024 ok=1;
1026 } while (!ok && !IsEof(file));
1027 if (ok==2)
1028 continue;
1030 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1031 continue;
1034 separateline(line, title, command, params, shortcut);
1036 if (!command[0]) {
1037 wwarning(_("%s:missing command in menu config: %s"), file_name,
1038 line);
1039 goto error;
1042 if (strcasecmp(command, "MENU")==0) {
1043 WMenu *cascade;
1045 /* start submenu */
1047 cascade = wMenuCreate(scr, title, False);
1048 cascade->on_destroy = removeShortcutsForMenu;
1049 if (parseCascade(scr, cascade, file, file_name)==NULL) {
1050 wMenuDestroy(cascade, True);
1051 } else {
1052 wMenuEntrySetCascade(menu,
1053 wMenuAddCallback(menu, title, NULL, NULL),
1054 cascade);
1056 } else if (strcasecmp(command, "END")==0) {
1057 /* end of menu */
1058 return menu;
1060 } else {
1061 /* normal items */
1062 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1063 params[0] ? params : NULL, file_name);
1067 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1068 file_name);
1069 return menu;
1071 error:
1072 return menu;
1076 static WMenu*
1077 readMenuFile(WScreen *scr, char *file_name)
1079 WMenu *menu=NULL;
1080 FILE *file = NULL;
1081 char linebuf[MAXLINE];
1082 char title[MAXLINE];
1083 char shortcut[MAXLINE];
1084 char command[MAXLINE];
1085 char params[MAXLINE];
1086 char *line;
1087 #ifdef USECPP
1088 char *args;
1089 int cpp = 0;
1090 #endif
1092 #ifdef USECPP
1093 if (!wPreferences.flags.nocpp) {
1094 args = MakeCPPArgs(file_name);
1095 if (!args) {
1096 wwarning(_("could not make arguments for menu file preprocessor"));
1097 } else {
1098 sprintf(command, "%s %s %s", CPP_PATH, args, file_name);
1099 free(args);
1100 file = popen(command, "r");
1101 if (!file) {
1102 wsyserror(_("%s:could not open/preprocess menu file"),
1103 file_name);
1104 } else {
1105 cpp = 1;
1109 #endif /* USECPP */
1111 if (!file) {
1112 file = fopen(file_name, "r");
1113 if (!file) {
1114 wsyserror(_("%s:could not open menu file"), file_name);
1115 return NULL;
1119 while (!IsEof(file)) {
1120 if (!fgets(linebuf, MAXLINE, file))
1121 break;
1122 line = cropline(linebuf);
1123 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1124 continue;
1126 separateline(line, title, command, params, shortcut);
1128 if (!command[0]) {
1129 wwarning(_("%s:missing command in menu config: %s"), file_name,
1130 line);
1131 break;
1133 if (strcasecmp(command, "MENU")==0) {
1134 menu = wMenuCreate(scr, title, True);
1135 menu->on_destroy = removeShortcutsForMenu;
1136 if (!parseCascade(scr, menu, file, file_name)) {
1137 wMenuDestroy(menu, True);
1139 break;
1140 } else {
1141 wwarning(_("%s:invalid menu file. MENU command is missing"),
1142 file_name);
1143 break;
1147 #ifdef CPP
1148 if (cpp) {
1149 if (pclose(file)==-1) {
1150 wsyserror(_("error reading preprocessed menu data"));
1152 } else {
1153 fclose(file);
1155 #else
1156 fclose(file);
1157 #endif
1159 return menu;
1162 /************ Menu Configuration From Pipe *************/
1164 static WMenu*
1165 readMenuPipe(WScreen *scr, char **file_name)
1167 WMenu *menu=NULL;
1168 FILE *file = NULL;
1169 char linebuf[MAXLINE];
1170 char title[MAXLINE];
1171 char command[MAXLINE];
1172 char params[MAXLINE];
1173 char shortcut[MAXLINE];
1174 char *line;
1175 char * filename;
1176 char flat_file[MAXLINE];
1177 int i;
1178 #ifdef USECPP
1179 char *args;
1180 int cpp = 0;
1181 #endif
1183 flat_file[0] = '\0';
1185 for(i = 0 ; file_name[i] != NULL ; i++) {
1186 strcat(flat_file, file_name[i]);
1187 strcat(flat_file, " ");
1189 filename = flat_file+1;
1191 #ifdef USECPP
1192 if (!wPreferences.flags.nocpp) {
1193 args = MakeCPPArgs(filename);
1194 if (!args) {
1195 wwarning(_("could not make arguments for menu file preprocessor"));
1196 } else {
1197 sprintf(command, "%s | %s %s", filename, CPP_PATH, args);
1199 free(args);
1200 file = popen(command, "r");
1201 if (!file) {
1202 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1203 } else {
1204 cpp = 1;
1209 #endif /* USECPP */
1211 if (!file) {
1212 file = popen(filename, "r");
1214 if (!file) {
1215 wsyserror(_("%s:could not open menu file"), filename);
1216 return NULL;
1220 while (!IsEof(file)) {
1221 if (!fgets(linebuf, MAXLINE, file))
1222 break;
1223 line = cropline(linebuf);
1224 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1225 continue;
1227 separateline(line, title, command, params, shortcut);
1229 if (!command[0]) {
1230 wwarning(_("%s:missing command in menu config: %s"), file_name,
1231 line);
1232 break;
1234 if (strcasecmp(command, "MENU")==0) {
1235 menu = wMenuCreate(scr, title, True);
1236 menu->on_destroy = removeShortcutsForMenu;
1237 if (!parseCascade(scr, menu, file, filename)) {
1238 wMenuDestroy(menu, True);
1240 break;
1241 } else {
1242 wwarning(_("%s:no title given for the root menu"), filename);
1243 break;
1247 pclose(file);
1249 return menu;
1254 typedef struct {
1255 char *name;
1256 int index;
1257 } dir_data;
1260 static int
1261 myCompare(dir_data *d1, dir_data *d2)
1263 return strcmp(d1->name, d2->name);
1267 /************ Menu Configuration From Directory *************/
1269 static Bool
1270 isFilePackage(char *file)
1272 int l;
1274 /* check if the extension indicates this file is a
1275 * file package. For now, only recognize .themed */
1277 l = strlen(file);
1279 if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
1280 return True;
1281 } else {
1282 return False;
1287 static WMenu*
1288 readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
1290 DIR *dir;
1291 struct dirent *dentry;
1292 struct stat stat_buf;
1293 WMenu *menu=NULL;
1294 char *buffer;
1295 LinkedList *dirs = NULL, *files = NULL;
1296 int length, i, have_space=0;
1297 dir_data *data;
1298 int stripExtension = 0;
1300 i=0;
1301 while (path[i]!=NULL) {
1302 if (strcmp(path[i], "-noext")==0) {
1303 stripExtension = 1;
1304 i++;
1305 continue;
1308 dir = opendir(path[i]);
1309 if (!dir) {
1310 i++;
1311 continue;
1314 while ((dentry = readdir(dir))) {
1316 if (strcmp(dentry->d_name, ".")==0 ||
1317 strcmp(dentry->d_name, "..")==0)
1318 continue;
1320 if (dentry->d_name[0] == '.')
1321 continue;
1323 buffer = wmalloc(strlen(path[i])+strlen(dentry->d_name)+4);
1324 if (!buffer) {
1325 wsyserror(_("out of memory while constructing directory menu %s"),
1326 path[i]);
1327 break;
1330 strcpy(buffer, path[i]);
1331 strcat(buffer, "/");
1332 strcat(buffer, dentry->d_name);
1334 if (stat(buffer, &stat_buf)!=0) {
1335 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1336 path[i], dentry->d_name);
1337 } else {
1338 Bool isFilePack = False;
1340 data = NULL;
1341 if (S_ISDIR(stat_buf.st_mode)
1342 && !(isFilePack = isFilePackage(dentry->d_name))) {
1344 /* access always returns success for user root */
1345 if (access(buffer, X_OK)==0) {
1346 /* Directory is accesible. Add to directory list */
1348 data = (dir_data*) wmalloc(sizeof(dir_data));
1349 data->name = wstrdup(dentry->d_name);
1350 data->index = i;
1352 list_insert_sorted(data, &dirs, (int(*)())myCompare);
1354 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1355 /* Hack because access always returns X_OK success for user root */
1356 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1357 if ((command!=NULL && access(buffer, R_OK)==0) ||
1358 (command==NULL && access(buffer, X_OK)==0 &&
1359 (stat_buf.st_mode & S_IXANY))) {
1361 data = (dir_data*) wmalloc(sizeof(dir_data));
1362 data->name = wstrdup(dentry->d_name);
1363 data->index = i;
1365 list_insert_sorted(data, &files, (int(*)())myCompare);
1369 free(buffer);
1372 closedir(dir);
1373 i++;
1376 if (!dirs && !files)
1377 return NULL;
1379 menu = wMenuCreate(scr, title, False);
1380 menu->on_destroy = removeShortcutsForMenu;
1382 while (dirs != NULL) {
1383 /* New directory. Use same OPEN_MENU command that was used
1384 * for the current directory. */
1385 dir_data *d = (dir_data*)dirs->head;
1387 length = strlen(path[d->index])+strlen(d->name)+6;
1388 if (command)
1389 length += strlen(command) + 6;
1390 buffer = wmalloc(length);
1391 if (!buffer) {
1392 wsyserror(_("out of memory while constructing directory menu %s"),
1393 path[d->index]);
1394 break;
1397 have_space = strchr(path[d->index], ' ')!=NULL ||
1398 strchr(d->name, ' ')!=NULL;
1399 if (have_space) {
1400 buffer[0] = '"';
1401 buffer[1] = 0;
1402 strcat(buffer, path[d->index]);
1403 } else {
1404 strcpy(buffer, path[d->index]);
1406 strcat(buffer, "/");
1407 strcat(buffer, d->name);
1408 if (have_space)
1409 strcat(buffer, "\"");
1410 if (command) {
1411 strcat(buffer, " WITH ");
1412 strcat(buffer, command);
1415 addMenuEntry(menu, d->name, NULL, "OPEN_MENU", buffer, path[d->index]);
1417 free(buffer);
1418 if (dirs->head) {
1419 if (d->name)
1420 free(d->name);
1421 free(dirs->head);
1423 list_remove_head(&dirs);
1426 while (files != NULL) {
1427 /* executable: add as entry */
1428 dir_data *f = (dir_data*) files->head;;
1430 length = strlen(path[f->index])+strlen(f->name)+6;
1431 if (command)
1432 length += strlen(command);
1434 buffer = wmalloc(length);
1435 if (!buffer) {
1436 wsyserror(_("out of memory while constructing directory menu %s"),
1437 path[f->index]);
1438 break;
1441 have_space = strchr(path[f->index], ' ')!=NULL ||
1442 strchr(f->name, ' ')!=NULL;
1443 if (command!=NULL) {
1444 strcpy(buffer, command);
1445 strcat(buffer, " ");
1446 if (have_space)
1447 strcat(buffer, "\"");
1448 strcat(buffer, path[f->index]);
1449 } else {
1450 if (have_space) {
1451 buffer[0] = '"';
1452 buffer[1] = 0;
1453 strcat(buffer, path[f->index]);
1454 } else {
1455 strcpy(buffer, path[f->index]);
1458 strcat(buffer, "/");
1459 strcat(buffer, f->name);
1460 if (have_space)
1461 strcat(buffer, "\"");
1463 if (stripExtension) {
1464 char *ptr = strrchr(f->name, '.');
1465 if (ptr && ptr!=f->name)
1466 *ptr = 0;
1468 addMenuEntry(menu, f->name, NULL, "EXEC", buffer, path[f->index]);
1470 free(buffer);
1471 if (files->head) {
1472 if (f->name)
1473 free(f->name);
1474 free(files->head);
1476 list_remove_head(&files);
1479 return menu;
1483 /************ Menu Configuration From WMRootMenu *************/
1485 static WMenu*
1486 makeDefaultMenu(WScreen *scr)
1488 WMenu *menu=NULL;
1490 menu = wMenuCreate(scr, _("Commands"), True);
1491 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1492 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1493 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1494 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1495 return menu;
1503 *----------------------------------------------------------------------
1504 * configureMenu--
1505 * Reads root menu configuration from defaults database.
1507 *----------------------------------------------------------------------
1509 static WMenu*
1510 configureMenu(WScreen *scr, proplist_t definition)
1512 WMenu *menu = NULL;
1513 proplist_t elem;
1514 int i, count;
1515 proplist_t title, command, params;
1516 char *tmp, *mtitle;
1519 if (PLIsString(definition)) {
1520 struct stat stat_buf;
1521 char *path = NULL;
1522 Bool menu_is_default = False;
1524 /* menu definition is a string. Probably a path, so parse the file */
1526 tmp = wexpandpath(PLGetString(definition));
1528 path = getLocalizedMenuFile(tmp);
1530 if (!path)
1531 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1533 if (!path) {
1534 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1535 menu_is_default = True;
1538 if (!path) {
1539 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1540 tmp);
1541 free(tmp);
1542 return NULL;
1545 if (stat(path, &stat_buf)<0) {
1546 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1547 free(path);
1548 free(tmp);
1549 return NULL;
1552 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1553 /* if the pointer in WMRootMenu has changed */
1554 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1556 if (menu_is_default) {
1557 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1558 path);
1561 menu = readMenuFile(scr, path);
1562 if (menu)
1563 menu->timestamp = MAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1564 } else {
1565 menu = NULL;
1567 free(path);
1568 free(tmp);
1570 return menu;
1573 count = PLGetNumberOfElements(definition);
1574 if (count==0)
1575 return NULL;
1577 elem = PLGetArrayElement(definition, 0);
1578 if (!PLIsString(elem)) {
1579 tmp = PLGetDescription(elem);
1580 wwarning(_("%s:format error in root menu configuration \"%s\""),
1581 "WMRootMenu", tmp);
1582 free(tmp);
1583 return NULL;
1585 mtitle = PLGetString(elem);
1587 menu = wMenuCreate(scr, mtitle, False);
1588 menu->on_destroy = removeShortcutsForMenu;
1590 #ifdef GLOBAL_SUBMENU_FILE
1592 WMenu *submenu;
1593 WMenuEntry *mentry;
1595 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1597 if (submenu) {
1598 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1599 wMenuEntrySetCascade(menu, mentry, submenu);
1602 #endif
1604 for (i=1; i<count; i++) {
1605 elem = PLGetArrayElement(definition, i);
1606 #if 0
1607 if (PLIsString(elem)) {
1608 char *file;
1610 file = PLGetString(elem);
1613 #endif
1614 if (!PLIsArray(elem) || PLGetNumberOfElements(elem) < 2)
1615 goto error;
1617 if (PLIsArray(PLGetArrayElement(elem,1))) {
1618 WMenu *submenu;
1619 WMenuEntry *mentry;
1621 /* submenu */
1622 submenu = configureMenu(scr, elem);
1623 if (submenu) {
1624 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
1625 NULL);
1626 wMenuEntrySetCascade(menu, mentry, submenu);
1628 } else {
1629 int idx = 0;
1630 char *shortcut;
1631 /* normal entry */
1633 title = PLGetArrayElement(elem, idx++);
1634 shortcut = PLGetArrayElement(elem, idx++);
1635 if (strcmp(PLGetString(shortcut), "SHORTCUT")==0) {
1636 shortcut = PLGetArrayElement(elem, idx++);
1637 command = PLGetArrayElement(elem, idx++);
1638 } else {
1639 command = shortcut;
1640 shortcut = NULL;
1642 params = PLGetArrayElement(elem, idx++);
1644 if (!title || !command)
1645 goto error;
1647 addMenuEntry(menu, PLGetString(title),
1648 shortcut ? PLGetString(shortcut) : NULL,
1649 PLGetString(command),
1650 params ? PLGetString(params) : NULL, "WMRootMenu");
1652 continue;
1654 error:
1655 tmp = PLGetDescription(elem);
1656 wwarning(_("%s:format error in root menu configuration \"%s\""),
1657 "WMRootMenu", tmp);
1658 free(tmp);
1661 return menu;
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.
1679 * Side effects:
1680 * The menu may be remade.
1682 * Notes:
1683 * Construction of OPEN_MENU entries are delayed to the moment the
1684 * user map's them.
1685 *----------------------------------------------------------------------
1687 void
1688 OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
1690 WMenu *menu=NULL;
1691 proplist_t definition;
1693 static proplist_t domain=NULL;
1695 if (!domain) {
1696 domain = PLMakeString("WMRootMenu");
1700 scr->flags.root_menu_changed_shortcuts = 0;
1701 scr->flags.added_workspace_menu = 0;
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);
1710 if (keyboard)
1711 wMenuMapAt(menu, 0, 0, True);
1712 else
1713 wMenuMapCopyAt(menu, x-menu->frame->core->width/2,
1714 y-menu->frame->top_width/2);
1716 return;
1720 definition = WDRootMenu->dictionary;
1723 definition = PLGetDomain(domain);
1725 if (definition) {
1726 if (PLIsArray(definition)) {
1727 if (!scr->root_menu
1728 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1729 menu = configureMenu(scr, definition);
1730 if (menu)
1731 menu->timestamp = WDRootMenu->timestamp;
1733 } else
1734 menu = NULL;
1735 } else {
1736 menu = configureMenu(scr, definition);
1740 if (!menu) {
1741 /* menu hasn't changed or could not be read */
1742 if (!scr->root_menu) {
1743 wMessageDialog(scr, _("Error"),
1744 _("The applications menu could not be loaded."
1745 "Look at the console output for a detailed"
1746 "description of the errors"),
1747 _("OK"), NULL, NULL);
1749 menu = makeDefaultMenu(scr);
1750 scr->root_menu = menu;
1752 menu = scr->root_menu;
1753 } else {
1754 /* new root menu */
1755 if (scr->root_menu)
1756 wMenuDestroy(scr->root_menu, True);
1757 scr->root_menu = menu;
1759 if (menu) {
1760 wMenuMapAt(menu, x-menu->frame->core->width/2, y-menu->frame->top_width/2,
1761 keyboard);
1764 if (scr->flags.root_menu_changed_shortcuts)
1765 rebindKeygrabs(scr);
1768 #endif /* !LITE */