/*
 *  texture.c:		Texture widgets, color widgets	
 *
 *  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: 2002/01/07 19:14:44 $
 *  $Author: ulli $
 *  $Revision: 1.75 $
 *  $State: Exp $
 */

#include "config.h"

#include <gtk/gtk.h>
#include "proplist_t.h"
#include <ctype.h>

#if HAVE_STRING_H
#	include <string.h>
#else /* not HAVE_STRING_H */
#	include <strings.h>
#endif /* not HAVE_STRING_H */

#include "misc.h"
#include "load.h"
#include "icons.h"
#include "window.h"
#include "dialog.h"
#include "previews.h"
#include "texture.h"
#include "menu.h"
#include "rimage.h"

#include "error.h"

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

		    global variables (imported from window.c)
  
*******************************************************************************/

extern proplist_t  windowmaker;
extern proplist_t  wmconfig;
extern bool_t	    changed;
extern GtkTooltips *tooltips;

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

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

static const char **workspace_names    = NULL;
static const char **ttype_strings      = NULL;
static const char **ptype_strings      = NULL;
static const char **gtype_strings      = NULL;
static const char **option_t_normal    = NULL;
static const char **option_t_wspace    = NULL;
static const char **option_t_wspecific = NULL;
static const char **option_p_normal    = NULL;
static const char **option_p_wspace    = NULL;
static const char **option_g           = NULL;
static       int    wsnumber           = 0;

#define KEY_OF_TEXTURE	(array == WMGetFromPLDictionary (windowmaker, wtexture->key) ? wtexture->key: wtexture->wssback)

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

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

static void
color_right_button (GtkWidget *dummy, gpointer ptr);
static void
update_gradient_preview (wtexture_t *wtexture);
static void
init_types (void);
static gint
list_button_press (GtkWidget *list, GdkEventButton *event, gpointer ptr);
static void
list_change (GtkWidget *widget, GtkWidget *list);
static void
list_remove (GtkWidget *widget, GtkWidget *list);
static void
list_append (GtkWidget *widget, GtkWidget *list);
static void
list_insert (GtkWidget *widget, GtkWidget *list);
static GtkWidget *
new_list_item (wtexture_t *wtexture, proplist_t color);
static void
set_pixmap (GtkWidget *widget, gpointer filesel);
static void
set_pixmap_name (GtkWidget *widget, gpointer ptr);
static void
change_path (GtkWidget *widget, gpointer filesel);
static void
file_selection (GtkWidget *widget, wtexture_t *wtexture);
static void
update_pixmap (GtkWidget *widget, GtkFileSelection *fs);
#ifdef HAVE_LIBWMFUN
static void
update_wmfun_preview (wtexture_t *wtexture, GtkWidget **preview);
static GtkWidget *
generate_bilinear_dialog (wtexture_t *wtexture);
static GtkWidget *
generate_fade_dialog (wtexture_t *wtexture);
static GtkWidget *
generate_waves_dialog (wtexture_t *wtexture);
static void
set_layers (GtkWidget *widget, gpointer ptr);
static void
set_frequency (GtkWidget *widget, gpointer ptr);
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT
static GtkWidget *
generate_interwoven_dialog (wtexture_t *wtexture);
static void
set_thickness_1 (GtkWidget *widget, gpointer ptr);
static void
set_thickness_2 (GtkWidget *widget, gpointer ptr);
static void
update_interwoven_preview (wtexture_t *wtexture, GtkWidget **preview);
#endif /* HAVE_INTERWOVEN_GRADIENT */
static GtkWidget *
generate_pixmap_dialog (wtexture_t *wtexture);
static GtkWidget *
generate_simple_gradient (wtexture_t *wtexture);
static GtkWidget *
generate_gradient_dialog (wtexture_t *wtexture);
static GtkWidget *
generate_default_color (wtexture_t *wtexture);
static void
show_texture_dialog (wtexture_t *wtexture);
static void
gradient_type_toggle (GtkWidget *widget, gpointer ptr);
static void
pixmap_type_toggle (GtkWidget *widget, gpointer ptr);
static void
texture_type_toggle (GtkWidget *widget, gpointer ptr);
static void
color_selection_dialog (GtkWidget *widget, proplist_t key);
static void
color_selection_ok (GtkWidget *w, GtkColorSelection *colorsel);
static void
color_selection_cancel (GtkWidget *w, GtkColorSelection *colorsel);
static void
set_opacity (GtkWidget *widget, gpointer ptr);
static char **
get_workspace_names (void);
static void
workspace_toggle (GtkWidget *widget, gpointer ptr);
static proplist_t
get_texture_array (wtexture_t *wtexture);
static void
update_texture (proplist_t key, gpointer ptr, proplist_t value,
		const char *path);
static void
combine_path_and_filename (proplist_t array, wtexture_t *wtexture);

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

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

GtkWidget *
texture_dialog (GtkWidget *box, GtkWidget *omenu_box, proplist_t key)
/*
 *  Generate a new texture dialog widget and pack widget into given 'box'.
 *
 *  No return value.
 */ 
{
   wtexture_t *wtexture;
   GtkWidget  *dialog;
   GtkWidget  *table;
   GtkWidget  *vbox1, *vbox2;
   
   if (!ttype_strings || !ptype_strings || !gtype_strings)
      init_types ();

   wtexture              = Calloc (1, sizeof (wtexture_t));
   wtexture->wpixmap     = Calloc (PIXMAP_LAST, sizeof (GtkWidget *));
   wtexture->wgradient   = Calloc (GRADIENT_LAST, sizeof (GtkWidget *));
   wtexture->wsgradient  = Calloc (SGRADIENT_LAST, sizeof (GtkWidget *));
#ifdef HAVE_LIBWMFUN
   wtexture->wbilinear   = Calloc (BILINEAR_LAST, sizeof (GtkWidget *));
   wtexture->wfade 	 = Calloc (FADE_LAST, sizeof (GtkWidget *));
   wtexture->wwaves  	 = Calloc (WAVES_LAST, sizeof (GtkWidget *));
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT
   wtexture->winterwoven = Calloc (INTERWOVEN_LAST, sizeof (GtkWidget *));
#endif /* INTERWOVEN_FRAME */
   wtexture->key	 = key;
   wtexture->wssback	 = WMCreatePLString ("WorkspaceSpecificBack");
   wtexture->wsnumber	 = 0;
   wtexture->path	 = NULL;
   
   {
      GtkWidget  *vbox = gtk_vbox_new (FALSE, 5);
      GtkWidget  *hbox = gtk_hbox_new (FALSE, 0);
      
      table = gtk_table_new (1, 2, TRUE);
      gtk_table_set_row_spacings (GTK_TABLE (table), 0);
      gtk_table_set_col_spacings (GTK_TABLE (table), 5);

      gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 5);
      gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 0);
      hbox = gtk_hbox_new (FALSE, 0);
      
      gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
      dialog = vbox;
   }
   
   vbox1 = gtk_vbox_new (FALSE, 5);
   vbox2 = gtk_vbox_new (FALSE, 0);

   gtk_table_attach (GTK_TABLE (table), vbox1, 0, 1, 0, 1,
		     GTK_SHRINK | GTK_EXPAND | GTK_FILL,
		     GTK_SHRINK | GTK_EXPAND | GTK_FILL, 0, 0);
   gtk_table_attach (GTK_TABLE (table), vbox2, 1, 2, 0, 1,
		     GTK_SHRINK | GTK_EXPAND | GTK_FILL,
		     GTK_SHRINK | GTK_EXPAND | GTK_FILL, 0, 0);
   gtk_widget_show_all (dialog);

   /*
    *  Texture type
    */
   if (omenu_box && strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key)))
   {
      GtkWidget *frame = gtk_frame_new (_("Workspace"));
      GtkWidget *vbox  = gtk_vbox_new (FALSE, 5);

      workspace_names = (const char **) get_workspace_names ();
	 
      gtk_box_pack_start (GTK_BOX (vbox),
			  generate_option_menu (NULL, tooltips,
						_("For each workspace a different"
						  " texture can be specified. "
						  "The texture of the default "
						  "workspace is used if the "
						  "texture type is set"
						  " to 'default' for a specific "
						  "workspace."), workspace_names,
						"Default", NULL,
						workspace_toggle, wtexture),
			  FALSE, FALSE, 0);
	 
      gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
      gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
      gtk_container_add (GTK_CONTAINER (frame), vbox);

      gtk_widget_show_all (frame);
      gtk_box_pack_start (GTK_BOX (omenu_box), frame, FALSE, TRUE, 5);
   }
   {
      GtkWidget *vbox  = gtk_vbox_new (FALSE, 5);
      
      gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
      
      if (strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key)))
	 wtexture->wttype
	    = generate_option_menu (NULL, tooltips,
				    _("A solid color, a pixmap, or a gradient. "
				      "If 'None' is selected, then the root "
				      "background is not changed by Window "
				      "Maker. If 'Default' is selected, then "
				      "the texture of the default workspace "
				      "is used"),
				    option_t_wspecific, option_t_wspecific [0],
				    NULL, texture_type_toggle, wtexture);
      else
	 wtexture->wttype
	    = generate_option_menu (NULL, tooltips,
				    _("A solid color, a pixmap, or a gradient."),
				    option_t_normal, option_t_normal [0], NULL,
				    texture_type_toggle, wtexture);
      
      gtk_box_pack_start (GTK_BOX (vbox), wtexture->wttype, FALSE, FALSE, 0);
      
      if (strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key)))
      {
	 wtexture->wwttype
	    = generate_option_menu (NULL, tooltips,
				    _("A solid color, a pixmap, or a gradient. "
				      "If 'None' is selected, then the root "
				      "background is not changed by Window "
				      "Maker."),
				    option_t_wspace, option_t_wspace [0], NULL,
				    texture_type_toggle, wtexture);
	 gtk_box_pack_start (GTK_BOX (vbox), wtexture->wwttype, FALSE, FALSE, 0);
      }

      {
	 GtkWidget *frame = gtk_frame_new (_("Type"));
	 
	 gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
	 gtk_container_add (GTK_CONTAINER (frame), vbox);
	 
	 gtk_box_pack_start (GTK_BOX (vbox1), frame, FALSE, TRUE, 0);
	 gtk_widget_show_all (frame);
      }
   }

   gtk_box_pack_start (GTK_BOX (vbox1), generate_default_color (wtexture),
		       TRUE, TRUE, 0);
   gtk_box_pack_start (GTK_BOX (vbox1), generate_simple_gradient (wtexture),
		       TRUE, TRUE, 0);
#ifdef HAVE_LIBWMFUN
   gtk_box_pack_start (GTK_BOX (vbox1), generate_bilinear_dialog (wtexture),
		       TRUE, TRUE, 0);
   gtk_box_pack_start (GTK_BOX (vbox1), generate_fade_dialog (wtexture),
		       TRUE, TRUE, 0);
   gtk_box_pack_start (GTK_BOX (vbox1), generate_waves_dialog (wtexture),
		       TRUE, TRUE, 0);
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT
   gtk_box_pack_start (GTK_BOX (vbox1), generate_interwoven_dialog (wtexture),
		       TRUE, TRUE, 0);
#endif /* HAVE_INTERWOVEN_GRADIENT */

   gtk_box_pack_start (GTK_BOX (vbox2),
		       generate_pixmap_dialog (wtexture), TRUE, TRUE, 0);
   gtk_box_pack_start (GTK_BOX (vbox2),
		       generate_gradient_dialog (wtexture), TRUE, TRUE, 0);
   show_texture_dialog (wtexture);

   connect_update_function (key, wtexture, update_texture);

   return dialog;
}

GtkWidget *
color_button (wtexture_t *wtexture, proplist_t key, proplist_t color, 
	      GtkTooltips *tooltips, const char *info)
/*
 *  Generate color button.
 *  The color value (in proplist format) is assigned to preview.
 */
{
   GtkWidget *frame   = gtk_frame_new (NULL);
   GtkWidget *preview = gtk_preview_new (GTK_PREVIEW_COLOR);
   GtkWidget *button  = gtk_button_new ();

   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
   gtk_container_set_border_width (GTK_CONTAINER (frame), 3);
		  
   gtk_preview_size (GTK_PREVIEW (preview), 30, 15);
   gtk_container_add (GTK_CONTAINER (frame), preview);
   gtk_object_set_user_data (GTK_OBJECT (preview), (gpointer) color);
   gtk_object_set_data (GTK_OBJECT (preview), "wtexture", wtexture);
   fill_preview (GTK_PREVIEW (preview), color ? WMGetFromPLString (color) : "tan");

   if (info)
      gtk_tooltips_set_tip (tooltips, button, info, NULL); 

   gtk_container_add (GTK_CONTAINER (button), frame);
   gtk_object_set_user_data (GTK_OBJECT (button), preview);
   gtk_signal_connect (GTK_OBJECT (button), "clicked",
		       GTK_SIGNAL_FUNC (color_selection_dialog),
		       (gpointer) key);

   gtk_widget_show_all (button);

   gtk_object_set_user_data (GTK_OBJECT (button), preview);

   return button;
}

void
fill_preview (GtkPreview *preview, const char *col_spec)
/*
 *  Fill a 'preview' with color 'col_spec'.
 *
 *  No return value.
 */
{
   unsigned	 y;
   GdkColor	 color;
   unsigned	 width   = GTK_WIDGET (preview)->requisition.width;
   unsigned	 height  = GTK_WIDGET (preview)->requisition.height;
   unsigned char *buffer = Calloc (width * 3, sizeof (unsigned char));
   
   if (!make_color (col_spec, &color))
      color.red = color.green = color.blue = 65535;

   for (y = 0; y < height; y++)
   {
      unsigned i;
      unsigned char *ptr = buffer;
			   
      for (i = 0; i < width; i++)
      {
	 *ptr++ = color.red / 256;
	 *ptr++ = color.green / 256;
	 *ptr++ = color.blue / 256;
      }

      gtk_preview_draw_row (preview, buffer, 0, y, width);
   }
   Free (buffer);
   gtk_widget_draw (GTK_WIDGET (preview), NULL);
}

