/*
 *  rootmenu.c:		Root menu configuration
 *
 *  Written by:		Ullrich Hafner
 *  
 *  Copyright (C) 1998 Ullrich Hafner <hafner@bigfoot.de>
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 */

/*
 *  $Date: 2000/09/25 18:15:24 $
 *  $Author: hafner $
 *  $Revision: 1.49 $
 *  $State: Exp $
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "proplist_t.h"

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif

/* unistd.h defines _POSIX_VERSION on POSIX.1 systems. */
#if defined(HAVE_DIRENT_H) || defined(_POSIX_VERSION)
#   include <dirent.h>
#   define NLENGTH(dirent) (strlen ((dirent)->d_name))
#else
#   define dirent direct
#   define NLENGTH(dirent) ((dirent)->d_namlen)
#   ifdef HAVE_SYS_NDIR_H
#       include <sys/ndir.h>
#   endif /* HAVE_SYS_NDIR_H */
#   ifdef HAVE_SYS_DIR_H
#       include <sys/dir.h>
#   endif /* HAVE_SYS_DIR_H */
#   ifdef HAVE_NDIR_H
#       include <ndir.h>
#   endif /* HAVE_NDIR_H */
#endif /* not (HAVE_DIRENT_H or _POSIX_VERSION) */

#include "misc.h"
#include "rootmenu.h"
#include "icons.h"
#include "dialog.h"
#include "keys.h"
#include "menu.h"
#include "load.h"
#include "error.h"
#include "dndtree.h"

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

			     local variables
  
*******************************************************************************/

typedef enum {WM_MENU, WM_INCLUDE, WM_PIPE, WM_OPEN_MENU, WM_EXEC, WM_SHEXEC,
	      WM_RESTART, WM_EXIT, WM_SHUTDOWN, WM_SAVE_SESSION, WM_CLEAR_SESSION,
	      WM_WORKSPACE_MENU, WM_REFRESH, WM_ARRANGE_ICONS, WM_HIDE_OTHERS,
	      WM_SHOW_ALL, WM_INFO_PANEL, WM_LEGAL_PANEL, WM_LAST} action_e;

typedef struct
{
   char		*name;
   action_e	action;
   char		*arg;
   char		*shortcut;
} item_t;

static const char *cmd_id [WM_LAST + 1];
static const char *cmd_name [WM_LAST + 1];

static GtkWidget *command_box       = NULL;
static GtkWidget *itemlabel_entry   = NULL;
static guint      itemlabel_entry_sig = 0;

static GtkWidget *box_confirm_button      = NULL;
static GtkWidget *box_commandline_entry   = NULL;
static GtkWidget *box_wmanager_entry      = NULL;
static GtkWidget *box_shortcut_widget     = NULL;
static GtkWidget *box_include_file_widget = NULL;
static GtkWidget *box_pipe_cmd_widget     = NULL;
static GtkWidget *box_dir_widget          = NULL;
static GtkWidget *box_suffix_button       = NULL;

static GtkWidget *menu_tree          = NULL;
static GtkWidget *command_menu       = NULL;
static GtkWidget *confirm_button     = NULL;
static GtkWidget *commandline_entry  = NULL;
static guint      commandline_entry_sig = 0;
static GtkWidget *wmanager_entry     = NULL;
static guint      wmanager_entry_sig = 0;
static GtkWidget *shortcut_widget    = NULL;
static GtkWidget *include_file_entry = NULL;
static guint      include_file_entry_sig = 0;
static GtkWidget *pipe_command_entry = NULL;
static guint      pipe_command_entry_sig = 0;
static GtkWidget *dir_clist          = NULL;
static GtkWidget *dir_remove         = NULL;
static GtkWidget *dir_with_entry     = NULL;
static guint      dir_with_entry_sig = 0;
static GtkWidget *suffix_button      = NULL;

static GtkWidget *notebook_page   = NULL;
static GtkWidget *info_message    = NULL;
static GtkTooltips *main_tooltips = NULL;

static bool_t menu_changed = NO;

static GNode     *last_node = NULL;	/* node cut buffer */
#ifndef WMCONFIG
static bool_t	debian_menu = FALSE;
#endif

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

				prototypes
  
*******************************************************************************/

static GtkCTreeNode *
build_tree (GtkCTreeNode *parent, GtkCTreeNode *sibling, proplist_t menu);
static GtkWidget *
real_rootmenu (GtkTooltips *tooltips, proplist_t menu);
static void
item_destructor (gpointer data);
static item_t *
item_constructor (const char *name, action_e action, const char *arg,
		  const char *shortcut);
static gboolean
itemcopy (GtkCTree *ctree, guint depth, GNode *gnode, GtkCTreeNode *cnode,
	  gpointer data);
static void
selection_made (GtkCTree *ctree, GtkCTreeNode *node, gint column,
		GdkEventButton *event, gpointer data);
static void
set_label (GtkWidget *widget, gpointer ptr);
static void
set_command (GtkWidget *widget, gpointer ptr);
static void
show_action_dialog (item_t *item);
static void
init_action_commands (void) ;
static void
toggle_confirm (GtkWidget *widget, gpointer ptr);
static gint
context_menu (GtkCTree *ctree, GdkEventButton *event, gpointer ptr);
static void
remove_node (GtkWidget *widget, gpointer ptr);
static gboolean
drag_compare (GtkCTree *ctree, GtkCTreeNode *source_node,
	      GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling);
static void
cut_node (GtkWidget *widget, gpointer ptr);
gboolean
node_freeitem (GNode *node, gpointer data);
static gboolean
nodecopy (GtkCTree *ctree, guint depth, GNode *gnode, GtkCTreeNode *cnode,
	  gpointer data);
static void
shortcut_changed (const key_widget_t *data);
static gint
menu_key_pressed (GtkCTree *ctree, GdkEventKey *event, gpointer ptr);
static void
collapse_recursive (GtkWidget *widget, gpointer ptr);
static void
expand_recursive (GtkWidget *widget, gpointer ptr);
static void
set_item_pixmap (GtkCTreeNode *node, item_t *item);
static void
open_filebrowser (GtkWidget *button, gpointer data);
static void
open_dirbrowser (GtkWidget *button, gpointer data);
static void
set_filename (GtkWidget *button, gpointer data);
static void
set_entry_text (GtkWidget *entry, gpointer data);
static void
import_menu (GtkWidget *entry, gpointer data);
static void
set_wmakerconf (GtkWidget *menuitem, gpointer data);
#ifdef GNOME_TO_WMAKER	    
static void
set_gnomemenu (GtkWidget *menuitem, gpointer data);
#endif /* GNOME_TO_WMAKER */
#ifdef PERL
#	ifdef KDE_TO_WMAKER	    
static void
set_kdemenu (GtkWidget *menuitem, gpointer data);
#	endif /* KDE_TO_WMAKER */
#endif /* PERL */
#ifdef WMCONFIG	    
static void
set_wmconfig (GtkWidget *menuitem, gpointer data);
#else /* not WMCONFIG */
static void
set_debian (GtkWidget *menuitem, gpointer data);
#endif /* not WMCONFIG */
static void
set_default_menu (GtkWidget *menuitem, gpointer data);
static void
import_dynamic_menu (GtkWidget *widget, gpointer data);
static void
remove_dir (GtkWidget *button, gpointer data);
static void
insert_dir  (GtkWidget *button, gpointer data);
static void
dir_list_to_item (GtkWidget *button, gpointer data);
static void
tree_moved (GtkCTree *ctree, GtkCTreeNode *node, GtkCTreeNode *new_parent, 
	    GtkCTreeNode *new_sibling);
static void
row_moved (GtkWidget *clist, gint source_row, gint dest_row);
static proplist_t
convert_to_proplist (const char *filename);
static void
convert_current_menu (GtkWidget *button, gpointer data);
static void
empty_menu (GtkWidget *button, gpointer data);
static void
use_wmaker_menu (GtkWidget *button, gpointer data);
static void
convert_wmaker_menu (GtkWidget *button, gpointer data);
static proplist_t
save_node (GNode *gnode);

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

				public code
  
*******************************************************************************/

GtkWidget *
rootmenu_dialog (GtkWidget *page, GtkTooltips *tooltips)
{
   char       *filename = get_gnustep_path ("Defaults/WMRootMenu");
   proplist_t menu 	= read_proplist (filename);

   if (!menu || WMIsPLString (menu))
   {
      GtkWidget *vbox = gtk_vbox_new (FALSE, 3);

      gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
      
      gtk_box_pack_start (GTK_BOX (vbox),
			  gtk_pixmap_new (p_array [P_INFO].pixmap,
					  p_array [P_INFO].mask),
			  FALSE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (vbox),
			  gtk_label_new (_("Application menu configuration "
					   "requires that your menu file")),
			  FALSE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (vbox),
			  gtk_label_new (filename),
			  FALSE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (vbox),
			  gtk_label_new (_("is in the new format.")),
			  FALSE, TRUE, 0);
      
      {
	 GtkWidget *button;
	 GtkWidget *bbox = gtk_hbutton_box_new ();
	    
	 gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox),
				    GTK_BUTTONBOX_SPREAD);
	 gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, TRUE, 5);

	 if (menu && WMIsPLString (menu))
	 {
	    button = gtk_button_new_with_label (_("Convert current menu"));
	    gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);
	    gtk_tooltips_set_tip (tooltips, button,
				  _("Convert current menu file to new format."),
				  NULL);
	    gtk_signal_connect (GTK_OBJECT (button), "clicked",
				GTK_SIGNAL_FUNC (convert_current_menu),
				g_strdup (WMGetFromPLString (menu)));
	 }

	 button = gtk_button_new_with_label (_("Start with empty menu"));
	 gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);
	 gtk_tooltips_set_tip (tooltips, button,
			       _("Replace current menu file with an empty one."),
			       NULL);
	 gtk_signal_connect (GTK_OBJECT (button), "clicked",
			     GTK_SIGNAL_FUNC (empty_menu), NULL);

      	 button = gtk_button_new_with_label (_("Use predefined menu"));
	 gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);
	 gtk_tooltips_set_tip (tooltips, button,
			       _("Use one of Window Maker's predefined menus "
				 "as a starting point."), NULL);
	 gtk_signal_connect (GTK_OBJECT (button), "clicked",
			     GTK_SIGNAL_FUNC (use_wmaker_menu), NULL);
      }

      gtk_box_pack_start (GTK_BOX (page), vbox, TRUE, TRUE, 0);
   
      info_message  = vbox;
      notebook_page = page;
      main_tooltips = tooltips;
      
      gtk_widget_show_all (page);
      if (menu)
	 WMReleasePropList (menu);
      
      {
	 GtkWidget *vbox = gtk_vbox_new (FALSE, 5);

	 gtk_box_pack_end (GTK_BOX (page), vbox, FALSE, FALSE, 0);
	 gtk_widget_show (vbox);
	 return vbox;
      }
   }
   else
   {
      gtk_box_pack_start (GTK_BOX (page), real_rootmenu (tooltips, menu),
			  TRUE, TRUE, 0);
      gtk_widget_show (page);
      return page;
   }
   if (filename)
      Free (filename);
}

