updated stock patch
- From: Havoc Pennington <hp redhat com>
- To: gtk-devel-list gnome org
- Subject: updated stock patch
- Date: 28 Jul 2000 15:15:16 -0400
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]