]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkrc.c
Must mark also gdk_error_warnings and gdk_error_code as GDKVAR, as they
[~andy/gtk] / gtk / gtkrc.c
index a855ef22c1be7d71738d1ffdd22936b69a9bc6fc..cf97c48ae848f64bec42e0797f0af8be16eade8b 100644 (file)
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+
+/*
+ * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#include "config.h"
+
+#include "glib.h"
+#include "gdkconfig.h"
+
+#ifdef GDK_WINDOWING_X11
+#include <X11/Xlocale.h>       /* so we get the right setlocale */
+#else
+#include <locale.h>
+#endif
 #include <ctype.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <sys/stat.h>
+#ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
+#endif
 #include <fcntl.h>
 #include <string.h>
 #include <stdio.h>
+#include <stdlib.h>
+
+#ifndef HAVE_LSTAT
+#define lstat stat
+#endif
+
+#ifdef G_OS_WIN32
+#include <windows.h>           /* For GetWindowsDirectory */
+#include <io.h>
+#endif
+
 #include "gtkrc.h"
 #include "gtkbindings.h"
 #include "gtkthemes.h"
+#include "gtkintl.h"
 
 typedef struct _GtkRcSet    GtkRcSet;
 typedef struct _GtkRcNode   GtkRcNode;
 typedef struct _GtkRcFile   GtkRcFile;
+typedef struct _GtkRcStylePrivate  GtkRcStylePrivate;
 
 struct _GtkRcSet
 {
@@ -45,6 +80,15 @@ struct _GtkRcFile
   gboolean reload;
 };
 
+struct _GtkRcStylePrivate
+{
+  GtkRcStyle style;
+
+  guint ref_count;
+  /* list of RC style lists including this RC style */
+  GSList *rc_style_lists;
+};
+
 static guint      gtk_rc_style_hash               (const char   *name);
 static gint       gtk_rc_style_compare            (const char   *a,
                                                    const char   *b);
@@ -94,13 +138,13 @@ static void        gtk_rc_clear_hash_node          (gpointer   key,
 static void        gtk_rc_clear_styles               (void);
 static void        gtk_rc_append_default_pixmap_path (void);
 static void        gtk_rc_append_default_module_path (void);
-static void        gtk_rc_append_pixmap_path         (gchar *dir);
+static void        gtk_rc_add_initial_default_files  (void);
 
 
-static GScannerConfig  gtk_rc_scanner_config =
+static const GScannerConfig    gtk_rc_scanner_config =
 {
   (
-   " \t\n"
+   " \t\r\n"
    )                   /* cset_skip_characters */,
   (
    G_CSET_a_2_z
@@ -138,7 +182,7 @@ static      GScannerConfig  gtk_rc_scanner_config =
   FALSE                        /* scope_0_fallback */,
 };
 
-static struct
+static const struct
 {
   gchar *name;
   guint token;
@@ -172,7 +216,7 @@ static struct
   { "module_path", GTK_RC_TOKEN_MODULE_PATH },
 };
 
-static guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);
+static const guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);
 
 static GHashTable *rc_style_ht = NULL;
 static GHashTable *realized_style_ht = NULL;
@@ -180,11 +224,20 @@ static GSList *gtk_rc_sets_widget = NULL;
 static GSList *gtk_rc_sets_widget_class = NULL;
 static GSList *gtk_rc_sets_class = NULL;
 
+#define GTK_RC_MAX_DEFAULT_FILES 128
+static gchar *gtk_rc_default_files[GTK_RC_MAX_DEFAULT_FILES];
+static gboolean gtk_rc_auto_parse = TRUE;
+
 #define GTK_RC_MAX_PIXMAP_PATHS 128
 static gchar *pixmap_path[GTK_RC_MAX_PIXMAP_PATHS];
 #define GTK_RC_MAX_MODULE_PATHS 128
 static gchar *module_path[GTK_RC_MAX_MODULE_PATHS];
 
+/* A stack of directories for RC files we are parsing currently.
+ * these are implicitely added to the end of PIXMAP_PATHS
+ */
+GSList *rc_dir_stack = NULL;
+
 /* The files we have parsed, to reread later if necessary */
 GSList *rc_files = NULL;
 
