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 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
488 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
490 priv->msg_view = modest_msg_view_new (msg);
491 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
492 main_vbox = gtk_vbox_new (FALSE, 6);
495 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
496 gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar));
497 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
499 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
500 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
501 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
502 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
504 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
505 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
506 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
508 priv->find_toolbar = hildon_find_toolbar_new (NULL);
509 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
510 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
511 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
512 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
514 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);
515 gtk_widget_show_all (GTK_WIDGET(main_vbox));
519 modest_msg_view_window_disconnect_signals (ModestWindow *self)
521 ModestMsgViewWindowPrivate *priv;
522 ModestHeaderView *header_view = NULL;
523 ModestMainWindow *main_window = NULL;
524 ModestWindowMgr *window_mgr = NULL;
526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
528 if (g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
529 priv->clipboard_change_handler))
530 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
531 priv->clipboard_change_handler);
533 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
534 priv->queue_change_handler))
535 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
536 priv->queue_change_handler);
538 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
539 priv->account_removed_handler))
540 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
541 priv->account_removed_handler);
543 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
544 priv->row_changed_handler))
545 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
546 priv->row_changed_handler);
548 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
549 priv->row_deleted_handler))
550 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
551 priv->row_deleted_handler);
553 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
554 priv->row_inserted_handler))
555 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
556 priv->row_inserted_handler);
558 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
559 priv->rows_reordered_handler))
560 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
561 priv->rows_reordered_handler);
563 window_mgr = modest_runtime_get_window_mgr();
564 g_assert(window_mgr != NULL);
566 main_window = MODEST_MAIN_WINDOW(
567 modest_window_mgr_get_main_window(window_mgr));
569 if(main_window == NULL)
572 header_view = MODEST_HEADER_VIEW(
573 modest_main_window_get_child_widget(
574 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
576 if (header_view == NULL)
579 modest_header_view_remove_observer(header_view,
580 MODEST_HEADER_VIEW_OBSERVER(self));
584 modest_msg_view_window_finalize (GObject *obj)
586 ModestMsgViewWindowPrivate *priv;
588 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
590 /* Sanity check: shouldn't be needed, the window mgr should
591 call this function before */
592 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
594 if (priv->header_model != NULL) {
595 g_object_unref (priv->header_model);
596 priv->header_model = NULL;
599 if (priv->progress_bar_timeout > 0) {
600 g_source_remove (priv->progress_bar_timeout);
601 priv->progress_bar_timeout = 0;
604 if (priv->remove_attachment_banner) {
605 gtk_widget_destroy (priv->remove_attachment_banner);
606 g_object_unref (priv->remove_attachment_banner);
607 priv->remove_attachment_banner = NULL;
610 if (priv->purge_timeout > 0) {
611 g_source_remove (priv->purge_timeout);
612 priv->purge_timeout = 0;
615 if (priv->row_reference) {
616 gtk_tree_row_reference_free (priv->row_reference);
617 priv->row_reference = NULL;
620 if (priv->next_row_reference) {
621 gtk_tree_row_reference_free (priv->next_row_reference);
622 priv->next_row_reference = NULL;
626 g_free (priv->msg_uid);
627 priv->msg_uid = NULL;
630 G_OBJECT_CLASS(parent_class)->finalize (obj);
634 select_next_valid_row (GtkTreeModel *model,
635 GtkTreeRowReference **row_reference,
638 GtkTreeIter tmp_iter;
639 GtkTreePath *path, *next;
640 gboolean retval = FALSE;
642 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
644 path = gtk_tree_row_reference_get_path (*row_reference);
645 gtk_tree_model_get_iter (model, &tmp_iter, path);
646 gtk_tree_row_reference_free (*row_reference);
647 *row_reference = NULL;
649 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
650 next = gtk_tree_model_get_path (model, &tmp_iter);
651 *row_reference = gtk_tree_row_reference_new (model, next);
653 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
654 next = gtk_tree_model_get_path (model, &tmp_iter);
656 /* Ensure that we are not selecting the same */
657 if (gtk_tree_path_compare (path, next) != 0) {
658 *row_reference = gtk_tree_row_reference_new (model, next);
664 gtk_tree_path_free (path);
669 /* TODO: This should be in _init(), with the parameters as properties. */
671 modest_msg_view_window_construct (ModestMsgViewWindow *self, TnyMsg *msg,
672 const gchar *modest_account_name,
673 const gchar *msg_uid)
676 ModestMsgViewWindowPrivate *priv = NULL;
677 ModestWindowPrivate *parent_priv = NULL;
678 ModestDimmingRulesGroup *menu_rules_group = NULL;
679 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
680 GtkActionGroup *action_group = NULL;
681 GError *error = NULL;
682 GdkPixbuf *window_icon;
684 g_return_if_fail (msg);
686 obj = G_OBJECT (self);
687 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
688 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
690 priv->msg_uid = g_strdup (msg_uid);
692 parent_priv->ui_manager = gtk_ui_manager_new();
693 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
695 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
696 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
698 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
699 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
701 /* Add common actions */
702 gtk_action_group_add_actions (action_group,
703 modest_action_entries,
704 G_N_ELEMENTS (modest_action_entries),
706 gtk_action_group_add_toggle_actions (action_group,
707 modest_toggle_action_entries,
708 G_N_ELEMENTS (modest_toggle_action_entries),
710 gtk_action_group_add_toggle_actions (action_group,
711 msg_view_toggle_action_entries,
712 G_N_ELEMENTS (msg_view_toggle_action_entries),
714 gtk_action_group_add_radio_actions (action_group,
715 msg_view_zoom_action_entries,
716 G_N_ELEMENTS (msg_view_zoom_action_entries),
718 G_CALLBACK (modest_ui_actions_on_change_zoom),
721 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
722 g_object_unref (action_group);
724 /* Load the UI definition */
725 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
728 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
729 g_error_free (error);
734 /* Add common dimming rules */
735 modest_dimming_rules_group_add_rules (menu_rules_group,
736 modest_msg_view_menu_dimming_entries,
737 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
739 modest_dimming_rules_group_add_rules (toolbar_rules_group,
740 modest_msg_view_toolbar_dimming_entries,
741 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
744 /* Insert dimming rules group for this window */
745 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
746 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
747 g_object_unref (menu_rules_group);
748 g_object_unref (toolbar_rules_group);
750 /* Add accelerators */
751 gtk_window_add_accel_group (GTK_WINDOW (obj),
752 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
755 init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
756 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
758 /* Set window icon */
759 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
761 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
762 g_object_unref (window_icon);
765 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
767 g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
768 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
769 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
770 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
771 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
772 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
773 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
774 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
775 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
776 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
778 g_signal_connect (G_OBJECT (obj), "key-release-event",
779 G_CALLBACK (modest_msg_view_window_key_release_event),
782 g_signal_connect (G_OBJECT (obj), "window-state-event",
783 G_CALLBACK (modest_msg_view_window_window_state_event),
786 /* Mail Operation Queue */
787 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
789 G_CALLBACK (on_queue_changed),
792 /* Account manager */
793 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
795 G_CALLBACK(on_account_removed),
798 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
800 priv->last_search = NULL;
802 /* Init the clipboard actions dim status */
803 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
805 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
807 /* Check toolbar dimming rules */
808 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
813 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
814 const gchar *modest_account_name,
815 const gchar *msg_uid,
817 GtkTreeRowReference *row_reference)
819 ModestMsgViewWindow *window = NULL;
820 ModestMsgViewWindowPrivate *priv = NULL;
821 TnyFolder *header_folder = NULL;
822 ModestHeaderView *header_view = NULL;
823 ModestMainWindow *main_window = NULL;
824 ModestWindowMgr *window_mgr = NULL;
826 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
827 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
828 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
830 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
832 /* Remember the message list's TreeModel so we can detect changes
833 * and change the list selection when necessary: */
834 window_mgr = modest_runtime_get_window_mgr();
835 g_assert(window_mgr != NULL);
836 main_window = MODEST_MAIN_WINDOW(
837 modest_window_mgr_get_main_window(window_mgr));
838 g_assert(main_window != NULL);
839 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
840 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
841 if (header_view != NULL){
842 header_folder = modest_header_view_get_folder(header_view);
843 g_assert(header_folder != NULL);
844 priv->header_folder_id = tny_folder_get_id(header_folder);
845 g_assert(priv->header_folder_id != NULL);
846 g_object_unref(header_folder);
849 priv->header_model = g_object_ref(model);
850 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
851 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
852 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
854 priv->row_changed_handler = g_signal_connect(
855 GTK_TREE_MODEL(model), "row-changed",
856 G_CALLBACK(modest_msg_view_window_on_row_changed),
858 priv->row_deleted_handler = g_signal_connect(
859 GTK_TREE_MODEL(model), "row-deleted",
860 G_CALLBACK(modest_msg_view_window_on_row_deleted),
862 priv->row_inserted_handler = g_signal_connect (
863 GTK_TREE_MODEL(model), "row-inserted",
864 G_CALLBACK(modest_msg_view_window_on_row_inserted),
866 priv->rows_reordered_handler = g_signal_connect(
867 GTK_TREE_MODEL(model), "rows-reordered",
868 G_CALLBACK(modest_msg_view_window_on_row_reordered),
871 if (header_view != NULL){
872 modest_header_view_add_observer(header_view,
873 MODEST_HEADER_VIEW_OBSERVER(window));
876 modest_msg_view_window_update_priority (window);
878 /* Check toolbar dimming rules */
879 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
881 return MODEST_WINDOW(window);
885 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
886 const gchar *modest_account_name,
887 const gchar *msg_uid)
889 ModestMsgViewWindow *window = NULL;
890 ModestMsgViewWindowPrivate *priv = NULL;
892 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
893 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
894 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
896 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
898 /* Remember that this is a search result,
899 * so we can disable some UI appropriately: */
900 priv->is_search_result = TRUE;
902 return MODEST_WINDOW(window);
906 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
907 const gchar *modest_account_name,
908 const gchar *msg_uid)
911 g_return_val_if_fail (msg, NULL);
913 obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
914 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
915 msg, modest_account_name, msg_uid);
917 return MODEST_WINDOW(obj);
920 void modest_msg_view_window_on_row_changed(
921 GtkTreeModel *header_model,
924 ModestMsgViewWindow *window){
925 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
928 void modest_msg_view_window_on_row_deleted(
929 GtkTreeModel *header_model,
931 ModestMsgViewWindow *window){
932 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
935 /* On insertions we check if the folder still has the message we are
936 * showing or do not. If do not, we do nothing. Which means we are still
937 * not attached to any header folder and thus next/prev buttons are
938 * still dimmed. Once the message that is shown by msg-view is found, the
939 * new model of header-view will be attached and the references will be set.
940 * On each further insertions dimming rules will be checked. However
941 * this requires extra CPU time at least works.
942 * (An message might be deleted from TnyFolder and thus will not be
943 * inserted into the model again for example if it is removed by the
944 * imap server and the header view is refreshed.)
946 void modest_msg_view_window_on_row_inserted(
947 GtkTreeModel *new_model,
948 GtkTreePath *tree_path,
949 GtkTreeIter *tree_iter,
950 ModestMsgViewWindow *window){
951 ModestMsgViewWindowPrivate *priv = NULL;
952 TnyHeader *header = NULL;
955 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
957 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
959 /* If we already has a model attached then the message shown by
960 * msg-view is in it, and thus we do not need any actions but
961 * to check the dimming rules.*/
962 if(priv->header_model != NULL){
963 gtk_tree_row_reference_free(priv->next_row_reference);
964 priv->next_row_reference = gtk_tree_row_reference_copy(
965 priv->row_reference);
966 select_next_valid_row (priv->header_model,
967 &(priv->next_row_reference), FALSE);
968 modest_ui_actions_check_toolbar_dimming_rules (
969 MODEST_WINDOW (window));
973 /* Check if the newly inserted message is the same we are actually
974 * showing. IF not, we should remain detached from the header model
975 * and thus prev and next toolbarbuttons should remain dimmed. */
976 gtk_tree_model_get (new_model, tree_iter,
977 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
978 uid = modest_tny_folder_get_header_unique_id(header);
979 if(!g_str_equal(priv->msg_uid, uid)){
985 /* Setup row_reference for the actual msg. */
986 priv->row_reference = gtk_tree_row_reference_new(
987 new_model, tree_path);
988 if(priv->row_reference == NULL){
989 g_warning("No reference for msg header item.");
993 /* Attach new_model and connect some callback to it to become able
994 * to detect changes in header-view. */
995 priv->header_model = g_object_ref(new_model);
996 g_signal_connect (new_model, "row-changed",
997 G_CALLBACK (modest_msg_view_window_on_row_changed),
999 g_signal_connect (new_model, "row-deleted",
1000 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1002 g_signal_connect (new_model, "rows-reordered",
1003 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1006 /* Now set up next_row_reference. */
1007 priv->next_row_reference = gtk_tree_row_reference_copy(
1008 priv->row_reference);
1009 select_next_valid_row (priv->header_model,
1010 &(priv->next_row_reference), FALSE);
1012 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1015 void modest_msg_view_window_on_row_reordered(
1016 GtkTreeModel *header_model,
1020 ModestMsgViewWindow *window){
1021 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1024 /* The modest_msg_view_window_update_model_replaced implements update
1025 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1026 * actually belongs to the header-view is the same as the TnyFolder of
1027 * the message of msg-view or not. If they are different, there is
1028 * nothing to do. If they are the same, then the model has replaced and
1029 * the reference in msg-view shall be replaced from the old model to
1030 * the new model. In this case the view will be detached from it's
1031 * header folder. From this point the next/prev buttons are dimmed.
1033 void modest_msg_view_window_update_model_replaced(
1034 ModestHeaderViewObserver *observer,
1035 GtkTreeModel *model,
1036 const gchar *tny_folder_id){
1037 ModestMsgViewWindowPrivate *priv = NULL;
1038 ModestMsgViewWindow *window = NULL;
1040 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1041 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1043 window = MODEST_MSG_VIEW_WINDOW(observer);
1044 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1046 /* If there is an other folder in the header-view then we do
1047 * not care about it's model (msg list). Else if the
1048 * header-view shows the folder the msg shown by us is in, we
1049 * shall replace our model reference and make some check. */
1050 if(tny_folder_id == NULL ||
1051 !g_str_equal(tny_folder_id, priv->header_folder_id))
1054 /* Model is changed(replaced), so we should forget the old
1055 * one. Because there might be other references and there
1056 * might be some change on the model even if we unreferenced
1057 * it, we need to disconnect our signals here. */
1058 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1059 priv->row_changed_handler))
1060 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1061 priv->row_changed_handler);
1062 priv->row_changed_handler = 0;
1063 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1064 priv->row_deleted_handler))
1065 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1066 priv->row_deleted_handler);
1067 priv->row_deleted_handler = 0;
1068 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1069 priv->row_inserted_handler))
1070 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1071 priv->row_inserted_handler);
1072 priv->row_inserted_handler = 0;
1073 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1074 priv->rows_reordered_handler))
1075 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1076 priv->rows_reordered_handler);
1077 priv->rows_reordered_handler = 0;
1078 g_object_unref(priv->header_model);
1079 priv->header_model = NULL;
1080 g_object_unref(priv->row_reference);
1081 priv->row_reference = NULL;
1082 g_object_unref(priv->next_row_reference);
1083 priv->next_row_reference = NULL;
1085 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1087 if(tny_folder_id == NULL)
1090 g_assert(model != NULL);
1092 /* Also we must connect to the new model for row insertions.
1093 * Only for insertions now. We will need other ones only after
1094 * the msg is show by msg-view is added to the new model. */
1095 priv->row_inserted_handler = g_signal_connect (
1096 model, "row-inserted",
1097 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1102 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1104 ModestMsgViewWindowPrivate *priv= NULL;
1106 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1107 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1109 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1113 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1115 ModestMsgViewWindowPrivate *priv= NULL;
1117 TnyHeader *header = NULL;
1118 GtkTreePath *path = NULL;
1121 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1122 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1124 /* If the message was not obtained from a treemodel,
1125 * for instance if it was opened directly by the search UI:
1127 if (priv->header_model == NULL) {
1128 msg = modest_msg_view_window_get_message (self);
1129 header = tny_msg_get_header (msg);
1130 g_object_unref (msg);
1134 /* Get iter of the currently selected message in the header view: */
1135 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1136 * instead of sometimes retrieving it from the header view?
1137 * Then we wouldn't be dependent on the message actually still being selected
1138 * in the header view. murrayc. */
1139 path = gtk_tree_row_reference_get_path (priv->row_reference);
1140 g_return_val_if_fail (path != NULL, NULL);
1141 gtk_tree_model_get_iter (priv->header_model,
1145 /* Get current message header */
1146 gtk_tree_model_get (priv->header_model, &iter,
1147 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1150 gtk_tree_path_free (path);
1155 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1157 ModestMsgView *msg_view;
1158 ModestMsgViewWindowPrivate *priv;
1160 g_return_val_if_fail (self, NULL);
1162 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1164 msg_view = MODEST_MSG_VIEW (priv->msg_view);
1166 return modest_msg_view_get_message (msg_view);
1170 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1172 ModestMsgViewWindowPrivate *priv;
1174 g_return_val_if_fail (self, NULL);
1176 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1178 return (const gchar*) priv->msg_uid;
1182 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1185 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1186 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1187 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1191 is_active = gtk_toggle_action_get_active (toggle);
1194 gtk_widget_show (priv->find_toolbar);
1195 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1197 gtk_widget_hide (priv->find_toolbar);
1200 /* update the toggle buttons status */
1201 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1202 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1203 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1204 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1209 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1210 ModestMsgViewWindow *obj)
1212 GtkToggleAction *toggle;
1213 ModestWindowPrivate *parent_priv;
1214 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1216 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1217 gtk_toggle_action_set_active (toggle, FALSE);
1221 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1222 ModestMsgViewWindow *obj)
1224 gchar *current_search;
1225 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1227 if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
1228 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1232 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1234 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1235 g_free (current_search);
1236 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1240 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1242 g_free (priv->last_search);
1243 priv->last_search = g_strdup (current_search);
1244 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
1247 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1248 g_free (priv->last_search);
1249 priv->last_search = NULL;
1252 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
1253 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1254 g_free (priv->last_search);
1255 priv->last_search = NULL;
1259 g_free (current_search);
1264 modest_msg_view_window_set_zoom (ModestWindow *window,
1267 ModestMsgViewWindowPrivate *priv;
1269 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1271 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1272 modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
1276 modest_msg_view_window_get_zoom (ModestWindow *window)
1278 ModestMsgViewWindowPrivate *priv;
1280 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1282 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1283 return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
1287 modest_msg_view_window_zoom_plus (ModestWindow *window)
1289 ModestWindowPrivate *parent_priv;
1290 GtkRadioAction *zoom_radio_action;
1291 GSList *group, *node;
1293 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1294 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1295 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1297 group = gtk_radio_action_get_group (zoom_radio_action);
1299 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1300 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1304 for (node = group; node != NULL; node = g_slist_next (node)) {
1305 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1306 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1314 modest_msg_view_window_zoom_minus (ModestWindow *window)
1316 ModestWindowPrivate *parent_priv;
1317 GtkRadioAction *zoom_radio_action;
1318 GSList *group, *node;
1320 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1321 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1322 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1324 group = gtk_radio_action_get_group (zoom_radio_action);
1326 for (node = group; node != NULL; node = g_slist_next (node)) {
1327 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1328 if (node->next != NULL) {
1329 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1332 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1342 modest_msg_view_window_key_release_event (GtkWidget *window,
1346 if (event->type == GDK_KEY_RELEASE) {
1347 switch (event->keyval) {
1349 modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1353 modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1366 modest_msg_view_window_scroll_up (ModestWindow *window)
1368 ModestMsgViewWindowPrivate *priv;
1369 gboolean return_value;
1371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1372 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1376 modest_msg_view_window_scroll_down (ModestWindow *window)
1378 ModestMsgViewWindowPrivate *priv;
1379 gboolean return_value;
1381 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1382 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1386 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1389 ModestMsgViewWindowPrivate *priv;
1390 GtkTreeIter tmp_iter;
1391 gboolean is_last_selected;
1393 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1394 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1396 /*if no model (so no rows at all), then virtually we are the last*/
1397 if (!priv->header_model)
1400 path = gtk_tree_row_reference_get_path (priv->row_reference);
1404 is_last_selected = TRUE;
1405 while (is_last_selected) {
1407 gtk_tree_path_next (path);
1408 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1410 gtk_tree_model_get (priv->header_model, &tmp_iter,
1411 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1413 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1414 is_last_selected = FALSE;
1416 gtk_tree_path_free (path);
1417 return is_last_selected;
1421 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1423 ModestMsgViewWindowPrivate *priv;
1425 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1426 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1428 return priv->header_model != NULL;
1432 modest_msg_view_window_is_search_result (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->is_search_result;
1443 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1446 ModestMsgViewWindowPrivate *priv;
1447 gboolean is_first_selected;
1448 GtkTreeIter tmp_iter;
1449 /* gchar * path_string;*/
1451 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1454 /*if no model (so no rows at all), then virtually we are the first*/
1455 if (!priv->header_model)
1458 path = gtk_tree_row_reference_get_path (priv->row_reference);
1462 /* path_string = gtk_tree_path_to_string (path);
1463 is_first_selected = strcmp (path_string, "0");
1465 g_free (path_string);
1466 gtk_tree_path_free (path);
1468 return is_first_selected;*/
1470 is_first_selected = TRUE;
1471 while (is_first_selected) {
1473 if(!gtk_tree_path_prev (path))
1475 /* Here the 'if' is needless for logic, but let make sure
1476 * iter is valid for gtk_tree_model_get. */
1477 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1479 gtk_tree_model_get (priv->header_model, &tmp_iter,
1480 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1482 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1483 is_first_selected = FALSE;
1485 gtk_tree_path_free (path);
1486 return is_first_selected;
1490 * Reads the message whose summary item is @header. It takes care of
1491 * several things, among others:
1493 * If the message was not previously downloaded then ask the user
1494 * before downloading. If there is no connection launch the connection
1495 * dialog. Update toolbar dimming rules.
1497 * Returns: TRUE if the mail operation was started, otherwise if the
1498 * user do not want to download the message, or if the user do not
1499 * want to connect, then the operation is not issued
1502 message_reader (ModestMsgViewWindow *window,
1503 ModestMsgViewWindowPrivate *priv,
1507 ModestMailOperation *mail_op = NULL;
1508 ModestMailOperationTypeOperation op_type;
1509 gboolean already_showing = FALSE;
1510 ModestWindow *msg_window = NULL;
1511 ModestWindowMgr *mgr;
1513 g_return_val_if_fail (path != NULL, FALSE);
1515 mgr = modest_runtime_get_window_mgr ();
1516 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1517 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1520 gtk_window_present (GTK_WINDOW (msg_window));
1521 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1525 /* Msg download completed */
1526 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1527 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1529 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1531 /* Ask the user if he wants to download the message if
1533 if (!tny_device_is_online (modest_runtime_get_device())) {
1534 TnyFolder *folder = NULL;
1535 GtkResponseType response;
1537 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1538 _("mcen_nc_get_msg"));
1539 if (response == GTK_RESPONSE_CANCEL)
1542 /* Offer the connection dialog if necessary */
1543 folder = tny_header_get_folder (header);
1545 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1546 TNY_FOLDER_STORE (folder))) {
1547 g_object_unref (folder);
1550 g_object_unref (folder);
1555 /* New mail operation */
1556 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1558 modest_ui_actions_get_msgs_full_error_handler,
1561 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1562 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1563 g_object_unref (mail_op);
1565 /* Update toolbar dimming rules */
1566 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1572 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1574 ModestMsgViewWindowPrivate *priv;
1575 GtkTreePath *path= NULL;
1576 GtkTreeIter tmp_iter;
1578 gboolean retval = TRUE;
1580 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1581 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1583 /* Update the next row reference if it's not valid. This could
1584 happen if for example the header which it was pointing to,
1585 was deleted. The best place to do it is in the row-deleted
1586 handler but the tinymail model do not work like the glib
1587 tree models and reports the deletion when the row is still
1589 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1590 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1591 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1592 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1595 if (priv->next_row_reference)
1596 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1600 gtk_tree_model_get_iter (priv->header_model,
1604 gtk_tree_model_get (priv->header_model, &tmp_iter,
1605 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1608 /* Read the message & show it */
1609 if (!message_reader (window, priv, header, path)) {
1611 gtk_tree_path_free (path);
1615 g_object_unref (header);
1621 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1623 ModestMsgViewWindowPrivate *priv = NULL;
1624 TnyHeader *header = NULL;
1628 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1629 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1631 /* Check that the model is not empty */
1632 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1635 /* Get the header */
1636 gtk_tree_model_get (priv->header_model,
1638 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1640 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1641 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1642 g_object_unref (header);
1643 return modest_msg_view_window_select_next_message (self);
1646 path = gtk_tree_model_get_path (priv->header_model, &iter);
1648 /* Read the message & show it */
1649 message_reader (self, priv, header, path);
1652 g_object_unref (header);
1658 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1660 ModestMsgViewWindowPrivate *priv = NULL;
1663 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1664 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1666 /* Return inmediatly if there is no header model */
1667 if (!priv->header_model)
1670 path = gtk_tree_row_reference_get_path (priv->row_reference);
1671 while (gtk_tree_path_prev (path)) {
1675 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1676 gtk_tree_model_get (priv->header_model, &iter,
1677 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1681 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1682 g_object_unref (header);
1686 /* Read the message & show it */
1687 if (!message_reader (window, priv, header, path)) {
1688 g_object_unref (header);
1692 g_object_unref (header);
1697 gtk_tree_path_free (path);
1702 view_msg_cb (ModestMailOperation *mail_op,
1707 ModestMsgViewWindow *self = NULL;
1708 ModestMsgViewWindowPrivate *priv = NULL;
1711 /* If there was any error */
1712 path = (GtkTreePath *) user_data;
1713 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1714 gtk_tree_path_free (path);
1718 /* Get the window */
1719 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1720 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1721 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1723 /* Update the row reference */
1724 gtk_tree_row_reference_free (priv->row_reference);
1725 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1726 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1727 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1728 gtk_tree_path_free (path);
1730 /* Mark header as read */
1731 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1732 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1734 /* Set new message */
1735 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1736 modest_msg_view_window_update_priority (self);
1737 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1738 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1740 /* Set the new message uid of the window */
1741 if (priv->msg_uid) {
1742 g_free (priv->msg_uid);
1743 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1746 /* Notify the observers */
1747 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1748 0, priv->header_model, priv->row_reference);
1750 /* Free new references */
1751 g_object_unref (self);
1755 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1757 ModestMsgViewWindowPrivate *priv;
1759 TnyFolderType folder_type;
1761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1763 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1765 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1769 folder = tny_msg_get_folder (msg);
1772 folder_type = tny_folder_get_folder_type (folder);
1774 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1775 const gchar *fname = tny_folder_get_name (folder);
1776 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1779 g_object_unref (folder);
1781 g_object_unref (msg);
1789 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1791 ModestMsgViewWindowPrivate *priv;
1792 TnyHeaderFlags flags = 0;
1794 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1796 if (priv->header_model) {
1799 GtkTreePath *path = NULL;
1801 path = gtk_tree_row_reference_get_path (priv->row_reference);
1802 g_return_if_fail (path != NULL);
1803 gtk_tree_model_get_iter (priv->header_model,
1805 gtk_tree_row_reference_get_path (priv->row_reference));
1807 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1809 flags = tny_header_get_flags (header);
1810 gtk_tree_path_free (path);
1813 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1818 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1820 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1821 ModestWindowPrivate *parent_priv;
1822 ModestWindowMgr *mgr;
1823 gboolean is_fullscreen;
1824 GtkAction *fs_toggle_action;
1827 mgr = modest_runtime_get_window_mgr ();
1828 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1830 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1832 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1833 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1834 if (is_fullscreen != active) {
1835 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1844 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
1846 ModestWindowPrivate *parent_priv;
1847 GtkAction *fs_toggle_action;
1848 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1850 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1851 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
1852 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
1856 set_homogeneous (GtkWidget *widget,
1859 if (GTK_IS_TOOL_ITEM (widget)) {
1860 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1861 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1866 modest_msg_view_window_show_toolbar (ModestWindow *self,
1867 gboolean show_toolbar)
1869 ModestMsgViewWindowPrivate *priv = NULL;
1870 ModestWindowPrivate *parent_priv;
1871 GtkWidget *reply_button = NULL, *menu = NULL;
1872 GtkWidget *placeholder = NULL;
1875 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1876 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1878 /* Set optimized view status */
1879 priv->optimized_view = !show_toolbar;
1881 if (!parent_priv->toolbar) {
1882 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1885 /* Set homogeneous toolbar */
1886 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1887 set_homogeneous, NULL);
1889 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1890 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1891 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1892 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1893 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1894 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1895 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1896 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1898 /* Add ProgressBar (Transfer toolbar) */
1899 priv->progress_bar = modest_progress_bar_widget_new ();
1900 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1901 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1902 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1903 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1904 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1906 /* Connect cancel 'clicked' signal to abort progress mode */
1907 g_signal_connect(priv->cancel_toolitem, "clicked",
1908 G_CALLBACK(cancel_progressbar),
1911 /* Add it to the observers list */
1912 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1915 hildon_window_add_toolbar (HILDON_WINDOW (self),
1916 GTK_TOOLBAR (parent_priv->toolbar));
1919 /* Set reply button tap and hold menu */
1920 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1921 "/ToolBar/ToolbarMessageReply");
1922 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1923 "/ToolbarReplyCSM");
1924 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1928 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1929 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1930 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1932 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1933 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1936 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1937 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1942 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1944 ModestMsgViewWindow *window)
1946 ModestWindowPrivate *parent_priv;
1947 /* GtkAction *action; */
1948 gboolean is_address;
1952 if (!GTK_WIDGET_VISIBLE (window))
1955 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1956 selection = gtk_clipboard_wait_for_text (clipboard);
1958 is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1960 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1961 /* gtk_action_set_sensitive (action, is_address); */
1963 focused = gtk_window_get_focus (GTK_WINDOW (window));
1965 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1966 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1968 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1969 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1972 /* modest_msg_view_window_update_dimmed (window); */
1977 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1979 ModestMsgViewWindowPrivate *priv;
1981 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1982 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1984 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1988 cancel_progressbar (GtkToolButton *toolbutton,
1989 ModestMsgViewWindow *self)
1992 ModestMsgViewWindowPrivate *priv;
1994 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1996 /* Get operation observers and cancel its current operation */
1997 tmp = priv->progress_widgets;
1999 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2000 tmp=g_slist_next(tmp);
2004 observers_empty (ModestMsgViewWindow *self)
2007 ModestMsgViewWindowPrivate *priv;
2008 gboolean is_empty = TRUE;
2009 guint pending_ops = 0;
2011 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2012 tmp = priv->progress_widgets;
2014 /* Check all observers */
2015 while (tmp && is_empty) {
2016 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2017 is_empty = pending_ops == 0;
2019 tmp = g_slist_next(tmp);
2026 on_account_removed (TnyAccountStore *account_store,
2027 TnyAccount *account,
2030 /* Do nothing if it's a transport account, because we only
2031 show the messages of a store account */
2032 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2033 const gchar *parent_acc = NULL;
2034 const gchar *our_acc = NULL;
2036 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2037 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2039 /* Close this window if I'm showing a message of the removed account */
2040 if (strcmp (parent_acc, our_acc) == 0)
2041 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2046 on_queue_changed (ModestMailOperationQueue *queue,
2047 ModestMailOperation *mail_op,
2048 ModestMailOperationQueueNotification type,
2049 ModestMsgViewWindow *self)
2052 ModestMsgViewWindowPrivate *priv;
2053 ModestMailOperationTypeOperation op_type;
2054 ModestToolBarModes mode;
2056 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2057 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2059 /* If this operations was created by another window, do nothing */
2060 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2063 /* Get toolbar mode from operation id*/
2064 op_type = modest_mail_operation_get_type_operation (mail_op);
2066 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
2067 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2068 case MODEST_MAIL_OPERATION_TYPE_OPEN:
2069 mode = TOOLBAR_MODE_TRANSFER;
2072 mode = TOOLBAR_MODE_NORMAL;
2076 /* Add operation observers and change toolbar if neccessary*/
2077 tmp = priv->progress_widgets;
2079 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2080 if (mode == TOOLBAR_MODE_TRANSFER) {
2081 /* Enable transfer toolbar mode */
2082 set_toolbar_transfer_mode(self);
2084 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2086 tmp = g_slist_next (tmp);
2091 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2092 if (mode == TOOLBAR_MODE_TRANSFER) {
2094 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2096 tmp = g_slist_next (tmp);
2100 /* If no more operations are being observed, NORMAL mode is enabled again */
2101 if (observers_empty (self)) {
2102 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2110 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2112 ModestMsgViewWindowPrivate *priv;
2113 GList *selected_attachments = NULL;
2115 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2116 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2118 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2120 return selected_attachments;
2124 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2126 ModestMsgViewWindowPrivate *priv;
2127 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2128 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2130 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2132 if (mime_part == NULL) {
2133 gboolean error = FALSE;
2134 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2135 if (selected_attachments == NULL) {
2137 } else if (g_list_length (selected_attachments) > 1) {
2138 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2141 mime_part = (TnyMimePart *) selected_attachments->data;
2142 g_object_ref (mime_part);
2144 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2145 g_list_free (selected_attachments);
2150 g_object_ref (mime_part);
2153 if (tny_mime_part_is_purged (mime_part)) {
2154 g_object_unref (mime_part);
2158 if (!TNY_IS_MSG (mime_part)) {
2159 gchar *filepath = NULL;
2160 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2161 TnyFsStream *temp_stream = NULL;
2163 temp_stream = modest_maemo_utils_create_temp_stream (att_filename, &filepath);
2166 const gchar *content_type;
2167 content_type = tny_mime_part_get_content_type (mime_part);
2168 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2170 modest_platform_activate_file (filepath, content_type);
2171 g_object_unref (temp_stream);
2173 /* NOTE: files in the temporary area will be automatically
2174 * cleaned after some time if they are no longer in use */
2177 /* message attachment */
2178 TnyHeader *header = NULL;
2179 ModestWindowMgr *mgr;
2180 ModestWindow *msg_win = NULL;
2183 header = tny_msg_get_header (TNY_MSG (mime_part));
2184 mgr = modest_runtime_get_window_mgr ();
2185 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2188 if (msg_win) /* there is already a window for this uid; top it */
2189 gtk_window_present (GTK_WINDOW(msg_win));
2191 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2192 * thus, we don't do anything */
2193 g_warning ("window for is already being created");
2195 /* it's not found, so create a new window for it */
2196 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
2197 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2199 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2200 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
2201 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2202 modest_window_get_zoom (MODEST_WINDOW (window)));
2203 modest_window_mgr_register_window (mgr, msg_win);
2204 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2205 gtk_widget_show_all (GTK_WIDGET (msg_win));
2208 g_object_unref (mime_part);
2224 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2225 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2226 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2227 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2230 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2234 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2235 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2236 g_free (pair->filename);
2237 g_object_unref (pair->part);
2238 g_slice_free (SaveMimePartPair, pair);
2240 g_list_free (info->pairs);
2243 gtk_widget_destroy (info->banner);
2244 g_object_unref (info->banner);
2245 g_slice_free (SaveMimePartInfo, info);
2250 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2252 if (info->pairs != NULL) {
2253 save_mime_part_to_file (info);
2256 result = info->result;
2258 /* This is a GDK lock because we are an idle callback and
2259 * hildon_banner_show_information is or does Gtk+ code */
2261 gdk_threads_enter (); /* CHECKED */
2262 save_mime_part_info_free (info, TRUE);
2264 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2266 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2268 gdk_threads_leave (); /* CHECKED */
2275 save_mime_part_to_file (SaveMimePartInfo *info)
2277 GnomeVFSResult result;
2278 GnomeVFSHandle *handle;
2280 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2282 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2283 if (result == GNOME_VFS_OK) {
2284 stream = tny_vfs_stream_new (handle);
2285 tny_mime_part_decode_to_stream (pair->part, stream);
2286 g_object_unref (G_OBJECT (stream));
2287 g_object_unref (pair->part);
2288 g_slice_free (SaveMimePartPair, pair);
2289 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2290 info->result = TRUE;
2292 save_mime_part_info_free (info, FALSE);
2293 info->result = FALSE;
2296 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2301 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2303 gboolean is_ok = TRUE;
2304 gint replaced_files = 0;
2305 const GList *files = info->pairs;
2308 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2309 SaveMimePartPair *pair = iter->data;
2310 if (modest_maemo_utils_file_exists (pair->filename)) {
2314 if (replaced_files) {
2315 GtkWidget *confirm_overwrite_dialog;
2316 const gchar *message = (replaced_files == 1) ?
2317 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2318 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2319 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2322 gtk_widget_destroy (confirm_overwrite_dialog);
2326 save_mime_part_info_free (info, TRUE);
2328 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2335 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2337 gboolean clean_list = FALSE;
2338 ModestMsgViewWindowPrivate *priv;
2339 GList *files_to_save = NULL;
2340 GtkWidget *save_dialog = NULL;
2341 gchar *folder = NULL;
2342 gboolean canceled = FALSE;
2343 const gchar *filename = NULL;
2344 gchar *save_multiple_str = NULL;
2346 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2347 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2349 if (mime_parts == NULL) {
2350 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2351 if (mime_parts == NULL)
2356 /* prepare dialog */
2357 if (mime_parts->next == NULL) {
2358 /* only one attachment selected */
2359 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2360 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2361 filename = tny_mime_part_get_filename (mime_part);
2363 g_warning ("Tried to save a non-file attachment");
2367 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2368 g_list_length (mime_parts));
2371 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2372 GTK_FILE_CHOOSER_ACTION_SAVE);
2375 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2376 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2380 if (filename != NULL)
2381 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2384 /* if multiple, set multiple string */
2385 if (save_multiple_str) {
2386 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2390 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2391 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2393 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2394 hildon_banner_show_information
2395 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2399 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2400 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2402 if (tny_mime_part_is_attachment (mime_part)) {
2403 SaveMimePartPair *pair;
2405 if ((mime_parts->next != NULL) &&
2406 (tny_mime_part_get_filename (mime_part) == NULL))
2409 pair = g_slice_new0 (SaveMimePartPair);
2410 if (mime_parts->next == NULL) {
2411 pair->filename = g_strdup (chooser_uri);
2414 g_build_filename (chooser_uri,
2415 tny_mime_part_get_filename (mime_part), NULL);
2417 pair->part = g_object_ref (mime_part);
2418 files_to_save = g_list_prepend (files_to_save, pair);
2422 g_free (chooser_uri);
2425 gtk_widget_destroy (save_dialog);
2428 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2429 g_list_free (mime_parts);
2432 if (files_to_save != NULL) {
2433 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2434 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2435 _CS("sfil_ib_saving"));
2436 info->pairs = files_to_save;
2437 info->banner = banner;
2438 info->result = TRUE;
2439 g_object_ref (banner);
2440 save_mime_parts_to_file_with_checks (info);
2445 show_remove_attachment_information (gpointer userdata)
2447 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2448 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2450 if (priv->remove_attachment_banner != NULL) {
2451 gtk_widget_destroy (priv->remove_attachment_banner);
2452 g_object_unref (priv->remove_attachment_banner);
2455 priv->remove_attachment_banner = g_object_ref (
2456 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2462 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2464 ModestMsgViewWindowPrivate *priv;
2465 GList *mime_parts = NULL, *node;
2466 gchar *confirmation_message;
2470 /* TnyFolder *folder; */
2472 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2473 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2476 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2478 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2480 /* Remove already purged messages from mime parts list */
2482 while (node != NULL) {
2483 TnyMimePart *part = TNY_MIME_PART (node->data);
2484 if (tny_mime_part_is_purged (part)) {
2485 GList *deleted_node = node;
2486 node = g_list_next (node);
2487 g_object_unref (part);
2488 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2490 node = g_list_next (node);
2494 if (mime_parts == NULL)
2497 n_attachments = g_list_length (mime_parts);
2498 if (n_attachments == 1) {
2499 const gchar *filename;
2501 if (TNY_IS_MSG (mime_parts->data)) {
2503 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2504 filename = tny_header_get_subject (header);
2505 g_object_unref (header);
2506 if (filename == NULL)
2507 filename = _("mail_va_no_subject");
2509 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2511 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2513 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2514 "mcen_nc_purge_files_text",
2515 n_attachments), n_attachments);
2517 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2518 confirmation_message);
2519 g_free (confirmation_message);
2521 if (response != GTK_RESPONSE_OK)
2524 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2525 /* folder = tny_msg_get_folder (msg); */
2526 /* tny_msg_uncache_attachments (msg); */
2527 /* tny_folder_refresh (folder, NULL); */
2528 /* g_object_unref (folder); */
2530 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2531 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2532 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2535 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2536 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2537 tny_msg_rewrite_cache (msg);
2538 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2540 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2541 g_list_free (mime_parts);
2543 if (priv->purge_timeout > 0) {
2544 g_source_remove (priv->purge_timeout);
2545 priv->purge_timeout = 0;
2548 if (priv->remove_attachment_banner) {
2549 gtk_widget_destroy (priv->remove_attachment_banner);
2550 g_object_unref (priv->remove_attachment_banner);
2551 priv->remove_attachment_banner = NULL;
2559 update_window_title (ModestMsgViewWindow *window)
2561 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2563 TnyHeader *header = NULL;
2564 const gchar *subject = NULL;
2566 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2568 header = tny_msg_get_header (msg);
2569 subject = tny_header_get_subject (header);
2570 g_object_unref (msg);
2573 if ((subject == NULL)||(subject[0] == '\0'))
2574 subject = _("mail_va_no_subject");
2576 gtk_window_set_title (GTK_WINDOW (window), subject);