// -*- C++ -*-

/* 
 * GChemPaint library
 * theme.cc
 *
 * Copyright (C) 2002-2007 Jean Bréfort <jean.brefort@normalesup.org>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */

#include "gchempaint-config.h"
#include "document.h"
#include "theme.h"
#include "settings.h"
#include <glib/gi18n-lib.h>
#include <sys/stat.h>
#include <cmath>
#include <cstring>

double DefaultBondLength = 140.;
double DefaultBondAngle = 120.;
double DefaultBondDist = 5.;
double DefaultBondWidth = 1.0;
double DefaultArrowLength = 200;
double DefaultArrowHeadA = 6.0;
double DefaultArrowHeadB = 8.0;
double DefaultArrowHeadC = 4.0;
double DefaultArrowDist = 5.0;
double DefaultArrowWidth = 1.0;
double DefaultHashWidth = 1.0;
double DefaultHashDist = 2.0;
double DefaultStereoBondWidth = 5.0;
double DefaultZoomFactor = 0.25;
double DefaultPadding = 2.0;
double DefaultArrowPadding = 16.0;
double DefaultStoichiometryPadding = 1.;
double DefaultObjectPadding = 16.0;
double DefaultSignPadding = 8.0;
//double DefaultChargeSignHeight = 12.;
double DefaultChargeSignSize = 9.;
//double DefaultChargeYAlign = 10.;
gchar *DefaultFontFamily = NULL;
PangoStyle DefaultFontStyle = PANGO_STYLE_NORMAL;
PangoWeight DefaultFontWeight = PANGO_WEIGHT_NORMAL;
PangoVariant DefaultFontVariant = PANGO_VARIANT_NORMAL;
PangoStretch DefaultFontStretch = PANGO_STRETCH_NORMAL;
gint DefaultFontSize = 12 * PANGO_SCALE;
gchar *DefaultTextFontFamily = NULL;
PangoStyle DefaultTextFontStyle = PANGO_STYLE_NORMAL;
PangoWeight DefaultTextFontWeight = PANGO_WEIGHT_NORMAL;
PangoVariant DefaultTextFontVariant = PANGO_VARIANT_NORMAL;
PangoStretch DefaultTextFontStretch = PANGO_STRETCH_NORMAL;
gint DefaultTextFontSize = 12 * PANGO_SCALE;

gcpTheme::gcpTheme (char const *name)
{
	m_ZoomFactor = DefaultZoomFactor;
	m_BondLength = DefaultBondLength;
	m_BondAngle = DefaultBondAngle;
	m_BondDist = DefaultBondDist;
	m_BondWidth = DefaultBondWidth;
	m_ArrowLength = DefaultArrowLength;
	m_HashWidth = DefaultHashWidth;
	m_HashDist = DefaultHashDist;
	m_StereoBondWidth = DefaultStereoBondWidth;
	m_Padding = DefaultPadding;
	m_ArrowHeadA = DefaultArrowHeadA;
	m_ArrowHeadB = DefaultArrowHeadB;
	m_ArrowHeadC = DefaultArrowHeadC;
	m_ArrowDist = DefaultArrowDist;
	m_ArrowPadding = DefaultArrowPadding;
	m_ArrowWidth = DefaultArrowWidth;
	m_StoichiometryPadding = DefaultStoichiometryPadding;
	m_ObjectPadding = DefaultObjectPadding;
	m_SignPadding = DefaultSignPadding;
	m_ChargeSignSize = DefaultChargeSignSize;
	m_FontFamily = strdup ((DefaultFontFamily)? DefaultFontFamily: "Bitstream Vera Sans");
	m_FontStyle = DefaultFontStyle;
	m_FontWeight = DefaultFontWeight;
	m_FontVariant = DefaultFontVariant;
	m_FontStretch = DefaultFontStretch;
	m_FontSize = DefaultFontSize;
	m_TextFontFamily = strdup ((DefaultTextFontFamily)? DefaultTextFontFamily: "Bitstream Vera Serif");
	m_TextFontStyle = DefaultTextFontStyle;
	m_TextFontWeight = DefaultTextFontWeight;
	m_TextFontVariant = DefaultTextFontVariant;
	m_TextFontStretch = DefaultTextFontStretch;
	m_TextFontSize = DefaultTextFontSize;
	if (name)
		m_Name = name;
	m_ThemeType = DEFAULT_THEME_TYPE;
	modified = false;
}

gcpTheme::~gcpTheme ()
{
	if (m_FontFamily)
		g_free (m_FontFamily);
	if (m_TextFontFamily)
		g_free (m_TextFontFamily);
}

gcpThemeManager ThemeManager;

static void on_config_changed (GConfClient *client, guint cnxn_id, GConfEntry *entry, gcpThemeManager *manager)
{
	manager->OnConfigChanged (client, cnxn_id, entry);
}

// transform functions for gconf key values

static double inv (double x) {return 1 / x;}

static PangoStyle set_fontstyle (int val)
{
	switch (val) {
		case 0: return PANGO_STYLE_NORMAL;
		case 1: return PANGO_STYLE_OBLIQUE;
		case 2: return PANGO_STYLE_ITALIC;
		default: return PANGO_STYLE_NORMAL;
	}
}

static PangoWeight set_fontweight (int val)
{
	switch (val) {
		case 2: return PANGO_WEIGHT_ULTRALIGHT;
		case 3: return PANGO_WEIGHT_LIGHT;
		case 4: return PANGO_WEIGHT_NORMAL;
		case 6: return PANGO_WEIGHT_SEMIBOLD;
		case 7: return PANGO_WEIGHT_BOLD;
		case 8: return PANGO_WEIGHT_ULTRABOLD;
		case 9: return PANGO_WEIGHT_HEAVY;
		default: return PANGO_WEIGHT_NORMAL;
	}
}

