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;
458 GList *iter, *children;
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 = children = 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);
475 g_list_free (children);
481 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
483 GtkWidget *main_vbox;
484 ModestMsgViewWindowPrivate *priv;
485 ModestWindowPrivate *parent_priv;
487 GtkAction *action = NULL;
489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
490 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
492 priv->msg_view = modest_msg_view_new (msg);
493 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
494 main_vbox = gtk_vbox_new (FALSE, 6);
497 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
498 conf = modest_runtime_get_conf ();
499 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
500 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
501 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
502 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
503 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
504 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
505 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
506 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
507 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
508 gtk_widget_show (GTK_WIDGET(parent_priv->menubar));
510 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
511 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
512 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
513 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
515 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
516 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
517 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
519 priv->find_toolbar = hildon_find_toolbar_new (NULL);
520 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
521 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
522 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
523 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
525 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);
526 gtk_widget_show_all (GTK_WIDGET(main_vbox));
530 modest_msg_view_window_disconnect_signals (ModestWindow *self)
532 ModestMsgViewWindowPrivate *priv;
533 ModestHeaderView *header_view = NULL;
534 ModestMainWindow *main_window = NULL;
535 ModestWindowMgr *window_mgr = NULL;
537 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
539 if (g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
540 priv->clipboard_change_handler))
541 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
542 priv->clipboard_change_handler);
544 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
545 priv->queue_change_handler))
546 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
547 priv->queue_change_handler);
549 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
550 priv->account_removed_handler))
551 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
552 priv->account_removed_handler);
554 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
555 priv->row_changed_handler))
556 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
557 priv->row_changed_handler);
559 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
560 priv->row_deleted_handler))
561 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
562 priv->row_deleted_handler);
564 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
565 priv->row_inserted_handler))
566 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
567 priv->row_inserted_handler);
569 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
570 priv->rows_reordered_handler))
571 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
572 priv->rows_reordered_handler);
574 window_mgr = modest_runtime_get_window_mgr();
575 g_assert(window_mgr != NULL);
577 main_window = MODEST_MAIN_WINDOW(
578 modest_window_mgr_get_main_window(window_mgr));
580 if(main_window == NULL)
583 header_view = MODEST_HEADER_VIEW(
584 modest_main_window_get_child_widget(
585 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
587 if (header_view == NULL)
590 modest_header_view_remove_observer(header_view,
591 MODEST_HEADER_VIEW_OBSERVER(self));
595 modest_msg_view_window_finalize (GObject *obj)
597 ModestMsgViewWindowPrivate *priv;
599 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
601 /* Sanity check: shouldn't be needed, the window mgr should
602 call this function before */
603 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
605 if (priv->header_model != NULL) {
606 g_object_unref (priv->header_model);
607 priv->header_model = NULL;
610 if (priv->progress_bar_timeout > 0) {
611 g_source_remove (priv->progress_bar_timeout);
612 priv->progress_bar_timeout = 0;
615 if (priv->remove_attachment_banner) {
616 gtk_widget_destroy (priv->remove_attachment_banner);
617 g_object_unref (priv->remove_attachment_banner);
618 priv->remove_attachment_banner = NULL;
621 if (priv->purge_timeout > 0) {
622 g_source_remove (priv->purge_timeout);
623 priv->purge_timeout = 0;
626 if (priv->row_reference) {
627 gtk_tree_row_reference_free (priv->row_reference);
628 priv->row_reference = NULL;
631 if (priv->next_row_reference) {
632 gtk_tree_row_reference_free (priv->next_row_reference);
633 priv->next_row_reference = NULL;
637 g_free (priv->msg_uid);
638 priv->msg_uid = NULL;
641 G_OBJECT_CLASS(parent_class)->finalize (obj);
645 select_next_valid_row (GtkTreeModel *model,
646 GtkTreeRowReference **row_reference,
649 GtkTreeIter tmp_iter;
650 GtkTreePath *path, *next;
651 gboolean retval = FALSE;
653 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
655 path = gtk_tree_row_reference_get_path (*row_reference);
656 gtk_tree_model_get_iter (model, &tmp_iter, path);
657 gtk_tree_row_reference_free (*row_reference);
658 *row_reference = NULL;
660 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
661 next = gtk_tree_model_get_path (model, &tmp_iter);
662 *row_reference = gtk_tree_row_reference_new (model, next);
664 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
665 next = gtk_tree_model_get_path (model, &tmp_iter);
667 /* Ensure that we are not selecting the same */
668 if (gtk_tree_path_compare (path, next) != 0) {
669 *row_reference = gtk_tree_row_reference_new (model, next);
675 gtk_tree_path_free (path);
680 /* TODO: This should be in _init(), with the parameters as properties. */
682 modest_msg_view_window_construct (ModestMsgViewWindow *self, TnyMsg *msg,
683 const gchar *modest_account_name,
684 const gchar *msg_uid)
687 ModestMsgViewWindowPrivate *priv = NULL;
688 ModestWindowPrivate *parent_priv = NULL;
689 ModestDimmingRulesGroup *menu_rules_group = NULL;
690 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
691 GtkActionGroup *action_group = NULL;
692 GError *error = NULL;
693 GdkPixbuf *window_icon;
695 g_return_if_fail (msg);
697 obj = G_OBJECT (self);
698 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
699 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
701 priv->msg_uid = g_strdup (msg_uid);
703 parent_priv->ui_manager = gtk_ui_manager_new();
704 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
706 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
707 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
709 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
710 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
712 /* Add common actions */
713 gtk_action_group_add_actions (action_group,
714 modest_action_entries,
715 G_N_ELEMENTS (modest_action_entries),
717 gtk_action_group_add_toggle_actions (action_group,
718 modest_toggle_action_entries,
719 G_N_ELEMENTS (modest_toggle_action_entries),
721 gtk_action_group_add_toggle_actions (action_group,
722 msg_view_toggle_action_entries,
723 G_N_ELEMENTS (msg_view_toggle_action_entries),
725 gtk_action_group_add_radio_actions (action_group,
726 msg_view_zoom_action_entries,
727 G_N_ELEMENTS (msg_view_zoom_action_entries),
729 G_CALLBACK (modest_ui_actions_on_change_zoom),
732 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
733 g_object_unref (action_group);
735 /* Load the UI definition */
736 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
739 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
740 g_error_free (error);
745 /* Add common dimming rules */
746 modest_dimming_rules_group_add_rules (menu_rules_group,
747 modest_msg_view_menu_dimming_entries,
748 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
750 modest_dimming_rules_group_add_rules (toolbar_rules_group,
751 modest_msg_view_toolbar_dimming_entries,
752 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
755 /* Insert dimming rules group for this window */
756 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
757 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
758 g_object_unref (menu_rules_group);
759 g_object_unref (toolbar_rules_group);
761 /* Add accelerators */
762 gtk_window_add_accel_group (GTK_WINDOW (obj),
763 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
766 init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
767 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
769 /* Set window icon */
770 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
772 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
773 g_object_unref (window_icon);
776 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
778 g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
779 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
780 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
781 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
783 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
785 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
787 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
789 g_signal_connect (G_OBJECT (obj), "key-release-event",
790 G_CALLBACK (modest_msg_view_window_key_release_event),
793 g_signal_connect (G_OBJECT (obj), "window-state-event",
794 G_CALLBACK (modest_msg_view_window_window_state_event),
797 /* Mail Operation Queue */
798 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
800 G_CALLBACK (on_queue_changed),
803 /* Account manager */
804 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
806 G_CALLBACK(on_account_removed),
809 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
811 priv->last_search = NULL;
813 /* Init the clipboard actions dim status */
814 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
816 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
818 /* Check toolbar dimming rules */
819 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
824 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
825 const gchar *modest_account_name,
826 const gchar *msg_uid,
828 GtkTreeRowReference *row_reference)
830 ModestMsgViewWindow *window = NULL;
831 ModestMsgViewWindowPrivate *priv = NULL;
832 TnyFolder *header_folder = NULL;
833 ModestHeaderView *header_view = NULL;
834 ModestMainWindow *main_window = NULL;
835 ModestWindowMgr *window_mgr = NULL;
837 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
838 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
839 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
841 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
843 /* Remember the message list's TreeModel so we can detect changes
844 * and change the list selection when necessary: */
845 window_mgr = modest_runtime_get_window_mgr();
846 g_assert(window_mgr != NULL);
847 main_window = MODEST_MAIN_WINDOW(
848 modest_window_mgr_get_main_window(window_mgr));
849 g_assert(main_window != NULL);
850 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
851 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
852 if (header_view != NULL){
853 header_folder = modest_header_view_get_folder(header_view);
854 g_assert(header_folder != NULL);
855 priv->header_folder_id = tny_folder_get_id(header_folder);
856 g_assert(priv->header_folder_id != NULL);
857 g_object_unref(header_folder);
860 priv->header_model = g_object_ref(model);
861 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
862 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
863 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
865 priv->row_changed_handler = g_signal_connect(
866 GTK_TREE_MODEL(model), "row-changed",
867 G_CALLBACK(modest_msg_view_window_on_row_changed),
869 priv->row_deleted_handler = g_signal_connect(
870 GTK_TREE_MODEL(model), "row-deleted",
871 G_CALLBACK(modest_msg_view_window_on_row_deleted),
873 priv->row_inserted_handler = g_signal_connect (
874 GTK_TREE_MODEL(model), "row-inserted",
875 G_CALLBACK(modest_msg_view_window_on_row_inserted),
877 priv->rows_reordered_handler = g_signal_connect(
878 GTK_TREE_MODEL(model), "rows-reordered",
879 G_CALLBACK(modest_msg_view_window_on_row_reordered),
882 if (header_view != NULL){
883 modest_header_view_add_observer(header_view,
884 MODEST_HEADER_VIEW_OBSERVER(window));
887 modest_msg_view_window_update_priority (window);
889 /* Check toolbar dimming rules */
890 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
892 return MODEST_WINDOW(window);
896 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
897 const gchar *modest_account_name,
898 const gchar *msg_uid)
900 ModestMsgViewWindow *window = NULL;
901 ModestMsgViewWindowPrivate *priv = NULL;
903 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
904 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
905 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
907 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
909 /* Remember that this is a search result,
910 * so we can disable some UI appropriately: */
911 priv->is_search_result = TRUE;
913 return MODEST_WINDOW(window);
917 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
918 const gchar *modest_account_name,
919 const gchar *msg_uid)
922 g_return_val_if_fail (msg, NULL);
924 obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
925 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
926 msg, modest_account_name, msg_uid);
928 return MODEST_WINDOW(obj);
931 void modest_msg_view_window_on_row_changed(
932 GtkTreeModel *header_model,
935 ModestMsgViewWindow *window){
936 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
939 void modest_msg_view_window_on_row_deleted(
940 GtkTreeModel *header_model,
942 ModestMsgViewWindow *window){
943 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
946 /* On insertions we check if the folder still has the message we are
947 * showing or do not. If do not, we do nothing. Which means we are still
948 * not attached to any header folder and thus next/prev buttons are
949 * still dimmed. Once the message that is shown by msg-view is found, the
950 * new model of header-view will be attached and the references will be set.
951 * On each further insertions dimming rules will be checked. However
952 * this requires extra CPU time at least works.
953 * (An message might be deleted from TnyFolder and thus will not be
954 * inserted into the model again for example if it is removed by the
955 * imap server and the header view is refreshed.)
957 void modest_msg_view_window_on_row_inserted(
958 GtkTreeModel *new_model,
959 GtkTreePath *tree_path,
960 GtkTreeIter *tree_iter,
961 ModestMsgViewWindow *window){
962 ModestMsgViewWindowPrivate *priv = NULL;
963 TnyHeader *header = NULL;
966 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
968 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
970 /* If we already has a model attached then the message shown by
971 * msg-view is in it, and thus we do not need any actions but
972 * to check the dimming rules.*/
973 if(priv->header_model != NULL){
974 gtk_tree_row_reference_free(priv->next_row_reference);
975 priv->next_row_reference = gtk_tree_row_reference_copy(
976 priv->row_reference);
977 select_next_valid_row (priv->header_model,
978 &(priv->next_row_reference), FALSE);
979 modest_ui_actions_check_toolbar_dimming_rules (
980 MODEST_WINDOW (window));
984 /* Check if the newly inserted message is the same we are actually
985 * showing. IF not, we should remain detached from the header model
986 * and thus prev and next toolbarbuttons should remain dimmed. */
987 gtk_tree_model_get (new_model, tree_iter,
988 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
989 uid = modest_tny_folder_get_header_unique_id(header);
990 if(!g_str_equal(priv->msg_uid, uid)){
996 /* Setup row_reference for the actual msg. */
997 priv->row_reference = gtk_tree_row_reference_new(
998 new_model, tree_path);
999 if(priv->row_reference == NULL){
1000 g_warning("No reference for msg header item.");
1004 /* Attach new_model and connect some callback to it to become able
1005 * to detect changes in header-view. */
1006 priv->header_model = g_object_ref(new_model);
1007 g_signal_connect (new_model, "row-changed",
1008 G_CALLBACK (modest_msg_view_window_on_row_changed),
1010 g_signal_connect (new_model, "row-deleted",
1011 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1013 g_signal_connect (new_model, "rows-reordered",
1014 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1017 /* Now set up next_row_reference. */
1018 priv->next_row_reference = gtk_tree_row_reference_copy(
1019 priv->row_reference);
1020 select_next_valid_row (priv->header_model,
1021 &(priv->next_row_reference), FALSE);
1023 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1026 void modest_msg_view_window_on_row_reordered(
1027 GtkTreeModel *header_model,
1031 ModestMsgViewWindow *window){
1032 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1035 /* The modest_msg_view_window_update_model_replaced implements update
1036 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1037 * actually belongs to the header-view is the same as the TnyFolder of
1038 * the message of msg-view or not. If they are different, there is
1039 * nothing to do. If they are the same, then the model has replaced and
1040 * the reference in msg-view shall be replaced from the old model to
1041 * the new model. In this case the view will be detached from it's
1042 * header folder. From this point the next/prev buttons are dimmed.
1044 void modest_msg_view_window_update_model_replaced(
1045 ModestHeaderViewObserver *observer,
1046 GtkTreeModel *model,
1047 const gchar *tny_folder_id){
1048 ModestMsgViewWindowPrivate *priv = NULL;
1049 ModestMsgViewWindow *window = NULL;
1051 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1052 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1054 window = MODEST_MSG_VIEW_WINDOW(observer);
1055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1057 /* If there is an other folder in the header-view then we do
1058 * not care about it's model (msg list). Else if the
1059 * header-view shows the folder the msg shown by us is in, we
1060 * shall replace our model reference and make some check. */
1061 if(tny_folder_id == NULL ||
1062 !g_str_equal(tny_folder_id, priv->header_folder_id))
1065 /* Model is changed(replaced), so we should forget the old
1066 * one. Because there might be other references and there
1067 * might be some change on the model even if we unreferenced
1068 * it, we need to disconnect our signals here. */
1069 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1070 priv->row_changed_handler))
1071 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1072 priv->row_changed_handler);
1073 priv->row_changed_handler = 0;
1074 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1075 priv->row_deleted_handler))
1076 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1077 priv->row_deleted_handler);
1078 priv->row_deleted_handler = 0;
1079 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1080 priv->row_inserted_handler))
1081 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1082 priv->row_inserted_handler);
1083 priv->row_inserted_handler = 0;
1084 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1085 priv->rows_reordered_handler))
1086 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1087 priv->rows_reordered_handler);
1088 priv->rows_reordered_handler = 0;
1089 g_object_unref(priv->header_model);
1090 priv->header_model = NULL;
1091 g_object_unref(priv->row_reference);
1092 priv->row_reference = NULL;
1093 g_object_unref(priv->next_row_reference);
1094 priv->next_row_reference = NULL;
1096 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1098 if(tny_folder_id == NULL)
1101 g_assert(model != NULL);
1103 /* Also we must connect to the new model for row insertions.
1104 * Only for insertions now. We will need other ones only after
1105 * the msg is show by msg-view is added to the new model. */
1106 priv->row_inserted_handler = g_signal_connect (
1107 model, "row-inserted",
1108 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1113 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1115 ModestMsgViewWindowPrivate *priv= NULL;
1117 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1118 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1120 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1124 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1126 ModestMsgViewWindowPrivate *priv= NULL;
1128 TnyHeader *header = NULL;
1129 GtkTreePath *path = NULL;
1132 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1133 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1135 /* If the message was not obtained from a treemodel,
1136 * for instance if it was opened directly by the search UI:
1138 if (priv->header_model == NULL) {
1139 msg = modest_msg_view_window_get_message (self);
1140 header = tny_msg_get_header (msg);
1141 g_object_unref (msg);
1145 /* Get iter of the currently selected message in the header view: */
1146 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1147 * instead of sometimes retrieving it from the header view?
1148 * Then we wouldn't be dependent on the message actually still being selected
1149 * in the header view. murrayc. */
1150 path = gtk_tree_row_reference_get_path (priv->row_reference);
1151 g_return_val_if_fail (path != NULL, NULL);
1152 gtk_tree_model_get_iter (priv->header_model,
1156 /* Get current message header */
1157 gtk_tree_model_get (priv->header_model, &iter,
1158 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1161 gtk_tree_path_free (path);
1166 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1168 ModestMsgView *msg_view;
1169 ModestMsgViewWindowPrivate *priv;
1171 g_return_val_if_fail (self, NULL);
1173 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1175 msg_view = MODEST_MSG_VIEW (priv->msg_view);
1177 return modest_msg_view_get_message (msg_view);
1181 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1183 ModestMsgViewWindowPrivate *priv;
1185 g_return_val_if_fail (self, NULL);
1187 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1189 return (const gchar*) priv->msg_uid;
1193 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1196 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1197 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1198 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1202 is_active = gtk_toggle_action_get_active (toggle);
1205 gtk_widget_show (priv->find_toolbar);
1206 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1208 gtk_widget_hide (priv->find_toolbar);
1211 /* update the toggle buttons status */
1212 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1213 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1214 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1215 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1220 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1221 ModestMsgViewWindow *obj)
1223 GtkToggleAction *toggle;
1224 ModestWindowPrivate *parent_priv;
1225 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1227 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1228 gtk_toggle_action_set_active (toggle, FALSE);
1232 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1233 ModestMsgViewWindow *obj)
1235 gchar *current_search;
1236 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1238 if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
1239 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1243 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1245 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1246 g_free (current_search);
1247 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1251 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1253 g_free (priv->last_search);
1254 priv->last_search = g_strdup (current_search);
1255 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
1258 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1259 g_free (priv->last_search);
1260 priv->last_search = NULL;
1263 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
1264 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1265 g_free (priv->last_search);
1266 priv->last_search = NULL;
1270 g_free (current_search);
1275 modest_msg_view_window_set_zoom (ModestWindow *window,
1278 ModestMsgViewWindowPrivate *priv;
1280 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1282 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1283 modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
1287 modest_msg_view_window_get_zoom (ModestWindow *window)
1289 ModestMsgViewWindowPrivate *priv;
1291 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1293 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1294 return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
1298 modest_msg_view_window_zoom_plus (ModestWindow *window)
1300 ModestWindowPrivate *parent_priv;
1301 GtkRadioAction *zoom_radio_action;
1302 GSList *group, *node;
1304 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1305 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1306 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1308 group = gtk_radio_action_get_group (zoom_radio_action);
1310 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1311 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1315 for (node = group; node != NULL; node = g_slist_next (node)) {
1316 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1317 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1325 modest_msg_view_window_zoom_minus (ModestWindow *window)
1327 ModestWindowPrivate *parent_priv;
1328 GtkRadioAction *zoom_radio_action;
1329 GSList *group, *node;
1331 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1332 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1333 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1335 group = gtk_radio_action_get_group (zoom_radio_action);
1337 for (node = group; node != NULL; node = g_slist_next (node)) {
1338 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1339 if (node->next != NULL) {
1340 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1343 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1353 modest_msg_view_window_key_release_event (GtkWidget *window,
1357 if (event->type == GDK_KEY_RELEASE) {
1358 switch (event->keyval) {
1360 modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1364 modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1377 modest_msg_view_window_scroll_up (ModestWindow *window)
1379 ModestMsgViewWindowPrivate *priv;
1380 gboolean return_value;
1382 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1383 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1387 modest_msg_view_window_scroll_down (ModestWindow *window)
1389 ModestMsgViewWindowPrivate *priv;
1390 gboolean return_value;
1392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1393 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1397 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1400 ModestMsgViewWindowPrivate *priv;
1401 GtkTreeIter tmp_iter;
1402 gboolean is_last_selected;
1404 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1405 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1407 /*if no model (so no rows at all), then virtually we are the last*/
1408 if (!priv->header_model)
1411 path = gtk_tree_row_reference_get_path (priv->row_reference);
1415 is_last_selected = TRUE;
1416 while (is_last_selected) {
1418 gtk_tree_path_next (path);
1419 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1421 gtk_tree_model_get (priv->header_model, &tmp_iter,
1422 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1424 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1425 is_last_selected = FALSE;
1427 gtk_tree_path_free (path);
1428 return is_last_selected;
1432 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1434 ModestMsgViewWindowPrivate *priv;
1436 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1437 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1439 return priv->header_model != NULL;
1443 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1445 ModestMsgViewWindowPrivate *priv;
1447 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1448 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1450 return priv->is_search_result;
1454 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1457 ModestMsgViewWindowPrivate *priv;
1458 gboolean is_first_selected;
1459 GtkTreeIter tmp_iter;
1460 /* gchar * path_string;*/
1462 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1463 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1465 /*if no model (so no rows at all), then virtually we are the first*/
1466 if (!priv->header_model)
1469 path = gtk_tree_row_reference_get_path (priv->row_reference);
1473 /* path_string = gtk_tree_path_to_string (path);
1474 is_first_selected = strcmp (path_string, "0");
1476 g_free (path_string);
1477 gtk_tree_path_free (path);
1479 return is_first_selected;*/
1481 is_first_selected = TRUE;
1482 while (is_first_selected) {
1484 if(!gtk_tree_path_prev (path))
1486 /* Here the 'if' is needless for logic, but let make sure
1487 * iter is valid for gtk_tree_model_get. */
1488 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1490 gtk_tree_model_get (priv->header_model, &tmp_iter,
1491 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1493 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1494 is_first_selected = FALSE;
1496 gtk_tree_path_free (path);
1497 return is_first_selected;
1501 * Reads the message whose summary item is @header. It takes care of
1502 * several things, among others:
1504 * If the message was not previously downloaded then ask the user
1505 * before downloading. If there is no connection launch the connection
1506 * dialog. Update toolbar dimming rules.
1508 * Returns: TRUE if the mail operation was started, otherwise if the
1509 * user do not want to download the message, or if the user do not
1510 * want to connect, then the operation is not issued
1513 message_reader (ModestMsgViewWindow *window,
1514 ModestMsgViewWindowPrivate *priv,
1518 ModestMailOperation *mail_op = NULL;
1519 ModestMailOperationTypeOperation op_type;
1520 gboolean already_showing = FALSE;
1521 ModestWindow *msg_window = NULL;
1522 ModestWindowMgr *mgr;
1524 g_return_val_if_fail (path != NULL, FALSE);
1526 mgr = modest_runtime_get_window_mgr ();
1527 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1528 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1531 gtk_window_present (GTK_WINDOW (msg_window));
1532 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1536 /* Msg download completed */
1537 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1538 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1540 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1542 /* Ask the user if he wants to download the message if
1544 if (!tny_device_is_online (modest_runtime_get_device())) {
1545 TnyFolder *folder = NULL;
1546 GtkResponseType response;
1548 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1549 _("mcen_nc_get_msg"));
1550 if (response == GTK_RESPONSE_CANCEL)
1553 /* Offer the connection dialog if necessary */
1554 folder = tny_header_get_folder (header);
1556 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1557 TNY_FOLDER_STORE (folder))) {
1558 g_object_unref (folder);
1561 g_object_unref (folder);
1566 /* New mail operation */
1567 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1569 modest_ui_actions_get_msgs_full_error_handler,
1572 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1573 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1574 g_object_unref (mail_op);
1576 /* Update toolbar dimming rules */
1577 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1583 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1585 ModestMsgViewWindowPrivate *priv;
1586 GtkTreePath *path= NULL;
1587 GtkTreeIter tmp_iter;
1589 gboolean retval = TRUE;
1591 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1592 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1594 /* Update the next row reference if it's not valid. This could
1595 happen if for example the header which it was pointing to,
1596 was deleted. The best place to do it is in the row-deleted
1597 handler but the tinymail model do not work like the glib
1598 tree models and reports the deletion when the row is still
1600 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1601 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1602 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1603 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1606 if (priv->next_row_reference)
1607 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1611 gtk_tree_model_get_iter (priv->header_model,
1615 gtk_tree_model_get (priv->header_model, &tmp_iter,
1616 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1619 /* Read the message & show it */
1620 if (!message_reader (window, priv, header, path)) {
1622 gtk_tree_path_free (path);
1626 g_object_unref (header);
1632 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1634 ModestMsgViewWindowPrivate *priv = NULL;
1635 TnyHeader *header = NULL;
1639 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1640 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1642 /* Check that the model is not empty */
1643 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1646 /* Get the header */
1647 gtk_tree_model_get (priv->header_model,
1649 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1651 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1652 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1653 g_object_unref (header);
1654 return modest_msg_view_window_select_next_message (self);
1657 path = gtk_tree_model_get_path (priv->header_model, &iter);
1659 /* Read the message & show it */
1660 message_reader (self, priv, header, path);
1663 g_object_unref (header);
1669 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1671 ModestMsgViewWindowPrivate *priv = NULL;
1674 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1675 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1677 /* Return inmediatly if there is no header model */
1678 if (!priv->header_model)
1681 path = gtk_tree_row_reference_get_path (priv->row_reference);
1682 while (gtk_tree_path_prev (path)) {
1686 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1687 gtk_tree_model_get (priv->header_model, &iter,
1688 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1692 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1693 g_object_unref (header);
1697 /* Read the message & show it */
1698 if (!message_reader (window, priv, header, path)) {
1699 g_object_unref (header);
1703 g_object_unref (header);
1708 gtk_tree_path_free (path);
1713 view_msg_cb (ModestMailOperation *mail_op,
1718 ModestMsgViewWindow *self = NULL;
1719 ModestMsgViewWindowPrivate *priv = NULL;
1722 /* If there was any error */
1723 path = (GtkTreePath *) user_data;
1724 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1725 gtk_tree_path_free (path);
1729 /* Get the window */
1730 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1731 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1732 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1734 /* Update the row reference */
1735 gtk_tree_row_reference_free (priv->row_reference);
1736 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1737 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1738 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1739 gtk_tree_path_free (path);
1741 /* Mark header as read */
1742 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1743 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1745 /* Set new message */
1746 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1747 modest_msg_view_window_update_priority (self);
1748 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1749 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1751 /* Set the new message uid of the window */
1752 if (priv->msg_uid) {
1753 g_free (priv->msg_uid);
1754 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1757 /* Notify the observers */
1758 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1759 0, priv->header_model, priv->row_reference);
1761 /* Free new references */
1762 g_object_unref (self);
1766 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1768 ModestMsgViewWindowPrivate *priv;
1770 TnyFolderType folder_type;
1772 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1774 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1776 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1780 folder = tny_msg_get_folder (msg);
1783 folder_type = tny_folder_get_folder_type (folder);
1785 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1786 const gchar *fname = tny_folder_get_name (folder);
1787 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1790 g_object_unref (folder);
1792 g_object_unref (msg);
1800 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1802 ModestMsgViewWindowPrivate *priv;
1803 TnyHeaderFlags flags = 0;
1805 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1807 if (priv->header_model) {
1810 GtkTreePath *path = NULL;
1812 path = gtk_tree_row_reference_get_path (priv->row_reference);
1813 g_return_if_fail (path != NULL);
1814 gtk_tree_model_get_iter (priv->header_model,
1816 gtk_tree_row_reference_get_path (priv->row_reference));
1818 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1820 flags = tny_header_get_flags (header);
1821 gtk_tree_path_free (path);
1824 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1829 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1831 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1832 ModestWindowPrivate *parent_priv;
1833 ModestWindowMgr *mgr;
1834 gboolean is_fullscreen;
1835 GtkAction *fs_toggle_action;
1838 mgr = modest_runtime_get_window_mgr ();
1839 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1841 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1843 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1844 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1845 if (is_fullscreen != active) {
1846 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1855 set_homogeneous (GtkWidget *widget,
1858 if (GTK_IS_TOOL_ITEM (widget)) {
1859 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1860 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1865 modest_msg_view_window_show_toolbar (ModestWindow *self,
1866 gboolean show_toolbar)
1868 ModestMsgViewWindowPrivate *priv = NULL;
1869 ModestWindowPrivate *parent_priv;
1870 GtkWidget *reply_button = NULL, *menu = NULL;
1871 GtkWidget *placeholder = NULL;
1873 const gchar *action_name;
1876 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1877 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1879 /* Set optimized view status */
1880 priv->optimized_view = !show_toolbar;
1882 if (!parent_priv->toolbar) {
1883 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1885 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1887 /* Set homogeneous toolbar */
1888 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1889 set_homogeneous, NULL);
1891 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1892 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1893 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1894 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1895 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1896 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1897 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1898 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1900 /* Add ProgressBar (Transfer toolbar) */
1901 priv->progress_bar = modest_progress_bar_widget_new ();
1902 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1903 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1904 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1905 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1906 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1908 /* Connect cancel 'clicked' signal to abort progress mode */
1909 g_signal_connect(priv->cancel_toolitem, "clicked",
1910 G_CALLBACK(cancel_progressbar),
1913 /* Add it to the observers list */
1914 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1917 hildon_window_add_toolbar (HILDON_WINDOW (self),
1918 GTK_TOOLBAR (parent_priv->toolbar));
1921 /* Set reply button tap and hold menu */
1922 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1923 "/ToolBar/ToolbarMessageReply");
1924 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1925 "/ToolbarReplyCSM");
1926 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1930 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1931 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1932 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1934 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1935 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1938 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1939 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1942 /* Update also the actions (to update the toggles in the
1943 menus), we have to do it manually because some other window
1944 of the same time could have changed it (remember that the
1945 toolbar fullscreen mode is shared by all the windows of the
1947 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
1948 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
1950 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
1952 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
1953 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
1958 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1960 ModestMsgViewWindow *window)
1962 ModestWindowPrivate *parent_priv;
1963 /* GtkAction *action; */
1964 gboolean is_address;
1968 if (!GTK_WIDGET_VISIBLE (window))
1971 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1972 selection = gtk_clipboard_wait_for_text (clipboard);
1974 is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1976 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1977 /* gtk_action_set_sensitive (action, is_address); */
1979 focused = gtk_window_get_focus (GTK_WINDOW (window));
1981 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1982 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1984 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1985 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1988 /* modest_msg_view_window_update_dimmed (window); */
1993 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1995 ModestMsgViewWindowPrivate *priv;
1997 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1998 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2000 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2004 cancel_progressbar (GtkToolButton *toolbutton,
2005 ModestMsgViewWindow *self)
2008 ModestMsgViewWindowPrivate *priv;
2010 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2012 /* Get operation observers and cancel its current operation */
2013 tmp = priv->progress_widgets;
2015 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2016 tmp=g_slist_next(tmp);
2020 observers_empty (ModestMsgViewWindow *self)
2023 ModestMsgViewWindowPrivate *priv;
2024 gboolean is_empty = TRUE;
2025 guint pending_ops = 0;
2027 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2028 tmp = priv->progress_widgets;
2030 /* Check all observers */
2031 while (tmp && is_empty) {
2032 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2033 is_empty = pending_ops == 0;
2035 tmp = g_slist_next(tmp);
2042 on_account_removed (TnyAccountStore *account_store,
2043 TnyAccount *account,
2046 /* Do nothing if it's a transport account, because we only
2047 show the messages of a store account */
2048 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2049 const gchar *parent_acc = NULL;
2050 const gchar *our_acc = NULL;
2052 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2053 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2055 /* Close this window if I'm showing a message of the removed account */
2056 if (strcmp (parent_acc, our_acc) == 0)
2057 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2062 on_queue_changed (ModestMailOperationQueue *queue,
2063 ModestMailOperation *mail_op,
2064 ModestMailOperationQueueNotification type,
2065 ModestMsgViewWindow *self)
2068 ModestMsgViewWindowPrivate *priv;
2069 ModestMailOperationTypeOperation op_type;
2070 ModestToolBarModes mode;
2072 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2073 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2075 /* If this operations was created by another window, do nothing */
2076 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2079 /* Get toolbar mode from operation id*/
2080 op_type = modest_mail_operation_get_type_operation (mail_op);
2082 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
2083 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2084 case MODEST_MAIL_OPERATION_TYPE_OPEN:
2085 mode = TOOLBAR_MODE_TRANSFER;
2088 mode = TOOLBAR_MODE_NORMAL;
2092 /* Add operation observers and change toolbar if neccessary*/
2093 tmp = priv->progress_widgets;
2095 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2096 if (mode == TOOLBAR_MODE_TRANSFER) {
2097 /* Enable transfer toolbar mode */
2098 set_toolbar_transfer_mode(self);
2100 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2102 tmp = g_slist_next (tmp);
2107 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2108 if (mode == TOOLBAR_MODE_TRANSFER) {
2110 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2112 tmp = g_slist_next (tmp);
2116 /* If no more operations are being observed, NORMAL mode is enabled again */
2117 if (observers_empty (self)) {
2118 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2126 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2128 ModestMsgViewWindowPrivate *priv;
2129 GList *selected_attachments = NULL;
2131 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2132 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2134 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2136 return selected_attachments;
2140 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2142 ModestMsgViewWindowPrivate *priv;
2143 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2144 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2146 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2148 if (mime_part == NULL) {
2149 gboolean error = FALSE;
2150 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2151 if (selected_attachments == NULL) {
2153 } else if (g_list_length (selected_attachments) > 1) {
2154 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2157 mime_part = (TnyMimePart *) selected_attachments->data;
2158 g_object_ref (mime_part);
2160 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2161 g_list_free (selected_attachments);
2166 g_object_ref (mime_part);
2169 if (tny_mime_part_is_purged (mime_part)) {
2170 g_object_unref (mime_part);
2174 if (!TNY_IS_MSG (mime_part)) {
2175 gchar *filepath = NULL;
2176 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2177 TnyFsStream *temp_stream = NULL;
2179 temp_stream = modest_maemo_utils_create_temp_stream (att_filename, &filepath);
2182 const gchar *content_type;
2183 content_type = tny_mime_part_get_content_type (mime_part);
2184 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2186 modest_platform_activate_file (filepath, content_type);
2187 g_object_unref (temp_stream);
2189 /* NOTE: files in the temporary area will be automatically
2190 * cleaned after some time if they are no longer in use */
2193 /* message attachment */
2194 TnyHeader *header = NULL;
2195 ModestWindowMgr *mgr;
2196 ModestWindow *msg_win = NULL;
2199 header = tny_msg_get_header (TNY_MSG (mime_part));
2200 mgr = modest_runtime_get_window_mgr ();
2201 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2204 if (msg_win) /* there is already a window for this uid; top it */
2205 gtk_window_present (GTK_WINDOW(msg_win));
2207 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2208 * thus, we don't do anything */
2209 g_warning ("window for is already being created");
2211 /* it's not found, so create a new window for it */
2212 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
2213 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2215 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2216 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
2217 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2218 modest_window_get_zoom (MODEST_WINDOW (window)));
2219 modest_window_mgr_register_window (mgr, msg_win);
2220 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2221 gtk_widget_show_all (GTK_WIDGET (msg_win));
2224 g_object_unref (mime_part);
2240 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2241 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2242 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2243 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2246 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2250 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2251 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2252 g_free (pair->filename);
2253 g_object_unref (pair->part);
2254 g_slice_free (SaveMimePartPair, pair);
2256 g_list_free (info->pairs);
2259 gtk_widget_destroy (info->banner);
2260 g_object_unref (info->banner);
2261 g_slice_free (SaveMimePartInfo, info);
2266 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2268 if (info->pairs != NULL) {
2269 save_mime_part_to_file (info);
2272 result = info->result;
2274 /* This is a GDK lock because we are an idle callback and
2275 * hildon_banner_show_information is or does Gtk+ code */
2277 gdk_threads_enter (); /* CHECKED */
2278 save_mime_part_info_free (info, TRUE);
2280 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2282 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2284 gdk_threads_leave (); /* CHECKED */
2291 save_mime_part_to_file (SaveMimePartInfo *info)
2293 GnomeVFSResult result;
2294 GnomeVFSHandle *handle;
2296 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2298 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2299 if (result == GNOME_VFS_OK) {
2300 stream = tny_vfs_stream_new (handle);
2301 tny_mime_part_decode_to_stream (pair->part, stream);
2302 g_object_unref (G_OBJECT (stream));
2303 g_object_unref (pair->part);
2304 g_slice_free (SaveMimePartPair, pair);
2305 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2306 info->result = TRUE;
2308 save_mime_part_info_free (info, FALSE);
2309 info->result = FALSE;
2312 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2317 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2319 gboolean is_ok = TRUE;
2320 gint replaced_files = 0;
2321 const GList *files = info->pairs;
2324 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2325 SaveMimePartPair *pair = iter->data;
2326 if (modest_maemo_utils_file_exists (pair->filename)) {
2330 if (replaced_files) {
2331 GtkWidget *confirm_overwrite_dialog;
2332 const gchar *message = (replaced_files == 1) ?
2333 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2334 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2335 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2338 gtk_widget_destroy (confirm_overwrite_dialog);
2342 save_mime_part_info_free (info, TRUE);
2344 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2351 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2353 gboolean clean_list = FALSE;
2354 ModestMsgViewWindowPrivate *priv;
2355 GList *files_to_save = NULL;
2356 GtkWidget *save_dialog = NULL;
2357 gchar *folder = NULL;
2358 gboolean canceled = FALSE;
2359 const gchar *filename = NULL;
2360 gchar *save_multiple_str = NULL;
2362 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2363 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2365 if (mime_parts == NULL) {
2366 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2367 if (mime_parts == NULL)
2372 /* prepare dialog */
2373 if (mime_parts->next == NULL) {
2374 /* only one attachment selected */
2375 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2376 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2377 filename = tny_mime_part_get_filename (mime_part);
2379 g_warning ("Tried to save a non-file attachment");
2383 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2384 g_list_length (mime_parts));
2387 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2388 GTK_FILE_CHOOSER_ACTION_SAVE);
2391 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2392 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2396 if (filename != NULL)
2397 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2400 /* if multiple, set multiple string */
2401 if (save_multiple_str) {
2402 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2406 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2407 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2409 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2410 hildon_banner_show_information
2411 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2415 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2416 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2418 if (tny_mime_part_is_attachment (mime_part)) {
2419 SaveMimePartPair *pair;
2421 if ((mime_parts->next != NULL) &&
2422 (tny_mime_part_get_filename (mime_part) == NULL))
2425 pair = g_slice_new0 (SaveMimePartPair);
2426 if (mime_parts->next == NULL) {
2427 pair->filename = g_strdup (chooser_uri);
2430 g_build_filename (chooser_uri,
2431 tny_mime_part_get_filename (mime_part), NULL);
2433 pair->part = g_object_ref (mime_part);
2434 files_to_save = g_list_prepend (files_to_save, pair);
2438 g_free (chooser_uri);
2441 gtk_widget_destroy (save_dialog);
2444 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2445 g_list_free (mime_parts);
2448 if (files_to_save != NULL) {
2449 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2450 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2451 _CS("sfil_ib_saving"));
2452 info->pairs = files_to_save;
2453 info->banner = banner;
2454 info->result = TRUE;
2455 g_object_ref (banner);
2456 save_mime_parts_to_file_with_checks (info);
2461 show_remove_attachment_information (gpointer userdata)
2463 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2464 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2466 if (priv->remove_attachment_banner != NULL) {
2467 gtk_widget_destroy (priv->remove_attachment_banner);
2468 g_object_unref (priv->remove_attachment_banner);
2471 priv->remove_attachment_banner = g_object_ref (
2472 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2478 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2480 ModestMsgViewWindowPrivate *priv;
2481 GList *mime_parts = NULL, *node;
2482 gchar *confirmation_message;
2486 /* TnyFolder *folder; */
2488 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2492 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2494 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2496 /* Remove already purged messages from mime parts list */
2498 while (node != NULL) {
2499 TnyMimePart *part = TNY_MIME_PART (node->data);
2500 if (tny_mime_part_is_purged (part)) {
2501 GList *deleted_node = node;
2502 node = g_list_next (node);
2503 g_object_unref (part);
2504 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2506 node = g_list_next (node);
2510 if (mime_parts == NULL)
2513 n_attachments = g_list_length (mime_parts);
2514 if (n_attachments == 1) {
2515 const gchar *filename;
2517 if (TNY_IS_MSG (mime_parts->data)) {
2519 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2520 filename = tny_header_get_subject (header);
2521 g_object_unref (header);
2522 if (filename == NULL)
2523 filename = _("mail_va_no_subject");
2525 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2527 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2529 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2530 "mcen_nc_purge_files_text",
2531 n_attachments), n_attachments);
2533 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2534 confirmation_message);
2535 g_free (confirmation_message);
2537 if (response != GTK_RESPONSE_OK)
2540 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2541 /* folder = tny_msg_get_folder (msg); */
2542 /* tny_msg_uncache_attachments (msg); */
2543 /* tny_folder_refresh (folder, NULL); */
2544 /* g_object_unref (folder); */
2546 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2547 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2548 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2551 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2552 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2553 tny_msg_rewrite_cache (msg);
2554 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2556 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2557 g_list_free (mime_parts);
2559 if (priv->purge_timeout > 0) {
2560 g_source_remove (priv->purge_timeout);
2561 priv->purge_timeout = 0;
2564 if (priv->remove_attachment_banner) {
2565 gtk_widget_destroy (priv->remove_attachment_banner);
2566 g_object_unref (priv->remove_attachment_banner);
2567 priv->remove_attachment_banner = NULL;
2575 update_window_title (ModestMsgViewWindow *window)
2577 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2579 TnyHeader *header = NULL;
2580 const gchar *subject = NULL;
2582 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2584 header = tny_msg_get_header (msg);
2585 subject = tny_header_get_subject (header);
2586 g_object_unref (msg);
2589 if ((subject == NULL)||(subject[0] == '\0'))
2590 subject = _("mail_va_no_subject");
2592 gtk_window_set_title (GTK_WINDOW (window), subject);