updated stock patch



Hi,

This patch has the following changes:

 - icon sizes are represented by string ID's, similar to the way 
   we do stock items (this stems from a discussion on IRC). 
   This allows dynamic addition of new icon sizes, and the creation 
   of aliases.   

 - New function gtk_stock_get_items() that returns a list of 
   stock items for GUI builders

 - You can omit part of an icon source spec, which creates an 
   implicit "*" for omitted portions (e.g. { "filename.png", * }
   creates two implicit "*"

Maybe some other change too, but I think that's it. I can't do a diff
vs. my old patch since I'm hacking on the result of update -j from my
branch, and thus can't use CVS.

I didn't add the label/stock_id object args to GtkButton or implement
stock GtkMenuItems yet, but those are both changes worth making.

I think I should commit everything but the dialog changes in the near
future, then Tim is going to hack on the inline pixbuf stuff, and I
can post about the dialog outstanding issues.

Havoc

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.1361
diff -u -u -r1.1361 ChangeLog
--- ChangeLog	2000/07/28 00:09:34	1.1361
+++ ChangeLog	2000/07/28 18:48:24
@@ -1,3 +1,63 @@
+2000-07-26  Havoc Pennington  <hp@redhat.com>
+	
+	* gtk/gtkiconfactory.h, gtk/gtkiconfactory.c: New icon system. Three 
+	important types:
+
+	(GtkIconSource): Specification for creating a pixbuf 
+	appropriate for a direction/state/size triplet from 
+	a source pixbuf or filename
+
+	(GtkIconSet): List of GtkIconSource that used to create
+	the "same" icon (e.g. an OK button icon), and cache 
+	for rendered icons
+
+	(GtkIconFactory): Hash from stock ID to GtkIconSet; used to look
+	up the icon set for a given stock ID.  GTK maintains a stack of
+	GtkIconFactory to search, and applications or libraries can add
+	additional icon factories on top of the stack
+	
+        * gtk/gtkrc.h, gtk/gtkrc.c: When loading an RcStyle, parse 
+	the set of GtkIconSource specified for a given stock ID into 
+	a GtkIconSet, and put the GtkIconSet into a GtkIconFactory for the 
+	RcStyle, under the specified stock ID.
+
+	* gtk/gtkstyle.h, gtk/gtkstyle.c: Add a virtual function
+	render_icon used to derive a GdkPixbuf from a GtkIconSource.
+	This allows people to theme how prelight, insensitive, etc. are
+	done.
+
+	(gtk_style_lookup_icon_set): Look up a stock ID in the list of
+	icon factories for a style, and return the resulting 
+	icon set if any.
+
+	(gtk_style_render_icon): Render an icon using the render_icon 
+	method in the GtkStyleClass.
+
+	(_gtk_default_render_icon): Default icon-render function, used
+	here and also in gtkiconset.c (because you can use an icon set 
+	without a style, though it won't be themed)
+
+	(GtkIconSizeType): new enum specifies semantic sizes of icons
+
+	* gtk/gtkwidget.h, gtk/gtkwidget.c (gtk_widget_render_icon): 
+	Use the style for a given widget to look up a stock ID, get the
+	icon set, and render an icon using the render_icon method 
+	of the style
+
+	* gtk/gtkstock.h, gtk/gtkstock.c: Header with the GtkStockItem type
+	(contains information about a stock item), the built-in stock item
+	IDs, and functions to add/lookup stock items.
+
+	* gtk/stock-icons/*: Stock icons that come with GTK
+
+	* gtk/gtkbutton.h, gtk/gtkbutton.c (gtk_button_new_stock): Returns
+	a button based on a GtkStockItem
+	(gtk_button_new_accel): Takes a uline string and accel group, and
+	installs the accelerator.
+
+	* gtk/gtkimage.h, gtk/gtkimage.c: Make this into a generic
+	image-display widget.
+	
 2000-07-27  Elliot Lee  <sopwith@redhat.com>
 
 	* gtk/gdk-pixbuf-loader.[ch]: Add gdk_pixbuf_loader_new_with_type
Index: gtk/gtkbutton.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkbutton.c,v
retrieving revision 1.46
diff -u -u -r1.46 gtkbutton.c
--- gtk/gtkbutton.c	2000/07/26 11:32:42	1.46
+++ gtk/gtkbutton.c	2000/07/28 18:48:24
@@ -29,8 +29,11 @@
 #include "gtklabel.h"
 #include "gtkmain.h"
 #include "gtksignal.h"
+#include "gtkimage.h"
+#include "gtkhbox.h"
+#include "gtkstock.h"
+#include "gtkiconfactory.h"
 
-
 #define CHILD_SPACING     1
 #define DEFAULT_LEFT_POS  4
 #define DEFAULT_TOP_POS   4
@@ -307,6 +310,93 @@
 
   gtk_container_add (GTK_CONTAINER (button), label_widget);
   gtk_widget_show (label_widget);
+
+  return button;
+}
+
+GtkWidget*
+gtk_button_new_stock (const gchar   *stock_id,
+                      GtkAccelGroup *accel_group)
+{
+  GtkWidget *button;
+  GtkStockItem item;
+
+  if (gtk_stock_lookup (stock_id, &item))
+    {
+      GtkWidget *label;
+      GtkWidget *image;
+      GtkWidget *hbox;
+      guint keyval;
+      
+      button = gtk_button_new ();
+
+      label = gtk_label_new ("");
+      keyval = gtk_label_parse_uline (GTK_LABEL (label),
+                                      item.label);
+
+      if (keyval && accel_group)
+        {
+          gtk_widget_add_accelerator (button,
+                                      "clicked",
+                                      accel_group,
+                                      keyval,
+                                      GDK_MOD1_MASK,
+                                      GTK_ACCEL_VISIBLE);
+        }
+
+      /* Also add the stock accelerator if one was specified. */
+      if (item.keyval && accel_group)
+        {
+          gtk_widget_add_accelerator (button,
+                                      "clicked",
+                                      accel_group,
+                                      item.keyval,
+                                      item.modifier,
+                                      GTK_ACCEL_VISIBLE);
+        }
+
+      image = gtk_image_new_from_stock (stock_id, GTK_ICON_BUTTON);
+      hbox = gtk_hbox_new (FALSE, 0);
+
+      gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 2);
+      gtk_box_pack_end (GTK_BOX (hbox), label, TRUE, TRUE, 2);
+      
+      gtk_container_add (GTK_CONTAINER (button), hbox);
+      gtk_widget_show_all (hbox);
+    }
+  else
+    {
+      button = gtk_button_new_accel (stock_id, accel_group);
+    }
+  
+  return button;
+}
+
+GtkWidget*
+gtk_button_new_accel (const gchar   *uline_label,
+                      GtkAccelGroup *accel_group)
+{
+  GtkWidget *button;
+  GtkWidget *label;
+  guint keyval;
+
+  button = gtk_button_new ();
+  
+  label = gtk_label_new ("");
+  keyval = gtk_label_parse_uline (GTK_LABEL (label), uline_label);
+
+  if (keyval && accel_group)
+    {
+      gtk_widget_add_accelerator (button,
+                                  "clicked",
+                                  accel_group,
+                                  keyval,
+                                  GDK_MOD1_MASK,
+                                  GTK_ACCEL_VISIBLE);
+    }
+  
+  gtk_container_add (GTK_CONTAINER (button), label);
+  gtk_widget_show (label);
 
   return button;
 }
Index: gtk/gtkbutton.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkbutton.h,v
retrieving revision 1.12
diff -u -u -r1.12 gtkbutton.h
--- gtk/gtkbutton.h	2000/07/26 11:32:42	1.12
+++ gtk/gtkbutton.h	2000/07/28 18:48:24
@@ -75,7 +75,11 @@
 
 GtkType        gtk_button_get_type       (void);
 GtkWidget*     gtk_button_new            (void);
-GtkWidget*     gtk_button_new_with_label (const gchar *label);
+GtkWidget*     gtk_button_new_with_label (const gchar   *label);
+GtkWidget*     gtk_button_new_stock      (const gchar   *stock_id,
+                                          GtkAccelGroup *accel_group);
+GtkWidget*     gtk_button_new_accel      (const gchar   *uline_label,
+                                          GtkAccelGroup *accel_group);
 void           gtk_button_pressed        (GtkButton *button);
 void           gtk_button_released       (GtkButton *button);
 void           gtk_button_clicked        (GtkButton *button);