static PangoVariant set_fontvariant (int val)
{
	switch (val) {
		case 0: return PANGO_VARIANT_NORMAL;
		case 1: return PANGO_VARIANT_SMALL_CAPS;
		default: return PANGO_VARIANT_NORMAL;
	}
}

static PangoStretch set_fontstretch (int val)
{
	switch (val) {
		case 0: return PANGO_STRETCH_ULTRA_CONDENSED;
		case 1: return PANGO_STRETCH_EXTRA_CONDENSED;
		case 2: return PANGO_STRETCH_CONDENSED;
		case 3: return PANGO_STRETCH_SEMI_CONDENSED;
		case 4: return PANGO_STRETCH_NORMAL;
		case 5: return PANGO_STRETCH_SEMI_EXPANDED;
		case 6: return PANGO_STRETCH_EXPANDED;
		case 7: return PANGO_STRETCH_EXTRA_EXPANDED;
		case 8: return PANGO_STRETCH_ULTRA_EXPANDED;
		default: return PANGO_STRETCH_NORMAL;
	}
}

static int set_fontsize (double val) {return (int) (val * PANGO_SCALE);}

gcpThemeManager::gcpThemeManager ()
{
	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
#ifdef ENABLE_NLS
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif
	GError *error = NULL;
	m_ConfClient = gconf_client_get_default ();
	gconf_client_add_dir (m_ConfClient, "/apps/gchempaint/settings", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
	GCU_GCONF_GET (ROOTDIR"bond-length", float, DefaultBondLength, 140.)
	GCU_GCONF_GET (ROOTDIR"bond-angle", float, DefaultBondAngle, 120.)
	GCU_GCONF_GET (ROOTDIR"bond-dist", float, DefaultBondDist, 5.)
	GCU_GCONF_GET (ROOTDIR"bond-width", float, DefaultBondWidth, 1.0)
	GCU_GCONF_GET (ROOTDIR"arrow-length", float, DefaultArrowLength, 200)
	GCU_GCONF_GET (ROOTDIR"arrow-headA", float, DefaultArrowHeadA, 6.0)
	GCU_GCONF_GET (ROOTDIR"arrow-headB", float, DefaultArrowHeadB, 8.0)
	GCU_GCONF_GET (ROOTDIR"arrow-headC", float, DefaultArrowHeadC, 4.0)
	GCU_GCONF_GET (ROOTDIR"arrow-dist", float, DefaultArrowDist, 5.0)
	GCU_GCONF_GET (ROOTDIR"arrow-width", float, DefaultArrowWidth, 1.0)
	GCU_GCONF_GET (ROOTDIR"hash-width", float, DefaultHashWidth, 1.0)
	GCU_GCONF_GET (ROOTDIR"hash-dist", float, DefaultHashDist, 2.0)
	GCU_GCONF_GET (ROOTDIR"stereo-width", float, DefaultStereoBondWidth, 5.0)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"scale", float, DefaultZoomFactor, 4.0, inv)
	GCU_GCONF_GET (ROOTDIR"padding", float, DefaultPadding, 2.0)
	GCU_GCONF_GET (ROOTDIR"arrow-padding", float, DefaultArrowPadding, 16.0)
	GCU_GCONF_GET (ROOTDIR"stoichiometry-padding", float, DefaultStoichiometryPadding, 1.)
	GCU_GCONF_GET (ROOTDIR"object-padding", float, DefaultObjectPadding, 16.0)
	GCU_GCONF_GET (ROOTDIR"sign-padding", float, DefaultSignPadding, 8.0)
	GCU_GCONF_GET (ROOTDIR"charge-sign-size", float, DefaultChargeSignSize, 9.)
	GCU_GCONF_GET_STRING (ROOTDIR"font-family", DefaultFontFamily, "Bitstream Vera Sans")
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"font-style", int, DefaultFontStyle, 0, set_fontstyle)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"font-weight", int, DefaultFontWeight, 4, set_fontweight)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"font-variant", int, DefaultFontVariant, 0, set_fontvariant)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"font-stretch", int, DefaultFontStretch, 4, set_fontstretch)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"font-size", float, DefaultFontSize, 12., set_fontsize)
	GCU_GCONF_GET_STRING (ROOTDIR"text-font-family", DefaultTextFontFamily, "Bitstream Vera Serif")
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"text-font-style", int, DefaultTextFontStyle, 0, set_fontstyle)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"text-font-weight", int, DefaultTextFontWeight, 4, set_fontweight)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"text-font-variant", int, DefaultTextFontVariant, 0, set_fontvariant)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"text-font-stretch", int, DefaultTextFontStretch, 4, set_fontstretch)
	GCU_GCONF_GET_N_TRANSFORM (ROOTDIR"text-font-size", float, DefaultTextFontSize, 12., set_fontsize)
	m_NotificationId = gconf_client_notify_add (m_ConfClient, "/apps/gchempaint/settings", (GConfClientNotifyFunc) on_config_changed, this, NULL, NULL);
	// Build default theme from settings
	m_Themes["GChemPaint"] = new gcpTheme ("GChemPaint");
	m_Names.push_front ("GChemPaint");
	// load global themes
	string path = DATADIR;
	path += "/gchempaint/themes";
	ParseDir (path, GLOBAL_THEME_TYPE);
	// load local themes
	char *szhome = getenv ("HOME");
	if (szhome)
		path = szhome;
	path += "/.gchempaint/themes";
	ParseDir (path, LOCAL_THEME_TYPE);
	gchar *default_theme  =NULL;
	GCU_GCONF_GET_STRING (ROOTDIR"default-theme", default_theme, "GChemPaint");
	m_DefaultTheme = m_Themes[default_theme];
	g_free (default_theme);
	if (!m_DefaultTheme)
		m_DefaultTheme = m_Themes["GChemPaint"];
}