bool_t
rootmenu_changed (void)
{
   return menu_changed;
}

bool_t
save_rootmenu (void)
{
   if (menu_changed)
   {
      GtkCTreeNode *root = gtk_ctree_node_nth (GTK_CTREE (menu_tree), 0);
      GNode	   *node = gtk_ctree_export_to_gnode (GTK_CTREE (menu_tree),
						      NULL, NULL, root, itemcopy,
						      NULL);
      proplist_t   menu = save_node (node);
      char	   *filename = get_gnustep_path ("Defaults/WMRootMenu");

      g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
		       node_freeitem, NULL);
      g_node_destroy (node);

      if (WMWritePropListToFile (menu, filename, YES))
      {
	 menu_changed = NO;
	 message (_("Window Maker root menu file '%s' saved."), filename);
	 Free (filename);
	 WMReleasePropList (menu);
      }
      else
      {
	 dialog_popup (DIALOG_ERROR, NULL, NULL,
		       _("Can't write to menu file\n`%s'"), filename);
	 Free (filename);
	 WMReleasePropList (menu);
	 
	 return FALSE;			/* ERROR */
      }
   }
   return TRUE;
}

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

				private code
  
*******************************************************************************/

static proplist_t
save_node (GNode *gnode)
{
   item_t     *item  = gnode->data;
   proplist_t pnode = WMCreatePLArray (NULL);
   unsigned   pos    = 0;

   WMInsertInPLArray (pnode, pos++, WMCreatePLString (item->name));
   if (item->action == WM_MENU)
   {
      GNode *gchild;

      if (gnode->children || gnode->parent == NULL)
	 for (gchild = gnode->children; gchild; gchild = gchild->next)
	 {
	    proplist_t pchild = save_node (gchild);
	    if (pchild)
	       WMInsertInPLArray (pnode, pos++, pchild);
	 }
      else
      {
	 WMReleasePropList (pnode);
	 return NULL;			/* don't export empty dir's */
      }
   }
   else
   {
      if (item->action != WM_INCLUDE && item->action != WM_OPEN_MENU
	  && item->action != WM_MENU && item->action != WM_PIPE)
      {
	 if (item->shortcut && strlen (item->shortcut)
	     && !strcaseeq (item->shortcut, "none"))
	 {
	    WMInsertInPLArray (pnode, pos++, WMCreatePLString ("SHORTCUT"));
	    WMInsertInPLArray (pnode, pos++, WMCreatePLString (item->shortcut));
	 }
      }
      WMInsertInPLArray (pnode, pos++,
		         WMCreatePLString ((char *) cmd_id [item->action]));
      if (!item->arg)
	 item->arg = g_strdup ("");
      if (item->action == WM_EXEC || item->action == WM_SHEXEC
	  || item->action == WM_INCLUDE || item->action == WM_OPEN_MENU)
	 WMInsertInPLArray (pnode, pos++, WMCreatePLString (item->arg));
      else if (item->action == WM_PIPE)
      {
	 char *tmp = g_strdup_printf ("| %s", item->arg);
	 WMInsertInPLArray (pnode, pos++, WMCreatePLString (tmp));
	 Free (tmp);
      }
      else if (item->action == WM_EXIT || item->action == WM_SHUTDOWN)
      {
	 if (streq (item->arg, "QUICK"))
	    WMInsertInPLArray (pnode, pos++, WMCreatePLString (item->arg));
      }
      else if (item->action == WM_RESTART)
      {
	 if (strlen (item->arg))
	    WMInsertInPLArray (pnode, pos++, WMCreatePLString (item->arg));
      }
   }
   return pnode;
}

