/*
 *  simple.c:		Simple dialog widgets
 *
 *  Written by:		Ullrich Hafner
 *  
 *  Copyright (C) 1999 Ullrich Hafner <hafner@bigfoot.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 */

/*
 *  $Date: 2000/09/03 19:22:05 $
 *  $Author: hafner $
 *  $Revision: 1.15 $
 *  $State: Exp $
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>

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

#include "simple.h"
#include "dialog.h"
#include "menu.h"
#include "texture.h"
#include "misc.h"
#include "window.h"
#include "themebrowser.h"

#include "error.h"

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

			     global variables
  
*******************************************************************************/

extern bool_t	  changed;		/* window.c */
extern proplist_t windowmaker;		/* window.c */
 
/*******************************************************************************

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

#ifdef HAVE_LIBWMFUN
static const char **color_types = NULL;

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

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

static void
color_toggle (GtkWidget *widget, gpointer ptr);
static void
show_color_buttons (text_color_t *text_color);
static void
update_text_color (proplist_t key, gpointer ptr, proplist_t value,
		   const char *path);
#endif /* HAVE_LIBWMFUN */
static void
set_bool (GtkWidget *widget, gpointer ptr);
static void
set_ibool (GtkWidget *widget, gpointer ptr);
static void
append_dialog (GtkWidget *container, GtkWidget *left, GtkWidget *right,
	       GtkAttachOptions attach);
static void
set_string (GtkWidget *widget, gpointer ptr);
static void
set_int (GtkWidget *widget, gpointer ptr);
static void
set_entry_text (GtkWidget *widget, gpointer ptr);
static void
convert_pango_to_fontconfig (const char *pangoname, char *fcname);
static void
convert_fontconfig_to_pango (const char *fcname, char *pangoname);
static void
set_fontname (GtkWidget *widget, gpointer ptr);
static void
fontsel_dialog (GtkWidget *widget, gpointer ptr);
static void
update_entry (proplist_t key, gpointer ptr, proplist_t value,
	      const char *path);
static void
update_color (proplist_t key, gpointer ptr, proplist_t value,
	      const char *path);
static void
update_boolean (proplist_t key, gpointer ptr, proplist_t value,
		const char *path);
static void
update_iboolean (proplist_t key, gpointer ptr, proplist_t value,
		 const char *path);
static void
update_int (proplist_t key, gpointer ptr, proplist_t value,
	    const char *path);

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

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

void
boolean_dialog (GtkWidget *table, proplist_t key, proplist_t value,
		proplist_t info)
{
   GtkWidget *label  = gtk_label_new (D_(WMGetFromPLString (info)));
   GtkWidget *button = gtk_check_button_new ();

   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
				 convert_bool (value));
   gtk_signal_connect (GTK_OBJECT (button), "toggled",
		       GTK_SIGNAL_FUNC (set_bool), (gpointer) key);
   append_dialog (table, label, button, GTK_SHRINK);

   connect_update_function (key, button, update_boolean);
}

void
iboolean_dialog (GtkWidget *table, proplist_t key, proplist_t value,
		 proplist_t info)
{
   GtkWidget *label  = gtk_label_new (D_(WMGetFromPLString (info)));
   GtkWidget *button = gtk_check_button_new ();

   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
				 convert_bool (value) ? FALSE : TRUE);
   gtk_signal_connect (GTK_OBJECT (button), "toggled",
		       GTK_SIGNAL_FUNC (set_ibool), (gpointer) key);
   append_dialog (table, label, button, GTK_SHRINK);

   connect_update_function (key, button, update_iboolean);
}