gcpThemeManager::~gcpThemeManager ()
{
	g_type_init ();
	gconf_client_notify_remove (m_ConfClient, m_NotificationId);
	gconf_client_remove_dir (m_ConfClient, "/apps/gchempaint/settings", NULL);
	g_object_unref (m_ConfClient);
	// save themes if needed, then delete
	gcpTheme *theme, *def = NULL;
	map <string, gcpTheme*>::iterator i, iend = m_Themes.end ();
	for (i = m_Themes.begin (); i != iend; i++) {
		theme = (*i).second;
		if (!theme || (def && theme == def))
			continue; // this theme has already been deleted
		if (theme->modified && theme->m_ThemeType == LOCAL_THEME_TYPE) {
			xmlDocPtr doc = xmlNewDoc((xmlChar*)"1.0");
			xmlDocSetRootElement (doc,  xmlNewDocNode (doc, NULL, (xmlChar*) "chemistry", NULL));
			if (theme->Save (doc)) {
				char *szhome = getenv ("HOME");
				string home, path;
				if (szhome)
					home = szhome;
				path = home + "/.gchempaint/themes";
				GDir *dir = g_dir_open (path.c_str (), 0, NULL);
				if (!dir) {
					string gcppath;
					gcppath = home + "/.gchempaint";
					dir = g_dir_open (gcppath.c_str (), 0, NULL);
					if (dir)
						g_dir_close (dir);
					else
						mkdir (gcppath.c_str (), 0x1ed);
					mkdir (path.c_str (), 0x1ed);
				} else
					g_dir_close (dir);
				path += string ("/") + theme->GetName ();
				xmlSaveFormatFile (path.c_str (), doc, true);
			}
		} else if (theme->m_Name == "Default")
			def = theme;
		delete theme;
	}
	g_free (DefaultFontFamily);
	g_free (DefaultTextFontFamily);
}

gcpTheme *gcpThemeManager::GetTheme (char const *name)
{
	if (!strcmp (_(name), _("Default")))
		return m_DefaultTheme;
	return m_Themes[name];
}

gcpTheme *gcpThemeManager::GetTheme (string &name)
{
	if (name == "Default" || name == _("Default") )
		return m_DefaultTheme;
	return m_Themes[name.c_str ()];
}

list <string> const &gcpThemeManager::GetThemesNames ()
{
	return m_Names;
}

void gcpThemeManager::OnConfigChanged (GConfClient *client, guint cnxn_id, GConfEntry *entry)
{
	if (client != m_ConfClient)
		return;	// we might want an error message?
	if (cnxn_id != m_NotificationId)
		return;	// we might want an error message?
	gcpTheme *Theme = m_Themes["GChemPaint"];
	if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"bond-length")) {
		DefaultBondLength = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_BondLength = DefaultBondLength;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"bond-angle"))  {
		DefaultBondAngle = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_BondAngle = DefaultBondAngle;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"bond-dist"))  {
		DefaultBondDist = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_BondDist = DefaultBondDist;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"bond-width"))  {
		DefaultBondWidth = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_BondWidth = DefaultBondWidth;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"arrow-length"))  {
		DefaultArrowLength = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_ArrowLength = DefaultArrowLength;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"arrow-headA"))  {
		DefaultArrowHeadA = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_ArrowHeadA = DefaultArrowHeadA;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"arrow-headB"))  {
		DefaultArrowHeadB = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_ArrowHeadB = DefaultArrowHeadB;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"arrow-headC"))  {
		DefaultArrowHeadC = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_ArrowHeadC = DefaultArrowHeadC;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"arrow-dist"))  {
		DefaultArrowDist = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_ArrowDist = DefaultArrowDist;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"arrow-width"))  {
		DefaultArrowWidth = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_ArrowWidth = DefaultArrowWidth;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"hash-width"))  {
		DefaultHashWidth = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_HashWidth = DefaultHashWidth;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"hash-dist"))  {
		DefaultHashDist = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_HashDist = DefaultHashDist;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"stereo-width"))  {
		DefaultStereoBondWidth = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_StereoBondWidth = DefaultStereoBondWidth;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"scale"))  {
		double scale = gconf_value_get_float (gconf_entry_get_value (entry));
		if (scale > 1e-5) {
			DefaultZoomFactor = 1 / scale;
			Theme->m_ZoomFactor = DefaultZoomFactor;
		}
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"padding"))  {
		DefaultPadding = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_Padding = DefaultPadding;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"stoichiometry-padding"))  {
		DefaultStoichiometryPadding = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_StoichiometryPadding = DefaultStoichiometryPadding;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"object-padding"))  {
		DefaultObjectPadding = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_ObjectPadding = DefaultObjectPadding;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"charge-sign-padding"))  {
		DefaultSignPadding = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_SignPadding = DefaultSignPadding;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"charge-sign-size"))  {
		DefaultChargeSignSize = gconf_value_get_float (gconf_entry_get_value (entry));
		Theme->m_ChargeSignSize = DefaultChargeSignSize;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"font-family"))  {
		char const *name = gconf_value_get_string (gconf_entry_get_value (entry));
		if (name) {
			if (DefaultFontFamily != NULL)
				g_free (DefaultFontFamily);
			DefaultFontFamily = g_strdup (name);
			Theme->m_FontFamily = DefaultFontFamily;
		}
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"font-style"))  {
		DefaultFontStyle = set_fontstyle (gconf_value_get_int (gconf_entry_get_value (entry)));
		Theme->m_FontStyle = DefaultFontStyle;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"font-weight"))  {
		DefaultFontWeight = set_fontweight (gconf_value_get_int (gconf_entry_get_value (entry)));
		Theme->m_FontWeight = DefaultFontWeight;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"font-variant"))  {
		DefaultFontVariant = set_fontvariant (gconf_value_get_int (gconf_entry_get_value (entry)));
		Theme->m_FontVariant = DefaultFontVariant;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"font-stretch"))  {
		DefaultFontStretch = set_fontstretch (gconf_value_get_int (gconf_entry_get_value (entry)));
		Theme->m_FontStretch = DefaultFontStretch;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"font-size"))  {
		DefaultFontSize = set_fontsize (gconf_value_get_float (gconf_entry_get_value (entry)));
		Theme->m_FontSize = DefaultFontSize;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"text-font-family"))  {
		char const *name = gconf_value_get_string (gconf_entry_get_value (entry));
		if (name) {
			if (DefaultTextFontFamily != NULL)
				g_free (DefaultTextFontFamily);
			DefaultTextFontFamily = g_strdup (name);
			Theme->m_TextFontFamily = DefaultTextFontFamily;
		}
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"text-font-style"))  {
		DefaultTextFontStyle = set_fontstyle (gconf_value_get_int (gconf_entry_get_value (entry)));
		Theme->m_TextFontStyle = DefaultTextFontStyle;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"text-font-weight"))  {
		DefaultTextFontWeight = set_fontweight (gconf_value_get_int (gconf_entry_get_value (entry)));
		Theme->m_TextFontWeight = DefaultTextFontWeight;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"text-font-variant"))  {
		DefaultTextFontVariant = set_fontvariant (gconf_value_get_int (gconf_entry_get_value (entry)));
		Theme->m_TextFontVariant = DefaultTextFontVariant;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"text-font-stretch"))  {
		DefaultTextFontStretch = set_fontstretch (gconf_value_get_int (gconf_entry_get_value (entry)));
		Theme->m_TextFontStretch = DefaultTextFontStretch;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"text-font-size"))  {
		DefaultTextFontSize = set_fontsize (gconf_value_get_float (gconf_entry_get_value (entry)));
		Theme->m_TextFontSize = DefaultTextFontSize;
	} else if (!strcmp (gconf_entry_get_key (entry),ROOTDIR"default-theme"))  {
		char const *name = gconf_value_get_string (gconf_entry_get_value (entry));
		gcpTheme *pTheme = m_Themes[name];
		if (pTheme)
			m_DefaultTheme = pTheme;
	}
}

