The big eye-candy commit.

Make thumbnails out of image attachments.

svn path=/trunk/; revision=5648
This commit is contained in:
Iain Holmes
2000-10-01 03:43:17 +00:00
parent 711bef6798
commit 9d6c76ce37
9 changed files with 3042 additions and 42 deletions

View File

@ -1,3 +1,20 @@
2000-10-01 Iain Holmes <iain@helixcode.com>
* e-msg-composer-attachment-bar.[ch] (update): If the attachment is
an image, then make a thumbnail for it.
Base the attachment bar on e-icon-list instead of gnome-icon-list.
* e-icon-list.[ch]: New files. These are modified versions of
gnome-icon-list from gnome-libs HEAD that uses gdk-pixbuf instead
of the evil Imlib.
* e-msg-composer-attachment.[ch]: Add a pixbuf_cache member, to
save us having to generate a thumbnail for the attachment every
time the bar changes.
* e-msg-composer.c (e_msg_composer_construct): Add dnd support for
files. Drag a file to the composer to add it as an attachment.
2000-09-28 Jeffrey Stedfast <fejj@helixcode.com>
* e-msg-composer.c (build_message): Check to see if the body has

View File

@ -64,7 +64,9 @@ libcomposer_la_SOURCES = \
e-msg-composer-select-file.c \
e-msg-composer-select-file.h \
e-msg-composer.c \
e-msg-composer.h
e-msg-composer.h \
e-icon-list.c \
e-icon-list.h
EXTRA_DIST = \
$(glade_DATA) \

2657
composer/e-icon-list.c Normal file

File diff suppressed because it is too large Load Diff

178
composer/e-icon-list.h Normal file
View File

