diff --git a/demos/Makefile.am b/demos/Makefile.am index 8d7ca2f756..369c5c0c0c 100644 --- a/demos/Makefile.am +++ b/demos/Makefile.am @@ -37,6 +37,11 @@ noinst_PROGRAMS = \ testpixbuf-scale \ pixbuf-demo +BUILT_SOURCES=test-inline-pixbufs.h + +test-inline-pixbufs.h: $(top_builddir)/gdk-pixbuf/make-inline-pixbuf apple-red.png gnome-foot.png + GDK_PIXBUF_MODULEDIR=$(top_builddir)/gdk-pixbuf/.libs $(top_builddir)/gdk-pixbuf/make-inline-pixbuf test-inline-pixbufs.h apple_red apple-red.png gnome_foot gnome-foot.png + testpixbuf_DEPENDENCIES = $(DEPS) testpixbuf_drawable_DEPENDENCIES = $(DEPS) testpixbuf_scale_DEPENDENCIES = $(DEPS) diff --git a/demos/testpixbuf.c b/demos/testpixbuf.c index 85dea5b7b7..a4e59201a1 100644 --- a/demos/testpixbuf.c +++ b/demos/testpixbuf.c @@ -25,6 +25,8 @@ #include #include +#include "test-inline-pixbufs.h" + typedef struct { FILE *imagefile; GdkPixbufLoader *loader; @@ -375,7 +377,7 @@ new_testrgb_window (GdkPixbuf *pixbuf, gchar *title) window = gtk_widget_new (gtk_window_get_type (), "GtkObject::user_data", NULL, "GtkWindow::type", GTK_WINDOW_TOPLEVEL, - "GtkWindow::title", "testrgb", + "GtkWindow::title", title ? title : "testrgb", "GtkWindow::allow_shrink", TRUE, NULL); gtk_signal_connect (GTK_OBJECT (window), "destroy", @@ -536,6 +538,13 @@ main (int argc, char **argv) new_testrgb_window (pixbuf, NULL); ++xpmp; } + + /* Test loading from inline data. */ + pixbuf = gdk_pixbuf_new_from_inline (apple_red, FALSE); + new_testrgb_window (pixbuf, "Red apple from inline data"); + + pixbuf = gdk_pixbuf_new_from_inline (gnome_foot, TRUE); + new_testrgb_window (pixbuf, "Foot from inline data"); found_valid = TRUE; } else { diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index c014e6b933..72807ac5c3 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,14 @@ +2000-06-23 Havoc Pennington + + * image-to-inline.c: Small program that creates C variable + declarations of inline pixbuf data. This can be read + by gdk_pixbuf_new_from_inline. + + * gdk-pixbuf.h (gdk_pixbuf_new_from_inline): New function to read + inline pixbuf data and create a pixbuf from it. + + * gdk-pixbuf-data.c (gdk_pixbuf_new_from_inline): implement here + 2000-06-21 Havoc Pennington * gdk-pixbuf.c: Convert GdkPixbuf to GObject, leaving it opaque diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index b66e2ef54d..734c58691a 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -138,6 +138,12 @@ LDADDS = libgdk_pixbuf.la $(GLIB_LIBS) $(STATIC_LIB_DEPS) test_gdk_pixbuf_LDADD = $(LDADDS) -lgmodule +bin_PROGRAMS=make-inline-pixbuf + +make_inline_pixbuf_SOURCES=make-inline-pixbuf.c + +make_inline_pixbuf_LDADD = $(LDADDS) -lgmodule + GDK_PIXBUF_LIBS = $(GLIB_LIBS) # diff --git a/gdk-pixbuf/gdk-pixbuf-data.c b/gdk-pixbuf/gdk-pixbuf-data.c index f03d869bac..b2f6611f86 100644 --- a/gdk-pixbuf/gdk-pixbuf-data.c +++ b/gdk-pixbuf/gdk-pixbuf-data.c @@ -75,3 +75,98 @@ gdk_pixbuf_new_from_data (const guchar *data, GdkColorspace colorspace, gboolean return pixbuf; } + +int +read_int (const guchar **p) +{ + guint32 num; + + num = g_ntohl (* (guint32*) *p); + + *p += 4; + + return num; +} + +gboolean +read_bool (const guchar **p) +{ + gboolean val = **p != 0; + + ++(*p); + + return val; +} + +/* sync with image-to-inline.c */ +#define MAGIC_NUMBER 1804289383 + +static void +free_buffer (guchar *pixels, gpointer data) +{ + free (pixels); +} + +/** + * gdk_pixbuf_new_from_inline: + * @data: An inlined GdkPixbuf + * @copy_pixels: whether to copy the pixels out of the inline data, or to use them in-place + * + * Create a #GdkPixbuf from a custom format invented to store pixbuf + * data in C program code. This library comes with a program called "image-to-inline" + * that can write out a variable definition containing an inlined pixbuf. + * This is useful if you want to ship a program with images, but + * don't want to depend on any external files. + * + * The inline data format contains the pixels in #GdkPixbuf's native format. + * Since the inline pixbuf is static data, you don't really need to copy it. + * However it's typically in read-only memory, so if you plan to modify + * it you must copy it. + * + * Return value: A newly-created #GdkPixbuf structure with a reference count of + * 1. + **/ +GdkPixbuf* +gdk_pixbuf_new_from_inline (const guchar *inline_pixbuf, + gboolean copy_pixels) +{ + const guchar *p; + GdkPixbuf *pixbuf; + + p = inline_pixbuf; + + if (read_int (&p) != MAGIC_NUMBER) { + g_warning ("Bad inline data; wrong magic number"); + return NULL; + } + + pixbuf = GDK_PIXBUF (g_type_create_instance (GDK_TYPE_PIXBUF)); + + pixbuf->rowstride = read_int (&p); + pixbuf->width = read_int (&p); + pixbuf->height = read_int (&p); + pixbuf->has_alpha = read_bool (&p); + pixbuf->colorspace = read_int (&p); + pixbuf->n_channels = read_int (&p); + pixbuf->bits_per_sample = read_int (&p); + + if (copy_pixels) { + pixbuf->pixels = malloc (pixbuf->height * pixbuf->rowstride); + + if (pixbuf->pixels == NULL) { + g_object_unref (G_OBJECT (pixbuf)); + return NULL; + } + + pixbuf->destroy_fn = free_buffer; + pixbuf->destroy_fn_data = NULL; + + memcpy (pixbuf->pixels, p, pixbuf->height * pixbuf->rowstride); + } else { + pixbuf->pixels = (guchar *) p; + } + + + return pixbuf; +} + diff --git a/gdk-pixbuf/gdk-pixbuf.h b/gdk-pixbuf/gdk-pixbuf.h index 04756b5b81..3e68b5dd67 100644 --- a/gdk-pixbuf/gdk-pixbuf.h +++ b/gdk-pixbuf/gdk-pixbuf.h @@ -103,6 +103,10 @@ GdkPixbuf *gdk_pixbuf_new_from_data (const guchar *data, GdkPixbuf *gdk_pixbuf_new_from_xpm_data (const char **data); +/* Read an inline pixbuf */ +GdkPixbuf *gdk_pixbuf_new_from_inline (const guchar *inline_pixbuf, + gboolean copy_pixels); + /* Adding an alpha channel */ GdkPixbuf *gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf, gboolean substitute_color, guchar r, guchar g, guchar b); diff --git a/gdk-pixbuf/make-inline-pixbuf.c b/gdk-pixbuf/make-inline-pixbuf.c new file mode 100644 index 0000000000..2dcc2317c5 --- /dev/null +++ b/gdk-pixbuf/make-inline-pixbuf.c @@ -0,0 +1,211 @@ +/* Program to convert an image file to inline C data + * + * Copyright (C) 2000 Red Hat, Inc. + * + * Developed by Havoc Pennington + * + * 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. + */ + +#include +#include "gdk-pixbuf.h" +#include +#include +#include + +/* sync with gdk-pixbuf-data.c */ +#define MAGIC_NUMBER 1804289383 + +void +output_int (FILE *outfile, int number, const char *comment) +{ + guchar bytes[4]; + guint32 num; + + g_assert (sizeof (bytes) == sizeof (num)); + + num = number; + *((guint32*)bytes) = g_htonl (num); + + fprintf(outfile, " /* %s (decimal: %u) */\n 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x,\n", + comment, num, + bytes[0], bytes[1], bytes[2], bytes[3]); +} + +void +output_bool (FILE *outfile, gboolean val, const char *comment) +{ + fprintf(outfile, " /* %s (%s) */\n 0x%.2x,\n", + comment, + val ? "TRUE" : "FALSE", + val ? 1 : 0); +} + +void +output_pixbuf (FILE *outfile, gboolean ext_symbols, + const char *varname, + GdkPixbuf *pixbuf) +{ + const char *modifier; + const guchar *p; + const guchar *end; + gboolean has_alpha; + int column; + + modifier = "static "; + if (ext_symbols) + modifier = ""; + + fprintf (outfile, "%sconst guchar ", modifier); + fputs (varname, outfile); + fputs (" [] =\n", outfile); + fputs ("{\n", outfile); + + p = gdk_pixbuf_get_pixels (pixbuf); + end = p + gdk_pixbuf_get_rowstride (pixbuf) * gdk_pixbuf_get_height (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + + /* Sync the order of writing with the order of reading in + * gdk-pixbuf-data.c + */ + output_int (outfile, MAGIC_NUMBER, "File magic"); + output_int (outfile, gdk_pixbuf_get_rowstride (pixbuf), "Rowstride"); + output_int (outfile, gdk_pixbuf_get_width (pixbuf), "Width"); + output_int (outfile, gdk_pixbuf_get_height (pixbuf), "Height"); + + output_bool (outfile, has_alpha, "Has an alpha channel"); + + output_int (outfile, gdk_pixbuf_get_colorspace (pixbuf), "Colorspace (0 == RGB, no other options implemented)"); + + output_int (outfile, gdk_pixbuf_get_n_channels (pixbuf), "Number of channels"); + + output_int (outfile, gdk_pixbuf_get_bits_per_sample (pixbuf), "Bits per sample"); + + fputs (" /* Image data */\n", outfile); + + /* Copy the data in the pixbuf */ + column = 0; + while (p != end) + { + guchar r, g, b, a; + + r = *p; + ++p; + g = *p; + ++p; + b = *p; + ++p; + if (has_alpha) + { + a = *p; + ++p; + } + else + a = 0; + + + if (has_alpha) + fprintf(outfile, " 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x", r, g, b, a); + else + fprintf(outfile, " 0x%.2x, 0x%.2x, 0x%.2x", r, g, b); + + if (p != end) + fputs (",", outfile); + else + fputs ("\n", outfile); + + ++column; + + if (column > 2) + { + fputs ("\n", outfile); + column = 0; + } + } + + fputs ("};\n\n", outfile); +} + +void +usage (void) +{ + fprintf (stderr, "Usage: image-to-inline [--extern-symbols] OUTFILE varname1 imagefile1 varname2 imagefile2 ...\n"); + exit (1); +} + +int +main (int argc, char **argv) +{ + gboolean ext_symbols = FALSE; + FILE *outfile; + int i; + + gdk_pixbuf_init (); + + if (argc < 4) + usage (); + + i = 1; + if (strcmp (argv[i], "--extern-symbols") == 0) + { + ext_symbols = TRUE; + ++i; + if (argc < 5) + usage (); + } + + outfile = fopen (argv[i], "w"); + if (outfile == NULL) + { + fprintf (stderr, "Failed to open output file `%s': %s\n", + argv[i], strerror (errno)); + exit (1); + } + + ++i; + + fputs ("/* This file was automatically generated by the image-to-inline program.\n" + " * It contains inline RGB image data.\n" + " */\n\n", outfile); + + while (i < argc) + { + GdkPixbuf *pixbuf; + + if ((i + 1) == argc) + usage (); + + pixbuf = gdk_pixbuf_new_from_file (argv[i+1]); + + if (pixbuf == NULL) + { + fprintf (stderr, "Failed to open image file `%s': %s\n", + argv[i+1], strerror (errno)); + + exit (1); + } + + output_pixbuf (outfile, ext_symbols, argv[i], pixbuf); + + gdk_pixbuf_unref (pixbuf); + + i += 2; + } + + fclose (outfile); + + return 0; +} diff --git a/gtk/Makefile.am b/gtk/Makefile.am index e1375c837d..1097117c66 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -95,6 +95,7 @@ gtk_public_h_sources = @STRIP_BEGIN@ \ gtkhscrollbar.h \ gtkhseparator.h \ gtkhsv.h \ + gtkiconfactory.h \ gtkimage.h \ gtkimcontext.h \ gtkimmulticontext.h \ @@ -140,6 +141,7 @@ gtk_public_h_sources = @STRIP_BEGIN@ \ gtkspinbutton.h \ gtkstyle.h \ gtkstatusbar.h \ + gtkstock.h \ gtktable.h \ gtktearoffmenuitem.h \ gtktextbuffer.h \ @@ -234,6 +236,7 @@ gtk_c_sources = @STRIP_BEGIN@ \ gtkhscrollbar.c \ gtkhseparator.c \ gtkhsv.c \ + gtkiconfactory.c \ gtkimage.c \ gtkimcontext.c \ gtkimcontextsimple.c \ @@ -279,6 +282,7 @@ gtk_c_sources = @STRIP_BEGIN@ \ gtksignal.c \ gtksocket.c \ gtkspinbutton.c \ + gtkstock.c \ gtkstyle.c \ gtkstatusbar.c \ gtktable.c \ diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c new file mode 100644 index 0000000000..932d5a64c9 --- /dev/null +++ b/gtk/gtkiconfactory.c @@ -0,0 +1,761 @@ +/* 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" + +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); + +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_type_create_instance (GTK_TYPE_ICON_FACTORY)); +} + +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); + + g_hash_table_insert (factory->icons, g_strdup (stock_id), icon_set); + + if (old_key) + g_free (old_key); + + 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) + { + /* FIXME create our default icons, should be kept + * in sync with the default stock items. + */ + gtk_default_icons = gtk_icon_factory_new (); + + } + + return gtk_icon_factory_lookup (gtk_default_icons, stock_id); +} + +/* 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 */ +}; + +static gint heights[] = +{ + 16, /* menu */ + 24, /* button */ + 18, /* small toolbar */ + 24 /* large toolbar */ +}; + +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_get_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; + } +} + diff --git a/gtk/gtkiconfactory.h b/gtk/gtkiconfactory.h new file mode 100644 index 0000000000..1160c47c3e --- /dev/null +++ b/gtk/gtkiconfactory.h @@ -0,0 +1,137 @@ +/* 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 +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct _GtkIconFactory GtkIconFactory; +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 real size from semantic size (eventually user-configurable) */ +void gtk_get_icon_size (GtkIconSizeType semantic_size, + gint *width, + gint *height); + + +/* Icon sets */ + +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_get_icon (GtkIconSet *icon_set, + GtkStyle *style, + GtkTextDirection direction, + GtkStateType state, + GtkIconSizeType size, + GtkWidget *widget, + const char *detail); + +GtkIconSet* gtk_icon_set_new (void); + +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; +}; + +void gtk_icon_set_add_source (GtkIconSet *icon_set, + const GtkIconSource *source); + +GtkIconSource *gtk_icon_source_copy (const GtkIconSource *source); +void gtk_icon_source_free (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); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GTK_ICON_FACTORY_H__ */ diff --git a/gtk/gtkstock.c b/gtk/gtkstock.c new file mode 100644 index 0000000000..d0056a43d2 --- /dev/null +++ b/gtk/gtkstock.c @@ -0,0 +1,145 @@ +/* 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 "gtkstock.h" +#include "gtkintl.h" +#include + +static GHashTable *stock_hash = NULL; +static void init_stock_hash (void); + +static void +real_add (const GtkStockItem *items, + guint n_items, + gboolean copy) +{ + int i; + + init_stock_hash (); + + if (n_items == 0) + return; + + i = 0; + while (i < n_items) + { + const GtkStockItem * item = &items[i]; + if (copy) + item = gtk_stock_item_copy (item); + + g_hash_table_insert (stock_hash, + (gchar*)item->stock_id, (GtkStockItem*)item); + + ++i; + } +} + +void +gtk_stock_add (const GtkStockItem *items, + guint n_items) +{ + g_return_if_fail (items != NULL); + + real_add (items, n_items, TRUE); +} + +void +gtk_stock_add_static (const GtkStockItem *items, + guint n_items) +{ + g_return_if_fail (items != NULL); + + real_add (items, n_items, FALSE); +} + +gboolean +gtk_stock_lookup (const gchar *stock_id, + GtkStockItem *item) +{ + const GtkStockItem *found; + + g_return_val_if_fail (stock_id != NULL, FALSE); + g_return_val_if_fail (item != NULL, FALSE); + + init_stock_hash (); + + found = g_hash_table_lookup (stock_hash, stock_id); + + if (found) + *item = *found; + + return found != NULL; +} + +GtkStockItem * +gtk_stock_item_copy (const GtkStockItem *item) +{ + GtkStockItem *copy; + + g_return_val_if_fail (item != NULL, NULL); + + copy = g_new (GtkStockItem, 1); + + *copy = *item; + + copy->stock_id = g_strdup (item->stock_id); + copy->label = g_strdup (item->label); + copy->translation_domain = g_strdup (item->translation_domain); + + return copy; +} + +void +gtk_stock_item_free (GtkStockItem *item) +{ + g_return_if_fail (item != NULL); + + g_free ((gchar*)item->stock_id); + g_free ((gchar*)item->label); + g_free ((gchar*)item->translation_domain); + + g_free (item); +} + +static GtkStockItem builtin_items [] = +{ + /* FIXME these are just examples */ + /* and the OK accelerator is wrong, Return means default button, + OK should have its own accelerator */ + { GTK_STOCK_OK, N_("OK"), 0, GDK_Return, "gtk+" }, + { GTK_STOCK_EXIT, N_("Exit"), GDK_CONTROL_MASK, GDK_x, "gtk+" } +}; + +static void +init_stock_hash (void) +{ + if (stock_hash == NULL) + { + stock_hash = g_hash_table_new (g_str_hash, g_str_equal); + + gtk_stock_add_static (builtin_items, G_N_ELEMENTS (builtin_items)); + } +} diff --git a/gtk/gtkstock.h b/gtk/gtkstock.h new file mode 100644 index 0000000000..e200916800 --- /dev/null +++ b/gtk/gtkstock.h @@ -0,0 +1,71 @@ +/* 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_STOCK_H__ +#define __GTK_STOCK_H__ + + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct _GtkStockItem GtkStockItem; + +struct _GtkStockItem +{ + const char *stock_id; + const char *label; + GdkModifierType modifier; + guint keyval; + const char *translation_domain; +}; + +void gtk_stock_add (const GtkStockItem *item, + guint n_items); +void gtk_stock_add_static (const GtkStockItem *item, + guint n_items); +gboolean gtk_stock_lookup (const gchar *stock_id, + GtkStockItem *item); + + + +GtkStockItem *gtk_stock_item_copy (const GtkStockItem *item); +void gtk_stock_item_free (GtkStockItem *item); + + +/* Stock IDs */ +#define GTK_STOCK_OK "OK" +#define GTK_STOCK_CANCEL "Cancel" +#define GTK_STOCK_EXIT "Exit" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_STOCK_H__ */ diff --git a/gtk/gtkstyle.c b/gtk/gtkstyle.c index e30acffd77..b73c7b7c15 100644 --- a/gtk/gtkstyle.c +++ b/gtk/gtkstyle.c @@ -31,7 +31,7 @@ #include "gtkstyle.h" #include "gtkwidget.h" #include "gtkthemes.h" - +#include "gtkiconfactory.h" #define LIGHTNESS_MULT 1.3 #define DARKNESS_MULT 0.7 @@ -432,6 +432,7 @@ gtk_style_class_init (GtkStyleClass *klass) object_class->finalize = gtk_style_finalize; + 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; @@ -795,6 +796,28 @@ gtk_style_realize (GtkStyle *style, style->engine->realize_style (style); } +GtkIconSet* +gtk_style_lookup_icon (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, @@ -1176,6 +1199,22 @@ gtk_style_set_background (GtkStyle *style, } +GdkPixbuf * +gtk_style_render_icon (GtkStyle *style, + const GtkIconSource *source, + GtkTextDirection direction, + GtkStateType state, + GtkIconSizeType size, + GtkWidget *widget, + const gchar *detail) +{ + g_return_val_if_fail (style != NULL, NULL); + g_return_val_if_fail (GTK_STYLE_GET_CLASS (style)->render_icon != NULL, NULL); + + return GTK_STYLE_GET_CLASS (style)->render_icon (style, source, direction, state, + size, widget, detail); +} + /* Default functions */ void gtk_style_apply_default_background (GtkStyle *style, @@ -1241,6 +1280,157 @@ gtk_style_apply_default_background (GtkStyle *style, } } +#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 gtk_default_draw_hline (GtkStyle *style, GdkWindow *window, diff --git a/gtk/gtkstyle.h b/gtk/gtkstyle.h index 45e028eacc..a936c80df6 100644 --- a/gtk/gtkstyle.h +++ b/gtk/gtkstyle.h @@ -51,7 +51,16 @@ typedef struct _GtkStyleClass GtkStyleClass; */ 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 +} GtkIconSizeType; /* We make this forward declaration here, since we pass * GtkWidgt's to the draw functions. @@ -114,11 +123,21 @@ struct _GtkStyle * was created */ GSList *styles; + + GSList *icon_factories; }; struct _GtkStyleClass { GObjectClass parent_class; + + GdkPixbuf * (* render_icon) (GtkStyle *style, + const GtkIconSource *source, + GtkTextDirection direction, + GtkStateType state, + GtkIconSizeType size, + GtkWidget *widget, + const gchar *detail); void (*draw_hline) (GtkStyle *style, GdkWindow *window, @@ -377,6 +396,16 @@ void gtk_style_apply_default_background (GtkStyle *style, gint width, gint height); +GtkIconSet* gtk_style_lookup_icon (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, @@ -784,6 +813,17 @@ void gtk_paint_handle (GtkStyle *style, 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 } #endif /* __cplusplus */ diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index e67d3297c5..b29ed4a52a 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -28,6 +28,7 @@ #include #include #include "gtkcontainer.h" +#include "gtkiconfactory.h" #include "gtkmain.h" #include "gtkrc.h" #include "gtkselection.h" @@ -3190,6 +3191,35 @@ gtk_widget_pop_style (void) } } +GdkPixbuf* +gtk_widget_get_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 (widget->style, stock_id); + + if (icon_set == NULL) + return NULL; + + retval = gtk_icon_set_get_icon (icon_set, + widget->style, + gtk_widget_get_direction (widget), + GTK_WIDGET_STATE (widget), + size, + widget, + detail); + + return retval; +} + /** * gtk_widget_create_pango_context: * @widget: a #PangoWidget diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 8fae055e30..5f465f94d2 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -576,6 +576,11 @@ void gtk_widget_restore_default_style (GtkWidget *widget); void gtk_widget_modify_style (GtkWidget *widget, GtkRcStyle *style); +GdkPixbuf* gtk_widget_get_icon (GtkWidget *widget, + const gchar *stock_id, + GtkIconSizeType size, + const gchar *detail); + PangoContext *gtk_widget_create_pango_context (GtkWidget *widget); PangoLayout *gtk_widget_create_pango_layout (GtkWidget *widget);