Index: gtk/gtkiconfactory.c
===================================================================
RCS file: gtkiconfactory.c
diff -N gtkiconfactory.c
--- /dev/null	Tue May  5 16:32:27 1998
+++ gtkiconfactory.c	Fri Jul 28 14:48:24 2000
@@ -0,0 +1,969 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  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 "gtkiconfactory.h"
+#include "stock-icons/gtkstockpixbufs.h"
+#include "gtkstock.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+
+static gpointer parent_class = NULL;
+
+static void gtk_icon_factory_init       (GtkIconFactory      *icon_factory);
+static void gtk_icon_factory_class_init (GtkIconFactoryClass *klass);
+static void gtk_icon_factory_finalize   (GObject             *object);
+static void get_default_icons           (GtkIconFactory      *icon_factory);
+
+GType
+gtk_icon_factory_get_type (void)
+{
+  static GType object_type = 0;
+
+  if (!object_type)
+    {
+      static const GTypeInfo object_info =
+      {
+        sizeof (GtkIconFactoryClass),
+        (GBaseInitFunc) NULL,
+        (GBaseFinalizeFunc) NULL,
+        (GClassInitFunc) gtk_icon_factory_class_init,
+        NULL,           /* class_finalize */
+        NULL,           /* class_data */
+        sizeof (GtkIconFactory),
+        0,              /* n_preallocs */
+        (GInstanceInitFunc) gtk_icon_factory_init,
+      };
+      
+      object_type = g_type_register_static (G_TYPE_OBJECT,
+                                            "GtkIconFactory",
+                                            &object_info);
+    }
+  
+  return object_type;
+}
+
+static void
+gtk_icon_factory_init (GtkIconFactory *factory)
+{
+  factory->icons = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+static void
+gtk_icon_factory_class_init (GtkIconFactoryClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+  parent_class = g_type_class_peek_parent (klass);
+
+  object_class->finalize = gtk_icon_factory_finalize;
+}
+
+static void
+free_icon_set (gpointer key, gpointer value, gpointer data)
+{
+  g_free (key);
+  gtk_icon_set_unref (value);
+}
+
+static void
+gtk_icon_factory_finalize (GObject *object)
+{
+  GtkIconFactory *factory = GTK_ICON_FACTORY (object);
+
+  g_hash_table_foreach (factory->icons, free_icon_set, NULL);
+  
+  g_hash_table_destroy (factory->icons);
+  
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GtkIconFactory*
+gtk_icon_factory_new (void)
+{
+  return GTK_ICON_FACTORY (g_object_new (GTK_TYPE_ICON_FACTORY, NULL));
+}
+
+void
+gtk_icon_factory_add (GtkIconFactory *factory,
+                      const gchar    *stock_id,
+                      GtkIconSet     *icon_set)
+{
+  gpointer old_key = NULL;
+  gpointer old_value = NULL;
+
+  g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
+  g_return_if_fail (stock_id != NULL);
+  g_return_if_fail (icon_set != NULL);  
+
+  g_hash_table_lookup_extended (factory->icons, stock_id,
+                                &old_key, &old_value);
+
+  if (old_value == icon_set)
+    return;
+  
+  gtk_icon_set_ref (icon_set);
+
+  /* GHashTable key memory management is so fantastically broken. */
+  if (old_key)
+    g_hash_table_insert (factory->icons, old_key, icon_set);
+  else
+    g_hash_table_insert (factory->icons, g_strdup (stock_id), icon_set);
+
+  if (old_value)
+    gtk_icon_set_unref (old_value);
+}
+
+GtkIconSet *
+gtk_icon_factory_lookup (GtkIconFactory *factory,
+                         const gchar    *stock_id)
+{
+  g_return_val_if_fail (GTK_IS_ICON_FACTORY (factory), NULL);
+  g_return_val_if_fail (stock_id != NULL, NULL);
+  
+  return g_hash_table_lookup (factory->icons, stock_id);
+}
+
+static GtkIconFactory *gtk_default_icons = NULL;
+static GSList *default_factories = NULL;
+
+void
+gtk_add_default_icon_factory (GtkIconFactory *factory)
+{
+  g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
+
+  g_object_ref (G_OBJECT (factory));
+  
+  default_factories = g_slist_prepend (default_factories, factory);
+}
+
+void
+gtk_remove_default_icon_factory (GtkIconFactory  *factory)
+{
+  g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
+
+  default_factories = g_slist_remove (default_factories, factory);
+
+  g_object_unref (G_OBJECT (factory));
+}
+
+GtkIconSet *
+gtk_default_icon_lookup (const gchar *stock_id)
+{
+  GSList *iter;
+
+  g_return_val_if_fail (stock_id != NULL, NULL);
+  
+  iter = default_factories;
+  while (iter != NULL)
+    {
+      GtkIconSet *icon_set =
+        gtk_icon_factory_lookup (GTK_ICON_FACTORY (iter->data),
+                                 stock_id);
+
+      if (icon_set)
+        return icon_set;
+
+      iter = g_slist_next (iter);
+    }
+
+  if (gtk_default_icons == NULL)
+    {
+      gtk_default_icons = gtk_icon_factory_new ();
+
+      get_default_icons (gtk_default_icons);
+    }
+  
+  return gtk_icon_factory_lookup (gtk_default_icons, stock_id);
+}
+
+static GtkIconSet *
+sized_icon_set_from_inline (const guchar *inline_data,
+                            const gchar  *size)
+{
+  GtkIconSet *set;
+
+  GtkIconSource source = { NULL, NULL, 0, 0, size,
+                           TRUE, TRUE, FALSE };
+
+  set = gtk_icon_set_new ();
+
+  source.pixbuf = gdk_pixbuf_new_from_inline (inline_data, FALSE, -1);
+
+  g_assert (source.pixbuf);
+
+  gtk_icon_set_add_source (set, &source);
+
+  return set;
+}
+
+static GtkIconSet *
+unsized_icon_set_from_inline (const guchar *inline_data)
+{
+  GtkIconSet *set;
+
+  /* This icon can be used for any direction/state/size */
+  GtkIconSource source = { NULL, NULL, 0, 0, 0,
+                           TRUE, TRUE, TRUE };
+
+  set = gtk_icon_set_new ();
+
+  source.pixbuf = gdk_pixbuf_new_from_inline (inline_data, FALSE, -1);
+
+  g_assert (source.pixbuf);
+
+  gtk_icon_set_add_source (set, &source);
+
+  return set;
+}
+
+static void
+add_sized (GtkIconFactory *factory,
+           const guchar *inline_data,
+           const gchar *size,
+           const gchar *stock_id)
+{
+  GtkIconSet *set;
+  
+  set = sized_icon_set_from_inline (inline_data, size);
+  
+  gtk_icon_factory_add (factory, stock_id, set);
+
+  gtk_icon_set_unref (set);
+}
+
+static void
+add_unsized (GtkIconFactory *factory,
+             const guchar *inline_data,
+             const gchar *stock_id)
+{
+  GtkIconSet *set;
+  
+  set = unsized_icon_set_from_inline (inline_data);
+  
+  gtk_icon_factory_add (factory, stock_id, set);
+
+  gtk_icon_set_unref (set);
+}
+
+static void
+get_default_icons (GtkIconFactory *factory)
+{
+  /* KEEP IN SYNC with gtkstock.c */
+
+  add_sized (factory, dialog_error, GTK_ICON_DIALOG, GTK_STOCK_DIALOG_ERROR);
+  add_sized (factory, dialog_info, GTK_ICON_DIALOG, GTK_STOCK_DIALOG_INFO);
+  add_sized (factory, dialog_question, GTK_ICON_DIALOG, GTK_STOCK_DIALOG_QUESTION);
+  add_sized (factory, dialog_warning, GTK_ICON_DIALOG, GTK_STOCK_DIALOG_WARNING);
+
+  add_sized (factory, stock_button_apply, GTK_ICON_BUTTON, GTK_STOCK_BUTTON_APPLY);
+  add_sized (factory, stock_button_ok, GTK_ICON_BUTTON, GTK_STOCK_BUTTON_OK);
+  add_sized (factory, stock_button_cancel, GTK_ICON_BUTTON, GTK_STOCK_BUTTON_CANCEL);
+  add_sized (factory, stock_button_close, GTK_ICON_BUTTON, GTK_STOCK_BUTTON_CLOSE);
+  add_sized (factory, stock_button_yes, GTK_ICON_BUTTON, GTK_STOCK_BUTTON_YES);
+  add_sized (factory, stock_button_no, GTK_ICON_BUTTON, GTK_STOCK_BUTTON_NO);
+    
+  add_unsized (factory, stock_close, GTK_STOCK_CLOSE);
+  add_unsized (factory, stock_exit, GTK_STOCK_QUIT);
+  add_unsized (factory, stock_help, GTK_STOCK_HELP);
+  add_unsized (factory, stock_new, GTK_STOCK_NEW);
+  add_unsized (factory, stock_open, GTK_STOCK_OPEN);
+  add_unsized (factory, stock_save, GTK_STOCK_SAVE);
+}
+
+/* Sizes */
+
+static GHashTable *icon_sizes = NULL;
+
+static void
+init_icon_sizes (void)
+{
+  if (icon_sizes == NULL)
+    {
+      icon_sizes = g_hash_table_new (g_str_hash, g_str_equal);
+
+      g_hash_table_insert (icon_sizes,
+                           g_strdup (GTK_ICON_MENU), g_strdup ("16x16"));
+      g_hash_table_insert (icon_sizes,
+                           g_strdup (GTK_ICON_BUTTON), g_strdup ("24x24"));
+      g_hash_table_insert (icon_sizes,
+                           g_strdup (GTK_ICON_SMALL_TOOLBAR), g_strdup ("18x18"));
+      g_hash_table_insert (icon_sizes,
+                           g_strdup (GTK_ICON_LARGE_TOOLBAR), g_strdup ("24x24"));
+      g_hash_table_insert (icon_sizes,
+                           g_strdup (GTK_ICON_DIALOG), g_strdup ("48x48"));
+    }
+}
+
+gboolean
+gtk_parse_icon_size (const gchar *alias,
+                     gint *widthp,
+                     gint *heightp)
+{
+  const gchar *prev;
+  const gchar *next;
+  gchar *end = NULL;
+  const gchar *p;
+  gint width, height;
+  
+  g_return_val_if_fail (alias != NULL, FALSE);
+  
+  init_icon_sizes ();
+
+  prev = alias;
+  while ( (next = g_hash_table_lookup (icon_sizes, prev)) != NULL)
+    prev = next;
+
+  /* prev should be a numeric string e.g. "10x10" */
+
+  g_assert (prev != NULL);
+
+  if (!isdigit (prev[0]))
+    {
+      g_warning ("Failure parsing icon size '%s': doesn't start with digit",
+                 prev);
+      return FALSE;
+    }
+
+  p = prev;
+  
+  errno = 0;
+  width = strtoul (p, &end, 10);
+  if (errno != 0 || p == end)
+    {
+      g_warning ("Failure parsing icon width in string '%s'", prev);
+      return FALSE;
+    }
+  
+  p = end;
+  if (*p != 'x')
+    {
+      g_warning ("Character after width in icon size is not 'x': '%s'", prev);
+      return FALSE;
+    }
+  ++p;
+  
+  errno = 0;
+  height = strtoul (p, &end, 10);
+  if (errno != 0 || p == end)
+    {
+      g_warning ("couldn't parse icon height in string '%s'", prev);
+      return FALSE;
+    }
+
+  if (*end != '\0')
+    {
+      g_warning ("Icon size '%s' contains extra characters after the size",
+                 prev);
+      return FALSE;
+    }
+
+  if (widthp)
+    *widthp = width;
+
+  if (heightp)
+    *heightp = height;
+
+  return TRUE;
+}
+
+void
+gtk_register_icon_size (const gchar *alias,
+                        const gchar *value)
+{
+  gpointer old_key, old_value;
+  
+  g_return_if_fail (alias != NULL);
+  g_return_if_fail (value != NULL);
+  
+  init_icon_sizes ();
+
+  if (g_hash_table_lookup_extended (icon_sizes,
+                                    alias,
+                                    &old_key, &old_value))
+    {
+      g_hash_table_insert (icon_sizes, old_key, g_strdup (value));
+      g_free (old_value); /* note, old_key is left in the hash */
+    }
+  else
+    g_hash_table_insert (icon_sizes, g_strdup (alias), g_strdup (value));
+}
+
+/* Icon Set */
+
+static GdkPixbuf *find_in_cache (GtkIconSet      *icon_set,
+                                 GtkStyle        *style,
+                                 GtkTextDirection   direction,
+                                 GtkStateType     state,
+                                 const gchar     *size);
+static void       add_to_cache  (GtkIconSet      *icon_set,
+                                 GtkStyle        *style,
+                                 GtkTextDirection   direction,
+                                 GtkStateType     state,
+                                 const gchar     *size,
+                                 GdkPixbuf       *pixbuf);
+static void       clear_cache   (GtkIconSet      *icon_set);
+static GSList*    copy_cache    (GtkIconSet      *icon_set);
+
+static GSList* direction_state_size_matches (GSList           *sources,
+                                             GtkTextDirection  direction,
+                                             GtkStateType      state,
+                                             const gchar      *size);
+static GSList* state_size_matches           (GSList           *sources,
+                                             GtkStateType      state,
+                                             const gchar      *size);
+static GSList* size_matches                 (GSList           *sources,
+                                             const gchar      *size);
+
+struct _GtkIconSet
+{
+  guint ref_count;
+
+  GSList *sources;
+
+  /* Cache of the last few rendered versions of the icon. */
+  GSList *cache;
+
+  guint cache_size;
+};
+
+GtkIconSet*
+gtk_icon_set_new (void)
+{
+  GtkIconSet *icon_set;
+
+  icon_set = g_new (GtkIconSet, 1);
+
+  icon_set->ref_count = 1;
+  icon_set->sources = NULL;
+  icon_set->cache = NULL;
+  icon_set->cache_size = 0;
+  
+  return icon_set;
+}
+
+GtkIconSet*
+gtk_icon_set_ref (GtkIconSet *icon_set)
+{
+  g_return_val_if_fail (icon_set != NULL, NULL);
+  g_return_val_if_fail (icon_set->ref_count > 0, NULL);
+
+  icon_set->ref_count += 1;
+
+  return icon_set;
+}
+
+void
+gtk_icon_set_unref (GtkIconSet *icon_set)
+{
+  g_return_if_fail (icon_set != NULL);
+  g_return_if_fail (icon_set->ref_count > 0);
+
+  icon_set->ref_count -= 1;
+
+  if (icon_set->ref_count == 0)
+    {
+      GSList *iter = icon_set->sources;
+      while (iter != NULL)
+        {
+          gtk_icon_source_free (iter->data);
+
+          iter = g_slist_next (iter);
+        }
+
+      clear_cache (icon_set);
+
+      g_free (icon_set);
+    }
+}
+
+GtkIconSet*
+gtk_icon_set_copy (GtkIconSet *icon_set)
+{
+  GtkIconSet *copy;
+  GSList *iter;
+  
+  copy = gtk_icon_set_new ();
+
+  iter = icon_set->sources;
+  while (iter != NULL)
+    {
+      copy->sources = g_slist_prepend (copy->sources,
+                                       gtk_icon_source_copy (iter->data));
+
+      iter = g_slist_next (iter);
+    }
+
+  copy->sources = g_slist_reverse (copy->sources);
+
+  copy->cache = copy_cache (icon_set);
+  copy->cache_size = icon_set->cache_size;
+
+  return copy;
+}
+
+GdkPixbuf*
+gtk_icon_set_render_icon (GtkIconSet        *icon_set,
+                          GtkStyle          *style,
+                          GtkTextDirection   direction,
+                          GtkStateType       state,
+                          const gchar       *size,
+                          GtkWidget         *widget,
+                          const char        *detail)
+{
+  GdkPixbuf *icon;
+
+  GSList *candidates = NULL;
+  GtkIconSource *source;
+  
+  /* FIXME conceivably, everywhere this function
+   * returns NULL, we should return some default
+   * dummy icon like a question mark or the image icon
+   * from netscape
+   */
+  
+  g_return_val_if_fail (icon_set != NULL, NULL);
+
+  if (icon_set->sources == NULL)
+    return NULL;
+  
+  icon = find_in_cache (icon_set, style, direction,
+                        state, size);
+
+  if (icon)
+    {
+      g_object_ref (G_OBJECT (icon));
+      add_to_cache (icon_set, style, direction, state, size, icon);
+      return icon;
+    }
+
+  /* We need to find the best icon source.  Direction matters more
+   * than state, state matters more than size.
+   */
+  candidates = direction_state_size_matches (icon_set->sources,
+                                             direction,
+                                             state,
+                                             size);
+
+
+  if (candidates == NULL)
+    return NULL; /* No sources were found. */
+  
+  /* If multiple candidates were returned, it basically means the
+   * RC file contained stupidness. We just pick one at random.
+   */
+  source = candidates->data;
+  g_slist_free (candidates);
+
+  if (source->pixbuf == NULL)
+    {
+      if (source->filename == NULL)
+        {
+          g_warning ("Useless GtkIconSource contains NULL filename and pixbuf");
+          return NULL;
+        }
+
+      source->pixbuf = gdk_pixbuf_new_from_file (source->filename);
+
+      /* This needs to fail silently and do something sane.
+       * A good argument for returning a default icon.
+       */
+      if (source->pixbuf == NULL)
+        return NULL;
+    }
+
+  g_assert (source->pixbuf != NULL);
+  
+  if (style)
+    {
+      icon = gtk_style_render_icon (style,
+                                    source,
+                                    direction,
+                                    state,
+                                    size,
+                                    widget,
+                                    detail);
+    }
+  else
+    {
+      /* Internal function used here and in the default theme. */
+      icon = _gtk_default_render_icon (style,
+                                       source,
+                                       direction,
+                                       state,
+                                       size,
+                                       widget,
+                                       detail);
+    }
+
+  g_assert (icon != NULL);
+
+  add_to_cache (icon_set, style, direction, state, size, icon);
+  
+  return icon;
+}
+
+void
+gtk_icon_set_add_source (GtkIconSet *icon_set,
+                         const GtkIconSource *source)
+{
+  g_return_if_fail (icon_set != NULL);
+  g_return_if_fail (source != NULL);
+  
+  icon_set->sources = g_slist_prepend (icon_set->sources,
+                                       gtk_icon_source_copy (source));
+}
+
+GtkIconSource *
+gtk_icon_source_copy (const GtkIconSource *source)
+{
+  GtkIconSource *copy;
+  
+  g_return_val_if_fail (source != NULL, NULL);
+
+  copy = g_new (GtkIconSource, 1);
+
+  *copy = *source;
+  
+  copy->filename = g_strdup (source->filename);
+  copy->size = g_strdup (source->size);
+  if (copy->pixbuf)
+    g_object_ref (G_OBJECT (copy->pixbuf));
+
+  return copy;
+}
+
+void
+gtk_icon_source_free (GtkIconSource *source)
+{
+  g_return_if_fail (source != NULL);
+
+  g_free ((char*) source->filename);
+  g_free ((char*) source->size);
+  if (source->pixbuf)
+    g_object_unref (G_OBJECT (source->pixbuf));
+
+  g_free (source);
+}
+
+void
+gtk_icon_set_clear (GtkIconSet *icon_set)
+{
+  GSList *iter;
+
+  g_return_if_fail (icon_set != NULL);
+  
+  iter = icon_set->sources;
+  while (iter != NULL)
+    {
+      gtk_icon_source_free (iter->data);
+
+      iter = g_slist_next (iter);
+    }
+
+  clear_cache (icon_set);
+}
+
+/* Note that the logical maximum is 20 per GtkTextDirection, so we could
+ * eventually set this to >20 to never throw anything out.
+ */
+#define NUM_CACHED_ICONS 8
+
+typedef struct _CachedIcon CachedIcon;
+
+struct _CachedIcon
+{
+  /* These must all match to use the cached pixbuf.
+   * If any don't match, we must re-render the pixbuf.
+   */
+  GtkStyle *style;
+  GtkTextDirection direction;
+  GtkStateType state;
+  gchar *size;
+
+  GdkPixbuf *pixbuf;
+};
+
+static GdkPixbuf *
+find_in_cache (GtkIconSet      *icon_set,
+               GtkStyle        *style,
+               GtkTextDirection   direction,
+               GtkStateType     state,
+               const gchar     *size)
+{
+  GSList *iter;
+
+  iter = icon_set->cache;
+  while (iter != NULL)
+    {
+      CachedIcon *icon = iter->data;
+
+      if (icon->style == style &&
+          icon->direction == direction &&
+          icon->state == state &&
+          strcmp (icon->size, size) == 0)
+        return icon->pixbuf;
+
+      iter = g_slist_next (iter);
+    }
+
+  return NULL;
+}
+
+static void
+add_to_cache (GtkIconSet      *icon_set,
+              GtkStyle        *style,
+              GtkTextDirection   direction,
+              GtkStateType     state,
+              const gchar     *size,
+              GdkPixbuf       *pixbuf)
+{
+  CachedIcon *icon;
+
+  g_object_ref (G_OBJECT (pixbuf));
+
+  /* We have to ref the style, since if the style was finalized
+   * its address could be reused by another style, creating a
+   * really weird bug
+   */
+  
+  if (style)
+    g_object_ref (G_OBJECT (style));
+  
+
+  icon = g_new (CachedIcon, 1);
+  icon_set->cache = g_slist_prepend (icon_set->cache, icon);
+
+  icon->style = style;
+  icon->direction = direction;
+  icon->state = state;
+  icon->size = g_strdup (size);
+  icon->pixbuf = pixbuf;
+
+  if (icon_set->cache_size >= NUM_CACHED_ICONS)
+    {
+      /* Remove oldest item in the cache */
+      
+      GSList *iter;
+      
+      iter = icon_set->cache;
+
+      /* Find next-to-last link */
+      g_assert (NUM_CACHED_ICONS > 2);
+      while (iter->next->next)
+        iter = iter->next;
+
+      g_assert (iter != NULL);
+      g_assert (iter->next != NULL);
+      g_assert (iter->next->next == NULL);
+
+      icon = iter->next->data;
+
+      g_slist_free (iter->next);
+      iter->next = NULL;
+
+      g_free (icon->size);
+      g_object_unref (G_OBJECT (icon->pixbuf));
+      if (icon->style)
+        g_object_unref (G_OBJECT (icon->style));
+    }
+}
+
+static void
+clear_cache (GtkIconSet *icon_set)
+{
+  GSList *iter;
+
+  iter = icon_set->cache;
+  while (iter != NULL)
+    {
+      CachedIcon *icon = iter->data;
+
+      g_free (icon->size);
+      g_object_unref (G_OBJECT (icon->pixbuf));
+      if (icon->style)
+        g_object_unref (G_OBJECT (icon->style));
+
+      g_free (icon);
+      
+      iter = g_slist_next (iter);
+    }
+
+  g_slist_free (icon_set->cache);
+  icon_set->cache = NULL;
+  icon_set->cache_size = 0;
+}
+
+static GSList*
+copy_cache (GtkIconSet *icon_set)
+{
+  GSList *iter;
+  GSList *copy = NULL;
+  
+  iter = icon_set->cache;
+  while (iter != NULL)
+    {
+      CachedIcon *icon = iter->data;
+      CachedIcon *icon_copy = g_new (CachedIcon, 1);
+
+      *icon_copy = *icon;
+
+      if (icon_copy->style)
+        g_object_ref (G_OBJECT (icon_copy->style));
+      g_object_ref (G_OBJECT (icon_copy->pixbuf));
+
+      icon_copy->size = g_strdup (icon->size);
+      
+      copy = g_slist_prepend (copy, icon_copy);      
+      
+      iter = g_slist_next (iter);
+    }
+
+  return g_slist_reverse (copy);
+}
+
+static GSList*
+direction_state_size_matches (GSList *sources,
+                              GtkTextDirection direction,
+                              GtkStateType state,
+                              const gchar *size)
+{
+  GSList *direction_matches = NULL;
+  GSList *direction_wild = NULL;
+  GSList *candidates;
+  GSList *iter;
+  
+  iter = sources;
+  while (iter != NULL)
+    {
+      GtkIconSource *source = iter->data;
+
+      if (!source->any_direction &&
+          source->direction == direction)
+        direction_matches = g_slist_prepend (direction_matches, source);
+      else if (source->any_direction)
+        direction_wild = g_slist_prepend (direction_wild, source);
+      
+      iter = g_slist_next (iter);
+    }
+
+  /* First look for a matching source among exact direction matches,
+   * then look for a matching source among wildcard direction sources
+   */
+  candidates = state_size_matches (direction_matches, state, size);
+  if (candidates == NULL)
+    candidates = state_size_matches (direction_wild, state, size);
+
+  g_slist_free (direction_wild);
+  g_slist_free (direction_matches);
+
+  return candidates;
+}
+
+
+static GSList*
+state_size_matches (GSList *sources,
+                    GtkStateType state,
+                    const gchar *size)
+{
+  GSList *state_matches = NULL;
+  GSList *state_wild = NULL;
+  GSList *candidates;
+  GSList *iter;
+  
+  iter = sources;
+  while (iter != NULL)
+    {
+      GtkIconSource *source = iter->data;
+
+      if (!source->any_state &&
+          source->state == state)
+        state_matches = g_slist_prepend (state_matches, source);
+      else if (source->any_state)
+        state_wild = g_slist_prepend (state_wild, source);
+      
+      iter = g_slist_next (iter);
+    }
+
+  /* First look for a matching source among exact state matches,
+   * then look for a matching source among wildcard state sources
+   */
+  candidates = size_matches (state_matches, size);
+  if (candidates == NULL)
+    candidates = size_matches (state_wild, size);
+
+  g_slist_free (state_wild);
+  g_slist_free (state_matches);
+
+  return candidates;
+}
+
+static gboolean
+sizes_equivalent (const gchar *rhs, const gchar *lhs)
+{
+  gint r_w, r_h, l_w, l_h;
+
+  gtk_parse_icon_size (rhs, &r_w, &r_h);
+  gtk_parse_icon_size (lhs, &l_w, &l_h);
+
+  return r_w == l_w && r_h == l_h;
+}
+
+static GSList*
+size_matches (GSList *sources,
+              const gchar *size)
+{
+  GSList *size_matches = NULL;
+  GSList *size_wild = NULL;
+  GSList *iter;
+  
+  iter = sources;
+  while (iter != NULL)
+    {
+      GtkIconSource *source = iter->data;
+
+      if (!source->any_size &&
+          sizes_equivalent (source->size, size))
+        size_matches = g_slist_prepend (size_matches, source);
+      else if (source->any_size)
+        size_wild = g_slist_prepend (size_wild, source);
+      
+      iter = g_slist_next (iter);
+    }
+
+  /* Prefer exact size matches, return wildcard sizes otherwise. */
+  if (size_matches)
+    {
+      g_slist_free (size_wild);
+      return size_matches;
+    }
+  else
+    {
+      return size_wild;
+    }
+}
+
Index: gtk/gtkiconfactory.h
===================================================================
RCS file: gtkiconfactory.h
diff -N gtkiconfactory.h
--- /dev/null	Tue May  5 16:32:27 1998
+++ gtkiconfactory.h	Fri Jul 28 14:48:24 2000
@@ -0,0 +1,159 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  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/. 
+ */
+
+#ifndef __GTK_ICON_FACTORY_H__
+#define __GTK_ICON_FACTORY_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkrc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _GtkIconFactoryClass GtkIconFactoryClass;
+
+#define GTK_TYPE_ICON_FACTORY              (gtk_icon_factory_get_type ())
+#define GTK_ICON_FACTORY(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_ICON_FACTORY, GtkIconFactory))
+#define GTK_ICON_FACTORY_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_ICON_FACTORY, GtkIconFactoryClass))
+#define GTK_IS_ICON_FACTORY(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_ICON_FACTORY))
+#define GTK_IS_ICON_FACTORY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ICON_FACTORY))
+#define GTK_ICON_FACTORY_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ICON_FACTORY, GtkIconFactoryClass))
+
+struct _GtkIconFactory
+{
+  GObject parent_instance;
+
+  GHashTable *icons;
+};
+
+struct _GtkIconFactoryClass
+{
+  GObjectClass parent_class;
+
+  
+};
+
+GType           gtk_icon_factory_get_type (void);
+GtkIconFactory* gtk_icon_factory_new      (void);
+void            gtk_icon_factory_add      (GtkIconFactory *factory,
+                                           const gchar    *stock_id,
+                                           GtkIconSet     *icon_set);
+GtkIconSet *    gtk_icon_factory_lookup   (GtkIconFactory *factory,
+                                           const gchar    *stock_id);
+
+/* Manage the default icon factory stack */
+
+void        gtk_add_default_icon_factory     (GtkIconFactory  *factory);
+void        gtk_remove_default_icon_factory  (GtkIconFactory  *factory);
+GtkIconSet *gtk_default_icon_lookup          (const gchar     *stock_id);
+
+/* Get preferred real size from semantic size or size string
+ * ("10x10").  Note that themes SHOULD use this size, but they aren't
+ * required to; for size requests and such, you should get the actual
+ * pixbuf from the icon set and see what size was rendered.
+ *
+ * This function is intended for people who are scaling icons,
+ * rather than for people who are displaying already-scaled icons.
+ * That is, if you are displaying an icon, you should get the
+ * size from the rendered pixbuf, not from here.
+ */
+gboolean   gtk_parse_icon_size              (const gchar     *alias,
+                                             gint            *width,
+                                             gint            *height);
+
+void       gtk_register_icon_size           (const gchar     *alias,
+                                             const gchar     *value);
+
+/* Standard sizes */
+
+#define GTK_ICON_MENU          "gtk-menu"
+#define GTK_ICON_SMALL_TOOLBAR "gtk-small-toolbar"
+#define GTK_ICON_LARGE_TOOLBAR "gtk-large-toolbar"
+#define GTK_ICON_BUTTON        "gtk-button"
+#define GTK_ICON_DIALOG        "gtk-dialog"
+
+/* Icon sets */
+
+GtkIconSet* gtk_icon_set_new             (void);
+
+GtkIconSet* gtk_icon_set_ref             (GtkIconSet      *icon_set);
+void        gtk_icon_set_unref           (GtkIconSet      *icon_set);
+GtkIconSet* gtk_icon_set_copy            (GtkIconSet      *icon_set);
+
+/* Get one of the icon variants in the set, creating the variant if
+ * necessary.
+ */
+GdkPixbuf*  gtk_icon_set_render_icon     (GtkIconSet      *icon_set,
+                                          GtkStyle        *style,
+                                          GtkTextDirection direction,
+                                          GtkStateType     state,
+                                          const gchar     *size,
+                                          GtkWidget       *widget,
+                                          const char      *detail);
+
+
+void           gtk_icon_set_add_source (GtkIconSet          *icon_set,
+                                        const GtkIconSource *source);
+
+
+/* Clear icon set contents, drop references to all contained
+ * GdkPixbuf objects and forget all GtkIconSources. Used to
+ * recycle an icon set.
+ */
+void           gtk_icon_set_clear      (GtkIconSet          *icon_set);
+
+
+struct _GtkIconSource
+{
+  /* Either filename or pixbuf can be NULL. If both are non-NULL,
+   * the filename gets ignored.
+   */
+  const char *filename;
+  GdkPixbuf *pixbuf;
+
+  GtkTextDirection direction;
+  GtkStateType state;
+  const gchar *size;
+
+  /* If TRUE, then the parameter is wildcarded, and the above
+   * fields should be ignored. If FALSE, the parameter is
+   * specified, and the above fields should be valid.
+   */
+  guint any_direction : 1;
+  guint any_state : 1;
+  guint any_size : 1;
+};
+
+
+GtkIconSource *gtk_icon_source_copy    (const GtkIconSource *source);
+void           gtk_icon_source_free    (GtkIconSource       *source);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_ICON_FACTORY_H__ */
Index: gtk/gtkrc.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkrc.c,v
retrieving revision 1.62
diff -u -u -r1.62 gtkrc.c
--- gtk/gtkrc.c	2000/07/26 11:32:45	1.62
+++ gtk/gtkrc.c	2000/07/28 18:48:24
@@ -60,6 +60,7 @@
 #include "gtkbindings.h"
 #include "gtkthemes.h"
 #include "gtkintl.h"
+#include "gtkiconfactory.h"
 
 typedef struct _GtkRcSet    GtkRcSet;
 typedef struct _GtkRcNode   GtkRcNode;
@@ -127,6 +128,9 @@
 static guint       gtk_rc_parse_module_path          (GScanner        *scanner);
 static void        gtk_rc_parse_module_path_string   (gchar           *mod_path);
 static guint       gtk_rc_parse_path_pattern         (GScanner        *scanner);
+static guint       gtk_rc_parse_stock                (GScanner        *scanner,
+                                                      GtkRcStyle      *rc_style,
+                                                      GtkIconFactory  *factory);
 static void        gtk_rc_clear_hash_node            (gpointer         key,
                                                       gpointer         data,
                                                       gpointer         user_data);
@@ -220,6 +224,9 @@
   { "highest", GTK_RC_TOKEN_HIGHEST },
   { "engine", GTK_RC_TOKEN_ENGINE },
   { "module_path", GTK_RC_TOKEN_MODULE_PATH },
+  { "stock", GTK_RC_TOKEN_STOCK },
+  { "LTR", GTK_RC_TOKEN_LTR },
+  { "RTL", GTK_RC_TOKEN_RTL }
 };
 
 static const guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);