@ -0,0 +1,178 @@
/*
* Copyright (C) 1998, 1999 Free Software Foundation
* Copyright (C) 2000 Red Hat, Inc.
* All rights reserved.
*
* This file is part of the Gnome Library.
*
* The Gnome 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.
*
* The Gnome 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 the Gnome Library; see the file COPYING.LIB. If not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
@NOTATION@
*/
/* GnomeIconList widget - scrollable icon list
*
*
* Authors:
* Federico Mena <federico@nuclecu.unam.mx>
* Miguel de Icaza <miguel@nuclecu.unam.mx>
*/
#ifndef _E_ICON_LIST_H_
#define _E_ICON_LIST_H_
#include <libgnome/gnome-defs.h>
#include <libgnomeui/gnome-canvas.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
BEGIN_GNOME_DECLS
#define E_TYPE_ICON_LIST (e_icon_list_get_type ())
#define E_ICON_LIST(obj) (GTK_CHECK_CAST ((obj), E_TYPE_ICON_LIST, EIconList))
#define E_ICON_LIST_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_ICON_LIST, EIconListClass))
#define E_IS_ICON_LIST(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_ICON_LIST))
#define E_IS_ICON_LIST_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), E_TYPE_ICON_LIST))
#define E_ICON_LIST_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), E_TYPE_ICON_LIST, EIconListClass))
typedef struct _EIconList EIconList;
typedef struct _EIconListPrivate EIconListPrivate;
typedef struct _EIconListClass EIconListClass;
typedef enum {
E_ICON_LIST_ICONS,
E_ICON_LIST_TEXT_BELOW,
E_ICON_LIST_TEXT_RIGHT
} EIconListMode;
/* This structure has been converted to use public and private parts. To avoid
* breaking binary compatibility, the slots for private fields have been
* replaced with padding. Please remove these fields when gnome-libs has
* reached another major version and it is "fine" to break binary compatibility.
*/
struct _EIconList {
GnomeCanvas canvas;
/*< private >*/
EIconListPrivate * _priv;
};
struct _EIconListClass {
GnomeCanvasClass parent_class;
void (*select_icon) (EIconList *gil, gint num, GdkEvent *event);
void (*unselect_icon) (EIconList *gil, gint num, GdkEvent *event);
gboolean (*text_changed) (EIconList *gil, gint num, const char *new_text);
};
enum {
E_ICON_LIST_IS_EDITABLE = 1 << 0,
E_ICON_LIST_STATIC_TEXT = 1 << 1
};
guint e_icon_list_get_type (void) G_GNUC_CONST;
GtkWidget *e_icon_list_new (guint icon_width,
int flags);
void e_icon_list_construct (EIconList *gil,
guint icon_width,
int flags);
/* To avoid excesive recomputes during insertion/deletion */
void e_icon_list_freeze (EIconList *gil);
void e_icon_list_thaw (EIconList *gil);
void e_icon_list_insert (EIconList *gil,
int idx,
const char *icon_filename,
const char *text);
void e_icon_list_insert_pixbuf (EIconList *gil,
int idx,
GdkPixbuf *im,
const char *icon_filename,
const char *text);
int e_icon_list_append (EIconList *gil,
const char *icon_filename,
const char *text);
int e_icon_list_append_pixbuf (EIconList *gil,
GdkPixbuf *im,
const char *icon_filename,
const char *text);
void e_icon_list_clear (EIconList *gil);
void e_icon_list_remove (EIconList *gil,
int idx);
guint e_icon_list_get_num_icons (EIconList *gil);
/* Managing the selection */
void e_icon_list_set_selection_mode (EIconList *gil,
GtkSelectionMode mode);
void e_icon_list_select_icon (EIconList *gil,
int idx);
void e_icon_list_unselect_icon (EIconList *gil,
int idx);
int e_icon_list_unselect_all (EIconList *gil);
GList * e_icon_list_get_selection (EIconList *gil);
/* Setting the spacing values */
void e_icon_list_set_icon_width (EIconList *gil,
int w);
void e_icon_list_set_row_spacing (EIconList *gil,
int pixels);
void e_icon_list_set_col_spacing (EIconList *gil,
int pixels);
void e_icon_list_set_text_spacing (EIconList *gil,
int pixels);
void e_icon_list_set_icon_border (EIconList *gil,
int pixels);
void e_icon_list_set_separators (EIconList *gil,
const char *sep);
/* Icon filename. */
gchar * e_icon_list_get_icon_filename (EIconList *gil,
int idx);
int e_icon_list_find_icon_from_filename (EIconList *gil,
const char *filename);
/* Attaching information to the icons */
void e_icon_list_set_icon_data (EIconList *gil,
int idx, gpointer data);
void e_icon_list_set_icon_data_full (EIconList *gil,
int idx, gpointer data,
GtkDestroyNotify destroy);
int e_icon_list_find_icon_from_data (EIconList *gil,
gpointer data);
gpointer e_icon_list_get_icon_data (EIconList *gil,
int idx);
/* Visibility */
void e_icon_list_moveto (EIconList *gil,
int idx, double yalign);
GtkVisibility e_icon_list_icon_is_visible (EIconList *gil,
int idx);
int e_icon_list_get_icon_at (EIconList *gil,
int x, int y);
int e_icon_list_get_items_per_line (EIconList *gil);
END_GNOME_DECLS
#endif /* _GNOME_ICON_LIST_H_ */

View File

