stock system



Hi,

This is just the stock icon system, more or less. It has
interdependencies with the other stuff, but these are the main stock
icon files.

See ChangeLog from the appended patch, and my earlier post on the RC
file format, for details.

In particular:

+	* 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
+

Havoc

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.1358
diff -u -u -r1.1358 ChangeLog
--- ChangeLog	2000/07/26 11:32:23	1.1358
+++ ChangeLog	2000/07/26 22:00:32
@@ -1,3 +1,63 @@
+2000-07-26  Havoc Pennington  <hp@redhat.com>
+
+        * 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/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/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.
+	
 Wed Jul 26 12:59:31 2000  Tim Janik  <timj@gtk.org>
 
         * *.[hc]: applied patch from Andreas Persenius <ndap@swipnet.se> that
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/26 22:00:32
@@ -29,8 +29,10 @@
 #include "gtklabel.h"
 #include "gtkmain.h"
 #include "gtksignal.h"
+#include "gtkimage.h"
+#include "gtkhbox.h"
+#include "gtkstock.h"
 
-
 #define CHILD_SPACING     1
 #define DEFAULT_LEFT_POS  4
 #define DEFAULT_TOP_POS   4
@@ -307,6 +309,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/26 22:00:32
@@ -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	Wed Jul 26 18:00:32 2000
@@ -0,0 +1,859 @@
+/* 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 Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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-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 "gtkiconfactory.h"
+#include "stock-icons/gtkstockpixbufs.h"
+#include "gtkstock.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_push_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);
+}
+
+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,
+                            GtkIconSizeType 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,
+           GtkIconSizeType 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 */
+
+
+/* FIXME this shouldn't be hard-coded forever. Eventually
+ * it will somehow be user-configurable.
+ */
+
+static gint widths[] =
+{
+  16, /* menu */
+  24, /* button */
+  18, /* small toolbar */
+  24, /* large toolbar */
+  48  /* dialog */
+};
+
+static gint heights[] =
+{
+  16, /* menu */
+  24, /* button */
+  18, /* small toolbar */
+  24, /* large toolbar */
+  48  /* dialog */
+};
+
+void
+gtk_get_icon_size (GtkIconSizeType semantic_size,
+                   gint *width,
+                   gint *height)
+{
+  g_return_if_fail (semantic_size < G_N_ELEMENTS (widths));
+
+  if (width)
+    *width = widths[semantic_size];
+
+  if (height)
+    *height = heights[semantic_size];
+}
+
+
+/* Icon Set */
+
+static GdkPixbuf *find_in_cache (GtkIconSet      *icon_set,
+                                 GtkStyle        *style,
+                                 GtkTextDirection   direction,
+                                 GtkStateType     state,
+                                 GtkIconSizeType  size);
+static void       add_to_cache  (GtkIconSet      *icon_set,
+                                 GtkStyle        *style,
+                                 GtkTextDirection   direction,
+                                 GtkStateType     state,
+                                 GtkIconSizeType  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,
+                                             GtkIconSizeType   size);
+static GSList* state_size_matches           (GSList           *sources,
+                                             GtkStateType      state,
+                                             GtkIconSizeType   size);
+static GSList* size_matches                 (GSList           *sources,
+                                             GtkIconSizeType   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,
+                          GtkIconSizeType    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);
+  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);
+  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;
+  GtkIconSizeType size;
+
+  GdkPixbuf *pixbuf;
+};
+
+static GdkPixbuf *
+find_in_cache (GtkIconSet      *icon_set,
+               GtkStyle        *style,
+               GtkTextDirection   direction,
+               GtkStateType     state,
+               GtkIconSizeType  size)
+{
+  GSList *iter;
+
+  iter = icon_set->cache;
+  while (iter != NULL)
+    {
+      CachedIcon *icon = iter->data;
+
+      if (icon->style == style &&
+          icon->direction == direction &&
+          icon->state == state &&
+          icon->size == size)
+        return icon->pixbuf;
+
+      iter = g_slist_next (iter);
+    }
+
+  return NULL;
+}
+
+static void
+add_to_cache (GtkIconSet      *icon_set,
+              GtkStyle        *style,
+              GtkTextDirection   direction,
+              GtkStateType     state,
+              GtkIconSizeType  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 = 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_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_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));
+          
+      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,
+                              GtkIconSizeType 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,
+                    GtkIconSizeType 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 GSList*
+size_matches (GSList *sources,
+              GtkIconSizeType size)
+{
+  GSList *size_matches = NULL;
+  GSList *size_wild = NULL;
+  GSList *iter;
+  
+  iter = sources;
+  while (iter != NULL)
+    {
+      GtkIconSource *source = iter->data;
+
+      if (!source->any_size &&
+          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	Wed Jul 26 18:00:32 2000
@@ -0,0 +1,146 @@
+/* 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 Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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-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/. 
+ */
+
+#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_push_default_icon_factory (GtkIconFactory  *factory);
+GtkIconSet *gtk_default_icon_lookup       (const gchar     *stock_id);
+
+/* Get preferred real size from semantic size (eventually
+ * user-configurable).  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 it is.
+ *
+ * This function is intended for people who are scaling icons,
+ * rather than for people who are displaying already-scaled icons.
+ */
+void        gtk_get_icon_size             (GtkIconSizeType  semantic_size,
+                                           gint            *width,
+                                           gint            *height);
+
+
+/* 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,
+                                          GtkIconSizeType  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;
+  GtkIconSizeType 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/26 22:00:32
@@ -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,14 @@
   { "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 },
+  { "MENU", GTK_RC_TOKEN_MENU },
+  { "BUTTON", GTK_RC_TOKEN_BUTTON },
+  { "SMALL_TOOLBAR", GTK_RC_TOKEN_SMALL_TOOLBAR },
+  { "LARGE_TOOLBAR", GTK_RC_TOKEN_LARGE_TOOLBAR },
+  { "DIALOG", GTK_RC_TOKEN_DIALOG }
 };
 
 static const guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);
@@ -858,6 +870,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 +1361,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 +1401,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 +1418,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 +1525,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 +1568,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 +1595,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 +1653,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 +2536,263 @@
     }
 
   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);
+
+  switch (token)
+    {
+    case GTK_RC_TOKEN_MENU:
+      source.any_size = FALSE;
+      source.size = GTK_ICON_MENU;
+      break;
+
+    case GTK_RC_TOKEN_BUTTON:
+      source.any_size = FALSE;
+      source.size = GTK_ICON_BUTTON;
+      break;
+
+    case GTK_RC_TOKEN_SMALL_TOOLBAR:
+      source.any_size = FALSE;
+      source.size = GTK_ICON_SMALL_TOOLBAR;
+      break;
+
+    case GTK_RC_TOKEN_LARGE_TOOLBAR:
+      source.any_size = FALSE;
+      source.size = GTK_ICON_LARGE_TOOLBAR;
+      break;
+
+    case GTK_RC_TOKEN_DIALOG:
+      source.any_size = FALSE;
+      source.size = GTK_ICON_DIALOG;
+      break;
+
+    case '*':
+      break;
+      
+    default:
+      g_free ((gchar*)source.filename);
+      return GTK_RC_TOKEN_MENU;
+      break;
+    }
+  
+  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/26 22:00:32
@@ -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,14 @@
   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_MENU,
+  GTK_RC_TOKEN_BUTTON,
+  GTK_RC_TOKEN_SMALL_TOOLBAR,
+  GTK_RC_TOKEN_LARGE_TOOLBAR,
+  GTK_RC_TOKEN_DIALOG,
   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/26 22:00:32
@@ -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,
+                       GtkIconSizeType      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,157 @@
                              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,
+                          GtkIconSizeType      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);
+  
+  gtk_get_icon_size (size, &width, &height);
+  
+  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/26 22:00:32
@@ -51,7 +51,17 @@
  */
 typedef struct _GtkThemeEngine GtkThemeEngine;
 typedef struct _GtkRcStyle     GtkRcStyle;
+typedef struct _GtkIconSet     GtkIconSet;
+typedef struct _GtkIconSource  GtkIconSource;
 
+typedef enum
+{
+  GTK_ICON_MENU,
+  GTK_ICON_BUTTON,
+  GTK_ICON_SMALL_TOOLBAR,
+  GTK_ICON_LARGE_TOOLBAR,
+  GTK_ICON_DIALOG
+} GtkIconSizeType;
 
 /* We make this forward declaration here, since we pass
  * GtkWidgt's to the draw functions.
@@ -110,6 +120,8 @@
 				 * was created
 				 */
   GSList	 *styles;
+
+  GSList         *icon_factories;
 };
 
 struct _GtkStyleClass
@@ -149,8 +161,18 @@
 				 GdkWindow              *window,
 				 GtkStateType            state_type);
 
+
+  GdkPixbuf * (* render_icon)   (GtkStyle               *style,
+                                 const GtkIconSource    *source,
+                                 GtkTextDirection        direction,
+                                 GtkStateType            state,
+                                 GtkIconSizeType         size,
+                                 GtkWidget              *widget,
+                                 const gchar            *detail);
+  
   /* Drawing functions
    */
+  
   void (*draw_hline)		(GtkStyle		*style,
 				 GdkWindow		*window,
 				 GtkStateType		 state_type,
@@ -408,6 +430,15 @@
 					      gint	    width, 
 					      gint	    height);
 
+GtkIconSet* gtk_style_lookup_icon_set (GtkStyle            *style,
+                                       const char          *stock_id);
+GdkPixbuf * gtk_style_render_icon     (GtkStyle            *style,
+                                       const GtkIconSource *source,
+                                       GtkTextDirection     direction,
+                                       GtkStateType         state,
+                                       GtkIconSizeType      size,
+                                       GtkWidget           *widget,
+                                       const gchar         *detail);
 void gtk_draw_hline      (GtkStyle        *style,
 			  GdkWindow       *window,
 			  GtkStateType     state_type,
@@ -814,6 +845,17 @@
 			   gint             width,
 			   gint             height,
 			   GtkOrientation   orientation);
+
+/* Internal */
+
+GdkPixbuf * _gtk_default_render_icon (GtkStyle            *style,
+                                      const GtkIconSource *source,
+                                      GtkTextDirection     direction,
+                                      GtkStateType         state,
+                                      GtkIconSizeType      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/26 22:00:32
@@ -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,
+                        GtkIconSizeType 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/26 22:00:32
@@ -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,
+                                         GtkIconSizeType 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]