@@ -193,22 +246,50 @@ static GtkImageLoader image_loader = NULL;
 /* RC file handling */
 
 
+#ifdef G_OS_WIN32
+
+gchar *
+get_gtk_sysconf_directory (void)
+{
+  static gchar gtk_sysconf_dir[200];
+  gchar win_dir[100];
+
+  GetWindowsDirectory (win_dir, sizeof (win_dir));
+  sprintf (gtk_sysconf_dir, "%s\\gtk+", win_dir);
+  return gtk_sysconf_dir;
+}
+
+static gchar *
+get_themes_directory (void)
+{
+  /* We really should fetch this from the Registry. The GIMP
+   * installation program stores the Themes installation
+   * directory in HKLM\Software\GNU\GTk+\Themes\InstallDirectory.
+   * Later.
+   */
+  static gchar themes_dir[200];
+
+  sprintf (themes_dir, "%s\\themes", get_gtk_sysconf_directory ());
+  return themes_dir;
+}
+
+#endif
 gchar *
 gtk_rc_get_theme_dir(void)
 {
   gchar *var, *path;
 
+#ifndef G_OS_WIN32
   var = getenv("GTK_DATA_PREFIX");
   if (var)
-    {
-      path = g_malloc(strlen(var) + strlen("/share/themes") +1);
-      sprintf(path, "%s%s", var, "/share/themes");
-    }
+    path = g_strdup_printf("%s%s", var, "/share/themes");
   else
-    {
-      path = g_malloc(strlen(GTK_DATA_PREFIX) + strlen("/share/themes") +1);
-      sprintf(path, "%s%s", GTK_DATA_PREFIX, "/share/themes");
-    }
+    path = g_strdup_printf("%s%s", GTK_DATA_PREFIX, "/share/themes");
+#else
+  path = g_strdup (get_themes_directory ());
+#endif
+
   return path;
 }
 
@@ -217,17 +298,16 @@ gtk_rc_get_module_dir(void)
 {
   gchar *var, *path;
 
+#ifndef G_OS_WIN32
   var = getenv("GTK_EXE_PREFIX");
   if (var)
-    {
-      path = g_malloc(strlen(var) + strlen("/lib/gtk/themes/engines") +1);
-      sprintf(path, "%s%s", var, "/lib/gtk/themes/engines");
-    }
+    path = g_strdup_printf("%s%s", var, "/lib/gtk/themes/engines");
   else
-    {
-      path = g_malloc(strlen(GTK_EXE_PREFIX) + strlen("/lib/gtk/themes/engines") +1);
-      sprintf(path, "%s%s", GTK_EXE_PREFIX, "/lib/gtk/themes/engines");
-    }
+    path = g_strdup_printf("%s%s", GTK_EXE_PREFIX, "/lib/gtk/themes/engines");
+#else
+  path = g_strdup_printf ("%s%s", get_themes_directory (), "\\engines");
+#endif
+
   return path;
 }
 
@@ -237,81 +317,295 @@ gtk_rc_append_default_pixmap_path(void)
   gchar *var, *path;
   gint n;
 
+#ifndef G_OS_WIN32
   var = getenv("GTK_DATA_PREFIX");
   if (var)
-    {
-      path = g_malloc(strlen(var) + strlen("/share/gtk/themes") +1);
-      sprintf(path, "%s%s", var, "/share/gtk/themes");
-    }
+    path = g_strdup_printf("%s%s", var, "/share/gtk/themes");
   else
-    {
-      path = g_malloc(strlen(GTK_DATA_PREFIX) + strlen("/share/gtk/themes") +1);
-      sprintf(path, "%s%s", GTK_DATA_PREFIX, "/share/gtk/themes");
-    }
+    path = g_strdup_printf("%s%s", GTK_DATA_PREFIX, "/share/gtk/themes");
+#else
+  path = g_strdup (get_themes_directory ());
+#endif      
   
   for (n = 0; pixmap_path[n]; n++) ;
   if (n >= GTK_RC_MAX_PIXMAP_PATHS - 1)
-    return;
-  pixmap_path[n++] = g_strdup(path);
+    {
+      g_free (path);
+      return;
+    }
+  pixmap_path[n++] = path;
   pixmap_path[n] = NULL;