@@ -858,6 +865,14 @@
     }
   g_slist_free (rc_style->rc_style_lists);
 
+  tmp_list1 = rc_style->icon_factories;
+  while (tmp_list1)
+    {
+      g_object_unref (G_OBJECT (tmp_list1->data));
+
+      tmp_list1 = tmp_list1->next;
+    }
+  
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -1341,10 +1356,11 @@
   style = GTK_RC_STYLE_GET_CLASS (rc_style)->create_style (rc_style);
 
   style->rc_style = rc_style;
+
   gtk_rc_style_ref (rc_style);
-  
-  GTK_STYLE_GET_CLASS (style)->init_from_rc (style, rc_style);
   
+  GTK_STYLE_GET_CLASS (style)->init_from_rc (style, rc_style);  
+
   return style;
 }
 
@@ -1380,13 +1396,13 @@
       while (tmp_styles)
 	{
 	  GtkRcStyle *rc_style = tmp_styles->data;
-
+          
 	  if (G_OBJECT_TYPE (rc_style) != rc_style_type)
 	    {
 	      base_style = rc_style;
 	      break;
 	    }
-
+          
 	  tmp_styles = tmp_styles->next;
 	}
       
@@ -1397,13 +1413,31 @@
       while (tmp_styles)
 	{
 	  GtkRcStyle *rc_style = tmp_styles->data;
-	  
-	  proto_style_class->merge (proto_style, rc_style);
-	  
+          GSList *factories;
+          
+	  proto_style_class->merge (proto_style, rc_style);	  
+          
 	  /* Point from each rc_style to the list of styles */
 	  if (!g_slist_find (rc_style->rc_style_lists, rc_styles))
 	    rc_style->rc_style_lists = g_slist_prepend (rc_style->rc_style_lists, rc_styles);
-	  
+
+          factories = g_slist_copy (rc_style->icon_factories);
+          if (factories)
+            {
+              GSList *iter;
+              
+              iter = factories;
+              while (iter != NULL)
+                {
+                  g_object_ref (G_OBJECT (iter->data));
+                  iter = g_slist_next (iter);
+                }
+
+              proto_style->icon_factories = g_slist_concat (proto_style->icon_factories,
+                                                            factories);
+
+            }
+          
 	  tmp_styles = tmp_styles->next;
 	}
 