gcpTheme *gcpThemeManager::CreateNewTheme (gcpTheme *theme)
{
	gchar *name = g_strdup (_("NewTheme1"));
	int i = 2;
	while (m_Themes[name] != NULL) {
		g_free (name);
		name = g_strdup_printf (_("NewTheme%d"), i++);
	}
	gcpTheme *pTheme = new gcpTheme (name);
	m_Themes[name] = pTheme;
	m_Names.push_back (name);
	g_free (name);
	if (theme) {
		pTheme->m_ZoomFactor = theme->m_ZoomFactor;
		pTheme->m_BondLength = theme->m_BondLength;
		pTheme->m_BondAngle = theme->m_BondAngle;
		pTheme->m_BondDist = theme->m_BondDist;
		pTheme->m_BondWidth = theme->m_BondWidth;
		pTheme->m_ArrowLength = theme->m_ArrowLength;
		pTheme->m_HashWidth = theme->m_HashWidth;
		pTheme->m_HashDist = theme->m_HashDist;
		pTheme->m_StereoBondWidth = theme->m_StereoBondWidth;
		pTheme->m_Padding = theme->m_Padding;
		pTheme->m_ArrowHeadA = theme->m_ArrowHeadA;
		pTheme->m_ArrowHeadB = theme->m_ArrowHeadB;
		pTheme->m_ArrowHeadC = theme->m_ArrowHeadC;
		pTheme->m_ArrowDist = theme->m_ArrowDist;
		pTheme->m_ArrowPadding = theme->m_ArrowPadding;
		pTheme->m_ArrowWidth = theme->m_ArrowWidth;
		pTheme->m_StoichiometryPadding = theme->m_StoichiometryPadding;
		pTheme->m_ObjectPadding = theme->m_ObjectPadding;
		pTheme->m_SignPadding = theme->m_SignPadding;
		pTheme->m_ChargeSignSize = theme->m_ChargeSignSize;
		g_free (pTheme->m_FontFamily);
		pTheme->m_FontFamily = strdup (theme->m_FontFamily);
		pTheme->m_FontStyle = theme->m_FontStyle;
		pTheme->m_FontWeight = theme->m_FontWeight;
		pTheme->m_FontVariant = theme->m_FontVariant;
		pTheme->m_FontStretch = theme->m_FontStretch;
		pTheme->m_FontSize = theme->m_FontSize;
		g_free (pTheme->m_TextFontFamily);
		pTheme->m_TextFontFamily = strdup (theme->m_TextFontFamily);
		pTheme->m_TextFontStyle = theme->m_TextFontStyle;
		pTheme->m_TextFontWeight = theme->m_TextFontWeight;
		pTheme->m_TextFontVariant = theme->m_TextFontVariant;
		pTheme->m_TextFontStretch = theme->m_TextFontStretch;
		pTheme->m_TextFontSize = theme->m_TextFontSize;
	}
	pTheme->m_ThemeType = LOCAL_THEME_TYPE;
	pTheme->modified = true;
	return pTheme;
}