-  g_free(path);
 }
 
 static void
-gtk_rc_append_pixmap_path(gchar *dir)
+gtk_rc_append_default_module_path(void)
 {
+  gchar *var, *path;
   gint n;
 
-  for (n = 0; pixmap_path[n]; n++) ;
+  for (n = 0; module_path[n]; n++) ;
   if (n >= GTK_RC_MAX_MODULE_PATHS - 1)
     return;
-  pixmap_path[n++] = g_strdup(dir);
-  pixmap_path[n] = NULL;
+  
+#ifndef G_OS_WIN32
+  var = getenv("GTK_EXE_PREFIX");
+  if (var)
+    path = g_strdup_printf("%s%s", var, "/lib/gtk/themes/engines");
+  else
+    path = g_strdup_printf("%s%s", GTK_EXE_PREFIX, "/lib/gtk/themes/engines");
+#else
+  path = g_strdup_printf ("%s%s", get_themes_directory (), "\\engines");
+#endif
+  module_path[n++] = path;
+
+  var = g_get_home_dir ();
+  if (var)
+    {
+#ifndef G_OS_WIN32
+      path = g_strdup_printf ("%s%s", var, "/.gtk/lib/themes/engines");
+#else
+      path = g_strdup_printf ("%s%s", var, "\\_gtk\\themes\\engines");
+#endif
+      module_path[n++] = path;
+    }
+  module_path[n] = NULL;
 }
 
 static void
-gtk_rc_append_default_module_path(void)
+gtk_rc_add_initial_default_files (void)
 {
-  gchar *var, *path;
-  gint n;
+  static gint init = FALSE;
+  gchar *var, *str;
+  gchar **files;
+  gint i;
 
-  for (n = 0; module_path[n]; n++) ;
-  if (n >= GTK_RC_MAX_MODULE_PATHS - 1)
+  if (init)
     return;
   
-  var = getenv("GTK_EXE_PREFIX");
+  gtk_rc_default_files[0] = NULL;
+  init = TRUE;
+
+  var = g_getenv("GTK_RC_FILES");
   if (var)
     {
-      path = g_malloc(strlen(var) + strlen("/lib/gtk/themes/engines") +1);
-      sprintf(path, "%s%s", var, "/lib/gtk/themes/engines");
+      files = g_strsplit (var, G_SEARCHPATH_SEPARATOR_S, 128);
+      i=0;
+      while (files[i])
+       {
+         gtk_rc_add_default_file (files[i]);
+         i++;
+       }
+      g_strfreev (files);
     }
   else
     {
-      path = g_malloc(strlen(GTK_EXE_PREFIX) + strlen("/lib/gtk/themes/engines") +1);
-      sprintf(path, "%s%s", GTK_EXE_PREFIX, "/lib/gtk/themes/engines");
+#ifndef G_OS_WIN32
+      str = g_strdup (GTK_SYSCONFDIR G_DIR_SEPARATOR_S "gtk" G_DIR_SEPARATOR_S "gtkrc");
+#else
+      str = g_strdup_printf ("%s\\gtkrc", get_gtk_sysconf_directory ());
+#endif
+      gtk_rc_add_default_file (str);
+      g_free (str);
+
+      var = g_get_home_dir ();
+      if (var)
+       {
+         str = g_strdup_printf ("%s" G_DIR_SEPARATOR_S ".gtkrc", var);
+         gtk_rc_add_default_file (str);
+         g_free (str);
+       }
     }
-  module_path[n++] = g_strdup(path);
-  g_free(path);
-  var = getenv("HOME");
-  if (var)
+}
+
+void
+gtk_rc_add_default_file (const gchar *file)
+{
+  guint n;
+  
+  gtk_rc_add_initial_default_files ();
+
+  for (n = 0; gtk_rc_default_files[n]; n++) ;
+  if (n >= GTK_RC_MAX_DEFAULT_FILES - 1)
+    return;
+  
+  gtk_rc_default_files[n++] = g_strdup (file);
+  gtk_rc_default_files[n] = NULL;
+}
+
+void
+gtk_rc_set_default_files (gchar **files)
+{
+  gint i;
+
+  gtk_rc_add_initial_default_files ();
+
+  i = 0;
+  while (gtk_rc_default_files[i])
     {
-      path = g_malloc(strlen(var) + strlen(".gtk/lib/themes/engines") +1);
-      sprintf(path, "%s%s", var, ".gtk/lib/themes/engines");
+      g_free (gtk_rc_default_files[i]);
+      i++;
+    }
+    
+  gtk_rc_default_files[0] = NULL;
+  gtk_rc_auto_parse = FALSE;
+
+  i = 0;
+  while (files[i] != NULL)
+    {
+      gtk_rc_add_default_file (files[i]);
+      i++;
     }
-  module_path[n++] = g_strdup(path);
-  module_path[n] = NULL;
-  g_free(path);
 }
 