void
string_dialog (GtkWidget *table, proplist_t key, proplist_t value,
	       proplist_t range, proplist_t rinfo, proplist_t info)
{
   GtkWidget	*option_menu;
   GtkWidget	*label = gtk_label_new (D_(WMGetFromPLString (info)));
   const char	**values;
   unsigned	n, current = 0;
   
   values = Calloc (WMGetPropListItemCount (range) + 1, sizeof (char *));
   for (n = 0; n < WMGetPropListItemCount (range); n++)
   {
      proplist_t element = WMGetFromPLArray (range, n);
	       
      if (strcaseeq (WMGetFromPLString (element), WMGetFromPLString (value)))
	 current = n;
      values [n] = WMGetFromPLString (element);
   }
   values [n] = NULL;
		  
   option_menu = generate_option_menu (NULL, NULL, NULL, values,
				       values [current], rinfo, set_string, key);
   append_dialog (table, option_menu, label, GTK_FILL | GTK_SHRINK);

   {
      omenudata_t *data = Calloc (1, sizeof (omenudata_t));
      
      data->omenu  = GTK_OPTION_MENU (option_menu);
      data->values = values;
      connect_update_function (key, data, update_optionmenu);
   }
}

void
int_dialog (GtkWidget *table, proplist_t key, proplist_t value,
	    proplist_t range, proplist_t rinfo, proplist_t info)
{
   GtkWidget    *label 	= gtk_label_new (D_(WMGetFromPLString (info)));
   GtkWidget    *hbox  	= gtk_hbox_new (FALSE, 5);
   GtkEntry	**array = Calloc (WMGetPropListItemCount (range) / 2 + 1,
				  sizeof (GtkEntry *));
   unsigned	n;
   
   for (n = 0; n < WMGetPropListItemCount (range); n += 2)
   {
      GtkWidget *spinner;
      GtkObject *adj;
      char	*m = WMGetFromPLString (WMGetFromPLArray (range, n));
      char	*M = WMGetFromPLString (WMGetFromPLArray (range, n + 1));
      char 	*v;
	       
      if (WMIsPLArray (value))
	 v = WMGetFromPLString (WMGetFromPLArray (value, n / 2));
      else
	 v = WMGetFromPLString (value);
	       
      adj = gtk_adjustment_new (strtod (v, NULL),
				strtod (m, NULL), strtod (M, NULL),
				1.0, 5.0, 0.0);
      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_object_set_user_data (GTK_OBJECT (spinner),
				(gpointer) (spinner + n / 2));

      gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
			  GTK_SIGNAL_FUNC (set_int), (gpointer) key);
      gtk_signal_connect (GTK_OBJECT (&GTK_SPIN_BUTTON (spinner)->entry),
			  "changed", GTK_SIGNAL_FUNC (set_entry_text),
			  (gpointer) key);

      array [n / 2] = &GTK_SPIN_BUTTON (spinner)->entry;
   }
   array [n / 2] = NULL;

   append_dialog (table, hbox, label, GTK_FILL | GTK_SHRINK);

   connect_update_function (key, array, update_int);
}

void
text_dialog (GtkWidget *table, proplist_t key, proplist_t value,
	     proplist_t info)
{
   GtkWidget *entry = gtk_entry_new ();
   GtkWidget *label = gtk_label_new (D_(WMGetFromPLString (info)));
		  
   gtk_widget_set_usize (entry, 100, 0);
   gtk_entry_set_text (GTK_ENTRY (entry), WMGetFromPLString (value));
   gtk_signal_connect (GTK_OBJECT (entry), "changed",
		       GTK_SIGNAL_FUNC (set_entry_text), key);
   append_dialog (table, entry, label, GTK_FILL | GTK_SHRINK);
   connect_update_function (key, entry, update_entry);
}