static GtkWidget *
real_rootmenu (GtkTooltips *tooltips, proplist_t menu)
{
   GtkWidget	*scrolled;
   GtkWidget	*ctree;
   GtkWidget	*table;
   GtkWidget	*action_box;
   GtkCTreeNode *root;

   init_action_commands ();

#ifndef WMCONFIG
   /*
    *  Check for Debian system menu
    */
   {
      FILE *file;

      file = fopen (get_gnustep_path ("Library/WindowMaker/menu.hook"), "r");

      if (!file)
      {
	 file = fopen ("/etc/X11/WindowMaker/menu.hook", "r");
	 if (file)
	 {
	    debian_menu = YES;
	    fclose (file);
	 }
      }
      else
      {
	 debian_menu = YES;
	 fclose (file);
      }
   }
#endif
   
   /*
    *  Scrolled window for ctree
    */
   scrolled = gtk_scrolled_window_new (NULL, NULL);
   gtk_container_set_border_width (GTK_CONTAINER (scrolled), 5);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
				   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
   /*
    *  Ctree to show the menu structure
    */
   ctree = menu_tree = (GtkWidget *) gtk_dndtree_new (1, 0);
   gtk_object_set_user_data (GTK_OBJECT (ctree), ctree);
   gtk_clist_set_row_height (GTK_CLIST (ctree), 18);

   gtk_signal_connect (GTK_OBJECT (ctree), "tree_move",
		       GTK_SIGNAL_FUNC (tree_moved), NULL);

   gtk_ctree_set_drag_compare_func (GTK_CTREE (ctree), drag_compare);
   gtk_clist_set_column_auto_resize (GTK_CLIST (ctree), 0, TRUE);
   gtk_ctree_set_line_style (GTK_CTREE (ctree), GTK_CTREE_LINES_DOTTED);
   gtk_clist_set_selection_mode (GTK_CLIST (ctree), GTK_SELECTION_BROWSE);
   gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (ctree)); 
   gtk_widget_set_usize (scrolled, 250, -1);

   action_box = gtk_vbox_new (FALSE, 0);

   /*
    *  Menulabel
    */
   {
      GtkWidget *frame;
      GtkWidget *box;

      frame = gtk_frame_new (_("Menu(item) label"));
      gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
      gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
      gtk_box_pack_start (GTK_BOX (action_box), frame, FALSE, TRUE, 0);

      box = gtk_hbox_new (FALSE, 5);
      gtk_container_add (GTK_CONTAINER (frame), box);
      gtk_container_set_border_width (GTK_CONTAINER (box), 5);
      
      itemlabel_entry = gtk_entry_new ();
      gtk_box_pack_start (GTK_BOX (box), itemlabel_entry, TRUE, TRUE, 5);
      itemlabel_entry_sig = gtk_signal_connect (GTK_OBJECT (itemlabel_entry),
				"changed", GTK_SIGNAL_FUNC (set_label),  NULL);
      gtk_widget_show_all (frame);
   }

   /*
    *  Command
    */
   {
      GtkWidget *frame;
      GtkWidget *box;
      GtkWidget *cmd_vbox;

      command_box = frame = gtk_frame_new (_("Command"));
      gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
      gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
      gtk_box_pack_start (GTK_BOX (action_box), frame, FALSE, TRUE, 0);

      cmd_vbox = gtk_vbox_new (FALSE, 5);
      gtk_container_add (GTK_CONTAINER (frame), cmd_vbox);

      box = gtk_hbox_new (FALSE, 5);
      gtk_box_pack_start (GTK_BOX (cmd_vbox), box, FALSE, FALSE, 5);

      command_menu = generate_option_menu (NULL, NULL, NULL,
					   &cmd_name [WM_MENU + 1],
					   cmd_name [WM_EXEC], NULL,
					   set_command, NULL);
      gtk_box_pack_start (GTK_BOX (box), command_menu, TRUE, TRUE, 10);

      /*
       *  Confirmation panel
       */
      box_confirm_button = box = gtk_hbox_new (FALSE, 5);
      gtk_box_pack_start (GTK_BOX (cmd_vbox), box, FALSE, FALSE, 5);


      confirm_button = gtk_check_button_new_with_label (_("Show confirm panel"));
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (confirm_button), FALSE);
      gtk_signal_connect (GTK_OBJECT (confirm_button), "toggled",
			  GTK_SIGNAL_FUNC (toggle_confirm), NULL);
      gtk_box_pack_start (GTK_BOX (box), confirm_button, TRUE, TRUE, 10);
      
      /*
       *  Commandline 
       */
      box_commandline_entry = box = gtk_hbox_new (FALSE, 5);
      gtk_box_pack_start (GTK_BOX (cmd_vbox), box, FALSE, FALSE, 5);
      
      {
	 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);

	 gtk_box_pack_start (GTK_BOX (box), hbox, TRUE, TRUE, 5);
	 gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Command:")),
			     FALSE, TRUE, 5);
	 commandline_entry = gtk_entry_new ();
	 gtk_box_pack_start (GTK_BOX (hbox), commandline_entry, TRUE, TRUE, 5);
      }
      commandline_entry_sig = gtk_signal_connect (GTK_OBJECT(commandline_entry),
			  "changed", GTK_SIGNAL_FUNC (set_entry_text),  NULL);
      gtk_tooltips_set_tip (tooltips, commandline_entry,
			    _("Commandline of application to execute. A %s "
			      "directive is replaced by the current selection"
			      ", %w by the XID of the focused window, "
			      "and %a(title[,prompt]) pops up an input box with "
			      "specified title and optional prompt"
			      " and substitutes with the typed text."), NULL);
      /*
       *  Commandline of PIPE command
       */
      {
	 GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
	 GtkWidget *hbox;
	 
	 box_pipe_cmd_widget = box = gtk_hbox_new (FALSE, 5);
	 gtk_box_pack_start (GTK_BOX (cmd_vbox), box, FALSE, FALSE, 5);
      
	 gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 5);

	 hbox = gtk_hbox_new (FALSE, 0);
	 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
	 
	 gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Command:")),
			     FALSE, TRUE, 5);
	 pipe_command_entry = gtk_entry_new ();
	 gtk_box_pack_start (GTK_BOX (hbox), pipe_command_entry, TRUE, TRUE, 5);
	 pipe_command_entry_sig = gtk_signal_connect
				  (GTK_OBJECT (pipe_command_entry), "changed",
				    GTK_SIGNAL_FUNC (set_entry_text),  NULL);
	 gtk_tooltips_set_tip (tooltips, pipe_command_entry,
			       _("Commandline to generate dynamic menu."), NULL);
      
	 {
	    GtkWidget *button;
	    GtkWidget *bbox = gtk_hbutton_box_new ();
	    
	    gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, TRUE, 5);

	    button = gtk_button_new_with_label (_("Import"));
	    gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox),
				       GTK_BUTTONBOX_SPREAD);
	    
	    gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);
	    gtk_tooltips_set_tip (tooltips, button,
				  _("Import output of command. "
				    "The 'include dynamic menu' item is removed "
				    "afterwards."), NULL);
	    gtk_object_set_user_data (GTK_OBJECT (button), NULL);
	    gtk_signal_connect (GTK_OBJECT (button), "clicked",
				GTK_SIGNAL_FUNC (import_dynamic_menu), NULL);
	 }
      }
      
      /*
       *  Windowmanager 
       */
      box_wmanager_entry = box = gtk_hbox_new (FALSE, 5);
      gtk_box_pack_start (GTK_BOX (cmd_vbox), box, FALSE, FALSE, 5);
      
      {
	 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);

	 gtk_box_pack_start (GTK_BOX (box), hbox, TRUE, TRUE, 5);
	 gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Name:")),
			     FALSE, TRUE, 5);
	 wmanager_entry = gtk_entry_new ();
	 gtk_box_pack_start (GTK_BOX (hbox), wmanager_entry, TRUE, TRUE, 5);
      }
      wmanager_entry_sig = gtk_signal_connect (GTK_OBJECT (wmanager_entry),
			  "changed", GTK_SIGNAL_FUNC (set_entry_text),  NULL);
      gtk_tooltips_set_tip (tooltips, wmanager_entry,
			    _("Name of window manager to start. "
			      "If omitted, restart Window Maker."), NULL);

      /*
       *  Filename of included menu 
       */
      {
	 GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
	 GtkWidget *hbox;
	 
	 box_include_file_widget = box = gtk_hbox_new (FALSE, 5);
	 gtk_box_pack_start (GTK_BOX (cmd_vbox), box, FALSE, FALSE, 5);
      
	 gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 5);

	 hbox = gtk_hbox_new (FALSE, 0);
	 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
	 
	 gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("File:")),
			     FALSE, TRUE, 5);
	 include_file_entry = gtk_entry_new ();
	 gtk_box_pack_start (GTK_BOX (hbox), include_file_entry, TRUE, TRUE, 5);
         include_file_entry_sig = gtk_signal_connect 
				 (GTK_OBJECT (include_file_entry), "changed",
				  GTK_SIGNAL_FUNC (set_entry_text),  NULL);
	 gtk_tooltips_set_tip (tooltips, include_file_entry,
			       _("Filename of menu to include."), NULL);
      
	 {
	    GtkWidget *button;
	    GtkWidget *bbox = gtk_hbutton_box_new ();
	    
	    gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, TRUE, 5);

	    button = gtk_button_new_with_label (_("Import"));
	    gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox),
				       GTK_BUTTONBOX_SPREAD);
	    
	    gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);
	    gtk_tooltips_set_tip (tooltips, button,
				  _("Import contents of selected menu file. "
				    "The 'include menu file' item is removed "
				    "afterwards."), NULL);
	    gtk_object_set_user_data (GTK_OBJECT (button), NULL);
	    gtk_signal_connect (GTK_OBJECT (button), "clicked",
				GTK_SIGNAL_FUNC (import_menu), NULL);

	    button = gtk_button_new_with_label (_("Browse..."));
	    gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);
	    gtk_tooltips_set_tip (tooltips, button,
				  _("Open file browser to select include file."),
				  NULL);
	    gtk_signal_connect (GTK_OBJECT (button), "clicked",
				GTK_SIGNAL_FUNC (open_filebrowser), NULL);
	    
	 }
      }

      /*
       *  Directory list 
       */
      {
	 GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
	 GtkWidget *hbox;
	 GtkWidget *scrolled;
	 GtkWidget *clist;
	 char	   *title = _("Directory list");
	 
	 box_dir_widget = box = gtk_hbox_new (FALSE, 5);
	 gtk_box_pack_start (GTK_BOX (cmd_vbox), box, FALSE, FALSE, 5);
      
	 gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 5);

	 hbox = gtk_hbox_new (FALSE, 0);
	 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);

	 gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Command:")),
			     FALSE, TRUE, 5);
	 dir_with_entry = gtk_entry_new ();
	 gtk_box_pack_start (GTK_BOX (hbox), dir_with_entry, TRUE, TRUE, 5);
         dir_with_entry_sig = gtk_signal_connect (GTK_OBJECT (dir_with_entry),
			"changed", GTK_SIGNAL_FUNC (dir_list_to_item),  NULL);
	 gtk_tooltips_set_tip (tooltips, dir_with_entry,
			       _("Recursively, a submenu with all files and"
				 " subdirectories of the given directory list is"
				 " generated - each file is then used as argument"
				 " for `command'. If `command' is omitted, a"
				 " submenu with all executable files"
				 " is generated."), NULL);

	 hbox = gtk_hbox_new (FALSE, 0);
	 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);

	 scrolled = gtk_scrolled_window_new (NULL, NULL);
	 gtk_container_set_border_width (GTK_CONTAINER (scrolled), 5);
	 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
					 GTK_POLICY_AUTOMATIC,
					 GTK_POLICY_AUTOMATIC);
	 dir_clist = clist = gtk_clist_new_with_titles (1, &title);
	 gtk_tooltips_set_tip (tooltips, clist,
			       _("Insert new directory."), NULL);
	 gtk_clist_set_reorderable (GTK_CLIST (clist), TRUE);
	 gtk_clist_set_use_drag_icons (GTK_CLIST (clist), TRUE);
	 gtk_signal_connect_after (GTK_OBJECT (clist), "row_move",
				   GTK_SIGNAL_FUNC (row_moved), NULL);
	 gtk_clist_set_column_auto_resize (GTK_CLIST (clist), 0, YES);
	 gtk_clist_column_titles_passive (GTK_CLIST (clist));
	 gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_BROWSE);
	 gtk_container_add (GTK_CONTAINER (scrolled), clist);
	 gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 0);
	 gtk_widget_set_usize (clist, -1, 100);
	 {
	    GtkWidget *button;
	    GtkWidget *bbox = gtk_hbutton_box_new ();
	    
	    gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, TRUE, 0);
	    gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox),
				       GTK_BUTTONBOX_SPREAD);

	    button = gtk_button_new_with_label (_("Insert..."));
	    gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);
	    gtk_tooltips_set_tip (tooltips, button,
				  _("Insert new directory."), NULL);
	    gtk_object_set_user_data (GTK_OBJECT (button), NULL);
	    gtk_signal_connect (GTK_OBJECT (button), "clicked",
				GTK_SIGNAL_FUNC (open_dirbrowser), NULL);

	    dir_remove = button = gtk_button_new_with_label (_("Remove"));
	    gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);
	    gtk_tooltips_set_tip (tooltips, button,
				  _("Remove selected directory."), NULL);
	    gtk_signal_connect_after (GTK_OBJECT (button), "clicked",
				      GTK_SIGNAL_FUNC (remove_dir), NULL);
	 }
      }
      /*
       *  Strip suffix of file names
       */
      box_suffix_button = box = gtk_hbox_new (FALSE, 5);
      gtk_box_pack_start (GTK_BOX (cmd_vbox), box, FALSE, FALSE, 0);

      suffix_button = gtk_check_button_new_with_label (_("Strip suffix of filenames"));
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (suffix_button), FALSE);
      gtk_signal_connect (GTK_OBJECT (suffix_button), "toggled",
			  GTK_SIGNAL_FUNC (dir_list_to_item), NULL);
      gtk_box_pack_start (GTK_BOX (box), suffix_button, TRUE, TRUE, 10);

   }
   /*
    *  Shortcut
    */
   {
      GtkWidget *frame;
      GtkWidget *box;

      box_shortcut_widget = frame = gtk_frame_new (_("Shortcut"));
      gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
      gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
      gtk_box_pack_start (GTK_BOX (action_box), frame, FALSE, TRUE, 0);

      box = gtk_hbox_new (FALSE, 5);
      gtk_container_add (GTK_CONTAINER (frame), box);
      gtk_container_set_border_width (GTK_CONTAINER (box), 5);
      
      shortcut_widget = key_dialog ("None", tooltips, NULL, shortcut_changed,
				    NULL);
      gtk_box_pack_start (GTK_BOX (box), shortcut_widget, TRUE, TRUE, 5);

   }

   if (menu)
   {
      root = build_tree (NULL, NULL, menu);
      WMReleasePropList (menu);
   }
   else
   {
      char *text = "Applications";
      
      root = gtk_ctree_insert_node (GTK_CTREE (ctree), NULL, NULL,
				    &text, 8,
				    p_array [P_BOOK_CLOSE].pixmap,
				    p_array [P_BOOK_CLOSE].mask,
				    p_array [P_BOOK_OPEN].pixmap,
				    p_array [P_BOOK_OPEN].mask, FALSE, FALSE);
      gtk_ctree_node_set_row_data_full (GTK_CTREE (ctree), root,
					item_constructor (text, WM_MENU, NULL,
							  NULL),
					item_destructor);
   }

   /*
    *  Horizontal box for ctree and action area
    */
   table = gtk_hpaned_new ();
   gtk_paned_add1 (GTK_PANED (table), scrolled);
   gtk_paned_add2 (GTK_PANED (table), action_box);
   gtk_widget_show_all (table);
   gtk_widget_set_usize (scrolled, 300, -1);
   
   gtk_widget_show_all (command_box);
   gtk_widget_hide (command_box);
   gtk_widget_hide (box_shortcut_widget);

   gtk_signal_connect (GTK_OBJECT(ctree), "tree_select_row",
		       GTK_SIGNAL_FUNC (selection_made), NULL);
   gtk_signal_connect (GTK_OBJECT(ctree), "key_press_event",
		       GTK_SIGNAL_FUNC (menu_key_pressed), NULL);
   gtk_signal_connect (GTK_OBJECT (ctree), "button_press_event",
		       GTK_SIGNAL_FUNC (context_menu), NULL);

   gtk_ctree_select (GTK_CTREE (ctree), root);

   return table;
}