+gchar **
+gtk_rc_get_default_files (void)
+{
+  gtk_rc_add_initial_default_files ();
+
+  return gtk_rc_default_files;
+}
+
+ /* The following routine is based on _nl_normalize_codeset from
+  * the GNU C library. Contributed by
+  *
+  * Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+  * Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+  * 
+  * Normalize codeset name.  There is no standard for the codeset
+  * names.  Normalization allows the user to use any of the common
+  * names.
+  */
+ static char *
+ _gtk_normalize_codeset (const char *codeset, int name_len)
+ {
+   int len = 0;
+   int only_digit = 1;
+   char *retval;
+   char *wp;
+   int cnt;
+   for (cnt = 0; cnt < name_len; ++cnt)
+     if (isalnum (codeset[cnt]))
+       {
+       ++len;
+       if (isalpha (codeset[cnt]))
+         only_digit = 0;
+       }
+   retval = g_malloc ((only_digit ? 3 : 0) + len + 1);
+   if (only_digit)
+     {
+       strcpy (retval, "iso");
+       wp = retval + 3;
+     }
+   else
+     wp = retval;
+   
+   for (cnt = 0; cnt < name_len; ++cnt)
+     if (isalpha (codeset[cnt]))
+       *wp++ = isupper(codeset[cnt]) ? tolower (codeset[cnt]) : codeset[cnt];
+     else if (isdigit (codeset[cnt]))
+       *wp++ = codeset[cnt];
+   
+   *wp = '\0';
+   return retval;
+ }
 void
 gtk_rc_init (void)
 {
-  rc_style_ht = g_hash_table_new ((GHashFunc) gtk_rc_style_hash,
-                                 (GCompareFunc) gtk_rc_style_compare);
-  pixmap_path[0] = NULL;
-  module_path[0] = NULL;
-  gtk_rc_append_default_pixmap_path();
-  gtk_rc_append_default_module_path();
+  static gchar *locale_suffixes[3];
+  static gint n_locale_suffixes = 0;
+
+  gint i, j;
+
+  static gboolean initted = FALSE;
+
+  if (!initted)
+    {
+      gint length;
+      gchar *locale;
+      gchar *p;
+
+#ifdef G_OS_WIN32      
+      locale = g_win32_getlocale ();
+#else      
+      locale = setlocale (LC_CTYPE, NULL);
+#endif      
+      
+      initted = TRUE;
+
+      pixmap_path[0] = NULL;
+      module_path[0] = NULL;
+      gtk_rc_append_default_pixmap_path();
+      gtk_rc_append_default_module_path();
+      
+      gtk_rc_add_initial_default_files ();
+
+      if (strcmp (locale, "C") && strcmp (locale, "POSIX"))
+       {
+         /* Determine locale-specific suffixes for RC files
+          *
+          * We normalize the charset into a standard form,
+          * which has all '-' and '_' characters removed,
+          * and is lowercase.
+          */
+         gchar *normalized_locale;
+
+         p = strchr (locale, '@');
+         length = p ? (p -locale) : strlen (locale);
+
+         p = strchr (locale, '.');
+         if (p)
+           {
+             gchar *tmp1 = g_strndup (locale, p - locale + 1);
+             gchar *tmp2 = _gtk_normalize_codeset (p + 1, length - (p - locale + 1));
+             
+             normalized_locale = g_strconcat (tmp1, tmp2, NULL);
+             g_free (tmp1);
+             g_free (tmp2);
+                                                
+             locale_suffixes[n_locale_suffixes++] = g_strdup (normalized_locale);
+             length = p - locale;
+           }
+         else
+           normalized_locale = g_strndup (locale, length);
+         
+         p = strchr (normalized_locale, '_');
+         if (p)
+           {
+             locale_suffixes[n_locale_suffixes++] = g_strndup (normalized_locale, length);
+             length = p - normalized_locale;
+           }
+         
+         locale_suffixes[n_locale_suffixes++] = g_strndup (normalized_locale, length);
+
+         g_free (normalized_locale);
+       }
+    }
+  
+  i = 0;
+  while (gtk_rc_default_files[i] != NULL)
+    {
+      /* Try to find a locale specific RC file corresponding to
+       * to parse before the default file.
+       */
+      for (j=n_locale_suffixes-1; j>=0; j--)
+       {
+         gchar *name = g_strconcat (gtk_rc_default_files[i],
+                                    ".",
+                                    locale_suffixes[j],
+                                    NULL);
+         gtk_rc_parse (name);
+         g_free (name);
+       }
+
+      gtk_rc_parse (gtk_rc_default_files[i]);
+      i++;
+    }
 }
 
 void