GtkWidget *
show_pixmap (proplist_t key, const char *name, GtkWidget *pixmap)
{
   const char *keyname = WMGetFromPLString (key);
   proplist_t  array   = WMGetFromPLDictionary (windowmaker, key);

   /* If we are modifying the WorkspaceSpecificBack array, ensure that
    * there are sufficient array elements to hold all the workspace textures,
    * then pass the appropriate sub-array as argument.
    * This is an ugly hack -- really the workspace-specific code should all
    * be rewritten. */
   if (strcaseeq (keyname, "workspacespecificback") && WMIsPLArray (array)) {
     assert (wsnumber > 0);
     if (wsnumber > WMGetPropListItemCount (array)) {
       int i = WMGetPropListItemCount (array);
       for ( ; i < wsnumber; i++)
        WMAddToPLArray (array, WMCreatePLArray (NULL));
     }
     if (WMIsPLArray (WMGetFromPLArray (array, wsnumber - 1)))
       array = WMGetFromPLArray (array, wsnumber - 1);
   }

   if (!WMIsPLArray (array) || WMGetPropListItemCount (array) != 5
#if !defined(PREVIEWS) || defined(CONVERT)
        || WMGetPropListItemCount (array) != 3
#endif /* !defined(PREVIEWS) || defined(CONVERT) */
       )
   {
      if (strcaseeq (keyname, "workspaceback") ||
	  strcaseeq (keyname, "workspacespecificback"))
	 return make_pixmap (name, 180, 180 * 3 / 4, pixmap);
      else if (strcaseeq (keyname + 1, "titleback")
	       || strcaseeq (keyname, "menutitleback")
	       || strcaseeq (keyname, "menutextback"))
	 return make_pixmap (name, 180, 22, pixmap);
      else
	 return make_pixmap (name, 64, 64, pixmap);
   }
#if defined(PREVIEWS) && !defined(CONVERT)
   else /* textured gradient: colored gradient over pixmap */
   {
      gtype_e	gtype;
      char	*type = WMGetFromPLString (WMGetFromPLArray (array, 0));
      
      if (*(type + 1) == 'h')
	 gtype = HGRADIENT;
      else if (*(type + 1) == 'v')
	 gtype = VGRADIENT;
      else
	 gtype = DGRADIENT;

      if (strcaseeq (keyname, "workspaceback") ||
	  strcaseeq (keyname, "workspacespecificback"))
	 return make_textured_gradient (get_pixmap_path (name), array,
					180, 180 * 3 / 4, gtype, pixmap);
      else if (strcaseeq (keyname + 1, "titleback")
	       || strcaseeq (keyname, "menutitleback")
	       || strcaseeq (keyname, "menutextback"))
	 return make_textured_gradient (get_pixmap_path (name), array,
					180, 22, gtype, pixmap);
      else
	 return make_textured_gradient (get_pixmap_path (name), array,
					64, 64, gtype, pixmap);
   }
#endif /* defined(PREVIEWS) && !defined(CONVERT) */
   return pixmap;
}

void
verify_pixmap_name (GtkWidget *widget, wtexture_t *wtexture)
/*
 *  Verify if typed pixmap name is valid.
 *
 *  No return value.
 *
 *  Side effects:
 *	pixmap is changed 
 */
{
   const char  *filename = gtk_entry_get_text (GTK_ENTRY (widget));
   proplist_t	array     = get_texture_array (wtexture);

   show_pixmap (KEY_OF_TEXTURE, filename, wtexture->wpixmap [PIXMAP_PIXMAP]);
}

void
cleanup_textures (void)
{
   Free (workspace_names);
   Free (ttype_strings);
   Free (ptype_strings);
   Free (gtype_strings);
   Free (option_t_normal);
   Free (option_t_wspace);
   Free (option_t_wspecific);
   Free (option_p_normal);
   Free (option_p_wspace);
   Free (option_g);
}


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

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

static GtkWidget *
generate_pixmap_dialog (wtexture_t *wtexture)
/*
 *  Generate all widgets for the pixmap dialog.
 *
 *  Return value:
 *	Container widget of pixmap dialog.
 */
{
   GtkWidget	*frame;
   const char	*name = WMGetFromPLString (wtexture->key);
   
   wtexture->wpixmap [PIXMAP_FRAME] = frame = gtk_frame_new (_("Pixmap"));
   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);

   if (strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key)))
      wtexture->wpixmap [PIXMAP_TYPE]
	 = generate_option_menu (NULL,
				 tooltips,
				 _("Centered: "
				   "The pixmap will be shown centered.\n"
				   "Tiled: The pixmap will be tiled in the "
				   "destination.\n"
				   "Scaled: The pixmap will be scaled to "
				   "adapt to the destination's size.\n"
				   "Scaled (x:y): The pixmap will be scaled to "
				   "adapt to the destination's size but"
				   " will maintain aspect ratio."),   
				 option_p_wspace, option_p_wspace [0], NULL,
				 pixmap_type_toggle, wtexture);
   else
      wtexture->wpixmap [PIXMAP_TYPE]
	 = generate_option_menu (NULL, tooltips,
				 _("Centered: "
				   "The pixmap will be shown centered.\n"
				   "Tiled: The pixmap will be tiled in the "
				   "destination.\n"
				   "Scaled: The pixmap will be scaled to "
				   "adapt to the destination's size."),   
				 option_p_normal, option_p_normal [0], NULL,
				 pixmap_type_toggle, wtexture);
   
   if (!strcaseeq (name, "iconback"))
   {
      GtkWidget *table = gtk_table_new (5, 1, FALSE);

      gtk_table_set_row_spacings (GTK_TABLE (table), 5);
      gtk_table_set_col_spacings (GTK_TABLE (table), 0);
      gtk_container_set_border_width (GTK_CONTAINER (table), 5);
      gtk_container_add (GTK_CONTAINER (frame), table);

      gtk_table_attach (GTK_TABLE (table), wtexture->wpixmap [PIXMAP_TYPE],
			0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);

      /*
       *  Frame for pixmap
       */
      wtexture->wpixmap [PIXMAP_PIXMAP]
	 = show_pixmap (wtexture->key, PKGDATADIR "/black.xpm", NULL);
      if (strcaseeq (name, "workspaceback") ||
	  strcaseeq (name, "workspacespecificback"))
      {
	 GtkWidget *frame = gtk_frame_new (NULL);
	 GtkWidget *hbox  = gtk_hbox_new (TRUE, 0);
      
	 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
	 gtk_container_add (GTK_CONTAINER (frame),
			    wtexture->wpixmap [PIXMAP_PIXMAP]);

	 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
	 gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
	 gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 1, 2,
			   GTK_SHRINK, GTK_SHRINK, 0, 0);
      }
      else
	 gtk_table_attach (GTK_TABLE (table), wtexture->wpixmap [PIXMAP_PIXMAP],
			   0, 1, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
	 
      /*
       *  Filename entry
       */
      {
	 GtkWidget *entry;
	 GtkWidget *hbox = gtk_hbox_new (TRUE, 0);
	 guint      entry_sig_handler;

	 wtexture->wpixmap [PIXMAP_NAME] = entry = gtk_entry_new ();
   	 entry_sig_handler = gtk_signal_connect
	                    (GTK_OBJECT (entry), "changed",
			     GTK_SIGNAL_FUNC (set_pixmap_name), wtexture);
	 gtk_object_set_user_data (GTK_OBJECT (entry),
				   GINT_TO_POINTER (entry_sig_handler));
	 gtk_signal_connect (GTK_OBJECT (entry), "activate",
			     GTK_SIGNAL_FUNC (verify_pixmap_name),  wtexture);
	 gtk_tooltips_set_tip (tooltips, entry,
			       _("Filename of the pixmap. <RETURN> checks if "
				 "the pixmap filename is valid."), NULL);

	 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
	 gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 2, 3,
			   GTK_SHRINK | GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
      }
      /*
       *  Buttons
       */
      {
	 GtkWidget *hbox, *button;

	 hbox = gtk_hbutton_box_new ();
	 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox),
				    GTK_BUTTONBOX_SPREAD);
	 
	 gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 3, 4,
			   GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 5);

	 button = gtk_button_new_with_label (_("Browse..."));
	 gtk_signal_connect (GTK_OBJECT (button), "clicked",
			     GTK_SIGNAL_FUNC (file_selection), wtexture);
	 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
	 gtk_tooltips_set_tip (tooltips, button,
			       _("Use file browser to select pixmap."), NULL);

#ifdef PREVIEWS	 
	 button = gtk_button_new_with_label (_("Previews..."));
	 gtk_signal_connect (GTK_OBJECT (button), "clicked",
			     GTK_SIGNAL_FUNC (preview_browser), wtexture);
	 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
	 gtk_tooltips_set_tip (tooltips, button,
			       _("Use preview browser to select pixmap."),
			       NULL);
#endif /* PREVIEWS */
      }
   }
   else
   {
      GtkWidget	*table = gtk_table_new (3, 2, FALSE);
      
      gtk_table_set_row_spacings (GTK_TABLE (table), 5);
      gtk_table_set_col_spacings (GTK_TABLE (table), 5);
      gtk_container_set_border_width (GTK_CONTAINER (table), 5);
      gtk_container_add (GTK_CONTAINER (frame), table);
      
      gtk_table_attach (GTK_TABLE (table), wtexture->wpixmap [PIXMAP_TYPE],
			0, 2, 0, 1,
			GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);

      {
	 GtkWidget	 *frame	   = gtk_frame_new (NULL);
	 GtkWidget	 *vbox     = gtk_vbox_new (TRUE, 0);
	 GtkWidget	 *hbox     = gtk_hbox_new (TRUE, 0);
      
	 wtexture->wpixmap [PIXMAP_PIXMAP]
	    = show_pixmap (wtexture->key, PKGDATADIR "/black.xpm", NULL);
	 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
	 gtk_container_add (GTK_CONTAINER (frame),
			    wtexture->wpixmap [PIXMAP_PIXMAP]);
	 gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0);
	 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, TRUE, 0);
	 gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 1, 2,
			   GTK_SHRINK, GTK_SHRINK, 0, 0);
      }
      /*
       *  Filename entry
       */
      {
	 GtkWidget	*entry;
	 guint           entry_sig_handler;

	 wtexture->wpixmap [PIXMAP_NAME] = entry = gtk_entry_new ();
   	 entry_sig_handler = gtk_signal_connect
	                    (GTK_OBJECT (entry), "changed",
			     GTK_SIGNAL_FUNC (set_pixmap_name), wtexture);
	 gtk_object_set_user_data (GTK_OBJECT (entry),
				   GINT_TO_POINTER (entry_sig_handler));
	 gtk_signal_connect (GTK_OBJECT (entry), "activate",
			     GTK_SIGNAL_FUNC (verify_pixmap_name),  wtexture);
	 gtk_table_attach (GTK_TABLE (table), entry, 0, 2, 2, 3,
			   GTK_FILL | GTK_SHRINK | GTK_EXPAND,
			   GTK_FILL | GTK_SHRINK, 0, 0);
	 gtk_tooltips_set_tip (tooltips, entry,
			       _("Filename of the pixmap. <RETURN> checks if "
				 "the pixmap filename is valid."), NULL);
      }
   
      /*
       *  Browse button
       */
      {
	 GtkWidget	*vbox, *button;

	 vbox = gtk_vbutton_box_new ();
	 gtk_button_box_set_layout (GTK_BUTTON_BOX (vbox),
				    GTK_BUTTONBOX_SPREAD);
	 gtk_table_attach (GTK_TABLE (table), vbox, 1, 2, 1, 2,
			   GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);

	 button = gtk_button_new_with_label (_("Browse..."));
	 gtk_signal_connect (GTK_OBJECT (button), "clicked",
			     GTK_SIGNAL_FUNC (file_selection), wtexture);
	 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 0);
	 gtk_tooltips_set_tip (tooltips, button,
			       _("Use file browser to select pixmap."), NULL);

#ifdef PREVIEWS	 
	 button = gtk_button_new_with_label (_("Previews..."));
	 gtk_signal_connect (GTK_OBJECT (button), "clicked",
			     GTK_SIGNAL_FUNC (preview_browser), wtexture);
	 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 0);
	 gtk_tooltips_set_tip (tooltips, button,
			       _("Use preview browser to select pixmap."),
			       NULL);
#endif /* PREVIEWS */
      }
   }

   gtk_widget_show_all (frame);

   return frame;
}

static GtkWidget *
generate_simple_gradient (wtexture_t *wtexture)
/*
 *  Generate all widgets for the simple gradient dialog.
 *
 *  Return value:
 *	Container widget of simple gradient dialog.
 */
{
   GtkWidget	*frame = gtk_frame_new (_("Gradient"));
   GtkWidget	*vbox  = gtk_vbox_new (FALSE, 0);
   GtkWidget	*hbox;

   wtexture->wsgradient [SGRADIENT_FRAME] = frame;
   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
   gtk_container_add (GTK_CONTAINER (frame), vbox);
   gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
   
   wtexture->wsgradient [SGRADIENT_TYPE]
      = generate_option_menu (NULL, tooltips,
			      _("Horizontal: A gradient going from left to "
				"right.\n"
				"Vertical: A gradient going from top to "
				"bottom.\n"
				"Diagonal: A gradient going from the top-left"
				" corner to the bottom-right corner.\n"
				"Always from color 1 to color 2"),
				option_g, option_g [0], NULL,
			      gradient_type_toggle, wtexture);
   
   gtk_box_pack_start (GTK_BOX (vbox), wtexture->wsgradient [SGRADIENT_TYPE],
		       FALSE, TRUE, 0);
   
   hbox = gtk_hbox_new (FALSE, 5);
   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
   gtk_widget_show (hbox);

   gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Color 1: ")),
		       FALSE, TRUE, 0);
   wtexture->wsgradient [SGRADIENT_COLOR1]
      = color_button (wtexture, wtexture->key, NULL, NULL, NULL);
   gtk_box_pack_start (GTK_BOX (hbox), wtexture->wsgradient [SGRADIENT_COLOR1],
		       FALSE, TRUE, 0);

   wtexture->wsgradient [SGRADIENT_COLOR2]
      = color_button (wtexture, wtexture->key, NULL, NULL, NULL);
   gtk_box_pack_end (GTK_BOX (hbox), wtexture->wsgradient [SGRADIENT_COLOR2],
		     FALSE, TRUE, 0);
   gtk_box_pack_end (GTK_BOX (hbox), gtk_label_new (_("Color 2: ")),
		       FALSE, TRUE, 0);
   
   hbox = gtk_hbox_new (FALSE, 5);
   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
   gtk_widget_show (hbox);

   gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Opacity:")),
		       FALSE, TRUE, 0);
   {
      GtkObject	*adj = gtk_adjustment_new (0, 0, 255, 1.0, 10.0, 0.0);
      GtkWidget	*spinner;
      
      spinner = wtexture->wsgradient [SGRADIENT_SPINNER]
	      = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0);
      gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_widget_show_all (spinner);
      gtk_object_set_user_data (GTK_OBJECT (adj), (gpointer) spinner);
      gtk_box_pack_start (GTK_BOX (hbox), spinner, TRUE, TRUE, 0);
      gtk_tooltips_set_tip (tooltips, spinner,
			    _("Opacity of the pixmap. If set to zero, only the"
			      " pixmap is visible (and vice versa)."), NULL);  

      gtk_signal_connect (GTK_OBJECT (&GTK_SPIN_BUTTON (spinner)->entry),
			  "changed", GTK_SIGNAL_FUNC (set_opacity), wtexture);
      gtk_widget_set_usize (spinner, 100, -1);
   }
   
   gtk_widget_show_all (frame);

   return frame;
}

static GtkWidget *
generate_default_color (wtexture_t *wtexture)
/*
 *  Generate default color dialog.
 *
 *  Return value:
 *	Container widget of default color dialog.
 */
{
   GtkWidget	*frame = gtk_frame_new (_("Default color"));
   GtkWidget	*box   = gtk_hbox_new (TRUE, 0);
   GtkWidget	*box1  = gtk_vbox_new (FALSE, 0);

   wtexture->wcolorbox = frame;
   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
   gtk_container_set_border_width (GTK_CONTAINER (box), 5);

   wtexture->wcolor = color_button (wtexture, wtexture->key, NULL,
				    tooltips,
				    _("This color is used as solid color,"
				      " if a gradient can't be drawn, or during "
				      "the pixmap scaling/tiling process."));  
   gtk_box_pack_start (GTK_BOX (box1), wtexture->wcolor, FALSE, FALSE, 0);
   gtk_box_pack_start (GTK_BOX (box), box1, FALSE, FALSE, 0);
   gtk_container_add (GTK_CONTAINER (frame), box);

   gtk_widget_show_all (frame);

   return frame;
}

