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_header_view_observer_init(
63 ModestHeaderViewObserverIface *iface_class);
64 static void modest_msg_view_window_finalize (GObject *obj);
65 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
67 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
68 ModestMsgViewWindow *obj);
69 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
70 ModestMsgViewWindow *obj);
72 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
73 static void modest_msg_view_window_set_zoom (ModestWindow *window,
75 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
76 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
77 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
78 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
81 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
82 GdkEventWindowState *event,
84 static void modest_msg_view_window_scroll_up (ModestWindow *window);
85 static void modest_msg_view_window_scroll_down (ModestWindow *window);
86 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
88 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
89 gboolean show_toolbar);
91 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
93 ModestMsgViewWindow *window);
94 void modest_msg_view_window_on_row_changed(
95 GtkTreeModel *header_model,
98 ModestMsgViewWindow *window);
100 void modest_msg_view_window_on_row_deleted(
101 GtkTreeModel *header_model,
103 ModestMsgViewWindow *window);
105 void modest_msg_view_window_on_row_inserted(
106 GtkTreeModel *header_model,
107 GtkTreePath *tree_path,
108 GtkTreeIter *tree_iter,
109 ModestMsgViewWindow *window);
111 void modest_msg_view_window_on_row_reordered(
112 GtkTreeModel *header_model,
116 ModestMsgViewWindow *window);
118 void modest_msg_view_window_update_model_replaced(
119 ModestHeaderViewObserver *window,
121 const gchar *tny_folder_id);
123 static void cancel_progressbar (GtkToolButton *toolbutton,
124 ModestMsgViewWindow *self);
126 static void on_queue_changed (ModestMailOperationQueue *queue,
127 ModestMailOperation *mail_op,
128 ModestMailOperationQueueNotification type,
129 ModestMsgViewWindow *self);
131 static void on_account_removed (TnyAccountStore *account_store,
135 static void view_msg_cb (ModestMailOperation *mail_op,
140 static void set_toolbar_mode (ModestMsgViewWindow *self,
141 ModestToolBarModes mode);
143 static void update_window_title (ModestMsgViewWindow *window);
145 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
148 /* list my signals */
154 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
155 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
156 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
159 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
160 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
161 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
162 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
163 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
164 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
165 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
168 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
169 struct _ModestMsgViewWindowPrivate {
172 GtkWidget *main_scroll;
173 GtkWidget *find_toolbar;
176 /* Progress observers */
177 GtkWidget *progress_bar;
178 GSList *progress_widgets;
181 GtkWidget *progress_toolitem;
182 GtkWidget *cancel_toolitem;
183 GtkWidget *prev_toolitem;
184 GtkWidget *next_toolitem;
185 ModestToolBarModes current_toolbar_mode;
187 /* Optimized view enabled */
188 gboolean optimized_view;
190 /* Whether this was created via the *_new_for_search_result() function. */
191 gboolean is_search_result;
193 /* A reference to the @model of the header view
194 * to allow selecting previous/next messages,
195 * if the message is currently selected in the header view.
197 const gchar *header_folder_id;
198 GtkTreeModel *header_model;
199 GtkTreeRowReference *row_reference;
200 GtkTreeRowReference *next_row_reference;
202 gulong clipboard_change_handler;
203 gulong queue_change_handler;
204 gulong account_removed_handler;
205 gulong row_changed_handler;
206 gulong row_deleted_handler;
207 gulong row_inserted_handler;
208 gulong rows_reordered_handler;
211 GtkWidget *remove_attachment_banner;
213 guint progress_bar_timeout;
218 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
219 MODEST_TYPE_MSG_VIEW_WINDOW, \
220 ModestMsgViewWindowPrivate))
222 static GtkWindowClass *parent_class = NULL;
224 /* uncomment the following if you have defined any signals */
225 static guint signals[LAST_SIGNAL] = {0};
228 modest_msg_view_window_get_type (void)
230 static GType my_type = 0;
232 static const GTypeInfo my_info = {
233 sizeof(ModestMsgViewWindowClass),
234 NULL, /* base init */
235 NULL, /* base finalize */
236 (GClassInitFunc) modest_msg_view_window_class_init,
237 NULL, /* class finalize */
238 NULL, /* class data */
239 sizeof(ModestMsgViewWindow),
241 (GInstanceInitFunc) modest_msg_view_window_init,
244 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
245 "ModestMsgViewWindow",
248 static const GInterfaceInfo modest_header_view_observer_info =
250 (GInterfaceInitFunc) modest_header_view_observer_init,
251 NULL, /* interface_finalize */
252 NULL /* interface_data */
255 g_type_add_interface_static (my_type,
256 MODEST_TYPE_HEADER_VIEW_OBSERVER,
257 &modest_header_view_observer_info);
263 save_state (ModestWindow *self)
265 modest_widget_memory_save (modest_runtime_get_conf (),
267 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
272 restore_settings (ModestMsgViewWindow *self)
274 modest_widget_memory_restore (modest_runtime_get_conf (),
276 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
280 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
282 GObjectClass *gobject_class;
283 ModestWindowClass *modest_window_class;
284 gobject_class = (GObjectClass*) klass;
285 modest_window_class = (ModestWindowClass *) klass;
287 parent_class = g_type_class_peek_parent (klass);
288 gobject_class->finalize = modest_msg_view_window_finalize;
290 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
291 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
292 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
293 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
294 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
295 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
297 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
299 modest_window_class->save_state_func = save_state;
301 signals[MSG_CHANGED_SIGNAL] =
302 g_signal_new ("msg-changed",
303 G_TYPE_FROM_CLASS (gobject_class),
305 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
307 modest_marshal_VOID__POINTER_POINTER,
308 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
311 static void modest_header_view_observer_init(
312 ModestHeaderViewObserverIface *iface_class)
314 iface_class->update_func = modest_msg_view_window_update_model_replaced;
318 modest_msg_view_window_init (ModestMsgViewWindow *obj)
320 ModestMsgViewWindowPrivate *priv;
321 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
323 priv->is_search_result = FALSE;
325 priv->msg_view = NULL;
326 priv->header_model = NULL;
327 priv->header_folder_id = NULL;
328 priv->clipboard_change_handler = 0;
329 priv->queue_change_handler = 0;
330 priv->account_removed_handler = 0;
331 priv->row_changed_handler = 0;
332 priv->row_deleted_handler = 0;
333 priv->row_inserted_handler = 0;
334 priv->rows_reordered_handler = 0;
335 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
337 priv->optimized_view = FALSE;
338 priv->progress_bar_timeout = 0;
339 priv->purge_timeout = 0;
340 priv->remove_attachment_banner = NULL;
341 priv->msg_uid = NULL;
346 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
348 ModestMsgViewWindowPrivate *priv = NULL;
350 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
352 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
354 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
356 if (priv->progress_bar_timeout > 0) {
357 g_source_remove (priv->progress_bar_timeout);
358 priv->progress_bar_timeout = 0;
365 set_toolbar_mode (ModestMsgViewWindow *self,
366 ModestToolBarModes mode)
368 ModestWindowPrivate *parent_priv;
369 ModestMsgViewWindowPrivate *priv;
370 /* GtkWidget *widget = NULL; */
372 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
374 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
375 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
377 /* Sets current toolbar mode */
378 priv->current_toolbar_mode = mode;
380 /* Update toolbar dimming state */
381 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
384 case TOOLBAR_MODE_NORMAL:
385 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
386 /* gtk_action_set_sensitive (widget, TRUE); */
387 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
388 /* gtk_action_set_sensitive (widget, TRUE); */
389 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
390 /* gtk_action_set_sensitive (widget, TRUE); */
391 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
392 /* gtk_action_set_sensitive (widget, TRUE); */
394 if (priv->prev_toolitem)
395 gtk_widget_show (priv->prev_toolitem);
397 if (priv->next_toolitem)
398 gtk_widget_show (priv->next_toolitem);
400 if (priv->progress_toolitem)
401 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
402 if (priv->progress_bar)
403 gtk_widget_hide (priv->progress_bar);
405 if (priv->cancel_toolitem)
406 gtk_widget_hide (priv->cancel_toolitem);
408 /* Hide toolbar if optimized view is enabled */
409 if (priv->optimized_view) {
410 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
411 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
415 case TOOLBAR_MODE_TRANSFER:
416 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
417 /* gtk_action_set_sensitive (widget, FALSE); */
418 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
419 /* gtk_action_set_sensitive (widget, FALSE); */
420 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
421 /* gtk_action_set_sensitive (widget, FALSE); */
422 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
423 /* gtk_action_set_sensitive (widget, FALSE); */
425 if (priv->prev_toolitem)
426 gtk_widget_hide (priv->prev_toolitem);
428 if (priv->next_toolitem)
429 gtk_widget_hide (priv->next_toolitem);
431 if (priv->progress_toolitem)
432 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
433 if (priv->progress_bar)
434 gtk_widget_show (priv->progress_bar);
436 if (priv->cancel_toolitem)
437 gtk_widget_show (priv->cancel_toolitem);
439 /* Show toolbar if it's hiden (optimized view ) */
440 if (priv->optimized_view) {
441 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
442 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
447 g_return_if_reached ();
454 menubar_to_menu (GtkUIManager *ui_manager)
456 GtkWidget *main_menu;
460 /* Create new main menu */
461 main_menu = gtk_menu_new();
463 /* Get the menubar from the UI manager */
464 menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
466 iter = gtk_container_get_children (GTK_CONTAINER (menubar));
470 menu = GTK_WIDGET (iter->data);
471 gtk_widget_reparent(menu, main_menu);
473 iter = g_list_next (iter);
479 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
481 GtkWidget *main_vbox;
482 ModestMsgViewWindowPrivate *priv;
483 ModestWindowPrivate *parent_priv;
485 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
486 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
488 priv->msg_view = modest_msg_view_new (msg);
489 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
490 main_vbox = gtk_vbox_new (FALSE, 6);
493 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
494 gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar));
495 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
497 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
498 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
499 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
500 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
502 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
503 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
504 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
506 priv->find_toolbar = hildon_find_toolbar_new (NULL);
507 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
508 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
509 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
510 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
512 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);
513 gtk_widget_show_all (GTK_WIDGET(main_vbox));
517 modest_msg_view_window_disconnect_signals (ModestWindow *self)
519 ModestMsgViewWindowPrivate *priv;
520 ModestHeaderView *header_view = NULL;
521 ModestMainWindow *main_window = NULL;
522 ModestWindowMgr *window_mgr = NULL;
524 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
526 if (g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
527 priv->clipboard_change_handler))
528 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
529 priv->clipboard_change_handler);
531 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
532 priv->queue_change_handler))
533 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
534 priv->queue_change_handler);
536 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
537 priv->account_removed_handler))
538 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
539 priv->account_removed_handler);
541 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
542 priv->row_changed_handler))
543 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
544 priv->row_changed_handler);
546 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
547 priv->row_deleted_handler))
548 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
549 priv->row_deleted_handler);
551 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
552 priv->row_inserted_handler))
553 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
554 priv->row_inserted_handler);
556 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
557 priv->rows_reordered_handler))
558 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
559 priv->rows_reordered_handler);
561 window_mgr = modest_runtime_get_window_mgr();
562 g_assert(window_mgr != NULL);
564 main_window = MODEST_MAIN_WINDOW(
565 modest_window_mgr_get_main_window(window_mgr));
567 if(main_window == NULL)
570 header_view = MODEST_HEADER_VIEW(
571 modest_main_window_get_child_widget(
572 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
574 if (header_view == NULL)
577 modest_header_view_remove_observer(header_view,
578 MODEST_HEADER_VIEW_OBSERVER(self));
582 modest_msg_view_window_finalize (GObject *obj)
584 ModestMsgViewWindowPrivate *priv;
586 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
588 /* Sanity check: shouldn't be needed, the window mgr should
589 call this function before */
590 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
592 if (priv->header_model != NULL) {
593 g_object_unref (priv->header_model);
594 priv->header_model = NULL;
597 if (priv->progress_bar_timeout > 0) {
598 g_source_remove (priv->progress_bar_timeout);
599 priv->progress_bar_timeout = 0;
602 if (priv->remove_attachment_banner) {
603 gtk_widget_destroy (priv->remove_attachment_banner);
604 g_object_unref (priv->remove_attachment_banner);
605 priv->remove_attachment_banner = NULL;
608 if (priv->purge_timeout > 0) {
609 g_source_remove (priv->purge_timeout);
610 priv->purge_timeout = 0;
613 if (priv->row_reference) {
614 gtk_tree_row_reference_free (priv->row_reference);
615 priv->row_reference = NULL;
618 if (priv->next_row_reference) {
619 gtk_tree_row_reference_free (priv->next_row_reference);
620 priv->next_row_reference = NULL;
624 g_free (priv->msg_uid);
625 priv->msg_uid = NULL;
628 G_OBJECT_CLASS(parent_class)->finalize (obj);
632 select_next_valid_row (GtkTreeModel *model,
633 GtkTreeRowReference **row_reference,
636 GtkTreeIter tmp_iter;
637 GtkTreePath *path, *next;
638 gboolean retval = FALSE;
640 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
642 path = gtk_tree_row_reference_get_path (*row_reference);
643 gtk_tree_model_get_iter (model, &tmp_iter, path);
644 gtk_tree_row_reference_free (*row_reference);
645 *row_reference = NULL;
647 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
648 next = gtk_tree_model_get_path (model, &tmp_iter);
649 *row_reference = gtk_tree_row_reference_new (model, next);
651 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
652 next = gtk_tree_model_get_path (model, &tmp_iter);
654 /* Ensure that we are not selecting the same */
655 if (gtk_tree_path_compare (path, next) != 0) {
656 *row_reference = gtk_tree_row_reference_new (model, next);
662 gtk_tree_path_free (path);
667 /* TODO: This should be in _init(), with the parameters as properties. */
669 modest_msg_view_window_construct (ModestMsgViewWindow *self, TnyMsg *msg,
670 const gchar *modest_account_name,
671 const gchar *msg_uid)
674 ModestMsgViewWindowPrivate *priv = NULL;
675 ModestWindowPrivate *parent_priv = NULL;
676 ModestDimmingRulesGroup *menu_rules_group = NULL;
677 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
678 GtkActionGroup *action_group = NULL;
679 GError *error = NULL;
680 GdkPixbuf *window_icon;
682 g_return_if_fail (msg);
684 obj = G_OBJECT (self);
685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
686 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
688 priv->msg_uid = g_strdup (msg_uid);
690 parent_priv->ui_manager = gtk_ui_manager_new();
691 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
693 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
694 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
696 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
697 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
699 /* Add common actions */
700 gtk_action_group_add_actions (action_group,
701 modest_action_entries,
702 G_N_ELEMENTS (modest_action_entries),
704 gtk_action_group_add_toggle_actions (action_group,
705 modest_toggle_action_entries,
706 G_N_ELEMENTS (modest_toggle_action_entries),
708 gtk_action_group_add_toggle_actions (action_group,
709 msg_view_toggle_action_entries,
710 G_N_ELEMENTS (msg_view_toggle_action_entries),
712 gtk_action_group_add_radio_actions (action_group,
713 msg_view_zoom_action_entries,
714 G_N_ELEMENTS (msg_view_zoom_action_entries),
716 G_CALLBACK (modest_ui_actions_on_change_zoom),
719 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
720 g_object_unref (action_group);
722 /* Load the UI definition */
723 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
726 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
727 g_error_free (error);
732 /* Add common dimming rules */
733 modest_dimming_rules_group_add_rules (menu_rules_group,
734 modest_msg_view_menu_dimming_entries,
735 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
737 modest_dimming_rules_group_add_rules (toolbar_rules_group,
738 modest_msg_view_toolbar_dimming_entries,
739 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
742 /* Insert dimming rules group for this window */
743 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
744 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
745 g_object_unref (menu_rules_group);
746 g_object_unref (toolbar_rules_group);
748 /* Add accelerators */
749 gtk_window_add_accel_group (GTK_WINDOW (obj),
750 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
753 init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
754 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
756 /* Set window icon */
757 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
759 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
760 g_object_unref (window_icon);
763 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
765 g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
766 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
767 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
768 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
769 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
770 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
771 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
772 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
773 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
774 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
776 g_signal_connect (G_OBJECT (obj), "key-release-event",
777 G_CALLBACK (modest_msg_view_window_key_release_event),
780 g_signal_connect (G_OBJECT (obj), "window-state-event",
781 G_CALLBACK (modest_msg_view_window_window_state_event),
784 /* Mail Operation Queue */
785 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
787 G_CALLBACK (on_queue_changed),
790 /* Account manager */
791 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
793 G_CALLBACK(on_account_removed),
796 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
798 priv->last_search = NULL;
800 /* Init the clipboard actions dim status */
801 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
803 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
805 /* Check toolbar dimming rules */
806 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
811 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
812 const gchar *modest_account_name,
813 const gchar *msg_uid,
815 GtkTreeRowReference *row_reference)
817 ModestMsgViewWindow *window = NULL;
818 ModestMsgViewWindowPrivate *priv = NULL;
819 TnyFolder *header_folder = NULL;
820 ModestHeaderView *header_view = NULL;
821 ModestMainWindow *main_window = NULL;
822 ModestWindowMgr *window_mgr = NULL;
824 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
825 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
826 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
828 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
830 /* Remember the message list's TreeModel so we can detect changes
831 * and change the list selection when necessary: */
832 window_mgr = modest_runtime_get_window_mgr();
833 g_assert(window_mgr != NULL);
834 main_window = MODEST_MAIN_WINDOW(
835 modest_window_mgr_get_main_window(window_mgr));
836 g_assert(main_window != NULL);
837 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
838 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
839 if (header_view != NULL){
840 header_folder = modest_header_view_get_folder(header_view);
841 g_assert(header_folder != NULL);
842 priv->header_folder_id = tny_folder_get_id(header_folder);
843 g_assert(priv->header_folder_id != NULL);
844 g_object_unref(header_folder);
847 priv->header_model = g_object_ref(model);
848 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
849 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
850 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
852 priv->row_changed_handler = g_signal_connect(
853 GTK_TREE_MODEL(model), "row-changed",
854 G_CALLBACK(modest_msg_view_window_on_row_changed),
856 priv->row_deleted_handler = g_signal_connect(
857 GTK_TREE_MODEL(model), "row-deleted",
858 G_CALLBACK(modest_msg_view_window_on_row_deleted),
860 priv->row_inserted_handler = g_signal_connect (
861 GTK_TREE_MODEL(model), "row-inserted",
862 G_CALLBACK(modest_msg_view_window_on_row_inserted),
864 priv->rows_reordered_handler = g_signal_connect(
865 GTK_TREE_MODEL(model), "rows-reordered",
866 G_CALLBACK(modest_msg_view_window_on_row_reordered),
869 if (header_view != NULL){
870 modest_header_view_add_observer(header_view,
871 MODEST_HEADER_VIEW_OBSERVER(window));
874 modest_msg_view_window_update_priority (window);
876 /* Check toolbar dimming rules */
877 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
879 return MODEST_WINDOW(window);
883 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
884 const gchar *modest_account_name,
885 const gchar *msg_uid)
887 ModestMsgViewWindow *window = NULL;
888 ModestMsgViewWindowPrivate *priv = NULL;
890 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
891 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
892 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
894 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
896 /* Remember that this is a search result,
897 * so we can disable some UI appropriately: */
898 priv->is_search_result = TRUE;
900 return MODEST_WINDOW(window);
904 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
905 const gchar *modest_account_name,
906 const gchar *msg_uid)
909 g_return_val_if_fail (msg, NULL);
911 obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
912 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
913 msg, modest_account_name, msg_uid);
915 return MODEST_WINDOW(obj);
918 void modest_msg_view_window_on_row_changed(
919 GtkTreeModel *header_model,
922 ModestMsgViewWindow *window){
923 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
926 void modest_msg_view_window_on_row_deleted(
927 GtkTreeModel *header_model,
929 ModestMsgViewWindow *window){
930 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
933 /* On insertions we check if the folder still has the message we are
934 * showing or do not. If do not, we do nothing. Which means we are still
935 * not attached to any header folder and thus next/prev buttons are
936 * still dimmed. Once the message that is shown by msg-view is found, the
937 * new model of header-view will be attached and the references will be set.
938 * On each further insertions dimming rules will be checked. However
939 * this requires extra CPU time at least works.
940 * (An message might be deleted from TnyFolder and thus will not be
941 * inserted into the model again for example if it is removed by the
942 * imap server and the header view is refreshed.)
944 void modest_msg_view_window_on_row_inserted(
945 GtkTreeModel *new_model,
946 GtkTreePath *tree_path,
947 GtkTreeIter *tree_iter,
948 ModestMsgViewWindow *window){
949 ModestMsgViewWindowPrivate *priv = NULL;
950 TnyHeader *header = NULL;
953 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
955 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
957 /* If we already has a model attached then the message shown by
958 * msg-view is in it, and thus we do not need any actions but
959 * to check the dimming rules.*/
960 if(priv->header_model != NULL){
961 gtk_tree_row_reference_free(priv->next_row_reference);
962 priv->next_row_reference = gtk_tree_row_reference_copy(
963 priv->row_reference);
964 select_next_valid_row (priv->header_model,
965 &(priv->next_row_reference), FALSE);
966 modest_ui_actions_check_toolbar_dimming_rules (
967 MODEST_WINDOW (window));
971 /* Check if the newly inserted message is the same we are actually
972 * showing. IF not, we should remain detached from the header model
973 * and thus prev and next toolbarbuttons should remain dimmed. */
974 gtk_tree_model_get (new_model, tree_iter,
975 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
976 uid = modest_tny_folder_get_header_unique_id(header);
977 if(!g_str_equal(priv->msg_uid, uid)){
983 /* Setup row_reference for the actual msg. */
984 priv->row_reference = gtk_tree_row_reference_new(
985 new_model, tree_path);
986 if(priv->row_reference == NULL){
987 g_warning("No reference for msg header item.");
991 /* Attach new_model and connect some callback to it to become able
992 * to detect changes in header-view. */
993 priv->header_model = g_object_ref(new_model);
994 g_signal_connect (new_model, "row-changed",
995 G_CALLBACK (modest_msg_view_window_on_row_changed),
997 g_signal_connect (new_model, "row-deleted",
998 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1000 g_signal_connect (new_model, "rows-reordered",
1001 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1004 /* Now set up next_row_reference. */
1005 priv->next_row_reference = gtk_tree_row_reference_copy(
1006 priv->row_reference);
1007 select_next_valid_row (priv->header_model,
1008 &(priv->next_row_reference), FALSE);
1010 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1013 void modest_msg_view_window_on_row_reordered(
1014 GtkTreeModel *header_model,
1018 ModestMsgViewWindow *window){
1019 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1022 /* The modest_msg_view_window_update_model_replaced implements update
1023 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1024 * actually belongs to the header-view is the same as the TnyFolder of
1025 * the message of msg-view or not. If they are different, there is
1026 * nothing to do. If they are the same, then the model has replaced and
1027 * the reference in msg-view shall be replaced from the old model to
1028 * the new model. In this case the view will be detached from it's
1029 * header folder. From this point the next/prev buttons are dimmed.
1031 void modest_msg_view_window_update_model_replaced(
1032 ModestHeaderViewObserver *observer,
1033 GtkTreeModel *model,
1034 const gchar *tny_folder_id){
1035 ModestMsgViewWindowPrivate *priv = NULL;
1036 ModestMsgViewWindow *window = NULL;
1038 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1039 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1041 window = MODEST_MSG_VIEW_WINDOW(observer);
1042 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1044 /* If there is an other folder in the header-view then we do
1045 * not care about it's model (msg list). Else if the
1046 * header-view shows the folder the msg shown by us is in, we
1047 * shall replace our model reference and make some check. */
1048 if(tny_folder_id == NULL ||
1049 !g_str_equal(tny_folder_id, priv->header_folder_id))
1052 /* Model is changed(replaced), so we should forget the old
1053 * one. Because there might be other references and there
1054 * might be some change on the model even if we unreferenced
1055 * it, we need to disconnect our signals here. */
1056 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1057 priv->row_changed_handler))
1058 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1059 priv->row_changed_handler);
1060 priv->row_changed_handler = 0;
1061 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1062 priv->row_deleted_handler))
1063 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1064 priv->row_deleted_handler);
1065 priv->row_deleted_handler = 0;
1066 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1067 priv->row_inserted_handler))
1068 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1069 priv->row_inserted_handler);
1070 priv->row_inserted_handler = 0;
1071 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1072 priv->rows_reordered_handler))
1073 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1074 priv->rows_reordered_handler);
1075 priv->rows_reordered_handler = 0;
1076 g_object_unref(priv->header_model);
1077 priv->header_model = NULL;
1078 g_object_unref(priv->row_reference);
1079 priv->row_reference = NULL;
1080 g_object_unref(priv->next_row_reference);
1081 priv->next_row_reference = NULL;
1083 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1085 if(tny_folder_id == NULL)
1088 g_assert(model != NULL);
1090 /* Also we must connect to the new model for row insertions.
1091 * Only for insertions now. We will need other ones only after
1092 * the msg is show by msg-view is added to the new model. */
1093 priv->row_inserted_handler = g_signal_connect (
1094 model, "row-inserted",
1095 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1100 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1102 ModestMsgViewWindowPrivate *priv= NULL;
1104 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1105 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1107 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1111 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1113 ModestMsgViewWindowPrivate *priv= NULL;
1115 TnyHeader *header = NULL;
1116 GtkTreePath *path = NULL;
1119 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1120 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1122 /* If the message was not obtained from a treemodel,
1123 * for instance if it was opened directly by the search UI:
1125 if (priv->header_model == NULL) {
1126 msg = modest_msg_view_window_get_message (self);
1127 header = tny_msg_get_header (msg);
1128 g_object_unref (msg);
1132 /* Get iter of the currently selected message in the header view: */
1133 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1134 * instead of sometimes retrieving it from the header view?
1135 * Then we wouldn't be dependent on the message actually still being selected
1136 * in the header view. murrayc. */
1137 path = gtk_tree_row_reference_get_path (priv->row_reference);
1138 g_return_val_if_fail (path != NULL, NULL);
1139 gtk_tree_model_get_iter (priv->header_model,
1143 /* Get current message header */
1144 gtk_tree_model_get (priv->header_model, &iter,
1145 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1148 gtk_tree_path_free (path);
1153 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1155 ModestMsgView *msg_view;
1156 ModestMsgViewWindowPrivate *priv;
1158 g_return_val_if_fail (self, NULL);
1160 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1162 msg_view = MODEST_MSG_VIEW (priv->msg_view);
1164 return modest_msg_view_get_message (msg_view);
1168 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1170 ModestMsgViewWindowPrivate *priv;
1172 g_return_val_if_fail (self, NULL);
1174 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1176 return (const gchar*) priv->msg_uid;
1180 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1183 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1184 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1185 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1189 is_active = gtk_toggle_action_get_active (toggle);
1192 gtk_widget_show (priv->find_toolbar);
1193 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1195 gtk_widget_hide (priv->find_toolbar);
1198 /* update the toggle buttons status */
1199 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1200 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1201 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1202 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1207 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1208 ModestMsgViewWindow *obj)
1210 GtkToggleAction *toggle;
1211 ModestWindowPrivate *parent_priv;
1212 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1214 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1215 gtk_toggle_action_set_active (toggle, FALSE);
1219 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1220 ModestMsgViewWindow *obj)
1222 gchar *current_search;
1223 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1225 if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
1226 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1230 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1232 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1233 g_free (current_search);
1234 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1238 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1240 g_free (priv->last_search);
1241 priv->last_search = g_strdup (current_search);
1242 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
1245 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1246 g_free (priv->last_search);
1247 priv->last_search = NULL;
1250 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
1251 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1252 g_free (priv->last_search);
1253 priv->last_search = NULL;
1257 g_free (current_search);
1262 modest_msg_view_window_set_zoom (ModestWindow *window,
1265 ModestMsgViewWindowPrivate *priv;
1267 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1269 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1270 modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
1274 modest_msg_view_window_get_zoom (ModestWindow *window)
1276 ModestMsgViewWindowPrivate *priv;
1278 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1280 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1281 return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
1285 modest_msg_view_window_zoom_plus (ModestWindow *window)
1287 ModestWindowPrivate *parent_priv;
1288 GtkRadioAction *zoom_radio_action;
1289 GSList *group, *node;
1291 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1292 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1293 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1295 group = gtk_radio_action_get_group (zoom_radio_action);
1297 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1298 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1302 for (node = group; node != NULL; node = g_slist_next (node)) {
1303 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1304 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1312 modest_msg_view_window_zoom_minus (ModestWindow *window)
1314 ModestWindowPrivate *parent_priv;
1315 GtkRadioAction *zoom_radio_action;
1316 GSList *group, *node;
1318 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1319 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1320 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1322 group = gtk_radio_action_get_group (zoom_radio_action);
1324 for (node = group; node != NULL; node = g_slist_next (node)) {
1325 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1326 if (node->next != NULL) {
1327 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1330 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1340 modest_msg_view_window_key_release_event (GtkWidget *window,
1344 if (event->type == GDK_KEY_RELEASE) {
1345 switch (event->keyval) {
1347 modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1351 modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1364 modest_msg_view_window_scroll_up (ModestWindow *window)
1366 ModestMsgViewWindowPrivate *priv;
1367 gboolean return_value;
1369 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1370 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1374 modest_msg_view_window_scroll_down (ModestWindow *window)
1376 ModestMsgViewWindowPrivate *priv;
1377 gboolean return_value;
1379 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1380 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1384 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1387 ModestMsgViewWindowPrivate *priv;
1388 GtkTreeIter tmp_iter;
1389 gboolean is_last_selected;
1391 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1394 /*if no model (so no rows at all), then virtually we are the last*/
1395 if (!priv->header_model)
1398 path = gtk_tree_row_reference_get_path (priv->row_reference);
1402 is_last_selected = TRUE;
1403 while (is_last_selected) {
1405 gtk_tree_path_next (path);
1406 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1408 gtk_tree_model_get (priv->header_model, &tmp_iter,
1409 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1411 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1412 is_last_selected = FALSE;
1414 gtk_tree_path_free (path);
1415 return is_last_selected;
1419 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1421 ModestMsgViewWindowPrivate *priv;
1423 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1424 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1426 return priv->header_model != NULL;
1430 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1432 ModestMsgViewWindowPrivate *priv;
1434 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1435 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1437 return priv->is_search_result;
1441 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1444 ModestMsgViewWindowPrivate *priv;
1445 gboolean is_first_selected;
1446 GtkTreeIter tmp_iter;
1447 /* gchar * path_string;*/
1449 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1450 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1452 /*if no model (so no rows at all), then virtually we are the first*/
1453 if (!priv->header_model)
1456 path = gtk_tree_row_reference_get_path (priv->row_reference);
1460 /* path_string = gtk_tree_path_to_string (path);
1461 is_first_selected = strcmp (path_string, "0");
1463 g_free (path_string);
1464 gtk_tree_path_free (path);
1466 return is_first_selected;*/
1468 is_first_selected = TRUE;
1469 while (is_first_selected) {
1471 if(!gtk_tree_path_prev (path))
1473 /* Here the 'if' is needless for logic, but let make sure
1474 * iter is valid for gtk_tree_model_get. */
1475 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1477 gtk_tree_model_get (priv->header_model, &tmp_iter,
1478 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1480 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1481 is_first_selected = FALSE;
1483 gtk_tree_path_free (path);
1484 return is_first_selected;
1488 * Reads the message whose summary item is @header. It takes care of
1489 * several things, among others:
1491 * If the message was not previously downloaded then ask the user
1492 * before downloading. If there is no connection launch the connection
1493 * dialog. Update toolbar dimming rules.
1495 * Returns: TRUE if the mail operation was started, otherwise if the
1496 * user do not want to download the message, or if the user do not
1497 * want to connect, then the operation is not issued
1500 message_reader (ModestMsgViewWindow *window,
1501 ModestMsgViewWindowPrivate *priv,
1505 ModestMailOperation *mail_op = NULL;
1506 ModestMailOperationTypeOperation op_type;
1507 gboolean already_showing = FALSE;
1508 ModestWindow *msg_window = NULL;
1509 ModestWindowMgr *mgr;
1511 g_return_val_if_fail (path != NULL, FALSE);
1513 mgr = modest_runtime_get_window_mgr ();
1514 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1515 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1518 gtk_window_present (GTK_WINDOW (msg_window));
1519 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1523 /* Msg download completed */
1524 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1525 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1528 GtkResponseType response;
1530 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1532 /* Ask the user if he wants to download the message */
1533 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1534 _("mcen_nc_get_msg"));
1535 if (response == GTK_RESPONSE_CANCEL)
1538 /* Offer the connection dialog if necessary */
1539 /* FIXME: should this stuff go directly to the mail
1540 operation instead of spread it all over the
1542 folder = tny_header_get_folder (header);
1544 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1545 g_object_unref (folder);
1548 g_object_unref (folder);
1552 /* New mail operation */
1553 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1555 modest_ui_actions_get_msgs_full_error_handler,
1558 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1559 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1560 g_object_unref (mail_op);
1562 /* Update toolbar dimming rules */
1563 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1569 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1571 ModestMsgViewWindowPrivate *priv;
1572 GtkTreePath *path= NULL;
1573 GtkTreeIter tmp_iter;
1575 gboolean retval = TRUE;
1577 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1578 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1580 /* Update the next row reference if it's not valid. This could
1581 happen if for example the header which it was pointing to,
1582 was deleted. The best place to do it is in the row-deleted
1583 handler but the tinymail model do not work like the glib
1584 tree models and reports the deletion when the row is still
1586 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1587 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1588 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1589 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1592 if (priv->next_row_reference)
1593 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1597 gtk_tree_model_get_iter (priv->header_model,
1601 gtk_tree_model_get (priv->header_model, &tmp_iter,
1602 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1605 /* Read the message & show it */
1606 if (!message_reader (window, priv, header, path)) {
1608 gtk_tree_path_free (path);
1612 g_object_unref (header);
1618 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1620 ModestMsgViewWindowPrivate *priv = NULL;
1621 TnyHeader *header = NULL;
1625 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1626 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1628 /* Check that the model is not empty */
1629 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1632 /* Get the header */
1633 gtk_tree_model_get (priv->header_model,
1635 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1637 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1638 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1639 g_object_unref (header);
1640 return modest_msg_view_window_select_next_message (self);
1643 path = gtk_tree_model_get_path (priv->header_model, &iter);
1645 /* Read the message & show it */
1646 message_reader (self, priv, header, path);
1649 g_object_unref (header);
1655 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1657 ModestMsgViewWindowPrivate *priv = NULL;
1660 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1661 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1663 /* Return inmediatly if there is no header model */
1664 if (!priv->header_model)
1667 path = gtk_tree_row_reference_get_path (priv->row_reference);
1668 while (gtk_tree_path_prev (path)) {
1672 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1673 gtk_tree_model_get (priv->header_model, &iter,
1674 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1678 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1679 g_object_unref (header);
1683 /* Read the message & show it */
1684 if (!message_reader (window, priv, header, path)) {
1685 g_object_unref (header);
1689 g_object_unref (header);
1694 gtk_tree_path_free (path);
1699 view_msg_cb (ModestMailOperation *mail_op,
1704 ModestMsgViewWindow *self = NULL;
1705 ModestMsgViewWindowPrivate *priv = NULL;
1708 /* If there was any error */
1709 path = (GtkTreePath *) user_data;
1710 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1711 gtk_tree_path_free (path);
1715 /* Get the window */
1716 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1717 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1720 /* Update the row reference */
1721 gtk_tree_row_reference_free (priv->row_reference);
1722 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1723 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1724 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1725 gtk_tree_path_free (path);
1727 /* Mark header as read */
1728 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1729 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1731 /* Set new message */
1732 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1733 modest_msg_view_window_update_priority (self);
1734 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1735 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1737 /* Set the new message uid of the window */
1738 if (priv->msg_uid) {
1739 g_free (priv->msg_uid);
1740 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1743 /* Notify the observers */
1744 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1745 0, priv->header_model, priv->row_reference);
1747 /* Free new references */
1748 g_object_unref (self);
1752 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1754 ModestMsgViewWindowPrivate *priv;
1756 TnyFolderType folder_type;
1758 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1760 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1762 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1766 folder = tny_msg_get_folder (msg);
1769 folder_type = tny_folder_get_folder_type (folder);
1771 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1772 const gchar *fname = tny_folder_get_name (folder);
1773 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1776 g_object_unref (folder);
1778 g_object_unref (msg);
1786 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1788 ModestMsgViewWindowPrivate *priv;
1789 TnyHeaderFlags flags = 0;
1791 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1793 if (priv->header_model) {
1796 GtkTreePath *path = NULL;
1798 path = gtk_tree_row_reference_get_path (priv->row_reference);
1799 g_return_if_fail (path != NULL);
1800 gtk_tree_model_get_iter (priv->header_model,
1802 gtk_tree_row_reference_get_path (priv->row_reference));
1804 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1806 flags = tny_header_get_flags (header);
1807 gtk_tree_path_free (path);
1810 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1815 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1817 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1818 ModestWindowPrivate *parent_priv;
1819 ModestWindowMgr *mgr;
1820 gboolean is_fullscreen;
1821 GtkAction *fs_toggle_action;
1824 mgr = modest_runtime_get_window_mgr ();
1825 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1827 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1829 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1830 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1831 if (is_fullscreen != active) {
1832 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1841 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
1843 ModestWindowPrivate *parent_priv;
1844 GtkAction *fs_toggle_action;
1845 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1847 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1848 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
1849 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
1853 set_homogeneous (GtkWidget *widget,
1856 if (GTK_IS_TOOL_ITEM (widget)) {
1857 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1858 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1863 modest_msg_view_window_show_toolbar (ModestWindow *self,
1864 gboolean show_toolbar)
1866 ModestMsgViewWindowPrivate *priv = NULL;
1867 ModestWindowPrivate *parent_priv;
1868 GtkWidget *reply_button = NULL, *menu = NULL;
1869 GtkWidget *placeholder = NULL;
1872 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1873 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1875 /* Set optimized view status */
1876 priv->optimized_view = !show_toolbar;
1878 if (!parent_priv->toolbar) {
1879 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1882 /* Set homogeneous toolbar */
1883 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1884 set_homogeneous, NULL);
1886 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1887 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1888 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1889 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1890 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1891 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1892 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1893 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1895 /* Add ProgressBar (Transfer toolbar) */
1896 priv->progress_bar = modest_progress_bar_widget_new ();
1897 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1898 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1899 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1900 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1901 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1903 /* Connect cancel 'clicked' signal to abort progress mode */
1904 g_signal_connect(priv->cancel_toolitem, "clicked",
1905 G_CALLBACK(cancel_progressbar),
1908 /* Add it to the observers list */
1909 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1912 hildon_window_add_toolbar (HILDON_WINDOW (self),
1913 GTK_TOOLBAR (parent_priv->toolbar));
1916 /* Set reply button tap and hold menu */
1917 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1918 "/ToolBar/ToolbarMessageReply");
1919 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1920 "/ToolbarReplyCSM");
1921 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1925 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1926 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1927 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1929 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1930 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1933 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1934 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1939 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1941 ModestMsgViewWindow *window)
1943 ModestWindowPrivate *parent_priv;
1944 /* GtkAction *action; */
1945 gboolean is_address;
1949 if (!GTK_WIDGET_VISIBLE (window))
1952 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1953 selection = gtk_clipboard_wait_for_text (clipboard);
1955 is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1957 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1958 /* gtk_action_set_sensitive (action, is_address); */
1960 focused = gtk_window_get_focus (GTK_WINDOW (window));
1962 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1963 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1965 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1966 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1969 /* modest_msg_view_window_update_dimmed (window); */
1974 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1976 ModestMsgViewWindowPrivate *priv;
1978 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1979 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1981 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1985 cancel_progressbar (GtkToolButton *toolbutton,
1986 ModestMsgViewWindow *self)
1989 ModestMsgViewWindowPrivate *priv;
1991 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1993 /* Get operation observers and cancel its current operation */
1994 tmp = priv->progress_widgets;
1996 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
1997 tmp=g_slist_next(tmp);
2001 observers_empty (ModestMsgViewWindow *self)
2004 ModestMsgViewWindowPrivate *priv;
2005 gboolean is_empty = TRUE;
2006 guint pending_ops = 0;
2008 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2009 tmp = priv->progress_widgets;
2011 /* Check all observers */
2012 while (tmp && is_empty) {
2013 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2014 is_empty = pending_ops == 0;
2016 tmp = g_slist_next(tmp);
2023 on_account_removed (TnyAccountStore *account_store,
2024 TnyAccount *account,
2027 /* Do nothing if it's a transport account, because we only
2028 show the messages of a store account */
2029 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2030 const gchar *parent_acc = NULL;
2031 const gchar *our_acc = NULL;
2033 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2034 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2036 /* Close this window if I'm showing a message of the removed account */
2037 if (strcmp (parent_acc, our_acc) == 0)
2038 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2043 on_queue_changed (ModestMailOperationQueue *queue,
2044 ModestMailOperation *mail_op,
2045 ModestMailOperationQueueNotification type,
2046 ModestMsgViewWindow *self)
2049 ModestMsgViewWindowPrivate *priv;
2050 ModestMailOperationTypeOperation op_type;
2051 ModestToolBarModes mode;
2053 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2054 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2056 /* If this operations was created by another window, do nothing */
2057 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2060 /* Get toolbar mode from operation id*/
2061 op_type = modest_mail_operation_get_type_operation (mail_op);
2063 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
2064 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2065 case MODEST_MAIL_OPERATION_TYPE_OPEN:
2066 mode = TOOLBAR_MODE_TRANSFER;
2069 mode = TOOLBAR_MODE_NORMAL;
2073 /* Add operation observers and change toolbar if neccessary*/
2074 tmp = priv->progress_widgets;
2076 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2077 if (mode == TOOLBAR_MODE_TRANSFER) {
2078 /* Enable transfer toolbar mode */
2079 set_toolbar_transfer_mode(self);
2081 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2083 tmp = g_slist_next (tmp);
2088 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2089 if (mode == TOOLBAR_MODE_TRANSFER) {
2091 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2093 tmp = g_slist_next (tmp);
2097 /* If no more operations are being observed, NORMAL mode is enabled again */
2098 if (observers_empty (self)) {
2099 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2107 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2109 ModestMsgViewWindowPrivate *priv;
2110 GList *selected_attachments = NULL;
2112 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2113 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2115 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2117 return selected_attachments;
2121 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2123 ModestMsgViewWindowPrivate *priv;
2124 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2125 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2127 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2129 if (mime_part == NULL) {
2130 gboolean error = FALSE;
2131 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2132 if (selected_attachments == NULL) {
2134 } else if (g_list_length (selected_attachments) > 1) {
2135 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2138 mime_part = (TnyMimePart *) selected_attachments->data;
2139 g_object_ref (mime_part);
2141 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2142 g_list_free (selected_attachments);
2147 g_object_ref (mime_part);
2150 if (tny_mime_part_is_purged (mime_part)) {
2151 g_object_unref (mime_part);
2155 if (!TNY_IS_MSG (mime_part)) {
2156 gchar *filepath = NULL;
2157 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2158 gchar *extension = NULL;
2159 TnyFsStream *temp_stream = NULL;
2162 extension = g_strrstr (att_filename, ".");
2163 if (extension != NULL)
2167 temp_stream = modest_maemo_utils_create_temp_stream (extension, &filepath);
2170 const gchar *content_type;
2171 content_type = tny_mime_part_get_content_type (mime_part);
2172 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2174 modest_platform_activate_file (filepath, content_type);
2175 g_object_unref (temp_stream);
2177 /* TODO: delete temporary file */
2180 /* message attachment */
2181 TnyHeader *header = NULL;
2182 ModestWindowMgr *mgr;
2183 ModestWindow *msg_win = NULL;
2186 header = tny_msg_get_header (TNY_MSG (mime_part));
2187 mgr = modest_runtime_get_window_mgr ();
2188 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2191 if (msg_win) /* there is already a window for this uid; top it */
2192 gtk_window_present (GTK_WINDOW(msg_win));
2194 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2195 * thus, we don't do anything */
2196 g_warning ("window for is already being created");
2198 /* it's not found, so create a new window for it */
2199 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
2200 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2202 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2203 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
2204 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2205 modest_window_get_zoom (MODEST_WINDOW (window)));
2206 modest_window_mgr_register_window (mgr, msg_win);
2207 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2208 gtk_widget_show_all (GTK_WIDGET (msg_win));
2211 g_object_unref (mime_part);
2227 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2228 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2229 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2230 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2233 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2237 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2238 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2239 g_free (pair->filename);
2240 g_object_unref (pair->part);
2241 g_slice_free (SaveMimePartPair, pair);
2243 g_list_free (info->pairs);
2246 gtk_widget_destroy (info->banner);
2247 g_object_unref (info->banner);
2248 g_slice_free (SaveMimePartInfo, info);
2253 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2255 if (info->pairs != NULL) {
2256 /* This is a GDK lock because we are an idle callback and
2257 * save_mime_parts_to_file_with_checks can contain Gtk+ code */
2259 gdk_threads_enter (); /* CHECKED */
2260 save_mime_parts_to_file_with_checks (info);
2261 gdk_threads_leave (); /* CHECKED */
2264 result = info->result;
2266 /* This is a GDK lock because we are an idle callback and
2267 * hildon_banner_show_information is or does Gtk+ code */
2269 gdk_threads_enter (); /* CHECKED */
2270 save_mime_part_info_free (info, TRUE);
2272 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2274 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2276 gdk_threads_leave (); /* CHECKED */
2283 save_mime_part_to_file (SaveMimePartInfo *info)
2285 GnomeVFSResult result;
2286 GnomeVFSHandle *handle;
2288 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2290 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2291 if (result == GNOME_VFS_OK) {
2292 stream = tny_vfs_stream_new (handle);
2293 tny_mime_part_decode_to_stream (pair->part, stream);
2294 g_object_unref (G_OBJECT (stream));
2295 g_object_unref (pair->part);
2296 g_slice_free (SaveMimePartPair, pair);
2297 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2298 info->result = TRUE;
2300 save_mime_part_info_free (info, FALSE);
2301 info->result = FALSE;
2304 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2309 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2311 SaveMimePartPair *pair;
2312 gboolean is_ok = TRUE;
2314 pair = info->pairs->data;
2315 if (modest_maemo_utils_file_exists (pair->filename)) {
2316 GtkWidget *confirm_overwrite_dialog;
2317 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2318 _FM("docm_nc_replace_file"));
2319 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2322 gtk_widget_destroy (confirm_overwrite_dialog);
2326 save_mime_part_info_free (info, TRUE);
2328 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2335 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2337 gboolean clean_list = FALSE;
2338 ModestMsgViewWindowPrivate *priv;
2339 GList *files_to_save = NULL;
2340 GtkWidget *save_dialog = NULL;
2341 gchar *folder = NULL;
2342 gboolean canceled = FALSE;
2343 const gchar *filename = NULL;
2344 gchar *save_multiple_str = NULL;
2346 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2347 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2349 if (mime_parts == NULL) {
2350 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2351 if (mime_parts == NULL)
2356 /* prepare dialog */
2357 if (mime_parts->next == NULL) {
2358 /* only one attachment selected */
2359 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2360 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2361 filename = tny_mime_part_get_filename (mime_part);
2363 g_warning ("Tried to save a non-file attachment");
2367 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2368 g_list_length (mime_parts));
2371 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2372 GTK_FILE_CHOOSER_ACTION_SAVE);
2375 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2376 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2380 if (filename != NULL)
2381 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2384 /* if multiple, set multiple string */
2385 if (save_multiple_str) {
2386 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2390 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2391 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2393 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2394 hildon_banner_show_information
2395 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2399 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2400 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2402 if (tny_mime_part_is_attachment (mime_part)) {
2403 SaveMimePartPair *pair;
2405 if ((mime_parts->next != NULL) &&
2406 (tny_mime_part_get_filename (mime_part) == NULL))
2409 pair = g_slice_new0 (SaveMimePartPair);
2410 if (mime_parts->next == NULL) {
2411 pair->filename = g_strdup (chooser_uri);
2414 g_build_filename (chooser_uri,
2415 tny_mime_part_get_filename (mime_part), NULL);
2417 pair->part = g_object_ref (mime_part);
2418 files_to_save = g_list_prepend (files_to_save, pair);
2422 g_free (chooser_uri);
2425 gtk_widget_destroy (save_dialog);
2428 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2429 g_list_free (mime_parts);
2432 if (files_to_save != NULL) {
2433 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2434 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2435 _CS("sfil_ib_saving"));
2436 info->pairs = files_to_save;
2437 info->banner = banner;
2438 info->result = TRUE;
2439 g_object_ref (banner);
2440 save_mime_parts_to_file_with_checks (info);
2445 show_remove_attachment_information (gpointer userdata)
2447 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2448 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2450 if (priv->remove_attachment_banner != NULL) {
2451 gtk_widget_destroy (priv->remove_attachment_banner);
2452 g_object_unref (priv->remove_attachment_banner);
2455 priv->remove_attachment_banner = g_object_ref (
2456 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2462 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2464 ModestMsgViewWindowPrivate *priv;
2465 GList *mime_parts = NULL, *node;
2466 gchar *confirmation_message;
2470 /* TnyFolder *folder; */
2472 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2473 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2476 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2478 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2480 /* Remove already purged messages from mime parts list */
2482 while (node != NULL) {
2483 TnyMimePart *part = TNY_MIME_PART (node->data);
2484 if (tny_mime_part_is_purged (part)) {
2485 GList *deleted_node = node;
2486 node = g_list_next (node);
2487 g_object_unref (part);
2488 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2490 node = g_list_next (node);
2494 if (mime_parts == NULL)
2497 n_attachments = g_list_length (mime_parts);
2498 if (n_attachments == 1) {
2499 const gchar *filename;
2501 if (TNY_IS_MSG (mime_parts->data)) {
2503 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2504 filename = tny_header_get_subject (header);
2505 g_object_unref (header);
2506 if (filename == NULL)
2507 filename = _("mail_va_no_subject");
2509 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2511 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2513 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2514 "mcen_nc_purge_files_text",
2515 n_attachments), n_attachments);
2517 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2518 confirmation_message);
2519 g_free (confirmation_message);
2521 if (response != GTK_RESPONSE_OK)
2524 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2525 /* folder = tny_msg_get_folder (msg); */
2526 /* tny_msg_uncache_attachments (msg); */
2527 /* tny_folder_refresh (folder, NULL); */
2528 /* g_object_unref (folder); */
2530 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2531 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2532 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2535 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2536 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2537 tny_msg_rewrite_cache (msg);
2538 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2540 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2541 g_list_free (mime_parts);
2543 if (priv->purge_timeout > 0) {
2544 g_source_remove (priv->purge_timeout);
2545 priv->purge_timeout = 0;
2548 if (priv->remove_attachment_banner) {
2549 gtk_widget_destroy (priv->remove_attachment_banner);
2550 g_object_unref (priv->remove_attachment_banner);
2551 priv->remove_attachment_banner = NULL;
2559 update_window_title (ModestMsgViewWindow *window)
2561 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2563 TnyHeader *header = NULL;
2564 const gchar *subject = NULL;
2566 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2568 header = tny_msg_get_header (msg);
2569 subject = tny_header_get_subject (header);
2570 g_object_unref (msg);
2573 if ((subject == NULL)||(subject[0] == '\0'))
2574 subject = _("mail_va_no_subject");
2576 gtk_window_set_title (GTK_WINDOW (window), subject);