/* rootmenu.c- user defined menu
 * 
 *  WindowMaker window manager
 * 
 *  Copyright (c) 1997 Alfredo K. Kojima
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "wconfig.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <dirent.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include "generic/wwmlib.h"
#include "WindowMaker.h"
#include "actions.h"
#include "menu.h"
#include "funcs.h" 
#include "dialog.h"
#include "keybind.h"
#include "stacking.h"
#include "workspace.h"
#include "defaults.h"
#include "framewin.h"
#include <proplist.h>



extern char *Locale;

extern wCursor[WCUR_LAST];

extern Time LastTimestamp;

extern WPreferences wPreferences;


static WMenu *readMenuFile(WScreen *scr, char *file_name);
static WMenu *readMenuDirectory(WScreen *scr, char *title, char *file_name,
				char *command);


/*
 * Syntax:
 * # main menu
 * "Menu Name" MENU
 * 	"Title" EXEC command_to_exec -params
 * 	"Submenu" MENU
 * 		"Title" EXEC command_to_exec -params
 * 	"Submenu" END
 * 	"Workspaces" WORKSPACE_MENU
 * 	"Title" built_in_command
 * 	"Quit" EXIT
 * 	"Quick Quit" EXIT QUICK
 * "Menu Name" END
 * 
 * Built-in commands:
 * 
 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
 * REFRESH - forces the desktop to be repainted
 * EXIT [QUICK] - exit the window manager [without confirmation]
 * EXEC <program> - execute an external program
 * WORKSPACE_MENU - places the workspace submenu
 * ARRANGE_ICONS
 * RESTART [<window manager>] - restarts the window manager
 * SHOW_ALL - unhide all windows on workspace
 * HIDE_OTHERS - hides all windows excep the focused one
 * OPEN_MENU - read menu data from file or director and 
 * 		precede each with a command
 * OPEN_PIPE_MENU - open command and use it's stdout
 */


#define M_QUICK		1

/* menu commands */

static void
execCommand(WMenu *menu, WMenuEntry *entry)
{
    char *cmdline;
#if 0
    char *path, *chr;
#endif
    cmdline = ExpandOptions(menu->frame->screen_ptr, (char*)entry->clientdata);
#if 0
    path = strdup(cmdline);
    chr = strchr(path, ' ');
    if (chr)
	*chr = 0;
    
    if (access(path, X_OK)!=0) {
	path = realloc(path, strlen(cmdline)+128);
	sprintf(path, _("Program \"%s\" not found or cannot be executed."),
		cmdline);
	wMessageDialog(menu->frame->screen_ptr, _("Error"), path, WD_ERROR);
	free(path);
	free(cmdline);
	return;
    }
    
    free(path);
#endif
    XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
		 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT],
		 CurrentTime);
    XSync(dpy, 0);

    if (cmdline) {
	if (fork()==0) {
	    execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
	    exit(-1);
	}
	free(cmdline);
    }
    XUngrabPointer(dpy, CurrentTime);
    XSync(dpy, 0);
}

static void
exitCommand(WMenu *menu, WMenuEntry *entry)
{
    /* prevent reentrant calls */
    wMenuSetEnabled(menu, entry->order, False);
    if ((int)entry->clientdata==M_QUICK
	|| wMessageDialog(menu->frame->screen_ptr, _("Exit"),
		_("Exit window manager?"), WD_EXIT_CONFIRM)==WDB_EXIT) {
#ifdef DEBUG
	printf("Exiting WindowMaker.\n");
#endif

	wScreenSaveState(menu->frame->screen_ptr);
	
	RestoreDesktop(menu->frame->screen_ptr);
	exit(0);
    }
    wMenuSetEnabled(menu, entry->order, True);
}


static void
shutdownCommand(WMenu *menu, WMenuEntry *entry)
{
    /* prevent reentrant calls */
    wMenuSetEnabled(menu, entry->order, False);
    if ((int)entry->clientdata==M_QUICK
	|| wMessageDialog(menu->frame->screen_ptr, _("Close X session"), 
	  _("Close Window System session?\n(all applications will be closed)"),
			  WD_EXIT_CONFIRM)==WDB_EXIT) {
	printf(_("Exiting...\n"));
	
	wScreenSaveState(menu->frame->screen_ptr);

 	WipeDesktop(menu->frame->screen_ptr);
	exit(0);
    }
    wMenuSetEnabled(menu, entry->order, True);
}