@@ -1486,6 +1520,7 @@
   guint token;
   gint insert;
   gint i;
+  GtkIconFactory *our_factory = NULL;
   
   token = g_scanner_get_next_token (scanner);
   if (token != GTK_RC_TOKEN_STYLE)
@@ -1528,6 +1563,8 @@
       parent_style = gtk_rc_style_find (scanner->value.v_string);
       if (parent_style)
 	{
+          GSList *factories;
+          
 	  for (i = 0; i < 5; i++)
 	    {
 	      rc_style->color_flags[i] = parent_style->color_flags[i];
@@ -1553,6 +1590,14 @@
 		g_free (rc_style->bg_pixmap_name[i]);
 	      rc_style->bg_pixmap_name[i] = g_strdup (parent_style->bg_pixmap_name[i]);
 	    }
+
+          g_assert (rc_style->icon_factories == NULL);
+          factories = rc_style->icon_factories = g_slist_copy (parent_style->icon_factories);
+          while (factories != NULL)
+            {
+              g_object_ref (G_OBJECT (factories->data));
+              factories = factories->next;
+            }
 	}
     }
   
@@ -1603,6 +1648,15 @@
 	case GTK_RC_TOKEN_ENGINE:
 	  token = gtk_rc_parse_engine (scanner, &rc_style);
 	  break;
