diff options
author | Matthew Barnes <mbarnes@src.gnome.org> | 2009-03-09 11:31:24 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@src.gnome.org> | 2009-03-09 11:31:24 +0800 |
commit | f963cc39a7d21f64f578dae50fd08c44181a3bf6 (patch) | |
tree | 7cdf0c0c9bab037272ba6fca48aebbccd4c0de74 /widgets | |
parent | 85d0142d21286ce87cd5f6c3d1e2f71aa994151f (diff) | |
download | gsoc2013-evolution-f963cc39a7d21f64f578dae50fd08c44181a3bf6.tar gsoc2013-evolution-f963cc39a7d21f64f578dae50fd08c44181a3bf6.tar.gz gsoc2013-evolution-f963cc39a7d21f64f578dae50fd08c44181a3bf6.tar.bz2 gsoc2013-evolution-f963cc39a7d21f64f578dae50fd08c44181a3bf6.tar.lz gsoc2013-evolution-f963cc39a7d21f64f578dae50fd08c44181a3bf6.tar.xz gsoc2013-evolution-f963cc39a7d21f64f578dae50fd08c44181a3bf6.tar.zst gsoc2013-evolution-f963cc39a7d21f64f578dae50fd08c44181a3bf6.zip |
Cleaning up the attachment bar, centralizing its popup menu, and converting
everything to GtkUIManager/GtkActions. Saving progress mid-stream... not
sure about the MIME part utilities yet.
Also, add some EActivity subclasses. Considering an EFileActivity subclass
for asynchronous GIO operations (loading/saving attachments, etc.), but still
ironing out details.
svn path=/branches/kill-bonobo/; revision=37389
Diffstat (limited to 'widgets')
-rw-r--r-- | widgets/misc/Makefile.am | 90 | ||||
-rw-r--r-- | widgets/misc/e-activity.c | 249 | ||||
-rw-r--r-- | widgets/misc/e-activity.h | 17 | ||||
-rw-r--r-- | widgets/misc/e-alert-activity.c | 254 | ||||
-rw-r--r-- | widgets/misc/e-alert-activity.h | 70 | ||||
-rw-r--r-- | widgets/misc/e-attachment-bar.c | 1982 | ||||
-rw-r--r-- | widgets/misc/e-attachment-bar.h | 135 | ||||
-rw-r--r-- | widgets/misc/e-attachment-dialog.c | 413 | ||||
-rw-r--r-- | widgets/misc/e-attachment-dialog.h | 73 | ||||
-rw-r--r-- | widgets/misc/e-attachment.c | 743 | ||||
-rw-r--r-- | widgets/misc/e-attachment.h | 116 | ||||
-rw-r--r-- | widgets/misc/e-mime-part-utils.c | 223 | ||||
-rw-r--r-- | widgets/misc/e-mime-part-utils.h | 46 | ||||
-rw-r--r-- | widgets/misc/e-timeout-activity.c | 180 | ||||
-rw-r--r-- | widgets/misc/e-timeout-activity.h | 71 |
15 files changed, 3449 insertions, 1213 deletions
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am index c422ae9972..ffbe7fa5fb 100644 --- a/widgets/misc/Makefile.am +++ b/widgets/misc/Makefile.am @@ -17,7 +17,6 @@ INCLUDES = \ privsolib_LTLIBRARIES = \ libemiscwidgets.la -# libefilterbar.la widgetsincludedir = $(privincludedir)/misc @@ -39,51 +38,55 @@ widgetsinclude_HEADERS = \ e-action-combo-box.h \ e-activity.h \ e-activity-proxy.h \ - e-attachment-bar.h \ + e-alert-activity.h \ e-attachment.h \ - e-spinner.c \ - e-spinner.h \ + e-attachment-bar.h \ + e-attachment-dialog.h \ e-calendar.h \ e-calendar-item.h \ + e-canvas.h \ + e-canvas-background.h \ + e-canvas-utils.h \ + e-canvas-vbox.h \ e-cell-date-edit.h \ e-cell-percent.h \ e-cell-renderer-combo.h \ e-charset-picker.h \ + e-colors.h \ e-combo-cell-editable.h \ + e-cursors.h \ e-dateedit.h \ e-expander.h \ + e-gui-utils.h \ + e-hsv-utils.h \ e-icon-entry.h \ e-image-chooser.h \ e-map.h \ e-menu-tool-button.h \ - e-popup-action.h \ - e-preferences-window.h \ + e-mime-part-utils.h \ e-online-button.h \ - e-search-bar.h \ - e-send-options.h \ - e-url-entry.h \ - e-canvas-background.h \ - e-canvas-utils.h \ - e-canvas-vbox.h \ - e-canvas.h \ - e-cursors.h \ - e-gui-utils.h \ - e-hsv-utils.h \ + e-popup-action.h \ e-popup-menu.h \ + e-preferences-window.h \ e-printable.h \ - e-reflow-model.h \ e-reflow.h \ + e-reflow-model.h \ + e-search-bar.h \ + e-selection-model.h \ e-selection-model-array.h \ e-selection-model-simple.h \ - e-selection-model.h \ + e-send-options.h \ e-signature-combo-box.h \ e-signature-editor.h \ e-signature-manager.h \ e-signature-preview.h \ e-signature-script-dialog.h \ e-signature-tree-view.h \ + e-spinner.c \ + e-spinner.h \ + e-timeout-activity.h \ e-unicode.h \ - e-colors.h + e-url-entry.h libemiscwidgets_la_SOURCES = \ $(widgetsinclude_HEADERS) \ @@ -92,54 +95,57 @@ libemiscwidgets_la_SOURCES = \ e-action-combo-box.c \ e-activity.c \ e-activity-proxy.c \ - e-calendar.c \ + e-alert-activity.c \ e-attachment.c \ e-attachment-bar.c \ + e-attachment-dialog.c \ + e-calendar.c \ e-calendar-item.c \ + e-canvas.c \ + e-canvas-background.c \ + e-canvas-utils.c \ + e-canvas-vbox.c \ e-cell-date-edit.c \ e-cell-percent.c \ e-cell-renderer-combo.c \ e-charset-picker.c \ + e-colors.c \ e-combo-cell-editable.c \ + e-cursors.c \ e-dateedit.c \ e-expander.c \ + e-gui-utils.c \ + e-hsv-utils.c \ e-icon-entry.c \ e-image-chooser.c \ e-map.c \ e-menu-tool-button.c \ - e-popup-action.c \ - e-preferences-window.c \ + e-mime-part-utils.c \ e-online-button.c \ - e-search-bar.c \ - e-send-options.c \ - e-url-entry.c \ - e-canvas-background.c \ - e-canvas-utils.c \ - e-canvas-vbox.c \ - e-canvas.c \ - e-cursors.c \ - e-gui-utils.c \ - e-hsv-utils.c \ + e-popup-action.c \ e-popup-menu.c \ + e-preferences-window.c \ e-printable.c \ e-reflow-model.c \ e-reflow.c \ + e-search-bar.c \ + e-selection-model.c \ e-selection-model-array.c \ e-selection-model-simple.c \ - e-selection-model.c \ + e-send-options.c \ e-signature-combo-box.c \ e-signature-editor.c \ e-signature-manager.c \ e-signature-preview.c \ e-signature-script-dialog.c \ e-signature-tree-view.c \ + e-timeout-activity.c \ e-unicode.c \ - e-colors.c - + e-url-entry.c libemiscwidgets_la_LDFLAGS = $(NO_UNDEFINED) -libemiscwidgets_la_LIBADD = $(top_builddir)/e-util/libeutil.la \ +libemiscwidgets_la_LIBADD = \ $(top_builddir)/e-util/libeutil.la \ $(top_builddir)/filter/libfilter.la \ $(top_builddir)/widgets/table/libetable.la \ @@ -148,20 +154,8 @@ libemiscwidgets_la_LIBADD = $(top_builddir)/e-util/libeutil.la \ $(top_builddir)/a11y/libevolution-a11y.la \ $(EVOLUTION_MAIL_LIBS) \ $(GNOME_PLATFORM_LIBS) \ - $(EVOLUTON_MAIL_LIBS) \ $(ICONV_LIBS) -#libefilterbar_la_SOURCES = \ -# e-filter-bar.c \ -# e-filter-bar.h -# -#libefilterbar_la_LDFLAGS = $(NO_UNDEFINED) -# -#libefilterbar_la_LIBADD = \ -# $(WIN32_BOOTSTRAP_LIBS) \ -# libemiscwidgets.la \ -# $(E_WIDGETS_LIBS) - noinst_PROGRAMS = \ test-calendar \ test-dateedit \ diff --git a/widgets/misc/e-activity.c b/widgets/misc/e-activity.c index 34e902f5a6..40ee0df35a 100644 --- a/widgets/misc/e-activity.c +++ b/widgets/misc/e-activity.c @@ -32,7 +32,6 @@ struct _EActivityPrivate { gchar *primary_text; gchar *secondary_text; gdouble percent; - guint timeout_id; guint blocking : 1; guint cancellable : 1; @@ -56,21 +55,12 @@ enum { CANCELLED, CLICKED, COMPLETED, - TIMEOUT, LAST_SIGNAL }; static gpointer parent_class; static gulong signals[LAST_SIGNAL]; -static gboolean -activity_timeout_cb (EActivity *activity) -{ - g_signal_emit (activity, signals[TIMEOUT], 0); - - return FALSE; -} - static void activity_set_property (GObject *object, guint property_id, @@ -188,13 +178,64 @@ activity_finalize (GObject *object) g_free (priv->primary_text); g_free (priv->secondary_text); - if (priv->timeout_id > 0) - g_source_remove (priv->timeout_id); - /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (parent_class)->finalize (object); } +static gchar * +activity_describe (EActivity *activity) +{ + GString *string; + const gchar *text; + gboolean cancelled; + gboolean completed; + gdouble percent; + + string = g_string_sized_new (256); + text = e_activity_get_primary_text (activity); + cancelled = e_activity_is_cancelled (activity); + completed = e_activity_is_completed (activity); + percent = e_activity_get_percent (activity); + + if (cancelled) { + /* Translators: This is a cancelled activity. */ + g_string_printf (string, _("%s (cancelled)"), text); + } else if (completed) { + /* Translators: This is a completed activity. */ + g_string_printf (string, _("%s (completed)"), text); + } else if (percent < 0.0) { + /* Translators: This is an activity whose percent + * complete is unknown. */ + g_string_printf (string, _("%s..."), text); + } else { + /* Translators: This is an activity whose percent + * complete is known. */ + g_string_printf ( + string, _("%s (%d%% complete)"), text, + (gint) (percent * 100.0 + 0.5)); + } + + return g_string_free (string, FALSE); +} + +static void +activity_cancelled (EActivity *activity) +{ + activity->priv->cancelled = TRUE; +} + +static void +activity_completed (EActivity *activity) +{ + activity->priv->completed = TRUE; +} + +static void +activity_clicked (EActivity *activity) +{ + /* Allow subclasses to safely chain up. */ +} + static void activity_class_init (EActivityClass *class) { @@ -208,6 +249,11 @@ activity_class_init (EActivityClass *class) object_class->get_property = activity_get_property; object_class->finalize = activity_finalize; + class->describe = activity_describe; + class->cancelled = activity_cancelled; + class->completed = activity_completed; + class->clicked = activity_clicked; + g_object_class_install_property ( object_class, PROP_BLOCKING, @@ -291,7 +337,8 @@ activity_class_init (EActivityClass *class) "cancelled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, - 0, NULL, NULL, + G_STRUCT_OFFSET (EActivityClass, cancelled), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -299,7 +346,8 @@ activity_class_init (EActivityClass *class) "clicked", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, - 0, NULL, NULL, + G_STRUCT_OFFSET (EActivityClass, clicked), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -307,15 +355,8 @@ activity_class_init (EActivityClass *class) "completed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[TIMEOUT] = g_signal_new ( - "timeout", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, + G_STRUCT_OFFSET (EActivityClass, completed), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } @@ -372,7 +413,6 @@ e_activity_cancel (EActivity *activity) if (activity->priv->completed) return; - activity->priv->cancelled = TRUE; g_signal_emit (activity, signals[CANCELLED], 0); } @@ -387,7 +427,6 @@ e_activity_complete (EActivity *activity) if (activity->priv->completed) return; - activity->priv->completed = TRUE; g_signal_emit (activity, signals[COMPLETED], 0); } @@ -402,39 +441,14 @@ e_activity_clicked (EActivity *activity) gchar * e_activity_describe (EActivity *activity) { - GString *string; - const gchar *text; - gboolean cancelled; - gboolean completed; - gdouble percent; + EActivityClass *class; g_return_val_if_fail (E_IS_ACTIVITY (activity), NULL); - string = g_string_sized_new (256); - text = e_activity_get_primary_text (activity); - cancelled = e_activity_is_cancelled (activity); - completed = e_activity_is_completed (activity); - percent = e_activity_get_percent (activity); - - if (cancelled) { - /* Translators: This is a cancelled activity. */ - g_string_printf (string, _("%s (cancelled)"), text); - } else if (completed) { - /* Translators: This is a completed activity. */ - g_string_printf (string, _("%s (completed)"), text); - } else if (percent < 0.0) { - /* Translators: This is an activity whose percent - * complete is unknown. */ - g_string_printf (string, _("%s..."), text); - } else { - /* Translators: This is an activity whose percent - * complete is known. */ - g_string_printf ( - string, _("%s (%d%% complete)"), text, - (gint) (percent * 100.0 + 0.5)); - } + class = E_ACTIVITY_GET_CLASS (activity); + g_return_val_if_fail (class->describe != NULL, NULL); - return g_string_free (string, FALSE); + return class->describe (activity); } gboolean @@ -453,29 +467,6 @@ e_activity_is_completed (EActivity *activity) return activity->priv->completed; } -void -e_activity_add_timeout (EActivity *activity, - guint seconds) -{ - g_return_if_fail (E_IS_ACTIVITY (activity)); - - e_activity_cancel_timeout (activity); - - activity->priv->timeout_id = g_timeout_add_seconds ( - seconds, (GSourceFunc) activity_timeout_cb, activity); -} - -void -e_activity_cancel_timeout (EActivity *activity) -{ - g_return_if_fail (E_IS_ACTIVITY (activity)); - - if (activity->priv->timeout_id > 0) { - g_source_remove (activity->priv->timeout_id); - activity->priv->timeout_id = 0; - } -} - gboolean e_activity_get_blocking (EActivity *activity) { @@ -611,105 +602,3 @@ e_activity_set_secondary_text (EActivity *activity, g_object_notify (G_OBJECT (activity), "secondary-text"); } - -/************************* Error Dialog Integration **************************/ - -void -e_activity_error (EActivity *activity, - GtkWidget *error_dialog) -{ - GObject *object; - const gchar *primary_text; - const gchar *secondary_text; - - /* XXX Convert an activity to a clickable error message. - * Clicking on the activity completes it and displays - * the error dialog. Eventually I'd like to eliminate - * error dialogs altogether and show errors directly - * in the shell window. */ - - g_return_if_fail (E_IS_ACTIVITY (activity)); - g_return_if_fail (GTK_IS_DIALOG (error_dialog)); - - object = G_OBJECT (error_dialog); - primary_text = g_object_get_data (object, "primary"); - secondary_text = g_object_get_data (object, "secondary"); - - e_activity_set_primary_text (activity, primary_text); - e_activity_set_secondary_text (activity, secondary_text); - e_activity_set_icon_name (activity, "dialog-warning"); - e_activity_set_clickable (activity, TRUE); - - g_signal_connect ( - activity, "cancelled", - G_CALLBACK (e_activity_cancel_timeout), NULL); - - g_signal_connect ( - activity, "completed", - G_CALLBACK (e_activity_cancel_timeout), NULL); - - g_signal_connect ( - activity, "clicked", - G_CALLBACK (e_activity_complete), NULL); - - g_signal_connect_swapped ( - activity, "clicked", - G_CALLBACK (gtk_dialog_run), error_dialog); - - g_signal_connect ( - activity, "timeout", - G_CALLBACK (e_activity_complete), NULL); - - /* XXX Allow for a configurable timeout. */ - e_activity_add_timeout (activity, 60); -} - -void -e_activity_info (EActivity *activity, - GtkWidget *info_dialog) -{ - GObject *object; - const gchar *primary_text; - const gchar *secondary_text; - - /* XXX Convert an activity to a clickable info message. - * Clicking on the activity completes it and displays - * the info dialog. Eventually I'd like to eliminate - * info dialogs altogether and show errors directly - * in the shell window. */ - - g_return_if_fail (E_IS_ACTIVITY (activity)); - g_return_if_fail (GTK_IS_DIALOG (info_dialog)); - - object = G_OBJECT (info_dialog); - primary_text = g_object_get_data (object, "primary"); - secondary_text = g_object_get_data (object, "secondary"); - - e_activity_set_primary_text (activity, primary_text); - e_activity_set_secondary_text (activity, secondary_text); - e_activity_set_icon_name (activity, "dialog-warning"); - e_activity_set_clickable (activity, TRUE); - - g_signal_connect ( - activity, "cancelled", - G_CALLBACK (e_activity_cancel_timeout), NULL); - - g_signal_connect ( - activity, "completed", - G_CALLBACK (e_activity_cancel_timeout), NULL); - - g_signal_connect ( - activity, "clicked", - G_CALLBACK (e_activity_complete), NULL); - - g_signal_connect_swapped ( - activity, "clicked", - G_CALLBACK (gtk_dialog_run), info_dialog); - - g_signal_connect ( - activity, "timeout", - G_CALLBACK (e_activity_complete), NULL); - - /* XXX Allow for a configurable timeout. */ - e_activity_add_timeout (activity, 60); -} diff --git a/widgets/misc/e-activity.h b/widgets/misc/e-activity.h index d949860333..5050d9e611 100644 --- a/widgets/misc/e-activity.h +++ b/widgets/misc/e-activity.h @@ -56,6 +56,14 @@ struct _EActivity { struct _EActivityClass { GObjectClass parent_class; + + /* Methods */ + gchar * (*describe) (EActivity *activity); + + /* Signals */ + void (*cancelled) (EActivity *activity); + void (*completed) (EActivity *activity); + void (*clicked) (EActivity *activity); }; GType e_activity_get_type (void); @@ -66,9 +74,6 @@ void e_activity_clicked (EActivity *activity); gchar * e_activity_describe (EActivity *activity); gboolean e_activity_is_cancelled (EActivity *activity); gboolean e_activity_is_completed (EActivity *activity); -void e_activity_add_timeout (EActivity *activity, - guint seconds); -void e_activity_cancel_timeout (EActivity *activity); gboolean e_activity_get_blocking (EActivity *activity); void e_activity_set_blocking (EActivity *activity, gboolean blocking); @@ -91,12 +96,6 @@ const gchar * e_activity_get_secondary_text (EActivity *activity); void e_activity_set_secondary_text (EActivity *activity, const gchar *secondary_text); -/* XXX Hacky integration with error dialogs. */ -void e_activity_error (EActivity *activity, - GtkWidget *error_dialog); -void e_activity_info (EActivity *activity, - GtkWidget *info_dialog); - G_END_DECLS #endif /* E_ACTIVITY_H */ diff --git a/widgets/misc/e-alert-activity.c b/widgets/misc/e-alert-activity.c new file mode 100644 index 0000000000..d8b9d6f3f3 --- /dev/null +++ b/widgets/misc/e-alert-activity.c @@ -0,0 +1,254 @@ +/* + * e-alert-activity.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-alert-activity.h" + +#define E_ALERT_ACTIVITY_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ALERT_ACTIVITY, EAlertActivityPrivate)) + +struct _EAlertActivityPrivate { + GtkWidget *message_dialog; +}; + +enum { + PROP_0, + PROP_MESSAGE_DIALOG +}; + +static gpointer parent_class; + +static void +alert_activity_set_message_dialog (EAlertActivity *alert_activity, + GtkWidget *message_dialog) +{ + g_return_if_fail (alert_activity->priv->message_dialog == NULL); + + alert_activity->priv->message_dialog = g_object_ref (message_dialog); +} + +static void +alert_activity_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_MESSAGE_DIALOG: + alert_activity_set_message_dialog ( + E_ALERT_ACTIVITY (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +alert_activity_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_MESSAGE_DIALOG: + g_value_set_object ( + value, e_alert_activity_get_message_dialog ( + E_ALERT_ACTIVITY (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +alert_activity_dispose (GObject *object) +{ + EAlertActivityPrivate *priv; + + priv = E_ALERT_ACTIVITY_GET_PRIVATE (object); + + if (priv->message_dialog != NULL) { + g_object_unref (priv->message_dialog); + priv->message_dialog = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +alert_activity_constructed (GObject *object) +{ + EActivity *activity; + EAlertActivity *alert_activity; + GtkWidget *message_dialog; + const gchar *primary_text; + const gchar *secondary_text; + + alert_activity = E_ALERT_ACTIVITY (object); + message_dialog = e_alert_activity_get_message_dialog (alert_activity); + + object = G_OBJECT (message_dialog); + primary_text = g_object_get_data (object, "primary"); + secondary_text = g_object_get_data (object, "secondary"); + + activity = E_ACTIVITY (alert_activity); + e_activity_set_primary_text (activity, primary_text); + e_activity_set_secondary_text (activity, secondary_text); +} + +static void +alert_activity_clicked (EActivity *activity) +{ + EAlertActivity *alert_activity; + GtkWidget *message_dialog; + + e_activity_complete (activity); + + alert_activity = E_ALERT_ACTIVITY (activity); + message_dialog = e_alert_activity_get_message_dialog (alert_activity); + gtk_dialog_run (GTK_DIALOG (message_dialog)); + gtk_widget_hide (message_dialog); + + /* Chain up to parent's clicked() method. */ + E_ACTIVITY_CLASS (parent_class)->clicked (activity); +} + +static void +alert_activity_timeout (ETimeoutActivity *activity) +{ + e_activity_complete (E_ACTIVITY (activity)); + + /* Chain up to parent's timeout() method. */ + E_TIMEOUT_ACTIVITY_CLASS (parent_class)->timeout (activity); +} + +static void +alert_activity_class_init (EAlertActivityClass *class) +{ + GObjectClass *object_class; + EActivityClass *activity_class; + ETimeoutActivityClass *timeout_activity_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EAlertActivityPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = alert_activity_set_property; + object_class->get_property = alert_activity_get_property; + object_class->dispose = alert_activity_dispose; + object_class->constructed = alert_activity_constructed; + + activity_class = E_ACTIVITY_CLASS (class); + activity_class->clicked = alert_activity_clicked; + + timeout_activity_class = E_TIMEOUT_ACTIVITY_CLASS (class); + timeout_activity_class->timeout = alert_activity_timeout; + + g_object_class_install_property ( + object_class, + PROP_MESSAGE_DIALOG, + g_param_spec_object ( + "message-dialog", + NULL, + NULL, + GTK_TYPE_DIALOG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +alert_activity_init (EAlertActivity *alert_activity) +{ + alert_activity->priv = E_ALERT_ACTIVITY_GET_PRIVATE (alert_activity); + + e_activity_set_clickable (E_ACTIVITY (alert_activity), TRUE); + e_timeout_activity_set_timeout (E_TIMEOUT_ACTIVITY (alert_activity), 60); +} + +GType +e_alert_activity_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EAlertActivityClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) alert_activity_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EAlertActivity), + 0, /* n_preallocs */ + (GInstanceInitFunc) alert_activity_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + E_TYPE_TIMEOUT_ACTIVITY, "EAlertActivity", + &type_info, 0); + } + + return type; +} + +EActivity * +e_alert_activity_new_info (GtkWidget *message_dialog) +{ + g_return_val_if_fail (GTK_IS_DIALOG (message_dialog), NULL); + + return g_object_new ( + E_TYPE_ALERT_ACTIVITY, + "icon-name", "dialog-information", + "message-dialog", message_dialog, NULL); +} + +EActivity * +e_alert_activity_new_error (GtkWidget *message_dialog) +{ + g_return_val_if_fail (GTK_IS_DIALOG (message_dialog), NULL); + + return g_object_new ( + E_TYPE_ALERT_ACTIVITY, + "icon-name", "dialog-error", + "message-dialog", message_dialog, NULL); +} + +EActivity * +e_alert_activity_new_warning (GtkWidget *message_dialog) +{ + g_return_val_if_fail (GTK_IS_DIALOG (message_dialog), NULL); + + return g_object_new ( + E_TYPE_ALERT_ACTIVITY, + "icon-name", "dialog-warning", + "message-dialog", message_dialog, NULL); +} + +GtkWidget * +e_alert_activity_get_message_dialog (EAlertActivity *alert_activity) +{ + g_return_val_if_fail (E_IS_ALERT_ACTIVITY (alert_activity), NULL); + + return alert_activity->priv->message_dialog; +} diff --git a/widgets/misc/e-alert-activity.h b/widgets/misc/e-alert-activity.h new file mode 100644 index 0000000000..4c5547d7fa --- /dev/null +++ b/widgets/misc/e-alert-activity.h @@ -0,0 +1,70 @@ +/* + * e-alert-activity.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_ALERT_ACTIVITY_H +#define E_ALERT_ACTIVITY_H + +#include <e-timeout-activity.h> + +/* Standard GObject macros */ +#define E_TYPE_ALERT_ACTIVITY \ + (e_alert_activity_get_type ()) +#define E_ALERT_ACTIVITY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ALERT_ACTIVITY, EAlertActivity)) +#define E_ALERT_ACTIVITY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ALERT_ACTIVITY, EAlertActivityClass)) +#define E_IS_ALERT_ACTIVITY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ALERT_ACTIVITY)) +#define E_IS_ALERT_ACTIVITY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ALERT_ACTIVITY)) +#define E_ALERT_ACTIVITY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ALERT_ACTIVITY, EAlertActivityClass)) + +G_BEGIN_DECLS + +typedef struct _EAlertActivity EAlertActivity; +typedef struct _EAlertActivityClass EAlertActivityClass; +typedef struct _EAlertActivityPrivate EAlertActivityPrivate; + +struct _EAlertActivity { + ETimeoutActivity parent; + EAlertActivityPrivate *priv; +}; + +struct _EAlertActivityClass { + ETimeoutActivityClass parent_class; +}; + +GType e_alert_activity_get_type (void); +EActivity * e_alert_activity_new_info (GtkWidget *message_dialog); +EActivity * e_alert_activity_new_error (GtkWidget *message_dialog); +EActivity * e_alert_activity_new_warning (GtkWidget *message_dialog); +GtkWidget * e_alert_activity_get_message_dialog + (EAlertActivity *alert_activity); + +G_END_DECLS + +#endif /* E_ALERT_ACTIVITY_H */ diff --git a/widgets/misc/e-attachment-bar.c b/widgets/misc/e-attachment-bar.c index 316aa85a0e..4608d77b9d 100644 --- a/widgets/misc/e-attachment-bar.c +++ b/widgets/misc/e-attachment-bar.c @@ -43,6 +43,7 @@ #include "e-attachment.h" #include "e-attachment-bar.h" +#include "e-mime-part-utils.h" #include <libedataserver/e-data-server-util.h> @@ -56,11 +57,13 @@ #include <camel/camel-mime-filter-bestenc.h> #include <camel/camel-mime-part.h> -#include "e-util/e-util.h" +#include "e-util/e-binding.h" +#include "e-util/e-error.h" #include "e-util/e-gui-utils.h" #include "e-util/e-icon-factory.h" -#include "e-util/e-error.h" #include "e-util/e-mktemp.h" +#include "e-util/e-util.h" +#include "e-util/gconf-bridge.h" #define ICON_WIDTH 64 #define ICON_SEPARATORS " /-_" @@ -70,84 +73,418 @@ #define ICON_BORDER 2 #define ICON_TEXT_SPACING 2 - -static GnomeIconListClass *parent_class = NULL; +#define E_ATTACHMENT_BAR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBarPrivate)) struct _EAttachmentBarPrivate { - GtkWidget *attach; /* attachment file dialogue, if active */ - gboolean batch_unref; GPtrArray *attachments; + gchar *current_folder; char *path; + + GtkUIManager *ui_manager; + GtkActionGroup *standard_actions; + GtkActionGroup *editable_actions; + GtkActionGroup *open_actions; + guint merge_id; + + gchar *background_filename; + gchar *background_options; + + guint editable : 1; +}; + +enum { + PROP_0, + PROP_BACKGROUND_FILENAME, + PROP_BACKGROUND_OPTIONS, + PROP_CURRENT_FOLDER, + PROP_EDITABLE, + PROP_UI_MANAGER }; - enum { CHANGED, + UPDATE_ACTIONS, LAST_SIGNAL }; -static guint signals[LAST_SIGNAL] = { 0 }; +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; + +static const gchar *ui = +"<ui>" +" <popup name='attachment-popup'>" +" <menuitem action='save-as'/>" +" <menuitem action='set-background'/>" +" <menuitem action='remove'/>" +" <menuitem action='properties'/>" +" <placeholder name='custom-actions'/>" +" <separator/>" +" <menuitem action='add'/>" +" <separator/>" +" <placeholder name='open-actions'/>" +" </popup>" +"</ui>"; - -static void update (EAttachmentBar *bar); +static void +action_add_cb (GtkAction *action, + EAttachmentBar *attachment_bar) +{ + GtkWidget *dialog; + GtkWidget *option; + GSList *uris, *iter; + const gchar *disposition; + gboolean active; + gpointer parent; + gint response; + + parent = gtk_widget_get_toplevel (GTK_WIDGET (attachment_bar)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + dialog = gtk_file_chooser_dialog_new ( + _("Insert Attachment"), parent, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("A_ttach"), GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response ( + GTK_DIALOG (dialog), GTK_RESPONSE_OK); + gtk_file_chooser_set_local_only ( + GTK_FILE_CHOOSER (dialog), FALSE); + gtk_file_chooser_set_select_multiple ( + GTK_FILE_CHOOSER (dialog), TRUE); + gtk_window_set_icon_name ( + GTK_WINDOW (dialog), "mail-attachment"); + + option = gtk_check_button_new_with_mnemonic ( + _("_Suggest automatic display of attachment")); + gtk_file_chooser_set_extra_widget ( + GTK_FILE_CHOOSER (dialog), option); + gtk_widget_show (option); + + response = e_attachment_bar_file_chooser_dialog_run ( + attachment_bar, dialog); + + if (response != GTK_RESPONSE_OK) + goto exit; + + uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog)); + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option)); + disposition = active ? "inline" : "attachment"; + + for (iter = uris; iter != NULL; iter = iter->next) { + CamelURL *url; + + url = camel_url_new (iter->data, NULL); + if (url == NULL) + continue; -/* Attachment handling functions. */ + /* XXX Do we really need two different attach functions? */ + if (g_ascii_strcasecmp (url->protocol, "file") == 0) + e_attachment_bar_attach ( + attachment_bar, url->path, disposition); + else + e_attachment_bar_attach_remote_file ( + attachment_bar, iter->data, disposition); + + camel_url_free (url); + } + + g_slist_foreach (uris, (GFunc) g_free, NULL); + g_slist_free (uris); + +exit: + gtk_widget_destroy (dialog); +} static void -attachment_destroy (EAttachmentBar *bar, EAttachment *attachment) +action_properties_cb (GtkAction *action, + EAttachmentBar *attachment_bar) { - if (bar->priv->batch_unref) - return; + GnomeIconList *icon_list; + GPtrArray *array; + GList *selection; + gpointer parent; - if (g_ptr_array_remove (bar->priv->attachments, attachment)) { - update (bar); - g_signal_emit (bar, signals[CHANGED], 0); + array = attachment_bar->priv->attachments; + + icon_list = GNOME_ICON_LIST (attachment_bar); + selection = gnome_icon_list_get_selection (icon_list); + g_return_if_fail (selection != NULL); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (icon_list)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + while (selection != NULL) { + gint index = GPOINTER_TO_INT (selection->data); + EAttachment *attachment; + + selection = g_list_next (selection); + + if (index >= array->len) + continue; + + attachment = array->pdata[index]; + e_attachment_edit (attachment, parent); } } static void -attachment_changed_cb (EAttachment *attachment, - gpointer data) +action_recent_cb (GtkAction *action, + EAttachmentBar *attachment_bar) { - update (E_ATTACHMENT_BAR (data)); + GtkRecentChooser *chooser; + GFile *file; + gchar *uri; + + chooser = GTK_RECENT_CHOOSER (action); + + /* Wish: gtk_recent_chooser_get_current_file() */ + uri = gtk_recent_chooser_get_current_uri (chooser); + file = g_file_new_for_uri (uri); + g_free (uri); + + if (g_file_is_native (file)) + e_attachment_bar_attach ( + E_ATTACHMENT_BAR (attachment_bar), + g_file_get_path (file), "attachment"); + else + e_attachment_bar_attach_remote_file ( + E_ATTACHMENT_BAR (attachment_bar), + g_file_get_uri (file), "attachment"); + + g_object_unref (file); } static void -add_common (EAttachmentBar *bar, EAttachment *attachment) +action_remove_cb (GtkAction *action, + EAttachmentBar *attachment_bar) { - g_return_if_fail (attachment != NULL); + GnomeIconList *icon_list; + GPtrArray *array; + GList *selection; + GList *trash = NULL; - g_ptr_array_add (bar->priv->attachments, attachment); - g_object_weak_ref ((GObject *) attachment, (GWeakNotify) attachment_destroy, bar); - g_signal_connect (attachment, "changed", G_CALLBACK (attachment_changed_cb), bar); + array = attachment_bar->priv->attachments; - update (bar); + icon_list = GNOME_ICON_LIST (attachment_bar); + selection = gnome_icon_list_get_selection (icon_list); + g_return_if_fail (selection != NULL); - g_signal_emit (bar, signals[CHANGED], 0); + while (selection != NULL) { + gint index = GPOINTER_TO_INT (selection->data); + + selection = g_list_next (selection); + + if (index >= array->len) + continue; + + /* We can't unref the attachment here because that may + * change the selection and invalidate the list we are + * iterating over. So move it to a trash list instead. */ + trash = g_list_prepend (trash, array->pdata[index]); + array->pdata[index] = NULL; + } + + /* Compress the attachment array. */ + while (g_ptr_array_remove (array, NULL)); + + /* Take out the trash. */ + g_list_foreach (trash, (GFunc) g_object_unref, NULL); + g_list_free (trash); + + e_attachment_bar_refresh (attachment_bar); + + g_signal_emit (attachment_bar, signals[CHANGED], 0); } static void -add_from_mime_part (EAttachmentBar *bar, CamelMimePart *part) +action_save_as_cb (GtkAction *action, + EAttachmentBar *attachment_bar) { - add_common (bar, e_attachment_new_from_mime_part (part)); } static void -add_from_file (EAttachmentBar *bar, const char *file_name, const char *disposition) +action_set_background_cb (GtkAction *action, + EAttachmentBar *attachment_bar) { + GnomeIconList *icon_list; + CamelContentType *content_type; + CamelMimePart *mime_part; EAttachment *attachment; - CamelException ex; + GPtrArray *array; + GList *selection; + gchar *basename; + gchar *filename; + gchar *dirname; + GFile *file; + gint index; + GError *error = NULL; + + icon_list = GNOME_ICON_LIST (attachment_bar); + selection = gnome_icon_list_get_selection (icon_list); + g_return_if_fail (selection != NULL); + + array = attachment_bar->priv->attachments; + index = GPOINTER_TO_INT (selection->data); + attachment = E_ATTACHMENT (array->pdata[index]); + mime_part = e_attachment_get_mime_part (attachment); + g_return_if_fail (CAMEL_IS_MIME_PART (mime_part)); + + content_type = camel_mime_part_get_content_type (mime_part); + basename = g_strdup (camel_mime_part_get_filename (mime_part)); + + if (basename == NULL || basename == '\0') { + g_free (basename); + basename = g_strdup_printf ( + _("untitled_image.%s"), + content_type->subtype); + } - camel_exception_init (&ex); + dirname = g_build_filename ( + g_get_home_dir (), ".gnome2", "wallpapers", NULL); - if ((attachment = e_attachment_new (file_name, disposition, &ex))) { - add_common (bar, attachment); - } else { - /* FIXME: Avoid using error from mailer */ - e_error_run ((GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) bar), "mail-composer:no-attach", - file_name, camel_exception_get_description (&ex), NULL); - camel_exception_clear (&ex); + index = 0; + filename = g_build_filename (dirname, basename, NULL); + + while (g_file_test (filename, G_FILE_TEST_EXISTS)) { + gchar *temp; + gchar *ext; + + ext = strrchr (filename, '.'); + if (ext != NULL) + *ext++ = '\0'; + + if (ext == NULL) + temp = g_strdup_printf ( + "%s (%d)", basename, index++); + else + temp = g_strdup_printf ( + "%s (%d).%s", basename, index++, ext); + + g_free (basename); + g_free (filename); + basename = temp; + + filename = g_build_filename (dirname, basename, NULL); + } + + g_free (basename); + g_free (dirname); + + file = g_file_new_for_path (filename); + + if (e_mime_part_utils_save_to_file (mime_part, file, &error)) { + const gchar *background_filename; + const gchar *background_options; + + background_filename = + e_attachment_bar_get_background_filename ( + attachment_bar); + background_options = + e_attachment_bar_get_background_options ( + attachment_bar); + + if (g_strcmp0 (background_filename, filename) == 0) + e_attachment_bar_set_background_filename ( + attachment_bar, NULL); + + e_attachment_bar_set_background_filename ( + attachment_bar, filename); + + if (g_strcmp0 (background_options, "none") == 0) + e_attachment_bar_set_background_options ( + attachment_bar, "wallpaper"); + } + + g_object_unref (file); + g_free (filename); + + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } +} + +static GtkActionEntry standard_entries[] = { + + { "save-as", + GTK_STOCK_SAVE_AS, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_save_as_cb) }, + + { "set-background", + NULL, + N_("Set as _Background"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_set_background_cb) } +}; + +static GtkActionEntry editable_entries[] = { + + { "add", + GTK_STOCK_ADD, + N_("A_dd Attachment..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_add_cb) }, + + { "properties", + GTK_STOCK_PROPERTIES, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_properties_cb) }, + + { "remove", + GTK_STOCK_REMOVE, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_remove_cb) } +}; + +static void +attachment_bar_show_popup_menu (EAttachmentBar *attachment_bar, + GdkEventButton *event) +{ + GtkUIManager *ui_manager; + GtkWidget *menu; + + ui_manager = e_attachment_bar_get_ui_manager (attachment_bar); + menu = gtk_ui_manager_get_widget (ui_manager, "/attachment-popup"); + g_return_if_fail (GTK_IS_MENU (menu)); + + e_attachment_bar_update_actions (attachment_bar); + + if (event != NULL) + gtk_menu_popup ( + GTK_MENU (menu), NULL, NULL, NULL, NULL, + event->button, event->time); + else + gtk_menu_popup ( + GTK_MENU (menu), NULL, NULL, NULL, NULL, + 0, gtk_get_current_event_time ()); +} + +/* Attachment handling functions. */ + +static void +attachment_destroy (EAttachmentBar *bar, + EAttachment *attachment) +{ + if (bar->priv->batch_unref) + return; + + if (g_ptr_array_remove (bar->priv->attachments, attachment)) { + e_attachment_bar_refresh (bar); + g_signal_emit (bar, signals[CHANGED], 0); } } @@ -161,6 +498,7 @@ get_system_thumbnail (EAttachment *attachment, CamelContentType *content_type) { GdkPixbuf *pixbuf = NULL; #ifdef HAVE_LIBGNOMEUI_GNOME_THUMBNAIL_H + CamelMimePart *mime_part; struct stat file_stat; char *file_uri = NULL; gboolean is_tmp = FALSE; @@ -168,9 +506,11 @@ get_system_thumbnail (EAttachment *attachment, CamelContentType *content_type) if (!attachment || !attachment->is_available_local) return NULL; + mime_part = e_attachment_get_mime_part (attachment); + if (attachment->store_uri && g_str_has_prefix (attachment->store_uri, "file://")) file_uri = attachment->store_uri; - else if (attachment->body) { + else if (mime_part != NULL) { /* save part to the temp directory */ char *tmp_file; @@ -182,7 +522,7 @@ get_system_thumbnail (EAttachment *attachment, CamelContentType *content_type) char *mfilename = NULL; const char * filename; - filename = camel_mime_part_get_filename (attachment->body); + filename = camel_mime_part_get_filename (mime_part); if (filename == NULL) filename = "unknown"; else { @@ -202,7 +542,7 @@ get_system_thumbnail (EAttachment *attachment, CamelContentType *content_type) if (stream) { CamelDataWrapper *content; - content = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body)); + content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); if (camel_data_wrapper_decode_to_stream (content, stream) == -1 || camel_stream_flush (stream) == -1) { @@ -298,17 +638,24 @@ scale_pixbuf (GdkPixbuf *pixbuf) /* Icon list contents handling. */ static void -calculate_height_width(EAttachmentBar *bar, int *new_width, int *new_height) +calculate_height_width (EAttachmentBar *bar, + gint *new_width, + gint *new_height) { int width, height, icon_width; PangoFontMetrics *metrics; PangoContext *context; - context = gtk_widget_get_pango_context ((GtkWidget *) bar); - metrics = pango_context_get_metrics (context, ((GtkWidget *) bar)->style->font_desc, pango_context_get_language (context)); - width = PANGO_PIXELS (pango_font_metrics_get_approximate_char_width (metrics)) * 15; + context = gtk_widget_get_pango_context (GTK_WIDGET (bar)); + metrics = pango_context_get_metrics ( + context, GTK_WIDGET (bar)->style->font_desc, + pango_context_get_language (context)); + width = PANGO_PIXELS ( + pango_font_metrics_get_approximate_char_width (metrics)) * 15; /* This should be *2, but the icon list creates too much space above ... */ - height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics)) * 3; + height = PANGO_PIXELS ( + pango_font_metrics_get_ascent (metrics) + + pango_font_metrics_get_descent (metrics)) * 3; pango_font_metrics_unref (metrics); icon_width = ICON_WIDTH + ICON_SPACING + ICON_BORDER + ICON_TEXT_SPACING; @@ -316,21 +663,23 @@ calculate_height_width(EAttachmentBar *bar, int *new_width, int *new_height) *new_width = MAX (icon_width, width); if (new_height) - *new_height = ICON_WIDTH + ICON_SPACING + ICON_BORDER + ICON_TEXT_SPACING + height; - - return; + *new_height = ICON_WIDTH + ICON_SPACING + + ICON_BORDER + ICON_TEXT_SPACING + height; } void e_attachment_bar_create_attachment_cache (EAttachment *attachment) { - CamelContentType *content_type; + CamelMimePart *mime_part; + + g_return_if_fail (E_IS_ATTACHMENT (attachment)); - if (!attachment->body) + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part == NULL) return; - content_type = camel_mime_part_get_content_type (attachment->body); + content_type = camel_mime_part_get_content_type (mime_part); if (camel_content_type_is(content_type, "image", "*")) { CamelDataWrapper *wrapper; @@ -339,7 +688,7 @@ e_attachment_bar_create_attachment_cache (EAttachment *attachment) gboolean error = TRUE; GdkPixbuf *pixbuf; - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body)); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); mstream = (CamelStreamMem *) camel_stream_mem_new (); camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream); @@ -350,12 +699,15 @@ e_attachment_bar_create_attachment_cache (EAttachment *attachment) gdk_pixbuf_loader_close (loader, NULL); if (!error) { + /* The loader owns the reference. */ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); - attachment->pixbuf_cache = scale_pixbuf (pixbuf); - pixbuf = attachment->pixbuf_cache; - g_object_ref(pixbuf); + + /* This returns a new GdkPixbuf. */ + pixbuf = scale_pixbuf (pixbuf); + e_attachment_set_thumbnail (attachment, pixbuf); + g_object_unref (pixbuf); } else { - attachment->pixbuf_cache = NULL; + e_attachment_set_thumbnail (attachment, NULL); g_warning ("GdkPixbufLoader Error"); } @@ -366,244 +718,40 @@ e_attachment_bar_create_attachment_cache (EAttachment *attachment) } static void -update (EAttachmentBar *bar) -{ - struct _EAttachmentBarPrivate *priv; - GnomeIconList *icon_list; - int bar_width, bar_height; - int i; - - priv = bar->priv; - icon_list = GNOME_ICON_LIST (bar); - - gnome_icon_list_freeze (icon_list); - - gnome_icon_list_clear (icon_list); - - /* FIXME could be faster, but we don't care. */ - for (i = 0; i < priv->attachments->len; i++) { - EAttachment *attachment; - CamelContentType *content_type; - char *size_string, *label; - GdkPixbuf *pixbuf = NULL; - const char *desc; - - attachment = priv->attachments->pdata[i]; - - if (!attachment->is_available_local || !attachment->body) { - if ((pixbuf = e_icon_factory_get_icon("mail-attachment", E_ICON_SIZE_DIALOG))) { - attachment->index = gnome_icon_list_append_pixbuf (icon_list, pixbuf, NULL, ""); - g_object_unref (pixbuf); - } - continue; - } - - content_type = camel_mime_part_get_content_type (attachment->body); - /* Get the image out of the attachment - and create a thumbnail for it */ - if ((pixbuf = attachment->pixbuf_cache)) { - g_object_ref(pixbuf); - } else if (camel_content_type_is(content_type, "image", "*")) { - CamelDataWrapper *wrapper; - CamelStreamMem *mstream; - GdkPixbufLoader *loader; - gboolean error = TRUE; - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body)); - mstream = (CamelStreamMem *) camel_stream_mem_new (); - - camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream); - - /* Stream image into pixbuf loader */ - loader = gdk_pixbuf_loader_new (); - error = !gdk_pixbuf_loader_write (loader, mstream->buffer->data, mstream->buffer->len, NULL); - gdk_pixbuf_loader_close (loader, NULL); - - if (!error) { - pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); - attachment->pixbuf_cache = scale_pixbuf (pixbuf); - pixbuf = attachment->pixbuf_cache; - g_object_ref (pixbuf); - } else { - pixbuf = NULL; - g_warning ("GdkPixbufLoader Error"); - } - - /* Destroy everything */ - g_object_unref (loader); - camel_object_unref (mstream); - } else if (!bar->expand && (pixbuf = get_system_thumbnail (attachment, content_type))) { - attachment->pixbuf_cache = scale_pixbuf (pixbuf); - pixbuf = attachment->pixbuf_cache; - g_object_ref (pixbuf); - } - - desc = camel_mime_part_get_description (attachment->body); - if (!desc || *desc == '\0') { - if (attachment->file_name) - desc = attachment->file_name; - else - desc = camel_mime_part_get_filename (attachment->body); - } - - if (!desc) - desc = _("attachment"); - - if (attachment->size && (size_string = g_format_size_for_display (attachment->size))) { - label = g_strdup_printf ("%s (%s)", desc, size_string); - g_free (size_string); - } else - label = g_strdup (desc); - - if (pixbuf == NULL) { - char *mime_type; - - mime_type = camel_content_type_simple (content_type); - pixbuf = e_icon_for_mime_type (mime_type, 48); - if (pixbuf == NULL) { - g_warning("cannot find icon for mime type %s (installation problem?)", mime_type); - pixbuf = e_icon_factory_get_icon("mail-attachment", E_ICON_SIZE_DIALOG); - } - g_free (mime_type); - - /* remember this picture and use it later again */ - if (pixbuf) - attachment->pixbuf_cache = g_object_ref (pixbuf); - } - - if (pixbuf) { - GdkPixbuf *pixbuf_orig = pixbuf; - pixbuf = gdk_pixbuf_add_alpha (pixbuf_orig, TRUE, 255, 255, 255); - - /* gdk_pixbuf_add_alpha returns a newly allocated pixbuf, - free the original one. - */ - g_object_unref (pixbuf_orig); - - /* In case of a attachment bar, in a signed/encrypted part, display the status as a emblem*/ - if (attachment->sign) { - /* Show the signature status at the right-bottom.*/ - GdkPixbuf *sign = NULL; - int x, y; - - if (attachment->sign == CAMEL_CIPHER_VALIDITY_SIGN_BAD) - sign = e_icon_factory_get_icon ("stock_signature-bad", E_ICON_SIZE_MENU); - else if (attachment->sign == CAMEL_CIPHER_VALIDITY_SIGN_GOOD) - sign = e_icon_factory_get_icon ("stock_signature-ok", E_ICON_SIZE_MENU); - else - sign = e_icon_factory_get_icon ("stock_signature", E_ICON_SIZE_MENU); - - x = gdk_pixbuf_get_width (pixbuf) - 17; - y = gdk_pixbuf_get_height (pixbuf) - 17; - - gdk_pixbuf_copy_area (sign, 0, 0, 16, 16, pixbuf, x, y); - g_object_unref (sign); - } - - if (attachment->encrypt) { - /* Show the encryption status at the top left.*/ - GdkPixbuf *encrypt = e_icon_factory_get_icon ("stock_lock-ok", E_ICON_SIZE_MENU); - - gdk_pixbuf_copy_area (encrypt, 0, 0, 16, 16, pixbuf, 1, 1); - g_object_unref (encrypt); - } - - gnome_icon_list_append_pixbuf (icon_list, pixbuf, NULL, label); - g_object_unref (pixbuf); - } - - g_free (label); - } - - gnome_icon_list_thaw (icon_list); - - /* Resize */ - if (bar->expand) { - gtk_widget_get_size_request ((GtkWidget *) bar, &bar_width, &bar_height); - - if (bar->priv->attachments->len) { - int per_col, rows, height, width; - - calculate_height_width(bar, &width, &height); - per_col = bar_width / width; - per_col = (per_col ? per_col : 1); - rows = (bar->priv->attachments->len + per_col -1) / per_col; - gtk_widget_set_size_request ((GtkWidget *) bar, bar_width, rows * height); - } - } -} - -static void update_remote_file (EAttachment *attachment, EAttachmentBar *bar) { GnomeIconList *icon_list; GnomeIconTextItem *item; + const gchar *filename; char *msg, *base; if (attachment->percentage == -1) { - update (bar); + e_attachment_bar_refresh (bar); return; } - base = g_path_get_basename(attachment->file_name); - msg = g_strdup_printf("%s (%d%%)", base, attachment->percentage); - g_free(base); + filename = e_attachment_get_filename (attachment); + base = g_path_get_basename (filename); + msg = g_strdup_printf ("%s (%d%%)", base, attachment->percentage); + g_free (base); icon_list = GNOME_ICON_LIST (bar); gnome_icon_list_freeze (icon_list); - item = gnome_icon_list_get_icon_text_item (icon_list, attachment->index); + item = gnome_icon_list_get_icon_text_item ( + icon_list, attachment->index); if (!item->is_text_allocated) g_free (item->text); - gnome_icon_text_item_configure (item, item->x, item->y, item->width, item->fontname, msg, item->is_editable, TRUE); + gnome_icon_text_item_configure ( + item, item->x, item->y, item->width, + item->fontname, msg, item->is_editable, TRUE); gnome_icon_list_thaw (icon_list); } void -e_attachment_bar_remove_selected (EAttachmentBar *bar) -{ - struct _EAttachmentBarPrivate *priv; - EAttachment *attachment; - int id, left, nrem = 0; - GList *items; - GPtrArray *temp_arr; - - g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); - - priv = bar->priv; - - if (!(items = gnome_icon_list_get_selection ((GnomeIconList *) bar))) - return; - - temp_arr = g_ptr_array_new (); - while (items != NULL) { - if ((id = GPOINTER_TO_INT (items->data) - nrem) < priv->attachments->len) { - attachment = E_ATTACHMENT(g_ptr_array_index (priv->attachments, id)); - g_ptr_array_add (temp_arr, (gpointer)attachment); - g_ptr_array_remove_index (priv->attachments, id); - nrem++; - } - - items = items->next; - } - - g_ptr_array_foreach (temp_arr, (GFunc)g_object_unref, NULL); - g_ptr_array_free (temp_arr, TRUE); - - update (bar); - - g_signal_emit (bar, signals[CHANGED], 0); - - id++; - - if ((left = gnome_icon_list_get_num_icons ((GnomeIconList *) bar)) > 0) - gnome_icon_list_focus_icon ((GnomeIconList *) bar, left > id ? id : left - 1); -} - -void e_attachment_bar_set_width(EAttachmentBar *bar, int bar_width) { int per_col, rows, height, width; @@ -615,37 +763,6 @@ e_attachment_bar_set_width(EAttachmentBar *bar, int bar_width) gtk_widget_set_size_request ((GtkWidget *)bar, bar_width, rows * height); } -void -e_attachment_bar_edit_selected (EAttachmentBar *bar) -{ - struct _EAttachmentBarPrivate *priv; - EAttachment *attachment; - GList *items; - int id; - - g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); - - priv = bar->priv; - - items = gnome_icon_list_get_selection ((GnomeIconList *) bar); - while (items != NULL) { - if ((id = GPOINTER_TO_INT (items->data)) < priv->attachments->len) { - attachment = priv->attachments->pdata[id]; - e_attachment_edit (attachment, GTK_WIDGET (bar)); - } - - items = items->next; - } -} - -GtkWidget ** -e_attachment_bar_get_selector(EAttachmentBar *bar) -{ - g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL); - - return &bar->priv->attach; -} - /** * e_attachment_bar_get_selected: * @bar: an #EAttachmentBar object @@ -656,7 +773,7 @@ e_attachment_bar_get_selector(EAttachmentBar *bar) GSList * e_attachment_bar_get_selected (EAttachmentBar *bar) { - struct _EAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; GSList *attachments = NULL; EAttachment *attachment; GList *items; @@ -696,7 +813,7 @@ e_attachment_bar_get_selected (EAttachmentBar *bar) GSList * e_attachment_bar_get_attachment (EAttachmentBar *bar, int id) { - struct _EAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; EAttachment *attachment; GSList *attachments; @@ -724,7 +841,7 @@ e_attachment_bar_get_attachment (EAttachmentBar *bar, int id) GSList * e_attachment_bar_get_all_attachments (EAttachmentBar *bar) { - struct _EAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; GSList *attachments = NULL; EAttachment *attachment; int i; @@ -748,7 +865,7 @@ e_attachment_bar_get_all_attachments (EAttachmentBar *bar) GSList * e_attachment_bar_get_parts (EAttachmentBar *bar) { - struct _EAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; EAttachment *attachment; GSList *parts = NULL; int i; @@ -758,50 +875,22 @@ e_attachment_bar_get_parts (EAttachmentBar *bar) priv = bar->priv; for (i = 0; i < priv->attachments->len; i++) { + CamelMimePart *mime_part; + attachment = priv->attachments->pdata[i]; + mime_part = e_attachment_get_mime_part (attachment); + if (attachment->is_available_local) - parts = g_slist_prepend (parts, attachment->body); + parts = g_slist_prepend (parts, mime_part); } return parts; } -/* GtkObject methods. */ - -static void -destroy (GtkObject *object) -{ - EAttachmentBar *bar = (EAttachmentBar *) object; - struct _EAttachmentBarPrivate *priv = bar->priv; - EAttachment *attachment; - int i; - - if ((priv = bar->priv)) { - priv->batch_unref = TRUE; - for (i = 0; i < priv->attachments->len; i++) { - attachment = priv->attachments->pdata[i]; - g_object_weak_unref ((GObject *) attachment, (GWeakNotify) attachment_destroy, bar); - g_object_unref (attachment); - } - g_ptr_array_free (priv->attachments, TRUE); - - if (priv->attach) - gtk_widget_destroy (priv->attach); - - if (priv->path) - g_free (priv->path); - - g_free (priv); - bar->priv = NULL; - } - - if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL) - (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); -} - static char * -temp_save_part (CamelMimePart *part, gboolean readonly) +temp_save_part (EAttachment *attachment, gboolean readonly) { + CamelMimePart *mime_part; const char *filename; char *tmpdir, *path, *mfilename = NULL, *utf8_mfilename = NULL; CamelStream *stream; @@ -810,7 +899,9 @@ temp_save_part (CamelMimePart *part, gboolean readonly) if (!(tmpdir = e_mkdtemp ("evolution-tmp-XXXXXX"))) return NULL; - if (!(filename = camel_mime_part_get_filename (part))) { + mime_part = e_attachment_get_mime_part (attachment); + + if (!(filename = camel_mime_part_get_filename (mime_part))) { /* This is the default filename used for temporary file creation */ filename = _("Unknown"); } else { @@ -825,7 +916,7 @@ temp_save_part (CamelMimePart *part, gboolean readonly) g_free (tmpdir); g_free (mfilename); - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); if (readonly) stream = camel_stream_fs_new_with_name (path, O_RDWR|O_CREAT|O_TRUNC, 0444); else @@ -853,9 +944,344 @@ temp_save_part (CamelMimePart *part, gboolean readonly) } static void -eab_drag_data_get(EAttachmentBar *bar, GdkDragContext *drag, GtkSelectionData *data, guint info, guint time) +attachment_bar_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_BACKGROUND_FILENAME: + e_attachment_bar_set_background_filename ( + E_ATTACHMENT_BAR (object), + g_value_get_string (value)); + return; + + case PROP_BACKGROUND_OPTIONS: + e_attachment_bar_set_background_options ( + E_ATTACHMENT_BAR (object), + g_value_get_string (value)); + return; + + case PROP_CURRENT_FOLDER: + e_attachment_bar_set_current_folder ( + E_ATTACHMENT_BAR (object), + g_value_get_string (value)); + return; + + case PROP_EDITABLE: + e_attachment_bar_set_editable ( + E_ATTACHMENT_BAR (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +attachment_bar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_BACKGROUND_FILENAME: + g_value_set_string ( + value, + e_attachment_bar_get_background_filename ( + E_ATTACHMENT_BAR (object))); + return; + + case PROP_BACKGROUND_OPTIONS: + g_value_set_string ( + value, + e_attachment_bar_get_background_options ( + E_ATTACHMENT_BAR (object))); + return; + + case PROP_CURRENT_FOLDER: + g_value_set_string ( + value, + e_attachment_bar_get_current_folder ( + E_ATTACHMENT_BAR (object))); + return; + + case PROP_EDITABLE: + g_value_set_boolean ( + value, + e_attachment_bar_get_editable ( + E_ATTACHMENT_BAR (object))); + return; + + case PROP_UI_MANAGER: + g_value_set_object ( + value, + e_attachment_bar_get_ui_manager ( + E_ATTACHMENT_BAR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +attachment_bar_dispose (GObject *object) +{ + EAttachmentBarPrivate *priv; + guint ii; + + priv = E_ATTACHMENT_BAR_GET_PRIVATE (object); + + priv->batch_unref = TRUE; + + for (ii = 0; ii < priv->attachments->len; ii++) { + EAttachment *attachment; + + attachment = priv->attachments->pdata[ii]; + g_object_weak_unref ( + G_OBJECT (attachment), (GWeakNotify) + attachment_destroy, object); + g_object_unref (attachment); + } + g_ptr_array_set_size (priv->attachments, 0); + + if (priv->ui_manager != NULL) { + g_object_unref (priv->ui_manager); + priv->ui_manager = NULL; + } + + if (priv->standard_actions != NULL) { + g_object_unref (priv->standard_actions); + priv->standard_actions = NULL; + } + + if (priv->editable_actions != NULL) { + g_object_unref (priv->editable_actions); + priv->editable_actions = NULL; + } + + if (priv->open_actions != NULL) { + g_object_unref (priv->open_actions); + priv->open_actions = NULL; + } + + /* Chain up to parent's dipose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +attachment_bar_finalize (GObject *object) +{ + EAttachmentBarPrivate *priv; + + priv = E_ATTACHMENT_BAR_GET_PRIVATE (object); + + g_ptr_array_free (priv->attachments, TRUE); + g_free (priv->current_folder); + g_free (priv->path); + + g_free (priv->background_filename); + g_free (priv->background_options); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +attachment_bar_constructed (GObject *object) +{ + EAttachmentBarPrivate *priv; + GtkActionGroup *action_group; + GConfBridge *bridge; + const gchar *prop; + const gchar *key; + + priv = E_ATTACHMENT_BAR_GET_PRIVATE (object); + action_group = priv->editable_actions; + bridge = gconf_bridge_get (); + + e_mutual_binding_new ( + G_OBJECT (object), "editable", + G_OBJECT (action_group), "visible"); + + prop = "background-filename"; + key = "/desktop/gnome/background/picture_filename"; + gconf_bridge_bind_property (bridge, key, object, prop); + + prop = "background-options"; + key = "/desktop/gnome/background/picture_options"; + gconf_bridge_bind_property (bridge, key, object, prop); +} + +static gboolean +attachment_bar_event (GtkWidget *widget, + GdkEvent *event) { - struct _EAttachmentBarPrivate *priv = bar->priv; + EAttachment *attachment; + gboolean ret = FALSE; + gpointer parent; + CamelURL *url; + char *path; + GSList *p; + + if (event->type != GDK_2BUTTON_PRESS) + return FALSE; + + parent = gtk_widget_get_toplevel (widget); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + p = e_attachment_bar_get_selected (E_ATTACHMENT_BAR (widget)); + /* check if has body already, remote files can take longer to fetch */ + if (p && p->next == NULL && e_attachment_get_mime_part (p->data) != NULL) { + attachment = p->data; + + /* Check if the file is stored already */ + if (!attachment->store_uri) { + path = temp_save_part (attachment, TRUE); + url = camel_url_new ("file://", NULL); + camel_url_set_path (url, path); + attachment->store_uri = camel_url_to_string (url, 0); + camel_url_free (url); + g_free (path); + } + + e_show_uri (parent, attachment->store_uri); + + ret = TRUE; + } + + g_slist_foreach (p, (GFunc) g_object_unref, NULL); + g_slist_free (p); + + return ret; +} + +static gboolean +attachment_bar_button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + GnomeIconList *icon_list; + GList *selected, *tmp; + int length, icon_number; + gboolean take_selected = FALSE; + + GtkTargetEntry drag_types[] = { + { "text/uri-list", 0, 0 }, + }; + + icon_list = GNOME_ICON_LIST (widget); + selected = gnome_icon_list_get_selection (icon_list); + length = g_list_length (selected); + + icon_number = gnome_icon_list_get_icon_at ( + icon_list, event->x, event->y); + if (icon_number < 0) { + /* When nothing is selected, deselect all */ + gnome_icon_list_unselect_all (icon_list); + length = 0; + selected = NULL; + } + + if (event->button == 1) { + /* If something is selected, then allow drag or else help to select */ + if (length) + gtk_drag_source_set ( + widget, GDK_BUTTON1_MASK, drag_types, + G_N_ELEMENTS (drag_types), GDK_ACTION_COPY); + else + gtk_drag_source_unset (widget); + goto exit; + } + + /* If not r-click dont progress any more.*/ + if (event->button != 3) + goto exit; + + /* When a r-click on something, if it is in the already selected list, consider a r-click of multiple things + * or deselect all and select only this for r-click + */ + if (icon_number >= 0) { + for (tmp = selected; tmp; tmp = tmp->next) { + if (GPOINTER_TO_INT (tmp->data) == icon_number) + take_selected = TRUE; + } + + if (!take_selected) { + gnome_icon_list_unselect_all (icon_list); + gnome_icon_list_select_icon (icon_list, icon_number); + } + } + + attachment_bar_show_popup_menu (E_ATTACHMENT_BAR (widget), event); + +exit: + /* Chain up to parent's button_press_event() method. */ + return GTK_WIDGET_CLASS (parent_class)-> + button_press_event (widget, event); +} + +static gboolean +attachment_bar_button_release_event (GtkWidget *widget, + GdkEventButton *event) +{ + GnomeIconList *icon_list; + GList *selected; + + GtkTargetEntry drag_types[] = { + { "text/uri-list", 0, 0 }, + }; + + if (event->button != 1) + goto exit; + + icon_list = GNOME_ICON_LIST (widget); + selected = gnome_icon_list_get_selection (icon_list); + + if (selected != NULL) + gtk_drag_source_set ( + widget, GDK_BUTTON1_MASK, drag_types, + G_N_ELEMENTS (drag_types), GDK_ACTION_COPY); + else + gtk_drag_source_unset (widget); + +exit: + /* Chain up to parent's button_release_event() method. */ + return GTK_WIDGET_CLASS (parent_class)-> + button_release_event (widget, event); +} + +static gboolean +attachment_bar_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + EAttachmentBar *attachment_bar; + gboolean editable; + + attachment_bar = E_ATTACHMENT_BAR (widget); + editable = e_attachment_bar_get_editable (attachment_bar); + + if (editable && event->keyval == GDK_Delete) { + GtkActionGroup *action_group; + GtkAction *action; + + action_group = attachment_bar->priv->editable_actions; + action = gtk_action_group_get_action (action_group, "remove"); + gtk_action_activate (action); + } + + /* Chain up to parent's key_press_event() method. */ + return GTK_WIDGET_CLASS (parent_class)-> + key_press_event (widget, event); +} + +static void +attachment_bar_drag_data_get (GtkWidget *widget, + GdkDragContext *drag, + GtkSelectionData *data, + guint info, + guint time) +{ + EAttachmentBarPrivate *priv; EAttachment *attachment; char *path, **uris; int len, n, i = 0; @@ -865,7 +1291,8 @@ eab_drag_data_get(EAttachmentBar *bar, GdkDragContext *drag, GtkSelectionData *d if (info) return; - items = gnome_icon_list_get_selection (GNOME_ICON_LIST (bar)); + priv = E_ATTACHMENT_BAR_GET_PRIVATE (widget); + items = gnome_icon_list_get_selection (GNOME_ICON_LIST (widget)); len = g_list_length (items); uris = g_malloc0 (sizeof (char *) * (len + 1)); @@ -885,7 +1312,7 @@ eab_drag_data_get(EAttachmentBar *bar, GdkDragContext *drag, GtkSelectionData *d } /* If we are not able to save, ignore it */ - if (!(path = temp_save_part (attachment->body, FALSE))) + if (!(path = temp_save_part (attachment, FALSE))) continue; url = camel_url_new ("file://", NULL); @@ -902,227 +1329,280 @@ eab_drag_data_get(EAttachmentBar *bar, GdkDragContext *drag, GtkSelectionData *d gtk_selection_data_set_uris (data, uris); g_free (uris); - - return; } static gboolean -eab_button_release_event(EAttachmentBar *bar, GdkEventButton *event, gpointer dummy) +attachment_bar_popup_menu (GtkWidget *widget) { - GnomeIconList *icon_list = GNOME_ICON_LIST(bar); - GList *selected; - int length; - GtkTargetEntry drag_types[] = { - { "text/uri-list", 0, 0 }, - }; - - if (event && event->button == 1) { - selected = gnome_icon_list_get_selection(icon_list); - length = g_list_length (selected); - if (length) - gtk_drag_source_set((GtkWidget *)bar, GDK_BUTTON1_MASK, drag_types, G_N_ELEMENTS(drag_types), GDK_ACTION_COPY); - else - gtk_drag_source_unset((GtkWidget *)bar); - } + attachment_bar_show_popup_menu (E_ATTACHMENT_BAR (widget), NULL); - return FALSE; + return TRUE; } -static gboolean -eab_button_press_event(EAttachmentBar *bar, GdkEventButton *event, gpointer dummy) +static void +attachment_bar_update_actions (EAttachmentBar *attachment_bar) { - GnomeIconList *icon_list = GNOME_ICON_LIST(bar); - GList *selected = NULL, *tmp; - int length, icon_number; - gboolean take_selected = FALSE; - GtkTargetEntry drag_types[] = { - { "text/uri-list", 0, 0 }, - }; + GnomeIconList *icon_list; + CamelMimePart *mime_part; + GtkUIManager *ui_manager; + GtkActionGroup *action_group; + GtkAction *action; + GList *selection; + guint n_selected; + gboolean is_image; + gpointer parent; + guint merge_id; - selected = gnome_icon_list_get_selection(icon_list); - length = g_list_length (selected); + icon_list = GNOME_ICON_LIST (attachment_bar); + selection = gnome_icon_list_get_selection (icon_list); + n_selected = g_list_length (selection); - if (event) { - icon_number = gnome_icon_list_get_icon_at(icon_list, event->x, event->y); - if (icon_number < 0) { - /* When nothing is selected, deselect all */ - gnome_icon_list_unselect_all (icon_list); - length = 0; - selected = NULL; - } + parent = gtk_widget_get_toplevel (GTK_WIDGET (attachment_bar)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; - if (event->button == 1) { - /* If something is selected, then allow drag or else help to select */ - if (length) - gtk_drag_source_set((GtkWidget *)bar, GDK_BUTTON1_MASK, drag_types, G_N_ELEMENTS(drag_types), GDK_ACTION_COPY); - else - gtk_drag_source_unset((GtkWidget *)bar); - return FALSE; - } + is_image = FALSE; + mime_part = NULL; - /* If not r-click dont progress any more.*/ - if (event->button != 3) - return FALSE; - - /* When a r-click on something, if it is in the already selected list, consider a r-click of multiple things - * or deselect all and select only this for r-click - */ - if (icon_number >= 0) { - for (tmp = selected; tmp; tmp = tmp->next) { - if (GPOINTER_TO_INT(tmp->data) == icon_number) - take_selected = TRUE; - } + if (n_selected == 1) { + GPtrArray *array; + EAttachment *attachment; + gint index; - if (!take_selected) { - gnome_icon_list_unselect_all(icon_list); - gnome_icon_list_select_icon(icon_list, icon_number); - } - } + array = attachment_bar->priv->attachments; + index = GPOINTER_TO_INT (selection->data); + attachment = E_ATTACHMENT (array->pdata[index]); + mime_part = e_attachment_get_mime_part (attachment); + is_image = e_attachment_is_image (attachment); } - return FALSE; -} - -static gboolean -eab_icon_clicked_cb (EAttachmentBar *bar, GdkEvent *event, gpointer *dummy) -{ - EAttachment *attachment; - gboolean ret = FALSE; - CamelURL *url; - char *path; - GSList *p; - - if (E_IS_ATTACHMENT_BAR (bar) && event->type == GDK_2BUTTON_PRESS) { - p = e_attachment_bar_get_selected (bar); - /* check if has body already, remote files can take longer to fetch */ - if (p && p->next == NULL && ((EAttachment *)p->data)->body) { - attachment = p->data; - - /* Check if the file is stored already */ - if (!attachment->store_uri) { - path = temp_save_part (attachment->body, TRUE); - url = camel_url_new ("file://", NULL); - camel_url_set_path (url, path); - attachment->store_uri = camel_url_to_string (url, 0); - camel_url_free (url); - g_free (path); - } + ui_manager = e_attachment_bar_get_ui_manager (attachment_bar); - /* FIXME Pass a parent window. */ - e_show_uri (NULL, attachment->store_uri); + action_group = attachment_bar->priv->standard_actions; - ret = TRUE; - } + action = gtk_action_group_get_action (action_group, "save-as"); + gtk_action_set_visible (action, n_selected > 0); - if (p) { - g_slist_foreach (p, (GFunc) g_object_unref, NULL); - g_slist_free (p); - } - } + action = gtk_action_group_get_action (action_group, "set-background"); + gtk_action_set_visible (action, is_image); - return ret; -} + action_group = attachment_bar->priv->editable_actions; -/* Initialization. */ + action = gtk_action_group_get_action (action_group, "properties"); + gtk_action_set_visible (action, n_selected == 1); -static void -class_init (EAttachmentBarClass *klass) -{ - GtkObjectClass *object_class; + action = gtk_action_group_get_action (action_group, "remove"); + gtk_action_set_visible (action, n_selected > 0); - object_class = GTK_OBJECT_CLASS (klass); + action_group = attachment_bar->priv->open_actions; - parent_class = g_type_class_ref (gnome_icon_list_get_type ()); + merge_id = attachment_bar->priv->merge_id; + gtk_ui_manager_remove_ui (ui_manager, merge_id); + e_action_group_remove_all_actions (action_group); - object_class->destroy = destroy; + if (mime_part == NULL) + return; - /* Setup signals. */ + e_mime_part_utils_add_open_actions ( + mime_part, ui_manager, action_group, + "/attachment-popup/open-actions", parent, merge_id); +} - signals[CHANGED] = - g_signal_new ("changed", - E_TYPE_ATTACHMENT_BAR, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EAttachmentBarClass, changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); +static void +attachment_bar_class_init (EAttachmentBarClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EAttachmentBarPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = attachment_bar_set_property; + object_class->get_property = attachment_bar_get_property; + object_class->dispose = attachment_bar_dispose; + object_class->finalize = attachment_bar_finalize; + object_class->constructed = attachment_bar_constructed; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->event = attachment_bar_event; + widget_class->button_press_event = attachment_bar_button_press_event; + widget_class->button_release_event = attachment_bar_button_release_event; + widget_class->key_press_event = attachment_bar_key_press_event; + widget_class->drag_data_get = attachment_bar_drag_data_get; + widget_class->popup_menu = attachment_bar_popup_menu; + + class->update_actions = attachment_bar_update_actions; + + g_object_class_install_property ( + object_class, + PROP_BACKGROUND_FILENAME, + g_param_spec_string ( + "background-filename", + "Background Filename", + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_BACKGROUND_OPTIONS, + g_param_spec_string ( + "background-options", + "Background Options", + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_CURRENT_FOLDER, + g_param_spec_string ( + "current-folder", + "Current Folder", + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_EDITABLE, + g_param_spec_boolean ( + "editable", + "Editable", + NULL, + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_UI_MANAGER, + g_param_spec_object ( + "ui-manager", + "UI Manager", + NULL, + GTK_TYPE_UI_MANAGER, + G_PARAM_READABLE)); + + signals[CHANGED] = g_signal_new ( + "changed", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAttachmentBarClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[UPDATE_ACTIONS] = g_signal_new ( + "update-actions", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EAttachmentBarClass, update_actions), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void -init (EAttachmentBar *bar) +attachment_bar_init (EAttachmentBar *bar) { - struct _EAttachmentBarPrivate *priv; + GnomeIconList *icon_list; + GtkUIManager *ui_manager; + GtkActionGroup *action_group; + gint icon_width, window_height; + const gchar *domain = GETTEXT_PACKAGE; + GError *error = NULL; + + bar->priv = E_ATTACHMENT_BAR_GET_PRIVATE (bar); + bar->priv->attachments = g_ptr_array_new (); + + GTK_WIDGET_SET_FLAGS (bar, GTK_CAN_FOCUS); + + icon_list = GNOME_ICON_LIST (bar); - priv = g_new (struct _EAttachmentBarPrivate, 1); + calculate_height_width (bar, &icon_width, &window_height); + gnome_icon_list_construct (icon_list, icon_width, NULL, 0); - priv->attach = NULL; - priv->batch_unref = FALSE; - priv->attachments = g_ptr_array_new (); + gtk_widget_set_size_request ( + GTK_WIDGET (bar), icon_width * 4, window_height); - priv->path = NULL; + atk_object_set_name ( + gtk_widget_get_accessible (GTK_WIDGET (bar)), + _("Attachment Bar")); - bar->priv = priv; - bar->expand = FALSE; + 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); + + ui_manager = gtk_ui_manager_new (); + bar->priv->ui_manager = ui_manager; + bar->priv->merge_id = gtk_ui_manager_new_merge_id (ui_manager); + + action_group = gtk_action_group_new ("standard"); + gtk_action_group_set_translation_domain (action_group, domain); + gtk_action_group_add_actions ( + action_group, standard_entries, + G_N_ELEMENTS (standard_entries), bar); + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + bar->priv->standard_actions = action_group; + + action_group = gtk_action_group_new ("editable"); + gtk_action_group_set_translation_domain (action_group, domain); + gtk_action_group_add_actions ( + action_group, editable_entries, + G_N_ELEMENTS (editable_entries), bar); + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + bar->priv->editable_actions = action_group; + + action_group = gtk_action_group_new ("open"); + gtk_action_group_set_translation_domain (action_group, domain); + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + bar->priv->open_actions = action_group; + + /* Because we are loading from a hard-coded string, there is + * no chance of I/O errors. Failure here imples a malformed + * UI definition. Full stop. */ + gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error); + if (error != NULL) + g_error ("%s", error->message); } - GType e_attachment_bar_get_type (void) { static GType type = 0; - if (type == 0) { - static const GTypeInfo info = { + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { sizeof (EAttachmentBarClass), - NULL, NULL, - (GClassInitFunc) class_init, - NULL, NULL, + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) attachment_bar_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ sizeof (EAttachmentBar), - 0, - (GInstanceInitFunc) init, + 0, /* n_preallocs */ + (GInstanceInitFunc) attachment_bar_init, + NULL /* value_table */ }; - type = g_type_register_static (GNOME_TYPE_ICON_LIST, "EAttachmentBar", &info, 0); + type = g_type_register_static ( + GNOME_TYPE_ICON_LIST, "EAttachmentBar", &type_info, 0); } return type; } GtkWidget * -e_attachment_bar_new (GtkAdjustment *adj) +e_attachment_bar_new (void) { - EAttachmentBar *new; - GnomeIconList *icon_list; - int icon_width, window_height; - - new = g_object_new (e_attachment_bar_get_type (), NULL); - - icon_list = GNOME_ICON_LIST (new); - - calculate_height_width (new, &icon_width, &window_height); - - gnome_icon_list_construct (icon_list, icon_width, adj, 0); - - gtk_widget_set_size_request (GTK_WIDGET (new), icon_width * 4, window_height); - - GTK_WIDGET_SET_FLAGS (new, GTK_CAN_FOCUS); - - 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); - - atk_object_set_name (gtk_widget_get_accessible (GTK_WIDGET (new)), - _("Attachment Bar")); - - g_signal_connect (new, "button_release_event", G_CALLBACK(eab_button_release_event), NULL); - g_signal_connect (new, "button_press_event", G_CALLBACK(eab_button_press_event), NULL); - g_signal_connect (new, "drag-data-get", G_CALLBACK(eab_drag_data_get), NULL); - g_signal_connect (icon_list, "event", G_CALLBACK (eab_icon_clicked_cb), NULL); - - return GTK_WIDGET (new); + return g_object_new (E_TYPE_ATTACHMENT_BAR, NULL); } static char * @@ -1159,12 +1639,14 @@ attach_to_multipart (CamelMultipart *multipart, { CamelContentType *content_type; CamelDataWrapper *content; + CamelMimePart *mime_part; - if (!attachment->body) + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part == NULL) return; - content_type = camel_mime_part_get_content_type (attachment->body); - content = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body)); + content_type = camel_mime_part_get_content_type (mime_part); + content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); if (!CAMEL_IS_MULTIPART (content)) { if (camel_content_type_is (content_type, "text", "*")) { @@ -1188,7 +1670,7 @@ attach_to_multipart (CamelMultipart *multipart, camel_object_unref (filter_stream); encoding = camel_mime_filter_bestenc_get_best_encoding (bestenc, CAMEL_BESTENC_8BIT); - camel_mime_part_set_encoding (attachment->body, encoding); + camel_mime_part_set_encoding (mime_part, encoding); if (encoding == CAMEL_TRANSFER_ENCODING_7BIT) { /* the text fits within us-ascii so this is safe */ @@ -1207,24 +1689,26 @@ attach_to_multipart (CamelMultipart *multipart, /* looks kinda nasty, but this is how ya have to do it */ camel_content_type_set_param (content_type, "charset", default_charset); type = camel_content_type_format (content_type); - camel_mime_part_set_content_type (attachment->body, type); + camel_mime_part_set_content_type (mime_part, type); g_free (type); g_free (buf); } camel_object_unref (bestenc); } else if (!CAMEL_IS_MIME_MESSAGE (content)) { - camel_mime_part_set_encoding (attachment->body, CAMEL_TRANSFER_ENCODING_BASE64); + camel_mime_part_set_encoding (mime_part, CAMEL_TRANSFER_ENCODING_BASE64); } } - camel_multipart_add_part (multipart, attachment->body); + camel_multipart_add_part (multipart, mime_part); } void -e_attachment_bar_to_multipart (EAttachmentBar *bar, CamelMultipart *multipart, const char *default_charset) +e_attachment_bar_to_multipart (EAttachmentBar *bar, + CamelMultipart *multipart, + const gchar *default_charset) { - struct _EAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; EAttachment *attachment; int i; @@ -1249,32 +1733,73 @@ e_attachment_bar_get_num_attachments (EAttachmentBar *bar) } void -e_attachment_bar_attach (EAttachmentBar *bar, const char *file_name, const char *disposition) +e_attachment_bar_attach (EAttachmentBar *bar, + const gchar *filename, + const gchar *disposition) { + EAttachment *attachment; + CamelException ex; + g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); - g_return_if_fail (file_name != NULL && disposition != NULL); + g_return_if_fail (filename != NULL); + g_return_if_fail (disposition != NULL); + + camel_exception_init (&ex); + + attachment = e_attachment_new (filename, disposition, &ex); - add_from_file (bar, file_name, disposition); + if (attachment != NULL) + e_attachment_bar_add_attachment (bar, attachment); + else { + GtkWidget *toplevel; + + /* FIXME: Avoid using error from mailer */ + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (bar)); + e_error_run ( + GTK_WINDOW (toplevel), "mail-composer:no-attach", + filename, camel_exception_get_description (&ex), NULL); + camel_exception_clear (&ex); + } } void -e_attachment_bar_add_attachment (EAttachmentBar *bar, EAttachment *attachment) +e_attachment_bar_add_attachment (EAttachmentBar *bar, + EAttachment *attachment) { g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + g_ptr_array_add (bar->priv->attachments, attachment); - add_common (bar, attachment); + g_object_weak_ref ( + G_OBJECT (attachment), (GWeakNotify) + attachment_destroy, bar); + + g_signal_connect_swapped ( + attachment, "changed", + G_CALLBACK (e_attachment_bar_refresh), bar); + + e_attachment_bar_refresh (bar); + + g_signal_emit (bar, signals[CHANGED], 0); } void -e_attachment_bar_add_attachment_silent (EAttachmentBar *bar, EAttachment *attachment) +e_attachment_bar_add_attachment_silent (EAttachmentBar *bar, + EAttachment *attachment) { g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); - g_return_if_fail (attachment != NULL); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); g_ptr_array_add (bar->priv->attachments, attachment); - g_object_weak_ref ((GObject *) attachment, (GWeakNotify) attachment_destroy, bar); - g_signal_connect (attachment, "changed", G_CALLBACK (attachment_changed_cb), bar); + g_object_weak_ref ( + G_OBJECT (attachment), (GWeakNotify) + attachment_destroy, bar); + + g_signal_connect_swapped ( + attachment, "changed", + G_CALLBACK (e_attachment_bar_refresh), bar); g_signal_emit (bar, signals[CHANGED], 0); } @@ -1282,13 +1807,180 @@ e_attachment_bar_add_attachment_silent (EAttachmentBar *bar, EAttachment *attach void e_attachment_bar_refresh (EAttachmentBar *bar) { - update (bar); + EAttachmentBarPrivate *priv; + GnomeIconList *icon_list; + int bar_width, bar_height; + int i; + + g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); + + priv = bar->priv; + icon_list = GNOME_ICON_LIST (bar); + + gnome_icon_list_freeze (icon_list); + + gnome_icon_list_clear (icon_list); + + /* FIXME could be faster, but we don't care. */ + for (i = 0; i < priv->attachments->len; i++) { + EAttachment *attachment; + CamelContentType *content_type; + CamelMimePart *mime_part; + char *size_string, *label; + GdkPixbuf *pixbuf = NULL; + const char *desc; + + attachment = priv->attachments->pdata[i]; + mime_part = e_attachment_get_mime_part (attachment); + + if (!attachment->is_available_local || mime_part == NULL) { + if ((pixbuf = e_icon_factory_get_icon("mail-attachment", E_ICON_SIZE_DIALOG))) { + attachment->index = gnome_icon_list_append_pixbuf (icon_list, pixbuf, NULL, ""); + g_object_unref (pixbuf); + } + continue; + } + + content_type = camel_mime_part_get_content_type (mime_part); + /* Get the image out of the attachment + and create a thumbnail for it */ + pixbuf = e_attachment_get_thumbnail (attachment); + if (pixbuf != NULL) + g_object_ref (pixbuf); + else if (camel_content_type_is(content_type, "image", "*")) { + CamelDataWrapper *wrapper; + CamelStreamMem *mstream; + GdkPixbufLoader *loader; + gboolean error = TRUE; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + mstream = (CamelStreamMem *) camel_stream_mem_new (); + + camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream); + + /* Stream image into pixbuf loader */ + loader = gdk_pixbuf_loader_new (); + error = !gdk_pixbuf_loader_write (loader, mstream->buffer->data, mstream->buffer->len, NULL); + gdk_pixbuf_loader_close (loader, NULL); + + if (!error) { + /* The loader owns the reference. */ + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + /* This returns a new GdkPixbuf. */ + pixbuf = scale_pixbuf (pixbuf); + e_attachment_set_thumbnail (attachment, pixbuf); + } else { + pixbuf = NULL; + g_warning ("GdkPixbufLoader Error"); + } + + /* Destroy everything */ + g_object_unref (loader); + camel_object_unref (mstream); + } else if (!bar->expand && (pixbuf = get_system_thumbnail (attachment, content_type))) { + /* This returns a new GdkPixbuf. */ + pixbuf = scale_pixbuf (pixbuf); + e_attachment_set_thumbnail (attachment, pixbuf); + } + + desc = camel_mime_part_get_description (mime_part); + if (desc == NULL || *desc == '\0') + desc = e_attachment_get_filename (attachment); + if (desc == NULL || *desc == '\0') + desc = camel_mime_part_get_filename (mime_part); + + if (!desc) + desc = _("attachment"); + + if (attachment->size && (size_string = g_format_size_for_display (attachment->size))) { + label = g_strdup_printf ("%s (%s)", desc, size_string); + g_free (size_string); + } else + label = g_strdup (desc); + + if (pixbuf == NULL) { + char *mime_type; + + mime_type = camel_content_type_simple (content_type); + pixbuf = e_icon_for_mime_type (mime_type, 48); + if (pixbuf == NULL) { + g_warning("cannot find icon for mime type %s (installation problem?)", mime_type); + pixbuf = e_icon_factory_get_icon("mail-attachment", E_ICON_SIZE_DIALOG); + } + g_free (mime_type); + + /* remember this picture and use it later again */ + if (pixbuf) + e_attachment_set_thumbnail (attachment, pixbuf); + } + + if (pixbuf) { + GdkPixbuf *pixbuf_orig = pixbuf; + pixbuf = gdk_pixbuf_add_alpha (pixbuf_orig, TRUE, 255, 255, 255); + + /* gdk_pixbuf_add_alpha returns a newly allocated pixbuf, + free the original one. + */ + g_object_unref (pixbuf_orig); + + /* In case of a attachment bar, in a signed/encrypted part, display the status as a emblem*/ + if (attachment->sign) { + /* Show the signature status at the right-bottom.*/ + GdkPixbuf *sign = NULL; + int x, y; + + if (attachment->sign == CAMEL_CIPHER_VALIDITY_SIGN_BAD) + sign = e_icon_factory_get_icon ("stock_signature-bad", E_ICON_SIZE_MENU); + else if (attachment->sign == CAMEL_CIPHER_VALIDITY_SIGN_GOOD) + sign = e_icon_factory_get_icon ("stock_signature-ok", E_ICON_SIZE_MENU); + else + sign = e_icon_factory_get_icon ("stock_signature", E_ICON_SIZE_MENU); + + x = gdk_pixbuf_get_width (pixbuf) - 17; + y = gdk_pixbuf_get_height (pixbuf) - 17; + + gdk_pixbuf_copy_area (sign, 0, 0, 16, 16, pixbuf, x, y); + g_object_unref (sign); + } + + if (attachment->encrypt) { + /* Show the encryption status at the top left.*/ + GdkPixbuf *encrypt = e_icon_factory_get_icon ("stock_lock-ok", E_ICON_SIZE_MENU); + + gdk_pixbuf_copy_area (encrypt, 0, 0, 16, 16, pixbuf, 1, 1); + g_object_unref (encrypt); + } + + gnome_icon_list_append_pixbuf (icon_list, pixbuf, NULL, label); + g_object_unref (pixbuf); + } + + g_free (label); + } + + gnome_icon_list_thaw (icon_list); + + /* Resize */ + if (bar->expand) { + gtk_widget_get_size_request ((GtkWidget *) bar, &bar_width, &bar_height); + + if (bar->priv->attachments->len) { + int per_col, rows, height, width; + + calculate_height_width(bar, &width, &height); + per_col = bar_width / width; + per_col = (per_col ? per_col : 1); + rows = (bar->priv->attachments->len + per_col -1) / per_col; + gtk_widget_set_size_request ((GtkWidget *) bar, bar_width, rows * height); + } + } } int e_attachment_bar_get_download_count (EAttachmentBar *bar) { - struct _EAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; EAttachment *attachment; int i, n = 0; @@ -1306,68 +1998,57 @@ e_attachment_bar_get_download_count (EAttachmentBar *bar) } void -e_attachment_bar_attach_remote_file (EAttachmentBar *bar, const char *url, const char *disposition) +e_attachment_bar_attach_remote_file (EAttachmentBar *bar, + const gchar *url, + const gchar *disposition) { EAttachment *attachment; CamelException ex; - GtkWindow *parent; + GtkWidget *parent; g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); - if (!bar->priv->path) + if (bar->priv->path == NULL) bar->priv->path = e_mkdtemp ("attach-XXXXXX"); - parent = (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) bar); + parent = gtk_widget_get_toplevel (GTK_WIDGET (bar)); camel_exception_init (&ex); - if ((attachment = e_attachment_new_remote_file (parent, url, disposition, bar->priv->path, &ex))) { - add_common (bar, attachment); - g_signal_connect (attachment, "update", G_CALLBACK (update_remote_file), bar); + + attachment = e_attachment_new_remote_file ( + GTK_WINDOW (parent), url, disposition, bar->priv->path, &ex); + + if (attachment != NULL) { + e_attachment_bar_add_attachment (bar, attachment); + g_signal_connect ( + attachment, "update", + G_CALLBACK (update_remote_file), bar); } else { - e_error_run (parent, "mail-composer:no-attach", - url, camel_exception_get_description (&ex), NULL); + e_error_run ( + GTK_WINDOW (parent), "mail-composer:no-attach", + url, camel_exception_get_description (&ex), NULL); camel_exception_clear (&ex); } } void -e_attachment_bar_attach_mime_part (EAttachmentBar *bar, CamelMimePart *part) +e_attachment_bar_attach_mime_part (EAttachmentBar *bar, + CamelMimePart *part) { - g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); - - add_from_mime_part (bar, part); -} - -static void -action_recent_cb (GtkAction *action, - EAttachmentBar *attachment_bar) -{ - GtkRecentChooser *chooser; - GFile *file; - gchar *uri; - - chooser = GTK_RECENT_CHOOSER (action); + EAttachment *attachment; - /* Wish: gtk_recent_chooser_get_current_file() */ - uri = gtk_recent_chooser_get_current_uri (chooser); - file = g_file_new_for_uri (uri); - g_free (uri); + /* XXX Is this function really worth keeping? */ - if (g_file_is_native (file)) - e_attachment_bar_attach ( - E_ATTACHMENT_BAR (attachment_bar), - g_file_get_path (file), "attachment"); - else - e_attachment_bar_attach_remote_file ( - E_ATTACHMENT_BAR (attachment_bar), - g_file_get_uri (file), "attachment"); + g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); + g_return_if_fail (CAMEL_IS_MIME_PART (part)); - g_object_unref (file); + attachment = e_attachment_new_from_mime_part (part); + e_attachment_bar_add_attachment (bar, attachment); } GtkAction * -e_attachment_bar_recent_action_new (EAttachmentBar *bar, - const gchar *action_name, - const gchar *action_label) +e_attachment_bar_recent_action_new (EAttachmentBar *bar, + const gchar *action_name, + const gchar *action_label) { GtkAction *action; GtkRecentChooser *chooser; @@ -1392,3 +2073,148 @@ e_attachment_bar_recent_action_new (EAttachmentBar *bar, return action; } +gint +e_attachment_bar_file_chooser_dialog_run (EAttachmentBar *attachment_bar, + GtkWidget *dialog) +{ + GtkFileChooser *file_chooser; + gint response = GTK_RESPONSE_NONE; + const gchar *current_folder; + gboolean save_folder; + + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), response); + g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), response); + + file_chooser = GTK_FILE_CHOOSER (dialog); + current_folder = e_attachment_bar_get_current_folder (attachment_bar); + gtk_file_chooser_set_current_folder (file_chooser, current_folder); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + save_folder = + (response == GTK_RESPONSE_ACCEPT) || + (response == GTK_RESPONSE_OK) || + (response == GTK_RESPONSE_YES) || + (response == GTK_RESPONSE_APPLY); + + if (save_folder) { + gchar *folder; + + folder = gtk_file_chooser_get_current_folder (file_chooser); + e_attachment_bar_set_current_folder (attachment_bar, folder); + g_free (folder); + } + + return response; +} + +void +e_attachment_bar_update_actions (EAttachmentBar *attachment_bar) +{ + g_return_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar)); + + g_signal_emit (attachment_bar, signals[UPDATE_ACTIONS], 0); +} + +const gchar * +e_attachment_bar_get_background_filename (EAttachmentBar *attachment_bar) +{ + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL); + + return attachment_bar->priv->background_filename; +} + +void +e_attachment_bar_set_background_filename (EAttachmentBar *attachment_bar, + const gchar *background_filename) +{ + EAttachmentBarPrivate *priv; + + g_return_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar)); + + if (background_filename == NULL) + background_filename = ""; + + priv = attachment_bar->priv; + g_free (priv->background_filename); + priv->background_filename = g_strdup (background_filename); + + g_object_notify (G_OBJECT (attachment_bar), "background-filename"); +} + +const gchar * +e_attachment_bar_get_background_options (EAttachmentBar *attachment_bar) +{ + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL); + + return attachment_bar->priv->background_options; +} + +void +e_attachment_bar_set_background_options (EAttachmentBar *attachment_bar, + const gchar *background_options) +{ + EAttachmentBarPrivate *priv; + + g_return_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar)); + + if (background_options == NULL) + background_options = "none"; + + priv = attachment_bar->priv; + g_free (priv->background_options); + priv->background_options = g_strdup (background_options); + + g_object_notify (G_OBJECT (attachment_bar), "background-options"); +} + +const gchar * +e_attachment_bar_get_current_folder (EAttachmentBar *attachment_bar) +{ + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL); + + return attachment_bar->priv->current_folder; +} + +void +e_attachment_bar_set_current_folder (EAttachmentBar *attachment_bar, + const gchar *current_folder) +{ + + g_return_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar)); + + if (current_folder == NULL) + current_folder = g_get_home_dir (); + + g_free (attachment_bar->priv->current_folder); + attachment_bar->priv->current_folder = g_strdup (current_folder); + + g_object_notify (G_OBJECT (attachment_bar), "current-folder"); +} + +gboolean +e_attachment_bar_get_editable (EAttachmentBar *attachment_bar) +{ + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), FALSE); + + return attachment_bar->priv->editable; +} + +void +e_attachment_bar_set_editable (EAttachmentBar *attachment_bar, + gboolean editable) +{ + g_return_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar)); + + attachment_bar->priv->editable = editable; + + g_object_notify (G_OBJECT (attachment_bar), "editable"); +} + +GtkUIManager * +e_attachment_bar_get_ui_manager (EAttachmentBar *attachment_bar) +{ + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (attachment_bar), NULL); + + return attachment_bar->priv->ui_manager; +} diff --git a/widgets/misc/e-attachment-bar.h b/widgets/misc/e-attachment-bar.h index 363b7f0fb8..a55dcb11f6 100644 --- a/widgets/misc/e-attachment-bar.h +++ b/widgets/misc/e-attachment-bar.h @@ -21,8 +21,8 @@ * */ -#ifndef __E_ATTACHMENT_BAR_H__ -#define __E_ATTACHMENT_BAR_H__ +#ifndef E_ATTACHMENT_BAR_H +#define E_ATTACHMENT_BAR_H #include <gtk/gtk.h> #include <libgnomeui/gnome-icon-list.h> @@ -30,70 +30,107 @@ #include <camel/camel-multipart.h> #include "e-attachment.h" -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - #define E_TYPE_ATTACHMENT_BAR \ (e_attachment_bar_get_type ()) #define E_ATTACHMENT_BAR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBar)) -#define E_ATTACHMENT_BAR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ATTACHMENT_BAR, EAttachmentBarClass)) + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBar)) +#define E_ATTACHMENT_BAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ATTACHMENT_BAR, EAttachmentBarClass)) #define E_IS_ATTACHMENT_BAR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ATTACHMENT_BAR)) -#define E_IS_ATTACHMENT_BAR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_ATTACHMENT_BAR)) + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ATTACHMENT_BAR)) +#define E_IS_ATTACHMENT_BAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ATTACHMENT_BAR)) +#define E_ATTACHMENT_BAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBarClass)) + +G_BEGIN_DECLS typedef struct _EAttachmentBar EAttachmentBar; typedef struct _EAttachmentBarClass EAttachmentBarClass; +typedef struct _EAttachmentBarPrivate EAttachmentBarPrivate; struct _EAttachmentBar { GnomeIconList parent; gboolean expand; - - struct _EAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; }; struct _EAttachmentBarClass { GnomeIconListClass parent_class; - void (* changed) (EAttachmentBar *bar); + /* Signals */ + void (*changed) (EAttachmentBar *bar); + void (*update_actions) (EAttachmentBar *bar); }; +GType e_attachment_bar_get_type (void); +GtkWidget * e_attachment_bar_new (void); +void e_attachment_bar_to_multipart (EAttachmentBar *bar, + CamelMultipart *multipart, + const gchar *default_charset); +guint e_attachment_bar_get_num_attachments + (EAttachmentBar *bar); +void e_attachment_bar_attach (EAttachmentBar *bar, + const gchar *filename, + const gchar *disposition); +void e_attachment_bar_attach_mime_part + (EAttachmentBar *bar, + CamelMimePart *part); +gint e_attachment_bar_get_download_count + (EAttachmentBar *bar); +void e_attachment_bar_attach_remote_file + (EAttachmentBar *bar, + const gchar *url, + const gchar *disposition); +GSList * e_attachment_bar_get_attachment (EAttachmentBar *bar, + gint id); +void e_attachment_bar_add_attachment (EAttachmentBar *bar, + EAttachment *attachment); +GSList * e_attachment_bar_get_parts (EAttachmentBar *bar); +GSList * e_attachment_bar_get_selected (EAttachmentBar *bar); +void e_attachment_bar_set_width (EAttachmentBar *bar, + gint bar_width); +GSList * e_attachment_bar_get_all_attachments + (EAttachmentBar *bar); +void e_attachment_bar_create_attachment_cache + (EAttachment *attachment); +GtkAction * e_attachment_bar_recent_action_new + (EAttachmentBar *bar, + const gchar *action_name, + const gchar *action_label); +void e_attachment_bar_add_attachment_silent + (EAttachmentBar *bar, + EAttachment *attachment); +void e_attachment_bar_refresh (EAttachmentBar *bar); +gint e_attachment_bar_file_chooser_dialog_run + (EAttachmentBar *attachment_bar, + GtkWidget *dialog); +void e_attachment_bar_update_actions (EAttachmentBar *attachment_bar); +const gchar * e_attachment_bar_get_background_filename + (EAttachmentBar *attachment_bar); +void e_attachment_bar_set_background_filename + (EAttachmentBar *attachment_bar, + const gchar *background_filename); +const gchar * e_attachment_bar_get_background_options + (EAttachmentBar *attachment_bar); +void e_attachment_bar_set_background_options + (EAttachmentBar *attachment_bar, + const gchar *background_options); +const gchar * e_attachment_bar_get_current_folder + (EAttachmentBar *attachment_bar); +void e_attachment_bar_set_current_folder + (EAttachmentBar *attachment_bar, + const gchar *current_folder); +gboolean e_attachment_bar_get_editable (EAttachmentBar *attachment_bar); +void e_attachment_bar_set_editable (EAttachmentBar *attachment_bar, + gboolean editable); +GtkUIManager * e_attachment_bar_get_ui_manager (EAttachmentBar *attachment_bar); -GType e_attachment_bar_get_type (void); - -GtkWidget *e_attachment_bar_new (GtkAdjustment *adj); -void e_attachment_bar_to_multipart (EAttachmentBar *bar, CamelMultipart *multipart, - const char *default_charset); -guint e_attachment_bar_get_num_attachments (EAttachmentBar *bar); -void e_attachment_bar_attach (EAttachmentBar *bar, const char *file_name, const char *disposition); -void e_attachment_bar_attach_mime_part (EAttachmentBar *bar, CamelMimePart *part); -int e_attachment_bar_get_download_count (EAttachmentBar *bar); -void e_attachment_bar_attach_remote_file (EAttachmentBar *bar, const char *url, const char *disposition); -GSList *e_attachment_bar_get_attachment (EAttachmentBar *bar, int id); -void e_attachment_bar_add_attachment (EAttachmentBar *bar, EAttachment *attachment); -void e_attachment_bar_edit_selected (EAttachmentBar *bar); -void e_attachment_bar_remove_selected (EAttachmentBar *bar); -GtkWidget ** e_attachment_bar_get_selector(EAttachmentBar *bar); -GSList *e_attachment_bar_get_parts (EAttachmentBar *bar); -GSList *e_attachment_bar_get_selected (EAttachmentBar *bar); -void e_attachment_bar_set_width(EAttachmentBar *bar, int bar_width); -GSList * e_attachment_bar_get_all_attachments (EAttachmentBar *bar); -void e_attachment_bar_create_attachment_cache (EAttachment *attachment); -GtkAction * -e_attachment_bar_recent_action_new (EAttachmentBar *bar, - const gchar *action_name, - const gchar *action_label); -void -e_attachment_bar_add_attachment_silent (EAttachmentBar *bar, EAttachment *attachment); -void -e_attachment_bar_refresh (EAttachmentBar *bar); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ +G_END_DECLS -#endif /* __E_ATTACHMENT_BAR_H__ */ +#endif /* E_ATTACHMENT_BAR_H */ diff --git a/widgets/misc/e-attachment-dialog.c b/widgets/misc/e-attachment-dialog.c new file mode 100644 index 0000000000..0668a7358d --- /dev/null +++ b/widgets/misc/e-attachment-dialog.c @@ -0,0 +1,413 @@ +/* + * e-attachment-dialog.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-attachment-dialog.h" + +#include <glib/gi18n.h> + +#define E_ATTACHMENT_DIALOG_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogPrivate)) + +struct _EAttachmentDialogPrivate { + EAttachment *attachment; + GtkWidget *filename_entry; + GtkWidget *description_entry; + GtkWidget *mime_type_label; + GtkWidget *disposition_checkbox; +}; + +enum { + PROP_0, + PROP_ATTACHMENT +}; + +static gpointer parent_class; + +static void +attachment_dialog_update (EAttachmentDialog *dialog) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + GtkWidget *widget; + gboolean sensitive; + const gchar *text; + gboolean active; + + /* XXX This is too complex. I shouldn't have to use the + * MIME part at all. */ + + attachment = e_attachment_dialog_get_attachment (dialog); + if (attachment != NULL) + mime_part = e_attachment_get_mime_part (attachment); + else + mime_part = NULL; + + sensitive = (attachment != NULL); + gtk_dialog_set_response_sensitive ( + GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive); + + text = NULL; + if (attachment != NULL) + text = e_attachment_get_filename (attachment); + text = (text != NULL) ? text : ""; + widget = dialog->priv->filename_entry; + gtk_widget_set_sensitive (widget, sensitive); + gtk_entry_set_text (GTK_ENTRY (widget), text); + + text = NULL; + if (attachment != NULL) + text = e_attachment_get_description (attachment); + text = (text != NULL) ? text : ""; + widget = dialog->priv->description_entry; + gtk_widget_set_sensitive (widget, sensitive); + gtk_entry_set_text (GTK_ENTRY (widget), text); + + text = NULL; + if (attachment != NULL) + text = e_attachment_get_mime_type (attachment); + text = (text != NULL) ? text : ""; + widget = dialog->priv->mime_type_label; + gtk_label_set_text (GTK_LABEL (widget), text); + + active = FALSE; + if (mime_part != NULL) { + const gchar *disposition; + + disposition = camel_mime_part_get_disposition (mime_part); + active = (g_ascii_strcasecmp (disposition, "inline") == 0); + } else if (attachment != NULL) + active = e_attachment_is_inline (attachment); + widget = dialog->priv->disposition_checkbox; + gtk_widget_set_sensitive (widget, sensitive); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), active); +} + +static void +attachment_dialog_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ATTACHMENT: + e_attachment_dialog_set_attachment ( + E_ATTACHMENT_DIALOG (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +attachment_dialog_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ATTACHMENT: + g_value_set_object ( + value, e_attachment_dialog_get_attachment ( + E_ATTACHMENT_DIALOG (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +attachment_dialog_dispose (GObject *object) +{ + EAttachmentDialogPrivate *priv; + + priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (object); + + if (priv->attachment != NULL) { + g_object_unref (priv->attachment); + priv->attachment = NULL; + } + + if (priv->filename_entry != NULL) { + g_object_unref (priv->filename_entry); + priv->filename_entry = NULL; + } + + if (priv->description_entry != NULL) { + g_object_unref (priv->description_entry); + priv->description_entry = NULL; + } + + if (priv->mime_type_label != NULL) { + g_object_unref (priv->mime_type_label); + priv->mime_type_label = NULL; + } + + if (priv->disposition_checkbox != NULL) { + g_object_unref (priv->disposition_checkbox); + priv->disposition_checkbox = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +attachment_dialog_map (GtkWidget *widget) +{ + GtkWidget *action_area; + GtkWidget *content_area; + + /* Chain up to parent's map() method. */ + GTK_WIDGET_CLASS (parent_class)->map (widget); + + /* XXX Override GtkDialog's broken style property defaults. */ + action_area = gtk_dialog_get_action_area (GTK_DIALOG (widget)); + content_area = gtk_dialog_get_content_area (GTK_DIALOG (widget)); + + gtk_box_set_spacing (GTK_BOX (content_area), 12); + gtk_container_set_border_width (GTK_CONTAINER (action_area), 0); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 12); +} + +static void +attachment_dialog_response (GtkDialog *dialog, + gint response_id) +{ + EAttachmentDialogPrivate *priv; + EAttachment *attachment; + GtkToggleButton *button; + GtkEntry *entry; + const gchar *text; + gboolean active; + + if (response_id != GTK_RESPONSE_OK) + return; + + priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog); + g_return_if_fail (priv->attachment != NULL); + attachment = priv->attachment; + + entry = GTK_ENTRY (priv->filename_entry); + text = gtk_entry_get_text (entry); + e_attachment_set_filename (attachment, text); + + entry = GTK_ENTRY (priv->description_entry); + text = gtk_entry_get_text (entry); + e_attachment_set_description (attachment, text); + + button = GTK_TOGGLE_BUTTON (priv->disposition_checkbox); + active = gtk_toggle_button_get_active (button); + text = active ? "inline" : "attachment"; + e_attachment_set_disposition (attachment, text); +} + +static void +attachment_dialog_class_init (EAttachmentDialogClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkDialogClass *dialog_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EAttachmentDialogPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = attachment_dialog_set_property; + object_class->get_property = attachment_dialog_get_property; + object_class->dispose = attachment_dialog_dispose; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->map = attachment_dialog_map; + + dialog_class = GTK_DIALOG_CLASS (class); + dialog_class->response = attachment_dialog_response; + + g_object_class_install_property ( + object_class, + PROP_ATTACHMENT, + g_param_spec_object ( + "attachment", + "Attachment", + NULL, + E_TYPE_ATTACHMENT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +attachment_dialog_init (EAttachmentDialog *dialog) +{ + GtkWidget *container; + GtkWidget *widget; + + dialog->priv = E_ATTACHMENT_DIALOG_GET_PRIVATE (dialog); + + gtk_dialog_add_button ( + GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button ( + GTK_DIALOG (dialog), GTK_STOCK_OK, GTK_RESPONSE_OK); + gtk_window_set_icon_name ( + GTK_WINDOW (dialog), "mail-attachment"); + gtk_window_set_title ( + GTK_WINDOW (dialog), _("Attachment Properties")); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + container = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + widget = gtk_table_new (4, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (widget), 6); + gtk_table_set_row_spacings (GTK_TABLE (widget), 6); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_entry_new (); + gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE); + gtk_table_attach ( + GTK_TABLE (container), widget, + 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); + dialog->priv->filename_entry = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_label_new_with_mnemonic (_("_Filename:")); + gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); + gtk_label_set_mnemonic_widget ( + GTK_LABEL (widget), dialog->priv->filename_entry); + gtk_table_attach ( + GTK_TABLE (container), widget, + 0, 1, 0, 1, GTK_FILL, 0, 0, 0); + gtk_widget_show (widget); + + widget = gtk_entry_new (); + gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE); + gtk_table_attach ( + GTK_TABLE (container), widget, + 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0); + dialog->priv->description_entry = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_label_new_with_mnemonic (_("_Description:")); + gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); + gtk_label_set_mnemonic_widget ( + GTK_LABEL (widget), dialog->priv->description_entry); + gtk_table_attach ( + GTK_TABLE (container), widget, + 0, 1, 1, 2, GTK_FILL, 0, 0, 0); + gtk_widget_show (widget); + + widget = gtk_label_new (NULL); + gtk_label_set_selectable (GTK_LABEL (widget), TRUE); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); + gtk_table_attach ( + GTK_TABLE (container), widget, + 1, 2, 2, 3, GTK_FILL | GTK_EXPAND, 0, 0, 0); + dialog->priv->mime_type_label = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_label_new (_("MIME Type:")); + gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); + gtk_table_attach ( + GTK_TABLE (container), widget, + 0, 1, 2, 3, GTK_FILL, 0, 0, 0); + gtk_widget_show (widget); + + widget = gtk_check_button_new_with_mnemonic ( + _("_Suggest automatic display of attachment")); + gtk_table_attach ( + GTK_TABLE (container), widget, + 0, 2, 3, 4, GTK_FILL | GTK_EXPAND, 0, 0, 0); + dialog->priv->disposition_checkbox = g_object_ref (widget); + gtk_widget_show (widget); +} + +GType +e_attachment_dialog_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EAttachmentDialogClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) attachment_dialog_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_init */ + sizeof (EAttachmentDialog), + 0, /* n_preallocs */ + (GInstanceInitFunc) attachment_dialog_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + GTK_TYPE_DIALOG, "EAttachmentDialog", &type_info, 0); + } + + return type; +} + +GtkWidget * +e_attachment_dialog_new (GtkWindow *parent, + EAttachment *attachment) +{ + if (parent != NULL) + g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL); + if (attachment != NULL) + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); + + return g_object_new ( + E_TYPE_ATTACHMENT_DIALOG, + "transient-for", parent, "attachment", attachment, NULL); +} + +EAttachment * +e_attachment_dialog_get_attachment (EAttachmentDialog *dialog) +{ + g_return_val_if_fail (E_IS_ATTACHMENT_DIALOG (dialog), NULL); + + return dialog->priv->attachment; +} + +void +e_attachment_dialog_set_attachment (EAttachmentDialog *dialog, + EAttachment *attachment) +{ + g_return_if_fail (E_IS_ATTACHMENT_DIALOG (dialog)); + + if (attachment != NULL) { + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + g_object_ref (attachment); + } + + if (dialog->priv->attachment != NULL) + g_object_unref (dialog->priv->attachment); + + dialog->priv->attachment = attachment; + + attachment_dialog_update (dialog); + + g_object_notify (G_OBJECT (dialog), "attachment"); +} diff --git a/widgets/misc/e-attachment-dialog.h b/widgets/misc/e-attachment-dialog.h new file mode 100644 index 0000000000..8e24e1840c --- /dev/null +++ b/widgets/misc/e-attachment-dialog.h @@ -0,0 +1,73 @@ +/* + * e-attachment-dialog.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_ATTACHMENT_DIALOG_H +#define E_ATTACHMENT_DIALOG_H + +#include <gtk/gtk.h> +#include <widgets/misc/e-attachment.h> + +/* Standard GObject macros */ +#define E_TYPE_ATTACHMENT_DIALOG \ + (e_attachment_dialog_get_type ()) +#define E_ATTACHMENT_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialog)) +#define E_ATTACHMENT_DIALOG_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogClass)) +#define E_IS_ATTACHMENT_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ATTACHMENT_DIALOG)) +#define E_IS_ATTACHMENT_DIALOG_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ATTACHMENT_DIALOG)) +#define E_ATTACHMENT_DIALOG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ATTACHMENT_DIALOG, EAttachmentDialogClass)) + +G_BEGIN_DECLS + +typedef struct _EAttachmentDialog EAttachmentDialog; +typedef struct _EAttachmentDialogClass EAttachmentDialogClass; +typedef struct _EAttachmentDialogPrivate EAttachmentDialogPrivate; + +struct _EAttachmentDialog { + GtkDialog parent; + EAttachmentDialogPrivate *priv; +}; + +struct _EAttachmentDialogClass { + GtkDialogClass parent_class; +}; + +GType e_attachment_dialog_get_type (void); +GtkWidget * e_attachment_dialog_new (GtkWindow *parent, + EAttachment *attachment); +EAttachment * e_attachment_dialog_get_attachment + (EAttachmentDialog *dialog); +void e_attachment_dialog_set_attachment + (EAttachmentDialog *dialog, + EAttachment *attachment); + +G_END_DECLS + +#endif /* E_ATTACHMENT_DIALOG_H */ diff --git a/widgets/misc/e-attachment.c b/widgets/misc/e-attachment.c index 4f5e9ace34..9bb6f09ade 100644 --- a/widgets/misc/e-attachment.c +++ b/widgets/misc/e-attachment.c @@ -27,6 +27,9 @@ #include <config.h> #endif +#include "e-attachment.h" +#include "e-attachment-dialog.h" + #ifdef G_OS_WIN32 /* Include <windows.h> early (as the gio stuff below will * include it anyway, sigh) to workaround the DATADIR problem. @@ -56,7 +59,27 @@ #include "e-util/e-mktemp.h" #include "e-util/e-util-private.h" -#include "e-attachment.h" +#define E_ATTACHMENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ATTACHMENT, EAttachmentPrivate)) + +struct _EAttachmentPrivate { + gchar *filename; + gchar *description; + gchar *disposition; + gchar *mime_type; + + GdkPixbuf *thumbnail; + CamelMimePart *mime_part; +}; + +enum { + PROP_0, + PROP_DESCRIPTION, + PROP_DISPOSITION, + PROP_FILENAME, + PROP_THUMBNAIL +}; enum { CHANGED, @@ -64,112 +87,207 @@ enum { LAST_SIGNAL }; -static guint signals[LAST_SIGNAL] = { 0 }; - -static GObjectClass *parent_class = NULL; +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; static void -changed (EAttachment *attachment) +attachment_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { - g_signal_emit (attachment, signals[CHANGED], 0); + switch (property_id) { + case PROP_DESCRIPTION: + e_attachment_set_description ( + E_ATTACHMENT (object), + g_value_get_string (value)); + return; + + case PROP_DISPOSITION: + e_attachment_set_disposition ( + E_ATTACHMENT (object), + g_value_get_string (value)); + return; + + case PROP_FILENAME: + e_attachment_set_filename ( + E_ATTACHMENT (object), + g_value_get_string (value)); + return; + + case PROP_THUMBNAIL: + e_attachment_set_thumbnail ( + E_ATTACHMENT (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } +static void +attachment_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_DESCRIPTION: + g_value_set_string ( + value, e_attachment_get_description ( + E_ATTACHMENT (object))); + return; + + case PROP_DISPOSITION: + g_value_set_string ( + value, e_attachment_get_disposition ( + E_ATTACHMENT (object))); + return; + + case PROP_FILENAME: + g_value_set_string ( + value, e_attachment_get_filename ( + E_ATTACHMENT (object))); + return; + + case PROP_THUMBNAIL: + g_value_set_object ( + value, e_attachment_get_thumbnail ( + E_ATTACHMENT (object))); + return; + } -/* GtkObject methods. */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} static void -finalise (GObject *object) +attachment_dispose (GObject *object) { - EAttachment *attachment = (EAttachment *) object; - GtkWidget *dialog; + EAttachmentPrivate *priv; - if (attachment->editor_gui != NULL) { - dialog = glade_xml_get_widget (attachment->editor_gui, "dialog"); - g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_CLOSE); - } + priv = E_ATTACHMENT_GET_PRIVATE (object); - if (attachment->is_available_local) { - camel_object_unref (attachment->body); - if (attachment->pixbuf_cache != NULL) - g_object_unref (attachment->pixbuf_cache); - } else { - if (attachment->cancellable) { - /* the operation is still running, so cancel it */ - g_cancellable_cancel (attachment->cancellable); - attachment->cancellable = NULL; - } - g_free (attachment->description); + if (priv->thumbnail != NULL) { + g_object_unref (priv->thumbnail); + priv->thumbnail = NULL; } - g_free (attachment->file_name); - g_free (attachment->store_uri); + if (priv->mime_part != NULL) { + camel_object_unref (priv->mime_part); + priv->mime_part = NULL; + } - G_OBJECT_CLASS (parent_class)->finalize (object); + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); } - -/* Signals. */ - static void -real_changed (EAttachment *attachment) +attachment_finalize (GObject *object) { - g_return_if_fail (E_IS_ATTACHMENT (attachment)); -} + EAttachment *attachment = (EAttachment *) object; -static void -real_update_attachment (EAttachment *attachment, char *msg) -{ - g_return_if_fail (E_IS_ATTACHMENT (attachment)); -} + if (attachment->cancellable) { + /* the operation is still running, so cancel it */ + g_cancellable_cancel (attachment->cancellable); + attachment->cancellable = NULL; + } + + g_free (attachment->store_uri); + + g_free (attachment->priv->filename); + g_free (attachment->priv->description); + g_free (attachment->priv->disposition); + g_free (attachment->priv->mime_type); + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} static void -class_init (EAttachmentClass *klass) +attachment_class_init (EAttachmentClass *class) { GObjectClass *object_class; - object_class = (GObjectClass*) klass; - parent_class = g_type_class_ref (G_TYPE_OBJECT); - - object_class->finalize = finalise; - klass->changed = real_changed; - klass->update = real_update_attachment; - - signals[CHANGED] = g_signal_new ("changed", - E_TYPE_ATTACHMENT, - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (EAttachmentClass, changed), - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - signals[UPDATE] = g_signal_new ("update", - E_TYPE_ATTACHMENT, - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (EAttachmentClass, update), - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EAttachmentPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = attachment_set_property; + object_class->get_property = attachment_get_property; + object_class->dispose = attachment_dispose; + object_class->finalize = attachment_finalize; + + g_object_class_install_property ( + object_class, + PROP_DESCRIPTION, + g_param_spec_string ( + "description", + "Description", + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_DESCRIPTION, + g_param_spec_string ( + "disposition", + "Disposition", + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_DESCRIPTION, + g_param_spec_string ( + "filename", + "Filename", + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_THUMBNAIL, + g_param_spec_object ( + "thumbnail", + "Thumbnail Image", + NULL, + GDK_TYPE_PIXBUF, + G_PARAM_READWRITE)); + + signals[CHANGED] = g_signal_new ( + "changed", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EAttachmentClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[UPDATE] = g_signal_new ( + "update", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EAttachmentClass, update), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void -init (EAttachment *attachment) +attachment_init (EAttachment *attachment) { - attachment->editor_gui = NULL; - attachment->body = NULL; - attachment->size = 0; - attachment->pixbuf_cache = NULL; + attachment->priv = E_ATTACHMENT_GET_PRIVATE (attachment); + attachment->index = -1; - attachment->file_name = NULL; attachment->percentage = -1; - attachment->description = NULL; - attachment->disposition = FALSE; attachment->sign = CAMEL_CIPHER_VALIDITY_SIGN_NONE; attachment->encrypt = CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE; - attachment->store_uri = NULL; - attachment->cancellable = NULL; } GType @@ -177,20 +295,22 @@ e_attachment_get_type (void) { static GType type = 0; - if (type == 0) { - static const GTypeInfo info = { + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { sizeof (EAttachmentClass), - NULL, - NULL, - (GClassInitFunc) class_init, - NULL, - NULL, + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) attachment_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ sizeof (EAttachment), - 0, - (GInstanceInitFunc) init, + 0, /* n_preallocs */ + (GInstanceInitFunc) attachment_init, + NULL /* value_table */ }; - type = g_type_register_static (G_TYPE_OBJECT, "EAttachment", &info, 0); + type = g_type_register_static ( + G_TYPE_OBJECT, "EAttachment", &type_info, 0); } return type; @@ -198,42 +318,42 @@ e_attachment_get_type (void) /** * file_ext_is: - * @param file_name: path for file + * @param filename: path for file * @param ext: desired extension, with a dot - * @return if file_name has extension ext or not + * @return if filename has extension ext or not **/ static gboolean -file_ext_is (const char *file_name, const char *ext) +file_ext_is (const char *filename, const char *ext) { int i, dot = -1; - if (!file_name || !ext) + if (!filename || !ext) return FALSE; - for (i = 0; file_name[i]; i++) { - if (file_name [i] == '.') + for (i = 0; filename[i]; i++) { + if (filename [i] == '.') dot = i; } if (dot > 0) { - return 0 == g_ascii_strcasecmp (file_name + dot, ext); + return 0 == g_ascii_strcasecmp (filename + dot, ext); } return FALSE; } static char * -attachment_guess_mime_type (const char *file_name) +attachment_guess_mime_type (const char *filename) { char *type; gchar *content = NULL; - type = e_util_guess_mime_type (file_name, TRUE); + type = e_util_guess_mime_type (filename, TRUE); if (type && strcmp (type, "text/directory") == 0 && - file_ext_is (file_name, ".vcf") && - g_file_get_contents (file_name, &content, NULL, NULL) && + file_ext_is (filename, ".vcf") && + g_file_get_contents (filename, &content, NULL, NULL) && content) { EVCard *vc = e_vcard_new_from_string (content); @@ -265,30 +385,30 @@ attachment_guess_mime_type (const char *file_name) /** * e_attachment_new: - * @file_name: filename to attach + * @filename: filename to attach * @disposition: Content-Disposition of the attachment * @ex: exception * * Return value: the new attachment, or %NULL on error **/ EAttachment * -e_attachment_new (const char *file_name, const char *disposition, CamelException *ex) +e_attachment_new (const char *filename, const char *disposition, CamelException *ex) { EAttachment *new; CamelMimePart *part; CamelDataWrapper *wrapper; CamelStream *stream; struct stat statbuf; - char *mime_type; - char *filename; + gchar *mime_type; + gchar *basename; CamelURL *url; - g_return_val_if_fail (file_name != NULL, NULL); + g_return_val_if_fail (filename != NULL, NULL); - if (g_stat (file_name, &statbuf) < 0) { + if (g_stat (filename, &statbuf) < 0) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach file %s: %s"), - file_name, g_strerror (errno)); + filename, g_strerror (errno)); return NULL; } @@ -296,18 +416,18 @@ e_attachment_new (const char *file_name, const char *disposition, CamelException if (!S_ISREG (statbuf.st_mode)) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach file %s: not a regular file"), - file_name); + filename); return NULL; } - if (!(stream = camel_stream_fs_new_with_name (file_name, O_RDONLY, 0))) { + if (!(stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0))) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach file %s: %s"), - file_name, g_strerror (errno)); + filename, g_strerror (errno)); return NULL; } - if ((mime_type = attachment_guess_mime_type (file_name))) { + if ((mime_type = attachment_guess_mime_type (filename))) { if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) { wrapper = (CamelDataWrapper *) camel_mime_message_new (); } else { @@ -330,8 +450,8 @@ e_attachment_new (const char *file_name, const char *disposition, CamelException camel_object_unref (wrapper); camel_mime_part_set_disposition (part, disposition); - filename = g_path_get_basename (file_name); - camel_mime_part_set_filename (part, filename); + basename = g_path_get_basename (filename); + camel_mime_part_set_filename (part, basename); #if 0 /* Note: Outlook 2002 is broken with respect to Content-Ids on @@ -344,17 +464,15 @@ e_attachment_new (const char *file_name, const char *disposition, CamelException g_free (content_id); #endif - new = g_object_new (E_TYPE_ATTACHMENT, NULL); - new->editor_gui = NULL; - new->body = part; + new = g_object_new (E_TYPE_ATTACHMENT, "filename", basename, NULL); + new->priv->mime_part = part; new->size = statbuf.st_size; new->guessed_type = TRUE; new->cancellable = NULL; new->is_available_local = TRUE; - new->file_name = filename; url = camel_url_new ("file://", NULL); - camel_url_set_path (url, file_name); + camel_url_set_path (url, filename); new->store_uri = camel_url_to_string (url, 0); camel_url_free (url); @@ -364,7 +482,7 @@ e_attachment_new (const char *file_name, const char *disposition, CamelException typedef struct { EAttachment *attachment; - char *file_name; + char *filename; char *uri; GtkWindow *parent; /* for error dialog */ @@ -394,7 +512,7 @@ download_info_free (DownloadInfo *download_info) if (download_info->cancellable) g_object_unref (download_info->cancellable); - g_free (download_info->file_name); + g_free (download_info->filename); g_free (download_info->uri); g_free (download_info->buffer); g_free (download_info); @@ -450,7 +568,7 @@ data_ready_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) download_info->attachment->cancellable = NULL; camel_exception_init (&ex); - e_attachment_build_remote_file (download_info->file_name, download_info->attachment, "attachment", &ex); + e_attachment_build_remote_file (download_info->filename, download_info->attachment, &ex); if (camel_exception_is_set (&ex)) { download_info->was_error = TRUE; @@ -482,7 +600,7 @@ download_to_local_path (DownloadInfo *download_info, CamelException *ex) { GError *error = NULL; GFile *src = g_file_new_for_uri (download_info->uri); - GFile *des = g_file_new_for_path (download_info->file_name); + GFile *des = g_file_new_for_path (download_info->filename); gboolean res = FALSE; g_return_val_if_fail (src != NULL && des != NULL, FALSE); @@ -538,6 +656,7 @@ e_attachment_new_remote_file (GtkWindow *error_dlg_parent, const char *uri, cons DownloadInfo *download_info; CamelURL *url; char *base; + gchar *filename; g_return_val_if_fail (uri != NULL, NULL); @@ -545,25 +664,26 @@ e_attachment_new_remote_file (GtkWindow *error_dlg_parent, const char *uri, cons base = g_path_get_basename (url->path); camel_url_free (url); - new = g_object_new (E_TYPE_ATTACHMENT, NULL); - new->editor_gui = NULL; - new->body = NULL; + filename = g_build_filename (path, base, NULL); + + new = g_object_new (E_TYPE_ATTACHMENT, "filename", filename, NULL); new->size = 0; new->guessed_type = FALSE; new->cancellable = NULL; new->is_available_local = FALSE; new->percentage = 0; - new->file_name = g_build_filename (path, base, NULL); g_free (base); download_info = g_new0 (DownloadInfo, 1); download_info->attachment = new; - download_info->file_name = g_strdup (new->file_name); + download_info->filename = g_strdup (filename); download_info->uri = g_strdup (uri); download_info->parent = error_dlg_parent; download_info->was_error = FALSE; + g_free (filename); + /* it frees all on the error, so do not free it twice */ if (!download_to_local_path (download_info, ex)) return NULL; @@ -573,23 +693,27 @@ e_attachment_new_remote_file (GtkWindow *error_dlg_parent, const char *uri, cons void -e_attachment_build_remote_file (const char *file_name, EAttachment *attachment, const char *disposition, CamelException *ex) +e_attachment_build_remote_file (const gchar *filename, + EAttachment *attachment, + CamelException *ex) { CamelMimePart *part; CamelDataWrapper *wrapper; CamelStream *stream; struct stat statbuf; - char *mime_type; - char *filename; + const gchar *description; + const gchar *disposition; + gchar *mime_type; + gchar *basename; CamelURL *url; - g_return_if_fail (file_name != NULL); + g_return_if_fail (filename != NULL); - if (g_stat (file_name, &statbuf) == -1) { + if (g_stat (filename, &statbuf) == -1) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach file %s: %s"), - file_name, g_strerror (errno)); - g_message ("Cannot attach file %s: %s\n", file_name, g_strerror (errno)); + filename, g_strerror (errno)); + g_message ("Cannot attach file %s: %s\n", filename, g_strerror (errno)); return; } @@ -597,19 +721,19 @@ e_attachment_build_remote_file (const char *file_name, EAttachment *attachment, if (!S_ISREG (statbuf.st_mode)) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach file %s: not a regular file"), - file_name); - g_message ("Cannot attach file %s: not a regular file", file_name); + filename); + g_message ("Cannot attach file %s: not a regular file", filename); return; } - if (!(stream = camel_stream_fs_new_with_name (file_name, O_RDONLY, 0))) { + if (!(stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0))) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach file %s: %s"), - file_name, g_strerror (errno)); + filename, g_strerror (errno)); return; } - if ((mime_type = attachment_guess_mime_type (file_name))) { + if ((mime_type = attachment_guess_mime_type (filename))) { if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) { wrapper = (CamelDataWrapper *) camel_mime_message_new (); } else { @@ -631,36 +755,34 @@ e_attachment_build_remote_file (const char *file_name, EAttachment *attachment, camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper); camel_object_unref (wrapper); - if (attachment->disposition) - camel_mime_part_set_disposition (part, "inline"); - else - camel_mime_part_set_disposition (part, "attachment"); + disposition = e_attachment_get_disposition (attachment); + camel_mime_part_set_disposition (part, disposition); - if (!attachment->file_name) - filename = g_path_get_basename (file_name); + if (e_attachment_get_filename (attachment) == NULL) + basename = g_path_get_basename (filename); else - filename = g_path_get_basename (attachment->file_name); + basename = g_path_get_basename (e_attachment_get_filename (attachment)); camel_mime_part_set_filename (part, filename); - if (attachment->description) { - camel_mime_part_set_description (part, attachment->description); - g_free (attachment->description); - attachment->description = NULL; + description = e_attachment_get_description (attachment); + if (description != NULL) { + camel_mime_part_set_description (part, description); + e_attachment_set_description (attachment, NULL); } - attachment->editor_gui = NULL; - attachment->body = part; + attachment->priv->mime_part = part; attachment->size = statbuf.st_size; attachment->guessed_type = TRUE; - g_free (attachment->file_name); - attachment->file_name = filename; + + e_attachment_set_filename (attachment, basename); url = camel_url_new ("file://", NULL); - camel_url_set_path (url, file_name); + camel_url_set_path (url, filename); attachment->store_uri = camel_url_to_string (url, 0); camel_url_free (url); + g_free (basename); } @@ -674,210 +796,231 @@ EAttachment * e_attachment_new_from_mime_part (CamelMimePart *part) { EAttachment *new; + const gchar *filename; g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL); - new = g_object_new (E_TYPE_ATTACHMENT, NULL); - new->editor_gui = NULL; + filename = camel_mime_part_get_filename (part); + + new = g_object_new (E_TYPE_ATTACHMENT, "filename", filename, NULL); camel_object_ref (part); - new->body = part; + new->priv->mime_part = part; new->guessed_type = FALSE; new->is_available_local = TRUE; new->size = camel_mime_part_get_content_size (part); - new->file_name = g_strdup (camel_mime_part_get_filename(part)); return new; } - -/* The attachment property dialog. */ - -typedef struct { +void +e_attachment_edit (EAttachment *attachment, + GtkWindow *parent) +{ GtkWidget *dialog; - GtkEntry *file_name_entry; - GtkEntry *description_entry; - GtkEntry *mime_type_entry; - GtkToggleButton *disposition_checkbox; - EAttachment *attachment; -} DialogData; -static void -destroy_dialog_data (DialogData *data) + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + dialog = e_attachment_dialog_new (parent, attachment); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +const gchar * +e_attachment_get_description (EAttachment *attachment) { - g_free (data); + CamelMimePart *mime_part; + const gchar *description; + + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); + + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part != NULL) + description = camel_mime_part_get_description (mime_part); + else + description = attachment->priv->description; + + return description; } -/* - * fixme: I am converting EVERYTHING to/from UTF-8, although mime types - * are in ASCII. This is not strictly necessary, but we want to be - * consistent and possibly check for errors somewhere. - */ +void +e_attachment_set_description (EAttachment *attachment, + const gchar *description) +{ + CamelMimePart *mime_part; -static void -set_entry (GladeXML *xml, const char *widget_name, const char *value) + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + g_free (attachment->priv->description); + attachment->priv->description = g_strdup (description); + + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part != NULL) + camel_mime_part_set_description (mime_part, description); + + g_object_notify (G_OBJECT (attachment), "description"); +} + +const gchar * +e_attachment_get_disposition (EAttachment *attachment) { - GtkEntry *entry; + CamelMimePart *mime_part; + const gchar *disposition; + + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); - entry = GTK_ENTRY (glade_xml_get_widget (xml, widget_name)); - if (entry == NULL) - g_warning ("Entry for `%s' not found.", widget_name); + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part != NULL) + disposition = camel_mime_part_get_disposition (mime_part); else - gtk_entry_set_text (entry, value ? value : ""); + disposition = attachment->priv->disposition; + + return disposition; } -static void -connect_widget (GladeXML *gui, const char *name, const char *signal_name, - GCallback func, gpointer data) +void +e_attachment_set_disposition (EAttachment *attachment, + const gchar *disposition) { - GtkWidget *widget; + CamelMimePart *mime_part; - widget = glade_xml_get_widget (gui, name); - g_signal_connect (widget, signal_name, func, data); + g_return_if_fail (E_IS_ATTACHMENT (attachment)); + + g_free (attachment->priv->disposition); + attachment->priv->disposition = g_strdup (disposition); + + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part != NULL) + camel_mime_part_set_disposition (mime_part, disposition); + + g_object_notify (G_OBJECT (attachment), "disposition"); } -static void -close_cb (GtkWidget *widget, gpointer data) +const gchar * +e_attachment_get_filename (EAttachment *attachment) { - EAttachment *attachment; - DialogData *dialog_data; + CamelMimePart *mime_part; + const gchar *filename; - dialog_data = (DialogData *) data; - attachment = dialog_data->attachment; + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); - gtk_widget_destroy (dialog_data->dialog); - g_object_unref (attachment->editor_gui); - attachment->editor_gui = NULL; + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part != NULL) + filename = camel_mime_part_get_filename (mime_part); + else + filename = attachment->priv->filename; - destroy_dialog_data (dialog_data); + return filename; } -static void -ok_cb (GtkWidget *widget, gpointer data) +void +e_attachment_set_filename (EAttachment *attachment, + const gchar *filename) { - DialogData *dialog_data; - EAttachment *attachment; - const char *str; + CamelMimePart *mime_part; - dialog_data = (DialogData *) data; - attachment = dialog_data->attachment; + g_return_if_fail (E_IS_ATTACHMENT (attachment)); - str = gtk_entry_get_text (dialog_data->file_name_entry); - if (attachment->is_available_local) - camel_mime_part_set_filename (attachment->body, str); - g_free (attachment->file_name); - attachment->file_name = g_strdup (str); + g_free (attachment->priv->filename); + attachment->priv->filename = g_strdup (filename); - str = gtk_entry_get_text (dialog_data->description_entry); - if (attachment->is_available_local) { - camel_mime_part_set_description (attachment->body, str); - } else { - g_free (attachment->description); - attachment->description = g_strdup (str); - } + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part != NULL) + camel_mime_part_set_filename (mime_part, filename); - str = gtk_entry_get_text (dialog_data->mime_type_entry); - if (attachment->is_available_local) { - camel_mime_part_set_content_type (attachment->body, str); - camel_data_wrapper_set_mime_type(camel_medium_get_content_object(CAMEL_MEDIUM (attachment->body)), str); - } + g_object_notify (G_OBJECT (attachment), "filename"); +} - if (attachment->is_available_local) { - switch (gtk_toggle_button_get_active (dialog_data->disposition_checkbox)) { - case 0: - camel_mime_part_set_disposition (attachment->body, "attachment"); - break; - case 1: - camel_mime_part_set_disposition (attachment->body, "inline"); - break; - default: - /* Hmmmm? */ - break; - } - } else { - attachment->disposition = gtk_toggle_button_get_active (dialog_data->disposition_checkbox); +CamelMimePart * +e_attachment_get_mime_part (EAttachment *attachment) +{ + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); + + return attachment->priv->mime_part; +} + +const gchar * +e_attachment_get_mime_type (EAttachment *attachment) +{ + CamelContentType *content_type; + CamelMimePart *mime_part; + const gchar *filename; + gchar *mime_type; + + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); + + if (attachment->priv->mime_type != NULL) + goto exit; + + mime_part = e_attachment_get_mime_part (attachment); + filename = e_attachment_get_filename (attachment); + content_type = camel_mime_part_get_content_type (mime_part); + + if (mime_part == NULL) + mime_type = attachment_guess_mime_type (filename); + else { + content_type = camel_mime_part_get_content_type (mime_part); + mime_type = camel_content_type_simple (content_type); } - changed (attachment); - close_cb (widget, data); + attachment->priv->mime_type = mime_type; + +exit: + return attachment->priv->mime_type; } -static void -response_cb (GtkWidget *widget, gint response, gpointer data) +GdkPixbuf * +e_attachment_get_thumbnail (EAttachment *attachment) { - if (response == GTK_RESPONSE_OK) - ok_cb (widget, data); - else - close_cb (widget, data); + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL); + + return attachment->priv->thumbnail; } void -e_attachment_edit (EAttachment *attachment, GtkWidget *parent) +e_attachment_set_thumbnail (EAttachment *attachment, + GdkPixbuf *thumbnail) { - CamelContentType *content_type; - const char *disposition; - DialogData *dialog_data; - GladeXML *editor_gui; - GtkWidget *window; - char *type; - char *filename; - g_return_if_fail (E_IS_ATTACHMENT (attachment)); - if (attachment->editor_gui != NULL) { - window = glade_xml_get_widget (attachment->editor_gui, "dialog"); - gdk_window_show (window->window); - return; + if (thumbnail != NULL) { + g_return_if_fail (GDK_IS_PIXBUF (thumbnail)); + g_object_ref (thumbnail); } - filename = g_build_filename (EVOLUTION_GLADEDIR, "e-attachment.glade", NULL); - editor_gui = glade_xml_new (filename, NULL, NULL); - g_free (filename); + if (attachment->priv->thumbnail != NULL) + g_object_unref (attachment->priv->thumbnail); - if (editor_gui == NULL) { - g_warning ("Cannot load `e-attachment.glade'"); - return; - } + attachment->priv->thumbnail = thumbnail; - attachment->editor_gui = editor_gui; - - gtk_window_set_transient_for (GTK_WINDOW (glade_xml_get_widget (editor_gui, "dialog")), - GTK_WINDOW (gtk_widget_get_toplevel (parent))); - - dialog_data = g_new (DialogData, 1); - dialog_data->attachment = attachment; - dialog_data->dialog = glade_xml_get_widget (editor_gui, "dialog"); - dialog_data->file_name_entry = GTK_ENTRY (glade_xml_get_widget (editor_gui, "file_name_entry")); - dialog_data->description_entry = GTK_ENTRY (glade_xml_get_widget (editor_gui, "description_entry")); - dialog_data->mime_type_entry = GTK_ENTRY (glade_xml_get_widget (editor_gui, "mime_type_entry")); - dialog_data->disposition_checkbox = GTK_TOGGLE_BUTTON (glade_xml_get_widget (editor_gui, "disposition_checkbox")); - - if (attachment->is_available_local && attachment->body) { - set_entry (editor_gui, "file_name_entry", camel_mime_part_get_filename (attachment->body)); - set_entry (editor_gui, "description_entry", camel_mime_part_get_description (attachment->body)); - content_type = camel_mime_part_get_content_type (attachment->body); - type = camel_content_type_simple (content_type); - set_entry (editor_gui, "mime_type_entry", type); - g_free (type); - - disposition = camel_mime_part_get_disposition (attachment->body); - gtk_toggle_button_set_active (dialog_data->disposition_checkbox, - disposition && !g_ascii_strcasecmp (disposition, "inline")); - } else { - set_entry (editor_gui, "file_name_entry", attachment->file_name); - set_entry (editor_gui, "description_entry", attachment->description); - if ((type = attachment_guess_mime_type (attachment->file_name))) { - set_entry (editor_gui, "mime_type_entry", type); - g_free (type); - } else { - set_entry (editor_gui, "mime_type_entry", ""); - } + g_object_notify (G_OBJECT (attachment), "thumbnail"); +} - gtk_toggle_button_set_active (dialog_data->disposition_checkbox, attachment->disposition); - } +gboolean +e_attachment_is_image (EAttachment *attachment) +{ + CamelContentType *content_type; + CamelMimePart *mime_part; + + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); + + mime_part = e_attachment_get_mime_part (attachment); + if (mime_part == NULL) + return FALSE; + + content_type = camel_mime_part_get_content_type (mime_part); + + return camel_content_type_is (content_type, "image", "*"); +} + +gboolean +e_attachment_is_inline (EAttachment *attachment) +{ + const gchar *disposition; + + g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); - connect_widget (editor_gui, "dialog", "response", (GCallback)response_cb, dialog_data); + disposition = e_attachment_get_disposition (attachment); + g_return_val_if_fail (disposition != NULL, FALSE); - /* make sure that when the parent gets hidden/closed that our windows also close */ - parent = gtk_widget_get_toplevel (parent); - gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog_data->dialog), TRUE); + return (g_ascii_strcasecmp (disposition, "inline") == 0); } diff --git a/widgets/misc/e-attachment.h b/widgets/misc/e-attachment.h index 7b45f24ae5..c69189c189 100644 --- a/widgets/misc/e-attachment.h +++ b/widgets/misc/e-attachment.h @@ -15,14 +15,14 @@ * * Authors: * Ettore Perazzoli <ettore@ximian.com> - * Srinivasa Ragavan <sragavan@novell.com> + * Srinivasa Ragavan <sragavan@novell.com> * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ -#ifndef __E_ATTACHMENT_H__ -#define __E_ATTACHMENT_H__ +#ifndef E_ATTACHMENT_H +#define E_ATTACHMENT_H #include <gio/gio.h> #include <gtk/gtk.h> @@ -31,73 +31,91 @@ #include <camel/camel-exception.h> #include <camel/camel-cipher-context.h> -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -#define E_TYPE_ATTACHMENT (e_attachment_get_type ()) -#define E_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ATTACHMENT, EAttachment)) -#define E_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ATTACHMENT, EAttachmentClass)) -#define E_IS_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ATTACHMENT)) -#define E_IS_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_ATTACHMENT)) - - -typedef struct _EAttachment EAttachment; -typedef struct _EAttachmentClass EAttachmentClass; +/* Standard GObject macros */ +#define E_TYPE_ATTACHMENT \ + (e_attachment_get_type ()) +#define E_ATTACHMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ATTACHMENT, EAttachment)) +#define E_ATTACHMENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ATTACHMENT, EAttachmentClass)) +#define E_IS_ATTACHMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ATTACHMENT)) +#define E_IS_ATTACHMENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((obj), E_TYPE_ATTACHMENT)) +#define E_ATTACHMENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ATTACHMENT, EAttachmentClass)) + +G_BEGIN_DECLS + +typedef struct _EAttachment EAttachment; +typedef struct _EAttachmentClass EAttachmentClass; +typedef struct _EAttachmentPrivate EAttachmentPrivate; struct _EAttachment { GObject parent; - GladeXML *editor_gui; - - CamelMimePart *body; gboolean guessed_type; gulong size; - GdkPixbuf *pixbuf_cache; - GCancellable *cancellable; gboolean is_available_local; int percentage; - char *file_name; - char *description; - gboolean disposition; int index; char *store_uri; /* Status of signed/encrypted attachments */ camel_cipher_validity_sign_t sign; camel_cipher_validity_encrypt_t encrypt; + + EAttachmentPrivate *priv; }; struct _EAttachmentClass { GObjectClass parent_class; - void (*changed) (EAttachment *attachment); - void (*update) (EAttachment *attachment, char *msg); + void (*changed) (EAttachment *attachment); + void (*update) (EAttachment *attachment, + gchar *message); }; -GType e_attachment_get_type (void); -EAttachment *e_attachment_new (const char *file_name, - const char *disposition, - CamelException *ex); -EAttachment * e_attachment_new_remote_file (GtkWindow *error_dlg_parent, - const char *url, - const char *disposition, - const char *path, - CamelException *ex); -void e_attachment_build_remote_file (const char *filename, - EAttachment *attachment, - const char *disposition, - CamelException *ex); -EAttachment *e_attachment_new_from_mime_part (CamelMimePart *part); -void e_attachment_edit (EAttachment *attachment, - GtkWidget *parent); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __E_ATTACHMENT_H__ */ +GType e_attachment_get_type (void); +EAttachment * e_attachment_new (const gchar *filename, + const gchar *disposition, + CamelException *ex); +EAttachment * e_attachment_new_remote_file (GtkWindow *error_dlg_parent, + const gchar *url, + const gchar *disposition, + const gchar *path, + CamelException *ex); +void e_attachment_build_remote_file (const gchar *filename, + EAttachment *attachment, + CamelException *ex); +EAttachment * e_attachment_new_from_mime_part (CamelMimePart *part); +void e_attachment_edit (EAttachment *attachment, + GtkWindow *parent); +const gchar * e_attachment_get_description (EAttachment *attachment); +void e_attachment_set_description (EAttachment *attachment, + const gchar *description); +const gchar * e_attachment_get_disposition (EAttachment *attachment); +void e_attachment_set_disposition (EAttachment *attachment, + const gchar *disposition); +const gchar * e_attachment_get_filename (EAttachment *attachment); +void e_attachment_set_filename (EAttachment *attachment, + const gchar *filename); +CamelMimePart * e_attachment_get_mime_part (EAttachment *attachment); +const gchar * e_attachment_get_mime_type (EAttachment *attachment); +GdkPixbuf * e_attachment_get_thumbnail (EAttachment *attachment); +void e_attachment_set_thumbnail (EAttachment *attachment, + GdkPixbuf *pixbuf); +gboolean e_attachment_is_image (EAttachment *attachment); +gboolean e_attachment_is_inline (EAttachment *attachment); + +G_END_DECLS + +#endif /* E_ATTACHMENT_H */ diff --git a/widgets/misc/e-mime-part-utils.c b/widgets/misc/e-mime-part-utils.c new file mode 100644 index 0000000000..3238ca93f5 --- /dev/null +++ b/widgets/misc/e-mime-part-utils.c @@ -0,0 +1,223 @@ +/* + * e-mime-part-utils.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-mime-part-utils.h" + +#include <errno.h> +#include <gio/gio.h> +#include <glib/gi18n.h> +#include <camel/camel-stream-vfs.h> + +#include "e-util/e-util.h" + +static void +mime_part_utils_open_in_cb (GtkAction *action, + CamelMimePart *mime_part) +{ + GtkWindow *parent; + GFile *file; + gchar *path; + gchar *uri; + gint fd; + GError *error = NULL; + + parent = g_object_get_data (G_OBJECT (action), "parent-window"); + + fd = e_file_open_tmp (&path, &error); + if (error != NULL) + goto fail; + + close (fd); + + file = g_file_new_for_path (path); + e_mime_part_utils_save_to_file (mime_part, file, &error); + g_free (path); + + if (error != NULL) { + g_object_unref (file); + goto fail; + } + + uri = g_file_get_uri (file); + e_show_uri (parent, uri); + g_free (uri); + + g_object_unref (file); + + return; + +fail: + g_warning ("%s", error->message); + g_error_free (error); +} + +GList * +e_mime_part_utils_get_apps (CamelMimePart *mime_part) +{ + GList *app_info_list; + const gchar *filename; + gchar *content_type; + gchar *mime_type; + gchar *cp; + + g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL); + + filename = camel_mime_part_get_filename (mime_part); + mime_type = camel_content_type_simple ( + camel_mime_part_get_content_type (mime_part)); + g_return_val_if_fail (mime_type != NULL, NULL); + + /* GIO expects lowercase MIME types. */ + for (cp = mime_type; *cp != '\0'; cp++) + *cp = g_ascii_tolower (*cp); + + content_type = g_content_type_from_mime_type (mime_type); + if (content_type != NULL) + app_info_list = g_app_info_get_all_for_type (content_type); + else + app_info_list = g_app_info_get_all_for_type (mime_type); + g_free (content_type); + + if (app_info_list != NULL || filename == NULL) + goto exit; + + if (strcmp (mime_type, "application/octet-stream") != 0) + goto exit; + + content_type = g_content_type_guess (filename, NULL, 0, NULL); + app_info_list = g_app_info_get_all_for_type (content_type); + g_free (content_type); + +exit: + g_free (mime_type); + + return app_info_list; +} + +gboolean +e_mime_part_utils_save_to_file (CamelMimePart *mime_part, + GFile *file, + GError **error) +{ + GFileOutputStream *output_stream; + CamelDataWrapper *content; + CamelStream *stream; + + g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + + output_stream = g_file_replace ( + file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error); + if (output_stream == NULL) + return FALSE; + + /* The CamelStream takes ownership of the GFileOutputStream. */ + content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + stream = camel_stream_vfs_new_with_stream (G_OBJECT (output_stream)); + + /* XXX Camel's streams are synchronous only, so we have to write + * the whole thing in one shot and hope it doesn't block the + * main loop for too long. */ + if (camel_data_wrapper_decode_to_stream (content, stream) < 0) + goto file_error; + + if (camel_stream_flush (stream) < 0) + goto file_error; + + camel_object_unref (stream); + + return TRUE; + +file_error: + g_set_error ( + error, G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + + camel_object_unref (stream); + + return FALSE; +} + +void +e_mime_part_utils_add_open_actions (CamelMimePart *mime_part, + GtkUIManager *ui_manager, + GtkActionGroup *action_group, + const gchar *widget_path, + GtkWindow *parent, + guint merge_id) +{ + GList *app_info_list; + GList *iter; + + g_return_if_fail (CAMEL_IS_MIME_PART (mime_part)); + g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager)); + g_return_if_fail (GTK_IS_ACTION_GROUP (action_group)); + g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); + g_return_if_fail (widget_path != NULL); + + app_info_list = e_mime_part_utils_get_apps (mime_part); + + for (iter = app_info_list; iter != NULL; iter = iter->next) { + GAppInfo *app_info = iter->data; + GtkAction *action; + const gchar *app_executable; + const gchar *app_name; + gchar *action_tooltip; + gchar *action_label; + gchar *action_name; + + if (!g_app_info_should_show (app_info)) + continue; + + app_executable = g_app_info_get_executable (app_info); + app_name = g_app_info_get_name (app_info); + + action_name = g_strdup_printf ("open-in-%s", app_executable); + action_label = g_strdup_printf (_("Open in %s..."), app_name); + + action_tooltip = g_strdup_printf ( + _("Open this attachment in %s"), app_name); + + action = gtk_action_new ( + action_name, action_label, action_tooltip, NULL); + + g_object_set_data ( + G_OBJECT (action), "parent-window", parent); + + g_signal_connect ( + action, "activate", + G_CALLBACK (mime_part_utils_open_in_cb), mime_part); + + gtk_action_group_add_action (action_group, action); + + gtk_ui_manager_add_ui ( + ui_manager, merge_id, widget_path, action_name, + action_name, GTK_UI_MANAGER_AUTO, FALSE); + + g_free (action_name); + g_free (action_label); + g_free (action_tooltip); + } + + g_list_foreach (app_info_list, (GFunc) g_object_unref, NULL); + g_list_free (app_info_list); +} diff --git a/widgets/misc/e-mime-part-utils.h b/widgets/misc/e-mime-part-utils.h new file mode 100644 index 0000000000..e923e07c8d --- /dev/null +++ b/widgets/misc/e-mime-part-utils.h @@ -0,0 +1,46 @@ +/* + * e-mime-part-utils.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_MIME_PART_UTILS_H +#define E_MIME_PART_UTILS_H + +#include <gtk/gtk.h> +#include <camel/camel-mime-part.h> +#include <shell/e-shell-window.h> + +G_BEGIN_DECLS + +GList * e_mime_part_utils_get_apps (CamelMimePart *mime_part); +gboolean e_mime_part_utils_save_to_file (CamelMimePart *mime_part, + GFile *file, + GError **error); + +void e_mime_part_utils_add_open_actions + (CamelMimePart *mime_part, + GtkUIManager *ui_manager, + GtkActionGroup *action_group, + const gchar *widget_path, + GtkWindow *parent, + guint merge_id); + +G_END_DECLS + +#endif /* E_MIME_PART_UTILS_H */ diff --git a/widgets/misc/e-timeout-activity.c b/widgets/misc/e-timeout-activity.c new file mode 100644 index 0000000000..d02bcf96d2 --- /dev/null +++ b/widgets/misc/e-timeout-activity.c @@ -0,0 +1,180 @@ +/* + * e-timeout-activity.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-timeout-activity.h" + +#define E_TIMEOUT_ACTIVITY_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_TIMEOUT_ACTIVITY, ETimeoutActivityPrivate)) + +struct _ETimeoutActivityPrivate { + guint timeout_id; +}; + +enum { + TIMEOUT, + LAST_SIGNAL +}; + +static gpointer parent_class; +static gulong signals[LAST_SIGNAL]; + +static gboolean +timeout_activity_cb (ETimeoutActivity *timeout_activity) +{ + g_signal_emit (timeout_activity, signals[TIMEOUT], 0); + + return FALSE; +} + +static void +timeout_activity_finalize (GObject *object) +{ + ETimeoutActivityPrivate *priv; + + priv = E_TIMEOUT_ACTIVITY_GET_PRIVATE (object); + + if (priv->timeout_id > 0) + g_source_remove (priv->timeout_id); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +timeout_activity_cancelled (EActivity *activity) +{ + ETimeoutActivityPrivate *priv; + + priv = E_TIMEOUT_ACTIVITY_GET_PRIVATE (activity); + + if (priv->timeout_id > 0) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + /* Chain up to parent's cancelled() method. */ + E_ACTIVITY_CLASS (parent_class)->cancelled (activity); +} + +static void +timeout_activity_completed (EActivity *activity) +{ + ETimeoutActivityPrivate *priv; + + priv = E_TIMEOUT_ACTIVITY_GET_PRIVATE (activity); + + if (priv->timeout_id > 0) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + /* Chain up to parent's completed() method. */ + E_ACTIVITY_CLASS (parent_class)->completed (activity); +} + +static void +timeout_activity_timeout (ETimeoutActivity *timeout_activity) +{ + /* Allow subclasses to safely chain up. */ +} + +static void +timeout_activity_class_init (ETimeoutActivityClass *class) +{ + GObjectClass *object_class; + EActivityClass *activity_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ETimeoutActivityPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = timeout_activity_finalize; + + activity_class = E_ACTIVITY_CLASS (class); + activity_class->cancelled = timeout_activity_cancelled; + activity_class->completed = timeout_activity_completed; + + class->timeout = timeout_activity_timeout; + + signals[TIMEOUT] = g_signal_new ( + "timeout", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ETimeoutActivityClass, timeout), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +timeout_activity_init (ETimeoutActivity *timeout_activity) +{ + timeout_activity->priv = + E_TIMEOUT_ACTIVITY_GET_PRIVATE (timeout_activity); +} + +GType +e_timeout_activity_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (ETimeoutActivityClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) timeout_activity_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ETimeoutActivity), + 0, /* n_preallocs */ + (GInstanceInitFunc) timeout_activity_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + E_TYPE_ACTIVITY, "ETimeoutActivity", &type_info, 0); + } + + return type; +} + +EActivity * +e_timeout_activity_new (const gchar *primary_text) +{ + return g_object_new ( + E_TYPE_TIMEOUT_ACTIVITY, + "primary-text", primary_text, NULL); +} + +void +e_timeout_activity_set_timeout (ETimeoutActivity *timeout_activity, + guint seconds) +{ + g_return_if_fail (E_IS_TIMEOUT_ACTIVITY (timeout_activity)); + + if (timeout_activity->priv->timeout_id > 0) + e_activity_cancel (E_ACTIVITY (timeout_activity)); + + timeout_activity->priv->timeout_id = g_timeout_add_seconds ( + seconds, (GSourceFunc) timeout_activity_cb, timeout_activity); +} diff --git a/widgets/misc/e-timeout-activity.h b/widgets/misc/e-timeout-activity.h new file mode 100644 index 0000000000..70aaee7dcb --- /dev/null +++ b/widgets/misc/e-timeout-activity.h @@ -0,0 +1,71 @@ +/* + * e-timeout-activity.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_TIMEOUT_ACTIVITY_H +#define E_TIMEOUT_ACTIVITY_H + +#include <e-activity.h> + +/* Standard GObject macros */ +#define E_TYPE_TIMEOUT_ACTIVITY \ + (e_timeout_activity_get_type ()) +#define E_TIMEOUT_ACTIVITY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_TIMEOUT_ACTIVITY, ETimeoutActivity)) +#define E_TIMEOUT_ACTIVITY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_TIMEOUT_ACTIVITY, ETimeoutActivityClass)) +#define E_IS_TIMEOUT_ACTIVITY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_TIMEOUT_ACTIVITY)) +#define E_IS_TIMEOUT_ACTIVITY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_TIMEOUT_ACTIVITY)) +#define E_TIMEOUT_ACTIVITY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_TIMEOUT_ACTIVITY, ETimeoutActivityClass)) + +G_BEGIN_DECLS + +typedef struct _ETimeoutActivity ETimeoutActivity; +typedef struct _ETimeoutActivityClass ETimeoutActivityClass; +typedef struct _ETimeoutActivityPrivate ETimeoutActivityPrivate; + +struct _ETimeoutActivity { + EActivity parent; + ETimeoutActivityPrivate *priv; +}; + +struct _ETimeoutActivityClass { + EActivityClass parent_class; + + /* Signals */ + void (*timeout) (ETimeoutActivity *timeout_activity); +}; + +GType e_timeout_activity_get_type (void); +EActivity * e_timeout_activity_new (const gchar *primary_text); +void e_timeout_activity_set_timeout (ETimeoutActivity *timeout_activity, + guint seconds); + +G_END_DECLS + +#endif /* E_TIMEOUT_ACTIVITY_H */ |