static void
restartCommand(WMenu *menu, WMenuEntry *entry)
{
    wScreenSaveState(menu->frame->screen_ptr);

    RestoreDesktop(menu->frame->screen_ptr);
    Restart((char*)entry->clientdata);
}


static void
refreshCommand(WMenu *menu, WMenuEntry *entry)
{
    wRefreshDesktop(menu->frame->screen_ptr);
}


static void
arrangeIconsCommand(WMenu *menu, WMenuEntry *entry)
{
    wArrangeIcons(menu->frame->screen_ptr);
}

static void
showAllCommand(WMenu *menu, WMenuEntry *entry)
{
    wShowAllWindows(menu->frame->screen_ptr);
}

static void
hideOthersCommand(WMenu *menu, WMenuEntry *entry)
{
    wHideOtherApplications(menu->frame->screen_ptr->focused_window);
}


static void
raiseMenus(WMenu *menu)
{
    int i;
    
    if (menu->flags.mapped) {
	wRaiseFrame(menu->frame->core);
    }
    for (i=0; i<menu->cascade_no; i++) {
	if (menu->cascades[i])
	  raiseMenus(menu->cascades[i]);
    }
}



/*******************************/

static char*
cropline(char *line)
{
    char *start, *end;
    
    if (strlen(line)==0)
	return line;
    
    start = line;
    end = &(line[strlen(line)])-1;
    while (isspace(*line) && *line!=0) line++;
    while (isspace(*end) && end!=line) {
	*end=0;
	end--;
    }
    return line;
}



static void
separateCommand(char *line, char **file, char **command)
{
    char *tmp;
    
    
    *file = strdup(line);
    
    tmp = *file;
    
    do {
	if (*tmp=='\\')
	    tmp++;
	
	if (*tmp!=0)
	    tmp++;
	
    } while (*tmp!=0 && *tmp!=' ' && *tmp!='\t');
    if (*tmp==0) {
	*command = NULL;
	return;
    }
    
    *tmp = 0;
    
    /* skip blanks */
    tmp++;
    while (*tmp==' ' || *tmp=='\t')
	tmp++;

    *command = strdup(tmp);
    
    /* check if param has some command */
    tmp = *command;
    while (*tmp!=0 && *tmp==' ' && *tmp=='\t' && *tmp=='\n') tmp++;

    if (*tmp==0) {
	free(*command);
	*command = NULL;
    }
}


static void
constructMenu(WMenu *menu, WMenuEntry *entry)
{
    WMenu *submenu;
    struct stat stat_buf;
    char *path;
    char *cmd;
    char *lpath = NULL;
    
    separateCommand((char*)entry->clientdata, &path, &cmd);
    if (!path || *path==0) {
	wWarning(_("invalid OPEN_MENU specification: %s"),
		 (char*)entry->clientdata);
	return;
    }
    
    if (Locale) {
	lpath = wmalloc(strlen(path)+32);
	
	strcpy(lpath, path);
	strcat(lpath, ".");
	strcat(lpath, Locale);
	if (stat(lpath, &stat_buf)<0) {
	    int i;
	    i = strlen(Locale);
	    if (i>2) {
		lpath[strlen(lpath)-(i-2)]=0;
		if (stat(lpath, &stat_buf)==0) {
		    path = lpath;
		}
	    }
	} else {
	    path = lpath;
	}
    }
    
    if (stat(path, &stat_buf)<0) {
	wSysError(_("%s:could not stat menu"), path);
	free(path);
	if (cmd)
	    free(cmd);
	if (lpath)
	    free(lpath);
	return;
    }
    
    if (!menu->cascades[entry->cascade]
	|| menu->cascades[entry->cascade]->timestamp < stat_buf.st_mtime) {
	
	if (S_ISDIR(stat_buf.st_mode)) {
	    /* menu directory */
	    submenu = readMenuDirectory(menu->frame->screen_ptr,
					entry->text, path, cmd);
	    if (submenu)
		submenu->timestamp = stat_buf.st_mtime;
	} else if (S_ISREG(stat_buf.st_mode)) {
	    /* menu file */
	    
	    if (cmd)
		wWarning(_("extra parameters to OPEN_MENU: %s"), 
			 (char*)entry->clientdata);
	    
	    submenu = readMenuFile(menu->frame->screen_ptr, path);
	    if (submenu)
		submenu->timestamp = stat_buf.st_mtime;
	} else {
	    submenu = NULL;
	}
    } else {
	submenu = NULL;
    }
    if (submenu) {
	wMenuEntryRemoveCascade(menu, entry);
	wMenuEntrySetCascade(menu, entry, submenu);
    }

    if (lpath)
	free(lpath);
    free(path);
    if (cmd)
	free(cmd);
}