GtkWidget *
font_dialog (GtkWidget *page, proplist_t key, proplist_t value,
	     proplist_t info)
{
   GtkWidget *entry  = gtk_entry_new ();
   GtkWidget *frame  = gtk_frame_new (D_(WMGetFromPLString (info)));
   GtkWidget *hbox   = gtk_hbox_new (FALSE, 0);
   GtkWidget *bbox   = gtk_hbutton_box_new ();
   GtkWidget *button = gtk_button_new_with_label (_("Browse..."));

   gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
   gtk_container_add (GTK_CONTAINER (frame), hbox);

   gtk_entry_set_text (GTK_ENTRY (entry), WMGetFromPLString (value));
   gtk_signal_connect (GTK_OBJECT (entry), "changed",
		       GTK_SIGNAL_FUNC (set_entry_text), key);
   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 5);
   gtk_box_pack_start (GTK_BOX (hbox), bbox, FALSE, TRUE, 5);
   gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 5);

   gtk_signal_connect (GTK_OBJECT (button), "clicked",
		       GTK_SIGNAL_FUNC (fontsel_dialog), (gpointer) entry);

   gtk_box_pack_end (GTK_BOX (page), frame, TRUE, TRUE, 5);
   gtk_widget_show_all (frame);

   connect_update_function (key, entry, update_entry);
   
   return frame;
}

GtkWidget *
color_dialog (GtkWidget *page, proplist_t key, proplist_t value,
	      GtkTooltips *tooltips, proplist_t info, proplist_t title)
{
   GtkWidget *frame;
   GtkWidget *button = color_button (NULL, key, value, tooltips,
				     D_(WMGetFromPLString (info)));
   GtkWidget *hbox = gtk_hbox_new (TRUE, 0);

   if (title)
      frame = gtk_frame_new (D_(WMGetFromPLString (title)));
   else
      frame = gtk_frame_new (WMGetFromPLString (key));

   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
   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), button, FALSE, FALSE, 0);
   
   gtk_box_pack_start (GTK_BOX (page), frame, FALSE, TRUE, 5);
   gtk_widget_show_all (frame);

   connect_update_function (key,
			    gtk_object_get_user_data (GTK_OBJECT (button)),
			    update_color);
   
   return frame;
}

#ifdef HAVE_LIBWMFUN
GtkWidget *
extended_color_dialog (GtkWidget *page, proplist_t key, proplist_t value,
		       GtkTooltips *tooltips, proplist_t info, proplist_t title)
{
   GtkWidget   	*frame;
   GtkWidget   	*option_menu;
   text_color_t *text_color = Calloc (1, sizeof (text_color_t));
   GtkWidget   	*hbox 	    = gtk_hbox_new (FALSE, 5);

   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
   
   frame = gtk_frame_new (_("Color of title text"));
   text_color->key = key;

   if (!color_types)
   {
      color_types = Calloc (TCOLOR_LAST, sizeof (char *));
      
      color_types [TCOLOR_SIMPLE]   = _("Solid color");
      color_types [TCOLOR_COLORSET] = _("Drop shadow");
      color_types [TCOLOR_LAST]     = NULL;
   }

   option_menu = generate_option_menu (NULL, tooltips,
				       _("A solid color or a drop shadow."),
				       color_types, color_types [0], NULL,
				       color_toggle, text_color);
   text_color->option_menu = option_menu;
   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), option_menu, FALSE, FALSE, 0);

   text_color->solid_color = color_button (NULL, key, NULL, NULL, NULL);
   gtk_box_pack_start (GTK_BOX (hbox), text_color->solid_color,
		       FALSE, TRUE, 0);
   text_color->color_set [0] = color_button (NULL, key, NULL,
					     tooltips, _("Foreground"));
   gtk_box_pack_start (GTK_BOX (hbox), text_color->color_set [0],
		       FALSE, TRUE, 0);
   text_color->color_set [1] = color_button (NULL, key, NULL,
					     tooltips, _("Shadow 1"));
   gtk_box_pack_start (GTK_BOX (hbox), text_color->color_set [1],
		       FALSE, TRUE, 0);
   text_color->color_set [2] = color_button (NULL, key, NULL,
					     tooltips, _("Shadow 2"));
   gtk_box_pack_start (GTK_BOX (hbox), text_color->color_set [2],
		       FALSE, TRUE, 0);
   
   gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
   gtk_widget_show_all (frame);

   show_color_buttons (text_color);
   
   connect_update_function (key, text_color, update_text_color);
   
   return frame;
}
#endif /* HAVE_LIBWMFUN */