+        case GTK_RC_TOKEN_STOCK:
+          if (our_factory == NULL)
+            {
+              our_factory = gtk_icon_factory_new ();
+              rc_style->icon_factories = g_slist_prepend (rc_style->icon_factories,
+                                                          our_factory);
+            }
+          token = gtk_rc_parse_stock (scanner, rc_style, our_factory);
+          break;
 	default:
 	  g_scanner_get_next_token (scanner);
 	  token = G_TOKEN_RIGHT_CURLY;
@@ -2477,6 +2531,238 @@
     }
 
   g_free (pattern);
+  return G_TOKEN_NONE;
+}
+
+static guint
+gtk_rc_parse_stock_id (GScanner	 *scanner,
+                       gchar    **stock_id)
+{
+  guint token;
+  
+  token = g_scanner_get_next_token (scanner);
+  if (token != G_TOKEN_LEFT_BRACE)
+    return G_TOKEN_LEFT_BRACE;
+
+  token = g_scanner_get_next_token (scanner);
+  
+  if (token != G_TOKEN_STRING)
+    return G_TOKEN_STRING;
+  
+  *stock_id = g_strdup (scanner->value.v_string);
+  
+  token = g_scanner_get_next_token (scanner);
+  if (token != G_TOKEN_RIGHT_BRACE)
+    {
+      g_free (*stock_id);
+      return G_TOKEN_RIGHT_BRACE;
+    }
+  
+  return G_TOKEN_NONE;
+}
+
+static guint
+gtk_rc_parse_icon_source (GScanner	 *scanner,
+                          GtkIconSet     *icon_set)
+{
+  guint token;
+  GtkIconSource source = { NULL, NULL,
+                           0, 0, 0,
+                           TRUE, TRUE, TRUE };
+  
+  token = g_scanner_get_next_token (scanner);
+  if (token != G_TOKEN_LEFT_CURLY)
+    return G_TOKEN_LEFT_CURLY;
+
+  token = g_scanner_get_next_token (scanner);
+  
+  if (token != G_TOKEN_STRING)
+    return G_TOKEN_STRING;
+  
+  source.filename = g_strdup (scanner->value.v_string);
+  
+  token = g_scanner_get_next_token (scanner);
+
+  if (token != G_TOKEN_COMMA)
+    {
+      g_free ((gchar*)source.filename);
+      return G_TOKEN_COMMA;
+    }
+
+  /* Get the direction */
+  
+  token = g_scanner_get_next_token (scanner);
+
+  switch (token)
+    {
+    case GTK_RC_TOKEN_RTL:
+      source.any_direction = FALSE;
+      source.direction = GTK_TEXT_DIR_RTL;
+      break;
+
+    case GTK_RC_TOKEN_LTR:
+      source.any_direction = FALSE;
+      source.direction = GTK_TEXT_DIR_LTR;
+      break;
+      
+    case '*':
+      break;
+      
+    default:
+      g_free ((gchar*)source.filename);
+      return GTK_RC_TOKEN_RTL;
+      break;
+    }
+
+  token = g_scanner_get_next_token (scanner);
+
+  if (token != G_TOKEN_COMMA)
+    {
+      g_free ((gchar*)source.filename);
+      return G_TOKEN_COMMA;
+    }
+
+  /* Get the state */
+  
+  token = g_scanner_get_next_token (scanner);
+  
+  switch (token)
+    {
+    case GTK_RC_TOKEN_NORMAL:
+      source.any_state = FALSE;
+      source.state = GTK_STATE_NORMAL;
+      break;
+
+    case GTK_RC_TOKEN_PRELIGHT:
+      source.any_state = FALSE;
+      source.state = GTK_STATE_PRELIGHT;
+      break;
+      
+
+    case GTK_RC_TOKEN_INSENSITIVE:
+      source.any_state = FALSE;
+      source.state = GTK_STATE_INSENSITIVE;
+      break;
+
+    case GTK_RC_TOKEN_ACTIVE:
+      source.any_state = FALSE;
+      source.state = GTK_STATE_ACTIVE;
+      break;
+
+    case GTK_RC_TOKEN_SELECTED:
+      source.any_state = FALSE;
+      source.state = GTK_STATE_SELECTED;
+      break;
+
+    case '*':
+      break;
+      
+    default:
+      g_free ((gchar*)source.filename);
+      return GTK_RC_TOKEN_PRELIGHT;
+      break;
+    }  
+
+  token = g_scanner_get_next_token (scanner);
+  
+  if (token != G_TOKEN_COMMA)
+    {
+      g_free ((gchar*)source.filename);
+      return G_TOKEN_COMMA;
+    }
+  
+  /* Get the size */
+  
+  token = g_scanner_get_next_token (scanner);
+
+  if (token != '*')
+    {
+      if (token != G_TOKEN_STRING)
+        return G_TOKEN_STRING;
+      
+      source.size = g_strdup (scanner->value.v_string);
+      source.any_size = FALSE;
+    }
+
+  /* Check the close brace */
+  
+  token = g_scanner_get_next_token (scanner);
+  if (token != G_TOKEN_RIGHT_CURLY)
+    {
+      g_free ((gchar*)source.filename);
+      return G_TOKEN_RIGHT_CURLY;
+    }
+
+  gtk_icon_set_add_source (icon_set, &source);
+  
+  return G_TOKEN_NONE;
+}
+
+static guint
+gtk_rc_parse_stock (GScanner       *scanner,
+                    GtkRcStyle     *rc_style,
+                    GtkIconFactory *factory)
+{
+  GtkIconSet *icon_set = NULL;
+  gchar *stock_id = NULL;
+  guint token;
+  
+  token = g_scanner_get_next_token (scanner);
+  if (token != GTK_RC_TOKEN_STOCK)
+    return GTK_RC_TOKEN_STOCK;
+  
+  token = gtk_rc_parse_stock_id (scanner, &stock_id);
+  if (token != G_TOKEN_NONE)
+    return token;
+  
+  token = g_scanner_get_next_token (scanner);
+  if (token != G_TOKEN_EQUAL_SIGN)
+    {
+      g_free (stock_id);
+      return G_TOKEN_EQUAL_SIGN;
+    }
+
+  token = g_scanner_get_next_token (scanner);
+  if (token != G_TOKEN_LEFT_CURLY)
+    {
+      g_free (stock_id);
+      return G_TOKEN_LEFT_CURLY;
+    }
+
+  token = g_scanner_peek_next_token (scanner);
+  while (token != G_TOKEN_RIGHT_CURLY)
+    {
+      if (icon_set == NULL)
+        icon_set = gtk_icon_set_new ();
+      
+      token = gtk_rc_parse_icon_source (scanner, icon_set);
+      if (token != G_TOKEN_NONE)
+        {
+          g_free (stock_id);
+          gtk_icon_set_unref (icon_set);
+          return token;
+        }
+
+      token = g_scanner_get_next_token (scanner);
+      
+      if (token != G_TOKEN_COMMA &&
+          token != G_TOKEN_RIGHT_CURLY)
+        {
+          g_free (stock_id);
+          gtk_icon_set_unref (icon_set);
+          return G_TOKEN_RIGHT_CURLY;
+        }
+    }
+
+  g_assert (token == G_TOKEN_RIGHT_CURLY);
+  
+  gtk_icon_factory_add (factory,
+                        stock_id,
+                        icon_set);
+
+  gtk_icon_set_unref (icon_set);
+  g_free (stock_id);
+
   return G_TOKEN_NONE;
 }
 
