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;
1527 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1529 /* Ask the user if he wants to download the message if
1531 if (!tny_device_is_online (modest_runtime_get_device())) {
1532 TnyFolder *folder = NULL;
1533 GtkResponseType response;
1535 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1536 _("mcen_nc_get_msg"));
1537 if (response == GTK_RESPONSE_CANCEL)
1540 /* Offer the connection dialog if necessary */
1541 folder = tny_header_get_folder (header);
1543 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1544 TNY_FOLDER_STORE (folder))) {
1545 g_object_unref (folder);
1548 g_object_unref (folder);
1553 /* New mail operation */
1554 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1556 modest_ui_actions_get_msgs_full_error_handler,
1559 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1560 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1561 g_object_unref (mail_op);
1563 /* Update toolbar dimming rules */
1564 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1570 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1572 ModestMsgViewWindowPrivate *priv;
1573 GtkTreePath *path= NULL;
1574 GtkTreeIter tmp_iter;
1576 gboolean retval = TRUE;
1578 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1581 /* Update the next row reference if it's not valid. This could
1582 happen if for example the header which it was pointing to,
1583 was deleted. The best place to do it is in the row-deleted
1584 handler but the tinymail model do not work like the glib
1585 tree models and reports the deletion when the row is still
1587 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1588 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1589 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1590 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1593 if (priv->next_row_reference)
1594 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1598 gtk_tree_model_get_iter (priv->header_model,
1602 gtk_tree_model_get (priv->header_model, &tmp_iter,
1603 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1606 /* Read the message & show it */
1607 if (!message_reader (window, priv, header, path)) {
1609 gtk_tree_path_free (path);
1613 g_object_unref (header);
1619 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1621 ModestMsgViewWindowPrivate *priv = NULL;
1622 TnyHeader *header = NULL;
1626 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1627 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1629 /* Check that the model is not empty */
1630 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1633 /* Get the header */
1634 gtk_tree_model_get (priv->header_model,
1636 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1638 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1639 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1640 g_object_unref (header);
1641 return modest_msg_view_window_select_next_message (self);
1644 path = gtk_tree_model_get_path (priv->header_model, &iter);
1646 /* Read the message & show it */
1647 message_reader (self, priv, header, path);
1650 g_object_unref (header);
1656 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1658 ModestMsgViewWindowPrivate *priv = NULL;
1661 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1662 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1664 /* Return inmediatly if there is no header model */
1665 if (!priv->header_model)
1668 path = gtk_tree_row_reference_get_path (priv->row_reference);
1669 while (gtk_tree_path_prev (path)) {
1673 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1674 gtk_tree_model_get (priv->header_model, &iter,
1675 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1679 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1680 g_object_unref (header);
1684 /* Read the message & show it */
1685 if (!message_reader (window, priv, header, path)) {
1686 g_object_unref (header);
1690 g_object_unref (header);
1695 gtk_tree_path_free (path);
1700 view_msg_cb (ModestMailOperation *mail_op,
1705 ModestMsgViewWindow *self = NULL;
1706 ModestMsgViewWindowPrivate *priv = NULL;
1709 /* If there was any error */
1710 path = (GtkTreePath *) user_data;
1711 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1712 gtk_tree_path_free (path);
1716 /* Get the window */
1717 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1718 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1719 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1721 /* Update the row reference */
1722 gtk_tree_row_reference_free (priv->row_reference);
1723 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1724 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1725 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1726 gtk_tree_path_free (path);
1728 /* Mark header as read */
1729 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1730 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1732 /* Set new message */
1733 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1734 modest_msg_view_window_update_priority (self);
1735 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1736 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1738 /* Set the new message uid of the window */
1739 if (priv->msg_uid) {
1740 g_free (priv->msg_uid);
1741 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1744 /* Notify the observers */
1745 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1746 0, priv->header_model, priv->row_reference);
1748 /* Free new references */
1749 g_object_unref (self);
1753 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1755 ModestMsgViewWindowPrivate *priv;
1757 TnyFolderType folder_type;
1759 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1761 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1763 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1767 folder = tny_msg_get_folder (msg);
1770 folder_type = tny_folder_get_folder_type (folder);
1772 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1773 const gchar *fname = tny_folder_get_name (folder);
1774 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1777 g_object_unref (folder);
1779 g_object_unref (msg);
1787 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1789 ModestMsgViewWindowPrivate *priv;
1790 TnyHeaderFlags flags = 0;
1792 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1794 if (priv->header_model) {
1797 GtkTreePath *path = NULL;
1799 path = gtk_tree_row_reference_get_path (priv->row_reference);
1800 g_return_if_fail (path != NULL);
1801 gtk_tree_model_get_iter (priv->header_model,
1803 gtk_tree_row_reference_get_path (priv->row_reference));
1805 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1807 flags = tny_header_get_flags (header);
1808 gtk_tree_path_free (path);
1811 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1816 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1818 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1819 ModestWindowPrivate *parent_priv;
1820 ModestWindowMgr *mgr;
1821 gboolean is_fullscreen;
1822 GtkAction *fs_toggle_action;
1825 mgr = modest_runtime_get_window_mgr ();
1826 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1828 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1830 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1831 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1832 if (is_fullscreen != active) {
1833 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1842 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
1844 ModestWindowPrivate *parent_priv;
1845 GtkAction *fs_toggle_action;
1846 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1848 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1849 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
1850 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
1854 set_homogeneous (GtkWidget *widget,
1857 if (GTK_IS_TOOL_ITEM (widget)) {
1858 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1859 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1864 modest_msg_view_window_show_toolbar (ModestWindow *self,
1865 gboolean show_toolbar)
1867 ModestMsgViewWindowPrivate *priv = NULL;
1868 ModestWindowPrivate *parent_priv;
1869 GtkWidget *reply_button = NULL, *menu = NULL;
1870 GtkWidget *placeholder = NULL;
1873 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1874 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1876 /* Set optimized view status */
1877 priv->optimized_view = !show_toolbar;
1879 if (!parent_priv->toolbar) {
1880 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1883 /* Set homogeneous toolbar */
1884 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1885 set_homogeneous, NULL);
1887 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1888 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1889 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1890 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1891 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1892 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1893 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1894 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1896 /* Add ProgressBar (Transfer toolbar) */
1897 priv->progress_bar = modest_progress_bar_widget_new ();
1898 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1899 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1900 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1901 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1902 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1904 /* Connect cancel 'clicked' signal to abort progress mode */
1905 g_signal_connect(priv->cancel_toolitem, "clicked",
1906 G_CALLBACK(cancel_progressbar),
1909 /* Add it to the observers list */
1910 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1913 hildon_window_add_toolbar (HILDON_WINDOW (self),
1914 GTK_TOOLBAR (parent_priv->toolbar));
1917 /* Set reply button tap and hold menu */
1918 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1919 "/ToolBar/ToolbarMessageReply");
1920 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1921 "/ToolbarReplyCSM");
1922 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1926 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1927 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1928 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1930 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1931 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1934 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1935 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1940 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1942 ModestMsgViewWindow *window)
1944 ModestWindowPrivate *parent_priv;
1945 /* GtkAction *action; */
1946 gboolean is_address;
1950 if (!GTK_WIDGET_VISIBLE (window))
1953 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1954 selection = gtk_clipboard_wait_for_text (clipboard);
1956 is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1958 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1959 /* gtk_action_set_sensitive (action, is_address); */
1961 focused = gtk_window_get_focus (GTK_WINDOW (window));
1963 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1964 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1966 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1967 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1970 /* modest_msg_view_window_update_dimmed (window); */
1975 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1977 ModestMsgViewWindowPrivate *priv;
1979 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1980 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1982 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1986 cancel_progressbar (GtkToolButton *toolbutton,
1987 ModestMsgViewWindow *self)
1990 ModestMsgViewWindowPrivate *priv;
1992 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1994 /* Get operation observers and cancel its current operation */
1995 tmp = priv->progress_widgets;
1997 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
1998 tmp=g_slist_next(tmp);
2002 observers_empty (ModestMsgViewWindow *self)
2005 ModestMsgViewWindowPrivate *priv;
2006 gboolean is_empty = TRUE;
2007 guint pending_ops = 0;
2009 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2010 tmp = priv->progress_widgets;
2012 /* Check all observers */
2013 while (tmp && is_empty) {
2014 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2015 is_empty = pending_ops == 0;
2017 tmp = g_slist_next(tmp);
2024 on_account_removed (TnyAccountStore *account_store,
2025 TnyAccount *account,
2028 /* Do nothing if it's a transport account, because we only
2029 show the messages of a store account */
2030 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2031 const gchar *parent_acc = NULL;
2032 const gchar *our_acc = NULL;
2034 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2035 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2037 /* Close this window if I'm showing a message of the removed account */
2038 if (strcmp (parent_acc, our_acc) == 0)
2039 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2044 on_queue_changed (ModestMailOperationQueue *queue,
2045 ModestMailOperation *mail_op,
2046 ModestMailOperationQueueNotification type,
2047 ModestMsgViewWindow *self)
2050 ModestMsgViewWindowPrivate *priv;
2051 ModestMailOperationTypeOperation op_type;
2052 ModestToolBarModes mode;
2054 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2057 /* If this operations was created by another window, do nothing */
2058 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2061 /* Get toolbar mode from operation id*/
2062 op_type = modest_mail_operation_get_type_operation (mail_op);
2064 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
2065 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2066 case MODEST_MAIL_OPERATION_TYPE_OPEN:
2067 mode = TOOLBAR_MODE_TRANSFER;
2070 mode = TOOLBAR_MODE_NORMAL;
2074 /* Add operation observers and change toolbar if neccessary*/
2075 tmp = priv->progress_widgets;
2077 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2078 if (mode == TOOLBAR_MODE_TRANSFER) {
2079 /* Enable transfer toolbar mode */
2080 set_toolbar_transfer_mode(self);
2082 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2084 tmp = g_slist_next (tmp);
2089 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2090 if (mode == TOOLBAR_MODE_TRANSFER) {
2092 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2094 tmp = g_slist_next (tmp);
2098 /* If no more operations are being observed, NORMAL mode is enabled again */
2099 if (observers_empty (self)) {
2100 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2108 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2110 ModestMsgViewWindowPrivate *priv;
2111 GList *selected_attachments = NULL;
2113 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2116 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2118 return selected_attachments;
2122 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2124 ModestMsgViewWindowPrivate *priv;
2125 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2126 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2128 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2130 if (mime_part == NULL) {
2131 gboolean error = FALSE;
2132 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2133 if (selected_attachments == NULL) {
2135 } else if (g_list_length (selected_attachments) > 1) {
2136 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2139 mime_part = (TnyMimePart *) selected_attachments->data;
2140 g_object_ref (mime_part);
2142 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2143 g_list_free (selected_attachments);
2148 g_object_ref (mime_part);
2151 if (tny_mime_part_is_purged (mime_part)) {
2152 g_object_unref (mime_part);
2156 if (!TNY_IS_MSG (mime_part)) {
2157 gchar *filepath = NULL;
2158 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2159 gchar *extension = NULL;
2160 TnyFsStream *temp_stream = NULL;
2163 extension = g_strrstr (att_filename, ".");
2164 if (extension != NULL)
2168 temp_stream = modest_maemo_utils_create_temp_stream (extension, &filepath);
2171 const gchar *content_type;
2172 content_type = tny_mime_part_get_content_type (mime_part);
2173 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2175 modest_platform_activate_file (filepath, content_type);
2176 g_object_unref (temp_stream);
2178 /* TODO: delete temporary file */
2181 /* message attachment */
2182 TnyHeader *header = NULL;
2183 ModestWindowMgr *mgr;
2184 ModestWindow *msg_win = NULL;
2187 header = tny_msg_get_header (TNY_MSG (mime_part));
2188 mgr = modest_runtime_get_window_mgr ();
2189 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2192 if (msg_win) /* there is already a window for this uid; top it */
2193 gtk_window_present (GTK_WINDOW(msg_win));
2195 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2196 * thus, we don't do anything */
2197 g_warning ("window for is already being created");
2199 /* it's not found, so create a new window for it */
2200 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
2201 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2203 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2204 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
2205 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2206 modest_window_get_zoom (MODEST_WINDOW (window)));
2207 modest_window_mgr_register_window (mgr, msg_win);
2208 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2209 gtk_widget_show_all (GTK_WIDGET (msg_win));
2212 g_object_unref (mime_part);
2228 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2229 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2230 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2231 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2234 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2238 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2239 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2240 g_free (pair->filename);
2241 g_object_unref (pair->part);
2242 g_slice_free (SaveMimePartPair, pair);
2244 g_list_free (info->pairs);
2247 gtk_widget_destroy (info->banner);
2248 g_object_unref (info->banner);
2249 g_slice_free (SaveMimePartInfo, info);
2254 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2256 if (info->pairs != NULL) {
2257 save_mime_part_to_file (info);
2260 result = info->result;
2262 /* This is a GDK lock because we are an idle callback and
2263 * hildon_banner_show_information is or does Gtk+ code */
2265 gdk_threads_enter (); /* CHECKED */
2266 save_mime_part_info_free (info, TRUE);
2268 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2270 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2272 gdk_threads_leave (); /* CHECKED */
2279 save_mime_part_to_file (SaveMimePartInfo *info)
2281 GnomeVFSResult result;
2282 GnomeVFSHandle *handle;
2284 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2286 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2287 if (result == GNOME_VFS_OK) {
2288 stream = tny_vfs_stream_new (handle);
2289 tny_mime_part_decode_to_stream (pair->part, stream);
2290 g_object_unref (G_OBJECT (stream));
2291 g_object_unref (pair->part);
2292 g_slice_free (SaveMimePartPair, pair);
2293 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2294 info->result = TRUE;
2296 save_mime_part_info_free (info, FALSE);
2297 info->result = FALSE;
2300 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2305 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2307 gboolean is_ok = TRUE;
2308 gint replaced_files = 0;
2309 const GList *files = info->pairs;
2312 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2313 SaveMimePartPair *pair = iter->data;
2314 if (modest_maemo_utils_file_exists (pair->filename)) {
2318 if (replaced_files) {
2319 GtkWidget *confirm_overwrite_dialog;
2320 const gchar *message = (replaced_files == 1) ?
2321 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2322 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2323 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2326 gtk_widget_destroy (confirm_overwrite_dialog);
2330 save_mime_part_info_free (info, TRUE);
2332 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2339 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2341 gboolean clean_list = FALSE;
2342 ModestMsgViewWindowPrivate *priv;
2343 GList *files_to_save = NULL;
2344 GtkWidget *save_dialog = NULL;
2345 gchar *folder = NULL;
2346 gboolean canceled = FALSE;
2347 const gchar *filename = NULL;
2348 gchar *save_multiple_str = NULL;
2350 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2351 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2353 if (mime_parts == NULL) {
2354 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2355 if (mime_parts == NULL)
2360 /* prepare dialog */
2361 if (mime_parts->next == NULL) {
2362 /* only one attachment selected */
2363 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2364 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2365 filename = tny_mime_part_get_filename (mime_part);
2367 g_warning ("Tried to save a non-file attachment");
2371 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2372 g_list_length (mime_parts));
2375 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2376 GTK_FILE_CHOOSER_ACTION_SAVE);
2379 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2380 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2384 if (filename != NULL)
2385 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2388 /* if multiple, set multiple string */
2389 if (save_multiple_str) {
2390 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2394 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2395 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2397 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2398 hildon_banner_show_information
2399 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2403 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2404 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2406 if (tny_mime_part_is_attachment (mime_part)) {
2407 SaveMimePartPair *pair;
2409 if ((mime_parts->next != NULL) &&
2410 (tny_mime_part_get_filename (mime_part) == NULL))
2413 pair = g_slice_new0 (SaveMimePartPair);
2414 if (mime_parts->next == NULL) {
2415 pair->filename = g_strdup (chooser_uri);
2418 g_build_filename (chooser_uri,
2419 tny_mime_part_get_filename (mime_part), NULL);
2421 pair->part = g_object_ref (mime_part);
2422 files_to_save = g_list_prepend (files_to_save, pair);
2426 g_free (chooser_uri);
2429 gtk_widget_destroy (save_dialog);
2432 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2433 g_list_free (mime_parts);
2436 if (files_to_save != NULL) {
2437 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2438 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2439 _CS("sfil_ib_saving"));
2440 info->pairs = files_to_save;
2441 info->banner = banner;
2442 info->result = TRUE;
2443 g_object_ref (banner);
2444 save_mime_parts_to_file_with_checks (info);
2449 show_remove_attachment_information (gpointer userdata)
2451 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2452 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2454 if (priv->remove_attachment_banner != NULL) {
2455 gtk_widget_destroy (priv->remove_attachment_banner);
2456 g_object_unref (priv->remove_attachment_banner);
2459 priv->remove_attachment_banner = g_object_ref (
2460 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2466 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2468 ModestMsgViewWindowPrivate *priv;
2469 GList *mime_parts = NULL, *node;
2470 gchar *confirmation_message;
2474 /* TnyFolder *folder; */
2476 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2480 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2482 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2484 /* Remove already purged messages from mime parts list */
2486 while (node != NULL) {
2487 TnyMimePart *part = TNY_MIME_PART (node->data);
2488 if (tny_mime_part_is_purged (part)) {
2489 GList *deleted_node = node;
2490 node = g_list_next (node);
2491 g_object_unref (part);
2492 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2494 node = g_list_next (node);
2498 if (mime_parts == NULL)
2501 n_attachments = g_list_length (mime_parts);
2502 if (n_attachments == 1) {
2503 const gchar *filename;
2505 if (TNY_IS_MSG (mime_parts->data)) {
2507 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2508 filename = tny_header_get_subject (header);
2509 g_object_unref (header);
2510 if (filename == NULL)
2511 filename = _("mail_va_no_subject");
2513 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2515 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2517 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2518 "mcen_nc_purge_files_text",
2519 n_attachments), n_attachments);
2521 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2522 confirmation_message);
2523 g_free (confirmation_message);
2525 if (response != GTK_RESPONSE_OK)
2528 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2529 /* folder = tny_msg_get_folder (msg); */
2530 /* tny_msg_uncache_attachments (msg); */
2531 /* tny_folder_refresh (folder, NULL); */
2532 /* g_object_unref (folder); */
2534 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2535 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2536 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2539 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2540 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2541 tny_msg_rewrite_cache (msg);
2542 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2544 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2545 g_list_free (mime_parts);
2547 if (priv->purge_timeout > 0) {
2548 g_source_remove (priv->purge_timeout);
2549 priv->purge_timeout = 0;
2552 if (priv->remove_attachment_banner) {
2553 gtk_widget_destroy (priv->remove_attachment_banner);
2554 g_object_unref (priv->remove_attachment_banner);
2555 priv->remove_attachment_banner = NULL;
2563 update_window_title (ModestMsgViewWindow *window)
2565 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2567 TnyHeader *header = NULL;
2568 const gchar *subject = NULL;
2570 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2572 header = tny_msg_get_header (msg);
2573 subject = tny_header_get_subject (header);
2574 g_object_unref (msg);
2577 if ((subject == NULL)||(subject[0] == '\0'))
2578 subject = _("mail_va_no_subject");
2580 gtk_window_set_title (GTK_WINDOW (window), subject);