commit 2f561bc58f1e94add4f32594eb31a7b3f6c7d497
Author: Matthew Barnes <mbarnes@redhat.com>
Date:   Fri May 29 22:04:51 2009 -0400

    Initial cut at a picture gallery feature for the composer.

diff --git a/composer/Makefile.am b/composer/Makefile.am
index 97debe5..698b22f 100644
--- a/composer/Makefile.am
+++ b/composer/Makefile.am
@@ -52,7 +52,6 @@ libcomposer_la_SOURCES = 			\
 	e-composer-text-header.c		\
 	e-msg-composer.c
 
-
 uidir = $(evolutionuidir)
 ui_DATA = evolution-composer.ui
 
diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c
index 3b40d27..55c7d3b 100644
--- a/composer/e-composer-actions.c
+++ b/composer/e-composer-actions.c
@@ -415,6 +415,14 @@ static GtkToggleActionEntry toggle_entries[] = {
 	  G_CALLBACK (action_pgp_sign_cb),
 	  FALSE },
 
+	{ "picture-gallery",
+	  "emblem-photos",
+	  N_("_Picture Gallery"),
+	  NULL,
+	  N_("Show a collection of pictures that you can drag to your message"),
+	  NULL,  /* no callback */
+	  FALSE },
+
 	{ "prioritize-message",
 	  NULL,
 	  N_("_Prioritize Message"),
@@ -518,8 +526,15 @@ e_composer_actions_init (EMsgComposer *composer)
 		ACTION (ATTACH), "short-label", _("Attach"), NULL);
 
 	g_object_set (
+		ACTION (PICTURE_GALLERY), "is-important", TRUE, NULL);
+
+	g_object_set (
 		ACTION (SAVE_DRAFT), "short-label", _("Save Draft"), NULL);
 
+	e_binding_new (
+		G_OBJECT (composer), "html-mode",
+		G_OBJECT (ACTION (PICTURE_GALLERY)), "sensitive");
+
 #if defined (HAVE_NSS)
 	visible = TRUE;
 #else
diff --git a/composer/e-composer-actions.h b/composer/e-composer-actions.h
index 5d49cde..e2d7cb4 100644
--- a/composer/e-composer-actions.h
+++ b/composer/e-composer-actions.h
@@ -29,6 +29,8 @@
 	E_COMPOSER_ACTION ((composer), "pgp-encrypt")
 #define E_COMPOSER_ACTION_PGP_SIGN(composer) \
 	E_COMPOSER_ACTION ((composer), "pgp-sign")
+#define E_COMPOSER_ACTION_PICTURE_GALLERY(composer) \
+	E_COMPOSER_ACTION ((composer), "picture-gallery")
 #define E_COMPOSER_ACTION_PRINT(composer) \
 	E_COMPOSER_ACTION ((composer), "print")
 #define E_COMPOSER_ACTION_PRINT_PREVIEW(composer) \
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index 2a2cea6..79e787e 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -17,6 +17,10 @@
 
 #include "e-composer-private.h"
 #include "e-util/e-util-private.h"
+#include "e-util/e-binding.h"
+
+/* Initial height of the picture gallery. */
+#define GALLERY_INITIAL_HEIGHT 150
 
 static void
 composer_setup_charset_menu (EMsgComposer *composer)
@@ -78,6 +82,26 @@ composer_setup_recent_menu (EMsgComposer *composer)
 	gtk_ui_manager_ensure_update (ui_manager);
 }
 
