bug fixes
[wmaker-crm.git] / src / rootmenu.c
blob50d5cb2f59d869b159929cda9be14aab46230c25
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 * SHEXEC <command> - execute a shell command
118 * WORKSPACE_MENU - places the workspace submenu
119 * ARRANGE_ICONS
120 * RESTART [<window manager>] - restarts the window manager
121 * SHOW_ALL - unhide all windows on workspace
122 * HIDE_OTHERS - hides all windows excep the focused one
123 * OPEN_MENU file - read menu data from file which must be a valid menu file.
124 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
125 * - read menu data from directory(ies) and
126 * eventually precede each with a command.
127 * OPEN_MENU | command
128 * - opens command and uses its stdout to construct and insert
129 * the resulting menu in current position. The output of
130 * command must be a valid menu description.
131 * The space between '|' and command is optional.
132 * SAVE_SESSION - saves the current state of the desktop, which include
133 * all running applications, all their hints (geometry,
134 * position on screen, workspace they live on, the dock
135 * or clip from where they were launched, and
136 * if minimized, shaded or hidden. Also saves the current
137 * workspace the user is on. All will be restored on every
138 * start of windowmaker until another SAVE_SESSION or
139 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
140 * WindowMaker domain file, then saving is automatically
141 * done on every windowmaker exit, overwriting any
142 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
143 * dock state now.
144 * CLEAR_SESSION - clears any previous saved session. This will not have
145 * any effect if SaveSessionOnExit is True.
149 #define MAX(a,b) ((a)>(b) ? (a) : (b))
152 #define M_QUICK 1
154 /* menu commands */
156 static void
157 execCommand(WMenu *menu, WMenuEntry *entry)
159 char *cmdline;
161 cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata);
163 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
164 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT],
165 CurrentTime);
166 XSync(dpy, 0);
168 if (cmdline) {
169 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
170 free(cmdline);
172 XUngrabPointer(dpy, CurrentTime);
173 XSync(dpy, 0);
177 static void
178 exitCommand(WMenu *menu, WMenuEntry *entry)
180 static int inside = 0;
182 /* prevent reentrant calls */
183 if (inside)
184 return;
185 inside = 1;
187 if ((long)entry->clientdata==M_QUICK
188 || wMessageDialog(menu->frame->screen_ptr, _("Exit"),
189 _("Exit window manager?"),
190 _("Exit"), _("Cancel"), NULL)==WAPRDefault) {
191 #ifdef DEBUG
192 printf("Exiting WindowMaker.\n");
193 #endif
194 Shutdown(WSExitMode);
196 inside = 0;
200 static void
201 shutdownCommand(WMenu *menu, WMenuEntry *entry)
203 static int inside = 0;
204 int result;
206 /* prevent reentrant calls */
207 if (inside)
208 return;
209 inside = 1;
211 #define R_CANCEL 0
212 #define R_CLOSE 1
213 #define R_KILL 2
216 result = R_CANCEL;
217 if ((long)entry->clientdata==M_QUICK)
218 result = R_CLOSE;
219 else {
220 #ifdef XSMP_ENABLED
221 if (wSessionIsManaged()) {
222 int r;
224 r = wMessageDialog(menu->frame->screen_ptr,
225 _("Close X session"),
226 _("Close Window System session?\n"
227 "Kill might close applications with unsaved data."),
228 _("Close"), _("Kill"), _("Cancel"));
229 if (r==WAPRDefault)
230 result = R_CLOSE;
231 else if (r==WAPRAlternate)
232 result = R_KILL;
233 } else
234 #endif
236 int r;
238 r = wMessageDialog(menu->frame->screen_ptr,
239 _("Kill X session"),
240 _("Kill Window System session?\n"
241 "(all applications will be closed)"),
242 _("Kill"), _("Cancel"), NULL);
243 if (r==WAPRDefault)
244 result = R_KILL;
248 if (result!=R_CANCEL) {
249 #ifdef XSMP_ENABLED
250 if (result == R_CLOSE) {
251 Shutdown(WSLogoutMode);
252 } else
253 #endif /* XSMP_ENABLED */
255 Shutdown(WSKillMode);
258 #undef R_CLOSE
259 #undef R_CANCEL
260 #undef R_KILL
261 inside = 0;
265 static void
266 restartCommand(WMenu *menu, WMenuEntry *entry)
268 Shutdown(WSRestartPreparationMode);
269 Restart((char*)entry->clientdata, False);
270 Restart(NULL, True);
274 static void
275 refreshCommand(WMenu *menu, WMenuEntry *entry)
277 wRefreshDesktop(menu->frame->screen_ptr);
281 static void
282 arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
284 wArrangeIcons(menu->frame->screen_ptr, True);
287 static void
288 showAllCommand(WMenu *menu, WMenuEntry *entry)
290 wShowAllWindows(menu->frame->screen_ptr);
293 static void
294 hideOthersCommand(WMenu *menu, WMenuEntry *entry)
296 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
300 static void
301 saveSessionCommand(WMenu *menu, WMenuEntry *entry)
303 if (!wPreferences.save_session_on_exit)
304 wSessionSaveState(menu->frame->screen_ptr);
306 wScreenSaveState(menu->frame->screen_ptr);
310 static void
311 clearSessionCommand(WMenu *menu, WMenuEntry *entry)
313 wSessionClearState(menu->frame->screen_ptr);
317 static void
318 infoPanelCommand(WMenu *menu, WMenuEntry *entry)
320 wShowInfoPanel(menu->frame->screen_ptr);
324 static void
325 legalPanelCommand(WMenu *menu, WMenuEntry *entry)
327 wShowLegalPanel(menu->frame->screen_ptr);
331 /********************************************************************/
334 static char*
335 getLocalizedMenuFile(char *menu)
337 char *buffer;
338 char *ptr;
340 if (!Locale)
341 return NULL;
343 buffer = wmalloc(strlen(menu)+32);
345 /* try menu.locale_name */
346 sprintf(buffer, "%s.%s", menu, Locale);
347 if (access(buffer, F_OK)==0) {
348 return buffer;
350 /* check if it is in the form aa_bb.encoding and check for aa_bb */
351 ptr = strchr(Locale, '.');
352 if (ptr) {
353 *ptr = 0;
354 if (access(buffer, F_OK)==0) {
355 return buffer;
358 /* now check for aa */
359 ptr = strchr(buffer, '_');
360 if (ptr) {
361 *ptr = 0;
362 if (access(buffer, F_OK)==0) {
363 return buffer;
367 return NULL;
371 static void
372 raiseMenus(WMenu *menu)
374 int i;
376 if (menu->flags.mapped) {
377 wRaiseFrame(menu->frame->core);
379 for (i=0; i<menu->cascade_no; i++) {
380 if (menu->cascades[i])
381 raiseMenus(menu->cascades[i]);
387 Bool
388 wRootMenuPerformShortcut(XEvent *event)
390 Shortcut *ptr;
391 int modifiers;
392 int done = 0;
394 /* ignore CapsLock */
395 modifiers = event->xkey.state & ValidModMask;
397 for (ptr = shortcutList; ptr!=NULL; ptr = ptr->next) {
398 if (ptr->keycode==0)
399 continue;
401 if (ptr->keycode==event->xkey.keycode && (ptr->modifier==modifiers)) {
402 (*ptr->entry->callback)(ptr->menu, ptr->entry);
403 done = True;
406 return done;
410 void
411 wRootMenuBindShortcuts(Window window)
413 Shortcut *ptr;
415 ptr = shortcutList;
416 while (ptr) {
417 if (ptr->modifier!=AnyModifier) {
418 XGrabKey(dpy, ptr->keycode, ptr->modifier|LockMask,
419 window, True, GrabModeAsync, GrabModeAsync);
420 #ifdef NUMLOCK_HACK
421 wHackedGrabKey(ptr->keycode, ptr->modifier,
422 window, True, GrabModeAsync, GrabModeAsync);
423 #endif
425 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True,
426 GrabModeAsync, GrabModeAsync);
427 ptr = ptr->next;
432 static void
433 rebindKeygrabs(WScreen *scr)
435 WWindow *wwin;
437 wwin = scr->focused_window;
439 while (wwin!=NULL) {
440 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
442 if (!WFLAGP(wwin, no_bind_keys)) {
443 wWindowSetKeyGrabs(wwin);
445 wwin = wwin->prev;
450 static void
451 removeShortcutsForMenu(WMenu *menu)
453 Shortcut *ptr, *tmp;
454 Shortcut *newList = NULL;
456 ptr = shortcutList;
457 while (ptr!=NULL) {
458 tmp = ptr->next;
459 if (ptr->menu == menu) {
460 free(ptr);
461 } else {
462 ptr->next = newList;
463 newList = ptr;
465 ptr = tmp;
467 shortcutList = newList;
468 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
472 static Bool
473 addShortcut(char *file, char *shortcutDefinition, WMenu *menu,
474 WMenuEntry *entry)
476 Shortcut *ptr;
477 KeySym ksym;
478 char *k;
479 char buf[128], *b;
481 ptr = wmalloc(sizeof(Shortcut));
483 strcpy(buf, shortcutDefinition);
484 b = (char*)buf;
486 /* get modifiers */
487 ptr->modifier = 0;
488 while ((k = strchr(b, '+'))!=NULL) {
489 int mod;
491 *k = 0;
492 mod = wXModifierFromKey(b);
493 if (mod<0) {
494 wwarning(_("%s:invalid key modifier \"%s\""), file, b);
495 free(ptr);
496 return False;
498 ptr->modifier |= mod;
500 b = k+1;
503 /* get key */
504 ksym = XStringToKeysym(b);
506 if (ksym==NoSymbol) {
507 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
508 file, shortcutDefinition, entry->text);
509 free(ptr);
510 return False;
513 ptr->keycode = XKeysymToKeycode(dpy, ksym);
514 if (ptr->keycode==0) {
515 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
516 shortcutDefinition, entry->text);
517 free(ptr);
518 return False;
521 ptr->menu = menu;
522 ptr->entry = entry;
524 ptr->next = shortcutList;
525 shortcutList = ptr;
527 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
529 return True;
533 /*******************************/
535 static char*
536 cropline(char *line)
538 char *end;
540 if (strlen(line)==0)
541 return line;
543 end = &(line[strlen(line)])-1;
544 while (isspace(*line) && *line!=0) line++;
545 while (end>line && isspace(*end)) {
546 *end=0;
547 end--;
549 return line;
553 static char*
554 next_token(char *line, char **next)
556 char *tmp, c;
557 char *ret;
559 *next = NULL;
560 while (*line==' ' || *line=='\t') line++;
562 tmp = line;
564 if (*tmp=='"') {
565 tmp++; line++;
566 while (*tmp!=0 && *tmp!='"') tmp++;
567 if (*tmp!='"') {
568 wwarning(_("%s: unmatched '\"' in menu file"), line);
569 return NULL;
571 } else {
572 do {
573 if (*tmp=='\\')
574 tmp++;
576 if (*tmp!=0)
577 tmp++;
579 } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
582 c = *tmp;
583 *tmp = 0;
584 ret = wstrdup(line);
585 *tmp = c;
587 if (c==0)
588 return ret;
589 else
590 tmp++;
592 /* skip blanks */
593 while (*tmp==' ' || *tmp=='\t') tmp++;
595 if (*tmp!=0)
596 *next = tmp;
598 return ret;
602 static void
603 separateCommand(char *line, char ***file, char **command)
605 char *token, *tmp = line;
606 LinkedList *list = NULL;
607 int count, i;
609 *file = NULL;
610 *command = NULL;
611 do {
612 token = next_token(tmp, &tmp);
613 if (token) {
614 if (strcmp(token, "WITH")==0) {
615 if (tmp!=NULL && *tmp!=0)
616 *command = wstrdup(tmp);
617 else
618 wwarning(_("%s: missing command"), line);
619 break;
621 list = list_cons(token, list);
623 } while (token!=NULL && tmp!=NULL);
625 count = list_length(list);
626 if (count>0) {
627 *file = wmalloc(sizeof(char*)*(count+1));
628 i = count;
629 (*file)[count] = NULL;
630 while (list!=NULL) {
631 (*file)[--i] = list->head;
632 list_remove_head(&list);
638 static void
639 constructMenu(WMenu *menu, WMenuEntry *entry)
641 WMenu *submenu;
642 struct stat stat_buf;
643 char **path;
644 char *cmd;
645 char *lpath = NULL;
646 int i, first=-1;
647 time_t last=0;
649 separateCommand((char*)entry->clientdata, &path, &cmd);
650 if (!path || *path==NULL || **path==0) {
651 wwarning(_("invalid OPEN_MENU specification: %s"),
652 (char*)entry->clientdata);
653 return;
656 if (path[0][0]=='|') {
657 /* pipe menu */
659 if (!menu->cascades[entry->cascade] ||
660 menu->cascades[entry->cascade]->timestamp == 0) {
661 /* parse pipe */
663 submenu = readMenuPipe(menu->frame->screen_ptr, path);
665 /* there's no automatic reloading */
666 if(submenu != NULL)
667 submenu->timestamp = 1;
668 } else {
669 submenu = NULL;
672 } else {
673 i=0;
674 while(path[i] != NULL) {
675 char *tmp;
677 if (strcmp(path[i], "-noext")==0) {
678 i++;
679 continue;
682 tmp = wexpandpath(path[i]);
683 free(path[i]);
684 lpath = getLocalizedMenuFile(tmp);
685 if (lpath) {
686 free(tmp);
687 path[i] = lpath;
688 lpath = NULL;
689 } else {
690 path[i] = tmp;
693 if (stat(path[i], &stat_buf)==0) {
694 if (last < stat_buf.st_mtime)
695 last = stat_buf.st_mtime;
696 if (first<0)
697 first=i;
698 } else {
699 wsyserror(_("%s:could not stat menu"), path[i]);
700 /*goto finish;*/
703 i++;
706 if (first < 0) {
707 wsyserror(_("%s:could not stat menu:%s"), "OPEN_MENU",
708 (char*)entry->clientdata);
709 goto finish;
711 stat(path[first], &stat_buf);
712 if (!menu->cascades[entry->cascade]
713 || menu->cascades[entry->cascade]->timestamp < last) {
715 if (S_ISDIR(stat_buf.st_mode)) {
716 /* menu directory */
717 submenu = readMenuDirectory(menu->frame->screen_ptr,
718 entry->text, path, cmd);
719 if (submenu)
720 submenu->timestamp = last;
721 } else if (S_ISREG(stat_buf.st_mode)) {
722 /* menu file */
724 if (cmd || path[1])
725 wwarning(_("too many parameters in OPEN_MENU: %s"),
726 (char*)entry->clientdata);
728 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
729 if (submenu)
730 submenu->timestamp = stat_buf.st_mtime;
731 } else {
732 submenu = NULL;
734 } else {
735 submenu = NULL;
739 if (submenu) {
740 wMenuEntryRemoveCascade(menu, entry);
741 wMenuEntrySetCascade(menu, entry, submenu);
744 finish:
745 i = 0;
746 while (path[i]!=NULL)
747 free(path[i++]);
748 free(path);
749 if (cmd)
750 free(cmd);
754 static WMenuEntry*
755 addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
757 WMenu *wsmenu;
758 WMenuEntry *entry;
760 if (scr->flags.added_workspace_menu) {
761 wwarning(_("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
762 return NULL;
763 } else {
764 scr->flags.added_workspace_menu = 1;
766 wsmenu = wWorkspaceMenuMake(scr, True);
767 scr->workspace_menu = wsmenu;
768 entry = wMenuAddCallback(menu, title, NULL, NULL);
769 wMenuEntrySetCascade(menu, entry, wsmenu);
771 wWorkspaceMenuUpdate(scr, wsmenu);
773 return entry;
777 static WMenuEntry*
778 addMenuEntry(WMenu *menu, char *title, char *shortcut, char *command,
779 char *params, char *file_name)
781 WScreen *scr;
782 WMenuEntry *entry = NULL;
783 Bool shortcutOk = False;
785 if (!menu)
786 return NULL;
787 scr = menu->frame->screen_ptr;
788 if (strcmp(command, "OPEN_MENU")==0) {
789 if (!params) {
790 wwarning(_("%s:missing parameter for menu command \"%s\""),
791 file_name, command);
792 } else {
793 WMenu *dummy;
794 char *path;
796 path = wfindfile(DEF_CONFIG_PATHS, params);
797 if (!path) {
798 path = wstrdup(params);
800 dummy = wMenuCreate(scr, title, False);
801 dummy->on_destroy = removeShortcutsForMenu;
802 entry = wMenuAddCallback(menu, title, constructMenu, path);
803 entry->free_cdata = free;
804 wMenuEntrySetCascade(menu, entry, dummy);
806 } else if (strcmp(command, "EXEC")==0) {
807 if (!params)
808 wwarning(_("%s:missing parameter for menu command \"%s\""),
809 file_name, command);
810 else {
811 entry = wMenuAddCallback(menu, title, execCommand,
812 wstrappend("exec ", params));
813 entry->free_cdata = free;
814 shortcutOk = True;
816 } else if (strcmp(command, "SHEXEC")==0) {
817 if (!params)
818 wwarning(_("%s:missing parameter for menu command \"%s\""),
819 file_name, command);
820 else {
821 entry = wMenuAddCallback(menu, title, execCommand,
822 wstrdup(params));
823 entry->free_cdata = free;
824 shortcutOk = True;
826 } else if (strcmp(command, "EXIT")==0) {
828 if (params && strcmp(params, "QUICK")==0)
829 entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
830 else
831 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
833 shortcutOk = True;
834 } else if (strcmp(command, "SHUTDOWN")==0) {
836 if (params && strcmp(params, "QUICK")==0)
837 entry = wMenuAddCallback(menu, title, shutdownCommand,
838 (void*)M_QUICK);
839 else
840 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
842 shortcutOk = True;
843 } else if (strcmp(command, "REFRESH")==0) {
844 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
846 shortcutOk = True;
847 } else if (strcmp(command, "WORKSPACE_MENU")==0) {
848 entry = addWorkspaceMenu(scr, menu, title);
850 shortcutOk = True;
851 } else if (strcmp(command, "ARRANGE_ICONS")==0) {
852 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
854 shortcutOk = True;
855 } else if (strcmp(command, "HIDE_OTHERS")==0) {
856 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
858 shortcutOk = True;
859 } else if (strcmp(command, "SHOW_ALL")==0) {
860 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
862 shortcutOk = True;
863 } else if (strcmp(command, "RESTART")==0) {
864 entry = wMenuAddCallback(menu, title, restartCommand,
865 params ? wstrdup(params) : NULL);
866 entry->free_cdata = free;
867 shortcutOk = True;
868 } else if (strcmp(command, "SAVE_SESSION")==0) {
869 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
871 shortcutOk = True;
872 } else if (strcmp(command, "CLEAR_SESSION")==0) {
873 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
874 shortcutOk = True;
875 } else if (strcmp(command, "INFO_PANEL")==0) {
876 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
877 shortcutOk = True;
878 } else if (strcmp(command, "LEGAL_PANEL")==0) {
879 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
880 shortcutOk = True;
881 } else {
882 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name,
883 command);
885 return NULL;
888 if (shortcut && entry) {
889 if (!shortcutOk) {
890 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name,
891 title);
892 } else {
893 if (addShortcut(file_name, shortcut, menu, entry)) {
895 entry->rtext = GetShortcutString(shortcut);
897 entry->rtext = wstrdup(shortcut);
903 return entry;
908 /******************* Menu Configuration From File *******************/
910 static void
911 separateline(char *line, char *title, char *command, char *parameter,
912 char *shortcut)
914 int l, i;
916 l = strlen(line);
918 *title = 0;
919 *command = 0;
920 *parameter = 0;
921 *shortcut = 0;
922 /* get the title */
923 while (isspace(*line) && (*line!=0)) line++;
924 if (*line=='"') {
925 line++;
926 i=0;
927 while (line[i]!='"' && (line[i]!=0)) i++;
928 if (line[i]!='"')
929 return;
930 } else {
931 i=0;
932 while (!isspace(line[i]) && (line[i]!=0)) i++;
934 strncpy(title, line, i);
935 title[i++]=0;
936 line+=i;
938 /* get the command or shortcut keyword */
939 while (isspace(*line) && (*line!=0)) line++;
940 if (*line==0)
941 return;
942 i=0;
943 while (!isspace(line[i]) && (line[i]!=0)) i++;
944 strncpy(command, line, i);
945 command[i++]=0;
946 line+=i;
948 if (strcmp(command, "SHORTCUT")==0) {
949 /* get the shortcut key */
950 while (isspace(*line) && (*line!=0)) line++;
951 if (*line=='"') {
952 line++;
953 i=0;
954 while (line[i]!='"' && (line[i]!=0)) i++;
955 if (line[i]!='"')
956 return;
957 } else {
958 i=0;
959 while (!isspace(line[i]) && (line[i]!=0)) i++;
961 strncpy(shortcut, line, i);
962 shortcut[i++]=0;
963 line+=i;
965 *command=0;
967 /* get the command */
968 while (isspace(*line) && (*line!=0)) line++;
969 if (*line==0)
970 return;
971 i=0;
972 while (!isspace(line[i]) && (line[i]!=0)) i++;
973 strncpy(command, line, i);
974 command[i++]=0;
975 line+=i;
978 /* get the parameters */
979 while (isspace(*line) && (*line!=0)) line++;
980 if (*line==0)
981 return;
983 if (*line=='"') {
984 line++;
985 l = 0;
986 while (line[l]!=0 && line[l]!='"') {
987 parameter[l] = line[l];
988 l++;
990 parameter[l] = 0;
991 return;
994 l = strlen(line);
995 while (isspace(line[l]) && (l>0)) l--;
996 strncpy(parameter, line, l);
997 parameter[l]=0;
1001 static WMenu*
1002 parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
1004 char linebuf[MAXLINE];
1005 char elinebuf[MAXLINE];
1006 char title[MAXLINE];
1007 char command[MAXLINE];
1008 char shortcut[MAXLINE];
1009 char params[MAXLINE];
1010 char *line;
1012 while (!IsEof(file)) {
1013 int lsize, ok;
1015 ok = 0;
1016 fgets(linebuf, MAXLINE, file);
1017 line = cropline(linebuf);
1018 lsize = strlen(line);
1019 do {
1020 if (line[lsize-1]=='\\') {
1021 char *line2;
1022 int lsize2;
1023 fgets(elinebuf, MAXLINE, file);
1024 line2=cropline(elinebuf);
1025 lsize2=strlen(line2);
1026 if (lsize2+lsize>MAXLINE) {
1027 wwarning(_("%s:maximal line size exceeded in menu config: %s"),
1028 file_name, line);
1029 ok=2;
1030 } else {
1031 line[lsize-1]=0;
1032 lsize+=lsize2-1;
1033 strcat(line, line2);
1035 } else {
1036 ok=1;
1038 } while (!ok && !IsEof(file));
1039 if (ok==2)
1040 continue;
1042 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1043 continue;
1046 separateline(line, title, command, params, shortcut);
1048 if (!command[0]) {
1049 wwarning(_("%s:missing command in menu config: %s"), file_name,
1050 line);
1051 goto error;
1054 if (strcasecmp(command, "MENU")==0) {
1055 WMenu *cascade;
1057 /* start submenu */
1059 cascade = wMenuCreate(scr, title, False);
1060 cascade->on_destroy = removeShortcutsForMenu;
1061 if (parseCascade(scr, cascade, file, file_name)==NULL) {
1062 wMenuDestroy(cascade, True);
1063 } else {
1064 wMenuEntrySetCascade(menu,
1065 wMenuAddCallback(menu, title, NULL, NULL),
1066 cascade);
1068 } else if (strcasecmp(command, "END")==0) {
1069 /* end of menu */
1070 return menu;
1072 } else {
1073 /* normal items */
1074 addMenuEntry(menu, title, shortcut[0] ? shortcut : NULL, command,
1075 params[0] ? params : NULL, file_name);
1079 wwarning(_("%s:syntax error in menu file:END declaration missing"),
1080 file_name);
1081 return menu;
1083 error:
1084 return menu;
1088 static WMenu*
1089 readMenuFile(WScreen *scr, char *file_name)
1091 WMenu *menu=NULL;
1092 FILE *file = NULL;
1093 char linebuf[MAXLINE];
1094 char title[MAXLINE];
1095 char shortcut[MAXLINE];
1096 char command[MAXLINE];
1097 char params[MAXLINE];
1098 char *line;
1099 #ifdef USECPP
1100 char *args;
1101 int cpp = 0;
1102 #endif
1104 #ifdef USECPP
1105 if (!wPreferences.flags.nocpp) {
1106 args = MakeCPPArgs(file_name);
1107 if (!args) {
1108 wwarning(_("could not make arguments for menu file preprocessor"));
1109 } else {
1110 sprintf(command, "%s %s %s", CPP_PATH, args, file_name);
1111 free(args);
1112 file = popen(command, "r");
1113 if (!file) {
1114 wsyserror(_("%s:could not open/preprocess menu file"),
1115 file_name);
1116 } else {
1117 cpp = 1;
1121 #endif /* USECPP */
1123 if (!file) {
1124 file = fopen(file_name, "r");
1125 if (!file) {
1126 wsyserror(_("%s:could not open menu file"), file_name);
1127 return NULL;
1131 while (!IsEof(file)) {
1132 if (!fgets(linebuf, MAXLINE, file))
1133 break;
1134 line = cropline(linebuf);
1135 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1136 continue;
1138 separateline(line, title, command, params, shortcut);
1140 if (!command[0]) {
1141 wwarning(_("%s:missing command in menu config: %s"), file_name,
1142 line);
1143 break;
1145 if (strcasecmp(command, "MENU")==0) {
1146 menu = wMenuCreate(scr, title, True);
1147 menu->on_destroy = removeShortcutsForMenu;
1148 if (!parseCascade(scr, menu, file, file_name)) {
1149 wMenuDestroy(menu, True);
1151 break;
1152 } else {
1153 wwarning(_("%s:invalid menu file. MENU command is missing"),
1154 file_name);
1155 break;
1159 #ifdef CPP
1160 if (cpp) {
1161 if (pclose(file)==-1) {
1162 wsyserror(_("error reading preprocessed menu data"));
1164 } else {
1165 fclose(file);
1167 #else
1168 fclose(file);
1169 #endif
1171 return menu;
1174 /************ Menu Configuration From Pipe *************/
1176 static WMenu*
1177 readMenuPipe(WScreen *scr, char **file_name)
1179 WMenu *menu=NULL;
1180 FILE *file = NULL;
1181 char linebuf[MAXLINE];
1182 char title[MAXLINE];
1183 char command[MAXLINE];
1184 char params[MAXLINE];
1185 char shortcut[MAXLINE];
1186 char *line;
1187 char * filename;
1188 char flat_file[MAXLINE];
1189 int i;
1190 #ifdef USECPP
1191 char *args;
1192 int cpp = 0;
1193 #endif
1195 flat_file[0] = '\0';
1197 for(i = 0 ; file_name[i] != NULL ; i++) {
1198 strcat(flat_file, file_name[i]);
1199 strcat(flat_file, " ");
1201 filename = flat_file+1;
1203 #ifdef USECPP
1204 if (!wPreferences.flags.nocpp) {
1205 args = MakeCPPArgs(filename);
1206 if (!args) {
1207 wwarning(_("could not make arguments for menu file preprocessor"));
1208 } else {
1209 sprintf(command, "%s | %s %s", filename, CPP_PATH, args);
1211 free(args);
1212 file = popen(command, "r");
1213 if (!file) {
1214 wsyserror(_("%s:could not open/preprocess menu file"), filename);
1215 } else {
1216 cpp = 1;
1221 #endif /* USECPP */
1223 if (!file) {
1224 file = popen(filename, "r");
1226 if (!file) {
1227 wsyserror(_("%s:could not open menu file"), filename);
1228 return NULL;
1232 while (!IsEof(file)) {
1233 if (!fgets(linebuf, MAXLINE, file))
1234 break;
1235 line = cropline(linebuf);
1236 if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/'))
1237 continue;
1239 separateline(line, title, command, params, shortcut);
1241 if (!command[0]) {
1242 wwarning(_("%s:missing command in menu config: %s"), file_name,
1243 line);
1244 break;
1246 if (strcasecmp(command, "MENU")==0) {
1247 menu = wMenuCreate(scr, title, True);
1248 menu->on_destroy = removeShortcutsForMenu;
1249 if (!parseCascade(scr, menu, file, filename)) {
1250 wMenuDestroy(menu, True);
1252 break;
1253 } else {
1254 wwarning(_("%s:no title given for the root menu"), filename);
1255 break;
1259 pclose(file);
1261 return menu;
1266 typedef struct {
1267 char *name;
1268 int index;
1269 } dir_data;
1272 static int
1273 myCompare(dir_data *d1, dir_data *d2)
1275 return strcmp(d1->name, d2->name);
1279 /************ Menu Configuration From Directory *************/
1281 static Bool
1282 isFilePackage(char *file)
1284 int l;
1286 /* check if the extension indicates this file is a
1287 * file package. For now, only recognize .themed */
1289 l = strlen(file);
1291 if (l > 7 && strcmp(&(file[l-7]), ".themed")==0) {
1292 return True;
1293 } else {
1294 return False;
1299 static WMenu*
1300 readMenuDirectory(WScreen *scr, char *title, char **path, char *command)
1302 DIR *dir;
1303 struct dirent *dentry;
1304 struct stat stat_buf;
1305 WMenu *menu=NULL;
1306 char *buffer;
1307 LinkedList *dirs = NULL, *files = NULL;
1308 int length, i, have_space=0;
1309 dir_data *data;
1310 int stripExtension = 0;
1312 i=0;
1313 while (path[i]!=NULL) {
1314 if (strcmp(path[i], "-noext")==0) {
1315 stripExtension = 1;
1316 i++;
1317 continue;
1320 dir = opendir(path[i]);
1321 if (!dir) {
1322 i++;
1323 continue;
1326 while ((dentry = readdir(dir))) {
1328 if (strcmp(dentry->d_name, ".")==0 ||
1329 strcmp(dentry->d_name, "..")==0)
1330 continue;
1332 if (dentry->d_name[0] == '.')
1333 continue;
1335 buffer = wmalloc(strlen(path[i])+strlen(dentry->d_name)+4);
1336 if (!buffer) {
1337 wsyserror(_("out of memory while constructing directory menu %s"),
1338 path[i]);
1339 break;
1342 strcpy(buffer, path[i]);
1343 strcat(buffer, "/");
1344 strcat(buffer, dentry->d_name);
1346 if (stat(buffer, &stat_buf)!=0) {
1347 wsyserror(_("%s:could not stat file \"%s\" in menu directory"),
1348 path[i], dentry->d_name);
1349 } else {
1350 Bool isFilePack = False;
1352 data = NULL;
1353 if (S_ISDIR(stat_buf.st_mode)
1354 && !(isFilePack = isFilePackage(dentry->d_name))) {
1356 /* access always returns success for user root */
1357 if (access(buffer, X_OK)==0) {
1358 /* Directory is accesible. Add to directory list */
1360 data = (dir_data*) wmalloc(sizeof(dir_data));
1361 data->name = wstrdup(dentry->d_name);
1362 data->index = i;
1364 list_insert_sorted(data, &dirs, (int(*)())myCompare);
1366 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1367 /* Hack because access always returns X_OK success for user root */
1368 #define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
1369 if ((command!=NULL && access(buffer, R_OK)==0) ||
1370 (command==NULL && access(buffer, X_OK)==0 &&
1371 (stat_buf.st_mode & S_IXANY))) {
1373 data = (dir_data*) wmalloc(sizeof(dir_data));
1374 data->name = wstrdup(dentry->d_name);
1375 data->index = i;
1377 list_insert_sorted(data, &files, (int(*)())myCompare);
1381 free(buffer);
1384 closedir(dir);
1385 i++;
1388 if (!dirs && !files)
1389 return NULL;
1391 menu = wMenuCreate(scr, title, False);
1392 menu->on_destroy = removeShortcutsForMenu;
1394 while (dirs != NULL) {
1395 /* New directory. Use same OPEN_MENU command that was used
1396 * for the current directory. */
1397 dir_data *d = (dir_data*)dirs->head;
1399 length = strlen(path[d->index])+strlen(d->name)+6;
1400 if (command)
1401 length += strlen(command) + 6;
1402 buffer = wmalloc(length);
1403 if (!buffer) {
1404 wsyserror(_("out of memory while constructing directory menu %s"),
1405 path[d->index]);
1406 break;
1409 have_space = strchr(path[d->index], ' ')!=NULL ||
1410 strchr(d->name, ' ')!=NULL;
1411 if (have_space) {
1412 buffer[0] = '"';
1413 buffer[1] = 0;
1414 strcat(buffer, path[d->index]);
1415 } else {
1416 strcpy(buffer, path[d->index]);
1418 strcat(buffer, "/");
1419 strcat(buffer, d->name);
1420 if (have_space)
1421 strcat(buffer, "\"");
1422 if (command) {
1423 strcat(buffer, " WITH ");
1424 strcat(buffer, command);
1427 addMenuEntry(menu, d->name, NULL, "OPEN_MENU", buffer, path[d->index]);
1429 free(buffer);
1430 if (dirs->head) {
1431 if (d->name)
1432 free(d->name);
1433 free(dirs->head);
1435 list_remove_head(&dirs);
1438 while (files != NULL) {
1439 /* executable: add as entry */
1440 dir_data *f = (dir_data*) files->head;;
1442 length = strlen(path[f->index])+strlen(f->name)+6;
1443 if (command)
1444 length += strlen(command);
1446 buffer = wmalloc(length);
1447 if (!buffer) {
1448 wsyserror(_("out of memory while constructing directory menu %s"),
1449 path[f->index]);
1450 break;
1453 have_space = strchr(path[f->index], ' ')!=NULL ||
1454 strchr(f->name, ' ')!=NULL;
1455 if (command!=NULL) {
1456 strcpy(buffer, command);
1457 strcat(buffer, " ");
1458 if (have_space)
1459 strcat(buffer, "\"");
1460 strcat(buffer, path[f->index]);
1461 } else {
1462 if (have_space) {
1463 buffer[0] = '"';
1464 buffer[1] = 0;
1465 strcat(buffer, path[f->index]);
1466 } else {
1467 strcpy(buffer, path[f->index]);
1470 strcat(buffer, "/");
1471 strcat(buffer, f->name);
1472 if (have_space)
1473 strcat(buffer, "\"");
1475 if (stripExtension) {
1476 char *ptr = strrchr(f->name, '.');
1477 if (ptr && ptr!=f->name)
1478 *ptr = 0;
1480 addMenuEntry(menu, f->name, NULL, "EXEC", buffer, path[f->index]);
1482 free(buffer);
1483 if (files->head) {
1484 if (f->name)
1485 free(f->name);
1486 free(files->head);
1488 list_remove_head(&files);
1491 return menu;
1495 /************ Menu Configuration From WMRootMenu *************/
1497 static WMenu*
1498 makeDefaultMenu(WScreen *scr)
1500 WMenu *menu=NULL;
1502 menu = wMenuCreate(scr, _("Commands"), True);
1503 wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
1504 wMenuAddCallback(menu, "rxvt", execCommand, "rxvt");
1505 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1506 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1507 return menu;
1515 *----------------------------------------------------------------------
1516 * configureMenu--
1517 * Reads root menu configuration from defaults database.
1519 *----------------------------------------------------------------------
1521 static WMenu*
1522 configureMenu(WScreen *scr, proplist_t definition)
1524 WMenu *menu = NULL;
1525 proplist_t elem;
1526 int i, count;
1527 proplist_t title, command, params;
1528 char *tmp, *mtitle;
1531 if (PLIsString(definition)) {
1532 struct stat stat_buf;
1533 char *path = NULL;
1534 Bool menu_is_default = False;
1536 /* menu definition is a string. Probably a path, so parse the file */
1538 tmp = wexpandpath(PLGetString(definition));
1540 path = getLocalizedMenuFile(tmp);
1542 if (!path)
1543 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1545 if (!path) {
1546 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1547 menu_is_default = True;
1550 if (!path) {
1551 wsyserror(_("could not find menu file \"%s\" referenced in WMRootMenu"),
1552 tmp);
1553 free(tmp);
1554 return NULL;
1557 if (stat(path, &stat_buf)<0) {
1558 wsyserror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
1559 free(path);
1560 free(tmp);
1561 return NULL;
1564 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1565 /* if the pointer in WMRootMenu has changed */
1566 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1568 if (menu_is_default) {
1569 wwarning(_("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1570 path);
1573 menu = readMenuFile(scr, path);
1574 if (menu)
1575 menu->timestamp = MAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1576 } else {
1577 menu = NULL;
1579 free(path);
1580 free(tmp);
1582 return menu;
1585 count = PLGetNumberOfElements(definition);
1586 if (count==0)
1587 return NULL;
1589 elem = PLGetArrayElement(definition, 0);
1590 if (!PLIsString(elem)) {
1591 tmp = PLGetDescription(elem);
1592 wwarning(_("%s:format error in root menu configuration \"%s\""),
1593 "WMRootMenu", tmp);
1594 free(tmp);
1595 return NULL;
1597 mtitle = PLGetString(elem);
1599 menu = wMenuCreate(scr, mtitle, False);
1600 menu->on_destroy = removeShortcutsForMenu;
1602 #ifdef GLOBAL_SUBMENU_FILE
1604 WMenu *submenu;
1605 WMenuEntry *mentry;
1607 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
1609 if (submenu) {
1610 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1611 wMenuEntrySetCascade(menu, mentry, submenu);
1614 #endif
1616 for (i=1; i<count; i++) {
1617 elem = PLGetArrayElement(definition, i);
1618 #if 0
1619 if (PLIsString(elem)) {
1620 char *file;
1622 file = PLGetString(elem);
1625 #endif
1626 if (!PLIsArray(elem) || PLGetNumberOfElements(elem) < 2)
1627 goto error;
1629 if (PLIsArray(PLGetArrayElement(elem,1))) {
1630 WMenu *submenu;
1631 WMenuEntry *mentry;
1633 /* submenu */
1634 submenu = configureMenu(scr, elem);
1635 if (submenu) {
1636 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL,
1637 NULL);
1638 wMenuEntrySetCascade(menu, mentry, submenu);
1640 } else {
1641 int idx = 0;
1642 char *shortcut;
1643 /* normal entry */
1645 title = PLGetArrayElement(elem, idx++);
1646 shortcut = PLGetArrayElement(elem, idx++);
1647 if (strcmp(PLGetString(shortcut), "SHORTCUT")==0) {
1648 shortcut = PLGetArrayElement(elem, idx++);
1649 command = PLGetArrayElement(elem, idx++);
1650 } else {
1651 command = shortcut;
1652 shortcut = NULL;
1654 params = PLGetArrayElement(elem, idx++);
1656 if (!title || !command)
1657 goto error;
1659 addMenuEntry(menu, PLGetString(title),
1660 shortcut ? PLGetString(shortcut) : NULL,
1661 PLGetString(command),
1662 params ? PLGetString(params) : NULL, "WMRootMenu");
1664 continue;
1666 error:
1667 tmp = PLGetDescription(elem);
1668 wwarning(_("%s:format error in root menu configuration \"%s\""),
1669 "WMRootMenu", tmp);
1670 free(tmp);
1673 return menu;
1684 *----------------------------------------------------------------------
1685 * OpenRootMenu--
1686 * Opens the root menu, parsing the menu configuration from the
1687 * defaults database.
1688 * If the menu is already mapped and is not sticked to the
1689 * root window, it will be unmapped.
1691 * Side effects:
1692 * The menu may be remade.
1694 * Notes:
1695 * Construction of OPEN_MENU entries are delayed to the moment the
1696 * user map's them.
1697 *----------------------------------------------------------------------
1699 void
1700 OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
1702 WMenu *menu=NULL;
1703 proplist_t definition;
1705 static proplist_t domain=NULL;
1707 if (!domain) {
1708 domain = PLMakeString("WMRootMenu");
1712 scr->flags.root_menu_changed_shortcuts = 0;
1713 scr->flags.added_workspace_menu = 0;
1715 if (scr->root_menu && scr->root_menu->flags.mapped) {
1716 menu = scr->root_menu;
1717 if (!menu->flags.buttoned) {
1718 wMenuUnmap(menu);
1719 } else {
1720 wRaiseFrame(menu->frame->core);
1722 if (keyboard)
1723 wMenuMapAt(menu, 0, 0, True);
1724 else
1725 wMenuMapCopyAt(menu, x-menu->frame->core->width/2, y);
1727 return;
1731 definition = WDRootMenu->dictionary;
1734 definition = PLGetDomain(domain);
1736 if (definition) {
1737 if (PLIsArray(definition)) {
1738 if (!scr->root_menu
1739 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1740 menu = configureMenu(scr, definition);
1741 if (menu)
1742 menu->timestamp = WDRootMenu->timestamp;
1744 } else
1745 menu = NULL;
1746 } else {
1747 menu = configureMenu(scr, definition);
1751 if (!menu) {
1752 /* menu hasn't changed or could not be read */
1753 if (!scr->root_menu) {
1754 wMessageDialog(scr, _("Error"),
1755 _("The applications menu could not be loaded."
1756 "Look at the console output for a detailed"
1757 "description of the errors"),
1758 _("OK"), NULL, NULL);
1760 menu = makeDefaultMenu(scr);
1761 scr->root_menu = menu;
1763 menu = scr->root_menu;
1764 } else {
1765 /* new root menu */
1766 if (scr->root_menu)
1767 wMenuDestroy(scr->root_menu, True);
1768 scr->root_menu = menu;
1770 if (menu) {
1771 wMenuMapAt(menu, x-menu->frame->core->width/2, y, keyboard);
1774 if (scr->flags.root_menu_changed_shortcuts)
1775 rebindKeygrabs(scr);
1778 #endif /* !LITE */