1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-maemo-utils.h>
39 #include <modest-tny-msg.h>
40 #include <modest-msg-view-window.h>
41 #include <modest-attachments-view.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar-widget.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include <gtkhtml/gtkhtml-search.h>
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
58 #define DEFAULT_FOLDER "MyDocs/.documents"
60 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
61 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
62 static void modest_msg_view_window_finalize (GObject *obj);
63 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
65 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
66 ModestMsgViewWindow *obj);
67 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
68 ModestMsgViewWindow *obj);
70 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
71 static void modest_msg_view_window_set_zoom (ModestWindow *window,
73 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
74 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
75 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
76 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
79 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
80 GdkEventWindowState *event,
82 static void modest_msg_view_window_scroll_up (ModestWindow *window);
83 static void modest_msg_view_window_scroll_down (ModestWindow *window);
84 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
86 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
87 gboolean show_toolbar);
89 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
91 ModestMsgViewWindow *window);
92 void modest_msg_view_window_on_row_changed(
93 GtkTreeModel *header_model,
96 ModestMsgViewWindow *window);
98 void modest_msg_view_window_on_row_deleted(
99 GtkTreeModel *header_model,
101 ModestMsgViewWindow *window);
103 void modest_msg_view_window_on_row_inserted(
104 GtkTreeModel *header_model,
107 ModestMsgViewWindow *window);
109 void modest_msg_view_window_on_row_reordered(
110 GtkTreeModel *header_model,
114 ModestMsgViewWindow *window);
116 static void cancel_progressbar (GtkToolButton *toolbutton,
117 ModestMsgViewWindow *self);
119 static void on_queue_changed (ModestMailOperationQueue *queue,
120 ModestMailOperation *mail_op,
121 ModestMailOperationQueueNotification type,
122 ModestMsgViewWindow *self);
124 static void on_account_removed (TnyAccountStore *account_store,
128 static void view_msg_cb (ModestMailOperation *mail_op,
133 static void set_toolbar_mode (ModestMsgViewWindow *self,
134 ModestToolBarModes mode);
136 static void update_window_title (ModestMsgViewWindow *window);
138 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
141 /* list my signals */
147 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
148 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
149 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
152 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
153 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
154 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
155 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
156 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
157 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
158 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
161 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
162 struct _ModestMsgViewWindowPrivate {
165 GtkWidget *main_scroll;
166 GtkWidget *find_toolbar;
169 /* Progress observers */
170 GtkWidget *progress_bar;
171 GSList *progress_widgets;
174 GtkWidget *progress_toolitem;
175 GtkWidget *cancel_toolitem;
176 GtkWidget *prev_toolitem;
177 GtkWidget *next_toolitem;
178 ModestToolBarModes current_toolbar_mode;
180 /* Optimized view enabled */
181 gboolean optimized_view;
183 /* Whether this was created via the *_new_for_search_result() function. */
184 gboolean is_search_result;
186 /* A reference to the @model of the header view
187 * to allow selecting previous/next messages,
188 * if the message is currently selected in the header view.
190 GtkTreeModel *header_model;
191 GtkTreeRowReference *row_reference;
192 GtkTreeRowReference *next_row_reference;
194 guint clipboard_change_handler;
195 guint queue_change_handler;
196 guint account_removed_handler;
199 GtkWidget *remove_attachment_banner;
201 guint progress_bar_timeout;
206 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
207 MODEST_TYPE_MSG_VIEW_WINDOW, \
208 ModestMsgViewWindowPrivate))
210 static GtkWindowClass *parent_class = NULL;
212 /* uncomment the following if you have defined any signals */
213 static guint signals[LAST_SIGNAL] = {0};
216 modest_msg_view_window_get_type (void)
218 static GType my_type = 0;
220 static const GTypeInfo my_info = {
221 sizeof(ModestMsgViewWindowClass),
222 NULL, /* base init */
223 NULL, /* base finalize */
224 (GClassInitFunc) modest_msg_view_window_class_init,
225 NULL, /* class finalize */
226 NULL, /* class data */
227 sizeof(ModestMsgViewWindow),
229 (GInstanceInitFunc) modest_msg_view_window_init,
232 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
233 "ModestMsgViewWindow",
240 save_state (ModestWindow *self)
242 modest_widget_memory_save (modest_runtime_get_conf (),
244 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
249 restore_settings (ModestMsgViewWindow *self)
251 modest_widget_memory_restore (modest_runtime_get_conf (),
253 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
257 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
259 GObjectClass *gobject_class;
260 ModestWindowClass *modest_window_class;
261 gobject_class = (GObjectClass*) klass;
262 modest_window_class = (ModestWindowClass *) klass;
264 parent_class = g_type_class_peek_parent (klass);
265 gobject_class->finalize = modest_msg_view_window_finalize;
267 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
268 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
269 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
270 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
271 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
272 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
274 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
276 modest_window_class->save_state_func = save_state;
278 signals[MSG_CHANGED_SIGNAL] =
279 g_signal_new ("msg-changed",
280 G_TYPE_FROM_CLASS (gobject_class),
282 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
284 modest_marshal_VOID__POINTER_POINTER,
285 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
289 modest_msg_view_window_init (ModestMsgViewWindow *obj)
291 ModestMsgViewWindowPrivate *priv;
292 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
294 priv->msg_view = NULL;
295 priv->header_model = NULL;
296 priv->clipboard_change_handler = 0;
297 priv->queue_change_handler = 0;
298 priv->account_removed_handler = 0;
299 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
301 priv->optimized_view = FALSE;
302 priv->progress_bar_timeout = 0;
303 priv->purge_timeout = 0;
304 priv->remove_attachment_banner = NULL;
305 priv->msg_uid = NULL;
310 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
312 ModestMsgViewWindowPrivate *priv = NULL;
314 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
316 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
318 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
320 if (priv->progress_bar_timeout > 0) {
321 g_source_remove (priv->progress_bar_timeout);
322 priv->progress_bar_timeout = 0;
329 set_toolbar_mode (ModestMsgViewWindow *self,
330 ModestToolBarModes mode)
332 ModestWindowPrivate *parent_priv;
333 ModestMsgViewWindowPrivate *priv;
334 /* GtkWidget *widget = NULL; */
336 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
338 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
341 /* Sets current toolbar mode */
342 priv->current_toolbar_mode = mode;
344 /* Update toolbar dimming state */
345 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
348 case TOOLBAR_MODE_NORMAL:
349 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
350 /* gtk_action_set_sensitive (widget, TRUE); */
351 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
352 /* gtk_action_set_sensitive (widget, TRUE); */
353 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
354 /* gtk_action_set_sensitive (widget, TRUE); */
355 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
356 /* gtk_action_set_sensitive (widget, TRUE); */
358 if (priv->prev_toolitem)
359 gtk_widget_show (priv->prev_toolitem);
361 if (priv->next_toolitem)
362 gtk_widget_show (priv->next_toolitem);
364 if (priv->progress_toolitem)
365 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
366 if (priv->progress_bar)
367 gtk_widget_hide (priv->progress_bar);
369 if (priv->cancel_toolitem)
370 gtk_widget_hide (priv->cancel_toolitem);
372 /* Hide toolbar if optimized view is enabled */
373 if (priv->optimized_view) {
374 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
375 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
379 case TOOLBAR_MODE_TRANSFER:
380 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
381 /* gtk_action_set_sensitive (widget, FALSE); */
382 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
383 /* gtk_action_set_sensitive (widget, FALSE); */
384 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
385 /* gtk_action_set_sensitive (widget, FALSE); */
386 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
387 /* gtk_action_set_sensitive (widget, FALSE); */
389 if (priv->prev_toolitem)
390 gtk_widget_hide (priv->prev_toolitem);
392 if (priv->next_toolitem)
393 gtk_widget_hide (priv->next_toolitem);
395 if (priv->progress_toolitem)
396 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
397 if (priv->progress_bar)
398 gtk_widget_show (priv->progress_bar);
400 if (priv->cancel_toolitem)
401 gtk_widget_show (priv->cancel_toolitem);
403 /* Show toolbar if it's hiden (optimized view ) */
404 if (priv->optimized_view) {
405 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
406 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
411 g_return_if_reached ();
418 menubar_to_menu (GtkUIManager *ui_manager)
420 GtkWidget *main_menu;
424 /* Create new main menu */
425 main_menu = gtk_menu_new();
427 /* Get the menubar from the UI manager */
428 menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
430 iter = gtk_container_get_children (GTK_CONTAINER (menubar));
434 menu = GTK_WIDGET (iter->data);
435 gtk_widget_reparent(menu, main_menu);
437 iter = g_list_next (iter);
443 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
445 GtkWidget *main_vbox;
446 ModestMsgViewWindowPrivate *priv;
447 ModestWindowPrivate *parent_priv;
449 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
450 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
452 priv->msg_view = modest_msg_view_new (msg);
453 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
454 main_vbox = gtk_vbox_new (FALSE, 6);
457 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
458 gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar));
459 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
461 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
462 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
463 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
464 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
466 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
467 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
468 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
470 priv->find_toolbar = hildon_find_toolbar_new (NULL);
471 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
472 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
473 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
474 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
476 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
477 gtk_widget_show_all (GTK_WIDGET(main_vbox));
481 modest_msg_view_window_disconnect_signals (ModestWindow *self)
483 ModestMsgViewWindowPrivate *priv;
485 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
487 if (g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
488 priv->clipboard_change_handler))
489 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
490 priv->clipboard_change_handler);
492 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
493 priv->queue_change_handler))
494 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
495 priv->queue_change_handler);
497 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
498 priv->account_removed_handler))
499 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
500 priv->account_removed_handler);
504 modest_msg_view_window_finalize (GObject *obj)
506 ModestMsgViewWindowPrivate *priv;
508 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
510 /* Sanity check: shouldn't be needed, the window mgr should
511 call this function before */
512 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
514 if (priv->header_model != NULL) {
515 g_object_unref (priv->header_model);
516 priv->header_model = NULL;
519 if (priv->progress_bar_timeout > 0) {
520 g_source_remove (priv->progress_bar_timeout);
521 priv->progress_bar_timeout = 0;
524 if (priv->remove_attachment_banner) {
525 gtk_widget_destroy (priv->remove_attachment_banner);
526 g_object_unref (priv->remove_attachment_banner);
527 priv->remove_attachment_banner = NULL;
530 if (priv->purge_timeout > 0) {
531 g_source_remove (priv->purge_timeout);
532 priv->purge_timeout = 0;
535 if (priv->row_reference) {
536 gtk_tree_row_reference_free (priv->row_reference);
537 priv->row_reference = NULL;
540 if (priv->next_row_reference) {
541 gtk_tree_row_reference_free (priv->next_row_reference);
542 priv->next_row_reference = NULL;
546 g_free (priv->msg_uid);
547 priv->msg_uid = NULL;
550 G_OBJECT_CLASS(parent_class)->finalize (obj);
554 select_next_valid_row (GtkTreeModel *model,
555 GtkTreeRowReference **row_reference,
558 GtkTreeIter tmp_iter;
559 GtkTreePath *path, *next;
560 gboolean retval = FALSE;
562 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
564 path = gtk_tree_row_reference_get_path (*row_reference);
565 gtk_tree_model_get_iter (model, &tmp_iter, path);
566 gtk_tree_row_reference_free (*row_reference);
567 *row_reference = NULL;
569 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
570 next = gtk_tree_model_get_path (model, &tmp_iter);
571 *row_reference = gtk_tree_row_reference_new (model, next);
573 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
574 next = gtk_tree_model_get_path (model, &tmp_iter);
576 /* Ensure that we are not selecting the same */
577 if (gtk_tree_path_compare (path, next) != 0) {
578 *row_reference = gtk_tree_row_reference_new (model, next);
584 gtk_tree_path_free (path);
589 /* TODO: This should be in _init(), with the parameters as properties. */
591 modest_msg_view_window_construct (ModestMsgViewWindow *self, TnyMsg *msg,
592 const gchar *modest_account_name,
593 const gchar *msg_uid)
596 ModestMsgViewWindowPrivate *priv = NULL;
597 ModestWindowPrivate *parent_priv = NULL;
598 ModestDimmingRulesGroup *menu_rules_group = NULL;
599 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
600 GtkActionGroup *action_group = NULL;
601 GError *error = NULL;
602 GdkPixbuf *window_icon;
604 g_return_if_fail (msg);
606 obj = G_OBJECT (self);
607 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
608 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
610 priv->msg_uid = g_strdup (msg_uid);
612 parent_priv->ui_manager = gtk_ui_manager_new();
613 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
615 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
616 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
618 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
619 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
621 /* Add common actions */
622 gtk_action_group_add_actions (action_group,
623 modest_action_entries,
624 G_N_ELEMENTS (modest_action_entries),
626 gtk_action_group_add_toggle_actions (action_group,
627 modest_toggle_action_entries,
628 G_N_ELEMENTS (modest_toggle_action_entries),
630 gtk_action_group_add_toggle_actions (action_group,
631 msg_view_toggle_action_entries,
632 G_N_ELEMENTS (msg_view_toggle_action_entries),
634 gtk_action_group_add_radio_actions (action_group,
635 msg_view_zoom_action_entries,
636 G_N_ELEMENTS (msg_view_zoom_action_entries),
638 G_CALLBACK (modest_ui_actions_on_change_zoom),
641 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
642 g_object_unref (action_group);
644 /* Load the UI definition */
645 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
648 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
649 g_error_free (error);
654 /* Add common dimming rules */
655 modest_dimming_rules_group_add_rules (menu_rules_group,
656 modest_msg_view_menu_dimming_entries,
657 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
659 modest_dimming_rules_group_add_rules (toolbar_rules_group,
660 modest_msg_view_toolbar_dimming_entries,
661 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
664 /* Insert dimming rules group for this window */
665 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
666 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
667 g_object_unref (menu_rules_group);
668 g_object_unref (toolbar_rules_group);
670 /* Add accelerators */
671 gtk_window_add_accel_group (GTK_WINDOW (obj),
672 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
675 init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
676 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
678 /* Set window icon */
679 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
681 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
682 g_object_unref (window_icon);
685 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
687 g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
688 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
689 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
690 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
691 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
692 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
693 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
694 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
695 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
696 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
698 g_signal_connect (G_OBJECT (obj), "key-release-event",
699 G_CALLBACK (modest_msg_view_window_key_release_event),
702 g_signal_connect (G_OBJECT (obj), "window-state-event",
703 G_CALLBACK (modest_msg_view_window_window_state_event),
706 /* Mail Operation Queue */
707 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
709 G_CALLBACK (on_queue_changed),
712 /* Account manager */
713 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
715 G_CALLBACK(on_account_removed),
718 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
720 priv->last_search = NULL;
722 /* Init the clipboard actions dim status */
723 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
725 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
727 /* Check toolbar dimming rules */
728 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
733 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
734 const gchar *modest_account_name,
735 const gchar *msg_uid,
737 GtkTreeRowReference *row_reference)
739 ModestMsgViewWindow *window = NULL;
740 ModestMsgViewWindowPrivate *priv = NULL;
742 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
743 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
744 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
746 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
748 /* Remember the message list's TreeModel so we can detect changes
749 * and change the list selection when necessary: */
750 g_object_ref (model);
751 priv->header_model = model;
752 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
753 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
754 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
756 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
757 G_CALLBACK (modest_msg_view_window_on_row_changed),
759 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
760 G_CALLBACK (modest_msg_view_window_on_row_deleted),
762 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
763 G_CALLBACK (modest_msg_view_window_on_row_inserted),
765 g_signal_connect (GTK_TREE_MODEL(model), "rows-reordered",
766 G_CALLBACK (modest_msg_view_window_on_row_reordered),
769 modest_msg_view_window_update_priority (window);
771 /* Check toolbar dimming rules */
772 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
774 return MODEST_WINDOW(window);
778 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
779 const gchar *modest_account_name,
780 const gchar *msg_uid)
782 ModestMsgViewWindow *window = NULL;
783 ModestMsgViewWindowPrivate *priv = NULL;
785 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
786 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
787 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
789 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
791 /* Remember that this is a search result,
792 * so we can disable some UI appropriately: */
793 priv->is_search_result = TRUE;
795 return MODEST_WINDOW(window);
799 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
800 const gchar *modest_account_name,
801 const gchar *msg_uid)
804 g_return_val_if_fail (msg, NULL);
806 obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
807 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
808 msg, modest_account_name, msg_uid);
810 return MODEST_WINDOW(obj);
813 void modest_msg_view_window_on_row_changed(
814 GtkTreeModel *header_model,
817 ModestMsgViewWindow *window){
818 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
821 void modest_msg_view_window_on_row_deleted(
822 GtkTreeModel *header_model,
824 ModestMsgViewWindow *window){
825 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
828 void modest_msg_view_window_on_row_inserted(
829 GtkTreeModel *header_model,
832 ModestMsgViewWindow *window){
833 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
836 void modest_msg_view_window_on_row_reordered(
837 GtkTreeModel *header_model,
841 ModestMsgViewWindow *window){
842 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
846 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
848 ModestMsgViewWindowPrivate *priv= NULL;
850 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
851 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
853 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
857 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
859 ModestMsgViewWindowPrivate *priv= NULL;
861 TnyHeader *header = NULL;
862 GtkTreePath *path = NULL;
865 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
866 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
868 /* If the message was not obtained from a treemodel,
869 * for instance if it was opened directly by the search UI:
871 if (priv->header_model == NULL) {
872 msg = modest_msg_view_window_get_message (self);
873 header = tny_msg_get_header (msg);
874 g_object_unref (msg);
878 /* Get iter of the currently selected message in the header view: */
879 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
880 * instead of sometimes retrieving it from the header view?
881 * Then we wouldn't be dependent on the message actually still being selected
882 * in the header view. murrayc. */
883 path = gtk_tree_row_reference_get_path (priv->row_reference);
884 g_return_val_if_fail (path != NULL, NULL);
885 gtk_tree_model_get_iter (priv->header_model,
889 /* Get current message header */
890 gtk_tree_model_get (priv->header_model, &iter,
891 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
894 gtk_tree_path_free (path);
899 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
901 ModestMsgView *msg_view;
902 ModestMsgViewWindowPrivate *priv;
904 g_return_val_if_fail (self, NULL);
906 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
908 msg_view = MODEST_MSG_VIEW (priv->msg_view);
910 return modest_msg_view_get_message (msg_view);
914 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
916 ModestMsgViewWindowPrivate *priv;
918 g_return_val_if_fail (self, NULL);
920 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
922 return (const gchar*) priv->msg_uid;
926 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
929 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
930 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
931 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
935 is_active = gtk_toggle_action_get_active (toggle);
938 gtk_widget_show (priv->find_toolbar);
939 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
941 gtk_widget_hide (priv->find_toolbar);
944 /* update the toggle buttons status */
945 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
946 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
947 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
948 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
953 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
954 ModestMsgViewWindow *obj)
956 GtkToggleAction *toggle;
957 ModestWindowPrivate *parent_priv;
958 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
960 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
961 gtk_toggle_action_set_active (toggle, FALSE);
965 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
966 ModestMsgViewWindow *obj)
968 gchar *current_search;
969 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
971 if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
972 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
976 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
978 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
979 g_free (current_search);
980 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
984 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
986 g_free (priv->last_search);
987 priv->last_search = g_strdup (current_search);
988 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
991 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
992 g_free (priv->last_search);
993 priv->last_search = NULL;
996 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
997 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
998 g_free (priv->last_search);
999 priv->last_search = NULL;
1003 g_free (current_search);
1008 modest_msg_view_window_set_zoom (ModestWindow *window,
1011 ModestMsgViewWindowPrivate *priv;
1013 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1015 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1016 modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
1020 modest_msg_view_window_get_zoom (ModestWindow *window)
1022 ModestMsgViewWindowPrivate *priv;
1024 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1026 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1027 return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
1031 modest_msg_view_window_zoom_plus (ModestWindow *window)
1033 ModestWindowPrivate *parent_priv;
1034 GtkRadioAction *zoom_radio_action;
1035 GSList *group, *node;
1037 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1038 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1039 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1041 group = gtk_radio_action_get_group (zoom_radio_action);
1043 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1044 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1048 for (node = group; node != NULL; node = g_slist_next (node)) {
1049 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1050 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1058 modest_msg_view_window_zoom_minus (ModestWindow *window)
1060 ModestWindowPrivate *parent_priv;
1061 GtkRadioAction *zoom_radio_action;
1062 GSList *group, *node;
1064 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1065 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1066 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1068 group = gtk_radio_action_get_group (zoom_radio_action);
1070 for (node = group; node != NULL; node = g_slist_next (node)) {
1071 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1072 if (node->next != NULL) {
1073 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1076 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1086 modest_msg_view_window_key_release_event (GtkWidget *window,
1090 if (event->type == GDK_KEY_RELEASE) {
1091 switch (event->keyval) {
1093 modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1097 modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1110 modest_msg_view_window_scroll_up (ModestWindow *window)
1112 ModestMsgViewWindowPrivate *priv;
1113 gboolean return_value;
1115 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1116 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1120 modest_msg_view_window_scroll_down (ModestWindow *window)
1122 ModestMsgViewWindowPrivate *priv;
1123 gboolean return_value;
1125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1126 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1130 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1133 ModestMsgViewWindowPrivate *priv;
1134 GtkTreeIter tmp_iter;
1135 gboolean is_last_selected;
1137 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1140 /*if no model (so no rows at all), then virtually we are the last*/
1141 if (!priv->header_model)
1144 path = gtk_tree_row_reference_get_path (priv->row_reference);
1148 is_last_selected = TRUE;
1149 while (is_last_selected) {
1151 gtk_tree_path_next (path);
1152 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1154 gtk_tree_model_get (priv->header_model, &tmp_iter,
1155 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1157 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1158 is_last_selected = FALSE;
1160 gtk_tree_path_free (path);
1161 return is_last_selected;
1165 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1167 ModestMsgViewWindowPrivate *priv;
1169 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1170 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1172 return priv->header_model != NULL;
1176 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1178 ModestMsgViewWindowPrivate *priv;
1180 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1183 return priv->is_search_result;
1187 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1190 ModestMsgViewWindowPrivate *priv;
1191 gboolean is_first_selected;
1192 GtkTreeIter tmp_iter;
1193 /* gchar * path_string;*/
1195 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1196 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1198 /*if no model (so no rows at all), then virtually we are the first*/
1199 if (!priv->header_model)
1202 path = gtk_tree_row_reference_get_path (priv->row_reference);
1206 /* path_string = gtk_tree_path_to_string (path);
1207 is_first_selected = strcmp (path_string, "0");
1209 g_free (path_string);
1210 gtk_tree_path_free (path);
1212 return is_first_selected;*/
1214 is_first_selected = TRUE;
1215 while (is_first_selected) {
1217 if(!gtk_tree_path_prev (path))
1219 /* Here the 'if' is needless for logic, but let make sure
1220 * iter is valid for gtk_tree_model_get. */
1221 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1223 gtk_tree_model_get (priv->header_model, &tmp_iter,
1224 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1226 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1227 is_first_selected = FALSE;
1229 gtk_tree_path_free (path);
1230 return is_first_selected;
1234 * Reads the message whose summary item is @header. It takes care of
1235 * several things, among others:
1237 * If the message was not previously downloaded then ask the user
1238 * before downloading. If there is no connection launch the connection
1239 * dialog. Update toolbar dimming rules.
1241 * Returns: TRUE if the mail operation was started, otherwise if the
1242 * user do not want to download the message, or if the user do not
1243 * want to connect, then the operation is not issued
1246 message_reader (ModestMsgViewWindow *window,
1247 ModestMsgViewWindowPrivate *priv,
1251 ModestMailOperation *mail_op = NULL;
1252 ModestMailOperationTypeOperation op_type;
1253 gboolean already_showing = FALSE;
1254 ModestWindow *msg_window = NULL;
1255 ModestWindowMgr *mgr;
1257 g_return_val_if_fail (path != NULL, FALSE);
1259 mgr = modest_runtime_get_window_mgr ();
1260 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1261 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1264 gtk_window_present (GTK_WINDOW (msg_window));
1265 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1269 /* Msg download completed */
1270 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1271 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1274 GtkResponseType response;
1276 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1278 /* Ask the user if he wants to download the message */
1279 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1280 _("mcen_nc_get_msg"));
1281 if (response == GTK_RESPONSE_CANCEL)
1284 /* Offer the connection dialog if necessary */
1285 /* FIXME: should this stuff go directly to the mail
1286 operation instead of spread it all over the
1288 folder = tny_header_get_folder (header);
1290 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1291 g_object_unref (folder);
1294 g_object_unref (folder);
1298 /* New mail operation */
1299 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1301 modest_ui_actions_get_msgs_full_error_handler,
1304 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1305 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1306 g_object_unref (mail_op);
1308 /* Update toolbar dimming rules */
1309 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1315 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1317 ModestMsgViewWindowPrivate *priv;
1318 GtkTreePath *path= NULL;
1319 GtkTreeIter tmp_iter;
1321 gboolean retval = TRUE;
1323 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1324 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1326 /* Update the next row reference if it's not valid. This could
1327 happen if for example the header which it was pointing to,
1328 was deleted. The best place to do it is in the row-deleted
1329 handler but the tinymail model do not work like the glib
1330 tree models and reports the deletion when the row is still
1332 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1333 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1334 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1335 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1338 if (priv->next_row_reference)
1339 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1343 gtk_tree_model_get_iter (priv->header_model,
1347 gtk_tree_model_get (priv->header_model, &tmp_iter,
1348 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1351 /* Read the message & show it */
1352 if (!message_reader (window, priv, header, path)) {
1354 gtk_tree_path_free (path);
1358 g_object_unref (header);
1364 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1366 ModestMsgViewWindowPrivate *priv = NULL;
1367 TnyHeader *header = NULL;
1371 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1372 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1374 /* Check that the model is not empty */
1375 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1378 /* Get the header */
1379 gtk_tree_model_get (priv->header_model,
1381 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1383 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1384 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1385 g_object_unref (header);
1386 return modest_msg_view_window_select_next_message (self);
1389 path = gtk_tree_model_get_path (priv->header_model, &iter);
1391 /* Read the message & show it */
1392 message_reader (self, priv, header, path);
1395 g_object_unref (header);
1401 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1403 ModestMsgViewWindowPrivate *priv = NULL;
1406 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1407 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1409 /* Return inmediatly if there is no header model */
1410 if (!priv->header_model)
1413 path = gtk_tree_row_reference_get_path (priv->row_reference);
1414 while (gtk_tree_path_prev (path)) {
1418 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1419 gtk_tree_model_get (priv->header_model, &iter,
1420 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1424 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1425 g_object_unref (header);
1429 /* Read the message & show it */
1430 if (!message_reader (window, priv, header, path)) {
1431 g_object_unref (header);
1435 g_object_unref (header);
1440 gtk_tree_path_free (path);
1445 view_msg_cb (ModestMailOperation *mail_op,
1450 ModestMsgViewWindow *self = NULL;
1451 ModestMsgViewWindowPrivate *priv = NULL;
1454 /* If there was any error */
1455 path = (GtkTreePath *) user_data;
1456 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1457 gtk_tree_path_free (path);
1461 /* Get the window */
1462 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1463 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1464 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1466 /* Update the row reference */
1467 gtk_tree_row_reference_free (priv->row_reference);
1468 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1469 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1470 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1471 gtk_tree_path_free (path);
1473 /* Mark header as read */
1474 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1475 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1477 /* Set new message */
1478 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1479 modest_msg_view_window_update_priority (self);
1480 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1481 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1483 /* Set the new message uid of the window */
1484 if (priv->msg_uid) {
1485 g_free (priv->msg_uid);
1486 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1489 /* Notify the observers */
1490 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1491 0, priv->header_model, priv->row_reference);
1493 /* Free new references */
1494 g_object_unref (self);
1498 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1500 ModestMsgViewWindowPrivate *priv;
1502 TnyFolderType folder_type;
1504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1506 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1508 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1512 folder = tny_msg_get_folder (msg);
1515 folder_type = tny_folder_get_folder_type (folder);
1517 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1518 const gchar *fname = tny_folder_get_name (folder);
1519 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1522 g_object_unref (folder);
1524 g_object_unref (msg);
1532 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1534 ModestMsgViewWindowPrivate *priv;
1535 TnyHeaderFlags flags = 0;
1537 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1539 if (priv->header_model) {
1542 GtkTreePath *path = NULL;
1544 path = gtk_tree_row_reference_get_path (priv->row_reference);
1545 g_return_if_fail (path != NULL);
1546 gtk_tree_model_get_iter (priv->header_model,
1548 gtk_tree_row_reference_get_path (priv->row_reference));
1550 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1552 flags = tny_header_get_flags (header);
1553 gtk_tree_path_free (path);
1556 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1561 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1563 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1564 ModestWindowPrivate *parent_priv;
1565 ModestWindowMgr *mgr;
1566 gboolean is_fullscreen;
1567 GtkAction *fs_toggle_action;
1570 mgr = modest_runtime_get_window_mgr ();
1571 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1573 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1575 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1576 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1577 if (is_fullscreen != active) {
1578 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1587 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
1589 ModestWindowPrivate *parent_priv;
1590 GtkAction *fs_toggle_action;
1591 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1593 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1594 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
1595 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
1599 set_homogeneous (GtkWidget *widget,
1602 if (GTK_IS_TOOL_ITEM (widget)) {
1603 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1604 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1609 modest_msg_view_window_show_toolbar (ModestWindow *self,
1610 gboolean show_toolbar)
1612 ModestMsgViewWindowPrivate *priv = NULL;
1613 ModestWindowPrivate *parent_priv;
1614 GtkWidget *reply_button = NULL, *menu = NULL;
1615 GtkWidget *placeholder = NULL;
1618 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1619 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1621 /* Set optimized view status */
1622 priv->optimized_view = !show_toolbar;
1624 if (!parent_priv->toolbar) {
1625 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1628 /* Set homogeneous toolbar */
1629 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1630 set_homogeneous, NULL);
1632 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1633 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1634 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1635 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1636 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1637 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1638 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1639 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1641 /* Add ProgressBar (Transfer toolbar) */
1642 priv->progress_bar = modest_progress_bar_widget_new ();
1643 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1644 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1645 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1646 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1647 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1649 /* Connect cancel 'clicked' signal to abort progress mode */
1650 g_signal_connect(priv->cancel_toolitem, "clicked",
1651 G_CALLBACK(cancel_progressbar),
1654 /* Add it to the observers list */
1655 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1658 hildon_window_add_toolbar (HILDON_WINDOW (self),
1659 GTK_TOOLBAR (parent_priv->toolbar));
1662 /* Set reply button tap and hold menu */
1663 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1664 "/ToolBar/ToolbarMessageReply");
1665 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1666 "/ToolbarReplyCSM");
1667 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1671 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1672 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1673 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1675 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1676 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1679 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1680 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1685 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1687 ModestMsgViewWindow *window)
1689 ModestWindowPrivate *parent_priv;
1690 /* GtkAction *action; */
1691 gboolean is_address;
1695 if (!GTK_WIDGET_VISIBLE (window))
1698 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1699 selection = gtk_clipboard_wait_for_text (clipboard);
1701 is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1703 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1704 /* gtk_action_set_sensitive (action, is_address); */
1706 focused = gtk_window_get_focus (GTK_WINDOW (window));
1708 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1709 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1711 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1712 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1715 /* modest_msg_view_window_update_dimmed (window); */
1720 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1722 ModestMsgViewWindowPrivate *priv;
1724 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1725 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1727 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1731 cancel_progressbar (GtkToolButton *toolbutton,
1732 ModestMsgViewWindow *self)
1735 ModestMsgViewWindowPrivate *priv;
1737 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1739 /* Get operation observers and cancel its current operation */
1740 tmp = priv->progress_widgets;
1742 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
1743 tmp=g_slist_next(tmp);
1747 observers_empty (ModestMsgViewWindow *self)
1750 ModestMsgViewWindowPrivate *priv;
1751 gboolean is_empty = TRUE;
1752 guint pending_ops = 0;
1754 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1755 tmp = priv->progress_widgets;
1757 /* Check all observers */
1758 while (tmp && is_empty) {
1759 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
1760 is_empty = pending_ops == 0;
1762 tmp = g_slist_next(tmp);
1769 on_account_removed (TnyAccountStore *account_store,
1770 TnyAccount *account,
1773 /* Do nothing if it's a transport account, because we only
1774 show the messages of a store account */
1775 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
1776 const gchar *parent_acc = NULL;
1777 const gchar *our_acc = NULL;
1779 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
1780 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1782 /* Close this window if I'm showing a message of the removed account */
1783 if (strcmp (parent_acc, our_acc) == 0)
1784 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
1789 on_queue_changed (ModestMailOperationQueue *queue,
1790 ModestMailOperation *mail_op,
1791 ModestMailOperationQueueNotification type,
1792 ModestMsgViewWindow *self)
1795 ModestMsgViewWindowPrivate *priv;
1796 ModestMailOperationTypeOperation op_type;
1797 ModestToolBarModes mode;
1799 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1800 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1802 /* If this operations was created by another window, do nothing */
1803 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
1806 /* Get toolbar mode from operation id*/
1807 op_type = modest_mail_operation_get_type_operation (mail_op);
1809 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
1810 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
1811 case MODEST_MAIL_OPERATION_TYPE_OPEN:
1812 mode = TOOLBAR_MODE_TRANSFER;
1815 mode = TOOLBAR_MODE_NORMAL;
1819 /* Add operation observers and change toolbar if neccessary*/
1820 tmp = priv->progress_widgets;
1822 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
1823 if (mode == TOOLBAR_MODE_TRANSFER) {
1824 /* Enable transfer toolbar mode */
1825 set_toolbar_transfer_mode(self);
1827 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1829 tmp = g_slist_next (tmp);
1834 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
1835 if (mode == TOOLBAR_MODE_TRANSFER) {
1837 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1839 tmp = g_slist_next (tmp);
1843 /* If no more operations are being observed, NORMAL mode is enabled again */
1844 if (observers_empty (self)) {
1845 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
1853 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
1855 ModestMsgViewWindowPrivate *priv;
1856 GList *selected_attachments = NULL;
1858 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
1859 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
1861 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1863 return selected_attachments;
1867 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
1869 ModestMsgViewWindowPrivate *priv;
1870 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1871 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
1873 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1875 if (mime_part == NULL) {
1876 gboolean error = FALSE;
1877 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1878 if (selected_attachments == NULL) {
1880 } else if (g_list_length (selected_attachments) > 1) {
1881 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
1884 mime_part = (TnyMimePart *) selected_attachments->data;
1885 g_object_ref (mime_part);
1887 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
1888 g_list_free (selected_attachments);
1893 g_object_ref (mime_part);
1896 if (tny_mime_part_is_purged (mime_part)) {
1897 g_object_unref (mime_part);
1901 if (!TNY_IS_MSG (mime_part)) {
1902 gchar *filepath = NULL;
1903 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
1904 gchar *extension = NULL;
1905 TnyFsStream *temp_stream = NULL;
1908 extension = g_strrstr (att_filename, ".");
1909 if (extension != NULL)
1913 temp_stream = modest_maemo_utils_create_temp_stream (extension, &filepath);
1916 const gchar *content_type;
1917 content_type = tny_mime_part_get_content_type (mime_part);
1918 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
1920 modest_platform_activate_file (filepath, content_type);
1921 g_object_unref (temp_stream);
1923 /* TODO: delete temporary file */
1926 /* message attachment */
1927 TnyHeader *header = NULL;
1928 ModestWindowMgr *mgr;
1929 ModestWindow *msg_win = NULL;
1932 header = tny_msg_get_header (TNY_MSG (mime_part));
1933 mgr = modest_runtime_get_window_mgr ();
1934 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
1937 if (msg_win) /* there is already a window for this uid; top it */
1938 gtk_window_present (GTK_WINDOW(msg_win));
1940 /* if it's found, but there is no msg_win, it's probably in the process of being created;
1941 * thus, we don't do anything */
1942 g_warning ("window for is already being created");
1944 /* it's not found, so create a new window for it */
1945 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
1946 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
1948 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
1949 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
1950 modest_window_set_zoom (MODEST_WINDOW (msg_win),
1951 modest_window_get_zoom (MODEST_WINDOW (window)));
1952 modest_window_mgr_register_window (mgr, msg_win);
1953 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
1954 gtk_widget_show_all (GTK_WIDGET (msg_win));
1957 g_object_unref (mime_part);
1973 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
1974 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
1975 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
1976 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
1979 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
1983 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
1984 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
1985 g_free (pair->filename);
1986 g_object_unref (pair->part);
1987 g_slice_free (SaveMimePartPair, pair);
1989 g_list_free (info->pairs);
1992 gtk_widget_destroy (info->banner);
1993 g_object_unref (info->banner);
1994 g_slice_free (SaveMimePartInfo, info);
1999 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2001 if (info->pairs != NULL) {
2002 /* This is a GDK lock because we are an idle callback and
2003 * save_mime_parts_to_file_with_checks can contain Gtk+ code */
2005 gdk_threads_enter (); /* CHECKED */
2006 save_mime_parts_to_file_with_checks (info);
2007 gdk_threads_leave (); /* CHECKED */
2010 result = info->result;
2012 /* This is a GDK lock because we are an idle callback and
2013 * hildon_banner_show_information is or does Gtk+ code */
2015 gdk_threads_enter (); /* CHECKED */
2016 save_mime_part_info_free (info, TRUE);
2018 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2020 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2022 gdk_threads_leave (); /* CHECKED */
2029 save_mime_part_to_file (SaveMimePartInfo *info)
2031 GnomeVFSResult result;
2032 GnomeVFSHandle *handle;
2034 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2036 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2037 if (result == GNOME_VFS_OK) {
2038 stream = tny_vfs_stream_new (handle);
2039 tny_mime_part_decode_to_stream (pair->part, stream);
2040 g_object_unref (G_OBJECT (stream));
2041 g_object_unref (pair->part);
2042 g_slice_free (SaveMimePartPair, pair);
2043 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2044 info->result = TRUE;
2046 save_mime_part_info_free (info, FALSE);
2047 info->result = FALSE;
2050 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2055 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2057 SaveMimePartPair *pair;
2058 gboolean is_ok = TRUE;
2060 pair = info->pairs->data;
2061 if (modest_maemo_utils_file_exists (pair->filename)) {
2062 GtkWidget *confirm_overwrite_dialog;
2063 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2064 _("emev_nc_replace_files"));
2065 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2068 gtk_widget_destroy (confirm_overwrite_dialog);
2072 save_mime_part_info_free (info, TRUE);
2074 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2081 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2083 gboolean clean_list = FALSE;
2084 ModestMsgViewWindowPrivate *priv;
2085 GList *files_to_save = NULL;
2086 GtkWidget *save_dialog = NULL;
2087 gchar *folder = NULL;
2088 gboolean canceled = FALSE;
2089 const gchar *filename = NULL;
2090 gchar *save_multiple_str = NULL;
2092 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2093 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2095 if (mime_parts == NULL) {
2096 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2097 if (mime_parts == NULL)
2102 /* prepare dialog */
2103 if (mime_parts->next == NULL) {
2104 /* only one attachment selected */
2105 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2106 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2107 filename = tny_mime_part_get_filename (mime_part);
2109 g_warning ("Tried to save a non-file attachment");
2113 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2114 g_list_length (mime_parts));
2117 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2118 GTK_FILE_CHOOSER_ACTION_SAVE);
2121 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2122 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2126 if (filename != NULL)
2127 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2130 /* if multiple, set multiple string */
2131 if (save_multiple_str) {
2132 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2136 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2137 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2139 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2140 hildon_banner_show_information
2141 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2145 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2146 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2148 if (tny_mime_part_is_attachment (mime_part)) {
2149 SaveMimePartPair *pair;
2151 if ((mime_parts->next != NULL) &&
2152 (tny_mime_part_get_filename (mime_part) == NULL))
2155 pair = g_slice_new0 (SaveMimePartPair);
2156 if (mime_parts->next == NULL) {
2157 pair->filename = g_strdup (chooser_uri);
2160 g_build_filename (chooser_uri,
2161 tny_mime_part_get_filename (mime_part), NULL);
2163 pair->part = g_object_ref (mime_part);
2164 files_to_save = g_list_prepend (files_to_save, pair);
2168 g_free (chooser_uri);
2171 gtk_widget_destroy (save_dialog);
2174 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2175 g_list_free (mime_parts);
2178 if (files_to_save != NULL) {
2179 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2180 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2181 _CS("sfil_ib_saving"));
2182 info->pairs = files_to_save;
2183 info->banner = banner;
2184 info->result = TRUE;
2185 g_object_ref (banner);
2186 save_mime_parts_to_file_with_checks (info);
2191 show_remove_attachment_information (gpointer userdata)
2193 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2194 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2196 if (priv->remove_attachment_banner != NULL) {
2197 gtk_widget_destroy (priv->remove_attachment_banner);
2198 g_object_unref (priv->remove_attachment_banner);
2201 priv->remove_attachment_banner = g_object_ref (
2202 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2208 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2210 ModestMsgViewWindowPrivate *priv;
2211 GList *mime_parts = NULL, *node;
2212 gchar *confirmation_message;
2216 /* TnyFolder *folder; */
2218 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2219 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2222 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2224 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2226 /* Remove already purged messages from mime parts list */
2228 while (node != NULL) {
2229 TnyMimePart *part = TNY_MIME_PART (node->data);
2230 if (tny_mime_part_is_purged (part)) {
2231 GList *deleted_node = node;
2232 node = g_list_next (node);
2233 g_object_unref (part);
2234 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2236 node = g_list_next (node);
2240 if (mime_parts == NULL)
2243 n_attachments = g_list_length (mime_parts);
2244 if (n_attachments == 1) {
2245 const gchar *filename;
2247 if (TNY_IS_MSG (mime_parts->data)) {
2249 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2250 filename = tny_header_get_subject (header);
2251 g_object_unref (header);
2252 if (filename == NULL)
2253 filename = _("mail_va_no_subject");
2255 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2257 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2259 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2260 "mcen_nc_purge_files_text",
2261 n_attachments), n_attachments);
2263 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2264 confirmation_message);
2265 g_free (confirmation_message);
2267 if (response != GTK_RESPONSE_OK)
2270 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2271 /* folder = tny_msg_get_folder (msg); */
2272 /* tny_msg_uncache_attachments (msg); */
2273 /* tny_folder_refresh (folder, NULL); */
2274 /* g_object_unref (folder); */
2276 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2277 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2278 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2281 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2282 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2283 tny_msg_rewrite_cache (msg);
2284 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2286 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2287 g_list_free (mime_parts);
2289 if (priv->purge_timeout > 0) {
2290 g_source_remove (priv->purge_timeout);
2291 priv->purge_timeout = 0;
2294 if (priv->remove_attachment_banner) {
2295 gtk_widget_destroy (priv->remove_attachment_banner);
2296 g_object_unref (priv->remove_attachment_banner);
2297 priv->remove_attachment_banner = NULL;
2305 update_window_title (ModestMsgViewWindow *window)
2307 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2309 TnyHeader *header = NULL;
2310 const gchar *subject = NULL;
2312 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2314 header = tny_msg_get_header (msg);
2315 subject = tny_header_get_subject (header);
2316 g_object_unref (msg);
2319 if ((subject == NULL)||(subject[0] == '\0'))
2320 subject = _("mail_va_no_subject");
2322 gtk_window_set_title (GTK_WINDOW (window), subject);