void gcpThemeManager::ParseDir (string &path, ThemeType type)
{
	char const *name;
	xmlDocPtr doc;
	xmlNodePtr node;
	gcpTheme *theme;
	string filename;
	GDir *dir = g_dir_open (path.c_str (), 0, NULL);
	if (dir) {
		path += "/";
		while ((name = g_dir_read_name (dir))) {
			if (name[strlen (name) - 1] == '~')
				continue; // don't read backups
			filename = path + name;
			doc = xmlParseFile (filename.c_str ());
			node = doc->children;
			if (!strcmp ((char*)node->name, "chemistry")) {
				node = node->children;
				while (node && !strcmp ((char*)node->name, "text"))
					node = node->next;
				if (node && !strcmp ((char*)node->name, "theme")) {
					theme = new gcpTheme ("");
					theme->Load (node);
					if (theme->GetName () != name) {
						theme->m_Name = name;
						theme->modified = true;
					}
					if (theme->m_ThemeType == DEFAULT_THEME_TYPE || theme->m_ThemeType == GLOBAL_THEME_TYPE)
						name = _(name);
					if (m_Themes.find (name) != m_Themes.end ()) {
						delete theme;
						continue;
					}
					theme->m_ThemeType = type;
					m_Themes[name] = theme;
					m_Names.push_back (name);
				}
			}
			xmlFree (doc);
		}
		g_dir_close (dir);
	}
}

void gcpThemeManager::AddFileTheme (gcpTheme *theme, char const *label)
{
	string name = theme->GetName ().c_str ();
	if (name == "Default")
		name = "GChemPaint";
	if (m_Themes.find (name) != m_Themes.end ()) {
		name = string ((label)? label: _("Unknown")) + ":" + name;
	}
	m_Themes[name] = theme;
	m_Names.push_back (name);
}

void gcpThemeManager::RemoveFileTheme (gcpTheme *theme)
{
	char const *name = NULL;
	map <string, gcpTheme*>::iterator i, iend = m_Themes.end ();
	for (i = m_Themes.begin (); i != iend; i++)
		if ((*i).second == theme) {
			name = (*i).first.c_str ();
			break;
		}
	m_Names.remove (name);
	m_Themes.erase (name);
}

void gcpThemeManager::ChangeThemeName (gcpTheme *theme, char const *name)
{
	m_Themes.erase (theme->m_Name);
	m_Names.remove (theme->m_Name);
	theme->m_Name = name;
	m_Themes[name] = theme;
	m_Names.push_back (name);
}

void gcpThemeManager::SetDefaultTheme (char const *name)
{
	gcpTheme *pTheme = m_Themes[name];
	if (pTheme) {
		m_DefaultTheme = pTheme;
	}
}