static WMenuEntry*
addWorkspaceMenu(WScreen *scr, WMenu *menu, char *title)
{
    WMenu *wsmenu;
    WMenuEntry *entry;

    wsmenu = wWorkspaceMenuMake(scr);
    scr->workspace_menu = wsmenu;
    entry=wMenuAddCallback(menu, title, NULL, NULL);
    wMenuEntrySetCascade(menu, entry, wsmenu);
      
    wWorkspaceMenuUpdate(scr, wsmenu);
    
    return entry;
}


static WMenuEntry*
addMenuEntry(WMenu *menu, char *title, char *command, 
	     char *params, char *file_name)
{
    WScreen *scr;
    WMenuEntry *entry = NULL;

    if (!menu)
	return NULL;
    scr = menu->frame->screen_ptr;
    if (strcmp(command, "OPEN_MENU")==0) {
	if (!params) {
	    wWarning(_("%s:missing parameter for menu command \"%s\""),
		     file_name, command);
	} else {
	    WMenu *dummy;
	    char *path;

	    path = find_file(DEF_CONFIG_PATHS, params);
	    if (!path) {
		path = strdup(params);
	    }
	    dummy = wMenuCreate(scr, title, False);
	    entry = wMenuAddCallback(menu, title, constructMenu, path);
	    entry->free_cdata = free;
	    wMenuEntrySetCascade(menu, entry, dummy);
	}
    } else if (strcmp(command, "EXEC")==0) {
	if (!params)
	    wWarning(_("%s:missing parameter for menu command \"%s\""),
		     file_name, command);
	else {
	    entry = wMenuAddCallback(menu, title, execCommand, strdup(params));
	    entry->free_cdata = free;
	}
    } else if (strcmp(command, "EXIT")==0) {
	
	if (params && strcmp(params, "QUICK")==0)
	    entry = wMenuAddCallback(menu, title, exitCommand, (void*)M_QUICK);
	else
	    entry = wMenuAddCallback(menu, title, exitCommand, NULL);
	
    } else if (strcmp(command, "SHUTDOWN")==0) {
	    
	if (params && strcmp(params, "QUICK")==0)
	    entry = wMenuAddCallback(menu, title, shutdownCommand, 
				     (void*)M_QUICK);
	else
	    entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
	
    } else if (strcmp(command, "REFRESH")==0) {
	entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
	    
    } else if (strcmp(command, "WORKSPACE_MENU")==0) {
	entry = addWorkspaceMenu(scr, menu, title);
	
    } else if (strcmp(command, "ARRANGE_ICONS")==0) {
	entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
	
    } else if (strcmp(command, "HIDE_OTHERS")==0) {
	entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
	
    } else if (strcmp(command, "SHOW_ALL")==0) {
	entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
	
    } else if (strcmp(command, "RESTART")==0) {
	entry = wMenuAddCallback(menu, title, restartCommand, 
				 params ? strdup(params) : NULL);
	entry->free_cdata = free;
    } else {
	wWarning(_("%s:unknown command \"%s\" in menu config."), file_name,
		 command);
	
	return NULL;
    }

    return entry;
}



/*******************   Menu Configuration From File   *******************/