static void
init_action_commands (void)
/*
 *  Initialize Window Maker's rootmenu commands and short description of
 *  every command.
 *
 *  No return value.
 *
 *  Side effects: cmd_id, cmd_name are filled
 */
{
   cmd_id [WM_MENU]             = "MENU";
   cmd_name [WM_MENU]           = _("Submenu");
   cmd_id [WM_INCLUDE]          = "OPEN_MENU";
   cmd_name [WM_INCLUDE]        = _("Include menu file");
   cmd_id [WM_PIPE]             = "OPEN_MENU";
   cmd_name [WM_PIPE]           = _("Include dynamic menu");
   cmd_id [WM_OPEN_MENU]        = "OPEN_MENU";
   cmd_name [WM_OPEN_MENU]      = _("Include directory menu");
   cmd_id [WM_EXEC]             = "EXEC";
   cmd_name [WM_EXEC]           = _("Execute application");
   cmd_id [WM_SHEXEC]           = "SHEXEC";
   cmd_name [WM_SHEXEC]         = _("Execute shell command");
   cmd_id [WM_REFRESH]          = "REFRESH";
   cmd_name [WM_REFRESH]        = _("Refresh screen");
   cmd_id [WM_RESTART]          = "RESTART";
   cmd_name [WM_RESTART]        = _("(Re)start window manager");
   cmd_id [WM_EXIT]             = "EXIT";
   cmd_name [WM_EXIT]           = _("Exit Window Maker");
   cmd_id [WM_SHUTDOWN]         = "SHUTDOWN";
   cmd_name [WM_SHUTDOWN]       = _("Exit session");
   cmd_id [WM_WORKSPACE_MENU]   = "WORKSPACE_MENU";
   cmd_name [WM_WORKSPACE_MENU] = _("Workspace menu");
   cmd_id [WM_ARRANGE_ICONS]    = "ARRANGE_ICONS";
   cmd_name [WM_ARRANGE_ICONS]  = _("Arrange miniwindows");
   cmd_id [WM_HIDE_OTHERS]      = "HIDE_OTHERS";
   cmd_name [WM_HIDE_OTHERS]    = _("Hide other windows");
   cmd_id [WM_SHOW_ALL]         = "SHOW_ALL";
   cmd_name [WM_SHOW_ALL]       = _("Show all windows");
   cmd_id [WM_SAVE_SESSION]     = "SAVE_SESSION";
   cmd_name [WM_SAVE_SESSION]   = _("Save session");
   cmd_id [WM_CLEAR_SESSION]    = "CLEAR_SESSION";
   cmd_name [WM_CLEAR_SESSION]  = _("Discard session data");
   cmd_id [WM_INFO_PANEL]       = "INFO_PANEL";
   cmd_name [WM_INFO_PANEL]     = _("Show info panel");
   cmd_id [WM_LEGAL_PANEL]      = "LEGAL_PANEL";
   cmd_name [WM_LEGAL_PANEL]    = _("Show legal panel");
   cmd_id [WM_LAST]             = NULL;
   cmd_name [WM_LAST]           = NULL;
}

static GtkCTreeNode *
build_tree (GtkCTreeNode *parent, GtkCTreeNode *sibling, proplist_t menu)
/*
 *  Recursively build a menu tree using the 'ctree' widget.
 *  If 'parent' != NULL insert node as a new child of 'parent'.
 *  If 'sibling' != NULL insert node just before 'sibling'. Otherwise append
 *  the node at the lists tail.
 *  'menu' is the current part of the menu in proplist format.
 *
 *  Return value:
 *	new node
 */
{
   char		*text;
   proplist_t	pl;
   unsigned	n;
   GtkCTreeNode *node;
	 
   if (!WMIsPLArray (menu) || (n = WMGetPropListItemCount (menu)) < 1)
      return NULL;			/* ERROR */
   
   pl = WMGetFromPLArray (menu, 0);
   if (!WMIsPLString (pl))
      return NULL;			/* ERROR */
   text = WMGetFromPLString (pl);
   
   pl = WMGetFromPLArray (menu, 1);
   if (n == 1 || WMIsPLArray (pl))	/* submenu */
   {
      unsigned k;
      item_t   *item = item_constructor (text, WM_MENU, NULL, NULL);
      
      node = gtk_ctree_insert_node (GTK_CTREE (menu_tree), parent, sibling,
				    &text, 8, NULL, NULL, NULL, NULL,
				    FALSE, !parent);
      gtk_ctree_node_set_row_data_full (GTK_CTREE (menu_tree), node, item,
					item_destructor);
      set_item_pixmap (node, item);
      
      for (k = 1; k < n; k++)
      {
	 pl = WMGetFromPLArray (menu, k);
	 if (WMIsPLArray (pl))		/* submenu */
	    build_tree (node, NULL, pl);
	 else
	    return NULL;		/* ERROR */
      }
   }
   else					/* leaf */
   {
      item_t	*new = item_constructor (text, WM_EXEC, NULL, NULL);
      char	*action;
      int	an;
      
      action  = WMGetFromPLString (pl);
	 
      if (streq (action, "SHORTCUT"))
      {
	 if (n >= 4)			/* name SHORTCUT shortcutdef COMMAND */
	 {
	    pl = WMGetFromPLArray (menu, 2);
	    new->shortcut = g_strdup (WMGetFromPLString (pl));
	    pl = WMGetFromPLArray (menu, 3);
	    if (n >= 5)			/* ... COMMAND OPTIONS */
	       new->arg = g_strdup (WMGetFromPLString (WMGetFromPLArray (menu, 4)));
	 }
	 else
	 {
	    item_destructor (new);
	    return NULL;		/* ERROR */
	 }
      }
      else if (n >= 3)		/* ... COMMAND OPTIONS */
	 new->arg = g_strdup (WMGetFromPLString (WMGetFromPLArray (menu, 2)));

      action = WMGetFromPLString (pl);
	 
      new->action = WM_MENU;
      for (an = 0; an < WM_LAST; an++)
	 if (streq (action, cmd_id [an]))
	 {
	    new->action = an;
	    break;
	 }
      if (new->action == WM_MENU)
      {
	 item_destructor (new);
	 return NULL;			/* ERROR */
      }

      if (new->action == WM_OPEN_MENU
	  || new->action == WM_INCLUDE
	  || new->action == WM_PIPE)	/* check type of command  */
      {
	 if (!new->arg)			/* ERROR */
	 {
	    item_destructor (new);
	    return NULL;		/* ERROR */
	 }
	 if (new->shortcut)		/* not allowed */
	 {
	    Free (new->shortcut);
	    new->shortcut = NULL;
	 }
	 if (new->arg [0] == '|')	/* WM_PIPE */
	 {
	    char *arg;

	    for (arg = new->arg + 1; *arg == ' '; arg++)
	       ;
	    if (!*arg)			/* ERROR */
	    {
	       item_destructor (new);
	       return NULL;		/* ERROR */
	    }
	    arg = g_strdup (arg);
	    Free (new->arg);
	    new->arg    = arg;
	    new->action = WM_PIPE;
	 }
	 else
	 {
	    if (strchr (new->arg, ' '))	/* include list of directories */
	    {
	       new->action = WM_OPEN_MENU;
	    }
	    else
	    {
	       char *path = expand_tilde (new->arg);
	       DIR  *dir;
	 
	       if ((dir = opendir (path))) /* include one directory */
	       {
		  closedir (dir);
		  new->action = WM_OPEN_MENU;
	       }
	       else			/* include single file */
	       {
		  new->action = WM_INCLUDE;
	       }
	    }
	 }
      }
      node = gtk_ctree_insert_node (GTK_CTREE (menu_tree), parent, sibling,
				    &text, 8, NULL, NULL, NULL, NULL, TRUE, TRUE);
      gtk_ctree_node_set_row_data_full (GTK_CTREE (menu_tree), node,
					(gpointer) new, item_destructor);
      set_item_pixmap (node, new);
   }

   return node;
}

static void
item_destructor (gpointer data)
{
   item_t *item = (item_t *) data;

   if (item->name)
      Free (item->name);
   if (item->shortcut)
      Free (item->shortcut);
   if (item->arg)
      Free (item->arg);
   Free (item);
}

static item_t *
item_constructor (const char *name, action_e action, const char *arg,
		  const char *shortcut)
{
   item_t *new   = Calloc (1, sizeof (item_t));

   assert (name);
   new->name     = g_strdup (name);
   new->action   = action;
   new->shortcut = g_strdup (shortcut);
   new->arg      = g_strdup (arg);

   return new;
}

static void
selection_made (GtkCTree *ctree, GtkCTreeNode *node, gint column,
		GdkEventButton *event, gpointer data)
/*
 *  Update widgets after new item has been selected.
 */
{
   gchar  *text;
   bool_t changed = menu_changed;
   item_t *item   = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree), node);

   if (!item)
      return;
   gtk_ctree_get_node_info (GTK_CTREE (menu_tree), node, &text, 
			    NULL, NULL, NULL, NULL, NULL, NULL, NULL);
   g_signal_handler_block (itemlabel_entry, itemlabel_entry_sig);
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), text);
   g_signal_handler_unblock (itemlabel_entry, itemlabel_entry_sig);
   show_action_dialog (gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree), node));
   menu_changed = changed;
}

