0.51.1 pre snapshot. Be careful, it may be buggy. It fixes some bugs though.
[wmaker-crm.git] / src / rootmenu.c
blob4704c5a5f7ee8feb550cafb0b790e340a0cc24d1
1 /* rootmenu.c- user defined menu
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
23 #include "wconfig.h"
25 #ifndef LITE
27 #include <assert.h>
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>
39 #include <X11/Xlib.h>
40 #include <X11/Xutil.h>
41 #include <X11/Xatom.h>
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 #include <proplist.h>
57 #include "list.h"
60 extern char *Locale;
62 extern WDDomain *WDRootMenu;
64 extern Cursor wCursor[WCUR_LAST];
66 extern Time LastTimestamp;
68 extern WPreferences wPreferences;
70 extern int wScreenCount;
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,
75 char *command);
78 typedef struct Shortcut {
79 struct Shortcut *next;
81 int modifier;
82 KeyCode keycode;
83 WMenuEntry *entry;
84 WMenu *menu;
85 } Shortcut;
89 static Shortcut *shortcutList = NULL;
93 * Syntax:
94 * # main menu
95 * "Menu Name" MENU
96 * "Title" EXEC command_to_exec -params
97 * "Submenu" MENU
98 * "Title" EXEC command_to_exec -params
99 * "Submenu" END
100 * "Workspaces" WORKSPACE_MENU
101 * "Title" built_in_command
102 * "Quit" EXIT
103 * "Quick Quit" EXIT QUICK
104 * "Menu Name" END
106 * Commands may be preceded by SHORTCUT key
108 * Built-in commands:
110 * INFO_PANEL - shows the Info Panel
111 * LEGAL_PANEL - shows the Legal info panel
112 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
113 * REFRESH - forces the desktop to be repainted
114 * EXIT [QUICK] - exit the window manager [without confirmation]
115 * EXEC <program> - execute an external program
116 * WORKSPACE_MENU - places the workspace submenu
117 * ARRANGE_ICONS
118 * RESTART [<window manager>] - restarts the window manager
119 * SHOW_ALL - unhide all windows on workspace
120 * HIDE_OTHERS - hides all windows excep the focused one
121 * OPEN_MENU file - read menu data from file which must be a valid menu file.
122 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
123 * - read menu data from directory(ies) and
124 * eventually precede each with a command.
125 * OPEN_MENU | command
126 * - opens command and uses its stdout to construct and insert
127 * the resulting menu in current position. The output of
128 * command must be a valid menu description.
129 * The space between '|' and command is optional.
130 * SAVE_SESSION - saves the current state of the desktop, which include
131 * all running applications, all their hints (geometry,
132 * position on screen, workspace they live on, the dock
133 * or clip from where they were launched, and
134 * if minimized, shaded or hidden. Also saves the current
135 * workspace the user is on. All will be restored on every
136 * start of windowmaker until another SAVE_SESSION or
137 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
138 * WindowMaker domain file, then saving is automatically
139 * done on every windowmaker exit, overwriting any
140 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
141 * dock state now.
142 * CLEAR_SESSION - clears any previous saved session. This will not have
143 * any effect if SaveSessionOnExit is True.
147 #define MAX(a,b) ((a)>(b) ? (a) : (b))
150 #define M_QUICK 1
152 /* menu commands */
154 static void
155 execCommand(WMenu *menu, WMenuEntry *entry)
157 char *cmdline;
159 cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata);
161 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
162 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT],
163 CurrentTime);
164 XSync(dpy, 0);
166 if (cmdline) {
167 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
168 free(cmdline);
170 XUngrabPointer(dpy, CurrentTime);
171 XSync(dpy, 0);
175 static void
176 exitCommand(WMenu *menu, WMenuEntry *entry)
178 static int inside = 0;
180 /* prevent reentrant calls */
181 if (inside)
182 return;
183 inside = 1;
185 if ((long)entry->clientdata==M_QUICK
186 || wMessageDialog(menu->frame->screen_ptr, _("Exit"),
187 _("Exit window manager?"),
188 _("Exit"), _("Cancel"), NULL)==WAPRDefault) {
189 #ifdef DEBUG
190 printf("Exiting WindowMaker.\n");
191 #endif
192 Shutdown(WSExitMode);
194 inside = 0;
198 static void
199 shutdownCommand(WMenu *menu, WMenuEntry *entry)
201 static int inside = 0;
202 int result;
204 /* prevent reentrant calls */
205 if (inside)
206 return;
207 inside = 1;
209 #define R_CANCEL 0
210 #define R_CLOSE 1
211 #define R_KILL 2
214 result = R_CANCEL;
215 if ((long)entry->clientdata==M_QUICK)
216 result = R_CLOSE;
217 else {
218 #ifdef XSMP_ENABLED
219 if (wSessionIsManaged()) {
220 int r;
222 r = wMessageDialog(menu->frame->screen_ptr,
223 _("Close X session"),
224 _("Close Window System session?\n"
225 "Kill might close applications with unsaved data."),
226 _("Close"), _("Kill"), _("Cancel"));
227 if (r==WAPRDefault)
228 result = R_CLOSE;
229 else if (r==WAPRAlternate)
230 result = R_KILL;
231 } else
232 #endif
234 int r;
236 r = wMessageDialog(menu->frame->screen_ptr,
237 _("Kill X session"),
238 _("Kill Window System session?\n"
239 "(all applications will be closed)"),
240 _("Kill"), _("Cancel"), NULL);
241 if (r==WAPRDefault)
242 result = R_KILL;
246 if (result!=R_CANCEL) {
247 #ifdef XSMP_ENABLED
248 if (result == R_CLOSE) {
249 Shutdown(WSLogoutMode);
250 } else
251 #endif /* XSMP_ENABLED */
253 Shutdown(WSKillMode);
256 #undef R_CLOSE
257 #undef R_CANCEL
258 #undef R_KILL
259 inside = 0;
263 static void
264 restartCommand(WMenu *menu, WMenuEntry *entry)
266 Shutdown(WSRestartPreparationMode);
267 Restart((char*)entry->clientdata);
271 static void
272 refreshCommand(WMenu *menu, WMenuEntry *entry)
274 wRefreshDesktop(menu->frame->screen_ptr);
278 static void
279 arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
281 wArrangeIcons(menu->frame->screen_ptr, True);
284 static void
285 showAllCommand(WMenu *menu, WMenuEntry *entry)
287 wShowAllWindows(menu->frame->screen_ptr);
290 static void
291 hideOthersCommand(WMenu *menu, WMenuEntry *entry)
293 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
297 static void
298 saveSessionCommand(WMenu *menu, WMenuEntry *entry)
300 if (!wPreferences.save_session_on_exit)
301 wSessionSaveState(menu->frame->screen_ptr);
303 wScreenSaveState(menu->frame->screen_ptr);
307 static void
308 clearSessionCommand(WMenu *menu, WMenuEntry *entry)
310 wSessionClearState(menu->frame->screen_ptr);
314 static void
315 infoPanelCommand(WMenu *menu, WMenuEntry *entry)
317 wShowInfoPanel(menu->frame->screen_ptr);
321 static void
322 legalPanelCommand(WMenu *menu, WMenuEntry *entry)
324 wShowLegalPanel(menu->frame->screen_ptr);
328 /********************************************************************/
331 static char*
332 getLocalizedMenuFile(char *menu)
334 char *buffer;
335 char *ptr;
337 if (!Locale)
338 return NULL;
340 buffer = wmalloc(strlen(menu)+32);
342 /* try menu.locale_name */
343 sprintf(buffer, "%s.%s", menu, Locale);
344 if (access(buffer, F_OK)==0) {
345 return buffer;
347 /* check if it is in the form aa_bb.encoding and check for aa_bb */
348 ptr = strchr(Locale, '.');
349 if (ptr) {
350 *ptr = 0;
351 if (access(buffer, F_OK)==0) {
352 return buffer;
355 /* now check for aa */
356 ptr = strchr(buffer, '_');
357 if (ptr) {
358 *ptr = 0;
359 if (access(buffer, F_OK)==0) {
360 return buffer;
364 return NULL;
368 static void
369 raiseMenus(WMenu *menu)
371 int i;
373 if (menu->flags.mapped) {
374 wRaiseFrame(menu->frame->core);
376 for (i=0; i<menu->cascade_no; i++) {
377 if (menu->cascades[i])
378 raiseMenus(menu->cascades[i]);
384 Bool
385 wRootMenuPerformShortcut(XEvent *event)
387 Shortcut *ptr;
388 int modifiers;
389 int done = 0;
391 /* ignore CapsLock */
392 modifiers = event->xkey.state & ValidModMask;
394 for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
395 if (ptr->keycode==0)
396 continue;
398 if (ptr->keycode==event->xkey.keycode && (ptr->modifier==modifiers)) {
399 (*ptr->entry->callback)(ptr->menu, ptr->entry);
400 done = True;
403 return done;
407 void
408 wRootMenuBindShortcuts(Window window)
410 Shortcut *ptr;
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,
419 window, True, GrabModeAsync, GrabModeAsync);
420 #endif
422 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
423 GrabModeAsync, GrabModeAsync);
424 ptr = ptr->next;
429 static void
430 rebindKeygrabs(WScreen *scr)
432 WWindow *wwin;
434 wwin = scr->focused_window;
436 while (wwin!=NULL) {
437 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
439 if (!WFLAGP(wwin, no_bind_keys)) {
440 wWindowSetKeyGrabs(wwin);
442 wwin = wwin->prev;
447 static void
448 removeShortcutsForMenu(WMenu *menu)
450 Shortcut *ptr, *tmp;
451 Shortcut *newList = NULL;
453 ptr = shortcutList;
454 while (ptr!=NULL) {
455 tmp = ptr->next;
456 if (ptr->menu == menu) {
457 free(ptr);
458 } else {
459 ptr->next = newList;
460 newList = ptr;
462 ptr = tmp;
464 shortcutList = newList;
465 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
469 static Bool
470 addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
471 WMenuEntry *entry)
473 Shortcut *ptr;
474 KeySym ksym;
475 char *k;
476 char buf[128], *b;
478 ptr = wmalloc(sizeof(Shortcut));
480 strcpy(buf, shortcutDefinition);
481 b = (char*)buf;
483 /* get modifiers */
484 ptr->modifier = 0;
485 while ((k = strchr(b, '+'))!=NULL) {
486 int mod;
488 *k = 0;
489 mod = wXModifierFromKey(b);
490 if (mod<0) {
491 wwarning(_("%s:invalid key modifier \"%s\""), file, b);
492 free(ptr);
493 return False;
495 ptr->modifier |= mod;
497 b = k+1;
500 /* get key */
501 ksym = XStringToKeysym(b);
503 if (ksym==NoSymbol) {
504 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
505 file, shortcutDefinition, entry->text);
506 free(ptr);
507 return False;
510 ptr->keycode = XKeysymToKeycode(dpy, ksym);
511 if (ptr->keycode==0) {
512 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
513 shortcutDefinition, entry->text);
514 free(ptr);
515 return False;
518 ptr->menu = menu;
519 ptr->entry = entry;
521 ptr->next = shortcutList;
522 shortcutList = ptr;
524 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
526 return True;
530 /*******************************/
532 static char*
533 cropline(char *line)
535 char *end;
537 if (strlen(line)==0)
538 return line;
540 end = &(line[strlen(line)])-1;
541 while (isspace(*line) && *line!=0) line++;
542 while (end>line && isspace(*end)) {
543 *end=0;
544 end--;
546 return line;
550 static char*
551 next_token(char *line, char **next)
553 char *tmp, c;
554 char *ret;
556 *next = NULL;
557 while (*line==' ' || *line=='\t') line++;
559 tmp = line;
561 if (*tmp=='"') {
562 tmp++; line++;
563 while (*tmp!=0 && *tmp!='"') tmp++;
564 if (*tmp!='"') {
565 wwarning(_("%s: unmatched '\"' in menu file"), line);
566 return NULL;
568 } else {
569 do {
570 if (*tmp=='\\')
571 tmp++;
573 if (*tmp!=0)
574 tmp++;
576 } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
579 c = *tmp;
580 *tmp = 0;
581 ret = wstrdup(line);
582 *tmp = c;
584 if (c==0)
585 return ret;
586 else
587 tmp++;
589 /* skip blanks */
590 while (*tmp==' ' || *tmp=='\t') tmp++;
592 if (*tmp!=0)
593 *next = tmp;
595 return ret;
599 static void
600 separateCommand(char *line, char ***file, char **command)
602 char *token, *tmp = line;
603 LinkedList *list = NULL;
604 int count, i;
606 *file = NULL;
607 *command = NULL;
608 do {
609 token = next_token(tmp, &tmp);
610 if (token) {
611 if (strcmp(token, "WITH")==0) {
612 if (tmp!=NULL && *tmp!=0)
613 *command = wstrdup(tmp);
614 else
615 wwarning(_("%s: missing command"), line);
616 break;
618 list = list_cons(token, list);
620 } while (token!=NULL && tmp!=NULL);
622 count = list_length(list);
623 if (count>0) {
624 *file = wmalloc(sizeof(char*)*(count+1));
625 i = count;
626 (*file)[count] = NULL;
627 while (list!=NULL) {
628 (*file)[--i] = list->head;
629 list_remove_head(&list);
635 static void
636 constructMenu(WMenu *menu, WMenuEntry *entry)
638 WMenu *submenu;
639 struct stat stat_buf;
640 char **path;
641 char *cmd;
642 char *lpath = NULL;
643 int i, first=-1;
644 time_t last=0;
646 separateCommand((char*)entry->clientdata, &path, &cmd);
647 if (!path || *path==NULL || **path==0) {
648 wwarning(_("invalid OPEN_MENU specification: %s"),
649 (char*)entry->clientdata);
650 return;
653 if (path[0][0]=='|') {
654 /* pipe menu */
656 if (!menu->cascades[entry->cascade] ||
657 menu->cascades[entry->cascade]->timestamp == 0) {
658 /* parse pipe */
660 submenu = readMenuPipe(menu->frame->screen_ptr, path);
662 /* there's no automatic reloading */
663 if(submenu != NULL)
664 submenu->timestamp = 1;
665 } else {
666 submenu = NULL;
669 } else {
670 i=0;
671 while(path[i] != NULL) {
672 char *tmp;
674 if (strcmp(path[i], "-noext")==0) {
675 i++;
676 continue;
679 tmp = wexpandpath(path[i]);
680 free(path[i]);
681 lpath = getLocalizedMenuFile(tmp);
682 if (lpath) {
683 free(tmp);
684 path[i] = lpath;
685 lpath = NULL;
686 } else {
687 path[i] = tmp;
690 if (stat(path[i], &stat_buf)==0) {
691 if (last < stat_buf.st_mtime)
692 last = stat_buf.st_mtime;
693 if (first<0)
694 first=i;
695 } else {
696 wsyserror(_("%s:could not stat menu"), path[i]);
697 /*goto finish;*/
700 i++;
703 if (first < 0) {
704 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
705 (char*)entry->clientdata);
706 goto finish;
708 stat(path[first], &stat_buf);
709 if (!menu->cascades[entry->cascade]
710 || menu->cascades[entry->cascade]->timestamp < last) {
712 if (S_ISDIR(stat_buf.st_mode)) {
713 /* menu directory */
714 submenu = readMenuDirectory(menu->frame->screen_ptr,
715 entry->text, path, cmd);
716 if (submenu)
717 submenu->timestamp = last;
718 } else if (S_ISREG(stat_buf.st_mode)) {
719 /* menu file */
721 if (cmd || path[1])
722 wwarning(_("too many parameters in OPEN_MENU: %s"),
723 (char*)entry->clientdata);
725 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
726 if (submenu)
727 submenu->timestamp = stat_buf.st_mtime;
728 } else {
729 submenu = NULL;
731 } else {
732 submenu = NULL;
736 if (submenu) {
737 wMenuEntryRemoveCascade(menu, entry);
738 wMenuEntrySetCascade(menu, entry, submenu);
741 finish:
742 i = 0;
743 while (path[i]!=NULL)
744 free(path[i++]);
745 free(path);
746 if (cmd)
747 free(cmd);
751 static WMenuEntry*
752 addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
754 WMenu *wsmenu;
755 WMenuEntry *entry;
757 if (scr->flags.added_workspace_menu) {
758 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
759 return NULL;
760 } else {
761 scr->flags.added_workspace_menu = 1;
763 wsmenu = wWorkspaceMenuMake(scr, True);
764 scr->workspace_menu = wsmenu;
765 entry = wMenuAddCallback(menu, title, NULL, NULL);
766 wMenuEntrySetCascade(menu, entry, wsmenu);
768 wWorkspaceMenuUpdate(scr, wsmenu);
770 return entry;
774 static WMenuEntry*
775 addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
776 char *params, char *file_name)
778 WScreen *scr;
779 WMenuEntry *entry = NULL;
780 Bool shortcutOk = False;
782 if (!menu)
783 return NULL;
784 scr = menu->frame->screen_ptr;
785 if (strcmp(command, "OPEN_MENU")==0) {
786 if (!params) {
787 wwarning(_("%s:missing parameter for menu command \"%s\""),
788 file_name, command);
789 } else {
790 WMenu *dummy;
791 char *path;
793 path = wfindfile(DEF_CONFIG_PATHS, params);
794 if (!path) {
795 path = wstrdup(params);
797 dummy = wMenuCreate(scr, title, False);
798 dummy->on_destroy = removeShortcutsForMenu;
799 entry = wMenuAddCallback(menu, title, constructMenu, path);
800 entry->free_cdata = free;
801 wMenuEntrySetCascade(menu, entry, dummy);
803 } else if (strcmp(command, "EXEC")==0) {
804 if (!params)
805 wwarning(_("%s:missing parameter for menu command \"%s\""),
806 file_name, command);
807 else {
808 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
809 entry->free_cdata = free;
810 shortcutOk = True;
812 } else if (strcmp(command, "EXIT")==0) {
814 if (params && strcmp(params, "QUICK")==0)
815 entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
816 else
817 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
819 shortcutOk = True;
820 } else if (strcmp(command, "SHUTDOWN")==0) {
822 if (params && strcmp(params, "QUICK")==0)
823 entry = wMenuAddCallback(menu, title, shutdownCommand,
824 (void*)M_QUICK);
825 else
826 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
828 shortcutOk = True;
829 } else if (strcmp(command, "REFRESH")==0) {
830 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
832 shortcutOk = True;
833 } else if (strcmp(command, "WORKSPACE_MENU")==0) {
834 entry = addWorkspaceMenu(scr, menu, title);
836 shortcutOk = True;
837 } else if (strcmp(command, "ARRANGE_ICONS")==0) {
838 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
840 shortcutOk = True;
841 } else if (strcmp(command, "HIDE_OTHERS")==0) {
842 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
844 shortcutOk = True;
845 } else if (strcmp(command, "SHOW_ALL")==0) {
846 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
848 shortcutOk = True;
849 } else if (strcmp(command, "RESTART")==0) {
850 entry = wMenuAddCallback(menu, title, restartCommand,
851 params ? wstrdup(params) : NULL);
852 entry->free_cdata = free;
853 shortcutOk = True;
854 } else if (strcmp(command, "SAVE_SESSION")==0) {
855 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
857 shortcutOk = True;
858 } else if (strcmp(command, "CLEAR_SESSION")==0) {
859 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
860 shortcutOk = True;
861 } else if (strcmp(command, "INFO_PANEL")==0) {
862 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
863 shortcutOk = True;
864 } else if (strcmp(command, "LEGAL_PANEL")==0) {
865 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
866 shortcutOk = True;
867 } else {
868 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
869 command);
871 return NULL;
874 if (shortcut && entry) {
875 if (!shortcutOk) {
876 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
877 title);
878 } else {
879 if (addShortcut(file_name, shortcut, menu, entry)) {
881 entry->rtext = GetShortcutString(shortcut);
883 entry->rtext = wstrdup(shortcut);
889 return entry;
894 /******************* Menu Configuration From File *******************/
896 static void
897 separateline(char *line, char *title, char *command, char *parameter,
898 char *shortcut)
900 int l, i;
902 l = strlen(line);
904 *title = 0;
905 *command = 0;
906 *parameter = 0;
907 *shortcut = 0;
908 /* get the title */
909 while (isspace(*line) && (*line!=0)) line++;
910 if (*line=='"') {
911 line++;
912 i=0;
913 while (line[i]!='"' && (line[i]!=0)) i++;
914 if (line[i]!='"')
915 return;
916 } else {
917 i=0;
918 while (!isspace(line[i]) && (line[i]!=0)) i++;
920 strncpy(title, line, i);
921 title[i++]=0;
922 line+=i;
924 /* get the command or shortcut keyword */
925 while (isspace(*line) && (*line!=0)) line++;
926 if (*line==0)
927 return;
928 i=0;
929 while (!isspace(line[i]) && (line[i]!=0)) i++;
930 strncpy(command, line, i);
931 command[i++]=0;
932 line+=i;
934 if (strcmp(command, "SHORTCUT")==0) {
935 /* get the shortcut key */
936 while (isspace(*line) && (*line!=0)) line++;
937 if (*line=='"') {
938 line++;
939 i=0;
940 while (line[i]!='"' && (line[i]!=0)) i++;
941 if (line[i]!='"')
942 return;
943 } else {
944 i=0;
945 while (!isspace(line[i]) && (line[i]!=0)) i++;
947 strncpy(shortcut, line, i);
948 shortcut[i++]=0;
949 line+=i;
951 *command=0;
953 /* get the command */
954 while (isspace(*line) && (*line!=0)) line++;
955 if (*line==0)
956 return;
957 i=0;
958 while (!isspace(line[i]) && (line[i]!=0)) i++;
959 strncpy(command, line, i);
960 command[i++]=0;
961 line+=i;
964 /* get the parameters */
965 while (isspace(*line) && (*line!=0)) line++;
966 if (*line==0)
967 return;
969 if (*line=='"') {
970 line++;
971 l = 0;
972 while (line[l]!=0 && line[l]!='"') {
973 parameter[l] = line[l];
974 l++;
976 parameter[l] = 0;
977 return;
980 l = strlen(line);
981 while (isspace(line[l]) && (l>0)) l--;
982 strncpy(parameter, line, l);
983 parameter[l]=0;
987 static WMenu*
988 parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
990 char linebuf[MAXLINE];
991 char elinebuf[MAXLINE];
992 char title[MAXLINE];
993 char command[MAXLINE];
994 char shortcut[MAXLINE];
995 char params[MAXLINE];
996 char *line;
998 while (!IsEof(file)) {
999 int lsize, ok;
1001 ok = 0;
1002 fgets(linebuf, MAXLINE, file);
1003 line = cropline(linebuf);
1004 lsize = strlen(line);
1005 do {
1006 if (line[lsize-1]=='\\') {
1007 char *line2;
1008 int lsize2;
1009 fgets(elinebuf, MAXLINE, file);
1010 line2=cropline(elinebuf);
1011 lsize2=strlen(line2);
1012 if (lsize2+lsize>MAXLINE) {
1013 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1014 file_name, line);
1015 ok=2;
1016 } else {
1017 line[lsize-1]=0;
1018 lsize+=lsize2-1;
1019 strcat(line, line2);
1021 } else {
1022 ok=1;
1024 } while (!ok && !IsEof(file));
1025 if (ok==2)
1026 continue;
1028 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1029 continue;
1032 separateline(line, title, command, params, shortcut);
1034 if (!command[0]) {
1035 wwarning(_("%s:missing command in menu config: %s"), file_name,
1036 line);
1037 goto error;
1040 if (strcasecmp(command, "MENU")==0) {
1041 WMenu *cascade;
1043 /* start submenu */
1045 cascade = wMenuCreate(scr, title, False);
1046 cascade->on_destroy = removeShortcutsForMenu;
1047 if (parseCascade(scr, cascade, file, file_name)==NULL) {
1048 wMenuDestroy(cascade, True);
1049 } else {
1050 wMenuEntrySetCascade(menu,
1051 wMenuAddCallback(menu, title, NULL, NULL),
1052 cascade);
1054 } else if (strcasecmp(command, "END")==0) {
1055 /* end of menu */
1056 return menu;
1058 } else {
1059 /* normal items */
1060 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1061 params[0] ? params : NULL, file_name);
1065 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1066 file_name);
1067 return menu;
1069 error:
1070 return menu;
1074 static WMenu*
1075 readMenuFile(WScreen *scr, char *file_name)
1077 WMenu *menu=NULL;
1078 FILE *file = NULL;
1079 char linebuf[MAXLINE];
1080 char title[MAXLINE];
1081 char shortcut[MAXLINE];
1082 char command[MAXLINE];
1083 char params[MAXLINE];
1084 char *line;
1085 #ifdef USECPP
1086 char *args;
1087 int cpp = 0;
1088 #endif
1090 #ifdef USECPP
1091 if (!wPreferences.flags.nocpp) {
1092 args = MakeCPPArgs(file_name);
1093 if (!args) {
1094 wwarning(_("could not make arguments for menu file preprocessor"));
1095 } else {
1096 sprintf(command, "%s %s %s", CPP_PATH, args, file_name);
1097 free(args);
1098 file = popen(command, "r");
1099 if (!file) {
1100 wsyserror(_("%s:could not open/preprocess menu file"),
1101 file_name);
1102 } else {
1103 cpp = 1;
1107 #endif /* USECPP */
1109 if (!file) {
1110 file = fopen(file_name, "r");
1111 if (!file) {
1112 wsyserror(_("%s:could not open menu file"), file_name);
1113 return NULL;
1117 while (!IsEof(file)) {
1118 if (!fgets(linebuf, MAXLINE, file))
1119 break;
1120 line = cropline(linebuf);
1121 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1122 continue;
1124 separateline(line, title, command, params, shortcut);
1126 if (!command[0]) {
1127 wwarning(_("%s:missing command in menu config: %s"), file_name,
1128 line);
1129 break;
1131 if (strcasecmp(command, "MENU")==0) {
1132 menu = wMenuCreate(scr, title, True);
1133 menu->on_destroy = removeShortcutsForMenu;
1134 if (!parseCascade(scr, menu, file, file_name)) {
1135 wMenuDestroy(menu, True);
1137 break;
1138 } else {
1139 wwarning(_("%s:invalid menu file. MENU command is missing"),
1140 file_name);
1141 break;
1145 #ifdef CPP
1146 if (cpp) {
1147 if (pclose(file)==-1) {
1148 wsyserror(_("error reading preprocessed menu data"));
1150 } else {
1151 fclose(file);
1153 #else
1154 fclose(file);
1155 #endif
1157 return menu;
1160 /************ Menu Configuration From Pipe *************/
1162 static WMenu*
1163 readMenuPipe(WScreen *scr, char **file_name)
1165 WMenu *menu=NULL;
1166 FILE *file = NULL;
1167 char linebuf[MAXLINE];
1168 char title[MAXLINE];
1169 char command[MAXLINE];
1170 char params[MAXLINE];
1171 char shortcut[MAXLINE];
1172 char *line;
1173 char * filename;
1174 char flat_file[MAXLINE];
1175 int i;
1176 #ifdef USECPP
1177 char *args;
1178 int cpp = 0;
1179 #endif
1181 flat_file[0] = '\0';
1183 for(i = 0 ; file_name[i] != NULL ; i++) {
1184 strcat(flat_file, file_name[i]);
1185 strcat(flat_file, " ");
1187 filename = flat_file+1;
1189 #ifdef USECPP
1190 if (!wPreferences.flags.nocpp) {
1191 args = MakeCPPArgs(filename);
1192 if (!args) {
1193 wwarning(_("could not make arguments for menu file preprocessor"));
1194 } else {
1195 sprintf(command, "%s | %s %s", filename, CPP_PATH, args);
1197 free(args);
1198 file = popen(command, "r");
1199 if (!file) {
1200 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1201 } else {
1202 cpp = 1;
1207 #endif /* USECPP */
1209 if (!file) {
1210 file = popen(filename, "r");
1212 if (!file) {
1213 wsyserror(_("%s:could not open menu file"), filename);
1214 return NULL;
1218 while (!IsEof(file)) {
1219 if (!fgets(linebuf, MAXLINE, file))
1220 break;
1221 line = cropline(linebuf);
1222 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1223 continue;
1225 separateline(line, title, command, params, shortcut);
1227 if (!command[0]) {
1228 wwarning(_("%s:missing command in menu config: %s"), file_name,
1229 line);
1230 break;
1232 if (strcasecmp(command, "MENU")==0) {
1233 menu = wMenuCreate(scr, title, True);
1234 menu->on_destroy = removeShortcutsForMenu;
1235 if (!parseCascade(scr, menu, file, filename)) {
1236 wMenuDestroy(menu, True);
1238 break;
1239 } else {
1240 wwarning(_("%s:no title given for the root menu"), filename);
1241 break;
1245 pclose(file);
1247 return menu;
1252 typedef struct {
1253 char *name;
1254 int index;
1255 } dir_data;
1258 static int
1259 myCompare(dir_data *d1, dir_data *d2)
1261 return strcmp(d1->name, d2->name);
1265 /************ Menu Configuration From Directory *************/
1267 static Bool
1268 isFilePackage(char *file)
1270 int l;
1272 /* check if the extension indicates this file is a
1273 * file package. For now, only recognize .themed */
1275 l = strlen(file);
1277 if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
1278 return True;
1279 } else {
1280 return False;
1285 static WMenu*
1286 readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
1288 DIR *dir;
1289 struct dirent *dentry;
1290 struct stat stat_buf;
1291 WMenu *menu=NULL;
1292 char *buffer;
1293 LinkedList *dirs = NULL, *files = NULL;
1294 int length, i, have_space=0;
1295 dir_data *data;
1296 int stripExtension = 0;
1298 i=0;
1299 while (path[i]!=NULL) {
1300 if (strcmp(path[i], "-noext")==0) {
1301 stripExtension = 1;
1302 i++;
1303 continue;
1306 dir = opendir(path[i]);
1307 if (!dir) {
1308 i++;
1309 continue;
1312 while ((dentry = readdir(dir))) {
1314 if (strcmp(dentry->d_name, ".")==0 ||
1315 strcmp(dentry->d_name, "..")==0)
1316 continue;
1318 if (dentry->d_name[0] == '.')
1319 continue;
1321 buffer = wmalloc(strlen(path[i])+strlen(dentry->d_name)+4);
1322 if (!buffer) {
1323 wsyserror(_("out of memory while constructing directory menu %s"),
1324 path[i]);
1325 break;
1328 strcpy(buffer, path[i]);
1329 strcat(buffer, "/");
1330 strcat(buffer, dentry->d_name);
1332 if (stat(buffer, &stat_buf)!=0) {
1333 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1334 path[i], dentry->d_name);
1335 } else {
1336 Bool isFilePack = False;
1338 data = NULL;
1339 if (S_ISDIR(stat_buf.st_mode)
1340 && !(isFilePack = isFilePackage(dentry->d_name))) {
1342 /* access always returns success for user root */
1343 if (access(buffer, X_OK)==0) {
1344 /* Directory is accesible. Add to directory list */
1346 data = (dir_data*) wmalloc(sizeof(dir_data));
1347 data->name = wstrdup(dentry->d_name);
1348 data->index = i;
1350 list_insert_sorted(data, &dirs, (int(*)())myCompare);
1352 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1353 /* Hack because access always returns X_OK success for user root */
1354 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1355 if ((command!=NULL && access(buffer, R_OK)==0) ||
1356 (command==NULL && access(buffer, X_OK)==0 &&
1357 (stat_buf.st_mode & S_IXANY))) {
1359 data = (dir_data*) wmalloc(sizeof(dir_data));
1360 data->name = wstrdup(dentry->d_name);
1361 data->index = i;
1363 list_insert_sorted(data, &files, (int(*)())myCompare);
1367 free(buffer);
1370 closedir(dir);
1371 i++;
1374 if (!dirs && !files)
1375 return NULL;
1377 menu = wMenuCreate(scr, title, False);
1378 menu->on_destroy = removeShortcutsForMenu;
1380 while (dirs != NULL) {
1381 /* New directory. Use same OPEN_MENU command that was used
1382 * for the current directory. */
1383 dir_data *d = (dir_data*)dirs->head;
1385 length = strlen(path[d->index])+strlen(d->name)+6;
1386 if (command)
1387 length += strlen(command) + 6;
1388 buffer = wmalloc(length);
1389 if (!buffer) {
1390 wsyserror(_("out of memory while constructing directory menu %s"),
1391 path[d->index]);
1392 break;
1395 have_space = strchr(path[d->index], ' ')!=NULL ||
1396 strchr(d->name, ' ')!=NULL;
1397 if (have_space) {
1398 buffer[0] = '"';
1399 buffer[1] = 0;
1400 strcat(buffer, path[d->index]);
1401 } else {
1402 strcpy(buffer, path[d->index]);
1404 strcat(buffer, "/");
1405 strcat(buffer, d->name);
1406 if (have_space)
1407 strcat(buffer, "\"");
1408 if (command) {
1409 strcat(buffer, " WITH ");
1410 strcat(buffer, command);
1413 addMenuEntry(menu, d->name, NULL, "OPEN_MENU", buffer, path[d->index]);
1415 free(buffer);
1416 if (dirs->head) {
1417 if (d->name)
1418 free(d->name);
1419 free(dirs->head);
1421 list_remove_head(&dirs);
1424 while (files != NULL) {
1425 /* executable: add as entry */
1426 dir_data *f = (dir_data*) files->head;;
1428 length = strlen(path[f->index])+strlen(f->name)+6;
1429 if (command)
1430 length += strlen(command);
1432 buffer = wmalloc(length);
1433 if (!buffer) {
1434 wsyserror(_("out of memory while constructing directory menu %s"),
1435 path[f->index]);
1436 break;
1439 have_space = strchr(path[f->index], ' ')!=NULL ||
1440 strchr(f->name, ' ')!=NULL;
1441 if (command!=NULL) {
1442 strcpy(buffer, command);
1443 strcat(buffer, " ");
1444 if (have_space)
1445 strcat(buffer, "\"");
1446 strcat(buffer, path[f->index]);
1447 } else {
1448 if (have_space) {
1449 buffer[0] = '"';
1450 buffer[1] = 0;
1451 strcat(buffer, path[f->index]);
1452 } else {
1453 strcpy(buffer, path[f->index]);
1456 strcat(buffer, "/");
1457 strcat(buffer, f->name);
1458 if (have_space)
1459 strcat(buffer, "\"");
1461 if (stripExtension) {
1462 char *ptr = strrchr(f->name, '.');
1463 if (ptr && ptr!=f->name)
1464 *ptr = 0;
1466 addMenuEntry(menu, f->name, NULL, "EXEC", buffer, path[f->index]);
1468 free(buffer);
1469 if (files->head) {
1470 if (f->name)
1471 free(f->name);
1472 free(files->head);
1474 list_remove_head(&files);
1477 return menu;
1481 /************ Menu Configuration From WMRootMenu *************/
1483 static WMenu*
1484 makeDefaultMenu(WScreen *scr)
1486 WMenu *menu=NULL;
1488 menu = wMenuCreate(scr, _("Commands"), True);
1489 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1490 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1491 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1492 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1493 return menu;
1501 *----------------------------------------------------------------------
1502 * configureMenu--
1503 * Reads root menu configuration from defaults database.
1505 *----------------------------------------------------------------------
1507 static WMenu*
1508 configureMenu(WScreen *scr, proplist_t definition)
1510 WMenu *menu = NULL;
1511 proplist_t elem;
1512 int i, count;
1513 proplist_t title, command, params;
1514 char *tmp, *mtitle;
1517 if (PLIsString(definition)) {
1518 struct stat stat_buf;
1519 char *path = NULL;
1520 Bool menu_is_default = False;
1522 /* menu definition is a string. Probably a path, so parse the file */
1524 tmp = wexpandpath(PLGetString(definition));
1526 path = getLocalizedMenuFile(tmp);
1528 if (!path)
1529 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1531 if (!path) {
1532 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1533 menu_is_default = True;
1536 if (!path) {
1537 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1538 tmp);
1539 free(tmp);
1540 return NULL;
1543 if (stat(path, &stat_buf)<0) {
1544 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1545 free(path);
1546 free(tmp);
1547 return NULL;
1550 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1551 /* if the pointer in WMRootMenu has changed */
1552 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1554 if (menu_is_default) {
1555 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1556 path);
1559 menu = readMenuFile(scr, path);
1560 if (menu)
1561 menu->timestamp = MAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1562 } else {
1563 menu = NULL;
1565 free(path);
1566 free(tmp);
1568 return menu;
1571 count = PLGetNumberOfElements(definition);
1572 if (count==0)
1573 return NULL;
1575 elem = PLGetArrayElement(definition, 0);
1576 if (!PLIsString(elem)) {
1577 tmp = PLGetDescription(elem);
1578 wwarning(_("%s:format error in root menu configuration \"%s\""),
1579 "WMRootMenu", tmp);
1580 free(tmp);
1581 return NULL;
1583 mtitle = PLGetString(elem);
1585 menu = wMenuCreate(scr, mtitle, False);
1586 menu->on_destroy = removeShortcutsForMenu;
1588 #ifdef GLOBAL_SUBMENU_FILE
1590 WMenu *submenu;
1591 WMenuEntry *mentry;
1593 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1595 if (submenu) {
1596 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1597 wMenuEntrySetCascade(menu, mentry, submenu);
1600 #endif
1602 for (i=1; i<count; i++) {
1603 elem = PLGetArrayElement(definition, i);
1604 #if 0
1605 if (PLIsString(elem)) {
1606 char *file;
1608 file = PLGetString(elem);
1611 #endif
1612 if (!PLIsArray(elem) || PLGetNumberOfElements(elem) < 2)
1613 goto error;
1615 if (PLIsArray(PLGetArrayElement(elem,1))) {
1616 WMenu *submenu;
1617 WMenuEntry *mentry;
1619 /* submenu */
1620 submenu = configureMenu(scr, elem);
1621 if (submenu) {
1622 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
1623 NULL);
1624 wMenuEntrySetCascade(menu, mentry, submenu);
1626 } else {
1627 int idx = 0;
1628 char *shortcut;
1629 /* normal entry */
1631 title = PLGetArrayElement(elem, idx++);
1632 shortcut = PLGetArrayElement(elem, idx++);
1633 if (strcmp(PLGetString(shortcut), "SHORTCUT")==0) {
1634 shortcut = PLGetArrayElement(elem, idx++);
1635 command = PLGetArrayElement(elem, idx++);
1636 } else {
1637 command = shortcut;
1638 shortcut = NULL;
1640 params = PLGetArrayElement(elem, idx++);
1642 if (!title || !command)
1643 goto error;
1645 addMenuEntry(menu, PLGetString(title),
1646 shortcut ? PLGetString(shortcut) : NULL,
1647 PLGetString(command),
1648 params ? PLGetString(params) : NULL, "WMRootMenu");
1650 continue;
1652 error:
1653 tmp = PLGetDescription(elem);
1654 wwarning(_("%s:format error in root menu configuration \"%s\""),
1655 "WMRootMenu", tmp);
1656 free(tmp);
1659 return menu;
1670 *----------------------------------------------------------------------
1671 * OpenRootMenu--
1672 * Opens the root menu, parsing the menu configuration from the
1673 * defaults database.
1674 * If the menu is already mapped and is not sticked to the
1675 * root window, it will be unmapped.
1677 * Side effects:
1678 * The menu may be remade.
1680 * Notes:
1681 * Construction of OPEN_MENU entries are delayed to the moment the
1682 * user map's them.
1683 *----------------------------------------------------------------------
1685 void
1686 OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
1688 WMenu *menu=NULL;
1689 proplist_t definition;
1691 static proplist_t domain=NULL;
1693 if (!domain) {
1694 domain = PLMakeString("WMRootMenu");
1698 scr->flags.root_menu_changed_shortcuts = 0;
1699 scr->flags.added_workspace_menu = 0;
1701 if (scr->root_menu && scr->root_menu->flags.mapped) {
1702 menu = scr->root_menu;
1703 if (!menu->flags.buttoned) {
1704 wMenuUnmap(menu);
1705 } else {
1706 wRaiseFrame(menu->frame->core);
1708 if (keyboard)
1709 wMenuMapAt(menu, 0, 0, True);
1710 else
1711 wMenuMapCopyAt(menu, x-menu->frame->core->width/2,
1712 y-menu->frame->top_width/2);
1714 return;
1718 definition = WDRootMenu->dictionary;
1721 definition = PLGetDomain(domain);
1723 if (definition) {
1724 if (PLIsArray(definition)) {
1725 if (!scr->root_menu
1726 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1727 menu = configureMenu(scr, definition);
1728 if (menu)
1729 menu->timestamp = WDRootMenu->timestamp;
1731 } else
1732 menu = NULL;
1733 } else {
1734 menu = configureMenu(scr, definition);
1738 if (!menu) {
1739 /* menu hasn't changed or could not be read */
1740 if (!scr->root_menu) {
1741 menu = makeDefaultMenu(scr);
1742 scr->root_menu = menu;
1744 menu = scr->root_menu;
1745 } else {
1746 /* new root menu */
1747 if (scr->root_menu)
1748 wMenuDestroy(scr->root_menu, True);
1749 scr->root_menu = menu;
1751 if (menu) {
1752 wMenuMapAt(menu, x-menu->frame->core->width/2, y-menu->frame->top_width/2,
1753 keyboard);
1756 if (scr->flags.root_menu_changed_shortcuts)
1757 rebindKeygrabs(scr);
1760 #endif /* !LITE */