static int
separateline(char *line, char *title, char *command, char *parameter)
{
    int l, i;
    
    l = strlen(line);

    *title = 0;
    *command = 0;
    *parameter = 0;
    /* get the title */
    while (isspace(*line) && (*line!=0)) line++;
    if (*line=='"') {
	line++;
	i=0;
	while (line[i]!='"' && (line[i]!=0)) i++;
	if (line[i]!='"')
	  return -1;
    } else {
	i=0;
	while (!isspace(line[i]) && (line[i]!=0)) i++;
    }
    strncpy(title, line, i);
    title[i++]=0;
    line+=i;

    /* get the command */
    while (isspace(*line) && (*line!=0)) line++;
    if (*line==0)
      return 1;
    i=0;
    while (!isspace(line[i]) && (line[i]!=0)) i++;
    strncpy(command, line, i);
    command[i++]=0;
    line+=i;

    /* get the parameters */
    while (isspace(*line) && (*line!=0)) line++;
    if (*line==0)
      return 2;
    
    if (*line=='"') {
	line++;
	l = 0;
	while (line[l]!=0 && line[l]!='"') {
	    parameter[l] = line[l];
	    l++;
	}
	parameter[l] = 0;
	return 3;
    }
    
    l = strlen(line);
    while (isspace(line[l]) && (l>0)) l--;
    strncpy(parameter, line, l);
    parameter[l]=0;

    return 3;
}


static WMenu*
parseCascade(WScreen *scr, WMenu *menu, FILE *file, char *file_name)
{
    char linebuf[MAXLINE];
    char elinebuf[MAXLINE];
    char title[MAXLINE];
    char command[MAXLINE];
    char params[MAXLINE];
    char *line;
    
    while (!feof(file)) {
	int lsize, ok, parts;
	
	ok = 0;
	fgets(linebuf, MAXLINE, file);
	line = cropline(linebuf);
	lsize = strlen(line);
	do {
	    if (line[lsize-1]=='\\') {
		char *line2;
		int lsize2;
		fgets(elinebuf, MAXLINE, file);
		line2=cropline(elinebuf);
		lsize2=strlen(line2);
		if (lsize2+lsize>MAXLINE) {
		    wWarning(_("%s:maximal line size exceeded in menu config: %s"),
			     file_name, line);
		    ok=2;
		} else {
		    line[lsize-1]=0;
		    lsize+=lsize2-1;
		    strcat(line, line2);
		}
	    } else {
		ok=1;
	    }
	} while (!ok && !feof(file));
	if (ok==2)
	  continue;
	
	if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/')) 
	  continue;

	
	if ((parts=separateline(line, title, command, params)) < 2) {
	    wWarning(_("%s:missing command in menu config: %s"), file_name,
		     line);
	    goto error;
	}

	if (strcasecmp(command, "MENU")==0) {
	    WMenu *cascade;
	    
	    /* start submenu */

	    cascade = wMenuCreate(scr, title, False);
	    if (parseCascade(scr, cascade, file, file_name)==NULL) {
		wMenuDestroy(cascade, True);
	    } else {
		wMenuEntrySetCascade(menu, 
				     wMenuAddCallback(menu, title, NULL, NULL),
				     cascade);
	    }
	} else if (strcasecmp(command, "END")==0) {
	    /* end of menu */
	    return menu;
	    
	} else {
	    /* normal items */
	    addMenuEntry(menu, title, command, parts > 2 ? params : NULL, 
			 file_name);
	}
    }

    wWarning(_("%s:syntax error in menu file:END declaration missing"),
	     file_name);
    return menu;

    error:
    return menu;
}


static WMenu*
readMenuFile(WScreen *scr, char *file_name)
{
    WMenu *menu=NULL;
    FILE *file = NULL;
    char linebuf[MAXLINE];
    char title[MAXLINE];
    char command[MAXLINE];
    char params[MAXLINE];
    char *line;
#ifdef USECPP
    char *args;
    int cpp = 0;
#endif

#ifdef USECPP
    if (!wPreferences.flags.nocpp) {
	args = MakeCPPArgs(file_name);
	if (!args) {
	    wWarning(_("could not make arguments for menu file preprocessor"));
	} else {
	    sprintf(command, "%s %s %s", CPP_PATH, args, file_name);
	    free(args);
	    file = popen(command, "r");
	    if (!file) {
		wSysError(_("%s:could open/preprocess menu file"), file_name);
	    } else {
		cpp = 1;
	    }
	}
    }
#endif /* USECPP */

    if (!file) {
	file = fopen(file_name, "r");
	if (!file) {
	    wSysError(_("%s:could not open menu file"), file_name);
	    return NULL;
	}
    }
    
    while (!feof(file)) {
	if (!fgets(linebuf, MAXLINE, file))
	    break;
	line = cropline(linebuf);
	if (line[0]==0 || line[0]=='#' || (line[0]=='/' && line[1]=='/')) 
	  continue;

	if (separateline(line, title, command, params) < 2) {
	    wWarning(_("%s:syntax error in menu config: %s"), file_name, line);
	    break;
	}
	if (strcasecmp(command, "MENU")==0) {
	    menu = wMenuCreate(scr, title, True);
	    if (!parseCascade(scr, menu, file, file_name)) {
		wMenuDestroy(menu, True);
	    }
	    break;
	} else {
	    wWarning(_("%s:no title given for the root menu"), file_name);
	    break;
	}
    }
    
#ifdef CPP
    if (cpp) {
	pclose(file):
    } else {
	fclose(file);
    }
#else
    fclose(file);
#endif
    
    return menu;
}