#ifdef HAVE_LIBWMFUN

static GtkWidget *
generate_bilinear_dialog (wtexture_t *wtexture)
/*
 *  Generate bilinear gradient dialog.
 *
 *  Return value:
 *	Container widget of bilinear gradient dialog.
 */
{
   GtkWidget *frame = gtk_frame_new (_("Bilinear gradient"));
   GtkWidget *table = gtk_table_new (3, 2, FALSE);
   GtkWidget *hbox  = gtk_hbox_new (TRUE, 0);
   GtkWidget *vbox  = gtk_hbox_new (FALSE, 0);
   
   wtexture->wbilinear [BILINEAR_FRAME] = frame;
   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
   gtk_container_add (GTK_CONTAINER (frame), hbox);
   gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
   gtk_container_set_border_width (GTK_CONTAINER (table), 5);
   gtk_table_set_row_spacings (GTK_TABLE (table), 5);
   gtk_table_set_col_spacings (GTK_TABLE (table), 5);
   gtk_container_set_border_width (GTK_CONTAINER (table), 5);
   
   wtexture->wbilinear [BILINEAR_NW]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("Color of the upper left corner."));
   wtexture->wbilinear [BILINEAR_NE]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("Color of the upper right corner."));
   wtexture->wbilinear [BILINEAR_SW]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("Color of the lower left corner."));
   wtexture->wbilinear [BILINEAR_SE]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("Color of the lower right corner."));
   wtexture->wbilinear [BILINEAR_PREVIEW] = NULL;
   update_wmfun_preview (wtexture, &wtexture->wbilinear [BILINEAR_PREVIEW]);

   hbox  = gtk_hbox_new (FALSE, 0);
   gtk_box_pack_start (GTK_BOX (hbox), wtexture->wbilinear [BILINEAR_NW],
		       FALSE, FALSE, 0);
   gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 0, 1,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND,
		     GTK_FILL | GTK_SHRINK, 0, 0);
   hbox  = gtk_hbox_new (FALSE, 0);
   gtk_box_pack_end (GTK_BOX (hbox), wtexture->wbilinear [BILINEAR_NE],
		       FALSE, FALSE, 0);
   gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 0, 1,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND,
		     GTK_FILL | GTK_SHRINK, 0, 0);
   hbox  = gtk_hbox_new (FALSE, 0);
   gtk_box_pack_start (GTK_BOX (hbox), wtexture->wbilinear [BILINEAR_SW],
		       FALSE, FALSE, 0);
   gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 2, 3,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND,
		     GTK_FILL | GTK_SHRINK, 0, 0);
   hbox  = gtk_hbox_new (FALSE, 0);
   gtk_box_pack_end (GTK_BOX (hbox), wtexture->wbilinear [BILINEAR_SE],
		       FALSE, FALSE, 0);
   gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 2, 3,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND,
		     GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table), wtexture->wbilinear [BILINEAR_PREVIEW],
		     0, 2, 1, 2,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);

   gtk_widget_show_all (frame);

   return frame;
}

static GtkWidget *
generate_fade_dialog (wtexture_t *wtexture)
/*
 *  Generate fading gradient dialog.
 *
 *  Return value:
 *	Container widget of fading gradient dialog.
 */
{
   GtkWidget *frame = gtk_frame_new (_("Fading vertical gradient"));
   GtkWidget *table = gtk_table_new (3, 1, FALSE);
   GtkWidget *hbox  = gtk_hbox_new (TRUE, 0);
   GtkWidget *vbox  = gtk_hbox_new (FALSE, 0);

   wtexture->wfade [FADE_FRAME] = frame;
   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
   gtk_container_add (GTK_CONTAINER (frame), hbox);
   gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
   gtk_table_set_row_spacings (GTK_TABLE (table), 5);
   gtk_table_set_col_spacings (GTK_TABLE (table), 5);
   gtk_container_set_border_width (GTK_CONTAINER (table), 5);
 
   wtexture->wfade [FADE_FROM]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("First color of gradient."));
   wtexture->wfade [FADE_TO]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("Last color of gradient."));
   wtexture->wfade [FADE_PREVIEW] = NULL;
   update_wmfun_preview (wtexture, &wtexture->wfade [FADE_PREVIEW]);

   hbox  = gtk_hbox_new (TRUE, 0);
   gtk_box_pack_start (GTK_BOX (hbox), wtexture->wfade [FADE_FROM],
		       FALSE, FALSE, 0);
   gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 0, 1,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND,
		     GTK_FILL | GTK_SHRINK, 0, 0);
   hbox  = gtk_hbox_new (TRUE, 0);
   gtk_box_pack_start (GTK_BOX (hbox), wtexture->wfade [FADE_TO],
		     FALSE, FALSE, 0);
   gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 2, 3,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND,
		     GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table), wtexture->wfade [FADE_PREVIEW],
		     0, 1, 1, 2,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);

   gtk_widget_show_all (frame);

   return frame;
}

static GtkWidget *
generate_waves_dialog (wtexture_t *wtexture)
/*
 *  Generate waves gradient dialog.
 *
 *  Return value:
 *	Container widget of waves gradient dialog.
 */
{
   GtkWidget *frame = gtk_frame_new (_("Waves gradient"));
   GtkWidget *table = gtk_table_new (2, 4, FALSE);
   GtkWidget *hbox  = gtk_hbox_new (TRUE, 0);
   GtkWidget *vbox  = gtk_hbox_new (FALSE, 0);

   wtexture->wwaves [WAVES_FRAME] = frame;
   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
   gtk_container_add (GTK_CONTAINER (frame), hbox);
   gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
   gtk_container_set_border_width (GTK_CONTAINER (table), 5);
   gtk_table_set_row_spacings (GTK_TABLE (table), 5);
   gtk_table_set_col_spacings (GTK_TABLE (table), 5);
 

   wtexture->wwaves [WAVES_FROM]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("First color of gradient."));
   wtexture->wwaves [WAVES_TO]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("Last color of gradient."));
   wtexture->wwaves [WAVES_PREVIEW] = NULL;

   {
      GtkObject	*adj = gtk_adjustment_new (0, 0, 255, 1.0, 10.0, 0.0);
      GtkWidget	*spinner;
      
      spinner = wtexture->wwaves [WAVES_LAYERS]
	      = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0);
      gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_widget_show_all (spinner);
      gtk_object_set_user_data (GTK_OBJECT (adj), (gpointer) spinner);
      gtk_tooltips_set_tip (tooltips, spinner,
			    _("Number of sine layers."), NULL);  

      gtk_signal_connect (GTK_OBJECT (&GTK_SPIN_BUTTON (spinner)->entry),
			  "changed", GTK_SIGNAL_FUNC (set_layers), wtexture);
   }
   {
      GtkObject	*adj = gtk_adjustment_new (0, 0, 255, 1.0, 10.0, 0.0);
      GtkWidget	*spinner;
      
      spinner = wtexture->wwaves [WAVES_FREQUENCY]
	      = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0);
      gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_widget_show_all (spinner);
      gtk_object_set_user_data (GTK_OBJECT (adj), (gpointer) spinner);
      gtk_tooltips_set_tip (tooltips, spinner,
			    _("Frequency range of sine layers."), NULL);  

      gtk_signal_connect (GTK_OBJECT (&GTK_SPIN_BUTTON (spinner)->entry),
			  "changed", GTK_SIGNAL_FUNC (set_frequency),
			  wtexture);
   }

   update_wmfun_preview (wtexture, &wtexture->wwaves [WAVES_PREVIEW]);
   
   gtk_table_attach (GTK_TABLE (table), wtexture->wwaves [WAVES_FROM],
		     0, 1, 0, 1,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table), wtexture->wwaves [WAVES_TO],
		     1, 2, 0, 1,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table), wtexture->wwaves [WAVES_LAYERS],
		     2, 3, 0, 1,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table), wtexture->wwaves [WAVES_FREQUENCY],
		     3, 4, 0, 1,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table), wtexture->wwaves [WAVES_PREVIEW],
		     0, 4, 1, 2,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);

   gtk_widget_show_all (frame);

   return frame;
}

#endif /* HAVE_LIBWMFUN */

#ifdef HAVE_INTERWOVEN_GRADIENT

static GtkWidget *
generate_interwoven_dialog (wtexture_t *wtexture)
/*
 *  Generate interwoven gradient dialog.
 *
 *  Return value:
 *	Container widget of interwoven gradient dialog.
 */
{
   GtkWidget *frame = gtk_frame_new (_("Interwoven gradient"));
   GtkWidget *table = gtk_table_new (3, 3, FALSE);
   GtkWidget *hbox  = gtk_hbox_new (TRUE, 0);
   GtkWidget *vbox  = gtk_hbox_new (FALSE, 0);

   wtexture->winterwoven [INTERWOVEN_FRAME] = frame;
   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
   gtk_container_add (GTK_CONTAINER (frame), hbox);
   gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
   gtk_container_set_border_width (GTK_CONTAINER (table), 5);
   gtk_table_set_row_spacings (GTK_TABLE (table), 5);
   gtk_table_set_col_spacings (GTK_TABLE (table), 5);

   wtexture->winterwoven [INTERWOVEN_FROM_1]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("First color of gradient 1."));
   wtexture->winterwoven [INTERWOVEN_TO_1]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("Last color of gradient 1."));
   
   wtexture->winterwoven [INTERWOVEN_FROM_2]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("First color of gradient 2."));
   wtexture->winterwoven [INTERWOVEN_TO_2]
      = color_button (wtexture, wtexture->key, NULL,
		      tooltips, _("Last color of gradient 2."));
   
   wtexture->winterwoven [INTERWOVEN_PREVIEW] = NULL;
   
   {
      GtkObject	*adj = gtk_adjustment_new (0, 0, 255, 1.0, 10.0, 0.0);
      GtkWidget	*spinner;
      
      spinner = wtexture->winterwoven [INTERWOVEN_THICKNESS_1]
	      = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0);
      gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_widget_show_all (spinner);
      gtk_object_set_user_data (GTK_OBJECT (adj), (gpointer) spinner);
      gtk_tooltips_set_tip (tooltips, spinner,
			    _("Thickness gradient 1."), NULL);  

      gtk_signal_connect (GTK_OBJECT (&GTK_SPIN_BUTTON (spinner)->entry),
			  "changed", GTK_SIGNAL_FUNC (set_thickness_1),
			  wtexture);
   }
   {
      GtkObject	*adj = gtk_adjustment_new (0, 0, 255, 1.0, 10.0, 0.0);
      GtkWidget	*spinner;
      
      spinner = wtexture->winterwoven [INTERWOVEN_THICKNESS_2]
	      = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0);
      gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
      gtk_widget_show_all (spinner);
      gtk_object_set_user_data (GTK_OBJECT (adj), (gpointer) spinner);
      gtk_tooltips_set_tip (tooltips, spinner,
			    _("Thickness gradient 2."), NULL);  

      gtk_signal_connect (GTK_OBJECT (&GTK_SPIN_BUTTON (spinner)->entry),
			  "changed", GTK_SIGNAL_FUNC (set_thickness_2),
			  wtexture);
   }
   
   wtexture->winterwoven [INTERWOVEN_PREVIEW] = NULL;
   update_interwoven_preview (wtexture,
			      &wtexture->winterwoven [INTERWOVEN_PREVIEW]);

   gtk_table_attach (GTK_TABLE (table),
		     wtexture->winterwoven [INTERWOVEN_FROM_1], 0, 1, 0, 1,
		     GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table),
		     wtexture->winterwoven [INTERWOVEN_TO_1], 1, 2, 0, 1,
		     GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table),
		     wtexture->winterwoven [INTERWOVEN_THICKNESS_1],
		     2, 3, 0, 1,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table),
		     wtexture->winterwoven [INTERWOVEN_PREVIEW], 0, 3, 1, 2,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table),
		     wtexture->winterwoven [INTERWOVEN_FROM_2], 0, 1, 2, 3,
		     GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table),
		     wtexture->winterwoven [INTERWOVEN_TO_2], 1, 2, 2, 3,
		     GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
   gtk_table_attach (GTK_TABLE (table),
		     wtexture->winterwoven [INTERWOVEN_THICKNESS_2],
		     2, 3, 2, 3,
		     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);

   gtk_widget_show_all (frame);

   return frame;
}

#endif /* HAVE_INTERWOVEN_GRADIENT */

static GtkWidget *
generate_gradient_dialog (wtexture_t *wtexture)
/*
 *  Generate all widgets for the gradient dialog.
 *
 *  Return value:
 *	Container widget of gradient dialog.
 */
{
   GtkWidget  *hbox;
   GtkWidget  *frame;
   GtkWidget  *scrolled;
   GtkWidget  *table;
   GtkWidget  *list;
   
   frame = wtexture->wgradient [GRADIENT_FRAME]
	 = gtk_frame_new (_("Gradient"));
   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
   
   wtexture->wgradient [GRADIENT_TYPE]
      = generate_option_menu (NULL, tooltips,
			      _("Horizontal: A gradient going from left to "
				"right.\n"
				"Vertical: A gradient going from top to "
				"bottom.\n"
				"Diagonal: A gradient going from the top-left"
				" corner to the bottom-right corner.\n"
				"Always from the first to the last color."),
			      option_g, option_g [0], NULL,
			      gradient_type_toggle, wtexture);
   
   table = gtk_table_new (2, 1, FALSE);
   gtk_container_set_border_width (GTK_CONTAINER (table), 5);
   gtk_table_set_row_spacings (GTK_TABLE (table), 5);
   gtk_table_set_col_spacings (GTK_TABLE (table), 5);
   gtk_container_add (GTK_CONTAINER (frame), table);
   
   gtk_table_attach (GTK_TABLE (table), wtexture->wgradient [GRADIENT_TYPE],
		     0, 2, 0, 1,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND,
		     GTK_FILL | GTK_SHRINK, 0, 0);
   
   hbox = gtk_hbox_new (FALSE, 5);
   gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 1, 2,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND,
		     GTK_FILL | GTK_SHRINK | GTK_EXPAND, 0, 0);

   /*
    *  Scrolled window for list
    */
   scrolled = gtk_scrolled_window_new (NULL, NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
				   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 0);
   gtk_scrolled_window_set_placement (GTK_SCROLLED_WINDOW (scrolled),
				      GTK_CORNER_TOP_LEFT);
   
   /*
    *  Color list
    */
   wtexture->wgradient [GRADIENT_LIST] = list = gtk_list_new ();
   gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_BROWSE);
   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), list);
   gtk_object_set_user_data (GTK_OBJECT (list), wtexture);
   
   gtk_signal_connect (GTK_OBJECT (list), "button_press_event",
		       GTK_SIGNAL_FUNC (list_button_press), wtexture);

   /*
    *  List manipulating buttons
    */
   {
      GtkWidget  *bbox; 
      const char *text [] = {N_("Remove"),
			     N_("Insert"),
			     N_("Append")};
      const char *info [] = {N_("Remove selected color from "
				"gradient color list."),
			     N_("Insert a new color before current color."),
			     N_("Append a new color after the last color.")};
      void (*fct []) (GtkWidget *, GtkWidget *list) = {list_remove, list_insert,
						       list_append};
      unsigned	i;
      
      bbox = gtk_vbutton_box_new ();
      {
	 proplist_t array = get_texture_array (wtexture);

	 if (!strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspaceback") &&
	     !strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspacespecificback") &&
	     !strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "iconback"))
	    gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox),
				       GTK_BUTTONBOX_SPREAD);
      }
      
      gtk_box_pack_start (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);

      for (i = 0; i < 3; i++)
      {
	 GtkWidget *button = gtk_button_new_with_label (_(text [i]));
	 if (i == 0)
	    wtexture->wgradient [GRADIENT_REMOVE] = button;
	 gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 0);
	 gtk_signal_connect (GTK_OBJECT (button), "clicked",
			     GTK_SIGNAL_FUNC (fct [i]), list);
	 gtk_tooltips_set_tip (tooltips, button, _(info [i]), NULL);  
      }
   }