void
update_optionmenu (proplist_t key, gpointer ptr, proplist_t value,
		   const char *path)
{
   omenudata_t *data = (omenudata_t *) ptr;

   if (WMIsPLString (value))
      set_option_menu_default (data->omenu, data->values, WMGetFromPLString (value));
}

bool_t
convert_bool (proplist_t p)
/*
 *  Parse boolean values of WindowMaker file.
 *
 *  Return value:
 *	converted value
 */
{
   char *val;

   assert (p && (WMIsPLString (p) || WMIsPLData (p)));

   val = WMGetFromPLString (p);

   if (strcaseeq (val, "y") || strcaseeq (val, "yes")
       || strcaseeq (val, "t") || strcaseeq (val, "true")
       || strcaseeq (val, "1"))
      return TRUE;
   else if (strcaseeq (val, "n") || strcaseeq (val, "no")
	    || strcaseeq (val, "f") || strcaseeq (val, "false")
	    || strcaseeq (val, "0"))
      return FALSE;
   else
   {
      warning (_("Undefined boolean value '%s' converted to true."), val);
      return TRUE;
   }
}

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

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

#ifdef HAVE_LIBWMFUN
static void
color_toggle (GtkWidget *widget, gpointer ptr)
{
   char 	*text 	    = gtk_object_get_user_data (GTK_OBJECT (widget));
   text_color_t *text_color = (text_color_t *) ptr;
   proplist_t 	value 	    = WMGetFromPLDictionary (windowmaker, text_color->key);
   
   if (text == color_types [TCOLOR_SIMPLE])
   {
      if (!WMIsPLString (value))
      {
	 WMPutInPLDictionary (windowmaker, text_color->key,
				  WMCreatePLString ("tan"));
	 toggle_save (changed = YES, text_color->key);
	 show_color_buttons (text_color);
      }
   }
   else
   {
      if (!WMIsPLArray (value))
      {
	 proplist_t pl;
	 pl = WMCreatePLArray (WMCreatePLString ("function"),
				       WMCreatePLString ("libwmfun.so"),
				       WMCreatePLString ("drawPlainString"),
				       WMCreatePLString ("white"),
				       WMCreatePLString ("black"),
				       WMCreatePLString ("red"), NULL);
      
	 WMPutInPLDictionary (windowmaker, text_color->key, pl);
	 toggle_save (changed = YES, text_color->key);
	 show_color_buttons (text_color);
      }
   }
}

