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>
59 #define DEFAULT_FOLDER "MyDocs/.documents"
61 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
62 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
63 static void modest_header_view_observer_init(
64 ModestHeaderViewObserverIface *iface_class);
65 static void modest_msg_view_window_finalize (GObject *obj);
66 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
68 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
69 ModestMsgViewWindow *obj);
70 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
71 ModestMsgViewWindow *obj);
73 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
74 static void modest_msg_view_window_set_zoom (ModestWindow *window,
76 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
77 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
78 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
79 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
82 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
83 GdkEventWindowState *event,
85 static void modest_msg_view_window_scroll_up (ModestWindow *window);
86 static void modest_msg_view_window_scroll_down (ModestWindow *window);
87 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
89 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
90 gboolean show_toolbar);
92 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
94 ModestMsgViewWindow *window);
95 void modest_msg_view_window_on_row_changed(
96 GtkTreeModel *header_model,
99 ModestMsgViewWindow *window);
101 void modest_msg_view_window_on_row_deleted(
102 GtkTreeModel *header_model,
104 ModestMsgViewWindow *window);
106 void modest_msg_view_window_on_row_inserted(
107 GtkTreeModel *header_model,
108 GtkTreePath *tree_path,
109 GtkTreeIter *tree_iter,
110 ModestMsgViewWindow *window);
112 void modest_msg_view_window_on_row_reordered(
113 GtkTreeModel *header_model,
117 ModestMsgViewWindow *window);
119 void modest_msg_view_window_update_model_replaced(
120 ModestHeaderViewObserver *window,
122 const gchar *tny_folder_id);
124 static void cancel_progressbar (GtkToolButton *toolbutton,
125 ModestMsgViewWindow *self);
127 static void on_queue_changed (ModestMailOperationQueue *queue,
128 ModestMailOperation *mail_op,
129 ModestMailOperationQueueNotification type,
130 ModestMsgViewWindow *self);
132 static void on_account_removed (TnyAccountStore *account_store,
136 static void on_move_focus (ModestMsgViewWindow *window,
137 GtkDirectionType direction,
140 static void view_msg_cb (ModestMailOperation *mail_op,
145 static void set_toolbar_mode (ModestMsgViewWindow *self,
146 ModestToolBarModes mode);
148 static void update_window_title (ModestMsgViewWindow *window);
150 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
153 /* list my signals */
159 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
160 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
161 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
164 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
165 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
166 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
167 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
168 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
169 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
170 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
173 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
174 struct _ModestMsgViewWindowPrivate {
177 GtkWidget *main_scroll;
178 GtkWidget *find_toolbar;
181 /* Progress observers */
182 GtkWidget *progress_bar;
183 GSList *progress_widgets;
186 GtkWidget *progress_toolitem;
187 GtkWidget *cancel_toolitem;
188 GtkWidget *prev_toolitem;
189 GtkWidget *next_toolitem;
190 ModestToolBarModes current_toolbar_mode;
192 /* Optimized view enabled */
193 gboolean optimized_view;
195 /* Whether this was created via the *_new_for_search_result() function. */
196 gboolean is_search_result;
198 /* A reference to the @model of the header view
199 * to allow selecting previous/next messages,
200 * if the message is currently selected in the header view.
202 const gchar *header_folder_id;
203 GtkTreeModel *header_model;
204 GtkTreeRowReference *row_reference;
205 GtkTreeRowReference *next_row_reference;
207 gulong clipboard_change_handler;
208 gulong queue_change_handler;
209 gulong account_removed_handler;
210 gulong row_changed_handler;
211 gulong row_deleted_handler;
212 gulong row_inserted_handler;
213 gulong rows_reordered_handler;
216 GtkWidget *remove_attachment_banner;
218 guint progress_bar_timeout;
223 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
224 MODEST_TYPE_MSG_VIEW_WINDOW, \
225 ModestMsgViewWindowPrivate))
227 static GtkWindowClass *parent_class = NULL;
229 /* uncomment the following if you have defined any signals */
230 static guint signals[LAST_SIGNAL] = {0};
233 modest_msg_view_window_get_type (void)
235 static GType my_type = 0;
237 static const GTypeInfo my_info = {
238 sizeof(ModestMsgViewWindowClass),
239 NULL, /* base init */
240 NULL, /* base finalize */
241 (GClassInitFunc) modest_msg_view_window_class_init,
242 NULL, /* class finalize */
243 NULL, /* class data */
244 sizeof(ModestMsgViewWindow),
246 (GInstanceInitFunc) modest_msg_view_window_init,
249 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
250 "ModestMsgViewWindow",
253 static const GInterfaceInfo modest_header_view_observer_info =
255 (GInterfaceInitFunc) modest_header_view_observer_init,
256 NULL, /* interface_finalize */
257 NULL /* interface_data */
260 g_type_add_interface_static (my_type,
261 MODEST_TYPE_HEADER_VIEW_OBSERVER,
262 &modest_header_view_observer_info);
268 save_state (ModestWindow *self)
270 modest_widget_memory_save (modest_runtime_get_conf (),
272 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
277 restore_settings (ModestMsgViewWindow *self)
279 modest_widget_memory_restore (modest_runtime_get_conf (),
281 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
285 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
287 GObjectClass *gobject_class;
288 ModestWindowClass *modest_window_class;
289 gobject_class = (GObjectClass*) klass;
290 modest_window_class = (ModestWindowClass *) klass;
292 parent_class = g_type_class_peek_parent (klass);
293 gobject_class->finalize = modest_msg_view_window_finalize;
295 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
296 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
297 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
298 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
299 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
300 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
302 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
304 modest_window_class->save_state_func = save_state;
306 signals[MSG_CHANGED_SIGNAL] =
307 g_signal_new ("msg-changed",
308 G_TYPE_FROM_CLASS (gobject_class),
310 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
312 modest_marshal_VOID__POINTER_POINTER,
313 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
316 static void modest_header_view_observer_init(
317 ModestHeaderViewObserverIface *iface_class)
319 iface_class->update_func = modest_msg_view_window_update_model_replaced;
323 modest_msg_view_window_init (ModestMsgViewWindow *obj)
325 ModestMsgViewWindowPrivate *priv;
326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
328 priv->is_search_result = FALSE;
330 priv->msg_view = NULL;
331 priv->header_model = NULL;
332 priv->header_folder_id = NULL;
333 priv->clipboard_change_handler = 0;
334 priv->queue_change_handler = 0;
335 priv->account_removed_handler = 0;
336 priv->row_changed_handler = 0;
337 priv->row_deleted_handler = 0;
338 priv->row_inserted_handler = 0;
339 priv->rows_reordered_handler = 0;
340 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
342 priv->optimized_view = FALSE;
343 priv->progress_bar_timeout = 0;
344 priv->purge_timeout = 0;
345 priv->remove_attachment_banner = NULL;
346 priv->msg_uid = NULL;
351 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
353 ModestMsgViewWindowPrivate *priv = NULL;
355 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
359 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
361 if (priv->progress_bar_timeout > 0) {
362 g_source_remove (priv->progress_bar_timeout);
363 priv->progress_bar_timeout = 0;
370 set_toolbar_mode (ModestMsgViewWindow *self,
371 ModestToolBarModes mode)
373 ModestWindowPrivate *parent_priv;
374 ModestMsgViewWindowPrivate *priv;
375 /* GtkWidget *widget = NULL; */
377 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
379 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
380 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
382 /* Sets current toolbar mode */
383 priv->current_toolbar_mode = mode;
385 /* Update toolbar dimming state */
386 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
389 case TOOLBAR_MODE_NORMAL:
390 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
391 /* gtk_action_set_sensitive (widget, TRUE); */
392 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
393 /* gtk_action_set_sensitive (widget, TRUE); */
394 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
395 /* gtk_action_set_sensitive (widget, TRUE); */
396 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
397 /* gtk_action_set_sensitive (widget, TRUE); */
399 if (priv->prev_toolitem)
400 gtk_widget_show (priv->prev_toolitem);
402 if (priv->next_toolitem)
403 gtk_widget_show (priv->next_toolitem);
405 if (priv->progress_toolitem)
406 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
407 if (priv->progress_bar)
408 gtk_widget_hide (priv->progress_bar);
410 if (priv->cancel_toolitem)
411 gtk_widget_hide (priv->cancel_toolitem);
413 /* Hide toolbar if optimized view is enabled */
414 if (priv->optimized_view) {
415 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
416 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
420 case TOOLBAR_MODE_TRANSFER:
421 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
422 /* gtk_action_set_sensitive (widget, FALSE); */
423 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
424 /* gtk_action_set_sensitive (widget, FALSE); */
425 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
426 /* gtk_action_set_sensitive (widget, FALSE); */
427 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
428 /* gtk_action_set_sensitive (widget, FALSE); */
430 if (priv->prev_toolitem)
431 gtk_widget_hide (priv->prev_toolitem);
433 if (priv->next_toolitem)
434 gtk_widget_hide (priv->next_toolitem);
436 if (priv->progress_toolitem)
437 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
438 if (priv->progress_bar)
439 gtk_widget_show (priv->progress_bar);
441 if (priv->cancel_toolitem)
442 gtk_widget_show (priv->cancel_toolitem);
444 /* Show toolbar if it's hiden (optimized view ) */
445 if (priv->optimized_view) {
446 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
447 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
452 g_return_if_reached ();
459 menubar_to_menu (GtkUIManager *ui_manager)
461 GtkWidget *main_menu;
463 GList *iter, *children;
465 /* Create new main menu */
466 main_menu = gtk_menu_new();
468 /* Get the menubar from the UI manager */
469 menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
471 iter = children = gtk_container_get_children (GTK_CONTAINER (menubar));
475 menu = GTK_WIDGET (iter->data);
476 gtk_widget_reparent(menu, main_menu);
478 iter = g_list_next (iter);
480 g_list_free (children);
486 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
488 GtkWidget *main_vbox;
489 ModestMsgViewWindowPrivate *priv;
490 ModestWindowPrivate *parent_priv;
492 GtkAction *action = NULL;
494 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
495 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
497 priv->msg_view = modest_msg_view_new (msg);
498 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
499 main_vbox = gtk_vbox_new (FALSE, 6);
502 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
503 conf = modest_runtime_get_conf ();
504 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
505 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
506 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
507 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
508 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
509 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
510 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
511 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
512 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
513 gtk_widget_show (GTK_WIDGET(parent_priv->menubar));
515 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
516 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
517 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
518 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
520 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
521 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
522 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
524 priv->find_toolbar = hildon_find_toolbar_new (NULL);
525 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
526 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
527 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
528 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
530 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);
531 gtk_widget_show_all (GTK_WIDGET(main_vbox));
535 modest_msg_view_window_disconnect_signals (ModestWindow *self)
537 ModestMsgViewWindowPrivate *priv;
538 ModestHeaderView *header_view = NULL;
539 ModestMainWindow *main_window = NULL;
540 ModestWindowMgr *window_mgr = NULL;
542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
544 if (g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
545 priv->clipboard_change_handler))
546 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
547 priv->clipboard_change_handler);
549 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
550 priv->queue_change_handler))
551 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
552 priv->queue_change_handler);
554 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
555 priv->account_removed_handler))
556 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
557 priv->account_removed_handler);
559 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
560 priv->row_changed_handler))
561 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
562 priv->row_changed_handler);
564 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
565 priv->row_deleted_handler))
566 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
567 priv->row_deleted_handler);
569 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
570 priv->row_inserted_handler))
571 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
572 priv->row_inserted_handler);
574 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
575 priv->rows_reordered_handler))
576 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
577 priv->rows_reordered_handler);
579 window_mgr = modest_runtime_get_window_mgr();
580 g_assert(window_mgr != NULL);
582 main_window = MODEST_MAIN_WINDOW(
583 modest_window_mgr_get_main_window(window_mgr));
585 if(main_window == NULL)
588 header_view = MODEST_HEADER_VIEW(
589 modest_main_window_get_child_widget(
590 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
592 if (header_view == NULL)
595 modest_header_view_remove_observer(header_view,
596 MODEST_HEADER_VIEW_OBSERVER(self));
600 modest_msg_view_window_finalize (GObject *obj)
602 ModestMsgViewWindowPrivate *priv;
604 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
606 /* Sanity check: shouldn't be needed, the window mgr should
607 call this function before */
608 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
610 if (priv->header_model != NULL) {
611 g_object_unref (priv->header_model);
612 priv->header_model = NULL;
615 if (priv->progress_bar_timeout > 0) {
616 g_source_remove (priv->progress_bar_timeout);
617 priv->progress_bar_timeout = 0;
620 if (priv->remove_attachment_banner) {
621 gtk_widget_destroy (priv->remove_attachment_banner);
622 g_object_unref (priv->remove_attachment_banner);
623 priv->remove_attachment_banner = NULL;
626 if (priv->purge_timeout > 0) {
627 g_source_remove (priv->purge_timeout);
628 priv->purge_timeout = 0;
631 if (priv->row_reference) {
632 gtk_tree_row_reference_free (priv->row_reference);
633 priv->row_reference = NULL;
636 if (priv->next_row_reference) {
637 gtk_tree_row_reference_free (priv->next_row_reference);
638 priv->next_row_reference = NULL;
642 g_free (priv->msg_uid);
643 priv->msg_uid = NULL;
646 G_OBJECT_CLASS(parent_class)->finalize (obj);
650 select_next_valid_row (GtkTreeModel *model,
651 GtkTreeRowReference **row_reference,
654 GtkTreeIter tmp_iter;
655 GtkTreePath *path, *next;
656 gboolean retval = FALSE;
658 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
660 path = gtk_tree_row_reference_get_path (*row_reference);
661 gtk_tree_model_get_iter (model, &tmp_iter, path);
662 gtk_tree_row_reference_free (*row_reference);
663 *row_reference = NULL;
665 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
666 next = gtk_tree_model_get_path (model, &tmp_iter);
667 *row_reference = gtk_tree_row_reference_new (model, next);
669 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
670 next = gtk_tree_model_get_path (model, &tmp_iter);
672 /* Ensure that we are not selecting the same */
673 if (gtk_tree_path_compare (path, next) != 0) {
674 *row_reference = gtk_tree_row_reference_new (model, next);
680 gtk_tree_path_free (path);
685 /* TODO: This should be in _init(), with the parameters as properties. */
687 modest_msg_view_window_construct (ModestMsgViewWindow *self, TnyMsg *msg,
688 const gchar *modest_account_name,
689 const gchar *msg_uid)
692 ModestMsgViewWindowPrivate *priv = NULL;
693 ModestWindowPrivate *parent_priv = NULL;
694 ModestDimmingRulesGroup *menu_rules_group = NULL;
695 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
696 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
697 GtkActionGroup *action_group = NULL;
698 GError *error = NULL;
699 GdkPixbuf *window_icon;
701 g_return_if_fail (msg);
703 obj = G_OBJECT (self);
704 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
705 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
707 priv->msg_uid = g_strdup (msg_uid);
709 parent_priv->ui_manager = gtk_ui_manager_new();
710 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
712 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
713 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
715 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
716 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
717 clipboard_rules_group = modest_dimming_rules_group_new ("ModestClipboardDimmingRules", FALSE);
719 /* Add common actions */
720 gtk_action_group_add_actions (action_group,
721 modest_action_entries,
722 G_N_ELEMENTS (modest_action_entries),
724 gtk_action_group_add_toggle_actions (action_group,
725 modest_toggle_action_entries,
726 G_N_ELEMENTS (modest_toggle_action_entries),
728 gtk_action_group_add_toggle_actions (action_group,
729 msg_view_toggle_action_entries,
730 G_N_ELEMENTS (msg_view_toggle_action_entries),
732 gtk_action_group_add_radio_actions (action_group,
733 msg_view_zoom_action_entries,
734 G_N_ELEMENTS (msg_view_zoom_action_entries),
736 G_CALLBACK (modest_ui_actions_on_change_zoom),
739 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
740 g_object_unref (action_group);
742 /* Load the UI definition */
743 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
746 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
747 g_error_free (error);
752 /* Add common dimming rules */
753 modest_dimming_rules_group_add_rules (menu_rules_group,
754 modest_msg_view_menu_dimming_entries,
755 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
757 modest_dimming_rules_group_add_rules (toolbar_rules_group,
758 modest_msg_view_toolbar_dimming_entries,
759 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
761 modest_dimming_rules_group_add_rules (clipboard_rules_group,
762 modest_msg_view_clipboard_dimming_entries,
763 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
766 /* Insert dimming rules group for this window */
767 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
768 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
769 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
770 g_object_unref (menu_rules_group);
771 g_object_unref (toolbar_rules_group);
772 g_object_unref (clipboard_rules_group);
774 /* Add accelerators */
775 gtk_window_add_accel_group (GTK_WINDOW (obj),
776 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
779 init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
780 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
782 /* Set window icon */
783 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
785 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
786 g_object_unref (window_icon);
789 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
791 g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
792 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
793 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
794 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
795 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
796 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
797 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
798 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
799 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
800 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
802 g_signal_connect (G_OBJECT (obj), "key-release-event",
803 G_CALLBACK (modest_msg_view_window_key_release_event),
806 g_signal_connect (G_OBJECT (obj), "window-state-event",
807 G_CALLBACK (modest_msg_view_window_window_state_event),
810 g_signal_connect (G_OBJECT (obj), "move-focus",
811 G_CALLBACK (on_move_focus), obj);
813 /* Mail Operation Queue */
814 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
816 G_CALLBACK (on_queue_changed),
819 /* Account manager */
820 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
822 G_CALLBACK(on_account_removed),
825 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
827 priv->last_search = NULL;
829 /* Init the clipboard actions dim status */
830 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
832 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
834 /* Check toolbar dimming rules */
835 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
836 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
841 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
842 const gchar *modest_account_name,
843 const gchar *msg_uid,
845 GtkTreeRowReference *row_reference)
847 ModestMsgViewWindow *window = NULL;
848 ModestMsgViewWindowPrivate *priv = NULL;
849 TnyFolder *header_folder = NULL;
850 ModestHeaderView *header_view = NULL;
851 ModestMainWindow *main_window = NULL;
852 ModestWindowMgr *window_mgr = NULL;
854 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
855 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
856 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
858 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
860 /* Remember the message list's TreeModel so we can detect changes
861 * and change the list selection when necessary: */
862 window_mgr = modest_runtime_get_window_mgr();
863 g_assert(window_mgr != NULL);
864 main_window = MODEST_MAIN_WINDOW(
865 modest_window_mgr_get_main_window(window_mgr));
866 g_assert(main_window != NULL);
867 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
868 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
869 if (header_view != NULL){
870 header_folder = modest_header_view_get_folder(header_view);
871 g_assert(header_folder != NULL);
872 priv->header_folder_id = tny_folder_get_id(header_folder);
873 g_assert(priv->header_folder_id != NULL);
874 g_object_unref(header_folder);
877 priv->header_model = g_object_ref(model);
878 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
879 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
880 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
882 priv->row_changed_handler = g_signal_connect(
883 GTK_TREE_MODEL(model), "row-changed",
884 G_CALLBACK(modest_msg_view_window_on_row_changed),
886 priv->row_deleted_handler = g_signal_connect(
887 GTK_TREE_MODEL(model), "row-deleted",
888 G_CALLBACK(modest_msg_view_window_on_row_deleted),
890 priv->row_inserted_handler = g_signal_connect (
891 GTK_TREE_MODEL(model), "row-inserted",
892 G_CALLBACK(modest_msg_view_window_on_row_inserted),
894 priv->rows_reordered_handler = g_signal_connect(
895 GTK_TREE_MODEL(model), "rows-reordered",
896 G_CALLBACK(modest_msg_view_window_on_row_reordered),
899 if (header_view != NULL){
900 modest_header_view_add_observer(header_view,
901 MODEST_HEADER_VIEW_OBSERVER(window));
904 modest_msg_view_window_update_priority (window);
906 /* Check toolbar dimming rules */
907 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
909 return MODEST_WINDOW(window);
913 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
914 const gchar *modest_account_name,
915 const gchar *msg_uid)
917 ModestMsgViewWindow *window = NULL;
918 ModestMsgViewWindowPrivate *priv = NULL;
920 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
921 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
922 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
924 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
926 /* Remember that this is a search result,
927 * so we can disable some UI appropriately: */
928 priv->is_search_result = TRUE;
930 return MODEST_WINDOW(window);
934 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
935 const gchar *modest_account_name,
936 const gchar *msg_uid)
939 g_return_val_if_fail (msg, NULL);
941 obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
942 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
943 msg, modest_account_name, msg_uid);
945 return MODEST_WINDOW(obj);
948 void modest_msg_view_window_on_row_changed(
949 GtkTreeModel *header_model,
952 ModestMsgViewWindow *window){
953 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
956 void modest_msg_view_window_on_row_deleted(
957 GtkTreeModel *header_model,
959 ModestMsgViewWindow *window){
960 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
963 /* On insertions we check if the folder still has the message we are
964 * showing or do not. If do not, we do nothing. Which means we are still
965 * not attached to any header folder and thus next/prev buttons are
966 * still dimmed. Once the message that is shown by msg-view is found, the
967 * new model of header-view will be attached and the references will be set.
968 * On each further insertions dimming rules will be checked. However
969 * this requires extra CPU time at least works.
970 * (An message might be deleted from TnyFolder and thus will not be
971 * inserted into the model again for example if it is removed by the
972 * imap server and the header view is refreshed.)
974 void modest_msg_view_window_on_row_inserted(
975 GtkTreeModel *new_model,
976 GtkTreePath *tree_path,
977 GtkTreeIter *tree_iter,
978 ModestMsgViewWindow *window){
979 ModestMsgViewWindowPrivate *priv = NULL;
980 TnyHeader *header = NULL;
983 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
985 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
987 /* If we already has a model attached then the message shown by
988 * msg-view is in it, and thus we do not need any actions but
989 * to check the dimming rules.*/
990 if(priv->header_model != NULL){
991 gtk_tree_row_reference_free(priv->next_row_reference);
992 priv->next_row_reference = gtk_tree_row_reference_copy(
993 priv->row_reference);
994 select_next_valid_row (priv->header_model,
995 &(priv->next_row_reference), FALSE);
996 modest_ui_actions_check_toolbar_dimming_rules (
997 MODEST_WINDOW (window));
1001 /* Check if the newly inserted message is the same we are actually
1002 * showing. IF not, we should remain detached from the header model
1003 * and thus prev and next toolbarbuttons should remain dimmed. */
1004 gtk_tree_model_get (new_model, tree_iter,
1005 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1006 uid = modest_tny_folder_get_header_unique_id(header);
1007 if(!g_str_equal(priv->msg_uid, uid)){
1013 /* Setup row_reference for the actual msg. */
1014 priv->row_reference = gtk_tree_row_reference_new(
1015 new_model, tree_path);
1016 if(priv->row_reference == NULL){
1017 g_warning("No reference for msg header item.");
1021 /* Attach new_model and connect some callback to it to become able
1022 * to detect changes in header-view. */
1023 priv->header_model = g_object_ref(new_model);
1024 g_signal_connect (new_model, "row-changed",
1025 G_CALLBACK (modest_msg_view_window_on_row_changed),
1027 g_signal_connect (new_model, "row-deleted",
1028 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1030 g_signal_connect (new_model, "rows-reordered",
1031 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1034 /* Now set up next_row_reference. */
1035 priv->next_row_reference = gtk_tree_row_reference_copy(
1036 priv->row_reference);
1037 select_next_valid_row (priv->header_model,
1038 &(priv->next_row_reference), FALSE);
1040 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1043 void modest_msg_view_window_on_row_reordered(
1044 GtkTreeModel *header_model,
1048 ModestMsgViewWindow *window){
1049 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1052 /* The modest_msg_view_window_update_model_replaced implements update
1053 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1054 * actually belongs to the header-view is the same as the TnyFolder of
1055 * the message of msg-view or not. If they are different, there is
1056 * nothing to do. If they are the same, then the model has replaced and
1057 * the reference in msg-view shall be replaced from the old model to
1058 * the new model. In this case the view will be detached from it's
1059 * header folder. From this point the next/prev buttons are dimmed.
1061 void modest_msg_view_window_update_model_replaced(
1062 ModestHeaderViewObserver *observer,
1063 GtkTreeModel *model,
1064 const gchar *tny_folder_id){
1065 ModestMsgViewWindowPrivate *priv = NULL;
1066 ModestMsgViewWindow *window = NULL;
1068 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1069 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1071 window = MODEST_MSG_VIEW_WINDOW(observer);
1072 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1074 /* If there is an other folder in the header-view then we do
1075 * not care about it's model (msg list). Else if the
1076 * header-view shows the folder the msg shown by us is in, we
1077 * shall replace our model reference and make some check. */
1078 if(tny_folder_id == NULL ||
1079 !g_str_equal(tny_folder_id, priv->header_folder_id))
1082 /* Model is changed(replaced), so we should forget the old
1083 * one. Because there might be other references and there
1084 * might be some change on the model even if we unreferenced
1085 * it, we need to disconnect our signals here. */
1086 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1087 priv->row_changed_handler))
1088 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1089 priv->row_changed_handler);
1090 priv->row_changed_handler = 0;
1091 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1092 priv->row_deleted_handler))
1093 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1094 priv->row_deleted_handler);
1095 priv->row_deleted_handler = 0;
1096 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1097 priv->row_inserted_handler))
1098 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1099 priv->row_inserted_handler);
1100 priv->row_inserted_handler = 0;
1101 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1102 priv->rows_reordered_handler))
1103 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1104 priv->rows_reordered_handler);
1105 priv->rows_reordered_handler = 0;
1106 g_object_unref(priv->header_model);
1107 priv->header_model = NULL;
1108 g_object_unref(priv->row_reference);
1109 priv->row_reference = NULL;
1110 g_object_unref(priv->next_row_reference);
1111 priv->next_row_reference = NULL;
1113 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1115 if(tny_folder_id == NULL)
1118 g_assert(model != NULL);
1120 /* Also we must connect to the new model for row insertions.
1121 * Only for insertions now. We will need other ones only after
1122 * the msg is show by msg-view is added to the new model. */
1123 priv->row_inserted_handler = g_signal_connect (
1124 model, "row-inserted",
1125 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1130 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1132 ModestMsgViewWindowPrivate *priv= NULL;
1134 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1135 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1137 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1141 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1143 ModestMsgViewWindowPrivate *priv= NULL;
1145 TnyHeader *header = NULL;
1146 GtkTreePath *path = NULL;
1149 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1150 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1152 /* If the message was not obtained from a treemodel,
1153 * for instance if it was opened directly by the search UI:
1155 if (priv->header_model == NULL) {
1156 msg = modest_msg_view_window_get_message (self);
1157 header = tny_msg_get_header (msg);
1158 g_object_unref (msg);
1162 /* Get iter of the currently selected message in the header view: */
1163 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1164 * instead of sometimes retrieving it from the header view?
1165 * Then we wouldn't be dependent on the message actually still being selected
1166 * in the header view. murrayc. */
1167 path = gtk_tree_row_reference_get_path (priv->row_reference);
1168 g_return_val_if_fail (path != NULL, NULL);
1169 gtk_tree_model_get_iter (priv->header_model,
1173 /* Get current message header */
1174 gtk_tree_model_get (priv->header_model, &iter,
1175 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1178 gtk_tree_path_free (path);
1183 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1185 ModestMsgView *msg_view;
1186 ModestMsgViewWindowPrivate *priv;
1188 g_return_val_if_fail (self, NULL);
1190 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1192 msg_view = MODEST_MSG_VIEW (priv->msg_view);
1194 return modest_msg_view_get_message (msg_view);
1198 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1200 ModestMsgViewWindowPrivate *priv;
1202 g_return_val_if_fail (self, NULL);
1204 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1206 return (const gchar*) priv->msg_uid;
1210 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1213 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1214 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1215 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1219 is_active = gtk_toggle_action_get_active (toggle);
1222 gtk_widget_show (priv->find_toolbar);
1223 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1225 gtk_widget_hide (priv->find_toolbar);
1228 /* update the toggle buttons status */
1229 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1230 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1231 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1232 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1237 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1238 ModestMsgViewWindow *obj)
1240 GtkToggleAction *toggle;
1241 ModestWindowPrivate *parent_priv;
1242 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1244 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1245 gtk_toggle_action_set_active (toggle, FALSE);
1249 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1250 ModestMsgViewWindow *obj)
1252 gchar *current_search;
1253 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1255 if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
1256 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1260 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1262 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1263 g_free (current_search);
1264 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1268 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1270 g_free (priv->last_search);
1271 priv->last_search = g_strdup (current_search);
1272 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
1275 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1276 g_free (priv->last_search);
1277 priv->last_search = NULL;
1280 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
1281 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1282 g_free (priv->last_search);
1283 priv->last_search = NULL;
1287 g_free (current_search);
1292 modest_msg_view_window_set_zoom (ModestWindow *window,
1295 ModestMsgViewWindowPrivate *priv;
1296 ModestWindowPrivate *parent_priv;
1297 GtkAction *action = NULL;
1298 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1300 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1302 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1303 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1304 modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
1306 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1307 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1309 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1313 modest_msg_view_window_get_zoom (ModestWindow *window)
1315 ModestMsgViewWindowPrivate *priv;
1317 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1319 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1320 return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
1324 modest_msg_view_window_zoom_plus (ModestWindow *window)
1326 ModestWindowPrivate *parent_priv;
1327 GtkRadioAction *zoom_radio_action;
1328 GSList *group, *node;
1330 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1331 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1332 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1334 group = gtk_radio_action_get_group (zoom_radio_action);
1336 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1337 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1341 for (node = group; node != NULL; node = g_slist_next (node)) {
1342 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1343 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1351 modest_msg_view_window_zoom_minus (ModestWindow *window)
1353 ModestWindowPrivate *parent_priv;
1354 GtkRadioAction *zoom_radio_action;
1355 GSList *group, *node;
1357 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1358 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1359 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1361 group = gtk_radio_action_get_group (zoom_radio_action);
1363 for (node = group; node != NULL; node = g_slist_next (node)) {
1364 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1365 if (node->next != NULL) {
1366 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1369 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1379 modest_msg_view_window_key_release_event (GtkWidget *window,
1383 if (event->type == GDK_KEY_RELEASE) {
1384 switch (event->keyval) {
1386 modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1390 modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1403 modest_msg_view_window_scroll_up (ModestWindow *window)
1405 ModestMsgViewWindowPrivate *priv;
1406 gboolean return_value;
1408 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1409 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1413 modest_msg_view_window_scroll_down (ModestWindow *window)
1415 ModestMsgViewWindowPrivate *priv;
1416 gboolean return_value;
1418 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1419 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1423 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1426 ModestMsgViewWindowPrivate *priv;
1427 GtkTreeIter tmp_iter;
1428 gboolean is_last_selected;
1430 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1431 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1433 /*if no model (so no rows at all), then virtually we are the last*/
1434 if (!priv->header_model)
1437 path = gtk_tree_row_reference_get_path (priv->row_reference);
1441 is_last_selected = TRUE;
1442 while (is_last_selected) {
1444 gtk_tree_path_next (path);
1445 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1447 gtk_tree_model_get (priv->header_model, &tmp_iter,
1448 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1450 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1451 is_last_selected = FALSE;
1453 gtk_tree_path_free (path);
1454 return is_last_selected;
1458 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1460 ModestMsgViewWindowPrivate *priv;
1462 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1463 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1465 return priv->header_model != NULL;
1469 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1471 ModestMsgViewWindowPrivate *priv;
1473 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1474 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1476 return priv->is_search_result;
1480 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1483 ModestMsgViewWindowPrivate *priv;
1484 gboolean is_first_selected;
1485 GtkTreeIter tmp_iter;
1486 /* gchar * path_string;*/
1488 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1491 /*if no model (so no rows at all), then virtually we are the first*/
1492 if (!priv->header_model)
1495 path = gtk_tree_row_reference_get_path (priv->row_reference);
1499 /* path_string = gtk_tree_path_to_string (path);
1500 is_first_selected = strcmp (path_string, "0");
1502 g_free (path_string);
1503 gtk_tree_path_free (path);
1505 return is_first_selected;*/
1507 is_first_selected = TRUE;
1508 while (is_first_selected) {
1510 if(!gtk_tree_path_prev (path))
1512 /* Here the 'if' is needless for logic, but let make sure
1513 * iter is valid for gtk_tree_model_get. */
1514 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1516 gtk_tree_model_get (priv->header_model, &tmp_iter,
1517 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1519 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1520 is_first_selected = FALSE;
1522 gtk_tree_path_free (path);
1523 return is_first_selected;
1527 * Reads the message whose summary item is @header. It takes care of
1528 * several things, among others:
1530 * If the message was not previously downloaded then ask the user
1531 * before downloading. If there is no connection launch the connection
1532 * dialog. Update toolbar dimming rules.
1534 * Returns: TRUE if the mail operation was started, otherwise if the
1535 * user do not want to download the message, or if the user do not
1536 * want to connect, then the operation is not issued
1539 message_reader (ModestMsgViewWindow *window,
1540 ModestMsgViewWindowPrivate *priv,
1544 ModestMailOperation *mail_op = NULL;
1545 ModestMailOperationTypeOperation op_type;
1546 gboolean already_showing = FALSE;
1547 ModestWindow *msg_window = NULL;
1548 ModestWindowMgr *mgr;
1550 g_return_val_if_fail (path != NULL, FALSE);
1552 mgr = modest_runtime_get_window_mgr ();
1553 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1554 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1557 gtk_window_present (GTK_WINDOW (msg_window));
1558 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1562 /* Msg download completed */
1563 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1564 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1566 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1568 /* Ask the user if he wants to download the message if
1570 if (!tny_device_is_online (modest_runtime_get_device())) {
1571 TnyFolder *folder = NULL;
1572 GtkResponseType response;
1574 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1575 _("mcen_nc_get_msg"));
1576 if (response == GTK_RESPONSE_CANCEL)
1579 /* Offer the connection dialog if necessary */
1580 folder = tny_header_get_folder (header);
1582 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1583 TNY_FOLDER_STORE (folder))) {
1584 g_object_unref (folder);
1587 g_object_unref (folder);
1592 /* New mail operation */
1593 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1595 modest_ui_actions_get_msgs_full_error_handler,
1598 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1599 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1600 g_object_unref (mail_op);
1602 /* Update toolbar dimming rules */
1603 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1609 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1611 ModestMsgViewWindowPrivate *priv;
1612 GtkTreePath *path= NULL;
1613 GtkTreeIter tmp_iter;
1615 gboolean retval = TRUE;
1617 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1618 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1620 /* Update the next row reference if it's not valid. This could
1621 happen if for example the header which it was pointing to,
1622 was deleted. The best place to do it is in the row-deleted
1623 handler but the tinymail model do not work like the glib
1624 tree models and reports the deletion when the row is still
1626 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1627 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1628 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1629 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1632 if (priv->next_row_reference)
1633 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1637 gtk_tree_model_get_iter (priv->header_model,
1641 gtk_tree_model_get (priv->header_model, &tmp_iter,
1642 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1645 /* Read the message & show it */
1646 if (!message_reader (window, priv, header, path)) {
1648 gtk_tree_path_free (path);
1652 g_object_unref (header);
1658 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1660 ModestMsgViewWindowPrivate *priv = NULL;
1661 TnyHeader *header = NULL;
1665 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1666 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1668 /* Check that the model is not empty */
1669 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1672 /* Get the header */
1673 gtk_tree_model_get (priv->header_model,
1675 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1677 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1678 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1679 g_object_unref (header);
1680 return modest_msg_view_window_select_next_message (self);
1683 path = gtk_tree_model_get_path (priv->header_model, &iter);
1685 /* Read the message & show it */
1686 message_reader (self, priv, header, path);
1689 g_object_unref (header);
1695 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1697 ModestMsgViewWindowPrivate *priv = NULL;
1700 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1701 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1703 /* Return inmediatly if there is no header model */
1704 if (!priv->header_model)
1707 path = gtk_tree_row_reference_get_path (priv->row_reference);
1708 while (gtk_tree_path_prev (path)) {
1712 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1713 gtk_tree_model_get (priv->header_model, &iter,
1714 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1718 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1719 g_object_unref (header);
1723 /* Read the message & show it */
1724 if (!message_reader (window, priv, header, path)) {
1725 g_object_unref (header);
1729 g_object_unref (header);
1734 gtk_tree_path_free (path);
1739 view_msg_cb (ModestMailOperation *mail_op,
1744 ModestMsgViewWindow *self = NULL;
1745 ModestMsgViewWindowPrivate *priv = NULL;
1748 /* If there was any error */
1749 path = (GtkTreePath *) user_data;
1750 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1751 gtk_tree_path_free (path);
1755 /* Get the window */
1756 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1757 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1758 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1760 /* Update the row reference */
1761 gtk_tree_row_reference_free (priv->row_reference);
1762 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1763 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1764 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1765 gtk_tree_path_free (path);
1767 /* Mark header as read */
1768 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1769 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1771 /* Set new message */
1772 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1773 modest_msg_view_window_update_priority (self);
1774 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1775 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1777 /* Set the new message uid of the window */
1778 if (priv->msg_uid) {
1779 g_free (priv->msg_uid);
1780 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1783 /* Notify the observers */
1784 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1785 0, priv->header_model, priv->row_reference);
1787 /* Free new references */
1788 g_object_unref (self);
1792 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1794 ModestMsgViewWindowPrivate *priv;
1796 TnyFolderType folder_type;
1798 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1800 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1802 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1806 folder = tny_msg_get_folder (msg);
1809 folder_type = tny_folder_get_folder_type (folder);
1811 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1812 const gchar *fname = tny_folder_get_name (folder);
1813 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1816 g_object_unref (folder);
1818 g_object_unref (msg);
1826 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1828 ModestMsgViewWindowPrivate *priv;
1829 TnyHeaderFlags flags = 0;
1831 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1833 if (priv->header_model) {
1836 GtkTreePath *path = NULL;
1838 path = gtk_tree_row_reference_get_path (priv->row_reference);
1839 g_return_if_fail (path != NULL);
1840 gtk_tree_model_get_iter (priv->header_model,
1842 gtk_tree_row_reference_get_path (priv->row_reference));
1844 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1846 flags = tny_header_get_flags (header);
1847 gtk_tree_path_free (path);
1850 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1855 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1857 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1858 ModestWindowPrivate *parent_priv;
1859 ModestWindowMgr *mgr;
1860 gboolean is_fullscreen;
1861 GtkAction *fs_toggle_action;
1864 mgr = modest_runtime_get_window_mgr ();
1865 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1867 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1869 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1870 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1871 if (is_fullscreen != active) {
1872 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1881 set_homogeneous (GtkWidget *widget,
1884 if (GTK_IS_TOOL_ITEM (widget)) {
1885 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1886 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1891 modest_msg_view_window_show_toolbar (ModestWindow *self,
1892 gboolean show_toolbar)
1894 ModestMsgViewWindowPrivate *priv = NULL;
1895 ModestWindowPrivate *parent_priv;
1896 GtkWidget *reply_button = NULL, *menu = NULL;
1897 GtkWidget *placeholder = NULL;
1899 const gchar *action_name;
1902 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1903 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1905 /* Set optimized view status */
1906 priv->optimized_view = !show_toolbar;
1908 if (!parent_priv->toolbar) {
1909 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1911 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1913 /* Set homogeneous toolbar */
1914 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1915 set_homogeneous, NULL);
1917 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1918 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1919 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1920 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1921 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1922 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1923 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1924 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1926 /* Add ProgressBar (Transfer toolbar) */
1927 priv->progress_bar = modest_progress_bar_widget_new ();
1928 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1929 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1930 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1931 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1932 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1934 /* Connect cancel 'clicked' signal to abort progress mode */
1935 g_signal_connect(priv->cancel_toolitem, "clicked",
1936 G_CALLBACK(cancel_progressbar),
1939 /* Add it to the observers list */
1940 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1943 hildon_window_add_toolbar (HILDON_WINDOW (self),
1944 GTK_TOOLBAR (parent_priv->toolbar));
1947 /* Set reply button tap and hold menu */
1948 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1949 "/ToolBar/ToolbarMessageReply");
1950 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1951 "/ToolbarReplyCSM");
1952 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1956 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1957 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1958 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1960 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1961 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1964 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1965 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1968 /* Update also the actions (to update the toggles in the
1969 menus), we have to do it manually because some other window
1970 of the same time could have changed it (remember that the
1971 toolbar fullscreen mode is shared by all the windows of the
1973 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
1974 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
1976 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
1978 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
1979 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
1984 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1986 ModestMsgViewWindow *window)
1988 if (!GTK_WIDGET_VISIBLE (window))
1991 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
1995 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1997 ModestMsgViewWindowPrivate *priv;
1999 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2000 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2002 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2006 cancel_progressbar (GtkToolButton *toolbutton,
2007 ModestMsgViewWindow *self)
2010 ModestMsgViewWindowPrivate *priv;
2012 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2014 /* Get operation observers and cancel its current operation */
2015 tmp = priv->progress_widgets;
2017 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2018 tmp=g_slist_next(tmp);
2022 observers_empty (ModestMsgViewWindow *self)
2025 ModestMsgViewWindowPrivate *priv;
2026 gboolean is_empty = TRUE;
2027 guint pending_ops = 0;
2029 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2030 tmp = priv->progress_widgets;
2032 /* Check all observers */
2033 while (tmp && is_empty) {
2034 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2035 is_empty = pending_ops == 0;
2037 tmp = g_slist_next(tmp);
2044 on_account_removed (TnyAccountStore *account_store,
2045 TnyAccount *account,
2048 /* Do nothing if it's a transport account, because we only
2049 show the messages of a store account */
2050 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2051 const gchar *parent_acc = NULL;
2052 const gchar *our_acc = NULL;
2054 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2055 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2057 /* Close this window if I'm showing a message of the removed account */
2058 if (strcmp (parent_acc, our_acc) == 0)
2059 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2064 on_queue_changed (ModestMailOperationQueue *queue,
2065 ModestMailOperation *mail_op,
2066 ModestMailOperationQueueNotification type,
2067 ModestMsgViewWindow *self)
2070 ModestMsgViewWindowPrivate *priv;
2071 ModestMailOperationTypeOperation op_type;
2072 ModestToolBarModes mode;
2074 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2075 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2077 /* If this operations was created by another window, do nothing */
2078 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2081 /* Get toolbar mode from operation id*/
2082 op_type = modest_mail_operation_get_type_operation (mail_op);
2084 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
2085 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2086 case MODEST_MAIL_OPERATION_TYPE_OPEN:
2087 mode = TOOLBAR_MODE_TRANSFER;
2090 mode = TOOLBAR_MODE_NORMAL;
2094 /* Add operation observers and change toolbar if neccessary*/
2095 tmp = priv->progress_widgets;
2097 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2098 if (mode == TOOLBAR_MODE_TRANSFER) {
2099 /* Enable transfer toolbar mode */
2100 set_toolbar_transfer_mode(self);
2102 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2104 tmp = g_slist_next (tmp);
2109 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2110 if (mode == TOOLBAR_MODE_TRANSFER) {
2112 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2114 tmp = g_slist_next (tmp);
2118 /* If no more operations are being observed, NORMAL mode is enabled again */
2119 if (observers_empty (self)) {
2120 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2128 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2130 ModestMsgViewWindowPrivate *priv;
2131 GList *selected_attachments = NULL;
2133 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2134 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2136 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2138 return selected_attachments;
2142 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2144 ModestMsgViewWindowPrivate *priv;
2145 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2146 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2148 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2150 if (mime_part == NULL) {
2151 gboolean error = FALSE;
2152 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2153 if (selected_attachments == NULL) {
2155 } else if (g_list_length (selected_attachments) > 1) {
2156 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2159 mime_part = (TnyMimePart *) selected_attachments->data;
2160 g_object_ref (mime_part);
2162 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2163 g_list_free (selected_attachments);
2168 g_object_ref (mime_part);
2171 if (tny_mime_part_is_purged (mime_part)) {
2172 g_object_unref (mime_part);
2176 if (!TNY_IS_MSG (mime_part)) {
2177 gchar *filepath = NULL;
2178 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2179 TnyFsStream *temp_stream = NULL;
2181 temp_stream = modest_maemo_utils_create_temp_stream (att_filename, &filepath);
2184 const gchar *content_type;
2185 content_type = tny_mime_part_get_content_type (mime_part);
2186 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2188 modest_platform_activate_file (filepath, content_type);
2189 g_object_unref (temp_stream);
2191 /* NOTE: files in the temporary area will be automatically
2192 * cleaned after some time if they are no longer in use */
2195 /* message attachment */
2196 TnyHeader *header = NULL;
2197 ModestWindowMgr *mgr;
2198 ModestWindow *msg_win = NULL;
2201 header = tny_msg_get_header (TNY_MSG (mime_part));
2202 mgr = modest_runtime_get_window_mgr ();
2203 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2206 if (msg_win) /* there is already a window for this uid; top it */
2207 gtk_window_present (GTK_WINDOW(msg_win));
2209 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2210 * thus, we don't do anything */
2211 g_warning ("window for is already being created");
2213 /* it's not found, so create a new window for it */
2214 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
2215 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2217 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2218 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
2219 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2220 modest_window_get_zoom (MODEST_WINDOW (window)));
2221 modest_window_mgr_register_window (mgr, msg_win);
2222 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2223 gtk_widget_show_all (GTK_WIDGET (msg_win));
2226 g_object_unref (mime_part);
2242 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2243 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2244 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2245 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2248 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2252 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2253 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2254 g_free (pair->filename);
2255 g_object_unref (pair->part);
2256 g_slice_free (SaveMimePartPair, pair);
2258 g_list_free (info->pairs);
2261 gtk_widget_destroy (info->banner);
2262 g_object_unref (info->banner);
2263 g_slice_free (SaveMimePartInfo, info);
2268 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2270 if (info->pairs != NULL) {
2271 save_mime_part_to_file (info);
2274 result = info->result;
2276 /* This is a GDK lock because we are an idle callback and
2277 * hildon_banner_show_information is or does Gtk+ code */
2279 gdk_threads_enter (); /* CHECKED */
2280 save_mime_part_info_free (info, TRUE);
2282 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2284 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2286 gdk_threads_leave (); /* CHECKED */
2293 save_mime_part_to_file (SaveMimePartInfo *info)
2295 GnomeVFSResult result;
2296 GnomeVFSHandle *handle;
2298 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2300 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2301 if (result == GNOME_VFS_OK) {
2302 stream = tny_vfs_stream_new (handle);
2303 tny_mime_part_decode_to_stream (pair->part, stream);
2304 g_object_unref (G_OBJECT (stream));
2305 g_object_unref (pair->part);
2306 g_slice_free (SaveMimePartPair, pair);
2307 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2308 info->result = TRUE;
2310 save_mime_part_info_free (info, FALSE);
2311 info->result = FALSE;
2314 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2319 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2321 gboolean is_ok = TRUE;
2322 gint replaced_files = 0;
2323 const GList *files = info->pairs;
2326 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2327 SaveMimePartPair *pair = iter->data;
2328 if (modest_maemo_utils_file_exists (pair->filename)) {
2332 if (replaced_files) {
2333 GtkWidget *confirm_overwrite_dialog;
2334 const gchar *message = (replaced_files == 1) ?
2335 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2336 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2337 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2340 gtk_widget_destroy (confirm_overwrite_dialog);
2344 save_mime_part_info_free (info, TRUE);
2346 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2353 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2355 gboolean clean_list = FALSE;
2356 ModestMsgViewWindowPrivate *priv;
2357 GList *files_to_save = NULL;
2358 GtkWidget *save_dialog = NULL;
2359 gchar *folder = NULL;
2360 gboolean canceled = FALSE;
2361 const gchar *filename = NULL;
2362 gchar *save_multiple_str = NULL;
2364 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2365 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2367 if (mime_parts == NULL) {
2368 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2369 if (mime_parts == NULL)
2374 /* prepare dialog */
2375 if (mime_parts->next == NULL) {
2376 /* only one attachment selected */
2377 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2378 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2379 filename = tny_mime_part_get_filename (mime_part);
2381 g_warning ("Tried to save a non-file attachment");
2385 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2386 g_list_length (mime_parts));
2389 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2390 GTK_FILE_CHOOSER_ACTION_SAVE);
2393 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2394 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2398 if (filename != NULL)
2399 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2402 /* if multiple, set multiple string */
2403 if (save_multiple_str) {
2404 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2408 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2409 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2411 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2412 hildon_banner_show_information
2413 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2417 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2418 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2420 if (tny_mime_part_is_attachment (mime_part)) {
2421 SaveMimePartPair *pair;
2423 if ((mime_parts->next != NULL) &&
2424 (tny_mime_part_get_filename (mime_part) == NULL))
2427 pair = g_slice_new0 (SaveMimePartPair);
2428 if (mime_parts->next == NULL) {
2429 pair->filename = g_strdup (chooser_uri);
2432 g_build_filename (chooser_uri,
2433 tny_mime_part_get_filename (mime_part), NULL);
2435 pair->part = g_object_ref (mime_part);
2436 files_to_save = g_list_prepend (files_to_save, pair);
2440 g_free (chooser_uri);
2443 gtk_widget_destroy (save_dialog);
2446 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2447 g_list_free (mime_parts);
2450 if (files_to_save != NULL) {
2451 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2452 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2453 _CS("sfil_ib_saving"));
2454 info->pairs = files_to_save;
2455 info->banner = banner;
2456 info->result = TRUE;
2457 g_object_ref (banner);
2458 save_mime_parts_to_file_with_checks (info);
2463 show_remove_attachment_information (gpointer userdata)
2465 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2466 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2468 if (priv->remove_attachment_banner != NULL) {
2469 gtk_widget_destroy (priv->remove_attachment_banner);
2470 g_object_unref (priv->remove_attachment_banner);
2473 priv->remove_attachment_banner = g_object_ref (
2474 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2480 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2482 ModestMsgViewWindowPrivate *priv;
2483 GList *mime_parts = NULL, *node;
2484 gchar *confirmation_message;
2488 /* TnyFolder *folder; */
2490 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2494 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2496 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2498 /* Remove already purged messages from mime parts list */
2500 while (node != NULL) {
2501 TnyMimePart *part = TNY_MIME_PART (node->data);
2502 if (tny_mime_part_is_purged (part)) {
2503 GList *deleted_node = node;
2504 node = g_list_next (node);
2505 g_object_unref (part);
2506 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2508 node = g_list_next (node);
2512 if (mime_parts == NULL)
2515 n_attachments = g_list_length (mime_parts);
2516 if (n_attachments == 1) {
2517 const gchar *filename;
2519 if (TNY_IS_MSG (mime_parts->data)) {
2521 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2522 filename = tny_header_get_subject (header);
2523 g_object_unref (header);
2524 if (filename == NULL)
2525 filename = _("mail_va_no_subject");
2527 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2529 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2531 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2532 "mcen_nc_purge_files_text",
2533 n_attachments), n_attachments);
2535 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2536 confirmation_message);
2537 g_free (confirmation_message);
2539 if (response != GTK_RESPONSE_OK)
2542 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2543 /* folder = tny_msg_get_folder (msg); */
2544 /* tny_msg_uncache_attachments (msg); */
2545 /* tny_folder_refresh (folder, NULL); */
2546 /* g_object_unref (folder); */
2548 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2549 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2550 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2553 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2554 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2555 tny_msg_rewrite_cache (msg);
2556 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2558 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2559 g_list_free (mime_parts);
2561 if (priv->purge_timeout > 0) {
2562 g_source_remove (priv->purge_timeout);
2563 priv->purge_timeout = 0;
2566 if (priv->remove_attachment_banner) {
2567 gtk_widget_destroy (priv->remove_attachment_banner);
2568 g_object_unref (priv->remove_attachment_banner);
2569 priv->remove_attachment_banner = NULL;
2577 update_window_title (ModestMsgViewWindow *window)
2579 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2581 TnyHeader *header = NULL;
2582 const gchar *subject = NULL;
2584 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2586 header = tny_msg_get_header (msg);
2587 subject = tny_header_get_subject (header);
2588 g_object_unref (msg);
2591 if ((subject == NULL)||(subject[0] == '\0'))
2592 subject = _("mail_va_no_subject");
2594 gtk_window_set_title (GTK_WINDOW (window), subject);
2597 static void on_move_focus (ModestMsgViewWindow *window,
2598 GtkDirectionType direction,
2601 GtkWidget *current_focus = NULL;
2603 current_focus = gtk_window_get_focus (GTK_WINDOW (window));
2604 if ((current_focus != NULL) &&
2605 MODEST_IS_ATTACHMENTS_VIEW (current_focus)) {
2606 g_signal_stop_emission_by_name (G_OBJECT (window), "move-focus");