#if defined(PREVIEWS) && !defined(CONVERT)

   wtexture->wgradient [GRADIENT_PREVIEW] = NULL;
   update_gradient_preview (wtexture);

   if (strcaseeq (WMGetFromPLString (wtexture->key), "workspaceback") ||
       strcaseeq (WMGetFromPLString (wtexture->key), "workspacespecificback"))
   {
      GtkWidget	 *frame	   = gtk_frame_new (NULL);
      GtkWidget	 *vbox     = gtk_vbox_new (TRUE, 0);
      GtkWidget	 *hbox     = gtk_hbox_new (TRUE, 0);
      
      gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
      gtk_container_add (GTK_CONTAINER (frame),
			 wtexture->wgradient [GRADIENT_PREVIEW]);
      gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, TRUE, 0);
      gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
      gtk_table_attach (GTK_TABLE (table), hbox,
			0, 1, 2, 3, GTK_SHRINK, GTK_SHRINK, 5, 5);
   }
   else
      gtk_table_attach (GTK_TABLE (table), wtexture->wgradient [GRADIENT_PREVIEW],
			0, 1, 2, 3, GTK_SHRINK, GTK_SHRINK, 5, 5);

#endif /* defined(PREVIEWS) && !defined(CONVERT) */

   gtk_widget_show_all (frame);

   return frame;
}   

static void
show_texture_dialog (wtexture_t *wtexture)
/*
 *  Generate set of visible/hidden widgets for the current texture attribute
 */
{
   unsigned    n;
   char	      *type    = NULL;
   texture_e   texture = TEXTURE_LAST;
   proplist_t array    = get_texture_array (wtexture);
   
   if (!WMIsPLArray (array))		/* uups */
   {
      if (strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key))
	  && wtexture->wsnumber > 0)
	 WMPutInPLDictionary (windowmaker, wtexture->wssback,
				  array = WMCreatePLArray (NULL));
      else
	 WMPutInPLDictionary (windowmaker, wtexture->key,
				  array = WMCreatePLArray (NULL));
   }
   /*
    *  Check which type of texture is used
    */
   if ((n = WMGetPropListItemCount (array)) != 0)
   {
      type = WMGetFromPLString (WMGetFromPLArray (array, 0));

      if (strcaseeq (type + 1, "gradient")

#ifdef HAVE_INTERWOVEN_GRADIENT
	  
	  && *type != 'i'
	  
#endif /* HAVE_INTERWOVEN_GRADIENT */
	  
	  ) /* convert to m[hvd]gradient */
      {
	 if (n == 3)
	 {
	    GdkColor	color1, color2;
	    char	*c1name = WMGetFromPLString (WMGetFromPLArray (array, 1));
	    char	*c2name = WMGetFromPLString (WMGetFromPLArray (array, 2));
	    /*
	     *  Compute default color = (color1 + color2) / 2
	     */
	    if (!make_color (c1name, &color1))
	       color1.red = color1.green = color1.blue = 65535; /* white */
	    if (!make_color (c2name, &color2))
	       color2.red = color2.green = color2.blue = 65535; /* white */

	    color1.red   = (color1.red + color2.red) >> 1;
	    color1.green = (color1.green + color2.green) >> 1;
	    color1.blue  = (color1.blue + color2.blue) >> 1;

	    {
	       char colstr [MAXSTRLEN];

	       sprintf (colstr, "#%02x%02x%02x", /* new color */
			(unsigned) (color1.red   / 256 + 0.5),
			(unsigned) (color1.green / 256 + 0.5),
			(unsigned) (color1.blue  / 256 + 0.5));
	       WMInsertInPLArray (array, 1, WMCreatePLString (colstr));
	    }

	    /*
	     *  Replace gradient by mgradient
	     */
	    {
	       char *tmp = g_strconcat ("m", type, NULL);
	       
	       WMDeleteFromPLArray (array, 0);
	       WMInsertInPLArray (array, 0, WMCreatePLString (tmp));
	       Free (tmp);
	       type = WMGetFromPLString (WMGetFromPLArray (array, 0));
	    }
	    n++;
	 }
      }
      if (strcaseeq (type, "solid"))
	 texture = n == 2 ? TEXTURE_SOLID : TEXTURE_LAST;
      else if (strcaseeq (type, "none"))
	 texture = TEXTURE_NONE;
#ifdef HAVE_INTERWOVEN_GRADIENT
      else if (strcaseeq (type, "igradient"))
	 texture = n == 7 ? TEXTURE_INTERWOVEN : TEXTURE_LAST;
#endif
      else if (strcaseeq (type + 2, "gradient"))
      {
	 if (*type != 'm' && *type != 't')
	    texture = TEXTURE_LAST;
	 else
	 {
	    if (*type == 't')
	       texture = n == 5 ? TEXTURE_TGRADIENT : TEXTURE_LAST;
	    else 
	       texture = n > 3 ? TEXTURE_GRADIENT : TEXTURE_LAST;
	    if (*(type + 1) != 'v' && *(type + 1) != 'h' && *(type + 1) != 'd')
	       texture = TEXTURE_LAST;
	 }
      }
      else if (strcaseeq (type + 1, "pixmap"))
      {
	 if ((*type != 's' && *type != 't' && *type != 'c' && *type != 'm')
	     || (*type == 'm'
		 && !strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key))))
	    texture = TEXTURE_LAST;
	 else
	    texture = n == 3 ? TEXTURE_PIXMAP : TEXTURE_LAST;
      }
#ifdef HAVE_LIBWMFUN
      else if (strcaseeq (type, "function"))
      {
	 texture = TEXTURE_LAST;
	 if (n >= 3)
	 {
	    char *library  = WMGetFromPLString (WMGetFromPLArray (array, 1));
	    char *function = WMGetFromPLString (WMGetFromPLArray (array, 2));

	    if (strncaseeq (library, "libwmfun", strlen ("libwmfun")))
	    {
	       if (strcaseeq (function, "bilinear") && n == 7)
		  texture = TEXTURE_BILINEAR;
	       else if (strcaseeq (function, "fade"))
		  texture = TEXTURE_FADE;
	       else if (strcaseeq (function, "waves") && n >= 5)
		  texture = TEXTURE_WAVES;
	    }
	 }
      }