static void
set_label (GtkWidget *widget, gpointer ptr)
/*
 *  Set label of submenu or command.
 */
{
   const char	*label = gtk_entry_get_text (GTK_ENTRY (itemlabel_entry));
   GtkCTreeNode *node  = (GTK_CLIST (menu_tree)->selection)->data;
   item_t	*item  = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree),
						      node);
   char		*old;
   guint8	spacing;
   GdkPixmap	*closed, *opened;
   GdkBitmap	*mask_closed, *mask_opened;
   gboolean	is_leaf, expanded;

   Free (item->name);
   item->name = g_strdup (label);

   gtk_ctree_get_node_info (GTK_CTREE (menu_tree), node, &old, &spacing,
			    &closed, &mask_closed, &opened, &mask_opened,
			    &is_leaf, &expanded);
   gtk_ctree_set_node_info (GTK_CTREE (menu_tree), node, label, spacing,
			    closed, mask_closed, opened, mask_opened,
			    is_leaf, expanded);
   if (item->action > WM_MENU)
      gtk_option_menu_set_history (GTK_OPTION_MENU (command_menu),
				   item->action - 1);
   toggle_save (menu_changed = YES, NULL);
}

static void
set_command (GtkWidget *widget, gpointer ptr)
/*
 *  Set type of command for menu item
 */
{
   char		*text  = gtk_object_get_user_data (GTK_OBJECT (widget));
   GtkCTreeNode *node  = (GTK_CLIST (menu_tree)->selection)->data;
   item_t	*item  = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree),
						      node);
   action_e	an;
   
   for (an = 0; an < WM_LAST; an++)
      if (streq (text, cmd_name [an]))
      {
	 if (an != item->action)	/* type of action changed ? */
	 {
	    item->action = an;
	    if (item->arg)
	       Free (item->arg);
	    item->arg = NULL;
	    set_item_pixmap (node, item);
	    show_action_dialog (item);
	    toggle_save (menu_changed = YES, NULL);
	 }
	 break;
      }
}

static void
show_action_dialog (item_t *item)
/*
 *  Show and hide widgets depending on type of action
 */
{
   gtk_widget_hide (command_box);

   if (item->action == WM_OPEN_MENU || item->action == WM_INCLUDE
       || item->action == WM_PIPE || item->action == WM_MENU)
   {
      gtk_widget_hide (box_shortcut_widget);
   }
   else
   {
      gtk_widget_show_all (box_shortcut_widget);
      update_keywidget (gtk_object_get_user_data (GTK_OBJECT (shortcut_widget)),
			item->shortcut ? item->shortcut : "None");
   }

   /*
    *  First of all, hide all subwidgets of the command_box
    */
   gtk_widget_hide (box_pipe_cmd_widget);
   gtk_widget_hide (box_include_file_widget);
   gtk_widget_hide (box_confirm_button);
   gtk_widget_hide (box_commandline_entry);
   gtk_widget_hide (box_wmanager_entry);
   gtk_widget_hide (box_dir_widget);
   gtk_widget_hide (box_suffix_button);
      
   if (item->action > WM_MENU)
      gtk_option_menu_set_history (GTK_OPTION_MENU (command_menu),
				   item->action - 1);
   if (item->action == WM_EXEC || item->action == WM_SHEXEC) /* show cmdline */
   {
      guint sighandler = GPOINTER_TO_INT (gtk_object_get_user_data
					  (GTK_OBJECT (commandline_entry)));
      if (!item->arg)
	 item->arg = g_strdup ("");
      g_signal_handler_block (commandline_entry, commandline_entry_sig);
      gtk_entry_set_text (GTK_ENTRY (commandline_entry), item->arg);
      g_signal_handler_unblock (commandline_entry, commandline_entry_sig);
      gtk_widget_show (box_commandline_entry);
   }
   else if (item->action == WM_INCLUDE)	/* show include file entry */
   {
      if (!item->arg)
	 item->arg = g_strdup ("");
      g_signal_handler_block (include_file_entry, include_file_entry_sig);
      gtk_entry_set_text (GTK_ENTRY (include_file_entry), item->arg);
      g_signal_handler_unblock (include_file_entry, include_file_entry_sig);
      gtk_widget_show (box_include_file_widget);
   }
   else if (item->action == WM_PIPE)	/* show pipe commandline entry */
   {
      if (!item->arg)
	 item->arg = g_strdup ("");
      g_signal_handler_block (pipe_command_entry, pipe_command_entry_sig);
      gtk_entry_set_text (GTK_ENTRY (pipe_command_entry), item->arg);
      g_signal_handler_unblock (pipe_command_entry, pipe_command_entry_sig);
      gtk_widget_show (box_pipe_cmd_widget);
   }
   else if (item->action == WM_EXIT
	    || item->action == WM_SHUTDOWN) /* show confirm toggle */
   {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (confirm_button),
				   !(item->arg && streq (item->arg, "QUICK")));
      gtk_widget_show (box_confirm_button);
   }
   else if (item->action == WM_RESTART)	/* show windowmanger entry */
   {
      g_signal_handler_block (wmanager_entry, wmanager_entry_sig);
      if (item->arg)
	 gtk_entry_set_text (GTK_ENTRY (wmanager_entry), item->arg);
      else
	 gtk_entry_set_text (GTK_ENTRY (wmanager_entry), "");
      g_signal_handler_unblock (wmanager_entry, wmanager_entry_sig);
      gtk_widget_show (box_wmanager_entry);
   }
   else if (item->action == WM_OPEN_MENU) /* show directory list */
   {
      if (!item->arg)
	 item->arg = g_strdup ("");
      {
	 char	*start, *end;
	 char	*dir;
	 bool_t strip_suffix = NO;
	 
	 gtk_clist_clear (GTK_CLIST (dir_clist));
	 start = item->arg;

	 while (1)
	 {
	    while (*start && isspace (*start))
	       start++;
	    end = start;
	    while (*end && !isspace (*end))
	       end++;
	    if (start == end)
	       break;

	    dir = Calloc (end - start + 1, sizeof (char));
	    strncpy (dir, start, end - start);

	    if (streq (dir, "WITH"))
	    {
	       while (*end && isspace (*end))
		  end++;
	       g_signal_handler_block (dir_with_entry, dir_with_entry_sig);
	       gtk_entry_set_text (GTK_ENTRY (dir_with_entry), end);
	       g_signal_handler_unblock (dir_with_entry, dir_with_entry_sig);
	       Free (dir);
	       break;
	    }

	    if (streq (dir, "-noext"))
	       strip_suffix = YES;
	    else
	       gtk_clist_insert (GTK_CLIST (dir_clist), -1, &dir);

	    start = end;
	    Free (dir);
	 }
	 gtk_clist_select_row (GTK_CLIST (dir_clist), 0, 0);
	 gtk_widget_set_sensitive (dir_remove, GTK_CLIST (dir_clist)->rows > 1);
	 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (suffix_button),
				      strip_suffix);
      }
      gtk_widget_show (box_dir_widget);
      gtk_widget_show (box_suffix_button);
   }
   if (item->action != WM_MENU)
      gtk_widget_show (command_box);
}

static void
toggle_confirm (GtkWidget *widget, gpointer ptr)
/*
 *  Toggle confirm panel when using the EXIT or SHUTDOWN command.
 */
{
   GtkCTreeNode *node  = (GTK_CLIST (menu_tree)->selection)->data;
   item_t	*item  = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree),
						      node);

   if (GTK_TOGGLE_BUTTON (widget)->active)
   {
      if (item->arg)
	 Free (item->arg);
      item->arg = NULL;
   }
   else
   {
      if (!item->arg || !streq (item->arg, "QUICK"))
      {
	 if (item->arg)
	    Free (item->arg);
	 item->arg = g_strdup ("QUICK");
      }
   }
   toggle_save (menu_changed = YES, NULL);
}

static void
insert_menu (GtkWidget *widget, gpointer ptr)
/*
 *  Insert a new submenu just at the current selection.
 */
{
   GtkCTreeNode *node;
   GtkCTreeNode *selection = (GTK_CLIST (menu_tree)->selection)->data;
   GtkCTreeNode *parent    = GTK_CTREE_ROW (selection)->parent;

   if (!parent || (!GTK_CTREE_ROW (selection)->is_leaf &&
		   !GTK_CTREE_ROW (selection)->children))
   {
      parent 	= selection;
      selection = GTK_CTREE_ROW (parent)->children;
   }

   if (ptr)
   {
      char *text = "Command";
      
      node = gtk_ctree_insert_node (GTK_CTREE (menu_tree),
				    parent, selection, &text, 8,
				    p_array [P_EXEC].pixmap,
				    p_array [P_EXEC].mask,
				    NULL, NULL, TRUE, TRUE);
      gtk_ctree_node_set_row_data_full (GTK_CTREE (menu_tree), node,
					item_constructor (text, WM_EXEC, "",
							  NULL),
					item_destructor);
   }
   else
   {
      char *text = "Submenu";
      
      node = gtk_ctree_insert_node (GTK_CTREE (menu_tree),
				    parent, selection, &text, 8,
				    p_array [P_BOOK_CLOSE].pixmap,
				    p_array [P_BOOK_CLOSE].mask,
				    p_array [P_BOOK_OPEN].pixmap,
				    p_array [P_BOOK_OPEN].mask, FALSE, FALSE);
      gtk_ctree_node_set_row_data_full (GTK_CTREE (menu_tree), node,
					item_constructor (text, WM_MENU, NULL,
							  NULL),
					item_destructor);
   }
   if (!gtk_ctree_node_is_visible (GTK_CTREE (menu_tree), node))
       gtk_ctree_expand (GTK_CTREE (menu_tree), parent);
   gtk_ctree_select (GTK_CTREE (menu_tree), node);
   gtk_entry_select_region (GTK_ENTRY (itemlabel_entry), 0, -1);
   gtk_widget_grab_focus (itemlabel_entry);
   toggle_save (menu_changed = YES, NULL);
}