+static void
+composer_update_gallery_visibility (EMsgComposer *composer)
+{
+	GtkhtmlEditor *editor;
+	GtkToggleAction *toggle_action;
+	gboolean gallery_active;
+	gboolean html_mode;
+
+	editor = GTKHTML_EDITOR (composer);
+	html_mode = gtkhtml_editor_get_html_mode (editor);
+
+	toggle_action = GTK_TOGGLE_ACTION (ACTION (PICTURE_GALLERY));
+	gallery_active = gtk_toggle_action_get_active (toggle_action);
+
+	if (html_mode && gallery_active)
+		gtk_widget_show (composer->priv->gallery_scrolled_window);
+	else
+		gtk_widget_hide (composer->priv->gallery_scrolled_window);
+}
+
 void
 e_composer_private_init (EMsgComposer *composer)
 {
@@ -213,34 +237,69 @@ e_composer_private_init (EMsgComposer *composer)
 
 	widget = e_composer_header_table_new ();
 	gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
-	gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 	if (composer->lite)
-		gtk_box_reorder_child (GTK_BOX (editor->vbox), widget, 0);
+		gtk_box_reorder_child (GTK_BOX (container), widget, 0);
 	else
-		gtk_box_reorder_child (GTK_BOX (editor->vbox), widget, 2);
-
+		gtk_box_reorder_child (GTK_BOX (container), widget, 2);
 	priv->header_table = g_object_ref (widget);
 	gtk_widget_show (widget);
 
 	/* Construct the attachment paned. */
 
+	container = editor->vbox;
+
 	widget = e_attachment_paned_new ();
 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
 	priv->attachment_paned = g_object_ref (widget);
 	gtk_widget_show (widget);
 
-	g_object_set_data ((GObject *)composer, "vbox", editor->vbox);
+	container = e_attachment_paned_get_content_area (
+		E_ATTACHMENT_PANED (priv->attachment_paned));
+
+	widget = gtk_vpaned_new ();
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (
+		GTK_SCROLLED_WINDOW (widget),
+		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (
+		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+	gtk_widget_set_size_request (widget, -1, GALLERY_INITIAL_HEIGHT);
+	gtk_paned_pack1 (GTK_PANED (container), widget, FALSE, FALSE);
+	priv->gallery_scrolled_window = g_object_ref (widget);
+	gtk_widget_show (widget);
 
 	/* Reparent the scrolled window containing the GtkHTML widget
 	 * into the content area of the top attachment pane. */
 
 	widget = GTK_WIDGET (gtkhtml_editor_get_html (editor));
 	widget = gtk_widget_get_parent (widget);
-	container = e_attachment_paned_get_content_area (
-		E_ATTACHMENT_PANED (priv->attachment_paned));
 	gtk_widget_reparent (widget, container);
-	gtk_box_set_child_packing (
-		GTK_BOX (container), widget, TRUE, TRUE, 0, GTK_PACK_START);
+
+	/* Construct the picture gallery. */
+
+	container = priv->gallery_scrolled_window;
+
+	widget = e_picture_gallery_new ();
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	priv->gallery_icon_view = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	g_signal_connect (
+		composer, "notify::html-mode",
+		G_CALLBACK (composer_update_gallery_visibility), NULL);
+
+	g_signal_connect_swapped (
+		ACTION (PICTURE_GALLERY), "toggled",
+		G_CALLBACK (composer_update_gallery_visibility), composer);
+
+	/* XXX What is this for? */
+	g_object_set_data (G_OBJECT (composer), "vbox", editor->vbox);
 
 	composer_setup_recent_menu (composer);
 
@@ -316,6 +375,11 @@ e_composer_private_dispose (EMsgComposer *composer)
 		composer->priv->composer_actions = NULL;
 	}
 
+	if (composer->priv->gallery_scrolled_window != NULL) {
+		g_object_unref (composer->priv->gallery_scrolled_window);
+		composer->priv->gallery_scrolled_window = NULL;
+	}
+
 	g_hash_table_remove_all (composer->priv->inline_images);
 	g_hash_table_remove_all (composer->priv->inline_images_by_url);
 
diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h
index 49d9ae6..a5c720c 100644
--- a/composer/e-composer-private.h
+++ b/composer/e-composer-private.h
@@ -32,6 +32,7 @@
 #include "e-util/gconf-bridge.h"
 #include "widgets/misc/e-attachment-paned.h"
 #include "widgets/misc/e-attachment-store.h"
+#include "widgets/misc/e-picture-gallery.h"
 
 #define E_MSG_COMPOSER_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -84,7 +85,6 @@ struct _EMsgComposerPrivate {
 
 	/*** UI Management ***/
 
-	GtkWidget *html_editor;
 	GtkWidget *header_table;
 	GtkWidget *attachment_paned;
 
@@ -96,6 +96,9 @@ struct _EMsgComposerPrivate {
 
 	GtkWidget *focused_entry;
 
+	GtkWidget *gallery_icon_view;
+	GtkWidget *gallery_scrolled_window;
+
 	GtkWidget *address_dialog;
 
 	GHashTable *inline_images;
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index a7a2dfa..c756b24 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -346,7 +346,9 @@ set_recipients_from_destv (CamelMimeMessage *msg,
 	if (camel_address_length (CAMEL_ADDRESS (to_addr)) > 0) {
 		camel_mime_message_set_recipients (msg, header, to_addr);
 	} else if (seen_hidden_list) {
-		camel_medium_set_header (CAMEL_MEDIUM (msg), header, "Undisclosed-Recipient:;");
+		camel_medium_set_header (
+			CAMEL_MEDIUM (msg),
+			header, (gpointer) "Undisclosed-Recipient:;");
 	}
 
 	header = redirect ? CAMEL_RECIPIENT_TYPE_RESENT_CC : CAMEL_RECIPIENT_TYPE_CC;
@@ -1747,6 +1749,34 @@ msg_composer_drag_data_received (GtkWidget *widget,
 }
 
 static void
+msg_composer_gallery_drag_data_get (GtkIconView *icon_view,
+                                    GdkDragContext *context,
+                                    GtkSelectionData *selection_data,
+                                    guint target_type,
+                                    guint time)
+{
+	GtkTreePath *path;
+	GtkCellRenderer *cell;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gchar *str_data;
+
+	if (!gtk_icon_view_get_cursor (icon_view, &path, &cell))
+		return;
+
+	model = gtk_icon_view_get_model (icon_view);
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_model_get (model, &iter, 1, &str_data, -1);
+	gtk_tree_path_free (path);
+
+	/* only supports "text/uri-list" */
+	gtk_selection_data_set (
+		selection_data, selection_data->target, 8,
+		(guchar *) str_data, strlen (str_data));
+	g_free (str_data);
+}
+
+static void
 msg_composer_cut_clipboard (GtkhtmlEditor *editor)
 {
 	EMsgComposer *composer;
@@ -2187,6 +2217,10 @@ msg_composer_init (EMsgComposer *composer)
 
 	gtk_target_table_free (targets, n_targets);
 
+        g_signal_connect (
+		composer->priv->gallery_icon_view, "drag-data-get",
+                G_CALLBACK (msg_composer_gallery_drag_data_get), NULL);
+
 	/* Configure Headers */
 
 	e_composer_header_table_set_account_list (
diff --git a/composer/evolution-composer.ui b/composer/evolution-composer.ui
index 1e72d9e..d63dc15 100644
--- a/composer/evolution-composer.ui
+++ b/composer/evolution-composer.ui
@@ -32,6 +32,8 @@
         <menuitem action='view-reply-to'/>
         <menuitem action='view-cc'/>
         <menuitem action='view-bcc'/>
+        <separator/>
+        <menuitem action='picture-gallery'/>
       </menu>
     </placeholder>
     <menu action='insert-menu'>
@@ -61,4 +63,8 @@
       <separator/>
     </placeholder>
   </toolbar>
+  <toolbar name='html-toolbar'>
+    <separator/>
+    <toolitem action='picture-gallery'/>
+  </toolbar>
 </ui>
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index e40664b..b457cef 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -65,6 +65,7 @@ widgetsinclude_HEADERS =			\
 	e-map.h					\
 	e-multi-config-dialog.h			\
 	e-online-button.h			\
+	e-picture-gallery.h			\
 	e-search-bar.h				\
 	e-task-bar.h				\
 	e-task-widget.h				\
@@ -121,6 +122,7 @@ libemiscwidgets_la_SOURCES =			\
 	e-map.c					\
 	e-multi-config-dialog.c			\
 	e-online-button.c			\
+	e-picture-gallery.c			\
 	e-search-bar.c				\
 	e-task-bar.c				\
 	e-task-widget.c				\
diff --git a/widgets/misc/e-picture-gallery.c b/widgets/misc/e-picture-gallery.c
new file mode 100644
index 0000000..c51f40c
--- /dev/null
+++ b/widgets/misc/e-picture-gallery.c
@@ -0,0 +1,194 @@
+/*
+ * e-picture-gallery.c
+ *
+ * This program 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) version 3.
+ *
+ * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-picture-gallery.h"
+
+#include <libgnomeui/libgnomeui.h>
+
+#define E_PICTURE_GALLERY_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_PICTURE_GALLERY, EPictureGalleryPrivate))
+
+struct _EPictureGalleryPrivate {
+	gint placeholder;
+};
+
+static gpointer parent_class;
+
+static gboolean
+image_gallery_loading_func (EPictureGallery *gallery)
+{
+	GtkIconView *icon_view;
+	GError *error = 0;
+	GtkListStore* list_store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
+	GtkTreeIter iter;
+	GDir *dir;
+	const gchar *dirname;
+
+	icon_view = GTK_ICON_VIEW (gallery);
+
+	dirname = g_getenv ("EVOLUTION_GALLERY_PATH");
+	if (dirname == NULL)
+		dirname = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
+
+	dir = g_dir_open (dirname, 0, NULL);
+	if (dir) {
+		GnomeThumbnailFactory *factory;
+		const gchar *basename;
+
+		factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
+
+		while ((basename = g_dir_read_name(dir)) != NULL) {
+			GFile *file;
+			GFileInfo *file_info;
+			gchar *filename;
+			gchar *uri;
+
+			filename = g_build_filename (
+				dirname, basename, NULL);
+			file = g_file_new_for_path (filename);
+			g_free (filename);
+
+			uri = g_file_get_uri (file);
+
+			file_info = g_file_query_info(file,
+				G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE","G_FILE_ATTRIBUTE_TIME_MODIFIED","G_FILE_ATTRIBUTE_THUMBNAIL_PATH","G_FILE_ATTRIBUTE_THUMBNAILING_FAILED,
+				G_FILE_QUERY_INFO_NONE,
+				NULL,
+				&error);
+
+			if (file_info!=NULL) {
+				const gchar * mime_type;
+				time_t mtime;
+				if (g_file_info_get_attribute_boolean(file_info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED)) {
+					continue;
+				}
+				mime_type = g_file_info_get_attribute_string(file_info,
+					G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
+				mtime = g_file_info_get_attribute_uint64(file_info,
+					G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+				if (gnome_thumbnail_factory_can_thumbnail(factory, uri, mime_type, mtime)) {
+					const gchar * existing_thumb = g_file_info_get_attribute_byte_string(file_info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
+					GdkPixbuf* pixbuf;
+
+					if (existing_thumb==NULL) {
+						pixbuf = gnome_thumbnail_factory_generate_thumbnail(factory, uri, mime_type);
+						if (pixbuf) {
+							gnome_thumbnail_factory_save_thumbnail(factory, pixbuf, uri, mtime);
+						} else {
+							gnome_thumbnail_factory_create_failed_thumbnail(factory, uri, mtime);
+						}
+					} else {
+						pixbuf = gdk_pixbuf_new_from_file(existing_thumb, &error);
+					}
+
+					if (pixbuf) {
+						gtk_list_store_append (list_store, &iter);
+						gtk_list_store_set(list_store, &iter, 0, pixbuf, 1, uri, -1);
+						g_object_unref(pixbuf);
+					}
+				}
+				g_object_unref(file_info);
+			}
+			g_free(uri);
+			g_object_unref(file);
+		}
+
+		g_dir_close(dir);
+	}
+
+	gtk_icon_view_set_model(icon_view, GTK_TREE_MODEL (list_store));
+	g_object_unref (icon_view);
+	return FALSE;
+}
+
+static void
+picture_gallery_class_init (EPictureGalleryClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EPictureGalleryPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->finalize = picture_gallery_finalize;
+}
+
+static void
+picture_gallery_init (EPictureGallery *gallery)
+{
+	GtkIconView *icon_view;
+	GtkTargetEntry *targets;
+	GtkTargetList *list;
+	gint n_targets;
+
+	gallery->priv = E_PICTURE_GALLERY_GET_PRIVATE (gallery);
+
+	icon_view = GTK_ICON_VIEW (gallery);
+	gtk_icon_view_set_pixbuf_column (icon_view, 0);
+	gtk_icon_view_set_text_column (icon_view, -1);
+
+	list = gtk_target_list_new (NULL, 0);
+	gtk_target_list_add_uri_targets (list, 0);
+	targets = gtk_target_table_new_from_list (list, &n_targets);
+
+	gtk_icon_view_enable_model_drag_source (
+		icon_view, GDK_BUTTON1_MASK,
+		targets, n_targets, GDK_ACTION_COPY);
+
+	gtk_target_table_free (targets, n_targets);
+	gtk_target_list_unref (list);
+
+	g_idle_add ((GSourceFunc) image_gallery_loading_func, gallery);
+}
+
+GType
+e_picture_gallery_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EPictureGalleryClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) picture_gallery_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EPictureGallery),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) picture_gallery_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_ICON_VIEW, "EPictureGallery", &type_info, 0);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_picture_gallery_new (void)
+{
+	return g_object_new (E_TYPE_PICTURE_GALLERY, NULL);
+}
diff --git a/widgets/misc/e-picture-gallery.h b/widgets/misc/e-picture-gallery.h
new file mode 100644
index 0000000..818a72b
--- /dev/null
+++ b/widgets/misc/e-picture-gallery.h
@@ -0,0 +1,66 @@
+/*
+ * e-picture-gallery.h
+ *
+ * This program 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) version 3.
+ *
+ * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_PICTURE_GALLERY_H
+#define E_PICTURE_GALLERY_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_PICTURE_GALLERY \
+	(e_picture_gallery_get_type ())
+#define E_PICTURE_GALLERY(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_PICTURE_GALLERY, EPictureGallery))
+#define E_PICTURE_GALLERY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_PICTURE_GALLERY, EPictureGalleryClass))
+#define E_IS_PICTURE_GALLERY(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_PICTURE_GALLERY))
+#define E_IS_PICTURE_GALLERY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_PICTURE_GALLERY))
+#define E_PICTURE_GALLERY_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_PICTURE_GALLERY, EPictureGalleryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EPictureGallery EPictureGallery;
+typedef struct _EPictureGalleryClass EPictureGalleryClass;
+typedef struct _EPictureGalleryPrivate EPictureGalleryPrivate;
+
+struct _EPictureGallery {
+	GtkIconView parent;
+	EPictureGalleryPrivate *priv;
+};
+
+struct _EPictureGalleryClass {
+	GtkIconViewClass parent_class;
+};
+
+GType		e_picture_gallery_get_type	(void);
+GtkWidget *	e_picture_gallery_new		(void);
+
+G_END_DECLS
+
+#endif /* E_PICTURE_GALLERY_H */