Index: gtk/gtkrc.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkrc.h,v
retrieving revision 1.23
diff -u -u -r1.23 gtkrc.h
--- gtk/gtkrc.h	2000/07/26 11:32:45	1.23
+++ gtk/gtkrc.h	2000/07/28 18:48:24
@@ -35,6 +35,9 @@
 extern "C" {
 #endif /* __cplusplus */
 
+/* Forward declaration */
+typedef struct _GtkIconFactory GtkIconFactory;
+
 #define GTK_TYPE_RC_STYLE              (gtk_rc_style_get_type ())
 #define GTK_RC_STYLE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_RC_STYLE, GtkRcStyle))
 #define GTK_RC_STYLE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RC_STYLE, GtkRcStyleClass))
@@ -70,11 +73,13 @@
 
   gint xthickness;
   gint ythickness;
-
+  
   /*< private >*/
   
   /* list of RC style lists including this RC style */
   GSList *rc_style_lists;
+
+  GSList *icon_factories;
 };
 
 struct _GtkRcStyleClass
@@ -176,6 +181,9 @@
   GTK_RC_TOKEN_HIGHEST,
   GTK_RC_TOKEN_ENGINE,
   GTK_RC_TOKEN_MODULE_PATH,
+  GTK_RC_TOKEN_STOCK,
+  GTK_RC_TOKEN_LTR,
+  GTK_RC_TOKEN_RTL,
   GTK_RC_TOKEN_LAST
 } GtkRcTokenType;
 