/************  Menu Configuration From Directory   *************/
static WMenu*
readMenuDirectory(WScreen *scr, char *title, char *path, char *command)
{
    DIR *dir;
    struct dirent *dentry;
    struct stat stat_buf;
    WMenu *menu;
    char *buffer, *newbuf;
    
    dir = opendir(path);
    if (!dir) {
	wSysError(_("%s:could not read menu directory"), path);
	return NULL;
    }
    
    menu = wMenuCreate(scr, title, False);
    
    while ((dentry = readdir(dir))) {
	
	if (strcmp(dentry->d_name, ".")==0 || 
	    strcmp(dentry->d_name, "..")==0)
	    continue;

	buffer = malloc(strlen(path)+strlen(dentry->d_name)+4);
	if (!buffer) {
	    wWarning(_("out of memory while constructing directory menu %s"),
		     path);
	    break;
	}
	
	strcpy(buffer, path);
	strcat(buffer, "/");
	strcat(buffer, dentry->d_name);
	
	if (stat(buffer, &stat_buf)!=0) {
	    wSysError(_("%s:could not stat file \"%s\" in menu directory"), 
		      path, dentry->d_name);
	    
	} else {
	    if (S_ISDIR(stat_buf.st_mode)) {
		/* New directory. Use same OPEN_MENU command that was used
		 * for the current directory. */

		if (access(buffer, X_OK)==0) {
		    if (command!=NULL) {			
			newbuf = realloc(buffer, strlen(command)+strlen(buffer)+4);
			if (!newbuf) {
			    wWarning(_("out of memory while constructing directory menu %s"),
				     buffer);
			    free(buffer);
			    continue;
			}
			buffer = newbuf;
			strcat(buffer, " ");
			strcat(buffer, command);
		    }
		    
		    addMenuEntry(menu, dentry->d_name, "OPEN_MENU", buffer, 
				 path);
		}
	    } else if (S_ISREG(stat_buf.st_mode)) {
		/* executable: add as entry */

		if (command!=NULL) {
		    if (access(buffer, R_OK)==0) {
			newbuf = malloc(strlen(command)+strlen(buffer)+4);
			if (!newbuf) {
			    wWarning(_("out of memory while constructing menu entry %s"),
				     buffer);
			    free(buffer);
			    continue;
			}
			strcpy(newbuf, command);
			strcat(newbuf, " ");
			strcat(newbuf, buffer);
			free(buffer);
			buffer = newbuf;
		    } else {
			free(buffer);
			continue;
		    }
		} else {
		    if (access(buffer, X_OK)!=0) {
			free(buffer);
			continue;
		    }
		}

		addMenuEntry(menu, dentry->d_name, "EXEC", buffer, path);
	    }
	    
	}
	
	free(buffer);
    }
    
    closedir(dir);
    
    return menu;
}


/************  Menu Configuration From Defaults DB   *************/

static WMenu*
makeDefaultMenu(WScreen *scr)
{
    WMenu *menu=NULL;
    
    menu = wMenuCreate(scr, _("Commands"), True);
    wMenuAddCallback(menu, "XTerm", execCommand, "xterm");
    wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
    return menu;
}




/*
 *---------------------------------------------------------------------- 
 * configureMenu--
 * 	Reads root menu configuration from defaults database.
 * 
 *---------------------------------------------------------------------- 
 */
