[evolution-patches] inline image scaling patch



Here's the final inline image scaling patch.

I dont think there's really any need to explain anything about it.

Index: mail/ChangeLog
===================================================================
RCS file: /cvs/gnome/evolution/mail/ChangeLog,v
retrieving revision 1.3649
diff -u -p -r1.3649 ChangeLog
--- mail/ChangeLog	1 Jul 2005 03:29:22 -0000	1.3649
+++ mail/ChangeLog	6 Jul 2005 03:25:41 -0000
@@ -1,3 +1,29 @@
+2005-07-05  Not Zed  <NotZed Ximian com>
+
+	* em-format-html-display.c (efhd_attachment_image): use the cache,
+	since we set it up.
+	(efhd_image): added a (private!) format handler for all the image
+	types so we intercept them and handle them directly.
+	(efhd_image_fit, efhd_image_unfit): replace the resize callback
+	with two much simpler ones.
+
+	* em-icon-stream.c (em_icon_stream_get_image): added 'fit to'
+	arguments.  Changed dramatically to get approximate fit-to image,
+	update cache, etc.
+	(em_icon_stream_is_resized): added 'fit to' arguments.  changed to
+	manipulate the cache properly.
+	(emis_fit): helper to fit an image to a size.
+	(em_icon_stream_new): added 'fit to' arguments rather than poking
+	structures.
+
+	* em-format-html-display.c (efhd_attachment_image): fixed a memory
+	leak, various style issues.  Removed all scaling code.
+	(efhd_attachment_popup): show menu's appropriately.  add back the
+	hide/show menu always.
+	(efhd_image_popup): fix formatting.
+
+	** Applied patch from Srini for scaling images to fit by default.
+
 2005-06-24	Matt Brown	<matt mattb net nz>
 
 	* em-inline-filter.c: implement extraction of inline signed/encrypted pgp
Index: mail/em-format-html-display.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-format-html-display.c,v
retrieving revision 1.68
diff -u -p -r1.68 em-format-html-display.c
--- mail/em-format-html-display.c	17 Jun 2005 15:20:29 -0000	1.68
+++ mail/em-format-html-display.c	6 Jul 2005 03:25:42 -0000
@@ -30,6 +30,7 @@
 #include <gtkhtml/gtkhtml-embedded.h>
 #include <gtkhtml/gtkhtml-search.h>
 
+#include <gtk/gtkeventbox.h>
 #include <gtk/gtkvbox.h>
 #include <gtk/gtkhbox.h>
 #include <gtk/gtkbutton.h>
@@ -106,6 +107,9 @@ static int efhd_html_button_press_event 
 static void efhd_html_link_clicked (GtkHTML *html, const char *url, EMFormatHTMLDisplay *efhd);
 static void efhd_html_on_url (GtkHTML *html, const char *url, EMFormatHTMLDisplay *efhd);
 
+static void efhd_attachment_frame(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri);
+static gboolean efhd_attachment_image(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject);
+
 struct _attach_puri {
 	EMFormatPURI puri;
 
@@ -119,8 +123,14 @@ struct _attach_puri {
 	GtkHTML *frame;
 	CamelStream *output;
 	unsigned int shown:1;
+
+	/* image stuff */
+	int fit_width;
+	int fit_height;
+	GtkImage *image;
 };
 
+
 static void efhd_iframe_created(GtkHTML *html, GtkHTML *iframe, EMFormatHTMLDisplay *efh);
 /*static void efhd_url_requested(GtkHTML *html, const char *url, GtkHTMLStream *handle, EMFormatHTMLDisplay *efh);
   static gboolean efhd_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTMLDisplay *efh);*/
@@ -903,9 +913,51 @@ efhd_format_secure(EMFormat *emf, CamelS
 	}
 }
 