static void
show_color_buttons (text_color_t *text_color)
{
   proplist_t value = WMGetFromPLDictionary (windowmaker, text_color->key);
   
   if (WMIsPLArray (value))		/* check syntax of value */
   {
      if (!(WMGetPropListItemCount (value) == 6
	    && strcaseeq (WMGetFromPLString (WMGetFromPLArray (value, 0)),
			  "function")
	    && strncaseeq (WMGetFromPLString (WMGetFromPLArray (value, 1)),
			   "libwmfun", strlen ("libwmfun"))
	    && strcaseeq (WMGetFromPLString (WMGetFromPLArray (value, 2)),
			  "drawPlainString")))
      {
	 proplist_t pl;
	 pl = WMCreatePLArray (WMCreatePLString ("function"),
				       WMCreatePLString ("libwmfun.so"),
				       WMCreatePLString ("drawPlainString"),
				       WMCreatePLString ("white"),
				       WMCreatePLString ("black"),
				       WMCreatePLString ("red"), NULL);
	 
	 WMPutInPLDictionary (windowmaker, text_color->key, pl);
	 value = pl;
      }
      set_option_menu_default (GTK_OPTION_MENU (text_color->option_menu),
			       color_types, color_types [TCOLOR_COLORSET]);

      /*
       *  Set colors
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (text_color->color_set [0]));
	 fill_preview (GTK_PREVIEW (preview), WMGetFromPLString (WMGetFromPLArray (value, 3)));
	 gtk_object_set_user_data (GTK_OBJECT (preview), WMGetFromPLArray (value, 3));
	 preview = gtk_object_get_user_data (GTK_OBJECT (text_color->color_set [1]));
	 fill_preview (GTK_PREVIEW (preview), WMGetFromPLString (WMGetFromPLArray (value, 4)));
	 gtk_object_set_user_data (GTK_OBJECT (preview), WMGetFromPLArray (value, 4));
	 preview = gtk_object_get_user_data (GTK_OBJECT (text_color->color_set [2]));
	 fill_preview (GTK_PREVIEW (preview), WMGetFromPLString (WMGetFromPLArray (value, 5)));
	 gtk_object_set_user_data (GTK_OBJECT (preview), WMGetFromPLArray (value, 5));
      }
      gtk_widget_show (text_color->color_set [0]);
      gtk_widget_show (text_color->color_set [1]);
      gtk_widget_show (text_color->color_set [2]);
      gtk_widget_hide (text_color->solid_color);
   }
   else
   {
      gtk_widget_hide (text_color->color_set [0]);
      gtk_widget_hide (text_color->color_set [1]);
      gtk_widget_hide (text_color->color_set [2]);
      gtk_widget_show (text_color->solid_color);
      set_option_menu_default (GTK_OPTION_MENU (text_color->option_menu),
			       color_types, color_types [TCOLOR_SIMPLE]);
      /*
       *  Set default color
       */
      {
	 GtkWidget *preview;

	 preview = gtk_object_get_user_data (GTK_OBJECT (text_color->solid_color));
	 fill_preview (GTK_PREVIEW (preview), WMGetFromPLString (value));
	 gtk_object_set_user_data (GTK_OBJECT (preview), value);
      }
   }
}
#endif /* HAVE_LIBWMFUN */

static void
append_dialog (GtkWidget *container, GtkWidget *left, GtkWidget *right,
	       GtkAttachOptions attach)
{
   if (GTK_IS_TABLE (container))
   {
      unsigned *number = (unsigned *) gtk_object_get_user_data (GTK_OBJECT (container));
      unsigned row, col;
   
      row  = ++*number;
      col  = (row % 2) * 2;
      row /= 2;

      gtk_table_attach (GTK_TABLE (container), left, col, col + 1, row, row + 1,
			GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
      gtk_table_attach (GTK_TABLE (container), right, col + 1, col + 2, row, row + 1,
			attach, attach, 0, 0);

      gtk_widget_show_all (left);
      gtk_widget_show_all (right);
   }
   else if (GTK_IS_VBOX (container))
   {
      GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
      gtk_container_set_border_width (GTK_CONTAINER (container), 5);
      gtk_box_pack_start (GTK_BOX (hbox), right, FALSE, FALSE, 0);
      gtk_box_pack_start (GTK_BOX (hbox), left, FALSE, FALSE, 0);
      gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 0);
      gtk_widget_show_all (hbox);
   }
}

static void
set_bool (GtkWidget *widget, gpointer ptr)
/*
 *  Update boolean value.
 *
 *  No return value.
 */
{
   proplist_t key   = (proplist_t) ptr;
   char       *text = GTK_TOGGLE_BUTTON (widget)->active ? "Yes" : "No";
   proplist_t pl    = WMCreatePLString (text);
   
   WMPutInPLDictionary (windowmaker, key, pl);
   WMReleasePropList (pl);
   toggle_save (changed = YES, key);

   if (strcaseeq (WMGetFromPLString (key), "NewStyle"))
      check_widgetstyle ();
}

static void
set_ibool (GtkWidget *widget, gpointer ptr)
/*
 *  Update boolean value.
 *
 *  No return value.
 */
{
   proplist_t key  = (proplist_t) ptr;
   char       *text = GTK_TOGGLE_BUTTON (widget)->active ? "No" : "Yes";
   proplist_t pl   = WMCreatePLString (text);
   
   WMPutInPLDictionary (windowmaker, key, pl);
   WMReleasePropList (pl);
   toggle_save (changed = YES, key);

   if (strcaseeq (WMGetFromPLString (key), "NewStyle"))
      check_widgetstyle ();
}