bool gcpTheme::Save (xmlDocPtr xml)
{
	xmlNodePtr node = xmlNewDocNode (xml, NULL, (xmlChar*) "theme", NULL);
	char *buf;
	if (!node)
		return false;
	if (m_Name.length () > 0)
		xmlNewProp (node, (xmlChar*) "name", (xmlChar*) m_Name.c_str ());
	buf = g_strdup_printf("%g", m_BondLength);
	xmlNewProp (node, (xmlChar*) "bond-length", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_BondAngle);
	xmlNewProp (node, (xmlChar*) "bond-angle", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_BondDist);
	xmlNewProp (node, (xmlChar*) "bond-dist", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_BondWidth);
	xmlNewProp (node, (xmlChar*) "bond-width", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ArrowLength);
	xmlNewProp (node, (xmlChar*) "arrow-length", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ArrowHeadA);
	xmlNewProp (node, (xmlChar*) "arrow-head-a", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ArrowHeadB);
	xmlNewProp (node, (xmlChar*) "arrow-head-b", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ArrowHeadC);
	xmlNewProp (node, (xmlChar*) "arrow-head-c", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ArrowDist);
	xmlNewProp (node, (xmlChar*) "arrow-dist", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ArrowWidth);
	xmlNewProp (node, (xmlChar*) "arrow-width", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ArrowPadding);
	xmlNewProp (node, (xmlChar*) "arrow-padding", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_HashWidth);
	xmlNewProp (node, (xmlChar*) "hash-width", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_HashDist);
	xmlNewProp (node, (xmlChar*) "hash-dist", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_StereoBondWidth);
	xmlNewProp (node, (xmlChar*) "stereo-bond-width", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", 1 / m_ZoomFactor);
	xmlNewProp (node, (xmlChar*) "zoom-factor", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_Padding);
	xmlNewProp (node, (xmlChar*) "padding", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_StoichiometryPadding);
	xmlNewProp (node, (xmlChar*) "stoichiometry-padding", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ObjectPadding);
	xmlNewProp (node, (xmlChar*) "object-padding", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_SignPadding);
	xmlNewProp (node, (xmlChar*) "sign-padding", (xmlChar*) buf);
	g_free (buf);
	buf = g_strdup_printf("%g", m_ChargeSignSize);
	xmlNewProp (node, (xmlChar*) "charge-sign-size", (xmlChar*) buf);
	g_free (buf);
	xmlNewProp (node, (xmlChar*) "font-family", (xmlChar*) m_FontFamily);
	buf = NULL;
	switch (m_FontStyle) {
	case PANGO_STYLE_NORMAL:
		buf = (char*) "normal";
		break;
	case PANGO_STYLE_OBLIQUE:
		buf = (char*) "oblique";
		break;
	case PANGO_STYLE_ITALIC:
		buf = (char*) "italic";
		break;
	}
	if (buf)
		xmlNewProp (node, (xmlChar*) "font-style", (xmlChar*) buf);
	buf = NULL;
	switch (m_FontWeight) {
	case PANGO_WEIGHT_ULTRALIGHT:
		buf = (char*) "ultra-light";
		break;
	case PANGO_WEIGHT_LIGHT:
		buf = (char*) "light";
		break;
	case PANGO_WEIGHT_NORMAL:
		buf = (char*) "normal";
		break;
	case PANGO_WEIGHT_SEMIBOLD:
		buf = (char*) "semi-bold";
		break;
	case PANGO_WEIGHT_BOLD:
		buf = (char*) "bold";
		break;
	case PANGO_WEIGHT_ULTRABOLD:
		buf = (char*) "ultra-bold";
		break;
	case PANGO_WEIGHT_HEAVY:
		buf = (char*) "heavy";
		break;
	}
	if (buf)
		xmlNewProp (node, (xmlChar*) "font-weight", (xmlChar*) buf);
	xmlNewProp (node, (xmlChar*) "font-variant", (xmlChar*) ((m_FontVariant == PANGO_VARIANT_SMALL_CAPS)? "small-caps": "normal"));
	buf = NULL;
	switch (m_FontStretch) {
	case PANGO_STRETCH_ULTRA_CONDENSED:
		buf = (char*) "ultra-condensed";
		break;
	case PANGO_STRETCH_EXTRA_CONDENSED:
		buf = (char*) "extra-condensed";
		break;
	case PANGO_STRETCH_CONDENSED:
		buf = (char*) "condensed";
		break;
	case PANGO_STRETCH_SEMI_CONDENSED:
		buf = (char*) "semi-condensed";
		break;
	case PANGO_STRETCH_NORMAL:
		buf = (char*) "normal";
		break;
	case PANGO_STRETCH_SEMI_EXPANDED:
		buf = (char*) "semi-expanded";
		break;
	case PANGO_STRETCH_EXPANDED:
		buf = (char*) "expanded";
		break;
	case PANGO_STRETCH_EXTRA_EXPANDED:
		buf = (char*) "extra-expanded";
		break;
	case PANGO_STRETCH_ULTRA_EXPANDED:
		buf = (char*) "ultra-expanded";
		break;
	}
	if (buf)
		xmlNewProp (node, (xmlChar*) "font-stretch", (xmlChar*) buf);
	buf = g_strdup_printf("%d", m_FontSize);
	xmlNewProp (node, (xmlChar*) "font-size", (xmlChar*) buf);
	g_free (buf);
	xmlNewProp (node, (xmlChar*) "text-font-family", (xmlChar*) m_TextFontFamily);
	buf = NULL;
	switch (m_TextFontStyle) {
	case PANGO_STYLE_NORMAL:
		buf = (char*) "normal";
		break;
	case PANGO_STYLE_OBLIQUE:
		buf = (char*) "oblique";
		break;
	case PANGO_STYLE_ITALIC:
		buf = (char*) "italic";
		break;
	}
	if (buf)
		xmlNewProp (node, (xmlChar*) "text-font-style", (xmlChar*) buf);
	buf = NULL;
	switch (m_TextFontWeight) {
	case PANGO_WEIGHT_ULTRALIGHT:
		buf = (char*) "ultra-light";
		break;
	case PANGO_WEIGHT_LIGHT:
		buf = (char*) "light";
		break;
	case PANGO_WEIGHT_NORMAL:
		buf = (char*) "normal";
		break;
	case PANGO_WEIGHT_SEMIBOLD:
		buf = (char*) "semi-bold";
		break;
	case PANGO_WEIGHT_BOLD:
		buf = (char*) "bold";
		break;
	case PANGO_WEIGHT_ULTRABOLD:
		buf = (char*) "ultra-bold";
		break;
	case PANGO_WEIGHT_HEAVY:
		buf = (char*) "heavy";
		break;
	}
	if (buf)
		xmlNewProp (node, (xmlChar*) "text-font-weight", (xmlChar*) buf);
	xmlNewProp (node, (xmlChar*) "text-font-variant", (xmlChar*) ((m_TextFontVariant == PANGO_VARIANT_SMALL_CAPS)? "small-caps": "normal"));
	buf = NULL;
	switch (m_TextFontStretch) {
	case PANGO_STRETCH_ULTRA_CONDENSED:
		buf = (char*) "ultra-condensed";
		break;
	case PANGO_STRETCH_EXTRA_CONDENSED:
		buf = (char*) "extra-condensed";
		break;
	case PANGO_STRETCH_CONDENSED:
		buf = (char*) "condensed";
		break;
	case PANGO_STRETCH_SEMI_CONDENSED:
		buf = (char*) "semi-condensed";
		break;
	case PANGO_STRETCH_NORMAL:
		buf = (char*) "normal";
		break;
	case PANGO_STRETCH_SEMI_EXPANDED:
		buf = (char*) "semi-expanded";
		break;
	case PANGO_STRETCH_EXPANDED:
		buf = (char*) "expanded";
		break;
	case PANGO_STRETCH_EXTRA_EXPANDED:
		buf = (char*) "extra-expanded";
		break;
	case PANGO_STRETCH_ULTRA_EXPANDED:
		buf = (char*) "ultra-expanded";
		break;
	}
	if (buf)
		xmlNewProp (node, (xmlChar*) "text-font-stretch", (xmlChar*) buf);
	buf = g_strdup_printf("%d", m_TextFontSize);
	xmlNewProp (node, (xmlChar*) "text-font-size", (xmlChar*) buf);
	g_free (buf);
	xmlAddChild (xml->children, node);
	return true;
}

#define READ_DOUBLE(var,name) \
	buf = (char*) xmlGetProp (node, (xmlChar*) name); \
	if (buf) { \
		var = strtod (buf, NULL); \
		xmlFree (buf); \
	}
#define READ_INT(var,name) \
	buf = (char*) xmlGetProp (node, (xmlChar*) name); \
	if (buf) { \
		var = strtol (buf, NULL, 10); \
		xmlFree (buf); \
	}

bool gcpTheme::Load (xmlNodePtr node)
{
	char *buf = (char*) xmlGetProp (node, (xmlChar*) "name");
	if (buf) {
		m_Name = buf;
		xmlFree (buf);
	}
	READ_DOUBLE (m_BondLength, "bond-length");
	READ_DOUBLE (m_BondAngle, "bond-angle");
	READ_DOUBLE (m_BondDist, "bond-dist");
	READ_DOUBLE (m_BondWidth, "bond-width");
	READ_DOUBLE (m_ArrowLength, "arrow-length");
	READ_DOUBLE (m_ArrowHeadA, "arrow-head-a");
	READ_DOUBLE (m_ArrowHeadB, "arrow-head-b");
	READ_DOUBLE (m_ArrowHeadC, "arrow-head-c");
	READ_DOUBLE (m_ArrowDist, "arrow-dist");
	READ_DOUBLE (m_ArrowWidth, "arrow-width");
	READ_DOUBLE (m_ArrowPadding, "arrow-padding");
	READ_DOUBLE (m_HashWidth, "hash-width");
	READ_DOUBLE (m_HashDist, "hash-dist");
	READ_DOUBLE (m_StereoBondWidth, "stereo-bond-width");
	READ_DOUBLE (m_ZoomFactor, "zoom-factor");
	m_ZoomFactor = 1 / m_ZoomFactor;
	READ_DOUBLE (m_Padding, "padding");
	READ_DOUBLE (m_StoichiometryPadding, "stoichiometry-padding");
	READ_DOUBLE (m_ObjectPadding, "object-padding");
	READ_DOUBLE (m_SignPadding, "sign-padding");
	READ_DOUBLE (m_ChargeSignSize, "charge-sign-size");
	buf = (char*) xmlGetProp (node, (xmlChar*) "font-family");
	if (buf) {
		if (m_FontFamily)
			g_free (m_FontFamily);
		m_FontFamily = g_strdup (buf);
	}
	buf = (char*) xmlGetProp (node, (xmlChar*) "font-style");
	if (buf) {
		if (!strcmp (buf, "normal"))
			m_FontStyle = PANGO_STYLE_NORMAL;
		else if (!strcmp (buf, "oblique"))
			m_FontStyle = PANGO_STYLE_OBLIQUE;
		else if (!strcmp (buf, "italic"))
			m_FontStyle = PANGO_STYLE_ITALIC;
		xmlFree (buf);
	}
	buf = (char*) xmlGetProp (node, (xmlChar*) "font-weight");
	if (buf) {
		if (!strcmp (buf, "ultra-light"))
			m_FontWeight= PANGO_WEIGHT_ULTRALIGHT;
		else if (!strcmp (buf, "light"))
			m_FontWeight = PANGO_WEIGHT_LIGHT;
		else if (!strcmp (buf, "normal"))
			m_FontWeight = PANGO_WEIGHT_NORMAL;
		else if (!strcmp (buf, "semi-bold"))
			m_FontWeight = PANGO_WEIGHT_SEMIBOLD;
		else if (!strcmp (buf, "bold"))
			m_FontWeight = PANGO_WEIGHT_BOLD;
		else if (!strcmp (buf, "ultra-bold"))
			m_FontWeight = PANGO_WEIGHT_ULTRABOLD;
		else if (!strcmp (buf, "heavy"))
			m_FontWeight = PANGO_WEIGHT_HEAVY;
		xmlFree (buf);
	}
	buf = (char*) xmlGetProp (node, (xmlChar*) "font-variant");
	if (buf) {
		if (!strcmp (buf, "normal"))
			m_FontVariant = PANGO_VARIANT_NORMAL;
		else if (!strcmp (buf, "small-caps"))
			m_FontVariant = PANGO_VARIANT_SMALL_CAPS;
		xmlFree (buf);
	}
	buf = (char*) xmlGetProp (node, (xmlChar*) "font-stretch");
	if (buf) {
		if (!strcmp (buf, "ultra-condensed"))
			m_FontStretch = PANGO_STRETCH_ULTRA_CONDENSED;
		else if (!strcmp (buf, "extra-condensed"))
			m_FontStretch = PANGO_STRETCH_EXTRA_CONDENSED;
		else if (!strcmp (buf, "condensed"))
			m_FontStretch = PANGO_STRETCH_CONDENSED;
		else if (!strcmp (buf, "semi-condensed"))
			m_FontStretch = PANGO_STRETCH_SEMI_CONDENSED;
		else if (!strcmp (buf, "normal"))
			m_FontStretch = PANGO_STRETCH_NORMAL;
		else if (!strcmp (buf, "semi-expanded"))
			m_FontStretch = PANGO_STRETCH_SEMI_EXPANDED;
		else if (!strcmp (buf, "expanded"))
			m_FontStretch = PANGO_STRETCH_EXPANDED;
		else if (!strcmp (buf, "extra-expanded"))
			m_FontStretch = PANGO_STRETCH_EXTRA_EXPANDED;
		else if (!strcmp (buf, "ultra-expanded"))
			m_FontStretch = PANGO_STRETCH_ULTRA_EXPANDED;
		xmlFree (buf);
	}
	READ_INT (m_FontSize, "font-size");
	buf = (char*) xmlGetProp (node, (xmlChar*) "text-font-family");
	if (buf) {
		if (m_TextFontFamily)
			g_free (m_TextFontFamily);
		m_TextFontFamily = g_strdup (buf);
		xmlFree (buf);
	}
	buf = (char*) xmlGetProp (node, (xmlChar*) "text-font-style");
	if (buf) {
		if (!strcmp (buf, "normal"))
			m_TextFontStyle = PANGO_STYLE_NORMAL;
		else if (!strcmp (buf, "oblique"))
			m_TextFontStyle = PANGO_STYLE_OBLIQUE;
		else if (!strcmp (buf, "italic"))
			m_TextFontStyle = PANGO_STYLE_ITALIC;
		xmlFree (buf);
	}
	buf = (char*) xmlGetProp (node, (xmlChar*) "text-font-weight");
	if (buf) {
		if (!strcmp (buf, "ultra-light"))
			m_TextFontWeight= PANGO_WEIGHT_ULTRALIGHT;
		else if (!strcmp (buf, "light"))
			m_TextFontWeight = PANGO_WEIGHT_LIGHT;
		else if (!strcmp (buf, "normal"))
			m_TextFontWeight = PANGO_WEIGHT_NORMAL;
		else if (!strcmp (buf, "semi-bold"))
			m_TextFontWeight = PANGO_WEIGHT_SEMIBOLD;
		else if (!strcmp (buf, "bold"))
			m_TextFontWeight = PANGO_WEIGHT_BOLD;
		else if (!strcmp (buf, "ultra-bold"))
			m_TextFontWeight = PANGO_WEIGHT_ULTRABOLD;
		else if (!strcmp (buf, "heavy"))
			m_TextFontWeight = PANGO_WEIGHT_HEAVY;
		xmlFree (buf);
	}
	buf = (char*) xmlGetProp (node, (xmlChar*) "text-font-variant");
	if (buf) {
		if (!strcmp (buf, "normal"))
			m_TextFontVariant = PANGO_VARIANT_NORMAL;
		else if (!strcmp (buf, "small-caps"))
			m_TextFontVariant = PANGO_VARIANT_SMALL_CAPS;
		xmlFree (buf);
	}
	buf = (char*) xmlGetProp (node, (xmlChar*) "text-font-stretch");
	if (buf) {
		if (!strcmp (buf, "ultra-condensed"))
			m_TextFontStretch = PANGO_STRETCH_ULTRA_CONDENSED;
		else if (!strcmp (buf, "extra-condensed"))
			m_TextFontStretch = PANGO_STRETCH_EXTRA_CONDENSED;
		else if (!strcmp (buf, "condensed"))
			m_TextFontStretch = PANGO_STRETCH_CONDENSED;
		else if (!strcmp (buf, "semi-condensed"))
			m_TextFontStretch = PANGO_STRETCH_SEMI_CONDENSED;
		else if (!strcmp (buf, "normal"))
			m_TextFontStretch = PANGO_STRETCH_NORMAL;
		else if (!strcmp (buf, "semi-expanded"))
			m_TextFontStretch = PANGO_STRETCH_SEMI_EXPANDED;
		else if (!strcmp (buf, "expanded"))
			m_TextFontStretch = PANGO_STRETCH_EXPANDED;
		else if (!strcmp (buf, "extra-expanded"))
			m_TextFontStretch = PANGO_STRETCH_EXTRA_EXPANDED;
		else if (!strcmp (buf, "ultra-expanded"))
			m_TextFontStretch = PANGO_STRETCH_ULTRA_EXPANDED;
		xmlFree (buf);
	}
	READ_INT (m_TextFontSize, "text-font-size");
	m_ThemeType = FILE_THEME_TYPE;
	return true;
}

#define TEST_FIELD(field) \
	if (field != theme.field) \
		return false;
#define TEST_FLOAT_FIELD(field) \
	if (fabs (1. - field / theme.field) > 1e-7) \
		return false;

bool gcpTheme::operator== (const gcpTheme &theme)
{
	TEST_FLOAT_FIELD (m_BondLength)
	TEST_FLOAT_FIELD (m_BondAngle)
	TEST_FLOAT_FIELD (m_BondDist)
	TEST_FLOAT_FIELD (m_BondWidth)
	TEST_FLOAT_FIELD (m_ArrowLength)
	TEST_FLOAT_FIELD (m_ArrowWidth)
	TEST_FLOAT_FIELD (m_ArrowDist)
	TEST_FLOAT_FIELD (m_ArrowHeadA)
	TEST_FLOAT_FIELD (m_ArrowHeadB)
	TEST_FLOAT_FIELD (m_ArrowHeadC)
	TEST_FLOAT_FIELD (m_ArrowPadding)
	TEST_FLOAT_FIELD (m_HashWidth)
	TEST_FLOAT_FIELD (m_HashDist)
	TEST_FLOAT_FIELD (m_StereoBondWidth)
	TEST_FLOAT_FIELD (m_ZoomFactor)
	TEST_FLOAT_FIELD (m_Padding)
	TEST_FLOAT_FIELD (m_StoichiometryPadding)
	TEST_FLOAT_FIELD (m_ObjectPadding)
	TEST_FLOAT_FIELD (m_SignPadding)
	TEST_FLOAT_FIELD (m_ChargeSignSize)
	if (strcmp (m_FontFamily, theme.m_FontFamily))
		return false;
	TEST_FIELD (m_FontStyle)
	TEST_FIELD (m_FontWeight)
	TEST_FIELD (m_FontVariant)
	TEST_FIELD (m_FontStretch)
	TEST_FIELD (m_FontSize)
	if (strcmp (m_TextFontFamily, theme.m_TextFontFamily))
		return false;
	TEST_FIELD (m_TextFontStyle)
	TEST_FIELD (m_TextFontWeight)
	TEST_FIELD (m_TextFontVariant)
	TEST_FIELD (m_TextFontStretch)
	TEST_FIELD (m_TextFontSize)
	return true;
}

void gcpTheme::RemoveClient (Object *client)
{
	set <Object*>::iterator iter = m_Clients.find (client);
	if (iter != m_Clients.end ())
		m_Clients.erase (iter);
	if (m_ThemeType == FILE_THEME_TYPE && m_Clients.size () == 0) {
		ThemeManager.RemoveFileTheme (this);
		delete this;
	}
}

void gcpTheme::NotifyChanged ()
{
	set <Object*>::iterator iter, enditer = m_Clients.end ();
	for (iter = m_Clients.begin (); iter != enditer; iter++)
		(*iter)->OnSignal (OnThemeChangedSignal, NULL);
}


syntax highlighted by Code2HTML, v. 0.9.1