Index: gtk/gtkstyle.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstyle.c,v
retrieving revision 1.42
diff -u -u -r1.42 gtkstyle.c
--- gtk/gtkstyle.c	2000/07/26 11:32:50	1.42
+++ gtk/gtkstyle.c	2000/07/28 18:48:24
@@ -31,8 +31,8 @@
 #include "gtkstyle.h"
 #include "gtkwidget.h"
 #include "gtkthemes.h"
+#include "gtkiconfactory.h"
 
-
 #define LIGHTNESS_MULT  1.3
 #define DARKNESS_MULT   0.7
 
@@ -445,7 +445,8 @@
   klass->realize = gtk_style_real_realize;
   klass->unrealize = gtk_style_real_unrealize;
   klass->set_background = gtk_style_real_set_background;
-  
+  klass->render_icon = _gtk_default_render_icon;
+
   klass->draw_hline = gtk_default_draw_hline;
   klass->draw_vline = gtk_default_draw_vline;
   klass->draw_shadow = gtk_default_draw_shadow;
@@ -667,6 +668,28 @@
   GTK_STYLE_GET_CLASS (style)->realize (style);
 }
 
+GtkIconSet*
+gtk_style_lookup_icon_set (GtkStyle   *style,
+                           const char *stock_id)
+{
+  GSList *iter;
+
+  iter = style->icon_factories;
+  while (iter != NULL)
+    {
+      GtkIconSet *icon_set =
+        gtk_icon_factory_lookup (GTK_ICON_FACTORY (iter->data),
+                                 stock_id);
+
+      if (icon_set)
+        return icon_set;
+      
+      iter = g_slist_next (iter);
+    }
+
+  return gtk_default_icon_lookup (stock_id);
+}
+
 void
 gtk_draw_hline (GtkStyle     *style,
                 GdkWindow    *window,
@@ -1103,6 +1126,22 @@
     style->xthickness = rc_style->xthickness;
   if (rc_style->ythickness >= 0)
     style->ythickness = rc_style->ythickness;
+
+  
+  if (rc_style->icon_factories)
+    {
+      GSList *iter;
+
+      style->icon_factories = g_slist_copy (rc_style->icon_factories);
+      
+      iter = style->icon_factories;
+      while (iter != NULL)
+        {
+          g_object_ref (G_OBJECT (iter->data));
+          
+          iter = g_slist_next (iter);
+        }
+    }
 }
 
 static void
@@ -1241,6 +1280,29 @@
     gdk_window_set_background (window, &style->bg[state_type]);
 }
 
+GdkPixbuf *
+gtk_style_render_icon (GtkStyle            *style,
+                       const GtkIconSource *source,
+                       GtkTextDirection     direction,
+                       GtkStateType         state,
+                       const gchar         *size,
+                       GtkWidget           *widget,
+                       const gchar         *detail)
+{
+  GdkPixbuf *pixbuf;
+  
+  g_return_val_if_fail (style != NULL, NULL);
+  g_return_val_if_fail (GTK_STYLE_GET_CLASS (style)->render_icon != NULL, NULL);
+  
+  pixbuf = GTK_STYLE_GET_CLASS (style)->render_icon (style, source, direction, state,
+                                                     size, widget, detail);
+
+  g_return_val_if_fail (pixbuf != NULL, NULL);
+
+  return pixbuf;
+}
+
+/* Default functions */
 void
 gtk_style_apply_default_background (GtkStyle     *style,
                                     GdkWindow    *window,
@@ -1303,6 +1365,161 @@
                              new_rect.x, new_rect.y, 
                              new_rect.width, new_rect.height);
     }