static void
set_string (GtkWidget *widget, gpointer ptr)
/*
 *  Update string value.
 *
 *  No return value.
 */
{
   proplist_t key  = (proplist_t) ptr;
   char       *text = gtk_object_get_user_data (GTK_OBJECT (widget));
   proplist_t pl   = WMCreatePLString (text);
   
   WMPutInPLDictionary (windowmaker, key, pl);
   WMReleasePropList (pl);
   toggle_save (changed = YES, key);
}

static void
set_int (GtkWidget *widget, gpointer ptr)
/*
 *  Update unsigned value.
 *
 *  No return value.
 */
{
   proplist_t	 key = (proplist_t) ptr;
   GtkSpinButton *spinner;
   int		 val;
   char		 tmp [MAXSTRLEN];
   
   spinner = GTK_SPIN_BUTTON (gtk_object_get_user_data (GTK_OBJECT (widget)));
   val     = gtk_spin_button_get_value_as_int (spinner);
   
   sprintf (tmp, "%d", val);

   if (WMIsPLArray (WMGetFromPLDictionary (windowmaker, key)))
   {
      proplist_t array = WMGetFromPLDictionary (windowmaker, key);
      unsigned	 n;
      
      n = (GtkWidget *) gtk_object_get_user_data (GTK_OBJECT (spinner))
	  - GTK_WIDGET (spinner);
      WMDeleteFromPLArray (array, n);
      WMInsertInPLArray (array, n, WMCreatePLString (tmp));
   }
   else
   {
      proplist_t pl = WMCreatePLString (tmp);
      WMPutInPLDictionary (windowmaker, key, pl);
      WMReleasePropList (pl);
   }
   
   toggle_save (changed = YES, key);
}

static void
set_entry_text (GtkWidget *widget, gpointer ptr)
/*
 *  Update string value.
 *
 *  No return value.
 */
{
   proplist_t element;
   proplist_t key   = (proplist_t) ptr;
   proplist_t array = WMGetFromPLDictionary (windowmaker, key);

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

   if (WMIsPLArray (array))
   {
      WMDeleteFromPLArray (array, 1);	/* filename */
      WMInsertInPLArray (array, 1, element);
   }
   else
   {
      WMPutInPLDictionary (windowmaker, key, element);
      WMReleasePropList (element);
   }
   
   toggle_save (changed = YES, key);
}