+static void
+efhd_image(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *handle)
+{
+	char *classid;
+	struct _attach_puri *info;
+	
+	classid = g_strdup_printf("image%s", ((EMFormat *)efh)->part_id->str);
+	info = (struct _attach_puri *)em_format_add_puri((EMFormat *)efh, sizeof(*info), classid, part, efhd_attachment_frame);
+	em_format_html_add_pobject(efh, sizeof(EMFormatHTMLPObject), classid, part, efhd_attachment_image);
+
+	info->handle = handle;
+	info->shown = TRUE;
+	info->snoop_mime_type = ((EMFormat *) efh)->snoop_mime_type;
+	info->fit_width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12;
+
+	camel_stream_printf(stream, "<td><object classid=\"%s\"></object></td>", classid);
+	g_free(classid);
+}
+
 /* ********************************************************************** */
 
 static EMFormatHandler type_builtin_table[] = {
+	{ "image/gif", (EMFormatFunc)efhd_image },
+	{ "image/jpeg", (EMFormatFunc)efhd_image },
+	{ "image/png", (EMFormatFunc)efhd_image },
+	{ "image/x-png", (EMFormatFunc)efhd_image },
+	{ "image/tiff", (EMFormatFunc)efhd_image },
+	{ "image/x-bmp", (EMFormatFunc)efhd_image },
+	{ "image/bmp", (EMFormatFunc)efhd_image },
+	{ "image/svg", (EMFormatFunc)efhd_image },
+	{ "image/x-cmu-raster", (EMFormatFunc)efhd_image },
+	{ "image/x-ico", (EMFormatFunc)efhd_image },
+	{ "image/x-portable-anymap", (EMFormatFunc)efhd_image },
+	{ "image/x-portable-bitmap", (EMFormatFunc)efhd_image },
+	{ "image/x-portable-graymap", (EMFormatFunc)efhd_image },
+	{ "image/x-portable-pixmap", (EMFormatFunc)efhd_image },
+	{ "image/x-xpixmap", (EMFormatFunc)efhd_image },
+
+	/* This is where one adds those busted, non-registered types,
+	   that some idiot mailer writers out there decide to pull out
+	   of their proverbials at random. */
+
+	{ "image/jpg", (EMFormatFunc)efhd_image },
+	{ "image/pjpeg", (EMFormatFunc)efhd_image },
+
 	{ "x-evolution/message/prefix", (EMFormatFunc)efhd_message_prefix },
 };
 
@@ -1059,10 +1111,30 @@ efhd_attachment_button_show(GtkWidget *w
 	efhd_attachment_show(NULL, NULL, data);
 }
 
+static void
+efhd_image_fit(EPopup *ep, EPopupItem *item, void *data)
+{
+	struct _attach_puri *info = data;
+
+	info->fit_width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12;
+	gtk_image_set_from_pixbuf(info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height));
+}
+
+static void
+efhd_image_unfit(EPopup *ep, EPopupItem *item, void *data)
+{
+	struct _attach_puri *info = data;
+
+	info->fit_width = 0;
+	gtk_image_set_from_pixbuf((GtkImage *)info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height));
+}
+
 static EPopupItem efhd_menu_items[] = {
 	{ E_POPUP_BAR, "05.display", },
 	{ E_POPUP_ITEM, "05.display.00", N_("_View Inline"), efhd_attachment_show },
 	{ E_POPUP_ITEM, "05.display.00", N_("_Hide"), efhd_attachment_show },
+	{ E_POPUP_ITEM, "05.display.01", N_("_Fit to Width"), efhd_image_fit, NULL, NULL, EM_POPUP_PART_IMAGE },
+	{ E_POPUP_ITEM, "05.display.01", N_("Show _Original Size"), efhd_image_unfit, NULL, NULL, EM_POPUP_PART_IMAGE },
 };
 
 static void