@ -24,11 +24,16 @@
#include <gnome.h>
#include <glade/glade.h>
#include <libgnomevfs/gnome-vfs-mime-info.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gdk-pixbuf/gdk-pixbuf-loader.h>
#include <gdk-pixbuf/gnome-canvas-pixbuf.h>
#include "e-msg-composer-attachment.h"
#include "e-msg-composer-attachment-bar.h"
#include "e-icon-list.h"
#include "camel/camel-data-wrapper.h"
#include "camel/camel-stream-fs.h"
#include "camel/camel-stream-mem.h"
#include "camel/camel-mime-part.h"
@ -41,7 +46,7 @@
#define ICON_TEXT_SPACING 2
static GnomeIconListClass *parent_class = NULL;
static EIconListClass *parent_class = NULL;
struct _EMsgComposerAttachmentBarPrivate {
GList *attachments;
@ -173,26 +178,28 @@ static void
update (EMsgComposerAttachmentBar *bar)
{
EMsgComposerAttachmentBarPrivate *priv;
GnomeIconList *icon_list;
EIconList *icon_list;
GList *p;
priv = bar->priv;
icon_list = GNOME_ICON_LIST (bar);
icon_list = E_ICON_LIST (bar);
gnome_icon_list_freeze (icon_list);
e_icon_list_freeze (icon_list);
gnome_icon_list_clear (icon_list);
e_icon_list_clear (icon_list);
/* FIXME could be faster, but we don't care. */
for (p = priv->attachments; p != NULL; p = p->next) {
EMsgComposerAttachment *attachment;
const gchar *icon_name, *desc;
gchar *size_string, *label, *mime_type;
GMimeContentField *content_type;
GdkPixbuf *pixbuf;
gboolean image = FALSE;
attachment = p->data;
content_type = camel_mime_part_get_content_type (attachment->body);
mime_type = g_strdup_printf ("%s/%s", content_type->type,
content_type->subtype);
icon_name = gnome_vfs_mime_get_value (mime_type,
@ -204,12 +211,90 @@ update (EMsgComposerAttachmentBar *bar)
icon_name = gnome_vfs_mime_get_value ("text/plain",
"icon-filename");
/* Get the image out of the attachment
and create a thumbnail for it */
if (strcmp (content_type->type, "image") == 0)
image = TRUE;
else
image = FALSE;
if (image && attachment->pixbuf_cache == NULL) {
CamelDataWrapper *wrapper;
CamelStream *mstream;
GdkPixbufLoader *loader;
gboolean error = TRUE;
char tmp[4096];
int t;
wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body));
mstream = camel_stream_mem_new ();
camel_data_wrapper_write_to_stream (wrapper, mstream);
camel_stream_reset (mstream);
/* Stream image into pixbuf loader */
loader = gdk_pixbuf_loader_new ();
do {
t = camel_stream_read (mstream, tmp, 4096);
if (t > 0) {
error = !gdk_pixbuf_loader_write (loader,
tmp, t);
if (error) {
break;
}
} else {
if (camel_stream_eos (mstream))
break;
error = TRUE;
break;
}
} while (!camel_stream_eos (mstream));
if (!error) {
int ratio, width, height;
/* Shrink pixbuf */
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
if (width >= height) {
if (width > 48) {
ratio = width / 48;
width = 48;
height = height / ratio;
}
} else {
if (height > 48) {
ratio = height / 48;
height = 48;
width = width / ratio;
}
}
attachment->pixbuf_cache = gdk_pixbuf_scale_simple
(pixbuf,
width,
height,
GDK_INTERP_BILINEAR);
} else {
g_warning ("GdkPixbufLoader Error");
image = FALSE;
}
/* Destroy everything */
gdk_pixbuf_loader_close (loader);
gtk_object_destroy (GTK_OBJECT (loader));
camel_stream_close (mstream);
}
desc = camel_mime_part_get_description (attachment->body);
if (!desc)
desc = camel_mime_part_get_filename (attachment->body);
if (!desc)
desc = "attachment";
if (attachment->size) {
size_string = size_to_string (attachment->size);
label = g_strdup_printf ("%s (%s)", desc, size_string);
@ -217,30 +302,36 @@ update (EMsgComposerAttachmentBar *bar)
} else
label = g_strdup (desc);
gnome_icon_list_append (icon_list, icon_name, label);
if (image) {
e_icon_list_append_pixbuf (icon_list, attachment->pixbuf_cache, icon_name, label);
} else {
e_icon_list_append (icon_list, icon_name, label);
}
g_free (label);
}
gnome_icon_list_thaw (icon_list);
e_icon_list_thaw (icon_list);
}
static void
remove_selected (EMsgComposerAttachmentBar *bar)
{
GnomeIconList *icon_list;
EIconList *icon_list;
EMsgComposerAttachment *attachment;
GList *attachment_list;
GList *p;
gint num;
icon_list = GNOME_ICON_LIST (bar);
icon_list = E_ICON_LIST (bar);
/* Weee! I am especially proud of this piece of cheesy code: it is
truly awful. But unless one attaches a huge number of files, it
will not be as greedy as intended. FIXME of course. */
attachment_list = NULL;
for (p = icon_list->selection; p != NULL; p = p->next) {
p = e_icon_list_get_selection (icon_list);
for (; p != NULL; p = p->next) {
num = GPOINTER_TO_INT (p->data);
attachment = E_MSG_COMPOSER_ATTACHMENT
(g_list_nth (bar->priv->attachments, num)->data);
@ -258,13 +349,15 @@ remove_selected (EMsgComposerAttachmentBar *bar)
static void
edit_selected (EMsgComposerAttachmentBar *bar)
{
GnomeIconList *icon_list;
EIconList *icon_list;
EMsgComposerAttachment *attachment;
GList *selection;
gint num;
icon_list = GNOME_ICON_LIST (bar);
icon_list = E_ICON_LIST (bar);
num = GPOINTER_TO_INT (icon_list->selection->data);
selection = e_icon_list_get_selection (icon_list);
num = GPOINTER_TO_INT (selection->data);
attachment = g_list_nth (bar->priv->attachments, num)->data;
e_msg_composer_attachment_edit (attachment, GTK_WIDGET (bar));
@ -378,7 +471,7 @@ get_icon_context_menu (EMsgComposerAttachmentBar *bar)
priv = bar->priv;
if (priv->icon_context_menu == NULL)
priv->icon_context_menu = gnome_popup_menu_new
(icon_context_menu_info);
(icon_context_menu_info);
return priv->icon_context_menu;
}
@ -447,21 +540,21 @@ button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
EMsgComposerAttachmentBar *bar;
GnomeIconList *icon_list;
EIconList *icon_list;
gint icon_number;
bar = E_MSG_COMPOSER_ATTACHMENT_BAR (widget);
icon_list = GNOME_ICON_LIST (widget);
icon_list = E_ICON_LIST (widget);
if (event->button != 3)
return GTK_WIDGET_CLASS (parent_class)->button_press_event
(widget, event);
(widget, event);
icon_number = gnome_icon_list_get_icon_at (icon_list,
event->x, event->y);
icon_number = e_icon_list_get_icon_at (icon_list,
event->x, event->y);
if (icon_number >= 0) {
gnome_icon_list_select_icon (icon_list, icon_number);
e_icon_list_select_icon (icon_list, icon_number);
popup_icon_context_menu (bar, icon_number, event);
} else {
popup_context_menu (bar, event);
@ -478,13 +571,13 @@ class_init (EMsgComposerAttachmentBarClass *class)
{
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
GnomeIconListClass *icon_list_class;
EIconListClass *icon_list_class;
object_class = GTK_OBJECT_CLASS (class);
widget_class = GTK_WIDGET_CLASS (class);
icon_list_class = GNOME_ICON_LIST_CLASS (class);
icon_list_class = E_ICON_LIST_CLASS (class);
parent_class = gtk_type_class (gnome_icon_list_get_type ());
parent_class = gtk_type_class (e_icon_list_get_type ());
object_class->destroy = destroy;
@ -546,7 +639,7 @@ e_msg_composer_attachment_bar_get_type (void)
(GtkClassInitFunc) NULL,
};
type = gtk_type_unique (gnome_icon_list_get_type (), &info);
type = gtk_type_unique (e_icon_list_get_type (), &info);
}
return type;
@ -556,24 +649,25 @@ GtkWidget *
e_msg_composer_attachment_bar_new (GtkAdjustment *adj)
{
EMsgComposerAttachmentBar *new;
GnomeIconList *icon_list;
EIconList *icon_list;
gtk_widget_push_visual (gdk_imlib_get_visual ());
gtk_widget_push_colormap (gdk_imlib_get_colormap ());
gdk_rgb_init ();
gtk_widget_push_visual (gdk_rgb_get_visual ());
gtk_widget_push_colormap (gdk_rgb_get_cmap ());
new = gtk_type_new (e_msg_composer_attachment_bar_get_type ());
gtk_widget_pop_visual ();
gtk_widget_pop_colormap ();
icon_list = GNOME_ICON_LIST (new);
icon_list = E_ICON_LIST (new);
gnome_icon_list_construct (icon_list, ICON_WIDTH, adj, 0);
e_icon_list_construct (icon_list, ICON_WIDTH, 0);
gnome_icon_list_set_separators (icon_list, ICON_SEPARATORS);
gnome_icon_list_set_row_spacing (icon_list, ICON_ROW_SPACING);
gnome_icon_list_set_col_spacing (icon_list, ICON_COL_SPACING);
gnome_icon_list_set_icon_border (icon_list, ICON_BORDER);
gnome_icon_list_set_text_spacing (icon_list, ICON_TEXT_SPACING);
gnome_icon_list_set_selection_mode (icon_list, GTK_SELECTION_MULTIPLE);
e_icon_list_set_separators (icon_list, ICON_SEPARATORS);
e_icon_list_set_row_spacing (icon_list, ICON_ROW_SPACING);
e_icon_list_set_col_spacing (icon_list, ICON_COL_SPACING);
e_icon_list_set_icon_border (icon_list, ICON_BORDER);
e_icon_list_set_text_spacing (icon_list, ICON_TEXT_SPACING);
e_icon_list_set_selection_mode (icon_list, GTK_SELECTION_MULTIPLE);
return GTK_WIDGET (new);
}

View File

@ -25,6 +25,7 @@
#define __E_MSG_COMPOSER_ATTACHMENT_BAR_H__
#include <gnome.h>
#include "e-icon-list.h"
#include <camel/camel-multipart.h>
#ifdef __cplusplus
@ -47,14 +48,14 @@ extern "C" {
typedef struct _EMsgComposerAttachmentBarPrivate EMsgComposerAttachmentBarPrivate;
struct _EMsgComposerAttachmentBar {
GnomeIconList parent;
EIconList parent;
EMsgComposerAttachmentBarPrivate *priv;
};
typedef struct _EMsgComposerAttachmentBar EMsgComposerAttachmentBar;
struct _EMsgComposerAttachmentBarClass {
GnomeIconListClass parent_class;
EIconListClass parent_class;
void (* changed) (EMsgComposerAttachmentBar *bar);
};

View File

@ -84,6 +84,8 @@ destroy (GtkObject *object)
attachment = E_MSG_COMPOSER_ATTACHMENT (object);
camel_object_unref (CAMEL_OBJECT (attachment->body));
if (attachment->pixbuf_cache != NULL)
gdk_pixbuf_unref (attachment->pixbuf_cache);
}
@ -129,6 +131,7 @@ init (EMsgComposerAttachment *msg_composer_attachment)
msg_composer_attachment->editor_gui = NULL;
msg_composer_attachment->body = NULL;
msg_composer_attachment->size = 0;
msg_composer_attachment->pixbuf_cache = NULL;
}
GtkType

View File

@ -26,6 +26,7 @@
#include <gnome.h>
#include <glade/glade-xml.h>
#include <camel/camel-mime-part.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#ifdef __cplusplus
extern "C" {
@ -50,6 +51,8 @@ struct _EMsgComposerAttachment {
CamelMimePart *body;
gboolean guessed_type;
gulong size;
GdkPixbuf *pixbuf_cache;
};
struct _EMsgComposerAttachmentClass {

View File

@ -1166,6 +1166,39 @@ delete_event (GtkWidget *widget,
return TRUE;
}
static void
drag_data_received (EMsgComposer *composer,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection,
guint info,
guint time)
{
gchar *temp, *filename;
filename = g_strdup (selection->data);
temp = strchr (filename, '\n');
if (temp) {
if (*(temp - 1) == '\r')
*(temp - 1) = '\0';
*temp = '\0';
}
/* Chop the file: part off */
if (strncasecmp (filename, "file:", 5) == 0) {
temp = g_strdup (filename + 5);
g_free (filename);
filename = temp;
}
e_msg_composer_attachment_bar_attach
(E_MSG_COMPOSER_ATTACHMENT_BAR (composer->attachment_bar),
filename);
g_free (filename);
}
static void
class_init (EMsgComposerClass *klass)
@ -1259,7 +1292,11 @@ e_msg_composer_construct (EMsgComposer *composer)
{
GtkWidget *vbox;
BonoboObject *editor_server;
static GtkTargetEntry drop_types[] = {
{"text/uri-list", 0, 1}
};
g_return_if_fail (gtk_main_level () > 0);
gtk_window_set_default_size (GTK_WINDOW (composer),
@ -1268,6 +1305,12 @@ e_msg_composer_construct (EMsgComposer *composer)
bonobo_win_construct (BONOBO_WIN (composer), "e-msg-composer",
_("Compose a message"));
/* DND support */
gtk_drag_dest_set (GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL,
drop_types, 1, GDK_ACTION_COPY);
gtk_signal_connect (GTK_OBJECT (composer), "drag_data_received",
GTK_SIGNAL_FUNC (drag_data_received), NULL);
composer->uih = bonobo_ui_handler_new ();
bonobo_ui_handler_set_app (composer->uih, BONOBO_WIN (composer));
@ -1407,6 +1450,8 @@ e_msg_composer_new_with_message (CamelMimeMessage *msg)
char *text, *final_text;
guint len, i;
g_return_val_if_fail (gtk_main_level () > 0, NULL);
new = create_composer ();
if (!new)
return NULL;