static void
fontsel_dialog (GtkWidget *widget, gpointer ptr)
{
  static GtkWidget *fontsel = NULL;

  if (!fontsel)
  {
     GtkWidget			*entry;
     const char			*text;
     char			*pangotext;
     GtkFontSelectionDialog	*fs;
     
     entry   = GTK_WIDGET (ptr);
     text    = gtk_entry_get_text (GTK_ENTRY (entry));
     pangotext = malloc (strlen (text) + 1);
     fontsel = gtk_font_selection_dialog_new (_("Font selection"));
     fs	     = GTK_FONT_SELECTION_DIALOG (fontsel);
     
     gtk_window_set_position (GTK_WINDOW (fontsel), GTK_WIN_POS_MOUSE);
     gtk_object_set_user_data (GTK_OBJECT (fontsel), entry);
     convert_fontconfig_to_pango (text, pangotext);
     gtk_font_selection_dialog_set_font_name (fs, pangotext);
     free (pangotext);

     gtk_object_set_data (GTK_OBJECT (entry), "fontsel", fontsel);
     gtk_signal_connect (GTK_OBJECT (fontsel), "destroy",
			 GTK_SIGNAL_FUNC (gtk_widget_destroyed),
			 &fontsel);
     gtk_signal_connect  (GTK_OBJECT (fs->ok_button), "clicked",
			  GTK_SIGNAL_FUNC (set_fontname), fontsel);
     gtk_signal_connect_object (GTK_OBJECT (fs->ok_button), "clicked",
				GTK_SIGNAL_FUNC (gtk_widget_destroy),
				GTK_OBJECT (fontsel));
     gtk_signal_connect_object (GTK_OBJECT (fs->cancel_button), "clicked",
				GTK_SIGNAL_FUNC (gtk_widget_destroy),
				GTK_OBJECT (fontsel));
  }

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

struct font_attr_t {
  int slant;
  int weight;
  int width;
  int size;
};

/* Function to convert from GTK+ 2's Pango font names to the slightly
 * different format used by WindowMaker 0.90 (fontconfig?)
 * fcname must already be allocated and have a length of at least
 * strlen(pangoname) + 50. */
static void
convert_pango_to_fontconfig (const char *pangoname, char *fcname)
{
  const char * posn;
  char * base;
  int size;
  struct font_attr_t attr = { 0, 80, 100, 12 };

  posn = pangoname + strlen(pangoname) - 1;
  while (posn > pangoname && isdigit(*posn)) posn--;
  
  size = atoi(posn);
  if (size) attr.size = size;

  /* check for italic */
  if (posn - pangoname > 6 && strncmp(posn - 6, "Italic", 6) == 0) {
    attr.slant = 100;
    posn -= 7;
  }

  /* check for condensed */
  if (posn - pangoname > 9 && strncmp(posn - 9, "Condensed", 9) == 0) {
    attr.width = 75;
    posn -= 10;
  }

  /* check for bold */
  if (posn - pangoname > 9 && strncmp(posn - 9, "Semi-Bold", 9) == 0) {
    attr.weight = 100;
    posn -= 10;
  }
  else if (posn - pangoname > 4 && strncmp(posn - 4, "Bold", 4) == 0) {
    attr.weight = 200;
    posn -= 5;
  }

  if (posn > pangoname && *(posn - 1) == ',')
    posn--;

  base = malloc(posn - pangoname + 10);
  strncpy(base, pangoname, posn - pangoname);
  base[posn - pangoname] = 0;

  /* Sans and Serif aliases are slightly different in Pango vs. fontconfig */
  if (strcmp(base, "Sans") == 0)
    strcpy(base, "sans serif");
  else if (strcmp(base, "Serif") == 0)
    strcpy(base, "serif");
  
  sprintf(fcname, "%s:slant=%d:weight=%d:width=%d:pixelsize=%d",
	  base, attr.slant, attr.weight, attr.width, attr.size);
  free(base);
}

/* Function to convert from WindowMaker 0.90's fontconfig font name format
 * to that used by Pango in GTK+ 2.
 * pangoname must already be allocated and have a length of at least
 * strlen(fcname). */
static void
convert_fontconfig_to_pango (const char *fcname, char *pangoname)
{
  char * base, * posn, * posn2;
  char slant[8] = { 0 }, weight[11] = { 0 }, width[11] = { 0 };
  int size = 12;

  /* Find the first ':' or ',' and assume this is the end of the font name */
  posn = strchr(fcname, ':');
  posn2 = strchr(fcname, ',');
  if (!posn || posn2 && posn2 < posn)
    posn = posn2;
  
  if (posn) {
    base = malloc(posn - fcname + 1);
    strncpy(base, fcname, posn - fcname);
    base[posn - fcname] = 0;
  }
  else {
    /* maybe XLFD, don't touch */
    strcpy(pangoname, fcname);
    return;
  }

  /* Sans and Serif aliases are slightly different in Pango vs. fontconfig */
  if (strcmp(base, "sans serif") == 0)
    strcpy(base, "Sans");
  else if (strcmp(base, "serif") == 0)
    strcpy(base, "Serif");
 
  if ((posn = strstr(fcname, ":weight="))) {
    int wt = atoi(posn + 8);
    if (wt >= 150)
      strcpy(weight, "Bold ");
    else if (wt >= 90)
      strcpy(weight, "Semi-Bold ");
  }

  if ((posn = strstr(fcname, ":bold")))
    strcpy(weight, "Bold ");
  
  if ((posn = strstr(fcname, ":width=")))
    if (atoi(posn + 7) < 90)
      strcpy(width, "Condensed ");
  
  if ((posn = strstr(fcname, ":slant=")))
    if (atoi(posn + 7))
      strcpy(slant, "Italic ");

  if ((posn = strstr(fcname, ":pixelsize="))) {
    int sz = atoi(posn + 11);
    if (sz) size = sz;
  }
    
  sprintf(pangoname, "%s, %s%s%s%d", base, weight, width, slant, size);
  free(base);
}

static void
set_fontname (GtkWidget *widget, gpointer ptr)
{
   GtkWidget *fontsel = GTK_WIDGET (ptr);
   GtkWidget *entry   = gtk_object_get_user_data (GTK_OBJECT (fontsel));
   gchar     *fontname;
   gchar     *fontconfigname;

   fontname = gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG (fontsel));
   fontconfigname = malloc(strlen(fontname) + 50);
   convert_pango_to_fontconfig(fontname, fontconfigname);
   gtk_entry_set_text (GTK_ENTRY (entry), fontconfigname);
   free(fontconfigname);
}