#endif
   }
   else if (strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key))) {
      if (wtexture->wsnumber)
         texture = TEXTURE_DEFAULT;
      else /* we're inspecting default texture */
	 texture = TEXTURE_NONE;
   }

   /*
    *  Convert unknown texture types to (solid, black)
    */
   if (texture == TEXTURE_LAST)
   {
      unsigned i;
      
      warning (_("Texture `%s' not yet supported or wrong number "
		 "of arguments.\nConverting to (solid, black) ..."),
	       type ? type : "()");
      for (i = n; i; i--)
	 WMDeleteFromPLArray (array, 0);
      WMInsertInPLArray (array, 0, WMCreatePLString ("solid"));
      WMInsertInPLArray (array, 1, WMCreatePLString ("black"));
      texture = TEXTURE_SOLID;
   }
   /*
    *  Set texture type option menu to given value
    */
   if (strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key)))
   {
      if (wtexture->wsnumber == 0)
      {
	 gtk_widget_show (wtexture->wwttype);
	 gtk_widget_hide (wtexture->wttype);
	 set_option_menu_default (GTK_OPTION_MENU (wtexture->wwttype),
				  option_t_wspace, ttype_strings [texture]);
      }
      else
      {
	 gtk_widget_show (wtexture->wttype);
	 gtk_widget_hide (wtexture->wwttype);
	 set_option_menu_default (GTK_OPTION_MENU (wtexture->wttype),
				  option_t_wspecific, ttype_strings [texture]);
      }
   }
   else
   {
      gtk_widget_show (wtexture->wttype);
      set_option_menu_default (GTK_OPTION_MENU (wtexture->wttype),
			       option_t_normal, ttype_strings [texture]);
   }
   if (texture == TEXTURE_SOLID)
   {
      gtk_widget_hide (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_hide (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wsgradient [SGRADIENT_FRAME]);
#ifdef HAVE_LIBWMFUN
      gtk_widget_hide (wtexture->wbilinear [BILINEAR_FRAME]);
      gtk_widget_hide (wtexture->wfade [FADE_FRAME]);
      gtk_widget_hide (wtexture->wwaves [WAVES_FRAME]);
#endif
#ifdef HAVE_INTERWOVEN_GRADIENT
      gtk_widget_hide (wtexture->winterwoven [INTERWOVEN_FRAME]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
      gtk_widget_show (wtexture->wcolorbox);

      /*
       *  Set default color
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wcolor));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 1)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 1));
      }
   }
#ifdef HAVE_LIBWMFUN
   else if (texture == TEXTURE_BILINEAR)
   {
      gtk_widget_hide (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_hide (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wsgradient [SGRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wcolorbox);
      gtk_widget_hide (wtexture->wfade [FADE_FRAME]);
      gtk_widget_hide (wtexture->wwaves [WAVES_FRAME]);
#ifdef HAVE_INTERWOVEN_GRADIENT
      gtk_widget_hide (wtexture->winterwoven [INTERWOVEN_FRAME]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
      gtk_widget_show (wtexture->wbilinear [BILINEAR_FRAME]);
      
      /*
       *  Set colors of bilinear gradient
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wbilinear [BILINEAR_NW]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 3)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 3));

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wbilinear [BILINEAR_NE]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 4)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 4));

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wbilinear [BILINEAR_SW]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 5)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 5));

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wbilinear [BILINEAR_SE]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 6)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 6));
      }
      update_wmfun_preview (wtexture, &wtexture->wbilinear [BILINEAR_PREVIEW]);
   }
   else if (texture == TEXTURE_FADE)
   {
      gtk_widget_hide (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_hide (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wsgradient [SGRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wcolorbox);
      gtk_widget_hide (wtexture->wwaves [WAVES_FRAME]);
      gtk_widget_hide (wtexture->wbilinear [BILINEAR_FRAME]);
#ifdef HAVE_INTERWOVEN_GRADIENT
      gtk_widget_hide (wtexture->winterwoven [INTERWOVEN_FRAME]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
      gtk_widget_show (wtexture->wfade [FADE_FRAME]);

      {
	 /*
	  *  Let's normalize the parameters to --from value --to value
	  */
	 char 	  *from = g_strdup ("black");
	 char 	  *to   = g_strdup ("white");
	 unsigned  i 	= 3;
	 
	 while (i <= n - 2)
	 {
	    char *name 	= WMGetFromPLString (WMGetFromPLArray (array, i++));
	    char *color = WMGetFromPLString (WMGetFromPLArray (array, i++));

	    if (streq (name, "--from"))
	       from = g_strdup (color);
	    else if (streq (name, "--to"))
	       to = g_strdup (color);
	 }
	 while (WMGetPropListItemCount (array) > 3)
	    WMDeleteFromPLArray (array, 3);
	 WMInsertInPLArray (array, 3, WMCreatePLString ("--from"));
	 WMInsertInPLArray (array, 4, WMCreatePLString (from));
	 WMInsertInPLArray (array, 5, WMCreatePLString ("--to"));
	 WMInsertInPLArray (array, 6, WMCreatePLString (to));
	 g_free (from);
	 g_free (to);
      }
      
      /*
       *  Set colors of fading gradient
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wfade [FADE_FROM]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 4)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 4));

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wfade [FADE_TO]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 6)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 6));
      }
      update_wmfun_preview (wtexture, &wtexture->wfade [FADE_PREVIEW]);
   }
   else if (texture == TEXTURE_WAVES)
   {
      gtk_widget_hide (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_hide (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wsgradient [SGRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wcolorbox);
      gtk_widget_hide (wtexture->wfade [FADE_FRAME]);
      gtk_widget_hide (wtexture->wbilinear [BILINEAR_FRAME]);
#ifdef HAVE_INTERWOVEN_GRADIENT
      gtk_widget_hide (wtexture->winterwoven [INTERWOVEN_FRAME]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
      gtk_widget_show (wtexture->wwaves [WAVES_FRAME]);

      {
	 /*
	  *  Let's normalize the parameters to --from value --to value
	  */
	 char 	  *from = g_strdup ("black");
	 char 	  *to   = g_strdup ("white");
	 unsigned  i 	= 3;
	 
	 while (i <= n - 4)
	 {
	    char *name 	= WMGetFromPLString (WMGetFromPLArray (array, i++));
	    char *color = WMGetFromPLString (WMGetFromPLArray (array, i++));

	    if (streq (name, "--from"))
	       from = g_strdup (color);
	    else if (streq (name, "--to"))
	       to = g_strdup (color);
	 }
	 while (WMGetPropListItemCount (array) > 5)
	    WMDeleteFromPLArray (array, 3);
	 WMInsertInPLArray (array, 3, WMCreatePLString ("--from"));
	 WMInsertInPLArray (array, 4, WMCreatePLString (from));
	 WMInsertInPLArray (array, 5, WMCreatePLString ("--to"));
	 WMInsertInPLArray (array, 6, WMCreatePLString (to));
	 g_free (from);
	 g_free (to);
      }

      /*
       *  Set colors of waves gradient
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wwaves [WAVES_FROM]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 4)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 4));

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wwaves [WAVES_TO]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 6)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 6));

	 gtk_spin_button_set_value (GTK_SPIN_BUTTON (wtexture->wwaves [WAVES_LAYERS]), strtod (WMGetFromPLString (WMGetFromPLArray (array, 7)), NULL));
	 gtk_spin_button_set_value (GTK_SPIN_BUTTON (wtexture->wwaves [WAVES_FREQUENCY]), strtod (WMGetFromPLString (WMGetFromPLArray (array, 8)), NULL));
      }
      update_wmfun_preview (wtexture, &wtexture->wwaves [WAVES_PREVIEW]);
   }
#endif
#ifdef HAVE_INTERWOVEN_GRADIENT
   else if (texture == TEXTURE_INTERWOVEN)
   {
      gtk_widget_hide (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_hide (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wsgradient [SGRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wcolorbox);
#ifdef HAVE_LIBWMFUN
      gtk_widget_hide (wtexture->wfade [FADE_FRAME]);
      gtk_widget_hide (wtexture->wbilinear [BILINEAR_FRAME]);
      gtk_widget_hide (wtexture->wwaves [WAVES_FRAME]);
#endif /* HAVE_LIBWMFUN */
      gtk_widget_show (wtexture->winterwoven [INTERWOVEN_FRAME]);

      /*
       *  Set colors of interwoven gradient
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->winterwoven [INTERWOVEN_FROM_1]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 1)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 1));

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->winterwoven [INTERWOVEN_TO_1]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 2)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 2));

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->winterwoven [INTERWOVEN_FROM_2]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 4)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 4));

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->winterwoven [INTERWOVEN_TO_2]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 5)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 5));

	 gtk_spin_button_set_value (GTK_SPIN_BUTTON (wtexture->winterwoven [INTERWOVEN_THICKNESS_1]), strtod (WMGetFromPLString (WMGetFromPLArray (array, 3)), NULL));
	 gtk_spin_button_set_value (GTK_SPIN_BUTTON (wtexture->winterwoven [INTERWOVEN_THICKNESS_2]), strtod (WMGetFromPLString (WMGetFromPLArray (array, 6)), NULL));
      }
      update_interwoven_preview (wtexture,
				 &wtexture->winterwoven [INTERWOVEN_PREVIEW]);

   }
#endif /* HAVE_INTERWOVEN_GRADIENT */
   else if (texture == TEXTURE_NONE || texture == TEXTURE_DEFAULT)
   {
#ifdef HAVE_LIBWMFUN
      gtk_widget_hide (wtexture->wbilinear [BILINEAR_FRAME]);
      gtk_widget_hide (wtexture->wfade [FADE_FRAME]);
      gtk_widget_hide (wtexture->wwaves [WAVES_FRAME]);
#endif
#ifdef HAVE_INTERWOVEN_GRADIENT
      gtk_widget_hide (wtexture->winterwoven [INTERWOVEN_FRAME]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
      gtk_widget_hide (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_hide (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wsgradient [SGRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wcolorbox);
   }
   else if (texture == TEXTURE_PIXMAP)
   {
#ifdef HAVE_LIBWMFUN
      gtk_widget_hide (wtexture->wbilinear [BILINEAR_FRAME]);
      gtk_widget_hide (wtexture->wfade [FADE_FRAME]);
      gtk_widget_hide (wtexture->wwaves [WAVES_FRAME]);
#endif
#ifdef HAVE_INTERWOVEN_GRADIENT
      gtk_widget_hide (wtexture->winterwoven [INTERWOVEN_FRAME]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
      gtk_widget_show (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_show (wtexture->wpixmap [PIXMAP_TYPE]);
      gtk_widget_hide (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wsgradient [SGRADIENT_FRAME]);
      gtk_widget_show (wtexture->wcolorbox);
      /*
       *  Set default color
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wcolor));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 2)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 2));
      }
      combine_path_and_filename (array, wtexture);
      g_signal_handler_block (wtexture->wpixmap [PIXMAP_NAME],
			      GPOINTER_TO_INT (gtk_object_get_user_data
	                      GTK_OBJECT (wtexture->wpixmap [PIXMAP_NAME])));
      gtk_entry_set_text (GTK_ENTRY (wtexture->wpixmap [PIXMAP_NAME]),
			  WMGetFromPLString (WMGetFromPLArray (array, 1)));
      g_signal_handler_unblock (wtexture->wpixmap [PIXMAP_NAME],
			        GPOINTER_TO_INT (gtk_object_get_user_data
	                        GTK_OBJECT (wtexture->wpixmap [PIXMAP_NAME])));
      show_pixmap (KEY_OF_TEXTURE, WMGetFromPLString (WMGetFromPLArray (array, 1)),
		   wtexture->wpixmap [PIXMAP_PIXMAP]);
      /*
       *  scaled, tiled, centered, maxaspect pixmap
       */
      if (strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key)))
      {
	 GtkOptionMenu *opt = GTK_OPTION_MENU (wtexture->wpixmap [PIXMAP_TYPE]);

	 if (*type == 't')
	    set_option_menu_default (opt, option_p_wspace,
				     ptype_strings [PIXMAP_TILED]);
	 else if (*type == 's')
	    set_option_menu_default (opt, option_p_wspace,
				     ptype_strings [PIXMAP_SCALED]);
	 else
	    set_option_menu_default (opt, option_p_wspace,
				     ptype_strings [PIXMAP_CENTERED]);
      }
      else
      {
	 GtkOptionMenu *opt = GTK_OPTION_MENU (wtexture->wpixmap [PIXMAP_TYPE]);

	 if (*type == 't')
	    set_option_menu_default (opt, option_p_normal,
				     ptype_strings [PIXMAP_TILED]);
	 else if (*type == 's')
	    set_option_menu_default (opt, option_p_normal,
				     ptype_strings [PIXMAP_SCALED]);
	 else if (*type == 'm')
	    set_option_menu_default (opt, option_p_normal,
				     ptype_strings [PIXMAP_MAXASPECT]);
	 else
	    set_option_menu_default (opt, option_p_normal,
				     ptype_strings [PIXMAP_CENTERED]);
      }
   }
   else if (texture == TEXTURE_GRADIENT)
   {
#ifdef HAVE_LIBWMFUN
      gtk_widget_hide (wtexture->wbilinear [BILINEAR_FRAME]);
      gtk_widget_hide (wtexture->wfade [FADE_FRAME]);
      gtk_widget_hide (wtexture->wwaves [WAVES_FRAME]);
#endif
#ifdef HAVE_INTERWOVEN_GRADIENT
      gtk_widget_hide (wtexture->winterwoven [INTERWOVEN_FRAME]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
      gtk_widget_hide (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_show (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wsgradient [SGRADIENT_FRAME]);
      gtk_widget_show (wtexture->wcolorbox);

      /*
       *  Set default color
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wcolor));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 1)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 1));
      }
      /*
       *  horizontal, vertical, or diagonal gradient
       */
      {
	 GtkOptionMenu *opt;
	 
	 opt = GTK_OPTION_MENU (wtexture->wgradient [GRADIENT_TYPE]);

	 if (*(type + 1) == 'h')
	    set_option_menu_default (opt, option_g,
				     gtype_strings [GRADIENT_HOR]);
	 else if (*(type + 1) == 'v')
	    set_option_menu_default (opt, option_g,
				     gtype_strings [GRADIENT_VER]);
	 else
	    set_option_menu_default (opt, option_g,
				     gtype_strings [GRADIENT_DIAG]);
      }
      /*
       *  gradient colors
       */
      {
	 unsigned  i;
	 GtkWidget *list = wtexture->wgradient [GRADIENT_LIST];
	 
	 while (g_list_length (GTK_LIST (list)->children))
	    gtk_list_clear_items (GTK_LIST (list), 0, -1);
	 for (i = 2; i < n; i++)
	    gtk_container_add (GTK_CONTAINER (list),
			       new_list_item (wtexture,
					      WMGetFromPLArray (array, i)));
	 gtk_widget_set_sensitive (wtexture->wgradient [GRADIENT_REMOVE], n > 4);
	 gtk_list_select_item (GTK_LIST (list), 0);
      }
      update_gradient_preview (wtexture);
   }
   else if (texture == TEXTURE_TGRADIENT)
   {
#ifdef HAVE_LIBWMFUN
      gtk_widget_hide (wtexture->wbilinear [BILINEAR_FRAME]);
      gtk_widget_hide (wtexture->wfade [FADE_FRAME]);
      gtk_widget_hide (wtexture->wwaves [WAVES_FRAME]);
#endif
#ifdef HAVE_INTERWOVEN_GRADIENT
      gtk_widget_hide (wtexture->winterwoven [INTERWOVEN_FRAME]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
      gtk_widget_show (wtexture->wpixmap [PIXMAP_FRAME]);
      gtk_widget_hide (wtexture->wgradient [GRADIENT_FRAME]);
      gtk_widget_show (wtexture->wsgradient [SGRADIENT_FRAME]);
      gtk_widget_hide (wtexture->wcolorbox);
      gtk_widget_hide (wtexture->wpixmap [PIXMAP_TYPE]);

      /*
       *  horizontal, vertical, or diagonal gradient
       */
      {
	 GtkOptionMenu *opt;
	 
	 opt = GTK_OPTION_MENU (wtexture->wsgradient [SGRADIENT_TYPE]);

	 if (*(type + 1) == 'h')
	    set_option_menu_default (opt, option_g,
				     gtype_strings [GRADIENT_HOR]);
	 else if (*(type + 1) == 'v')
	    set_option_menu_default (opt, option_g,
				     gtype_strings [GRADIENT_VER]);
	 else
	    set_option_menu_default (opt, option_g,
				     gtype_strings [GRADIENT_DIAG]);
      }

      combine_path_and_filename (array, wtexture);
      g_signal_handler_block (wtexture->wpixmap [PIXMAP_NAME],
			      GPOINTER_TO_INT (gtk_object_get_user_data
	                      GTK_OBJECT (wtexture->wpixmap [PIXMAP_NAME])));
      gtk_entry_set_text (GTK_ENTRY (wtexture->wpixmap [PIXMAP_NAME]),
			  WMGetFromPLString (WMGetFromPLArray (array, 1)));
      g_signal_handler_unblock (wtexture->wpixmap [PIXMAP_NAME],
			        GPOINTER_TO_INT (gtk_object_get_user_data
	                        GTK_OBJECT (wtexture->wpixmap [PIXMAP_NAME])));
      show_pixmap (KEY_OF_TEXTURE, WMGetFromPLString (WMGetFromPLArray (array, 1)),
		   wtexture->wpixmap [PIXMAP_PIXMAP]);
      gtk_spin_button_set_value (GTK_SPIN_BUTTON (wtexture->wsgradient [SGRADIENT_SPINNER]), strtod (WMGetFromPLString (WMGetFromPLArray (array, 2)), NULL));
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wsgradient [SGRADIENT_COLOR1]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 3)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 3));
	 preview = gtk_object_get_user_data (GTK_OBJECT (wtexture->wsgradient [SGRADIENT_COLOR2]));
	 fill_preview (GTK_PREVIEW (preview),
		       WMGetFromPLString (WMGetFromPLArray (array, 4)));
	 gtk_object_set_user_data (GTK_OBJECT (preview),
				   WMGetFromPLArray (array, 4));
      }
   }
}

static void
update_gradient_preview (wtexture_t *wtexture)
{
   proplist_t	array = get_texture_array (wtexture);
   proplist_t   ttype = WMGetFromPLArray (array, 0);
   char		*type;
   
   if (ttype)
      type = WMGetFromPLString (ttype);

#if defined(PREVIEWS) && !defined(CONVERT)
   {
      gtype_e	gtype;
      unsigned	width, height;
   
      if (!ttype || *(type + 1) == 'h')
	 gtype = HGRADIENT;
      else if (*(type + 1) == 'v')
	 gtype = VGRADIENT;
      else
	 gtype = DGRADIENT;
   
      if (strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "iconback"))
	 width = height = 64;
      else if (strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspaceback") ||
	       strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspacespecificback"))
      {
	 width  = 160;
	 height = 160 * 3 / 4;
      }
      else 
      {
	 width  = 180;
	 height = 22;
      }
      wtexture->wgradient [GRADIENT_PREVIEW]
	 = make_gradient (array, width, height, gtype,
			  wtexture->wgradient [GRADIENT_PREVIEW]);
   }
#endif /* defined(PREVIEWS) && !defined(CONVERT) */
}

#ifdef HAVE_LIBWMFUN
static void
update_wmfun_preview (wtexture_t *wtexture, GtkWidget **preview)
{
   proplist_t array = get_texture_array (wtexture);

#if defined(PREVIEWS) && !defined(CONVERT)
   {
      unsigned	width, height;
      
      if (strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "iconback"))
	 width = height = 64;
      else if (strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspaceback") ||
	       strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspacespecificback"))
      {
	 width  = 180;
	 height = 180 * 3 / 4;
      }
      else 
      {
	 width  = 180;
	 height = 22;
      }
      *preview = make_wmfun (array, width, height, *preview);
   }
#endif /* defined(PREVIEWS) && !defined(CONVERT) */
}
#endif /* HAVE_LIBWMFUN */

#ifdef HAVE_INTERWOVEN_GRADIENT
static void
update_interwoven_preview (wtexture_t *wtexture, GtkWidget **preview)
{
   proplist_t array = get_texture_array (wtexture);

#if defined(PREVIEWS) && !defined(CONVERT)
   {
      unsigned	width, height;
      
      if (strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "iconback"))
	 width = height = 64;
      else if (strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspaceback") ||
	       strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspacespecificback"))
      {
	 width  = 180;
	 height = 180 * 3 / 4;
      }
      else 
      {
	 width  = 180;
	 height = 22;
      }
      *preview = make_igradient (array, width, height, *preview);
   }
#endif /* defined(PREVIEWS) && !defined(CONVERT) */
}
#endif /* HAVE_INTERWOVEN_GRADIENT */

static void
combine_path_and_filename (proplist_t array, wtexture_t *wtexture)
/*
 *  Set pixmap and filename
 */
{
   if (wtexture->path)
   {
      char *tmp  = g_strconcat (wtexture->path, "/",
				WMGetFromPLString (WMGetFromPLArray (array, 1)), NULL);
      char *test = get_pixmap_path (tmp);

      if (test)
      {
	 WMDeleteFromPLArray (array, 1);
	 WMInsertInPLArray (array, 1, WMCreatePLString (tmp));
	 Free (test);
      }
      Free (tmp);
      wtexture->path = NULL;
   }
}

static void
color_selection_dialog (GtkWidget *widget, proplist_t key)
/*
 *  Generate a color selection popup window.
 *  associated user data of 'widget' must be a pointer to corresponding preview.
 *  'key' is the associated wmaker attribute name
 *
 *  No return value.
 */
{
   GdkColor	 color;
   GtkWidget  *preview = gtk_object_get_user_data (GTK_OBJECT (widget));
   proplist_t value   = gtk_object_get_user_data (GTK_OBJECT (preview));
   GtkColorSelection		*cs;
   GtkColorSelectionDialog	*dialog;
   gdouble *rgb = Calloc (4, sizeof (gdouble));
   GtkWidget *window = NULL;
   
   gtk_preview_set_install_cmap (TRUE);
   gtk_widget_push_visual (gtk_preview_get_visual ());
   gtk_widget_push_colormap (gtk_preview_get_cmap ());

   window = gtk_color_selection_dialog_new (_("Color selection"));
   dialog = GTK_COLOR_SELECTION_DIALOG (window);
   cs     = GTK_COLOR_SELECTION (dialog->colorsel);

   gtk_widget_hide (dialog->help_button);
   gtk_object_set_data (GTK_OBJECT (cs), "key", key);
   gtk_object_set_data (GTK_OBJECT (cs), "old-color", rgb);
   gtk_object_set_user_data (GTK_OBJECT (cs), preview);
      
   if (!make_color (WMGetFromPLString (value), &color))
      color.red = color.green = color.blue = 65530;

   rgb [0] = color.red   / 65535.0;
   rgb [1] = color.green / 65535.0;
   rgb [2] = color.blue  / 65535.0;

   gtk_color_selection_set_color (cs, rgb);
   gtk_color_selection_get_color (cs, rgb);
   gtk_color_selection_set_color (cs, rgb);
   gtk_color_selection_set_has_opacity_control (cs, FALSE);
   gtk_color_selection_set_update_policy (cs, GTK_UPDATE_CONTINUOUS);
   gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE);

   gtk_signal_connect_object (GTK_OBJECT (window), "destroy",
			      GTK_SIGNAL_FUNC (gtk_widget_destroy),
			      GTK_OBJECT (window));
   gtk_signal_connect (GTK_OBJECT (cs), "color_changed",
		       GTK_SIGNAL_FUNC (color_selection_ok), cs);
   gtk_signal_connect_object (GTK_OBJECT (dialog->ok_button),
			      "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy),
			      GTK_OBJECT (window));
   gtk_signal_connect (GTK_OBJECT (dialog->cancel_button),
		       "clicked", GTK_SIGNAL_FUNC (color_selection_cancel), cs);
   gtk_signal_connect_object_after (GTK_OBJECT (dialog->cancel_button), "clicked",
				    GTK_SIGNAL_FUNC (gtk_widget_destroy),
				    GTK_OBJECT (window));
   gtk_widget_pop_colormap ();
   gtk_widget_pop_visual ();
   gtk_widget_show (window);
}

static void
color_selection_cancel (GtkWidget *w, GtkColorSelection *colorsel)
/*
 *  Restore old color
 */
{
   gdouble *color = gtk_object_get_data (GTK_OBJECT (colorsel), "old-color");

   gtk_signal_disconnect_by_func (GTK_OBJECT (colorsel),
				  GTK_SIGNAL_FUNC (color_selection_ok),
				  colorsel);
   gtk_color_selection_set_color (colorsel, color);
   color_selection_ok (w, colorsel);
}

static void
color_selection_ok (GtkWidget *w, GtkColorSelection *colorsel)
/*
 *  Replace old color with new one.
 *
 *  No return value.
 *
 *  Side effects:
 *	value of wmaker attribute w->key is modified.
 *	corresponding preview color update
 */
{
   gdouble	color [4];
   proplist_t	key      = gtk_object_get_data (GTK_OBJECT (colorsel), "key");
   GtkPreview	*preview = gtk_object_get_user_data (GTK_OBJECT (colorsel));
   proplist_t	value    = gtk_object_get_user_data (GTK_OBJECT (preview));
   gtk_color_selection_get_color (colorsel, color);
 
   {
      char tmp [MAXSTRLEN];

      sprintf (tmp, "#%02x%02x%02x",	/* new color */
	       (unsigned) (color [0] * 255 + 0.5),
	       (unsigned) (color [1] * 255 + 0.5),
	       (unsigned) (color [2] * 255 + 0.5));

      {
	 wtexture_t *wtexture = gtk_object_get_data (GTK_OBJECT (preview),
						     "wtexture");
	 if (wtexture)			/* texture */
	 {
	    unsigned	i;
	    proplist_t  array = get_texture_array (wtexture);
	    char	*type = WMGetFromPLString (WMGetFromPLArray (array, 0));
	    for (i = 1; i < WMGetPropListItemCount (array); i++)
	       if (WMGetFromPLArray (array, i) == value)
	       {
		  WMDeleteFromPLArray (array, i);
		  WMInsertInPLArray (array, i, WMCreatePLString (tmp));
		  gtk_object_set_user_data (GTK_OBJECT (preview),
					    WMGetFromPLArray (array, i));
		  fill_preview (preview, tmp);
		  toggle_save (changed = YES, KEY_OF_TEXTURE);
		  break;
  	       }
	    if (tolower (*type) == 't')
	       show_pixmap (KEY_OF_TEXTURE,
			    WMGetFromPLString (WMGetFromPLArray (array, 1)),
			    wtexture->wpixmap [PIXMAP_PIXMAP]);
#ifdef HAVE_LIBWMFUN
	    else if (strcaseeq (type, "function"))
	    {
	       char *function = WMGetFromPLString (WMGetFromPLArray (array, 2));
	       if (function && strcaseeq (function, "bilinear"))
		  update_wmfun_preview (wtexture,
					&wtexture->wbilinear [BILINEAR_PREVIEW]);
	       else if (function && strcaseeq (function, "fade"))
		  update_wmfun_preview (wtexture,
					&wtexture->wfade [FADE_PREVIEW]);
	       else if (function && strcaseeq (function, "waves"))
		  update_wmfun_preview (wtexture,
					&wtexture->wwaves [WAVES_PREVIEW]);
	    }
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT
	    else if (strcaseeq (type, "igradient"))
	       update_interwoven_preview (wtexture, &wtexture->winterwoven [INTERWOVEN_PREVIEW]);
#endif /* HAVE_INTERWOVEN_GRADIENT */
	    else
	       update_gradient_preview (wtexture);
	 }
	 else				/* color attribute */
	 {
	    proplist_t array = WMGetFromPLDictionary (windowmaker, key);
	    proplist_t plval = WMCreatePLString (tmp);
	    
	    gtk_object_set_user_data (GTK_OBJECT (preview), plval);
	    fill_preview (preview, tmp);
	    toggle_save (changed = YES, key);
	    if (WMIsPLString (array))
	       WMPutInPLDictionary (windowmaker, key, plval);
	    else
	    {
	       unsigned i;
	       
	       for (i = 0; i < WMGetPropListItemCount (array); i++)
		  if (WMGetFromPLArray (array, i) == value)
		  {
		     WMDeleteFromPLArray (array, i);
		     WMInsertInPLArray (array, i, plval);
		     break;
		  }
	    }
	 }
      }
   }
}

static proplist_t
get_texture_array (wtexture_t *wtexture)
{
   proplist_t array;

   if (strcaseeq ("workspaceback", WMGetFromPLString (wtexture->key))
       && wtexture->wsnumber > 0)
   {
      proplist_t subarray;
      
      array = WMGetFromPLDictionary (windowmaker, wtexture->wssback);
      if (!array || !WMIsPLArray (array))
      {
	 if (array)
	    WMReleasePropList (array);
	 array = WMCreatePLArray (NULL);
	 WMPutInPLDictionary (windowmaker, wtexture->wssback, array);
      }
      while (WMGetPropListItemCount (array) < wtexture->wsnumber)
      {
	 WMAddToPLArray (array, WMCreatePLArray (NULL));
	 toggle_save (changed = YES, wtexture->wssback);
      }
      subarray = WMGetFromPLArray (array, wtexture->wsnumber - 1);
      if (!subarray || !WMIsPLArray (subarray))
      {
	 if (subarray)
	    WMReleasePropList (subarray);
	 subarray = WMCreatePLArray (NULL);
	 WMInsertInPLArray (array, wtexture->wsnumber - 1, subarray);
      }
      array = subarray;
   }
   else
   {
      array = WMGetFromPLDictionary (windowmaker, wtexture->key);
      if (!array || !WMIsPLArray (array))
      {
	 if (array)
	    WMReleasePropList (array);
	 array = WMCreatePLArray (WMCreatePLString ("solid"),
					  WMCreatePLString ("black"),
					  NULL);
	 WMPutInPLDictionary (windowmaker, wtexture->key, array);
      }
   }
   
   return array;
}

static void
gradient_type_toggle (GtkWidget *widget, gpointer ptr)
/*
 *  Toggle gradient type (horizontal, vertical, diagonal).
 *
 *  Side effects:
 *	Window Maker attribute is updated
 */
{
   const char *text     = gtk_object_get_user_data (GTK_OBJECT (widget));
   wtexture_t *wtexture = (wtexture_t *) ptr;
   proplist_t array     = get_texture_array (wtexture);
   const char *type     = WMGetFromPLString (WMGetFromPLArray (array, 0));
   
   if (strcaseeq (type + 2, "gradient")
       &&
       ((tolower (*(type + 1)) == 'h'
	 && !streq (text, gtype_strings [GRADIENT_HOR]))
	|| (tolower (*(type + 1)) == 'v'
	    && !streq (text, gtype_strings [GRADIENT_VER]))
	|| (tolower (*(type + 1)) == 'd'
	    && !streq (text, gtype_strings [GRADIENT_DIAG]))))
   {
      char *tmp = g_strdup (type);

      if (streq (text, gtype_strings [GRADIENT_HOR]))
	 *(tmp + 1) = 'h';
      else if (streq (text, gtype_strings [GRADIENT_VER]))
	 *(tmp + 1) = 'v';
      else 
	 *(tmp + 1) = 'd';
      
      WMDeleteFromPLArray (array, 0);
      WMInsertInPLArray (array, 0, WMCreatePLString (tmp));
      Free (tmp);
      toggle_save (changed = YES, KEY_OF_TEXTURE);
      if (tolower (*type) == 't')
	 show_pixmap (KEY_OF_TEXTURE, WMGetFromPLString (WMGetFromPLArray (array, 1)),
		      wtexture->wpixmap [PIXMAP_PIXMAP]);
      else
	 update_gradient_preview (wtexture);
   }
}

static void
pixmap_type_toggle (GtkWidget *widget, gpointer ptr)
/*
 *  Toggle pixmap type (scaled, tiled, centered, maxaspect).
 *
 *  Side effects:
 *	Window Maker attribute is updated
 */
{
   const char *text     = gtk_object_get_user_data (GTK_OBJECT (widget));
   wtexture_t *wtexture = (wtexture_t *) ptr;
   proplist_t array     = get_texture_array (wtexture);
   const char *type     = WMGetFromPLString (WMGetFromPLArray (array, 0));
   
   if (strcaseeq (type + 1, "pixmap")
       &&
       ((tolower (*type) == 's'
	 && !streq (text, ptype_strings [PIXMAP_SCALED]))
	|| (tolower (*type) == 't'
	    && !streq (text, ptype_strings [PIXMAP_TILED]))
	|| (tolower (*type) == 'm'
	    && !streq (text, ptype_strings [PIXMAP_MAXASPECT]))
	|| (tolower (*type) == 'c'
	    && !streq (text, ptype_strings [PIXMAP_CENTERED]))))
   {
      char *tmp = g_strdup (type);

      if (streq (text, ptype_strings [PIXMAP_SCALED]))
	 *tmp = 's';
      else if (streq (text, ptype_strings [PIXMAP_TILED]))
	 *tmp = 't';
      else if (streq (text, ptype_strings [PIXMAP_MAXASPECT]))
	 *tmp = 'm';
      else
	 *tmp = 'c';
      WMDeleteFromPLArray (array, 0);
      WMInsertInPLArray (array, 0, WMCreatePLString (tmp));
      Free (tmp);
      toggle_save (changed = YES, KEY_OF_TEXTURE);
   }
}

static void
texture_type_toggle (GtkWidget *widget, gpointer ptr)
/*
 *  Toggle texture type (solid, pixmap, gradient, textured gradient).
 *
 *  Side effects:
 *	Window Maker attribute is updated
 */
{
   const char 	*text     = gtk_object_get_user_data (GTK_OBJECT (widget));
   wtexture_t 	*wtexture = (wtexture_t *) ptr;
   proplist_t 	 array    = get_texture_array (wtexture);
   unsigned   	 n	  = WMGetPropListItemCount (array) > 0;
   const char	*type     = n
			    ? WMGetFromPLString (WMGetFromPLArray (array, 0)) : NULL;
#ifdef HAVE_LIBWMFUN
   const char	*library  = n > 2
			    ? WMGetFromPLString (WMGetFromPLArray (array, 1)) : NULL;
   const char	*function = n > 2
			    ? WMGetFromPLString (WMGetFromPLArray (array, 2)) : NULL;
#endif
   
   if ((type == NULL && !streq (text, ttype_strings [TEXTURE_DEFAULT]))
       || (type
	   && (!((strcaseeq (type + 1, "pixmap")
		  && streq (text, ttype_strings [TEXTURE_PIXMAP]))
		 || (strcaseeq (type, "solid")
		     && streq (text, ttype_strings [TEXTURE_SOLID]))
#ifdef HAVE_LIBWMFUN
		 || (library && function && strcaseeq (type, "function") &&
		     strncaseeq (library, "libwmfun", strlen ("libwmfun")) &&
		     ((strcaseeq (function, "bilinear")
		       && streq (text, ttype_strings [TEXTURE_BILINEAR])) ||
		      (strcaseeq (function, "fade")
		       && streq (text, ttype_strings [TEXTURE_FADE])) ||
		      (strcaseeq (function, "waves")
		       && streq (text, ttype_strings [TEXTURE_WAVES])))) 
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT
		 || (strcaseeq (type, "igradient")
		     && streq (text, ttype_strings [TEXTURE_INTERWOVEN]))
#endif /* HAVE_INTERWOVEN_GRADIENT */
		 || (strcaseeq (type + 2, "gradient") && tolower (*type) == 'm'
		     && streq (text, ttype_strings [TEXTURE_GRADIENT]))
		 || (strcaseeq (type + 2, "gradient") && tolower (*type) == 't'
		     && streq (text, ttype_strings [TEXTURE_TGRADIENT]))
		 || (strcaseeq (type, "none")
		     && streq (text, ttype_strings [TEXTURE_NONE]))))))
   {
      unsigned n;

      for (n = WMGetPropListItemCount (array); n; n--)
	 WMDeleteFromPLArray (array, 0); /* clear array */
      
      if (streq (text, ttype_strings [TEXTURE_SOLID]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("solid"));
	 WMInsertInPLArray (array, 1, WMCreatePLString ("white"));
      }
      else if (streq (text, ttype_strings [TEXTURE_NONE]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("none"));
      }
      else if (streq (text, ttype_strings [TEXTURE_PIXMAP]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("spixmap"));
	 WMInsertInPLArray (array, 1,
			    WMCreatePLString (PKGDATADIR "/black.xpm"));
	 WMInsertInPLArray (array, 2, WMCreatePLString ("white"));
      }
      else if (streq (text, ttype_strings [TEXTURE_GRADIENT]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("mhgradient"));
	 WMInsertInPLArray (array, 1, WMCreatePLString ("blue"));
	 WMInsertInPLArray (array, 2, WMCreatePLString ("yellow"));
	 WMInsertInPLArray (array, 3, WMCreatePLString ("blue"));
      }
#ifdef HAVE_LIBWMFUN
      else if (streq (text, ttype_strings [TEXTURE_BILINEAR]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("function"));
	 WMInsertInPLArray (array, 1, WMCreatePLString ("libwmfun.so"));
	 WMInsertInPLArray (array, 2, WMCreatePLString ("bilinear"));
	 WMInsertInPLArray (array, 3, WMCreatePLString ("black"));
	 WMInsertInPLArray (array, 4, WMCreatePLString ("yellow"));
	 WMInsertInPLArray (array, 5, WMCreatePLString ("white"));
	 WMInsertInPLArray (array, 6, WMCreatePLString ("red"));
      }
      else if (streq (text, ttype_strings [TEXTURE_FADE]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("function"));
	 WMInsertInPLArray (array, 1, WMCreatePLString ("libwmfun.so"));
	 WMInsertInPLArray (array, 2, WMCreatePLString ("fade"));
	 WMInsertInPLArray (array, 3, WMCreatePLString ("--from"));
	 WMInsertInPLArray (array, 4, WMCreatePLString ("black"));
	 WMInsertInPLArray (array, 5, WMCreatePLString ("--to"));
	 WMInsertInPLArray (array, 6, WMCreatePLString ("white"));
      }
      else if (streq (text, ttype_strings [TEXTURE_WAVES]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("function"));
	 WMInsertInPLArray (array, 1, WMCreatePLString ("libwmfun.so"));
	 WMInsertInPLArray (array, 2, WMCreatePLString ("waves"));
	 WMInsertInPLArray (array, 3, WMCreatePLString ("--from"));
	 WMInsertInPLArray (array, 4, WMCreatePLString ("black"));
	 WMInsertInPLArray (array, 5, WMCreatePLString ("--to"));
	 WMInsertInPLArray (array, 6, WMCreatePLString ("white"));
	 WMInsertInPLArray (array, 7, WMCreatePLString ("5"));
	 WMInsertInPLArray (array, 8, WMCreatePLString ("7"));
      }
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT
      else if (streq (text, ttype_strings [TEXTURE_INTERWOVEN]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("igradient"));
	 WMInsertInPLArray (array, 1, WMCreatePLString ("black"));
	 WMInsertInPLArray (array, 2, WMCreatePLString ("yellow"));
	 WMInsertInPLArray (array, 3, WMCreatePLString ("1"));
	 WMInsertInPLArray (array, 4, WMCreatePLString ("white"));
	 WMInsertInPLArray (array, 5, WMCreatePLString ("red"));
	 WMInsertInPLArray (array, 6, WMCreatePLString ("1"));
      }
#endif /* HAVE_INTERWOVEN_GRADIENT */
      else if (streq (text, ttype_strings [TEXTURE_TGRADIENT]))
      {
	 WMInsertInPLArray (array, 0, WMCreatePLString ("thgradient"));
	 WMInsertInPLArray (array, 1,
			    WMCreatePLString (PKGDATADIR "/black.xpm"));
	 WMInsertInPLArray (array, 2, WMCreatePLString ("128"));
	 WMInsertInPLArray (array, 3, WMCreatePLString ("yellow"));
	 WMInsertInPLArray (array, 4, WMCreatePLString ("blue"));
      } /* nothing to do for () */
	 
      show_texture_dialog (wtexture);
      toggle_save (changed = YES, KEY_OF_TEXTURE);
   }
}

static void
workspace_toggle (GtkWidget *widget, gpointer ptr)
/*
 *  Toggle workspace name.
 */
{
   wtexture_t	*wtexture = (wtexture_t *) ptr;
   char		*text     = gtk_object_get_user_data (GTK_OBJECT (widget));
   
   for (wtexture->wsnumber = 0;
	workspace_names [wtexture->wsnumber];
	wtexture->wsnumber++)
      if (streq (workspace_names [wtexture->wsnumber], text))
	 break;
   wsnumber = wtexture->wsnumber;
   show_texture_dialog (wtexture);
}

static void
set_opacity (GtkWidget *widget, gpointer ptr)
/*
 *  Set opacity.
 *
 *  Side effects:
 *	Window Maker attribute is updated
 */
{
   char	      *value;
   wtexture_t *wtexture = (wtexture_t *) ptr;
   proplist_t  array 	  = get_texture_array (wtexture);

   value = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
   WMDeleteFromPLArray (array, 2);
   WMInsertInPLArray (array, 2, WMCreatePLString (value));
   toggle_save (changed = YES, KEY_OF_TEXTURE);

   show_pixmap (KEY_OF_TEXTURE,
		WMGetFromPLString (WMGetFromPLArray (array, 1)),
		wtexture->wpixmap [PIXMAP_PIXMAP]);

}

static void
file_selection (GtkWidget *widget, wtexture_t *wtexture)
/*
 *  Show pixmap file selection popup window.
 *
 *  No return value.
 */
{
   static GtkWidget	*filesel = NULL;

   if (!filesel)
   {
      GtkFileSelection *fs;

      /*
       *  Fileselection dialog window
       */
      filesel = gtk_file_selection_new (_("Pixmap file browser"));
      fs      = GTK_FILE_SELECTION (filesel);
      gtk_file_selection_hide_fileop_buttons (fs);
      gtk_window_set_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE);
      gtk_object_set_user_data (GTK_OBJECT (fs), wtexture);
      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_pixmap),
			  (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));
      
      /*
       *  Initial filename and pixmap display window
       */
      {
	 proplist_t	array = get_texture_array (wtexture);
	 char		*filename;
	 
	 filename = get_pixmap_path (WMGetFromPLString (WMGetFromPLArray (array, 1)));
	 if (filename)
	    gtk_file_selection_set_filename (fs, filename);
	 /*
	  *  Display of pixmap 
	  */
	 {
	    GtkWidget *vbox  = gtk_vbox_new (TRUE, 0);
	    GtkWidget *hbox  = gtk_hbox_new (TRUE, 0);
	    GtkWidget *pixmap = show_pixmap (KEY_OF_TEXTURE,
					     filename
					     ? filename
					     : PKGDATADIR "/black.xpm", NULL);
	    gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
	    if (strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "iconback")
	        || strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspaceback")
		|| strcaseeq (WMGetFromPLString (KEY_OF_TEXTURE), "workspacespecificback"))
	    {
	       GtkWidget *frame = gtk_frame_new (NULL);
	       
	       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
	       gtk_container_add (GTK_CONTAINER (frame), pixmap);
	       gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
	    }
	    else
	    {
	       gtk_box_pack_start (GTK_BOX (vbox), pixmap, FALSE, FALSE, 0);
	    }
	    gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
	    
	    {
	       GtkWidget *parent = fs->dir_list;
	       while (parent && !GTK_IS_BOX (parent))
		  parent = parent->parent;
	       
	       gtk_box_pack_start (GTK_BOX (parent ? parent : fs->main_vbox),
				   hbox, FALSE, FALSE, 10);
	    }

	    gtk_signal_connect (GTK_OBJECT (fs->selection_entry), "changed",
				GTK_SIGNAL_FUNC (update_pixmap), fs);
	    gtk_object_set_data (GTK_OBJECT (fs), "preview", pixmap);
	    gtk_widget_show_all (hbox);
	 }
	 if (filename)
	    Free (filename);
      }
      /*
       *  Optionmenu pixmap path
       */
      {
	 const char **pathlist;
	 unsigned     n;
	 GtkWidget   *box;
	 GtkWidget  **wpixmap;
	 proplist_t  plpath      = WMCreatePLString ("PixmapPath");
	 proplist_t  pixmap_path = WMGetFromPLDictionary (windowmaker, plpath);
	 proplist_t  pltitle     = WMCreatePLString ("Title");
	 proplist_t  plinfo      = WMCreatePLString ("Info");
	 proplist_t  keydef      = WMGetFromPLDictionary (wmconfig, plpath);
	 proplist_t  title_text  = WMGetFromPLDictionary (keydef, pltitle);
	 proplist_t  info_text   = WMGetFromPLDictionary (keydef, plinfo);

	 WMReleasePropList (plpath);
	 WMReleasePropList (plinfo);
	 WMReleasePropList (pltitle);
	 
	 wpixmap = gtk_object_get_user_data (GTK_OBJECT (widget));
	 
	 pathlist = Calloc (WMGetPropListItemCount (pixmap_path) + 1,
			    sizeof (char *));

	 for (n = 0; n < WMGetPropListItemCount (pixmap_path); n++)
	    pathlist [n] = WMGetFromPLString (WMGetFromPLArray (pixmap_path, n));
	 pathlist [n] = NULL;

	 box = generate_option_menu (D_(WMGetFromPLString (title_text)), tooltips,
				     D_(WMGetFromPLString (info_text)),
				     pathlist, pathlist [0], NULL,
				     change_path, filesel);
	 gtk_box_pack_start (GTK_BOX (fs->main_vbox), box, FALSE, TRUE, 5);
	 Free (pathlist);
      }
   }
   
   if (!GTK_WIDGET_VISIBLE (filesel))
      gtk_widget_show (filesel);
   else
      gtk_widget_destroy (filesel);
}