@@ -1088,7 +1160,6 @@ efhd_attachment_popup(GtkWidget *w, GdkE
 	GSList *menus = NULL;
 	EMPopup *emp;
 	EMPopupTargetPart *target;
-	EPopupItem *item;
 
 	d(printf("attachment popup, button %d\n", event->button));
 
@@ -1113,8 +1184,14 @@ efhd_attachment_popup(GtkWidget *w, GdkE
 	if (info->handle) {
 		/* show/hide menus, only if we have an inline handler */
 		menus = g_slist_prepend(menus, &efhd_menu_items[0]);
-		item = &efhd_menu_items[info->shown?2:1];
-		menus = g_slist_prepend(menus, item);
+		menus = g_slist_prepend(menus, &efhd_menu_items[info->shown?2:1]);
+		if (info->shown && info->image) {
+			if (info->fit_width != 0) {
+				if (em_icon_stream_is_resized(info->puri.cid, info->fit_width, info->fit_height))
+				    menus = g_slist_prepend(menus, &efhd_menu_items[4]);
+			} else
+				menus = g_slist_prepend(menus, &efhd_menu_items[3]);
+		}
 	}
 
 	e_popup_add_items((EPopup *)emp, menus, NULL, efhd_menu_items_free, info);
@@ -1129,6 +1206,15 @@ efhd_attachment_popup(GtkWidget *w, GdkE
 }
 
 static gboolean
+efhd_image_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri *info)
+{
+	if (event && event->button != 3)
+		return FALSE;
+
+	return efhd_attachment_popup(w, event, info);
+}
+
+static gboolean
 efhd_attachment_popup_menu(GtkWidget *w, struct _attach_puri *info)
 {
 	return efhd_attachment_popup(w, NULL, info);
@@ -1213,6 +1299,73 @@ efhd_write_icon_job(struct _EMFormatHTML
 	camel_stream_close(job->stream);
 }
 
+static void
+efhd_image_resized(GtkWidget *w, GtkAllocation *event, struct _attach_puri *info)
+{
+	GdkPixbuf *pb;
+	int width;
+
+	if (info->fit_width == 0)
+		return;
+
+	width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12;
+	if (info->fit_width == width)
+		return;
+	info->fit_width = width;
+	pb = em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height);
+	gtk_image_set_from_pixbuf(info->image, pb);
+	g_object_unref(pb);
+}
+
+static gboolean
+efhd_attachment_image(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject)
+{
+	GtkWidget *box;	
+	EMFormatHTMLJob *job;
+	struct _attach_puri *info;
+	GdkPixbuf *pixbuf;
+	GtkTargetEntry drag_types[] = {
+		{ NULL, 0, 0 },
+		{ "text/uri-list", 0, 1 },
+	};
+	char *simple_type;
+
+	info = (struct _attach_puri *)em_format_find_puri((EMFormat *)efh, pobject->classid);
+
+	info->image = (GtkImage *)gtk_image_new();
+	pixbuf = em_icon_stream_get_image(pobject->classid, info->fit_width, info->fit_height);
+	if (pixbuf) {
+		gtk_image_set_from_pixbuf(info->image, pixbuf);
+		g_object_unref(pixbuf);
+	} else {
+		job = em_format_html_job_new(efh, efhd_write_icon_job, pobject);	
+		job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)info->image, pobject->classid, info->fit_width, info->fit_height, TRUE);
+		em_format_html_job_queue(efh, job);
+	}
+
+	box = gtk_event_box_new();
+	gtk_container_add((GtkContainer *)box, (GtkWidget *)info->image);
+	gtk_widget_show_all(box);
+	gtk_container_add((GtkContainer *)eb, box);
+
+	g_signal_connect(eb, "size_allocate", G_CALLBACK(efhd_image_resized), info);
+	
+	simple_type = camel_content_type_simple(((CamelDataWrapper *)pobject->part)->mime_type);
+	camel_strdown(simple_type);
+	
+	drag_types[0].target = simple_type;
+	gtk_drag_source_set(box, GDK_BUTTON1_MASK, drag_types, sizeof(drag_types)/sizeof(drag_types[0]), GDK_ACTION_COPY);
+	g_free(simple_type);
+
+	g_signal_connect(box, "drag-data-get", G_CALLBACK(efhd_drag_data_get), pobject);
+	g_signal_connect (box, "drag-data-delete", G_CALLBACK(efhd_drag_data_delete), pobject);
+
+	g_signal_connect(box, "button_press_event", G_CALLBACK(efhd_image_popup), info);
+	g_signal_connect(box, "popup_menu", G_CALLBACK(efhd_attachment_popup_menu), info);
+
+	return TRUE;
+}
+
 /* attachment button callback */
 static gboolean
 efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject)