static void
remove_node (GtkWidget *widget, gpointer ptr)
/*
 *  Insert a new submenu just behind the current selection.
 */
{
   GtkCTreeNode *selection = (GTK_CLIST (menu_tree)->selection)->data;

   if (GTK_CTREE_ROW (selection)->parent)
   {
      if (GTK_CTREE_NODE_NEXT (selection))
	 gtk_ctree_select (GTK_CTREE (menu_tree),
			   GTK_CTREE_NODE_NEXT (selection));
      else if (GTK_CTREE_NODE_PREV (selection))
	 gtk_ctree_select (GTK_CTREE (menu_tree),
			   GTK_CTREE_NODE_PREV (selection));
      
      gtk_ctree_remove_node (GTK_CTREE (menu_tree), selection);
      toggle_save (menu_changed = YES, NULL);
   }
}

static void
cut_node (GtkWidget *widget, gpointer ptr)
/*
 *  Insert a new submenu just behind the current selection.
 */
{
   GtkCTreeNode *selection = (GTK_CLIST (menu_tree)->selection)->data;

   if (GTK_CTREE_ROW (selection)->parent)
   {
      if (last_node)			/* free memory */
      {
	 g_node_traverse (last_node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
			  node_freeitem, NULL);
	 g_node_destroy (last_node);
      }
      last_node = gtk_ctree_export_to_gnode (GTK_CTREE (menu_tree), NULL, NULL,
					     selection, itemcopy, NULL);
      remove_node (widget, ptr);
      toggle_save (menu_changed = YES, NULL);
   }
}

static void
expand_recursive (GtkWidget *widget, gpointer ptr)
{
   gtk_ctree_expand_recursive (GTK_CTREE (menu_tree),
			       gtk_ctree_node_nth (GTK_CTREE (menu_tree), 0));
}

static void
collapse_recursive (GtkWidget *widget, gpointer ptr)
{
   gtk_ctree_collapse_recursive (GTK_CTREE (menu_tree),
				 gtk_ctree_node_nth (GTK_CTREE (menu_tree), 0));
   gtk_ctree_expand (GTK_CTREE (menu_tree),
		     gtk_ctree_node_nth (GTK_CTREE (menu_tree), 0));
}

static void
paste_node (GtkWidget *widget, gpointer ptr)
/*
 *  Insert a new submenu just behind the current selection.
 */
{
   GtkCTreeNode *selection = (GTK_CLIST (menu_tree)->selection)->data;
   if (last_node)
   {
      GtkCTreeNode *parent = GTK_CTREE_ROW (selection)->parent;
      GtkCTreeNode *node;
      
      if (!parent || (!GTK_CTREE_ROW (selection)->is_leaf &&
		      !GTK_CTREE_ROW (selection)->children))
      {
	 parent    = selection;
	 selection = GTK_CTREE_ROW (parent)->children;
      }
      node = gtk_ctree_insert_gnode (GTK_CTREE (menu_tree), parent, selection,
				     last_node, nodecopy, NULL);
      if (!gtk_ctree_node_is_visible (GTK_CTREE (menu_tree), node))
	 gtk_ctree_expand (GTK_CTREE (menu_tree), parent);
      toggle_save (menu_changed = YES, NULL);
   }
}

gboolean
node_freeitem (GNode *node, gpointer data)
{
   item_destructor (node->data);
   node->data = NULL;

   return FALSE;
}

static gint
context_menu (GtkCTree *ctree, GdkEventButton *event, gpointer ptr)
/*
 *  Popup context menu (right click)
 */
{
   gint		row;
   gint		column;
   GtkCTreeNode *node;

   if (!gtk_clist_get_selection_info (GTK_CLIST (menu_tree), event->x, event->y, 
				      &row, &column)
       || event->button != 3)
      return FALSE;

   node = GTK_CTREE_NODE (g_list_nth (GTK_CLIST (menu_tree)->row_list, row));
   
   gtk_ctree_select (GTK_CTREE (menu_tree), node);
   
   {
      static GtkWidget  *menu = NULL;
      GtkWidget		*menu_item;

      if (!menu)
      {
	 menu = gtk_menu_new ();

	 menu_item = gtk_menu_item_new_with_label (_("Insert new command"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (insert_menu), "");

	 menu_item = gtk_menu_item_new_with_label (_("Insert new submenu"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (insert_menu), NULL);

	 menu_item = gtk_menu_item_new ();
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 
	 menu_item = gtk_menu_item_new_with_label (_("Insert WMAKERCONF menu"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (set_wmakerconf), NULL);

#ifdef GNOME_TO_WMAKER	    
	 menu_item = gtk_menu_item_new_with_label (_("Insert GNOME menu"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (set_gnomemenu), NULL);
#endif /* GNOME_TO_WMAKER */

#ifdef PERL
#	ifdef KDE_TO_WMAKER	    
	 menu_item = gtk_menu_item_new_with_label (_("Insert KDE menu"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (set_kdemenu), NULL);
#	endif /* KDE_TO_WMAKER */
#endif /* PERL */
#ifdef WMCONFIG	    
	 menu_item = gtk_menu_item_new_with_label (_("Insert RedHat menu"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (set_wmconfig), NULL);
#else  /* not WMCONFIG */
	 if (debian_menu)
	 {
	    menu_item = gtk_menu_item_new_with_label (_("Insert Debian menu"));
	    gtk_menu_append (GTK_MENU (menu), menu_item);
	    gtk_widget_show (menu_item);
	    gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
				GTK_SIGNAL_FUNC (set_debian), NULL);
	 }
#endif /* not WMCONFIG */

	 menu_item = gtk_menu_item_new_with_label (_("Insert default menu"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (set_default_menu), NULL);

	 menu_item = gtk_menu_item_new ();
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 
      	 menu_item = gtk_menu_item_new_with_label (_("Cut item/submenu"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (cut_node), NULL);

	 menu_item = gtk_menu_item_new_with_label (_("Paste item/submenu"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (paste_node), NULL);

	 menu_item = gtk_menu_item_new ();
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 
	 menu_item = gtk_menu_item_new_with_label (_("Expand submenus"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (expand_recursive), NULL);

      	 menu_item = gtk_menu_item_new_with_label (_("Collapse submenus"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (collapse_recursive), NULL);
      }
      
      gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3, event->time);
   }
   return TRUE;
}

static gboolean
drag_compare (GtkCTree *ctree, GtkCTreeNode *source_node,
	      GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling)
{
   if (new_parent)
      return TRUE;
   else
      return FALSE;
}

static gboolean
itemcopy (GtkCTree *ctree, guint depth, GNode *gnode, GtkCTreeNode *cnode,
	  gpointer data)
{
   if (!cnode || !gnode)
      return FALSE;
   else
   {
      item_t *old = gtk_ctree_node_get_row_data (ctree, cnode);
      item_t *new = item_constructor (old->name, old->action, old->arg,
				      old->shortcut);
      gnode->data = new;
      return TRUE;
   }
}

static gboolean
nodecopy (GtkCTree *ctree, guint depth, GNode *gnode, GtkCTreeNode *cnode,
	  gpointer data)
{
   item_t *item;
   
   if (!cnode || !gnode || (!(item = gnode->data)))
      return FALSE;
   else
   {
      set_item_pixmap (cnode, item);
      gtk_ctree_node_set_row_data_full (GTK_CTREE (menu_tree), cnode,
					item_constructor (item->name,
							  item->action,
							  item->arg,
							  item->shortcut),
					item_destructor);
      return TRUE;
   }
}

static void
shortcut_changed (const key_widget_t *data)
{
   GtkCTreeNode *node  = (GTK_CLIST (menu_tree)->selection)->data;
   item_t	*item  = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree),
						      node);

   if (item->shortcut)
      Free (item->shortcut);
   item->shortcut = g_strdup (data->keytext);
   toggle_save (menu_changed = YES, NULL);
}

static gint
menu_key_pressed (GtkCTree *ctree, GdkEventKey *event, gpointer ptr)
{
   GtkCTreeNode *selection = (GTK_CLIST (menu_tree)->selection)->data;

   switch (event->keyval)
   {
      case GDK_Return:
      case GDK_space:
	 if (GTK_CTREE_ROW (selection)->expanded)
	    gtk_ctree_collapse (GTK_CTREE (menu_tree), selection);
	 else
	    gtk_ctree_expand (GTK_CTREE (menu_tree), selection);
	 return TRUE;
      case GDK_Insert:
	 paste_node (menu_tree, NULL);
	 return TRUE;
      case GDK_y:
	 if (event->state & GDK_CONTROL_MASK)
	    paste_node (menu_tree, NULL);
	 return TRUE;
      case GDK_BackSpace:
      case GDK_Delete:
	 cut_node (menu_tree, NULL);
	 return TRUE;
      case GDK_k:
	 if (event->state & GDK_CONTROL_MASK)
	    cut_node (menu_tree, NULL);
	 return TRUE;
      case GDK_m:
	 if (event->state & GDK_CONTROL_MASK)
	    insert_menu (menu_tree, NULL);
	 return TRUE;
      case GDK_c:
	 if (event->state & GDK_CONTROL_MASK)
	    insert_menu (menu_tree, "");
	 return TRUE;
      default:
	 break;
   }
   return FALSE;
}

static void
set_item_pixmap (GtkCTreeNode *node, item_t *item)
/*
 *  Set pixmap of tree item.
 */
{
   if (item->action == WM_MENU)
   {
      char	*old;
      guint8	spacing;
      GdkPixmap	*closed, *opened;
      GdkBitmap	*mask_closed, *mask_opened;
      gboolean	is_leaf, expanded;
      
      gtk_ctree_get_node_info (GTK_CTREE (menu_tree), node, &old, &spacing,
			       &closed, &mask_closed, &opened, &mask_opened,
			       &is_leaf, &expanded);
      gtk_ctree_set_node_info (GTK_CTREE (menu_tree), node, item->name, 8,
			       p_array [P_BOOK_CLOSE].pixmap,
			       p_array [P_BOOK_CLOSE].mask,
			       p_array [P_BOOK_OPEN].pixmap,
			       p_array [P_BOOK_OPEN].mask, FALSE, expanded);
   }
   else if (item->action == WM_INCLUDE)
      gtk_ctree_set_node_info (GTK_CTREE (menu_tree), node, item->name, 8,
			       p_array [P_MINIPAGE].pixmap,
			       p_array [P_MINIPAGE].mask,
			       NULL, NULL, TRUE, TRUE);
   else if (item->action == WM_PIPE)
      gtk_ctree_set_node_info (GTK_CTREE (menu_tree), node, item->name, 8,
			       p_array [P_PIPE].pixmap,
			       p_array [P_PIPE].mask,
			       NULL, NULL, TRUE, TRUE);
   else if (item->action == WM_OPEN_MENU)
      gtk_ctree_set_node_info (GTK_CTREE (menu_tree), node, item->name, 8,
			       p_array [P_DIR].pixmap,
			       p_array [P_DIR].mask,
			       NULL, NULL, TRUE, TRUE);
   else if (item->action == WM_EXEC || item->action == WM_SHEXEC)
      gtk_ctree_set_node_info (GTK_CTREE (menu_tree), node, item->name, 8,
			       p_array [P_EXEC].pixmap,
			       p_array [P_EXEC].mask,
			       NULL, NULL, TRUE, TRUE);
   else
      gtk_ctree_set_node_info (GTK_CTREE (menu_tree), node, item->name, 8,
			       p_array [P_GNUSTEP].pixmap,
			       p_array [P_GNUSTEP].mask,
			       NULL, NULL, TRUE, TRUE);
}

static void
open_filebrowser (GtkWidget *button, gpointer data)
{
   static GtkWidget *filesel = NULL;

   if (!filesel)
   {
      GtkFileSelection *fs;

      /*
       *  Fileselection dialog window
       */
      filesel = gtk_file_selection_new (_("Menu file selection"));
      fs      = GTK_FILE_SELECTION (filesel);
      gtk_file_selection_hide_fileop_buttons (fs);
      gtk_window_set_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE);
      gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
                          GTK_SIGNAL_FUNC (gtk_widget_destroyed),
                          &filesel);
      gtk_signal_connect (GTK_OBJECT (fs->ok_button), "clicked",
			  GTK_SIGNAL_FUNC (set_filename),
			  (gpointer) filesel);
      gtk_signal_connect_object (GTK_OBJECT (fs->cancel_button), "clicked",
				 GTK_SIGNAL_FUNC (gtk_widget_destroy),
				 GTK_OBJECT (filesel));
      gtk_signal_connect_object (GTK_OBJECT (fs->ok_button), "clicked",
				 GTK_SIGNAL_FUNC (gtk_widget_destroy),
				 GTK_OBJECT (filesel));
      {
	 const char *filename =
	   gtk_entry_get_text (GTK_ENTRY (include_file_entry));

	 if (streq (filename, ""))
	    gtk_file_selection_set_filename (fs, WMAKERDIR "/");
	 else
	    gtk_file_selection_set_filename (fs, filename);
      }
      
   }

   if (!GTK_WIDGET_VISIBLE (filesel))
      gtk_widget_show (filesel);
   else
      gtk_widget_destroy (filesel);
}

static void
set_filename (GtkWidget *button, gpointer data)
{
   GtkFileSelection *fs = GTK_FILE_SELECTION (data);

   g_signal_handler_block (include_file_entry, include_file_entry_sig);
   gtk_entry_set_text (GTK_ENTRY (include_file_entry),
		       gtk_file_selection_get_filename (fs));
   g_signal_handler_unblock (include_file_entry, include_file_entry_sig);
   gtk_entry_set_position (GTK_ENTRY (include_file_entry), -1);
   toggle_save (menu_changed = YES, NULL);
}

static void
set_entry_text (GtkWidget *entry, gpointer data)
{
   GtkCTreeNode *node  = (GTK_CLIST (menu_tree)->selection)->data;
   item_t	*item  = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree),
						      node);

   if (item->arg)
      Free (item->arg);
   item->arg = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
   toggle_save (menu_changed = YES, NULL);
}

static void
import_menu (GtkWidget *widget, gpointer data)
{
   proplist_t	menu;
   GtkCTreeNode *node = (GTK_CLIST (menu_tree)->selection)->data;
   GtkCTreeNode *new;
   item_t	*item_old = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree),
							 node);
   char		*filename;
   item_t	*item_new;
   
   filename = expand_tilde (data ? data : item_old->arg);
   if (!strlen (filename))
      return;

   if (filename [0] != '/')		/* relative path */
   {
      FILE *file;
      char *tmp = g_strdup_printf ("Library/WindowMaker/%s", filename);
      
      Free (filename);
      filename = get_gnustep_path (tmp);
      Free (tmp);
      file = fopen (filename, "r");
      if (!file)			/* check WMAKERDIR */
      {
	 Free (filename);
	 tmp = expand_tilde (data ? data : item_old->arg);
	 filename = g_strdup_printf ("%s/%s", WMAKERDIR, tmp);
	 Free (tmp);
	 /* error checking is done a few lines below */
      }
      else
      {
	 fclose (file);
      }
   }
   
   menu = convert_to_proplist (filename);
   Free (filename);

   if (!menu)
      return;				/* ERROR */
   
   gtk_clist_freeze (GTK_CLIST (menu_tree));
   new = build_tree (GTK_CTREE_ROW (node)->parent, node, menu);

   WMReleasePropList (menu);

   item_new = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree), new);
   Free (item_new->name);
   if (data)
      item_new->name = g_strdup_printf (_("Output of command '%s'"),
					item_old->arg);
   else
      item_new->name = g_strdup_printf (_("Imported file '%s'"),
					item_old->arg);
      
   {
      char	*old;
      guint8	spacing;
      GdkPixmap	*closed, *opened;
      GdkBitmap	*mask_closed, *mask_opened;
      gboolean	is_leaf, expanded;

      gtk_ctree_get_node_info (GTK_CTREE (menu_tree), new, &old, &spacing,
			       &closed, &mask_closed, &opened, &mask_opened,
			       &is_leaf, &expanded);
      gtk_ctree_set_node_info (GTK_CTREE (menu_tree), new, item_new->name,
			       spacing, closed, mask_closed, opened, mask_opened,
			       is_leaf, expanded);
   }
   remove_node (widget, NULL);
   
   gtk_clist_thaw (GTK_CLIST (menu_tree));
   gtk_ctree_select (GTK_CTREE (menu_tree), new);
   
   gtk_entry_select_region (GTK_ENTRY (itemlabel_entry), 0, -1);
   gtk_widget_grab_focus (itemlabel_entry);
   toggle_save (menu_changed = YES, NULL);
}