static void
update_pixmap (GtkWidget *widget, GtkFileSelection *fs)
{
   const char *path     = gtk_file_selection_get_filename (fs);
   wtexture_t *wtexture = gtk_object_get_user_data (GTK_OBJECT (fs));
   GtkWidget  *pixmap	= gtk_object_get_data (GTK_OBJECT (fs), "preview");
   char	      *filename;

   if (!strlen (path) || (strlen (path) && path [strlen (path) - 1] == '/'))
      return;

   filename = get_pixmap_path (path);
   if (filename)
   {
      proplist_t array = get_texture_array (wtexture);

      show_pixmap (KEY_OF_TEXTURE, filename, pixmap);
      Free (filename);
   }
}

static void
change_path (GtkWidget *widget, gpointer filesel)
/*
 *  Change current path of fileselection dialog 'filesel'.
 *  'path' is given by user data of the option menu 'widget'.
 *
 *  No return value.
 */
{
   const char *path = gtk_object_get_user_data (GTK_OBJECT (widget));

   if (strlen (path) && path [strlen (path) - 1] != '/') /* append a slash */
   {
      char *tmp = g_strconcat (path, "/", NULL);
      gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), tmp);
      Free (tmp);
   }
   else
      gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), path);
}

static void
set_pixmap (GtkWidget *widget, gpointer filesel)
/*
 *  Set pixmap name of texture.
 *
 *  No return value.
 *
 *  Side effects:
 *	value of wmaker array element 1 of attribute wtexture->key is modified.
 *	update of filename entry and pixmap
 *	
 */
{
   const char		*filename;
   GtkFileSelection	*fs       = GTK_FILE_SELECTION (filesel);
   wtexture_t		*wtexture = gtk_object_get_user_data (GTK_OBJECT (fs));
   const char		*path     = gtk_file_selection_get_filename (fs);

   if ((filename = strrchr (path, '/'))) /* remove path components */
      filename++;
   else
      filename = path;
   /*
    *  Check if pixmap is in PixmapPath
    */
   {
      char *tmp = get_pixmap_path (filename);

      if (!tmp)				/* pixmap not found in PixmapPath */
	 filename = path;
      else
	 Free (tmp);
   }
   
   {
      proplist_t array = get_texture_array (wtexture);
      
      WMDeleteFromPLArray (array, 1);
      WMInsertInPLArray (array, 1, WMCreatePLString ((char *) filename));

      show_pixmap (KEY_OF_TEXTURE, filename, wtexture->wpixmap [PIXMAP_PIXMAP]);
      g_signal_handler_block (wtexture->wpixmap [PIXMAP_NAME],
			      GPOINTER_TO_INT (gtk_object_get_user_data
	                      GTK_OBJECT (wtexture->wpixmap [PIXMAP_NAME])));
      gtk_entry_set_text (GTK_ENTRY (wtexture->wpixmap [PIXMAP_NAME]),filename);
      g_signal_handler_unblock (wtexture->wpixmap [PIXMAP_NAME],
			        GPOINTER_TO_INT (gtk_object_get_user_data
	                        GTK_OBJECT (wtexture->wpixmap [PIXMAP_NAME])));

      toggle_save (changed = YES, KEY_OF_TEXTURE);
   }
   
}