static WMenu*
configureMenu(WScreen *scr, proplist_t definition)
{
    WMenu *menu = NULL;
    proplist_t elem, entry;
    int i, count;
    proplist_t title, command, params;
    char *tmp, *mtitle;

    
    if (PLIsString(definition)) {
	struct stat stat_buf;
	char *path;
	
	/* menu definition is a string. Probably a path, so parse the file */
	
	tmp = PLGetString(definition);
	path = find_file(DEF_CONFIG_PATHS, tmp);
	
	if (!path) {
	    wWarning(_("%s:could not find menu file \"%s\""), "defaults DB",
		     tmp);
	    return NULL;
	}
	
	if (stat(path, &stat_buf)<0) {
	    wSysError(_("%s:could not stat menu \"%s\""), "defaults DB", path);
	    free(path);
	    return NULL;
	}

	if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp) {
	    menu = readMenuFile(scr, path);
	    if (menu)
		menu->timestamp = stat_buf.st_mtime;
	} else {
	    menu = NULL;
	}
	free(path);

	return menu;
    }
    
    count = PLGetNumberOfElements(definition);
    if (count==0)
	return NULL;
    
    elem = PLGetArrayElement(definition, 0);
    if (!PLIsString(elem)) {
	tmp = PLGetDescription(elem);
	wWarning(_("%s:format error in root menu configuration \"%s\""), 
		 "defaults DB", tmp);
	free(tmp);
	return NULL;
    }
    
    mtitle = PLGetString(elem);
    
    menu = wMenuCreate(scr, mtitle, False);


    for (i=1; i<count; i++) {
	elem = PLGetArrayElement(definition, i);

	if (!PLIsArray(elem))
	    goto error;

	entry = PLGetArrayElement(elem, 0);
	
	if (PLIsArray(entry)) {
	    WMenu *submenu;
	    WMenuEntry *mentry;
	    
	    /* submenu */
	    submenu = configureMenu(scr, entry);
	    if (submenu) {
		mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, 
					  NULL);
		wMenuEntrySetCascade(menu, mentry, submenu);
	    }
	} else {
	    /* normal entry */
	    
	    title = entry;
	    command = PLGetArrayElement(elem, 1);
	    params = PLGetArrayElement(elem, 2);

	    if (!title || !command)
		goto error;
	    
	    addMenuEntry(menu, PLGetString(title), PLGetString(command),
			 params ? PLGetString(params) : NULL, "defaults DB");
	}
	continue;

      error:
	tmp = PLGetDescription(elem);
	wWarning(_("%s:format error in root menu configuration \"%s\""), 
		 "defaults DB", tmp);
	free(tmp);
    }
    
    return menu;
}








/*
 *---------------------------------------------------------------------- 
 * OpenRootMenu--
 * 	Opens the root menu, parsing the menu configuration from the
 * defaults database.
 *	If the menu is already mapped and is not sticked to the
 * root window, it will be unmapped.
 * 
 * Side effects:
 * 	The menu may be remade.
 * 
 * Notes:
 * Construction of OPEN_MENU entries are delayed to the moment the
 * user map's them.
 *---------------------------------------------------------------------- 
 */
void
OpenRootMenu(WScreen *scr, int x, int y, int keyboard)
{
    WMenu *menu=NULL;
    proplist_t definition;
    /*
    static proplist_t domain=NULL;
    
    if (!domain) {
	domain = PLMakeString("WMRootMenu");
    }
     */

    if (scr->root_menu && scr->root_menu->flags.mapped) {
	menu = scr->root_menu;
	if (!menu->flags.buttoned) {
	    wMenuUnmap(menu);
	} else {
	    wRaiseFrame(menu->frame->core);
	    
	    if (keyboard)
		wMenuMapAt(menu, 0, 0, True);
	    else
		wMenuMapCopyAt(menu, x-menu->frame->core->width/2, 
			       y-menu->frame->top_width/2);
	}
	return;
    }
    
    
    definition = scr->droot_menu->dictionary;

/*
    definition = PLGetDomain(domain);
 */
    if (definition)
	menu = configureMenu(scr, definition);

    if (!menu) {
	/* menu hasn't changed or could not be read */
	if (!scr->root_menu) {
	    menu = makeDefaultMenu(scr);
	    scr->root_menu = menu;
	}
	menu = scr->root_menu;
    } else {
	/* new root menu */
	if (scr->root_menu)
	    wMenuDestroy(scr->root_menu, True);
	scr->root_menu = menu;
    }
    if (menu) {
	wMenuMapAt(menu, x-menu->frame->core->width/2, y-menu->frame->top_width/2,
		   keyboard);
    }
}

