Code update for Window Maker version 0.50.0
[wmaker-crm.git] / src / rootmenu.c
blobd3f03eeef9ffb81545e4f71b4e3f518786e7f893
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).
141 * CLEAR_SESSION - clears any previous saved session. This will not have
142 * any effect if SaveSessionOnExit is True.
146 #define MAX(a,b) ((a)>(b) ? (a) : (b))
149 #define M_QUICK 1
151 /* menu commands */
153 static void
154 execCommand(WMenu *menu, WMenuEntry *entry)
156 char *cmdline;
158 cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata);
160 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
161 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT],
162 CurrentTime);
163 XSync(dpy, 0);
165 if (cmdline) {
166 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
167 free(cmdline);
169 XUngrabPointer(dpy, CurrentTime);
170 XSync(dpy, 0);
174 static void
175 exitCommand(WMenu *menu, WMenuEntry *entry)
177 static int inside = 0;
179 /* prevent reentrant calls */
180 if (inside)
181 return;
182 inside = 1;
184 if ((int)entry->clientdata==M_QUICK
185 || wMessageDialog(menu->frame->screen_ptr, _("Exit"),
186 _("Exit window manager?"),
187 _("Exit"), _("Cancel"), NULL)==WAPRDefault) {
188 #ifdef DEBUG
189 printf("Exiting WindowMaker.\n");
190 #endif
191 Shutdown(WSExitMode);
193 inside = 0;
197 static void
198 shutdownCommand(WMenu *menu, WMenuEntry *entry)
200 static int inside = 0;
201 int result;
203 /* prevent reentrant calls */
204 if (inside)
205 return;
206 inside = 1;
208 #define R_CANCEL 0
209 #define R_CLOSE 1
210 #define R_KILL 2
213 result = R_CANCEL;
214 if ((int)entry->clientdata==M_QUICK)
215 result = R_CLOSE;
216 else {
217 #ifdef R6SM
218 if (wSessionIsManaged()) {
219 int r;
221 r = wMessageDialog(menu->frame->screen_ptr,
222 _("Close X session"),
223 _("Close Window System session?\n"
224 "Kill might close applications with unsaved data."),
225 _("Close"), _("Kill"), _("Cancel"));
226 if (r==WAPRDefault)
227 result = R_CLOSE;
228 else if (r==WAPRAlternate)
229 result = R_KILL;
230 } else
231 #endif
233 int r;
235 r = wMessageDialog(menu->frame->screen_ptr,
236 _("Kill X session"),
237 _("Kill Window System session?\n"
238 "(all applications will be closed)"),
239 _("Kill"), _("Cancel"), NULL);
240 if (r==WAPRDefault)
241 result = R_KILL;
245 if (result!=R_CANCEL) {
246 #ifdef R6SM
247 if (result == R_CLOSE) {
248 Shutdown(WSLogoutMode);
249 } else
250 #endif /* R6SM */
252 Shutdown(WSKillMode);
255 #undef R_CLOSE
256 #undef R_CANCEL
257 #undef R_KILL
258 inside = 0;
262 static void
263 restartCommand(WMenu *menu, WMenuEntry *entry)
265 Shutdown(WSRestartPreparationMode);
266 Restart((char*)entry->clientdata);
270 static void
271 refreshCommand(WMenu *menu, WMenuEntry *entry)
273 wRefreshDesktop(menu->frame->screen_ptr);
277 static void
278 arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
280 wArrangeIcons(menu->frame->screen_ptr, True);
283 static void
284 showAllCommand(WMenu *menu, WMenuEntry *entry)
286 wShowAllWindows(menu->frame->screen_ptr);
289 static void
290 hideOthersCommand(WMenu *menu, WMenuEntry *entry)
292 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
296 static void
297 saveSessionCommand(WMenu *menu, WMenuEntry *entry)
299 wSessionSaveState(menu->frame->screen_ptr);
303 static void
304 clearSessionCommand(WMenu *menu, WMenuEntry *entry)
306 wSessionClearState(menu->frame->screen_ptr);
310 static void
311 infoPanelCommand(WMenu *menu, WMenuEntry *entry)
313 wShowInfoPanel(menu->frame->screen_ptr);
317 static void
318 legalPanelCommand(WMenu *menu, WMenuEntry *entry)
320 wShowLegalPanel(menu->frame->screen_ptr);
325 /********************************************************************/
327 static void
328 raiseMenus(WMenu *menu)
330 int i;
332 if (menu->flags.mapped) {
333 wRaiseFrame(menu->frame->core);
335 for (i=0; i<menu->cascade_no; i++) {
336 if (menu->cascades[i])
337 raiseMenus(menu->cascades[i]);
343 Bool
344 wRootMenuPerformShortcut(XEvent *event)
346 Shortcut *ptr;
347 int modifiers;
348 int done = 0;
350 /* ignore CapsLock */
351 modifiers = event->xkey.state & ValidModMask;
353 for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
354 if (ptr->keycode==0)
355 continue;
357 if (ptr->keycode==event->xkey.keycode && (ptr->modifier==modifiers)) {
358 (*ptr->entry->callback)(ptr->menu, ptr->entry);
359 done = True;
362 return done;
366 void
367 wRootMenuBindShortcuts(Window window)
369 Shortcut *ptr;
371 ptr = shortcutList;
372 while (ptr) {
373 if (ptr->modifier!=AnyModifier) {
374 XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
375 window, True, GrabModeAsync, GrabModeAsync);
376 #ifdef NUMLOCK_HACK
377 wHackedGrabKey(ptr->keycode, ptr->modifier,
378 window, True, GrabModeAsync, GrabModeAsync);
379 #endif
381 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
382 GrabModeAsync, GrabModeAsync);
383 ptr = ptr->next;
388 static void
389 rebindKeygrabs(WScreen *scr)
391 WWindow *wwin;
393 wwin = scr->focused_window;
395 while (wwin!=NULL) {
396 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
398 if (!WFLAGP(wwin, no_bind_keys)) {
399 wWindowSetKeyGrabs(wwin);
401 wwin = wwin->prev;
406 static void
407 removeShortcutsForMenu(WMenu *menu)
409 Shortcut *ptr, *tmp;
410 Shortcut *newList = NULL;
412 ptr = shortcutList;
413 while (ptr!=NULL) {
414 tmp = ptr->next;
415 if (ptr->menu == menu) {
416 free(ptr);
417 } else {
418 ptr->next = newList;
419 newList = ptr;
421 ptr = tmp;
423 shortcutList = newList;
424 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
428 static Bool
429 addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
430 WMenuEntry *entry)
432 Shortcut *ptr;
433 KeySym ksym;
434 char *k;
435 char buf[128], *b;
437 ptr = wmalloc(sizeof(Shortcut));
439 strcpy(buf, shortcutDefinition);
440 b = (char*)buf;
442 /* get modifiers */
443 ptr->modifier = 0;
444 while ((k = strchr(b, '+'))!=NULL) {
445 int mod;
447 *k = 0;
448 mod = wXModifierFromKey(b);
449 if (mod<0) {
450 wwarning(_("%s:invalid key modifier \"%s\""), file, b);
451 free(ptr);
452 return False;
454 ptr->modifier |= mod;
456 b = k+1;
459 /* get key */
460 ksym = XStringToKeysym(b);
462 if (ksym==NoSymbol) {
463 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
464 file, shortcutDefinition, entry->text);
465 free(ptr);
466 return False;
469 ptr->keycode = XKeysymToKeycode(dpy, ksym);
470 if (ptr->keycode==0) {
471 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
472 shortcutDefinition, entry->text);
473 free(ptr);
474 return False;
477 ptr->menu = menu;
478 ptr->entry = entry;
480 ptr->next = shortcutList;
481 shortcutList = ptr;
483 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
485 return True;
489 /*******************************/
491 static char*
492 cropline(char *line)
494 char *end;
496 if (strlen(line)==0)
497 return line;
499 end = &(line[strlen(line)])-1;
500 while (isspace(*line) && *line!=0) line++;
501 while (end>line && isspace(*end)) {
502 *end=0;
503 end--;
505 return line;
509 static char*
510 next_token(char *line, char **next)
512 char *tmp, c;
513 char *ret;
515 *next = NULL;
516 while (*line==' ' || *line=='\t') line++;
518 tmp = line;
520 if (*tmp=='"') {
521 tmp++; line++;
522 while (*tmp!=0 && *tmp!='"') tmp++;
523 if (*tmp!='"') {
524 wwarning(_("%s: unmatched '\"' in menu file"), line);
525 return NULL;
527 } else {
528 do {
529 if (*tmp=='\\')
530 tmp++;
532 if (*tmp!=0)
533 tmp++;
535 } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
538 c = *tmp;
539 *tmp = 0;
540 ret = wstrdup(line);
541 *tmp = c;
543 if (c==0)
544 return ret;
545 else
546 tmp++;
548 /* skip blanks */
549 while (*tmp==' ' || *tmp=='\t') tmp++;
551 if (*tmp!=0)
552 *next = tmp;
554 return ret;
558 static void
559 separateCommand(char *line, char ***file, char **command)
561 char *token, *tmp = line;
562 LinkedList *list = NULL;
563 int count, i;
565 *file = NULL;
566 *command = NULL;
567 do {
568 token = next_token(tmp, &tmp);
569 if (token) {
570 if (strcmp(token, "WITH")==0) {
571 if (tmp!=NULL && *tmp!=0)
572 *command = wstrdup(tmp);
573 else
574 wwarning(_("%s: missing command"), line);
575 break;
577 list = list_cons(token, list);
579 } while (token!=NULL && tmp!=NULL);
581 count = list_length(list);
582 if (count>0) {
583 *file = wmalloc(sizeof(char*)*(count+1));
584 i = count;
585 (*file)[count] = NULL;
586 while (list!=NULL) {
587 (*file)[--i] = list->head;
588 list_remove_head(&list);
594 static void
595 constructMenu(WMenu *menu, WMenuEntry *entry)
597 WMenu *submenu;
598 struct stat stat_buf;
599 char **path;
600 char *cmd;
601 char *lpath = NULL;
602 int i, first=-1;
603 time_t last=0;
605 separateCommand((char*)entry->clientdata, &path, &cmd);
606 if (!path || *path==NULL || **path==0) {
607 wwarning(_("invalid OPEN_MENU specification: %s"),
608 (char*)entry->clientdata);
609 return;
612 if (path[0][0]=='|') {
613 /* pipe menu */
615 if (!menu->cascades[entry->cascade] ||
616 menu->cascades[entry->cascade]->timestamp == 0) {
617 /* parse pipe */
619 submenu = readMenuPipe(menu->frame->screen_ptr, path);
621 /* there's no automatic reloading */
622 if(submenu != NULL)
623 submenu->timestamp = 1;
624 } else {
625 submenu = NULL;
628 } else {
629 i=0;
630 while(path[i] != NULL) {
631 char *tmp;
632 Bool statted = False;
634 if (strcmp(path[i], "-noext")==0) {
635 i++;
636 continue;
639 tmp = wexpandpath(path[i]);
640 free(path[i]);
641 path[i] = tmp;
643 if (Locale) {
644 lpath = wmalloc(strlen(path[i])+32);
646 strcpy(lpath, path[i]);
647 strcat(lpath, ".");
648 strcat(lpath, Locale);
649 if (stat(lpath, &stat_buf)<0) {
650 int i;
651 i = strlen(Locale);
652 if (i>2) {
653 lpath[strlen(lpath)-(i-2)]=0;
654 if (stat(lpath, &stat_buf)==0) {
655 statted = True;
656 free(path[i]);
657 path[i] = lpath;
658 lpath = NULL;
661 } else {
662 statted = True;
663 free(path[i]);
664 path[i] = lpath;
665 lpath = NULL;
669 if (lpath) {
670 free(lpath);
671 lpath = NULL;
674 if (statted || stat(path[i], &stat_buf)==0) {
675 if (last < stat_buf.st_mtime)
676 last = stat_buf.st_mtime;
677 if (first<0)
678 first=i;
679 } else {
680 wsyserror(_("%s:could not stat menu"), path[i]);
681 /*goto finish;*/
684 i++;
687 if (first < 0) {
688 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
689 (char*)entry->clientdata);
690 goto finish;
692 stat(path[first], &stat_buf);
693 if (!menu->cascades[entry->cascade]
694 || menu->cascades[entry->cascade]->timestamp < last) {
696 if (S_ISDIR(stat_buf.st_mode)) {
697 /* menu directory */
698 submenu = readMenuDirectory(menu->frame->screen_ptr,
699 entry->text, path, cmd);
700 if (submenu)
701 submenu->timestamp = last;
702 } else if (S_ISREG(stat_buf.st_mode)) {
703 /* menu file */
705 if (cmd || path[1])
706 wwarning(_("too many parameters in OPEN_MENU: %s"),
707 (char*)entry->clientdata);
709 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
710 if (submenu)
711 submenu->timestamp = stat_buf.st_mtime;
712 } else {
713 submenu = NULL;
715 } else {
716 submenu = NULL;
720 if (submenu) {
721 wMenuEntryRemoveCascade(menu, entry);
722 wMenuEntrySetCascade(menu, entry, submenu);
725 finish:
726 i = 0;
727 while (path[i]!=NULL)
728 free(path[i++]);
729 free(path);
730 if (cmd)
731 free(cmd);
735 static WMenuEntry*
736 addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
738 WMenu *wsmenu;
739 WMenuEntry *entry;
741 if (scr->flags.added_workspace_menu) {
742 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
743 return NULL;
744 } else {
745 scr->flags.added_workspace_menu = 1;
747 wsmenu = wWorkspaceMenuMake(scr, True);
748 scr->workspace_menu = wsmenu;
749 entry = wMenuAddCallback(menu, title, NULL, NULL);
750 wMenuEntrySetCascade(menu, entry, wsmenu);
752 wWorkspaceMenuUpdate(scr, wsmenu);
754 return entry;
758 static WMenuEntry*
759 addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
760 char *params, char *file_name)
762 WScreen *scr;
763 WMenuEntry *entry = NULL;
764 Bool shortcutOk = False;
766 if (!menu)
767 return NULL;
768 scr = menu->frame->screen_ptr;
769 if (strcmp(command, "OPEN_MENU")==0) {
770 if (!params) {
771 wwarning(_("%s:missing parameter for menu command \"%s\""),
772 file_name, command);
773 } else {
774 WMenu *dummy;
775 char *path;
777 path = wfindfile(DEF_CONFIG_PATHS, params);
778 if (!path) {
779 path = wstrdup(params);
781 dummy = wMenuCreate(scr, title, False);
782 dummy->on_destroy = removeShortcutsForMenu;
783 entry = wMenuAddCallback(menu, title, constructMenu, path);
784 entry->free_cdata = free;
785 wMenuEntrySetCascade(menu, entry, dummy);
787 } else if (strcmp(command, "EXEC")==0) {
788 if (!params)
789 wwarning(_("%s:missing parameter for menu command \"%s\""),
790 file_name, command);
791 else {
792 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
793 entry->free_cdata = free;
794 shortcutOk = True;
796 } else if (strcmp(command, "EXIT")==0) {
798 if (params && strcmp(params, "QUICK")==0)
799 entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
800 else
801 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
803 shortcutOk = True;
804 } else if (strcmp(command, "SHUTDOWN")==0) {
806 if (params && strcmp(params, "QUICK")==0)
807 entry = wMenuAddCallback(menu, title, shutdownCommand,
808 (void*)M_QUICK);
809 else
810 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
812 shortcutOk = True;
813 } else if (strcmp(command, "REFRESH")==0) {
814 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
816 shortcutOk = True;
817 } else if (strcmp(command, "WORKSPACE_MENU")==0) {
818 entry = addWorkspaceMenu(scr, menu, title);
820 shortcutOk = True;
821 } else if (strcmp(command, "ARRANGE_ICONS")==0) {
822 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
824 shortcutOk = True;
825 } else if (strcmp(command, "HIDE_OTHERS")==0) {
826 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
828 shortcutOk = True;
829 } else if (strcmp(command, "SHOW_ALL")==0) {
830 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
832 shortcutOk = True;
833 } else if (strcmp(command, "RESTART")==0) {
834 entry = wMenuAddCallback(menu, title, restartCommand,
835 params ? wstrdup(params) : NULL);
836 entry->free_cdata = free;
837 shortcutOk = True;
838 } else if (strcmp(command, "SAVE_SESSION")==0) {
839 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
841 shortcutOk = True;
842 } else if (strcmp(command, "CLEAR_SESSION")==0) {
843 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
844 shortcutOk = True;
845 } else if (strcmp(command, "INFO_PANEL")==0) {
846 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
847 shortcutOk = True;
848 } else if (strcmp(command, "LEGAL_PANEL")==0) {
849 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
850 shortcutOk = True;
851 } else {
852 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
853 command);
855 return NULL;
858 if (shortcut && entry) {
859 if (!shortcutOk) {
860 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
861 title);
862 } else {
863 if (addShortcut(file_name, shortcut, menu, entry)) {
865 entry->rtext = GetShortcutString(shortcut);
867 entry->rtext = wstrdup(shortcut);
873 return entry;
878 /******************* Menu Configuration From File *******************/
880 static void
881 separateline(char *line, char *title, char *command, char *parameter,
882 char *shortcut)
884 int l, i;
886 l = strlen(line);
888 *title = 0;
889 *command = 0;
890 *parameter = 0;
891 *shortcut = 0;
892 /* get the title */
893 while (isspace(*line) && (*line!=0)) line++;
894 if (*line=='"') {
895 line++;
896 i=0;
897 while (line[i]!='"' && (line[i]!=0)) i++;
898 if (line[i]!='"')
899 return;
900 } else {
901 i=0;
902 while (!isspace(line[i]) && (line[i]!=0)) i++;
904 strncpy(title, line, i);
905 title[i++]=0;
906 line+=i;
908 /* get the command or shortcut keyword */
909 while (isspace(*line) && (*line!=0)) line++;
910 if (*line==0)
911 return;
912 i=0;
913 while (!isspace(line[i]) && (line[i]!=0)) i++;
914 strncpy(command, line, i);
915 command[i++]=0;
916 line+=i;
918 if (strcmp(command, "SHORTCUT")==0) {
919 /* get the shortcut key */
920 while (isspace(*line) && (*line!=0)) line++;
921 if (*line=='"') {
922 line++;
923 i=0;
924 while (line[i]!='"' && (line[i]!=0)) i++;
925 if (line[i]!='"')
926 return;
927 } else {
928 i=0;
929 while (!isspace(line[i]) && (line[i]!=0)) i++;
931 strncpy(shortcut, line, i);
932 shortcut[i++]=0;
933 line+=i;
935 *command=0;
937 /* get the command */
938 while (isspace(*line) && (*line!=0)) line++;
939 if (*line==0)
940 return;
941 i=0;
942 while (!isspace(line[i]) && (line[i]!=0)) i++;
943 strncpy(command, line, i);
944 command[i++]=0;
945 line+=i;
948 /* get the parameters */
949 while (isspace(*line) && (*line!=0)) line++;
950 if (*line==0)
951 return;
953 if (*line=='"') {
954 line++;
955 l = 0;
956 while (line[l]!=0 && line[l]!='"') {
957 parameter[l] = line[l];
958 l++;
960 parameter[l] = 0;
961 return;
964 l = strlen(line);
965 while (isspace(line[l]) && (l>0)) l--;
966 strncpy(parameter, line, l);
967 parameter[l]=0;
971 static WMenu*
972 parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
974 char linebuf[MAXLINE];
975 char elinebuf[MAXLINE];
976 char title[MAXLINE];
977 char command[MAXLINE];
978 char shortcut[MAXLINE];
979 char params[MAXLINE];
980 char *line;
982 while (!IsEof(file)) {
983 int lsize, ok;
985 ok = 0;
986 fgets(linebuf, MAXLINE, file);
987 line = cropline(linebuf);
988 lsize = strlen(line);
989 do {
990 if (line[lsize-1]=='\\') {
991 char *line2;
992 int lsize2;
993 fgets(elinebuf, MAXLINE, file);
994 line2=cropline(elinebuf);
995 lsize2=strlen(line2);
996 if (lsize2+lsize>MAXLINE) {
997 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
998 file_name, line);
999 ok=2;
1000 } else {
1001 line[lsize-1]=0;
1002 lsize+=lsize2-1;
1003 strcat(line, line2);
1005 } else {
1006 ok=1;
1008 } while (!ok && !IsEof(file));
1009 if (ok==2)
1010 continue;
1012 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1013 continue;
1016 separateline(line, title, command, params, shortcut);
1018 if (!command[0]) {
1019 wwarning(_("%s:missing command in menu config: %s"), file_name,
1020 line);
1021 goto error;
1024 if (strcasecmp(command, "MENU")==0) {
1025 WMenu *cascade;
1027 /* start submenu */
1029 cascade = wMenuCreate(scr, title, False);
1030 cascade->on_destroy = removeShortcutsForMenu;
1031 if (parseCascade(scr, cascade, file, file_name)==NULL) {
1032 wMenuDestroy(cascade, True);
1033 } else {
1034 wMenuEntrySetCascade(menu,
1035 wMenuAddCallback(menu, title, NULL, NULL),
1036 cascade);
1038 } else if (strcasecmp(command, "END")==0) {
1039 /* end of menu */
1040 return menu;
1042 } else {
1043 /* normal items */
1044 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1045 params[0] ? params : NULL, file_name);
1049 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1050 file_name);
1051 return menu;
1053 error:
1054 return menu;
1058 static WMenu*
1059 readMenuFile(WScreen *scr, char *file_name)
1061 WMenu *menu=NULL;
1062 FILE *file = NULL;
1063 char linebuf[MAXLINE];
1064 char title[MAXLINE];
1065 char shortcut[MAXLINE];
1066 char command[MAXLINE];
1067 char params[MAXLINE];
1068 char *line;
1069 #ifdef USECPP
1070 char *args;
1071 int cpp = 0;
1072 #endif
1074 #ifdef USECPP
1075 if (!wPreferences.flags.nocpp) {
1076 args = MakeCPPArgs(file_name);
1077 if (!args) {
1078 wwarning(_("could not make arguments for menu file preprocessor"));
1079 } else {
1080 sprintf(command, "%s %s %s", CPP_PATH, args, file_name);
1081 free(args);
1082 file = popen(command, "r");
1083 if (!file) {
1084 wsyserror(_("%s:could not open/preprocess menu file"),
1085 file_name);
1086 } else {
1087 cpp = 1;
1091 #endif /* USECPP */
1093 if (!file) {
1094 file = fopen(file_name, "r");
1095 if (!file) {
1096 wsyserror(_("%s:could not open menu file"), file_name);
1097 return NULL;
1101 while (!IsEof(file)) {
1102 if (!fgets(linebuf, MAXLINE, file))
1103 break;
1104 line = cropline(linebuf);
1105 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1106 continue;
1108 separateline(line, title, command, params, shortcut);
1110 if (!command[0]) {
1111 wwarning(_("%s:missing command in menu config: %s"), file_name,
1112 line);
1113 break;
1115 if (strcasecmp(command, "MENU")==0) {
1116 menu = wMenuCreate(scr, title, True);
1117 menu->on_destroy = removeShortcutsForMenu;
1118 if (!parseCascade(scr, menu, file, file_name)) {
1119 wMenuDestroy(menu, True);
1121 break;
1122 } else {
1123 wwarning(_("%s:invalid menu file. MENU command is missing"),
1124 file_name);
1125 break;
1129 #ifdef CPP
1130 if (cpp) {
1131 if (pclose(file)==-1) {
1132 wsyserror(_("error reading preprocessed menu data"));
1134 } else {
1135 fclose(file);
1137 #else
1138 fclose(file);
1139 #endif
1141 return menu;
1144 /************ Menu Configuration From Pipe *************/
1146 static WMenu*
1147 readMenuPipe(WScreen *scr, char **file_name)
1149 WMenu *menu=NULL;
1150 FILE *file = NULL;
1151 char linebuf[MAXLINE];
1152 char title[MAXLINE];
1153 char command[MAXLINE];
1154 char params[MAXLINE];
1155 char shortcut[MAXLINE];
1156 char *line;
1157 char * filename;
1158 char flat_file[MAXLINE];
1159 int i;
1160 #ifdef USECPP
1161 char *args;
1162 int cpp = 0;
1163 #endif
1165 flat_file[0] = '\0';
1167 for(i = 0 ; file_name[i] != NULL ; i++) {
1168 strcat(flat_file, file_name[i]);
1169 strcat(flat_file, " ");
1171 filename = flat_file+1;
1173 #ifdef USECPP
1174 if (!wPreferences.flags.nocpp) {
1175 args = MakeCPPArgs(filename);
1176 if (!args) {
1177 wwarning(_("could not make arguments for menu file preprocessor"));
1178 } else {
1179 sprintf(command, "%s | %s %s", filename, CPP_PATH, args);
1181 free(args);
1182 file = popen(command, "r");
1183 if (!file) {
1184 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1185 } else {
1186 cpp = 1;
1191 #endif /* USECPP */
1193 if (!file) {
1194 file = popen(filename, "r");
1196 if (!file) {
1197 wsyserror(_("%s:could not open menu file"), filename);
1198 return NULL;
1202 while (!IsEof(file)) {
1203 if (!fgets(linebuf, MAXLINE, file))
1204 break;
1205 line = cropline(linebuf);
1206 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1207 continue;
1209 separateline(line, title, command, params, shortcut);
1211 if (!command[0]) {
1212 wwarning(_("%s:missing command in menu config: %s"), file_name,
1213 line);
1214 break;
1216 if (strcasecmp(command, "MENU")==0) {
1217 menu = wMenuCreate(scr, title, True);
1218 menu->on_destroy = removeShortcutsForMenu;
1219 if (!parseCascade(scr, menu, file, filename)) {
1220 wMenuDestroy(menu, True);
1222 break;
1223 } else {
1224 wwarning(_("%s:no title given for the root menu"), filename);
1225 break;
1229 pclose(file);
1231 return menu;
1236 typedef struct {
1237 char *name;
1238 int index;
1239 } dir_data;
1242 static int
1243 myCompare(dir_data *d1, dir_data *d2)
1245 return strcmp(d1->name, d2->name);
1249 /************ Menu Configuration From Directory *************/
1251 static Bool
1252 isFilePackage(char *file)
1254 int l;
1256 /* check if the extension indicates this file is a
1257 * file package. For now, only recognize .themed */
1259 l = strlen(file);
1261 if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
1262 return True;
1263 } else {
1264 return False;
1269 static WMenu*
1270 readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
1272 DIR *dir;
1273 struct dirent *dentry;
1274 struct stat stat_buf;
1275 WMenu *menu=NULL;
1276 char *buffer;
1277 LinkedList *dirs = NULL, *files = NULL;
1278 int length, i, have_space=0;
1279 dir_data *data;
1280 int stripExtension = 0;
1282 i=0;
1283 while (path[i]!=NULL) {
1284 if (strcmp(path[i], "-noext")==0) {
1285 stripExtension = 1;
1286 i++;
1287 continue;
1290 dir = opendir(path[i]);
1291 if (!dir) {
1292 i++;
1293 continue;
1296 while ((dentry = readdir(dir))) {
1298 if (strcmp(dentry->d_name, ".")==0 ||
1299 strcmp(dentry->d_name, "..")==0)
1300 continue;
1302 if (dentry->d_name[0] == '.')
1303 continue;
1305 buffer = wmalloc(strlen(path[i])+strlen(dentry->d_name)+4);
1306 if (!buffer) {
1307 wsyserror(_("out of memory while constructing directory menu %s"),
1308 path[i]);
1309 break;
1312 strcpy(buffer, path[i]);
1313 strcat(buffer, "/");
1314 strcat(buffer, dentry->d_name);
1316 if (stat(buffer, &stat_buf)!=0) {
1317 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1318 path[i], dentry->d_name);
1319 } else {
1320 Bool isFilePack = False;
1322 data = NULL;
1323 if (S_ISDIR(stat_buf.st_mode)
1324 && !(isFilePack = isFilePackage(dentry->d_name))) {
1326 /* access always returns success for user root */
1327 if (access(buffer, X_OK)==0) {
1328 /* Directory is accesible. Add to directory list */
1330 data = (dir_data*) wmalloc(sizeof(dir_data));
1331 data->name = wstrdup(dentry->d_name);
1332 data->index = i;
1334 list_insert_sorted(data, &dirs, (int(*)())myCompare);
1336 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1337 /* Hack because access always returns X_OK success for user root */
1338 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1339 if ((command!=NULL && access(buffer, R_OK)==0) ||
1340 (command==NULL && access(buffer, X_OK)==0 &&
1341 (stat_buf.st_mode & S_IXANY))) {
1343 data = (dir_data*) wmalloc(sizeof(dir_data));
1344 data->name = wstrdup(dentry->d_name);
1345 data->index = i;
1347 list_insert_sorted(data, &files, (int(*)())myCompare);
1351 free(buffer);
1354 closedir(dir);
1355 i++;
1358 if (!dirs && !files)
1359 return NULL;
1361 menu = wMenuCreate(scr, title, False);
1362 menu->on_destroy = removeShortcutsForMenu;
1364 while (dirs != NULL) {
1365 /* New directory. Use same OPEN_MENU command that was used
1366 * for the current directory. */
1367 dir_data *d = (dir_data*)dirs->head;
1369 length = strlen(path[d->index])+strlen(d->name)+6;
1370 if (command)
1371 length += strlen(command) + 6;
1372 buffer = wmalloc(length);
1373 if (!buffer) {
1374 wsyserror(_("out of memory while constructing directory menu %s"),
1375 path[d->index]);
1376 break;
1379 have_space = strchr(path[d->index], ' ')!=NULL ||
1380 strchr(d->name, ' ')!=NULL;
1381 if (have_space) {
1382 buffer[0] = '"';
1383 buffer[1] = 0;
1384 strcat(buffer, path[d->index]);
1385 } else {
1386 strcpy(buffer, path[d->index]);
1388 strcat(buffer, "/");
1389 strcat(buffer, d->name);
1390 if (have_space)
1391 strcat(buffer, "\"");
1392 if (command) {
1393 strcat(buffer, " WITH ");
1394 strcat(buffer, command);
1397 addMenuEntry(menu, d->name, NULL, "OPEN_MENU", buffer, path[d->index]);
1399 free(buffer);
1400 if (dirs->head) {
1401 if (d->name)
1402 free(d->name);
1403 free(dirs->head);
1405 list_remove_head(&dirs);
1408 while (files != NULL) {
1409 /* executable: add as entry */
1410 dir_data *f = (dir_data*) files->head;;
1412 length = strlen(path[f->index])+strlen(f->name)+6;
1413 if (command)
1414 length += strlen(command);
1416 buffer = wmalloc(length);
1417 if (!buffer) {
1418 wsyserror(_("out of memory while constructing directory menu %s"),
1419 path[f->index]);
1420 break;
1423 have_space = strchr(path[f->index], ' ')!=NULL ||
1424 strchr(f->name, ' ')!=NULL;
1425 if (command!=NULL) {
1426 strcpy(buffer, command);
1427 strcat(buffer, " ");
1428 if (have_space)
1429 strcat(buffer, "\"");
1430 strcat(buffer, path[f->index]);
1431 } else {
1432 if (have_space) {
1433 buffer[0] = '"';
1434 buffer[1] = 0;
1435 strcat(buffer, path[f->index]);
1436 } else {
1437 strcpy(buffer, path[f->index]);
1440 strcat(buffer, "/");
1441 strcat(buffer, f->name);
1442 if (have_space)
1443 strcat(buffer, "\"");
1445 if (stripExtension) {
1446 char *ptr = strrchr(f->name, '.');
1447 if (ptr && ptr!=f->name)
1448 *ptr = 0;
1450 addMenuEntry(menu, f->name, NULL, "EXEC", buffer, path[f->index]);
1452 free(buffer);
1453 if (files->head) {
1454 if (f->name)
1455 free(f->name);
1456 free(files->head);
1458 list_remove_head(&files);
1461 return menu;
1465 /************ Menu Configuration From WMRootMenu *************/
1467 static WMenu*
1468 makeDefaultMenu(WScreen *scr)
1470 WMenu *menu=NULL;
1472 menu = wMenuCreate(scr, _("Commands"), True);
1473 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1474 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1475 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1476 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1477 return menu;
1485 *----------------------------------------------------------------------
1486 * configureMenu--
1487 * Reads root menu configuration from defaults database.
1489 *----------------------------------------------------------------------
1491 static WMenu*
1492 configureMenu(WScreen *scr, proplist_t definition)
1494 WMenu *menu = NULL;
1495 proplist_t elem;
1496 int i, count;
1497 proplist_t title, command, params;
1498 char *tmp, *mtitle;
1501 if (PLIsString(definition)) {
1502 struct stat stat_buf;
1503 char *path = NULL;
1504 Bool menu_is_default = False;
1506 /* menu definition is a string. Probably a path, so parse the file */
1508 tmp = wexpandpath(PLGetString(definition));
1510 if (Locale) {
1511 path = wmalloc(strlen(tmp)+32);
1513 strcpy(path, tmp);
1514 strcat(path, ".");
1515 strcat(path, Locale);
1517 /* look for menu.xy */
1518 if (stat(path, &stat_buf)<0) {
1519 int i;
1520 i = strlen(Locale);
1521 if (i>2) {
1522 path[strlen(path)-(i-2)]=0;
1523 /* look for menu.xy_zw */
1524 if (stat(path, &stat_buf)<0) {
1525 free(path);
1526 /* If did not find any localized menus, try
1527 * only menu. This can also mean that
1528 * the path in WMRootMenu was already the
1529 * path for the localized menu (eg: menu = "menu.ab")
1531 path = NULL;
1533 } else {
1534 free(path);
1535 path = NULL;
1540 if (!path)
1541 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1543 if (!path) {
1544 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1545 menu_is_default = True;
1548 if (!path) {
1549 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1550 tmp);
1551 free(tmp);
1552 return NULL;
1555 if (stat(path, &stat_buf)<0) {
1556 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1557 free(path);
1558 free(tmp);
1559 return NULL;
1562 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1563 /* if the pointer in WMRootMenu has changed */
1564 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1566 if (menu_is_default) {
1567 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1568 path);
1571 menu = readMenuFile(scr, path);
1572 if (menu)
1573 menu->timestamp = MAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1574 } else {
1575 menu = NULL;
1577 free(path);
1578 free(tmp);
1580 return menu;
1583 count = PLGetNumberOfElements(definition);
1584 if (count==0)
1585 return NULL;
1587 elem = PLGetArrayElement(definition, 0);
1588 if (!PLIsString(elem)) {
1589 tmp = PLGetDescription(elem);
1590 wwarning(_("%s:format error in root menu configuration \"%s\""),
1591 "WMRootMenu", tmp);
1592 free(tmp);
1593 return NULL;
1595 mtitle = PLGetString(elem);
1597 menu = wMenuCreate(scr, mtitle, False);
1598 menu->on_destroy = removeShortcutsForMenu;
1600 #ifdef GLOBAL_SUBMENU_FILE
1602 WMenu *submenu;
1603 WMenuEntry *mentry;
1605 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1607 if (submenu) {
1608 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1609 wMenuEntrySetCascade(menu, mentry, submenu);
1612 #endif
1614 for (i=1; i<count; i++) {
1615 elem = PLGetArrayElement(definition, i);
1616 #if 0
1617 if (PLIsString(elem)) {
1618 char *file;
1620 file = PLGetString(elem);
1623 #endif
1624 if (!PLIsArray(elem) || PLGetNumberOfElements(elem) < 2)
1625 goto error;
1627 if (PLIsArray(PLGetArrayElement(elem,1))) {
1628 WMenu *submenu;
1629 WMenuEntry *mentry;
1631 /* submenu */
1632 submenu = configureMenu(scr, elem);
1633 if (submenu) {
1634 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
1635 NULL);
1636 wMenuEntrySetCascade(menu, mentry, submenu);
1638 } else {
1639 int idx = 0;
1640 char *shortcut;
1641 /* normal entry */
1643 title = PLGetArrayElement(elem, idx++);
1644 shortcut = PLGetArrayElement(elem, idx++);
1645 if (strcmp(PLGetString(shortcut), "SHORTCUT")==0) {
1646 shortcut = PLGetArrayElement(elem, idx++);
1647 command = PLGetArrayElement(elem, idx++);
1648 } else {
1649 command = shortcut;
1650 shortcut = NULL;
1652 params = PLGetArrayElement(elem, idx++);
1654 if (!title || !command)
1655 goto error;
1657 addMenuEntry(menu, PLGetString(title),
1658 shortcut ? PLGetString(shortcut) : NULL,
1659 PLGetString(command),
1660 params ? PLGetString(params) : NULL, "WMRootMenu");
1662 continue;
1664 error:
1665 tmp = PLGetDescription(elem);
1666 wwarning(_("%s:format error in root menu configuration \"%s\""),
1667 "WMRootMenu", tmp);
1668 free(tmp);
1671 return menu;
1682 *----------------------------------------------------------------------
1683 * OpenRootMenu--
1684 * Opens the root menu, parsing the menu configuration from the
1685 * defaults database.
1686 * If the menu is already mapped and is not sticked to the
1687 * root window, it will be unmapped.
1689 * Side effects:
1690 * The menu may be remade.
1692 * Notes:
1693 * Construction of OPEN_MENU entries are delayed to the moment the
1694 * user map's them.
1695 *----------------------------------------------------------------------
1697 void
1698 OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
1700 WMenu *menu=NULL;
1701 proplist_t definition;
1703 static proplist_t domain=NULL;
1705 if (!domain) {
1706 domain = PLMakeString("WMRootMenu");
1710 scr->flags.root_menu_changed_shortcuts = 0;
1711 scr->flags.added_workspace_menu = 0;
1713 if (scr->root_menu && scr->root_menu->flags.mapped) {
1714 menu = scr->root_menu;
1715 if (!menu->flags.buttoned) {
1716 wMenuUnmap(menu);
1717 } else {
1718 wRaiseFrame(menu->frame->core);
1720 if (keyboard)
1721 wMenuMapAt(menu, 0, 0, True);
1722 else
1723 wMenuMapCopyAt(menu, x-menu->frame->core->width/2,
1724 y-menu->frame->top_width/2);
1726 return;
1730 definition = WDRootMenu->dictionary;
1733 definition = PLGetDomain(domain);
1735 if (definition) {
1736 if (PLIsArray(definition)) {
1737 if (!scr->root_menu
1738 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1739 menu = configureMenu(scr, definition);
1740 if (menu)
1741 menu->timestamp = WDRootMenu->timestamp;
1743 } else
1744 menu = NULL;
1745 } else {
1746 menu = configureMenu(scr, definition);
1750 if (!menu) {
1751 /* menu hasn't changed or could not be read */
1752 if (!scr->root_menu) {
1753 menu = makeDefaultMenu(scr);
1754 scr->root_menu = menu;
1756 menu = scr->root_menu;
1757 } else {
1758 /* new root menu */
1759 if (scr->root_menu)
1760 wMenuDestroy(scr->root_menu, True);
1761 scr->root_menu = menu;
1763 if (menu) {
1764 wMenuMapAt(menu, x-menu->frame->core->width/2, y-menu->frame->top_width/2,
1765 keyboard);
1768 if (scr->flags.root_menu_changed_shortcuts)
1769 rebindKeygrabs(scr);
1772 #endif /* !LITE */