static GtkWidget *
new_list_item (wtexture_t *wtexture, proplist_t color)
/*
 *  Allocate new color cell in color list.
 *
 *  Return value:
 *	list item
 */
{
   GtkWidget *list_item;
   GtkWidget *preview;
   GtkWidget *lhbox;
      
   list_item = gtk_list_item_new ();
   gtk_widget_show (list_item);
   gtk_container_set_border_width (GTK_CONTAINER (list_item), 2);
      
   lhbox = gtk_hbox_new (TRUE, 0);
   gtk_widget_show (lhbox);
   gtk_container_add (GTK_CONTAINER (list_item), lhbox);
			
   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
   gtk_object_set_data (GTK_OBJECT (preview), "wtexture", wtexture);
   gtk_preview_size (GTK_PREVIEW (preview), 30, 15);
   gtk_box_pack_start (GTK_BOX (lhbox), preview, FALSE, TRUE, 5);
   gtk_widget_show (preview);
   fill_preview (GTK_PREVIEW (preview), WMGetFromPLString (color));
   gtk_object_set_user_data (GTK_OBJECT (list_item), preview);
   gtk_object_set_user_data (GTK_OBJECT (preview), color);

   return list_item;
}

static char *last_color = NULL;		/* save last color name */

static void
color_right_button (GtkWidget *dummy, gpointer ptr)
{
   color_selection_dialog ((GTK_LIST (ptr)->selection)->data,
			   gtk_object_get_user_data (GTK_OBJECT (ptr)));
}