#ifdef GNOME_TO_WMAKER	    
static void
set_gnomemenu (GtkWidget *menuitem, gpointer data)
{
   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_PIPE]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (pipe_command_entry), GNOME_TO_WMAKER);
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "GNOME menu");
   toggle_save (menu_changed = YES, NULL);
}
#endif /* GNOME_TO_WMAKER */

#ifdef PERL
#	ifdef KDE_TO_WMAKER	    
static void
set_kdemenu (GtkWidget *menuitem, gpointer data)
{
   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_PIPE]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (pipe_command_entry), KDE_TO_WMAKER);
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "KDE menu");
   toggle_save (menu_changed = YES, NULL);
}
#	endif /* KDE_TO_WMAKER */
#endif /* PERL */

static void
set_wmakerconf (GtkWidget *menuitem, gpointer data)
{
   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_MENU]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "WM Configuration");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show themes");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Themes");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show appearance");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Appearance");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show menu");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Menu");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_MENU]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Settings");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show misc");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Misc");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show paths");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Paths");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show workspace");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Workspace");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show windows");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Windows");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show effects");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Effects");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show mouse");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Mouse");

   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_EXEC]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (commandline_entry),
		       "wmakerconf --show shortcuts");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Shortcuts");

   toggle_save (menu_changed = YES, NULL);
}

#ifdef WMCONFIG
static void
set_wmconfig (GtkWidget *menuitem, gpointer data)
{
   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_PIPE]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (pipe_command_entry),
		       WMCONFIG " --output wmaker");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "RedHat menu");
   toggle_save (menu_changed = YES, NULL);
}
#else
static void
set_debian (GtkWidget *menuitem, gpointer data)
{
   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_INCLUDE]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (include_file_entry), "menu.hook");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Debian menu");
   toggle_save (menu_changed = YES, NULL);
}
#endif /* WMCONFIG */
static void
set_default_menu (GtkWidget *menuitem, gpointer data)
{
   insert_menu (menuitem, "");
   gtk_object_set_user_data (GTK_OBJECT (menuitem),
			     (gpointer) cmd_name [WM_INCLUDE]);
   set_command (menuitem, NULL);
   gtk_entry_set_text (GTK_ENTRY (include_file_entry), WMAKERDIR "/menu");
   gtk_entry_set_text (GTK_ENTRY (itemlabel_entry), "Default menu");
   toggle_save (menu_changed = YES, NULL);
}

static void
import_dynamic_menu (GtkWidget *widget, gpointer data)
{
   GtkCTreeNode *node     = (GTK_CLIST (menu_tree)->selection)->data;
   item_t	*item     = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree),
							 node);
   char		*tmp_name = get_temporary_file_name ();
   char		*command  = g_strdup_printf ("%s > %s", item->arg, tmp_name);
   
   if (system (command))
   {
      dialog_popup (DIALOG_ERROR, NULL, NULL,
		    _("Can't execute dynamic menu command."
		    "\nPlease check `stderr' for more details."));
      Free (command);
      return;
   }
   Free (command);
   import_menu (widget, tmp_name);

   delete_file_or_dir (tmp_name);
   toggle_save (menu_changed = YES, NULL);
}