+}
+
+#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
+
+static void
+gdk_pixbuf_saturate_and_pixelate(const GdkPixbuf *src,
+                                 GdkPixbuf *dest,
+                                 gfloat saturation,
+                                 gboolean pixelate)
+{
+  /* NOTE that src and dest MAY be the same pixbuf! */
+  
+  g_return_if_fail (src != NULL);
+  g_return_if_fail (dest != NULL);
+  g_return_if_fail (gdk_pixbuf_get_height (src) == gdk_pixbuf_get_height (dest));
+  g_return_if_fail (gdk_pixbuf_get_width (src) == gdk_pixbuf_get_width (dest));
+  g_return_if_fail (gdk_pixbuf_get_rowstride (src) == gdk_pixbuf_get_rowstride (dest));
+  g_return_if_fail (gdk_pixbuf_get_colorspace (src) == gdk_pixbuf_get_colorspace (dest));
+  
+  if (saturation == 1.0 && !pixelate)
+    {
+      if (dest != src)
+        memcpy (gdk_pixbuf_get_pixels (dest),
+                gdk_pixbuf_get_pixels (src),
+                gdk_pixbuf_get_height (src) * gdk_pixbuf_get_rowstride (src));
+
+      return;
+    }
+  else
+    {
+      gint i, j;
+      gint width, height, has_alpha, rowstride;
+      guchar *target_pixels;
+      guchar *original_pixels;
+      guchar *current_pixel;
+      guchar intensity;
+
+      has_alpha = gdk_pixbuf_get_has_alpha (src);
+      width = gdk_pixbuf_get_width (src);
+      height = gdk_pixbuf_get_height (src);
+      rowstride = gdk_pixbuf_get_rowstride (src);
+                
+      target_pixels = gdk_pixbuf_get_pixels (dest);
+      original_pixels = gdk_pixbuf_get_pixels (src);
+
+      for (i = 0; i < height; i++)
+        {
+          for (j = 0; j < width; j++)
+            {
+              current_pixel = original_pixels + i*rowstride + j*(has_alpha?4:3);
+              intensity = INTENSITY (*(current_pixel), *(current_pixel + 1), *(current_pixel + 2));
+              if (pixelate && (i+j)%2 == 0)
+                {
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3)) = intensity/2 + 127;
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3) + 1) = intensity/2 + 127;
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3) + 2) = intensity/2 + 127;
+                }
+              else if (pixelate)
+                {
+#define DARK_FACTOR 0.7
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3)) =
+                    (guchar) (((1.0 - saturation) * intensity
+                               + saturation * (*(current_pixel)))) * DARK_FACTOR;
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3) + 1) =
+                    (guchar) (((1.0 - saturation) * intensity
+                               + saturation * (*(current_pixel + 1)))) * DARK_FACTOR;
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3) + 2) =
+                    (guchar) (((1.0 - saturation) * intensity
+                               + saturation * (*(current_pixel + 2)))) * DARK_FACTOR;
+                }
+              else
+                {
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3)) =
+                    (guchar) ((1.0 - saturation) * intensity
+                              + saturation * (*(current_pixel)));
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3) + 1) =
+                    (guchar) ((1.0 - saturation) * intensity
+                              + saturation * (*(current_pixel + 1)));
+                  *(target_pixels + i*rowstride + j*(has_alpha?4:3) + 2) =
+                    (guchar) ((1.0 - saturation) * intensity
+                              + saturation * (*(current_pixel + 2)));
+                }
+              
+              if (has_alpha)
+                *(target_pixels + i*rowstride + j*(has_alpha?4:3) + 3) = *(current_pixel + 3);
+            }
+        }
+
+      return;
+    }
+}
+
+static GdkPixbuf*
+scale_or_ref (GdkPixbuf *src,
+              gint width,
+              gint height)
+{
+  if (width == gdk_pixbuf_get_width (src) &&
+      height == gdk_pixbuf_get_height (src))
+    {
+      gdk_pixbuf_ref (src);
+      return src;
+    }
+  else
+    {
+      return gdk_pixbuf_scale_simple (src,
+                                      width, height,
+                                      GDK_INTERP_BILINEAR);
+    }
+}
+
+GdkPixbuf *
+_gtk_default_render_icon (GtkStyle            *style,
+                          const GtkIconSource *source,
+                          GtkTextDirection     direction,
+                          GtkStateType         state,
+                          const gchar         *size,
+                          GtkWidget           *widget,
+                          const gchar         *detail)
+{
+  gint width = 1;
+  gint height = 1;
+  GdkPixbuf *scaled;
+  GdkPixbuf *stated;
+
+  /* Oddly, style can be NULL in this function, because
+   * GtkIconSet can be used without a style and if so
+   * it uses this function.
+   */
+  
+  g_return_val_if_fail (source->pixbuf != NULL, NULL);
+  
+  if (!gtk_parse_icon_size (size, &width, &height))
+    {
+      g_warning ("Bad icon size '%s' passed to render_icon", size);
+      return NULL;
+    }
+  
+  scaled = scale_or_ref (source->pixbuf, width, height);
+
+  if (state == GTK_STATE_INSENSITIVE)
+    {
+      stated = gdk_pixbuf_copy (scaled);      
+
+      gdk_pixbuf_saturate_and_pixelate (scaled, stated,
+                                        0.8, TRUE);
+
+      gdk_pixbuf_unref (scaled);
+    }
+  else
+    {
+      stated = scaled;
+    }
+  
+  return stated;
 }
 
 static void
Index: gtk/gtkstyle.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkstyle.h,v
retrieving revision 1.17
diff -u -u -r1.17 gtkstyle.h
--- gtk/gtkstyle.h	2000/07/26 11:32:52	1.17
+++ gtk/gtkstyle.h	2000/07/28 18:48:24
@@ -51,8 +51,9 @@
  */
 typedef struct _GtkThemeEngine GtkThemeEngine;
 typedef struct _GtkRcStyle     GtkRcStyle;
+typedef struct _GtkIconSet     GtkIconSet;
+typedef struct _GtkIconSource  GtkIconSource;
 
-
 /* We make this forward declaration here, since we pass
  * GtkWidgt's to the draw functions.
  */
@@ -110,6 +111,8 @@
 				 * was created
 				 */
   GSList	 *styles;
+
+  GSList         *icon_factories;
 };
 
 struct _GtkStyleClass
@@ -149,8 +152,18 @@
 				 GdkWindow              *window,
 				 GtkStateType            state_type);
 
+
+  GdkPixbuf * (* render_icon)   (GtkStyle               *style,
+                                 const GtkIconSource    *source,
+                                 GtkTextDirection        direction,
+                                 GtkStateType            state,
+                                 const gchar            *size,
+                                 GtkWidget              *widget,
+                                 const gchar            *detail);
+  
   /* Drawing functions
    */
+  
   void (*draw_hline)		(GtkStyle		*style,
 				 GdkWindow		*window,
 				 GtkStateType		 state_type,
@@ -408,6 +421,15 @@
 					      gint	    width, 
 					      gint	    height);
 
+GtkIconSet* gtk_style_lookup_icon_set (GtkStyle            *style,
+                                       const gchar         *stock_id);
+GdkPixbuf * gtk_style_render_icon     (GtkStyle            *style,
+                                       const GtkIconSource *source,
+                                       GtkTextDirection     direction,
+                                       GtkStateType         state,
+                                       const gchar *        size,
+                                       GtkWidget           *widget,
+                                       const gchar         *detail);
 void gtk_draw_hline      (GtkStyle        *style,
 			  GdkWindow       *window,
 			  GtkStateType     state_type,
@@ -814,6 +836,17 @@
 			   gint             width,
 			   gint             height,
 			   GtkOrientation   orientation);
+
+/* Internal */
+
+GdkPixbuf * _gtk_default_render_icon (GtkStyle            *style,
+                                      const GtkIconSource *source,
+                                      GtkTextDirection     direction,
+                                      GtkStateType         state,
+                                      const gchar         *size,
+                                      GtkWidget           *widget,
+                                      const gchar         *detail);
+
 
 #ifdef __cplusplus
 }
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.172
diff -u -u -r1.172 gtkwidget.c
--- gtk/gtkwidget.c	2000/07/26 11:32:59	1.172
+++ gtk/gtkwidget.c	2000/07/28 18:48:24
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <locale.h>
 #include "gtkcontainer.h"
+#include "gtkiconfactory.h"
 #include "gtkmain.h"
 #include "gtkrc.h"
 #include "gtkselection.h"
@@ -3327,6 +3328,35 @@
     pango_layout_set_text (layout, text, -1);
 
   return layout;
+}
+
+GdkPixbuf*
+gtk_widget_render_icon (GtkWidget      *widget,
+                        const gchar    *stock_id,
+                        const gchar    *size,
+                        const gchar    *detail)
+{
+  GtkIconSet *icon_set;
+  GdkPixbuf *retval;
+  
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+  
+  gtk_widget_ensure_style (widget);
+  
+  icon_set = gtk_style_lookup_icon_set (widget->style, stock_id);
+
+  if (icon_set == NULL)
+    return NULL;
+
+  retval = gtk_icon_set_render_icon (icon_set,
+                                     widget->style,
+                                     gtk_widget_get_direction (widget),
+                                     GTK_WIDGET_STATE (widget),
+                                     size,
+                                     widget,
+                                     detail);
+
+  return retval;
 }
 
 /*************************************************************
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.78
diff -u -u -r1.78 gtkwidget.h
--- gtk/gtkwidget.h	2000/07/26 11:33:01	1.78
+++ gtk/gtkwidget.h	2000/07/28 18:48:24
@@ -580,6 +580,11 @@
 PangoLayout  *gtk_widget_create_pango_layout  (GtkWidget   *widget,
 					       const gchar *text);
 
+GdkPixbuf* gtk_widget_render_icon       (GtkWidget      *widget,
+                                         const gchar    *stock_id,
+                                         const gchar    *size,
+                                         const gchar    *detail);
+
 /* handle composite names for GTK_COMPOSITE_CHILD widgets,
  * the returned name is newly allocated.
  */



 




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]