@@ -356,7 +650,7 @@ gtk_rc_parse_file (const gchar *filename, gboolean reload)
     {
       /* Get the absolute pathname */
 
-      if (rc_file->name[0] == '/')
+      if (g_path_is_absolute (rc_file->name))
        rc_file->canonical_name = rc_file->name;
       else
        {
@@ -367,7 +661,7 @@ gtk_rc_parse_file (const gchar *filename, gboolean reload)
 
          str = g_string_new (cwd);
          g_free (cwd);
-         g_string_append_c (str, '/');
+         g_string_append_c (str, G_DIR_SEPARATOR);
          g_string_append (str, rc_file->name);
          
          rc_file->canonical_name = str->str;
@@ -378,6 +672,7 @@ gtk_rc_parse_file (const gchar *filename, gboolean reload)
   if (!lstat (rc_file->canonical_name, &statbuf))
     {
       gint fd;
+      GSList *tmp_list;
 
       rc_file->mtime = statbuf.st_mtime;
 
@@ -385,17 +680,18 @@ gtk_rc_parse_file (const gchar *filename, gboolean reload)
       if (fd < 0)
        return;
 
-       {
-         gint i;
-         gchar *dir;
-         
-         dir = g_strdup(rc_file->canonical_name);
-         for (i = strlen(dir) - 1; (i >= 0) && (dir[i] != '/'); i--)
-           dir[i] = 0;
-         gtk_rc_append_pixmap_path(dir);
-         g_free(dir);
-       }
+      /* Temporarily push directory name for this file on
+       * a stack of directory names while parsing it
+       */
+      rc_dir_stack = g_slist_prepend (rc_dir_stack,
+                                     g_dirname (rc_file->canonical_name));
       gtk_rc_parse_any (filename, fd, NULL);
+      tmp_list = rc_dir_stack;
+      rc_dir_stack = rc_dir_stack->next;
+      g_free (tmp_list->data);
+      g_slist_free_1 (tmp_list);
 
       close (fd);
     }
@@ -414,12 +710,12 @@ gtk_rc_parse (const gchar *filename)
 GtkRcStyle *
 gtk_rc_style_new              (void)
 {
-  GtkRcStyle *new_style;
+  GtkRcStylePrivate *new_style;
 
-  new_style = g_new0 (GtkRcStyle, 1);
+  new_style = g_new0 (GtkRcStylePrivate, 1);
   new_style->ref_count = 1;
 
-  return new_style;
+  return (GtkRcStyle *)new_style;
 }
 
 void      
@@ -427,20 +723,62 @@ gtk_rc_style_ref (GtkRcStyle  *rc_style)
 {
   g_return_if_fail (rc_style != NULL);
 
-  rc_style->ref_count++;
+  ((GtkRcStylePrivate *)rc_style)->ref_count++;
+}
+
+/* Like g_slist_remove, but remove all copies of data */
+static GSList*
+gtk_rc_slist_remove_all (GSList   *list,
+                        gpointer  data)
+{
+  GSList *tmp;
+  GSList *prev;
+
+  prev = NULL;
+  tmp = list;
+
+  while (tmp)
+    {
+      if (tmp->data == data)
+       {
+         if (list == tmp)
+           list = list->next;
+
+         if (prev) 
+           prev->next = tmp->next;
+
+         g_slist_free_1 (tmp);
+
+         if (prev)
+           tmp = prev->next;
+         else
+           tmp = list;
+       }
+      else
+       {
+         prev = tmp;
+         tmp = tmp->next;
+       }
+    }
+
+  return list;
 }
 
 void      
 gtk_rc_style_unref (GtkRcStyle  *rc_style)
 {
+  GtkRcStylePrivate *private = (GtkRcStylePrivate *)rc_style;
   gint i;
 
   g_return_if_fail (rc_style != NULL);
+  g_return_if_fail (private->ref_count > 0);
 
-  rc_style->ref_count--;
+  private->ref_count--;
 
-  if (rc_style->ref_count == 0)
+  if (private->ref_count == 0)
     {
+      GSList *tmp_list1, *tmp_list2;
+       
       if (rc_style->engine)
        {
          rc_style->engine->destroy_rc_style (rc_style);
@@ -458,16 +796,42 @@ gtk_rc_style_unref (GtkRcStyle  *rc_style)
        if (rc_style->bg_pixmap_name[i])
          g_free (rc_style->bg_pixmap_name[i]);
       
-      g_free (rc_style);
-    }
-}
+      /* Now remove all references to this rc_style from
+       * realized_style_ht
+       */
+      tmp_list1 = private->rc_style_lists;
+      while (tmp_list1)
+       {
+         GSList *rc_styles = tmp_list1->data;
+         GtkStyle *style = g_hash_table_lookup (realized_style_ht, rc_styles);
+         gtk_style_unref (style);
+
+         /* Remove the list of styles from the other rc_styles
+          * in the list
+          */
+         tmp_list2 = rc_styles;
+         while (tmp_list2)
+           {
+             GtkRcStylePrivate *other_style = tmp_list2->data;
 
-static void
-gtk_rc_clear_realized_node (gpointer key,
-                           gpointer data,
-                           gpointer user_data)
-{
-  gtk_style_unref (data);
+             if (other_style != private)
+               other_style->rc_style_lists =
+                 gtk_rc_slist_remove_all (other_style->rc_style_lists, rc_styles);
+                 
+             tmp_list2 = tmp_list2->next;
+           }
+
+         /* And from the hash table itself
+          */
+         g_hash_table_remove (realized_style_ht, rc_styles);
+         g_slist_free (rc_styles);
+
+         tmp_list1 = tmp_list1->next;
+       }
+      g_slist_free (private->rc_style_lists);
+
+      g_free (private);
+    }
 }
 
 static void
@@ -505,13 +869,6 @@ gtk_rc_clear_styles (void)
       rc_style_ht = NULL;
     }
 
-  if (realized_style_ht)
-    {
-      g_hash_table_foreach (realized_style_ht, gtk_rc_clear_realized_node, NULL);
-      g_hash_table_destroy (realized_style_ht);
-      realized_style_ht = NULL;
-    }
-
   gtk_rc_free_rc_sets (gtk_rc_sets_widget);
   g_slist_free (gtk_rc_sets_widget);
   gtk_rc_sets_widget = NULL;
@@ -523,8 +880,6 @@ gtk_rc_clear_styles (void)
   gtk_rc_free_rc_sets (gtk_rc_sets_class);
   g_slist_free (gtk_rc_sets_class);
   gtk_rc_sets_class = NULL;
-
-  gtk_rc_init ();
 }
 
 gboolean