static void
open_dirbrowser (GtkWidget *button, gpointer data)
{
   static GtkWidget *filesel = NULL;

   if (!filesel)
   {
      GtkFileSelection *fs;

      /*
       *  Fileselection dialog window
       */
      filesel = gtk_file_selection_new (_("Choose directory"));
      fs      = GTK_FILE_SELECTION (filesel);
      gtk_widget_set_sensitive (fs->file_list, FALSE);
      gtk_file_selection_hide_fileop_buttons (fs);
      gtk_window_set_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE);
      gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
                          GTK_SIGNAL_FUNC (gtk_widget_destroyed),
                          &filesel);
      gtk_signal_connect (GTK_OBJECT (fs->ok_button), "clicked",
			  GTK_SIGNAL_FUNC (insert_dir),
			  (gpointer) filesel);
      gtk_signal_connect_object (GTK_OBJECT (fs->cancel_button), "clicked",
				 GTK_SIGNAL_FUNC (gtk_widget_destroy),
				 GTK_OBJECT (filesel));
      gtk_signal_connect_object (GTK_OBJECT (fs->ok_button), "clicked",
				 GTK_SIGNAL_FUNC (gtk_widget_destroy),
				 GTK_OBJECT (filesel));
      {
	 char *filename;
	 
	 if (GTK_CLIST (dir_clist)->rows > 0)
	    gtk_clist_get_text (GTK_CLIST (dir_clist),
				GPOINTER_TO_INT (GTK_CLIST (dir_clist)->selection->data),
				0, &filename);
	 else
	    filename = "";
	 
	 if (streq (filename, ""))
	    gtk_file_selection_set_filename (fs, WMAKERDATADIR "/");
	 else
	    gtk_file_selection_set_filename (fs, filename);
      }
   }

   if (!GTK_WIDGET_VISIBLE (filesel))
      gtk_widget_show (filesel);
   else
      gtk_widget_destroy (filesel);
}

static void
remove_dir (GtkWidget *button, gpointer data)
{
   if (GTK_CLIST (dir_clist)->rows > 1)
   {
      gint row = GPOINTER_TO_INT (GTK_CLIST (dir_clist)->selection->data);

      if (row == GTK_CLIST (dir_clist)->rows - 1) /* last row */
	 gtk_clist_select_row (GTK_CLIST (dir_clist), row - 1, 0);
      else
	 gtk_clist_select_row (GTK_CLIST (dir_clist), row + 1, 0);
      gtk_clist_remove (GTK_CLIST (dir_clist), row);
      if (GTK_CLIST (dir_clist)->rows <= 1)
	 gtk_widget_set_sensitive (button, FALSE);
      dir_list_to_item (button, data);
   }
}

static void
insert_dir  (GtkWidget *button, gpointer data)
{
   GtkFileSelection *fs = GTK_FILE_SELECTION (data);
   char *text 		= g_strdup (gtk_file_selection_get_filename (fs));

   if (strlen (text) > 1 && text [strlen (text) - 1] == '/')
      text [strlen (text) - 1] = 0;

   if (GTK_CLIST (dir_clist)->rows > 0)
      gtk_clist_insert (GTK_CLIST (dir_clist),
			GPOINTER_TO_INT (GTK_CLIST (dir_clist)->selection->data),
			&text);
   else
      gtk_clist_insert (GTK_CLIST (dir_clist), 0, &text);
   Free (text);

   if (GTK_CLIST (dir_clist)->rows > 1)
      gtk_widget_set_sensitive (dir_remove, TRUE);
   dir_list_to_item (button, data);
}

static void
dir_list_to_item (GtkWidget *button, gpointer data)
{
   int		n;
   unsigned	len = 0;
   char		*text;
   const char   *ctext;
   GtkCTreeNode *node = (GTK_CLIST (menu_tree)->selection)->data;
   item_t	*item = gtk_ctree_node_get_row_data (GTK_CTREE (menu_tree), node);

   if (GTK_TOGGLE_BUTTON (suffix_button)->active)
      len += strlen ("-noext ");
   for (n = 0; n < GTK_CLIST (dir_clist)->rows; n++)
   {
      gtk_clist_get_text (GTK_CLIST (dir_clist), n, 0, &text);
      len += strlen (text) + 1;
   }
   if (strlen (ctext = gtk_entry_get_text (GTK_ENTRY (dir_with_entry))) > 0)
   {
      len += strlen ("WITH ");
      len += strlen (ctext) + 1;
   }
   if (!len)
      return;				/* should not happen ;-) */

   if (item->arg)
      Free (item->arg);
   item->arg = Calloc (len, sizeof (char));

   if (GTK_TOGGLE_BUTTON (suffix_button)->active)
      strcpy (item->arg, "-noext ");
   for (n = 0; n < GTK_CLIST (dir_clist)->rows; n++)
   {
      gtk_clist_get_text (GTK_CLIST (dir_clist), n, 0, &text);
      strcat (item->arg, text);
      if (n != GTK_CLIST (dir_clist)->rows - 1)
	 strcat (item->arg, " ");
   }
   if (strlen (ctext = gtk_entry_get_text (GTK_ENTRY (dir_with_entry))) > 0)
   {
      strcat (item->arg, " WITH ");
      strcat (item->arg, ctext);
   }
   toggle_save (menu_changed = YES, NULL);
}

static void
tree_moved (GtkCTree *ctree, GtkCTreeNode *node, GtkCTreeNode *new_parent, 
	    GtkCTreeNode *new_sibling)
{
   toggle_save (menu_changed = YES, NULL);
}

static void
row_moved (GtkWidget *clist, gint source_row, gint dest_row)
{
   dir_list_to_item (clist, NULL);
}

static proplist_t
convert_to_proplist (const char *cfilename)
{
   proplist_t	menu;
   int		c         = 0;
   char         *filename = g_strdup (cfilename);
   char		*tmp_name = NULL;
   FILE		*file     = NULL;
   
   /*
    *  Check if file is already in proplist format
    */
   if (filename[0] == '/')
      file = fopen (filename, "r");
   else /* not an absolute path; look for file in the usual places */
   {
      int i;
      char * prefixes[2];
      prefixes[0] = get_gnustep_path ("Library/WindowMaker/");
      prefixes[1] = g_strdup ("/etc/X11/WindowMaker/");

      for (i = 0; i < 2 && !file; i++)
      {
         Free (filename);
	 filename = g_strconcat (prefixes[i], cfilename, NULL);
	 file = fopen (filename, "r");
      }
      Free (prefixes[0]);
      Free (prefixes[1]);
   }
   
   if (file)
   {
      while ((c = fgetc (file)) != EOF && isspace (c))
         ;
      fclose (file);
   }
   
   if (c == '(')
      menu = read_proplist (filename);
   else
   {
      char *command;
      char *txt = g_strdup_printf (_("Can't convert old style menu\n%s\n"
				     "to menu in proplist format."), filename);

      tmp_name = get_temporary_file_name ();
      command  = g_strdup_printf ("%s/wm-oldmenu2new.sh %s %s", PKGDATADIR,
				  filename, tmp_name);

      Free (filename);
      if (shell_command (command, txt))
      {
	 Free (command);
	 Free (txt);
	 return NULL;
      }
      Free (txt);
      Free (command);
      menu = read_proplist (tmp_name);
   }
   
   if (!menu || !WMIsPLArray (menu))
   {
      dialog_popup (DIALOG_ERROR, NULL, NULL,
		    _("Can't parse generated menu file\n`%s'.\n"
		      "Please check syntax of this file."), tmp_name);
      if (menu)
	 WMReleasePropList (menu);
      
      return NULL;			/* ERROR */
   }
   
   if (tmp_name)
      delete_file_or_dir (tmp_name);
   
   return menu;
}

static void
convert_current_menu (GtkWidget *button, gpointer data)
{
   char *menu = (char *) data;

   gtk_widget_destroy (info_message);
   gtk_box_pack_start (GTK_BOX (notebook_page),
		       real_rootmenu (main_tooltips, convert_to_proplist (menu)),
		       TRUE, TRUE, 0);
   toggle_save (menu_changed = YES, NULL);
}

static void
empty_menu (GtkWidget *button, gpointer data)
{
   gtk_widget_destroy (info_message);
   gtk_box_pack_start (GTK_BOX (notebook_page),
		       real_rootmenu (main_tooltips, NULL), TRUE, TRUE, 0);
   toggle_save (menu_changed = YES, NULL);
}

static void
convert_wmaker_menu (GtkWidget *button, gpointer data)
{
   GtkFileSelection	*fs   = GTK_FILE_SELECTION (data);
   const char		*menu = gtk_file_selection_get_filename (fs);

   gtk_widget_destroy (info_message);
   gtk_box_pack_start (GTK_BOX (notebook_page),
		       real_rootmenu (main_tooltips, convert_to_proplist (menu)),
		       TRUE, TRUE, 0);
   toggle_save (menu_changed = YES, NULL);
}

static void
use_wmaker_menu (GtkWidget *button, gpointer data)
{
   static GtkWidget *filesel = NULL;

   if (!filesel)
   {
      GtkFileSelection *fs;

      /*
       *  Fileselection dialog window
       */
      filesel = gtk_file_selection_new ("Menu file selection");
      fs      = GTK_FILE_SELECTION (filesel);
      gtk_file_selection_hide_fileop_buttons (fs);
      gtk_window_set_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE);
      gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
                          GTK_SIGNAL_FUNC (gtk_widget_destroyed),
                          &filesel);
      gtk_signal_connect (GTK_OBJECT (fs->ok_button), "clicked",
			  GTK_SIGNAL_FUNC (convert_wmaker_menu),
			  (gpointer) filesel);
      gtk_signal_connect_object (GTK_OBJECT (fs->cancel_button), "clicked",
				 GTK_SIGNAL_FUNC (gtk_widget_destroy),
				 GTK_OBJECT (filesel));
      gtk_signal_connect_object (GTK_OBJECT (fs->ok_button), "clicked",
				 GTK_SIGNAL_FUNC (gtk_widget_destroy),
				 GTK_OBJECT (filesel));

      gtk_file_selection_set_filename (fs, WMAKERDIR "/");
      gtk_file_selection_complete (fs, "*menu*");
   }

   if (!GTK_WIDGET_VISIBLE (filesel))
      gtk_widget_show (filesel);
   else
      gtk_widget_destroy (filesel);
}


syntax highlighted by Code2HTML, v. 0.9.1