static gint
list_button_press (GtkWidget *widget, GdkEventButton *event, gpointer ptr)
{
   if (event->type == GDK_2BUTTON_PRESS)
      color_selection_dialog ((GTK_LIST (widget)->selection)->data,
			      gtk_object_get_user_data (GTK_OBJECT (widget)));
   else if (event->button == 3)
   {
      static GtkWidget *menu = NULL;
      GtkWidget	       *menu_item;
      static GtkWidget *remove_item = NULL;

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

	 menu_item = gtk_menu_item_new_with_label (_("Change"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (color_right_button), widget);

	 remove_item = menu_item = gtk_menu_item_new_with_label (_("Remove"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (list_remove), widget);

	 menu_item = gtk_menu_item_new_with_label (_("Insert"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (list_insert), widget);

	 menu_item = gtk_menu_item_new_with_label (_("Append"));
	 gtk_menu_append (GTK_MENU (menu), menu_item);
	 gtk_widget_show (menu_item);
	 gtk_signal_connect (GTK_OBJECT(menu_item), "activate",
			     GTK_SIGNAL_FUNC (list_append), widget);
      }
      gtk_widget_set_sensitive (remove_item,
				g_list_length (GTK_LIST (widget)->children) > 2);
      gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3, event->time);
   }
   
   return TRUE;
}

static void
list_insert (GtkWidget *widget, GtkWidget *list)
/*
 *  Insert new color cell before current position.
 *
 *  No return value.
 *
 *  Side effects:
 *	new element is inserted in wmaker array attribute wtexture->key
 */
{
   GtkWidget  *list_item;
   int	      position;
   wtexture_t *wtexture = gtk_object_get_user_data (GTK_OBJECT (list));
   proplist_t array    = get_texture_array (wtexture);
   proplist_t element  = WMCreatePLString (last_color?last_color:g_strdup ("black"));

   position = gtk_list_child_position (GTK_LIST (list),
				       (GTK_LIST (list)->selection)->data);
   
   WMInsertInPLArray (array, position + 2, element);
   list_item = new_list_item (wtexture, element);
   /*
    *  Insert list item at selection
    */
   {
      GList *gl = Calloc (1, sizeof (GList));
      
      gl->next = gl->prev = NULL;
      gl->data = list_item;

      gtk_list_insert_items (GTK_LIST (list), gl, position);
   }

   if (g_list_length (GTK_LIST (list)->children) > 2)
      gtk_widget_set_sensitive (wtexture->wgradient [GRADIENT_REMOVE], TRUE);
   else
      gtk_widget_set_sensitive (wtexture->wgradient [GRADIENT_REMOVE], FALSE);

   gtk_list_select_item (GTK_LIST (list), position);
   list_change (widget, list);
   toggle_save (changed = YES, KEY_OF_TEXTURE);
   update_gradient_preview (wtexture);
}

static void
list_append (GtkWidget *widget, GtkWidget *list)
/*
 *  Append new color cell after last position.
 *
 *  No return value.
 *
 *  Side effects:
 *	new element is inserted in wmaker array attribute wtexture->key
 */
{
   wtexture_t *wtexture = gtk_object_get_user_data (GTK_OBJECT (list));
   proplist_t array    = get_texture_array (wtexture);
   proplist_t element  = WMCreatePLString (last_color?last_color:g_strdup("black"));

   WMAddToPLArray (array, element);
   
   gtk_container_add (GTK_CONTAINER (list), new_list_item (wtexture, element));
   if (g_list_length (GTK_LIST (list)->children) > 2)
      gtk_widget_set_sensitive (wtexture->wgradient [GRADIENT_REMOVE], TRUE);
   else
      gtk_widget_set_sensitive (wtexture->wgradient [GRADIENT_REMOVE], FALSE);

   gtk_list_select_item (GTK_LIST (list),
			 g_list_length (GTK_LIST (list)->children) - 1);
   list_change (widget, list);

   toggle_save (changed = YES, KEY_OF_TEXTURE);
   update_gradient_preview (wtexture);
}

static void
list_remove (GtkWidget *widget, GtkWidget *list)
/*
 *  Remove color cell at current position.
 *
 *  No return value.
 *
 *  Side effects:
 *	one element of wmaker array attribute wtexture->key is removed.
 */
{
   GList      *tmp_list;
   GList      *clear_list;
   unsigned   i;
   wtexture_t *wtexture = gtk_object_get_user_data (GTK_OBJECT (list));
   proplist_t array    = get_texture_array (wtexture);
   unsigned   position;

   position = gtk_list_child_position (GTK_LIST (list),
				       (GTK_LIST (list)->selection)->data);
   
   tmp_list   = GTK_LIST (list)->selection;
   clear_list = NULL;

   /*
    *  Remove color from proplist array
    */
   for (i = 2; i < WMGetPropListItemCount (array); i++)
   {
      GtkPreview *p = gtk_object_get_user_data (GTK_OBJECT (tmp_list->data));
      
      if (WMGetFromPLArray (array, i)
	  == gtk_object_get_user_data (GTK_OBJECT (p)))
      {
	 if (last_color)
	    Free (last_color);
	 last_color = g_strdup (WMGetFromPLString (WMGetFromPLArray (array, i)));
	 WMDeleteFromPLArray (array, i);
	 break;
      }
   }
   
   /*
    *  Remove list item
    */
   while (tmp_list)
   {
      clear_list = g_list_prepend (clear_list, tmp_list->data);
      tmp_list = tmp_list->next;
   }

   clear_list = g_list_reverse (clear_list);
   gtk_list_remove_items (GTK_LIST (list), clear_list);
   g_list_free (clear_list);

   if (g_list_length (GTK_LIST (list)->children))
   {
      if (position < g_list_length (GTK_LIST (list)->children))
	 gtk_list_select_item (GTK_LIST (list), position);
      else
	 gtk_list_select_item (GTK_LIST (list), position - 1);
   }

   if (g_list_length (GTK_LIST (list)->children) > 2)
      gtk_widget_set_sensitive (wtexture->wgradient [GRADIENT_REMOVE], TRUE);
   else
      gtk_widget_set_sensitive (wtexture->wgradient [GRADIENT_REMOVE], FALSE);

   toggle_save (changed = YES, KEY_OF_TEXTURE);
   update_gradient_preview (wtexture);
}

static void
list_change (GtkWidget *widget, GtkWidget *list)
/*
 *  Popup a colorselection dialog to change select color cell.
 *
 *  No return value.
 *
 *  Side effects:
 *	value of wmaker array attribute wtexture->key is modified.
 */
{
   wtexture_t *wtexture;

   wtexture = (wtexture_t *) gtk_object_get_user_data (GTK_OBJECT (list));
   
   color_selection_dialog ((GTK_LIST (list)->selection)->data, wtexture->key);
}

static void
set_pixmap_name (GtkWidget *widget, gpointer ptr)
/*
 *  Update pixmap string value.
 *
 *  No return value.
 */
{
   wtexture_t *wtexture  = (wtexture_t *) ptr;
   proplist_t element;
   proplist_t array = get_texture_array (wtexture);

   element = WMCreatePLString ((char *) gtk_entry_get_text (GTK_ENTRY (widget)));

   WMDeleteFromPLArray (array, 1);	/* filename */
   WMInsertInPLArray (array, 1, element);
   
   toggle_save (changed = YES, KEY_OF_TEXTURE);
}

static char **
get_workspace_names (void)
{
   char		*filename = get_gnustep_path ("Defaults/WMState");
   proplist_t	state     = read_proplist (filename);
   proplist_t	ws        = WMCreatePLString ("Workspaces");
   proplist_t	name	  = WMCreatePLString ("Name");
   char		**array   = NULL;
   unsigned	pos       = 0;

   if (state)
   {
      proplist_t workspaces = WMGetFromPLDictionary (state, ws);
      unsigned	 i, n;
      
      if (workspaces && (n = WMGetPropListItemCount (workspaces)) > 0)
      {
	 array         = Calloc (n + 2, sizeof (char *));
	 array [pos++] = g_strdup ("Default");
	 for (i = 0; i < n; i++)
	 {
	    proplist_t wspace = WMGetFromPLArray (workspaces, i);

	    if (wspace)
	    {
	       proplist_t wname = WMGetFromPLDictionary (wspace, name);

	       array [pos++] = wname ? WMGetFromPLString (wname) : NULL;
	    }
	    else
	       array [pos++] = NULL;
	 }
      }
   }

   if (!array)
   {
      array         = Calloc (2, sizeof (char *));
      array [pos++] = g_strdup ("Default");
      array [pos++] = NULL;
   }
   else
      array [pos++] = NULL;

   Free (filename);
   WMReleasePropList (ws);
   WMReleasePropList (name);
   
   return array;
}

static void
init_types (void)
/*
 * Initialize local string variables and option menus ...
 */
{
   unsigned i;
   
   ttype_strings = Calloc (TEXTURE_LAST, sizeof (char *));
   ptype_strings = Calloc (PIXMAP_LAST, sizeof (char *));
   gtype_strings = Calloc (GRADIENT_LAST, sizeof (char *));

   ttype_strings [TEXTURE_SOLID]      = _("Solid color");
   ttype_strings [TEXTURE_GRADIENT]   = _("Gradient");
   ttype_strings [TEXTURE_PIXMAP]     = _("Pixmap");
   ttype_strings [TEXTURE_TGRADIENT]  = _("Textured gradient");
   ttype_strings [TEXTURE_NONE]       = _("None");
   ttype_strings [TEXTURE_DEFAULT]    = _("Default");
#ifdef HAVE_LIBWMFUN
   ttype_strings [TEXTURE_BILINEAR]   = _("Bilinear gradient");
   ttype_strings [TEXTURE_FADE]       = _("Fading gradient");
   ttype_strings [TEXTURE_WAVES]      = _("Waves gradient");
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT   
   ttype_strings [TEXTURE_INTERWOVEN] = _("Interwoven gradient");
#endif /* HAVE_INTERWOVEN_GRADIENT */
   
   ptype_strings [PIXMAP_SCALED]    = _("Scaled");
   ptype_strings [PIXMAP_TILED]     = _("Tiled");
   ptype_strings [PIXMAP_CENTERED]  = _("Centered");
   ptype_strings [PIXMAP_MAXASPECT] = _("Scaled (x:y)");

   gtype_strings [GRADIENT_HOR]  = _("Horizontal");
   gtype_strings [GRADIENT_VER]  = _("Vertical");
   gtype_strings [GRADIENT_DIAG] = _("Diagonal");

   i = 0;
   option_t_normal       = Calloc (TEXTURE_LAST + 1, sizeof (char *));
   option_t_normal [i++] = ttype_strings [TEXTURE_SOLID];
   option_t_normal [i++] = ttype_strings [TEXTURE_PIXMAP];
   option_t_normal [i++] = ttype_strings [TEXTURE_GRADIENT];
   option_t_normal [i++] = ttype_strings [TEXTURE_TGRADIENT];
#ifdef HAVE_LIBWMFUN
   option_t_normal [i++] = ttype_strings [TEXTURE_BILINEAR];
   option_t_normal [i++] = ttype_strings [TEXTURE_FADE];
   option_t_normal [i++] = ttype_strings [TEXTURE_WAVES];
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT   
   option_t_normal [i++] = ttype_strings [TEXTURE_INTERWOVEN];
#endif /* HAVE_INTERWOVEN_GRADIENT */
   option_t_normal [i++] = NULL;
   
   i = 0;
   option_t_wspace       = Calloc (TEXTURE_LAST + 1, sizeof (char *));
   option_t_wspace [i++] = ttype_strings [TEXTURE_SOLID];
   option_t_wspace [i++] = ttype_strings [TEXTURE_PIXMAP];
   option_t_wspace [i++] = ttype_strings [TEXTURE_GRADIENT];
   option_t_wspace [i++] = ttype_strings [TEXTURE_TGRADIENT];
#ifdef HAVE_LIBWMFUN
   option_t_wspace [i++] = ttype_strings [TEXTURE_BILINEAR];
   option_t_wspace [i++] = ttype_strings [TEXTURE_FADE];
   option_t_wspace [i++] = ttype_strings [TEXTURE_WAVES];
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT   
   option_t_wspace [i++] = ttype_strings [TEXTURE_INTERWOVEN];
#endif /* HAVE_INTERWOVEN_GRADIENT */
   option_t_wspace [i++] = ttype_strings [TEXTURE_NONE];
   option_t_wspace [i++] = NULL;

   i = 0;
   option_t_wspecific     = Calloc (TEXTURE_LAST + 1, sizeof (char *));
   option_t_wspecific [i++] = ttype_strings [TEXTURE_SOLID];
   option_t_wspecific [i++] = ttype_strings [TEXTURE_PIXMAP];
   option_t_wspecific [i++] = ttype_strings [TEXTURE_GRADIENT];
   option_t_wspecific [i++] = ttype_strings [TEXTURE_TGRADIENT];
#ifdef HAVE_LIBWMFUN
   option_t_wspecific [i++] = ttype_strings [TEXTURE_BILINEAR];
   option_t_wspecific [i++] = ttype_strings [TEXTURE_FADE];
   option_t_wspecific [i++] = ttype_strings [TEXTURE_WAVES];
#endif /* HAVE_LIBWMFUN */
#ifdef HAVE_INTERWOVEN_GRADIENT   
   option_t_wspecific [i++] = ttype_strings [TEXTURE_INTERWOVEN];
#endif /* HAVE_INTERWOVEN_GRADIENT */
   option_t_wspecific [i++] = ttype_strings [TEXTURE_NONE];
   option_t_wspecific [i++] = ttype_strings [TEXTURE_DEFAULT];
   option_t_wspecific [i++] = NULL;

   option_p_normal     = Calloc (4, sizeof (char *));
   option_p_normal [0] = ptype_strings [PIXMAP_CENTERED];
   option_p_normal [1] = ptype_strings [PIXMAP_TILED];
   option_p_normal [2] = ptype_strings [PIXMAP_SCALED];
   option_p_normal [3] = NULL;
   
   option_p_wspace     = Calloc (5, sizeof (char *));
   option_p_wspace [0] = ptype_strings [PIXMAP_CENTERED];
   option_p_wspace [1] = ptype_strings [PIXMAP_TILED];
   option_p_wspace [2] = ptype_strings [PIXMAP_SCALED];
   option_p_wspace [3] = ptype_strings [PIXMAP_MAXASPECT];
   option_p_wspace [4] = NULL;

   option_g     = Calloc (4, sizeof (char *));
   option_g [0] = gtype_strings [GRADIENT_HOR];
   option_g [1] = gtype_strings [GRADIENT_VER];
   option_g [2] = gtype_strings [GRADIENT_DIAG];
   option_g [3] = NULL;
}

static void
update_texture (proplist_t key, gpointer ptr, proplist_t value,
		const char *path)
{
   if (WMIsPLArray (value))
   {
      wtexture_t *wtexture = (wtexture_t *) ptr;

      wtexture->path = path;
      WMPutInPLDictionary (windowmaker, key, value);
      toggle_save (changed = YES, key);
      show_texture_dialog (wtexture);
   }
}

#ifdef HAVE_LIBWMFUN

static void
set_layers (GtkWidget *widget, gpointer ptr)
/*
 *  Set number of layers for waves gradient.
 *
 *  Side effects:
 *	Window Maker attribute is updated
 */
{
   char	      *value;
   wtexture_t *wtexture = (wtexture_t *) ptr;
   proplist_t  array 	= get_texture_array (wtexture);
   unsigned    n 	= WMGetPropListItemCount (array);
   
   value = gtk_entry_get_text (GTK_ENTRY (widget));
   WMDeleteFromPLArray (array, n - 2);
   WMInsertInPLArray (array, n - 2, WMCreatePLString (value));
   toggle_save (changed = YES, KEY_OF_TEXTURE);

   update_wmfun_preview (wtexture, &wtexture->wwaves [WAVES_PREVIEW]);
}

static void
set_frequency (GtkWidget *widget, gpointer ptr)
/*
 *  Set number of layers for waves gradient.
 *
 *  Side effects:
 *	Window Maker attribute is updated
 */
{
   char	      *value;
   wtexture_t *wtexture = (wtexture_t *) ptr;
   proplist_t  array 	= get_texture_array (wtexture);
   unsigned    n 	= WMGetPropListItemCount (array);
   
   value = gtk_entry_get_text (GTK_ENTRY (widget));
   WMDeleteFromPLArray (array, n - 1);
   WMInsertInPLArray (array, n - 1, WMCreatePLString (value));
   toggle_save (changed = YES, KEY_OF_TEXTURE);

   update_wmfun_preview (wtexture, &wtexture->wwaves [WAVES_PREVIEW]);
}

#endif /* HAVE_LIBWMFUN */

#ifdef HAVE_INTERWOVEN_GRADIENT

static void
set_thickness_1 (GtkWidget *widget, gpointer ptr)
/*
 *  Set thickness 1 of interwoven gradient.
 *
 *  Side effects:
 *	Window Maker attribute is updated
 */
{
   char       *value;
   wtexture_t *wtexture = (wtexture_t *) ptr;
   proplist_t  array 	= get_texture_array (wtexture);
   //unsigned    n 	= WMGetPropListItemCount (array);
   
   value = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
   WMDeleteFromPLArray (array, 3);
   WMInsertInPLArray (array, 3, WMCreatePLString (value));
   toggle_save (changed = YES, KEY_OF_TEXTURE);

   update_interwoven_preview (wtexture,
			      &wtexture->winterwoven [INTERWOVEN_PREVIEW]);
}

static void
set_thickness_2 (GtkWidget *widget, gpointer ptr)
/*
 *  Set thickness 2 of interwoven gradient.
 *
 *  Side effects:
 *	Window Maker attribute is updated
 */
{
   char       *value;
   wtexture_t *wtexture = (wtexture_t *) ptr;
   proplist_t  array 	= get_texture_array (wtexture);
   //unsigned    n 	= WMGetPropListItemCount (array);
   
   value = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
   WMDeleteFromPLArray (array, 6);
   WMInsertInPLArray (array, 6, WMCreatePLString (value));
   toggle_save (changed = YES, KEY_OF_TEXTURE);

   update_interwoven_preview (wtexture,
			      &wtexture->winterwoven [INTERWOVEN_PREVIEW]);
}

#endif /* HAVE_INTERWOVEN_GRADIENT */