@@ -729,7 +1084,7 @@ gtk_rc_parse_any (const gchar  *input_name,
   guint           i;
   gboolean done;
   
-  scanner = g_scanner_new (&gtk_rc_scanner_config);
+  scanner = g_scanner_new ((GScannerConfig *) &gtk_rc_scanner_config);
   
   if (input_fd >= 0)
     {
@@ -861,11 +1216,10 @@ gtk_rc_style_compare (const char *a,
 static GtkRcStyle*
 gtk_rc_style_find (const char *name)
 {
-  GtkRcStyle *rc_style;
-  
-  rc_style = g_hash_table_lookup (rc_style_ht, (gpointer) name);
-  
-  return rc_style;
+  if (rc_style_ht)
+    return g_hash_table_lookup (rc_style_ht, (gpointer) name);
+  else
+    return NULL;
 }
 
 /* Assumes ownership of rc_style */
@@ -928,7 +1282,6 @@ gtk_rc_style_init (GSList *rc_styles)
   gint i;
 
   GtkStyle *style = NULL;
-  GtkRcStyle *proto_style;
 
   if (!realized_style_ht)
     realized_style_ht = g_hash_table_new ((GHashFunc)gtk_rc_styles_hash,
@@ -938,13 +1291,16 @@ gtk_rc_style_init (GSList *rc_styles)
 
   if (!style)
     {
-      GSList *tmp_styles = rc_styles;
+      GtkRcStyle *proto_style;
+      GSList *tmp_styles;
       
       proto_style = gtk_rc_style_new ();
 
+      tmp_styles = rc_styles;
       while (tmp_styles)
        {
          GtkRcStyle *rc_style = tmp_styles->data;
+         GtkRcStylePrivate *rc_style_private;
 
          for (i=0; i<5; i++)
            {
@@ -992,13 +1348,29 @@ gtk_rc_style_init (GSList *rc_styles)
              (proto_style->engine == rc_style->engine))
            proto_style->engine->merge_rc_style (proto_style, rc_style);
 
+         /* Point from each rc_style to the list of styles */
+
+         rc_style_private = (GtkRcStylePrivate *)rc_style;
+         if (!g_slist_find (rc_style_private->rc_style_lists, rc_styles))
+           rc_style_private->rc_style_lists = g_slist_prepend (rc_style_private->rc_style_lists, rc_styles);
+
          tmp_styles = tmp_styles->next;
        }
 
+      for (i=0; i<5; i++)
+       if (proto_style->bg_pixmap_name[i] &&
+           (strcmp (proto_style->bg_pixmap_name[i], "<none>") == 0))
+         {
+           g_free (proto_style->bg_pixmap_name[i]);
+           proto_style->bg_pixmap_name[i] = NULL;
+         }
+
       style = gtk_rc_style_to_style (proto_style);
 
       g_hash_table_insert (realized_style_ht, rc_styles, style);
     }
+  else
+    g_slist_free (rc_styles);
 
   return style;
 }
@@ -1073,7 +1445,7 @@ gtk_rc_parse_style (GScanner *scanner)
     return G_TOKEN_STRING;
   
   insert = FALSE;
-  rc_style = g_hash_table_lookup (rc_style_ht, scanner->value.v_string);
+  rc_style = gtk_rc_style_find (scanner->value.v_string);
   
   if (!rc_style)
     {
@@ -1105,7 +1477,7 @@ gtk_rc_parse_style (GScanner *scanner)
          return G_TOKEN_STRING;
        }
       
-      parent_style = g_hash_table_lookup (rc_style_ht, scanner->value.v_string);
+      parent_style = gtk_rc_style_find (scanner->value.v_string);
       if (parent_style)
        {
          for (i = 0; i < 5; i++)
@@ -1221,7 +1593,13 @@ gtk_rc_parse_style (GScanner *scanner)
     }
   
   if (insert)
-    g_hash_table_insert (rc_style_ht, rc_style->name, rc_style);
+    {
+      if (!rc_style_ht)
+       rc_style_ht = g_hash_table_new ((GHashFunc) gtk_rc_style_hash,
+                                       (GCompareFunc) gtk_rc_style_compare);
+      
+      g_hash_table_insert (rc_style_ht, rc_style->name, rc_style);
+    }
   
   return G_TOKEN_NONE;
 }
@@ -1342,10 +1720,11 @@ gtk_rc_parse_bg_pixmap (GScanner   *scanner,
   if (token != G_TOKEN_STRING)
     return G_TOKEN_STRING;
   
-  if (strcmp (scanner->value.v_string, "<parent>"))
-    pixmap_file = gtk_rc_find_pixmap_in_path (scanner, scanner->value.v_string);
-  else
+  if ((strcmp (scanner->value.v_string, "<parent>") == 0) ||
+      (strcmp (scanner->value.v_string, "<none>") == 0))
     pixmap_file = g_strdup (scanner->value.v_string);
+  else
+    pixmap_file = gtk_rc_find_pixmap_in_path (scanner, scanner->value.v_string);
   
   if (pixmap_file)
     {
@@ -1357,34 +1736,56 @@ gtk_rc_parse_bg_pixmap (GScanner   *scanner,
   return G_TOKEN_NONE;
 }
 
+static gchar*
+gtk_rc_check_pixmap_dir (const gchar *dir, const gchar *pixmap_file)
+{
+  gchar *buf;
+  gint fd;
+
+  buf = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s", dir, pixmap_file);
+  
+  fd = open (buf, O_RDONLY);
+  if (fd >= 0)
+    {
+      close (fd);
+      return buf;
+    }
+   
+  g_free (buf);
+   return NULL;
+ }
 gchar*
 gtk_rc_find_pixmap_in_path (GScanner *scanner,
-                           const gchar *pixmap_file)
+                           const gchar *pixmap_file)
 {
   gint i;
-  gint fd;
-  gchar *buf;
-  
+  gchar *filename;
+  GSList *tmp_list;
+    
   for (i = 0; (i < GTK_RC_MAX_PIXMAP_PATHS) && (pixmap_path[i] != NULL); i++)
     {
-      buf = g_malloc (strlen (pixmap_path[i]) + strlen (pixmap_file) + 2);
-      sprintf (buf, "%s%c%s", pixmap_path[i], '/', pixmap_file);
-      
-      fd = open (buf, O_RDONLY);
-      if (fd >= 0)
-       {
-         close (fd);
-         return buf;
-       }
-      
-      g_free (buf);
+      filename = gtk_rc_check_pixmap_dir (pixmap_path[i], pixmap_file);
+      if (filename)
+       return filename;
     }
-
+  tmp_list = rc_dir_stack;
+  while (tmp_list)
+    {
+      filename = gtk_rc_check_pixmap_dir (tmp_list->data, pixmap_file);
+      if (filename)
+       return filename;
+       
+      tmp_list = tmp_list->next;
+    }
+  
   if (scanner)
-    g_warning ("Unable to locate image file in pixmap_path: \"%s\" line %d",
+    g_warning (_("Unable to locate image file in pixmap_path: \"%s\" line %d"),
               pixmap_file, scanner->line);
   else
-    g_warning ("Unable to locate image file in pixmap_path: \"%s\"",
+    g_warning (_("Unable to locate image file in pixmap_path: \"%s\""),
               pixmap_file);
     
   return NULL;
@@ -1399,8 +1800,8 @@ gtk_rc_find_module_in_path (const gchar *module_file)
   
   for (i = 0; (i < GTK_RC_MAX_MODULE_PATHS) && (module_path[i] != NULL); i++)
     {
-      buf = g_malloc (strlen (module_path[i]) + strlen (module_file) + 2);
-      sprintf (buf, "%s%c%s", module_path[i], '/', module_file);
+      buf = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s",
+                            module_path[i], module_file);
       
       fd = open (buf, O_RDONLY);
       if (fd >= 0)
@@ -1764,7 +2165,7 @@ gtk_rc_parse_pixmap_path_string (gchar *pix_path)
   
   for (end_offset = 0; end_offset <= path_len; end_offset++)
     {
-      if ((buf[end_offset] == ':') ||
+      if ((buf[end_offset] == G_SEARCHPATH_SEPARATOR) ||
          (end_offset == path_len))
        {
          buf[end_offset] = '\0';
@@ -1820,7 +2221,7 @@ gtk_rc_parse_module_path_string (gchar *mod_path)
   
   for (end_offset = 0; end_offset <= path_len; end_offset++)
     {
-      if ((buf[end_offset] == ':') ||
+      if ((buf[end_offset] == G_SEARCHPATH_SEPARATOR) ||
          (end_offset == path_len))
        {
          buf[end_offset] = '\0';