@@ -1266,13 +1419,13 @@ efhd_attachment_button(EMFormatHTML *efh
 	camel_strdown(simple_type);
 
 	/* FIXME: offline parts, just get icon */
-	if (camel_content_type_is (((CamelDataWrapper *)pobject->part)->mime_type, "image", "*")) {
+	if (camel_content_type_is(((CamelDataWrapper *)pobject->part)->mime_type, "image", "*")) {
 		EMFormatHTMLJob *job;
 		GdkPixbuf *mini;
 		char *key;
 
 		key = pobject->classid;
-		mini = em_icon_stream_get_image(key);
+		mini = em_icon_stream_get_image(key, 24, 24);
 		if (mini) {
 			d(printf("got image from cache '%s'\n", key));
 			gtk_image_set_from_pixbuf((GtkImage *)w, mini);
@@ -1280,7 +1433,7 @@ efhd_attachment_button(EMFormatHTML *efh
 		} else {
 			d(printf("need to create icon image '%s'\n", key));
 			job = em_format_html_job_new(efh, efhd_write_icon_job, pobject);
-			job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)w, key);
+			job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)w, key, 24, 24, FALSE);
 			em_format_html_job_queue(efh, job);
 		}
 	} else {
@@ -1307,7 +1460,6 @@ efhd_attachment_button(EMFormatHTML *efh
 
 	a11y = gtk_widget_get_accessible (button);
 	atk_object_set_name (a11y, _("Attachment Button"));
-
 
 	g_signal_connect(button, "button_press_event", G_CALLBACK(efhd_attachment_popup), info);
 	g_signal_connect(button, "popup_menu", G_CALLBACK(efhd_attachment_popup_menu), info);
Index: mail/em-icon-stream.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-icon-stream.c,v
retrieving revision 1.6
diff -u -p -r1.6 em-icon-stream.c
--- mail/em-icon-stream.c	2 Mar 2005 05:23:45 -0000	1.6
+++ mail/em-icon-stream.c	6 Jul 2005 03:25:42 -0000
@@ -26,6 +26,8 @@
 #endif
 
 #include <stdio.h>
+#include <string.h>
+
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gdk-pixbuf/gdk-pixbuf-loader.h>
 #ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H
@@ -38,6 +40,9 @@
 
 #define d(x) 
 
+/* fixed-point scale factor for scaled images in cache */
+#define EMIS_SCALE (1024)
+
 struct _emis_cache_node {
 	EMCacheNode node;
 
@@ -98,8 +103,7 @@ em_icon_stream_init (CamelObject *object
 {
 	EMIconStream *emis = (EMIconStream *)object;
 
-	emis->width = 24;
-	emis->height = 24;
+	emis = emis;
 }
 
 static void
@@ -153,13 +157,47 @@ emis_sync_flush(CamelStream *stream)
 	return 0;
 }
 
+static GdkPixbuf *
+emis_fit(GdkPixbuf *pixbuf, int maxwidth, int maxheight, int *scale)
+{
+	GdkPixbuf *mini = NULL;
+	int width, height;
+
+	width = gdk_pixbuf_get_width(pixbuf);
+	height = gdk_pixbuf_get_height(pixbuf);
+
+	if ((maxwidth && width > maxwidth)
+	    || (maxheight && height > maxheight)) {
+		if (width >= height) {
+			if (scale)
+				*scale = maxwidth * EMIS_SCALE / width;
+			height = height * maxwidth / width;
+			width = maxwidth;
+		} else {
+			if (scale)
+				*scale = maxheight * EMIS_SCALE / height;
+			width = width * maxheight / height;
+			height = maxheight;
+		}
+
+#ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H
+		mini = gnome_thumbnail_scale_down_pixbuf(pixbuf, width, height);
+#else
+		mini = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
+#endif
+	}
+
+	return mini;
+}
+
 static int
 emis_sync_close(CamelStream *stream)
 {
 	EMIconStream *emis = (EMIconStream *)stream;
-	int width, height, ratio;
 	GdkPixbuf *pixbuf, *mini;
 	struct _emis_cache_node *node;
+	char *scalekey;
+	int scale;
 
 	if (emis->loader == NULL)
 		return -1;
@@ -173,39 +211,22 @@ emis_sync_close(CamelStream *stream)
 		return -1;
 	}
 
-	width = gdk_pixbuf_get_width(pixbuf);
-	height = gdk_pixbuf_get_height(pixbuf);
-
-	if (width != emis->width || height != emis->height) {
-		if (width >= height) {
-			if (width > emis->width) {
-				ratio = width / emis->width;
-				width = emis->width;
-				height /= ratio;
-			}
-		} else {
-			if (height > emis->height) {
-				ratio = height / emis->height;
-				height = emis->height;
-				width /= ratio;
-			}
-		}
+	mini = emis_fit(pixbuf, emis->width, emis->height, &scale);
+	gtk_image_set_from_pixbuf(emis->image, mini?mini:pixbuf);
 
-#ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H
-		mini = gnome_thumbnail_scale_down_pixbuf (pixbuf, width, height);
-#else
-		mini = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
-#endif
-		gtk_image_set_from_pixbuf(emis->image, mini);
-		pixbuf = mini;
-	} else {
-		g_object_ref(pixbuf);
-		gtk_image_set_from_pixbuf(emis->image, pixbuf);
+	if (emis->keep) {
+		node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, emis->key);
+		node->pixbuf = g_object_ref(pixbuf);
+		em_cache_add(emis_cache, (EMCacheNode *)node);
 	}
 
-	node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, emis->key);
-	node->pixbuf = pixbuf;
-	em_cache_add(emis_cache, (EMCacheNode *)node);
+	if (!emis->keep || mini) {
+		scalekey = g_alloca(strlen(emis->key) + 20);
+		sprintf(scalekey, "%s.%x", emis->key, scale);
+		node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, scalekey);
+		node->pixbuf = mini?mini:g_object_ref(pixbuf);
+		em_cache_add(emis_cache, (EMCacheNode *)node);
+	}
 
 	g_object_unref(emis->loader);
 	emis->loader = NULL;
@@ -223,12 +244,15 @@ emis_image_destroy(struct _GtkImage *ima
 }
 
 CamelStream *
-em_icon_stream_new(GtkImage *image, const char *key)
+em_icon_stream_new(GtkImage *image, const char *key, unsigned int maxwidth, unsigned int maxheight, int keep)
 {
 	EMIconStream *new;
 
 	new = EM_ICON_STREAM(camel_object_new(EM_ICON_STREAM_TYPE));
+	new->width = maxwidth;
+	new->height = maxheight;
 	new->image = image;
+	new->keep = keep;
 	new->destroy_id = g_signal_connect(image, "destroy", G_CALLBACK(emis_image_destroy), new);
 	new->loader = gdk_pixbuf_loader_new();
 	new->key = g_strdup(key);
@@ -237,22 +261,77 @@ em_icon_stream_new(GtkImage *image, cons
 }
 
 GdkPixbuf *
-em_icon_stream_get_image(const char *key)
+em_icon_stream_get_image(const char *key, unsigned int maxwidth, unsigned int maxheight)
 {
 	struct _emis_cache_node *node;
 	GdkPixbuf *pb = NULL;
 
 	/* forces the cache to be setup if not */
-	em_icon_stream_get_type();
+	em_icon_stream_get_type();	
 
 	node = (struct _emis_cache_node *)em_cache_lookup(emis_cache, key);
 	if (node) {
+		int width, height;
+
 		pb = node->pixbuf;
 		g_object_ref(pb);
 		em_cache_node_unref(emis_cache, (EMCacheNode *)node);
+
+		width = gdk_pixbuf_get_width(pb);
+		height = gdk_pixbuf_get_height(pb);
+
+		if ((maxwidth && width > maxwidth)
+		    || (maxheight && height > maxheight)) {
+			unsigned int scale;
+			char *realkey;
+
+			if (width >= height)
+				scale = width * EMIS_SCALE / maxwidth;
+			else
+				scale = height * EMIS_SCALE / maxheight;
+
+			realkey = g_alloca(strlen(key)+20);
+			sprintf(realkey, "%s.%x", key, scale);
+			node = (struct _emis_cache_node *)em_cache_lookup(emis_cache, realkey);
+			if (node) {
+				g_object_unref(pb);
+				pb = node->pixbuf;
+				g_object_ref(pb);
+				em_cache_node_unref(emis_cache, (EMCacheNode *)node);
+			} else {
+				GdkPixbuf *mini = emis_fit(pb, maxwidth, maxheight, NULL);
+
+				g_object_unref(pb);
+				pb = mini;
+				node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, realkey);
+				node->pixbuf = pb;
+				g_object_ref(pb);
+				em_cache_add(emis_cache, (EMCacheNode *)node);
+			}
+		}
 	}
 
 	return pb;
+}
+
+int
+em_icon_stream_is_resized(const char *key, unsigned int maxwidth, unsigned int maxheight)
+{
+	int res = FALSE;
+	struct _emis_cache_node *node;
+	
+	/* forces the cache to be setup if not */
+	em_icon_stream_get_type();
+
+	node = (struct _emis_cache_node *)em_cache_lookup(emis_cache, key);
+	if (node) {
+		res = (maxwidth && gdk_pixbuf_get_width(node->pixbuf) > maxwidth)
+			|| (maxheight && gdk_pixbuf_get_width(node->pixbuf) > maxheight);
+
+		em_cache_node_unref(emis_cache, (EMCacheNode *)node);
+	}
+
+	return res;
 }
 
 void
Index: mail/em-icon-stream.h
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-icon-stream.h,v
retrieving revision 1.4
diff -u -p -r1.4 em-icon-stream.h
--- mail/em-icon-stream.h	16 May 2005 07:53:53 -0000	1.4
+++ mail/em-icon-stream.h	6 Jul 2005 03:25:42 -0000
@@ -46,6 +46,8 @@ typedef struct _EMIconStream {
 	struct _GdkPixbufLoader *loader;
 	struct _GtkImage *image;
 	char *key;
+
+	int keep:1;
 } EMIconStream;
 
 typedef struct {
@@ -53,9 +55,11 @@ typedef struct {
 } EMIconStreamClass;
 
 CamelType    em_icon_stream_get_type (void);
+CamelStream *em_icon_stream_new(GtkImage *image, const char *key, unsigned int maxwidth, unsigned int maxheight, int keep);
+
+struct _GdkPixbuf *em_icon_stream_get_image(const char *key, unsigned int maxwidth, unsigned int maxheight);
+int em_icon_stream_is_resized(const char *key, unsigned int maxwidth, unsigned int maxheight);
 
-CamelStream *em_icon_stream_new(GtkImage *image, const char *key);
-struct _GdkPixbuf *em_icon_stream_get_image(const char *key);
 void em_icon_stream_clear_cache(void);
 
 #ifdef __cplusplus


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