static void
update_entry (proplist_t key, gpointer ptr, proplist_t value,
	      const char *path)
{
   if (WMIsPLString (value))
      gtk_entry_set_text (GTK_ENTRY (ptr), WMGetFromPLString (value));
}

static void
update_color (proplist_t key, gpointer ptr, proplist_t value,
	      const char *path)
{
   if (WMIsPLString (value))
   {
      GtkWidget  *preview  = GTK_WIDGET (ptr);
      //the below wasn't used; was it supposed to be?
      //proplist_t oldvalue  = gtk_object_get_user_data (GTK_OBJECT (preview));

      WMRetainPropList (value);
      gtk_object_set_user_data (GTK_OBJECT (preview), value);
      fill_preview (GTK_PREVIEW (preview), WMGetFromPLString (value));
      WMPutInPLDictionary (windowmaker, key, value);
      toggle_save (changed = YES, key);
   }
}

#ifdef HAVE_LIBWMFUN
static void
update_text_color (proplist_t key, gpointer ptr, proplist_t value,
		   const char *path)
{
   if (WMIsPLString (value) || WMIsPLArray (value))
   {
      text_color_t *text_color = (text_color_t *) ptr;

      WMPutInPLDictionary (windowmaker, key, value);
      toggle_save (changed = YES, key);
      show_color_buttons (text_color);
   }
}
#endif /* HAVE_LIBWMFUN */

static void
update_boolean (proplist_t key, gpointer ptr, proplist_t value,
		const char *path)
{
   if (WMIsPLString (value))
   {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptr),
				    convert_bool (value));
      set_bool (GTK_WIDGET (ptr), key);
   }
}

static void
update_iboolean (proplist_t key, gpointer ptr, proplist_t value,
		 const char *path)
{
   if (WMIsPLString (value))
   {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptr),
				    convert_bool (value) ? FALSE : TRUE);
      set_bool (GTK_WIDGET (ptr), key);
   }
}

static void
update_int (proplist_t key, gpointer ptr, proplist_t value,
	    const char *path)
{
   GtkEntry **entry = (GtkEntry **) ptr;
   
   if (WMIsPLString (value))
      gtk_entry_set_text (entry [0], WMGetFromPLString (value));
   else if (WMIsPLArray (value))
   {
      unsigned n;

      for (n = 0; n < WMGetPropListItemCount (value); n++)
	 if (entry [n])
	    gtk_entry_set_text (entry [n],
				WMGetFromPLString (WMGetFromPLArray (value, n)));
	 else
	    break;
   }
}
