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 view_msg_cb (ModestMailOperation *mail_op,
141 static void set_toolbar_mode (ModestMsgViewWindow *self,
142 ModestToolBarModes mode);
144 static void update_window_title (ModestMsgViewWindow *window);
146 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
149 /* list my signals */
155 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
156 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
157 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
160 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
161 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
162 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
163 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
164 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
165 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
166 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
169 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
170 struct _ModestMsgViewWindowPrivate {
173 GtkWidget *main_scroll;
174 GtkWidget *find_toolbar;
177 /* Progress observers */
178 GtkWidget *progress_bar;
179 GSList *progress_widgets;
182 GtkWidget *progress_toolitem;
183 GtkWidget *cancel_toolitem;
184 GtkWidget *prev_toolitem;
185 GtkWidget *next_toolitem;
186 ModestToolBarModes current_toolbar_mode;
188 /* Optimized view enabled */
189 gboolean optimized_view;
191 /* Whether this was created via the *_new_for_search_result() function. */
192 gboolean is_search_result;
194 /* A reference to the @model of the header view
195 * to allow selecting previous/next messages,
196 * if the message is currently selected in the header view.
198 const gchar *header_folder_id;
199 GtkTreeModel *header_model;
200 GtkTreeRowReference *row_reference;
201 GtkTreeRowReference *next_row_reference;
203 gulong clipboard_change_handler;
204 gulong queue_change_handler;
205 gulong account_removed_handler;
206 gulong row_changed_handler;
207 gulong row_deleted_handler;
208 gulong row_inserted_handler;
209 gulong rows_reordered_handler;
212 GtkWidget *remove_attachment_banner;
214 guint progress_bar_timeout;
219 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
220 MODEST_TYPE_MSG_VIEW_WINDOW, \
221 ModestMsgViewWindowPrivate))
223 static GtkWindowClass *parent_class = NULL;
225 /* uncomment the following if you have defined any signals */
226 static guint signals[LAST_SIGNAL] = {0};
229 modest_msg_view_window_get_type (void)
231 static GType my_type = 0;
233 static const GTypeInfo my_info = {
234 sizeof(ModestMsgViewWindowClass),
235 NULL, /* base init */
236 NULL, /* base finalize */
237 (GClassInitFunc) modest_msg_view_window_class_init,
238 NULL, /* class finalize */
239 NULL, /* class data */
240 sizeof(ModestMsgViewWindow),
242 (GInstanceInitFunc) modest_msg_view_window_init,
245 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
246 "ModestMsgViewWindow",
249 static const GInterfaceInfo modest_header_view_observer_info =
251 (GInterfaceInitFunc) modest_header_view_observer_init,
252 NULL, /* interface_finalize */
253 NULL /* interface_data */
256 g_type_add_interface_static (my_type,
257 MODEST_TYPE_HEADER_VIEW_OBSERVER,
258 &modest_header_view_observer_info);
264 save_state (ModestWindow *self)
266 modest_widget_memory_save (modest_runtime_get_conf (),
268 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
273 restore_settings (ModestMsgViewWindow *self)
275 modest_widget_memory_restore (modest_runtime_get_conf (),
277 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
281 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
283 GObjectClass *gobject_class;
284 ModestWindowClass *modest_window_class;
285 gobject_class = (GObjectClass*) klass;
286 modest_window_class = (ModestWindowClass *) klass;
288 parent_class = g_type_class_peek_parent (klass);
289 gobject_class->finalize = modest_msg_view_window_finalize;
291 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
292 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
293 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
294 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
295 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
296 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
298 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
300 modest_window_class->save_state_func = save_state;
302 signals[MSG_CHANGED_SIGNAL] =
303 g_signal_new ("msg-changed",
304 G_TYPE_FROM_CLASS (gobject_class),
306 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
308 modest_marshal_VOID__POINTER_POINTER,
309 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
312 static void modest_header_view_observer_init(
313 ModestHeaderViewObserverIface *iface_class)
315 iface_class->update_func = modest_msg_view_window_update_model_replaced;
319 modest_msg_view_window_init (ModestMsgViewWindow *obj)
321 ModestMsgViewWindowPrivate *priv;
322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
324 priv->is_search_result = FALSE;
326 priv->msg_view = NULL;
327 priv->header_model = NULL;
328 priv->header_folder_id = NULL;
329 priv->clipboard_change_handler = 0;
330 priv->queue_change_handler = 0;
331 priv->account_removed_handler = 0;
332 priv->row_changed_handler = 0;
333 priv->row_deleted_handler = 0;
334 priv->row_inserted_handler = 0;
335 priv->rows_reordered_handler = 0;
336 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
338 priv->optimized_view = FALSE;
339 priv->progress_bar_timeout = 0;
340 priv->purge_timeout = 0;
341 priv->remove_attachment_banner = NULL;
342 priv->msg_uid = NULL;
347 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
349 ModestMsgViewWindowPrivate *priv = NULL;
351 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
353 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
355 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
357 if (priv->progress_bar_timeout > 0) {
358 g_source_remove (priv->progress_bar_timeout);
359 priv->progress_bar_timeout = 0;
366 set_toolbar_mode (ModestMsgViewWindow *self,
367 ModestToolBarModes mode)
369 ModestWindowPrivate *parent_priv;
370 ModestMsgViewWindowPrivate *priv;
371 /* GtkWidget *widget = NULL; */
373 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
375 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
376 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
378 /* Sets current toolbar mode */
379 priv->current_toolbar_mode = mode;
381 /* Update toolbar dimming state */
382 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
385 case TOOLBAR_MODE_NORMAL:
386 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
387 /* gtk_action_set_sensitive (widget, TRUE); */
388 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
389 /* gtk_action_set_sensitive (widget, TRUE); */
390 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
391 /* gtk_action_set_sensitive (widget, TRUE); */
392 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
393 /* gtk_action_set_sensitive (widget, TRUE); */
395 if (priv->prev_toolitem)
396 gtk_widget_show (priv->prev_toolitem);
398 if (priv->next_toolitem)
399 gtk_widget_show (priv->next_toolitem);
401 if (priv->progress_toolitem)
402 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
403 if (priv->progress_bar)
404 gtk_widget_hide (priv->progress_bar);
406 if (priv->cancel_toolitem)
407 gtk_widget_hide (priv->cancel_toolitem);
409 /* Hide toolbar if optimized view is enabled */
410 if (priv->optimized_view) {
411 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
412 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
416 case TOOLBAR_MODE_TRANSFER:
417 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
418 /* gtk_action_set_sensitive (widget, FALSE); */
419 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
420 /* gtk_action_set_sensitive (widget, FALSE); */
421 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
422 /* gtk_action_set_sensitive (widget, FALSE); */
423 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
424 /* gtk_action_set_sensitive (widget, FALSE); */
426 if (priv->prev_toolitem)
427 gtk_widget_hide (priv->prev_toolitem);
429 if (priv->next_toolitem)
430 gtk_widget_hide (priv->next_toolitem);
432 if (priv->progress_toolitem)
433 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
434 if (priv->progress_bar)
435 gtk_widget_show (priv->progress_bar);
437 if (priv->cancel_toolitem)
438 gtk_widget_show (priv->cancel_toolitem);
440 /* Show toolbar if it's hiden (optimized view ) */
441 if (priv->optimized_view) {
442 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
443 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
448 g_return_if_reached ();
455 menubar_to_menu (GtkUIManager *ui_manager)
457 GtkWidget *main_menu;
459 GList *iter, *children;
461 /* Create new main menu */
462 main_menu = gtk_menu_new();
464 /* Get the menubar from the UI manager */
465 menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
467 iter = children = gtk_container_get_children (GTK_CONTAINER (menubar));
471 menu = GTK_WIDGET (iter->data);
472 gtk_widget_reparent(menu, main_menu);
474 iter = g_list_next (iter);
476 g_list_free (children);
482 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
484 GtkWidget *main_vbox;
485 ModestMsgViewWindowPrivate *priv;
486 ModestWindowPrivate *parent_priv;
488 GtkAction *action = NULL;
490 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
491 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
493 priv->msg_view = modest_msg_view_new (msg);
494 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
495 main_vbox = gtk_vbox_new (FALSE, 6);
498 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
499 conf = modest_runtime_get_conf ();
500 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
501 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
502 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
503 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
504 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
505 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
506 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
507 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
508 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
509 gtk_widget_show (GTK_WIDGET(parent_priv->menubar));
511 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
512 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
513 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
514 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
516 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
517 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
518 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
520 priv->find_toolbar = hildon_find_toolbar_new (NULL);
521 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
522 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
523 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
524 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
526 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);
527 gtk_widget_show_all (GTK_WIDGET(main_vbox));
531 modest_msg_view_window_disconnect_signals (ModestWindow *self)
533 ModestMsgViewWindowPrivate *priv;
534 ModestHeaderView *header_view = NULL;
535 ModestMainWindow *main_window = NULL;
536 ModestWindowMgr *window_mgr = NULL;
538 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
540 if (g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
541 priv->clipboard_change_handler))
542 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
543 priv->clipboard_change_handler);
545 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
546 priv->queue_change_handler))
547 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
548 priv->queue_change_handler);
550 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
551 priv->account_removed_handler))
552 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
553 priv->account_removed_handler);
555 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
556 priv->row_changed_handler))
557 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
558 priv->row_changed_handler);
560 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
561 priv->row_deleted_handler))
562 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
563 priv->row_deleted_handler);
565 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
566 priv->row_inserted_handler))
567 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
568 priv->row_inserted_handler);
570 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
571 priv->rows_reordered_handler))
572 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
573 priv->rows_reordered_handler);
575 window_mgr = modest_runtime_get_window_mgr();
576 g_assert(window_mgr != NULL);
578 main_window = MODEST_MAIN_WINDOW(
579 modest_window_mgr_get_main_window(window_mgr));
581 if(main_window == NULL)
584 header_view = MODEST_HEADER_VIEW(
585 modest_main_window_get_child_widget(
586 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
588 if (header_view == NULL)
591 modest_header_view_remove_observer(header_view,
592 MODEST_HEADER_VIEW_OBSERVER(self));
596 modest_msg_view_window_finalize (GObject *obj)
598 ModestMsgViewWindowPrivate *priv;
600 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
602 /* Sanity check: shouldn't be needed, the window mgr should
603 call this function before */
604 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
606 if (priv->header_model != NULL) {
607 g_object_unref (priv->header_model);
608 priv->header_model = NULL;
611 if (priv->progress_bar_timeout > 0) {
612 g_source_remove (priv->progress_bar_timeout);
613 priv->progress_bar_timeout = 0;
616 if (priv->remove_attachment_banner) {
617 gtk_widget_destroy (priv->remove_attachment_banner);
618 g_object_unref (priv->remove_attachment_banner);
619 priv->remove_attachment_banner = NULL;
622 if (priv->purge_timeout > 0) {
623 g_source_remove (priv->purge_timeout);
624 priv->purge_timeout = 0;
627 if (priv->row_reference) {
628 gtk_tree_row_reference_free (priv->row_reference);
629 priv->row_reference = NULL;
632 if (priv->next_row_reference) {
633 gtk_tree_row_reference_free (priv->next_row_reference);
634 priv->next_row_reference = NULL;
638 g_free (priv->msg_uid);
639 priv->msg_uid = NULL;
642 G_OBJECT_CLASS(parent_class)->finalize (obj);
646 select_next_valid_row (GtkTreeModel *model,
647 GtkTreeRowReference **row_reference,
650 GtkTreeIter tmp_iter;
651 GtkTreePath *path, *next;
652 gboolean retval = FALSE;
654 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
656 path = gtk_tree_row_reference_get_path (*row_reference);
657 gtk_tree_model_get_iter (model, &tmp_iter, path);
658 gtk_tree_row_reference_free (*row_reference);
659 *row_reference = NULL;
661 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
662 next = gtk_tree_model_get_path (model, &tmp_iter);
663 *row_reference = gtk_tree_row_reference_new (model, next);
665 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
666 next = gtk_tree_model_get_path (model, &tmp_iter);
668 /* Ensure that we are not selecting the same */
669 if (gtk_tree_path_compare (path, next) != 0) {
670 *row_reference = gtk_tree_row_reference_new (model, next);
676 gtk_tree_path_free (path);
681 /* TODO: This should be in _init(), with the parameters as properties. */
683 modest_msg_view_window_construct (ModestMsgViewWindow *self, TnyMsg *msg,
684 const gchar *modest_account_name,
685 const gchar *msg_uid)
688 ModestMsgViewWindowPrivate *priv = NULL;
689 ModestWindowPrivate *parent_priv = NULL;
690 ModestDimmingRulesGroup *menu_rules_group = NULL;
691 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
692 GtkActionGroup *action_group = NULL;
693 GError *error = NULL;
694 GdkPixbuf *window_icon;
696 g_return_if_fail (msg);
698 obj = G_OBJECT (self);
699 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
700 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
702 priv->msg_uid = g_strdup (msg_uid);
704 parent_priv->ui_manager = gtk_ui_manager_new();
705 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
707 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
708 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
710 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
711 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
713 /* Add common actions */
714 gtk_action_group_add_actions (action_group,
715 modest_action_entries,
716 G_N_ELEMENTS (modest_action_entries),
718 gtk_action_group_add_toggle_actions (action_group,
719 modest_toggle_action_entries,
720 G_N_ELEMENTS (modest_toggle_action_entries),
722 gtk_action_group_add_toggle_actions (action_group,
723 msg_view_toggle_action_entries,
724 G_N_ELEMENTS (msg_view_toggle_action_entries),
726 gtk_action_group_add_radio_actions (action_group,
727 msg_view_zoom_action_entries,
728 G_N_ELEMENTS (msg_view_zoom_action_entries),
730 G_CALLBACK (modest_ui_actions_on_change_zoom),
733 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
734 g_object_unref (action_group);
736 /* Load the UI definition */
737 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
740 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
741 g_error_free (error);
746 /* Add common dimming rules */
747 modest_dimming_rules_group_add_rules (menu_rules_group,
748 modest_msg_view_menu_dimming_entries,
749 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
751 modest_dimming_rules_group_add_rules (toolbar_rules_group,
752 modest_msg_view_toolbar_dimming_entries,
753 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
756 /* Insert dimming rules group for this window */
757 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
758 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
759 g_object_unref (menu_rules_group);
760 g_object_unref (toolbar_rules_group);
762 /* Add accelerators */
763 gtk_window_add_accel_group (GTK_WINDOW (obj),
764 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
767 init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
768 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
770 /* Set window icon */
771 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
773 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
774 g_object_unref (window_icon);
777 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
779 g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
780 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
781 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
782 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
783 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
784 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
785 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
786 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
787 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
788 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
790 g_signal_connect (G_OBJECT (obj), "key-release-event",
791 G_CALLBACK (modest_msg_view_window_key_release_event),
794 g_signal_connect (G_OBJECT (obj), "window-state-event",
795 G_CALLBACK (modest_msg_view_window_window_state_event),
798 /* Mail Operation Queue */
799 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
801 G_CALLBACK (on_queue_changed),
804 /* Account manager */
805 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
807 G_CALLBACK(on_account_removed),
810 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
812 priv->last_search = NULL;
814 /* Init the clipboard actions dim status */
815 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
817 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
819 /* Check toolbar dimming rules */
820 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
825 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
826 const gchar *modest_account_name,
827 const gchar *msg_uid,
829 GtkTreeRowReference *row_reference)
831 ModestMsgViewWindow *window = NULL;
832 ModestMsgViewWindowPrivate *priv = NULL;
833 TnyFolder *header_folder = NULL;
834 ModestHeaderView *header_view = NULL;
835 ModestMainWindow *main_window = NULL;
836 ModestWindowMgr *window_mgr = NULL;
838 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
839 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
840 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
842 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
844 /* Remember the message list's TreeModel so we can detect changes
845 * and change the list selection when necessary: */
846 window_mgr = modest_runtime_get_window_mgr();
847 g_assert(window_mgr != NULL);
848 main_window = MODEST_MAIN_WINDOW(
849 modest_window_mgr_get_main_window(window_mgr));
850 g_assert(main_window != NULL);
851 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
852 main_window, MODEST_WIDGET_TYPE_HEADER_VIEW));
853 if (header_view != NULL){
854 header_folder = modest_header_view_get_folder(header_view);
855 g_assert(header_folder != NULL);
856 priv->header_folder_id = tny_folder_get_id(header_folder);
857 g_assert(priv->header_folder_id != NULL);
858 g_object_unref(header_folder);
861 priv->header_model = g_object_ref(model);
862 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
863 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
864 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
866 priv->row_changed_handler = g_signal_connect(
867 GTK_TREE_MODEL(model), "row-changed",
868 G_CALLBACK(modest_msg_view_window_on_row_changed),
870 priv->row_deleted_handler = g_signal_connect(
871 GTK_TREE_MODEL(model), "row-deleted",
872 G_CALLBACK(modest_msg_view_window_on_row_deleted),
874 priv->row_inserted_handler = g_signal_connect (
875 GTK_TREE_MODEL(model), "row-inserted",
876 G_CALLBACK(modest_msg_view_window_on_row_inserted),
878 priv->rows_reordered_handler = g_signal_connect(
879 GTK_TREE_MODEL(model), "rows-reordered",
880 G_CALLBACK(modest_msg_view_window_on_row_reordered),
883 if (header_view != NULL){
884 modest_header_view_add_observer(header_view,
885 MODEST_HEADER_VIEW_OBSERVER(window));
888 modest_msg_view_window_update_priority (window);
890 /* Check toolbar dimming rules */
891 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
893 return MODEST_WINDOW(window);
897 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
898 const gchar *modest_account_name,
899 const gchar *msg_uid)
901 ModestMsgViewWindow *window = NULL;
902 ModestMsgViewWindowPrivate *priv = NULL;
904 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
905 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
906 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
908 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
910 /* Remember that this is a search result,
911 * so we can disable some UI appropriately: */
912 priv->is_search_result = TRUE;
914 return MODEST_WINDOW(window);
918 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
919 const gchar *modest_account_name,
920 const gchar *msg_uid)
923 g_return_val_if_fail (msg, NULL);
925 obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
926 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
927 msg, modest_account_name, msg_uid);
929 return MODEST_WINDOW(obj);
932 void modest_msg_view_window_on_row_changed(
933 GtkTreeModel *header_model,
936 ModestMsgViewWindow *window){
937 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
940 void modest_msg_view_window_on_row_deleted(
941 GtkTreeModel *header_model,
943 ModestMsgViewWindow *window){
944 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
947 /* On insertions we check if the folder still has the message we are
948 * showing or do not. If do not, we do nothing. Which means we are still
949 * not attached to any header folder and thus next/prev buttons are
950 * still dimmed. Once the message that is shown by msg-view is found, the
951 * new model of header-view will be attached and the references will be set.
952 * On each further insertions dimming rules will be checked. However
953 * this requires extra CPU time at least works.
954 * (An message might be deleted from TnyFolder and thus will not be
955 * inserted into the model again for example if it is removed by the
956 * imap server and the header view is refreshed.)
958 void modest_msg_view_window_on_row_inserted(
959 GtkTreeModel *new_model,
960 GtkTreePath *tree_path,
961 GtkTreeIter *tree_iter,
962 ModestMsgViewWindow *window){
963 ModestMsgViewWindowPrivate *priv = NULL;
964 TnyHeader *header = NULL;
967 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
969 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
971 /* If we already has a model attached then the message shown by
972 * msg-view is in it, and thus we do not need any actions but
973 * to check the dimming rules.*/
974 if(priv->header_model != NULL){
975 gtk_tree_row_reference_free(priv->next_row_reference);
976 priv->next_row_reference = gtk_tree_row_reference_copy(
977 priv->row_reference);
978 select_next_valid_row (priv->header_model,
979 &(priv->next_row_reference), FALSE);
980 modest_ui_actions_check_toolbar_dimming_rules (
981 MODEST_WINDOW (window));
985 /* Check if the newly inserted message is the same we are actually
986 * showing. IF not, we should remain detached from the header model
987 * and thus prev and next toolbarbuttons should remain dimmed. */
988 gtk_tree_model_get (new_model, tree_iter,
989 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
990 uid = modest_tny_folder_get_header_unique_id(header);
991 if(!g_str_equal(priv->msg_uid, uid)){
997 /* Setup row_reference for the actual msg. */
998 priv->row_reference = gtk_tree_row_reference_new(
999 new_model, tree_path);
1000 if(priv->row_reference == NULL){
1001 g_warning("No reference for msg header item.");
1005 /* Attach new_model and connect some callback to it to become able
1006 * to detect changes in header-view. */
1007 priv->header_model = g_object_ref(new_model);
1008 g_signal_connect (new_model, "row-changed",
1009 G_CALLBACK (modest_msg_view_window_on_row_changed),
1011 g_signal_connect (new_model, "row-deleted",
1012 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1014 g_signal_connect (new_model, "rows-reordered",
1015 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1018 /* Now set up next_row_reference. */
1019 priv->next_row_reference = gtk_tree_row_reference_copy(
1020 priv->row_reference);
1021 select_next_valid_row (priv->header_model,
1022 &(priv->next_row_reference), FALSE);
1024 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1027 void modest_msg_view_window_on_row_reordered(
1028 GtkTreeModel *header_model,
1032 ModestMsgViewWindow *window){
1033 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1036 /* The modest_msg_view_window_update_model_replaced implements update
1037 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1038 * actually belongs to the header-view is the same as the TnyFolder of
1039 * the message of msg-view or not. If they are different, there is
1040 * nothing to do. If they are the same, then the model has replaced and
1041 * the reference in msg-view shall be replaced from the old model to
1042 * the new model. In this case the view will be detached from it's
1043 * header folder. From this point the next/prev buttons are dimmed.
1045 void modest_msg_view_window_update_model_replaced(
1046 ModestHeaderViewObserver *observer,
1047 GtkTreeModel *model,
1048 const gchar *tny_folder_id){
1049 ModestMsgViewWindowPrivate *priv = NULL;
1050 ModestMsgViewWindow *window = NULL;
1052 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1053 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1055 window = MODEST_MSG_VIEW_WINDOW(observer);
1056 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1058 /* If there is an other folder in the header-view then we do
1059 * not care about it's model (msg list). Else if the
1060 * header-view shows the folder the msg shown by us is in, we
1061 * shall replace our model reference and make some check. */
1062 if(tny_folder_id == NULL ||
1063 !g_str_equal(tny_folder_id, priv->header_folder_id))
1066 /* Model is changed(replaced), so we should forget the old
1067 * one. Because there might be other references and there
1068 * might be some change on the model even if we unreferenced
1069 * it, we need to disconnect our signals here. */
1070 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1071 priv->row_changed_handler))
1072 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1073 priv->row_changed_handler);
1074 priv->row_changed_handler = 0;
1075 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1076 priv->row_deleted_handler))
1077 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1078 priv->row_deleted_handler);
1079 priv->row_deleted_handler = 0;
1080 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1081 priv->row_inserted_handler))
1082 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1083 priv->row_inserted_handler);
1084 priv->row_inserted_handler = 0;
1085 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1086 priv->rows_reordered_handler))
1087 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1088 priv->rows_reordered_handler);
1089 priv->rows_reordered_handler = 0;
1090 g_object_unref(priv->header_model);
1091 priv->header_model = NULL;
1092 g_object_unref(priv->row_reference);
1093 priv->row_reference = NULL;
1094 g_object_unref(priv->next_row_reference);
1095 priv->next_row_reference = NULL;
1097 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1099 if(tny_folder_id == NULL)
1102 g_assert(model != NULL);
1104 /* Also we must connect to the new model for row insertions.
1105 * Only for insertions now. We will need other ones only after
1106 * the msg is show by msg-view is added to the new model. */
1107 priv->row_inserted_handler = g_signal_connect (
1108 model, "row-inserted",
1109 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1114 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1116 ModestMsgViewWindowPrivate *priv= NULL;
1118 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1119 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1121 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1125 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1127 ModestMsgViewWindowPrivate *priv= NULL;
1129 TnyHeader *header = NULL;
1130 GtkTreePath *path = NULL;
1133 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1134 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1136 /* If the message was not obtained from a treemodel,
1137 * for instance if it was opened directly by the search UI:
1139 if (priv->header_model == NULL) {
1140 msg = modest_msg_view_window_get_message (self);
1141 header = tny_msg_get_header (msg);
1142 g_object_unref (msg);
1146 /* Get iter of the currently selected message in the header view: */
1147 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1148 * instead of sometimes retrieving it from the header view?
1149 * Then we wouldn't be dependent on the message actually still being selected
1150 * in the header view. murrayc. */
1151 path = gtk_tree_row_reference_get_path (priv->row_reference);
1152 g_return_val_if_fail (path != NULL, NULL);
1153 gtk_tree_model_get_iter (priv->header_model,
1157 /* Get current message header */
1158 gtk_tree_model_get (priv->header_model, &iter,
1159 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1162 gtk_tree_path_free (path);
1167 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1169 ModestMsgView *msg_view;
1170 ModestMsgViewWindowPrivate *priv;
1172 g_return_val_if_fail (self, NULL);
1174 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1176 msg_view = MODEST_MSG_VIEW (priv->msg_view);
1178 return modest_msg_view_get_message (msg_view);
1182 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1184 ModestMsgViewWindowPrivate *priv;
1186 g_return_val_if_fail (self, NULL);
1188 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1190 return (const gchar*) priv->msg_uid;
1194 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1197 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1198 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1199 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1203 is_active = gtk_toggle_action_get_active (toggle);
1206 gtk_widget_show (priv->find_toolbar);
1207 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1209 gtk_widget_hide (priv->find_toolbar);
1212 /* update the toggle buttons status */
1213 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1214 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1215 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1216 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1221 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1222 ModestMsgViewWindow *obj)
1224 GtkToggleAction *toggle;
1225 ModestWindowPrivate *parent_priv;
1226 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1228 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1229 gtk_toggle_action_set_active (toggle, FALSE);
1233 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1234 ModestMsgViewWindow *obj)
1236 gchar *current_search;
1237 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1239 if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
1240 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1244 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1246 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1247 g_free (current_search);
1248 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1252 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1254 g_free (priv->last_search);
1255 priv->last_search = g_strdup (current_search);
1256 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
1259 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1260 g_free (priv->last_search);
1261 priv->last_search = NULL;
1264 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
1265 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1266 g_free (priv->last_search);
1267 priv->last_search = NULL;
1271 g_free (current_search);
1276 modest_msg_view_window_set_zoom (ModestWindow *window,
1279 ModestMsgViewWindowPrivate *priv;
1280 ModestWindowPrivate *parent_priv;
1281 GtkAction *action = NULL;
1282 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1284 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1286 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1287 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1288 modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
1290 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1291 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1293 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1297 modest_msg_view_window_get_zoom (ModestWindow *window)
1299 ModestMsgViewWindowPrivate *priv;
1301 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1303 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1304 return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
1308 modest_msg_view_window_zoom_plus (ModestWindow *window)
1310 ModestWindowPrivate *parent_priv;
1311 GtkRadioAction *zoom_radio_action;
1312 GSList *group, *node;
1314 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1315 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1316 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1318 group = gtk_radio_action_get_group (zoom_radio_action);
1320 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1321 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1325 for (node = group; node != NULL; node = g_slist_next (node)) {
1326 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1327 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1335 modest_msg_view_window_zoom_minus (ModestWindow *window)
1337 ModestWindowPrivate *parent_priv;
1338 GtkRadioAction *zoom_radio_action;
1339 GSList *group, *node;
1341 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1342 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1343 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1345 group = gtk_radio_action_get_group (zoom_radio_action);
1347 for (node = group; node != NULL; node = g_slist_next (node)) {
1348 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1349 if (node->next != NULL) {
1350 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1353 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1363 modest_msg_view_window_key_release_event (GtkWidget *window,
1367 if (event->type == GDK_KEY_RELEASE) {
1368 switch (event->keyval) {
1370 modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1374 modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1387 modest_msg_view_window_scroll_up (ModestWindow *window)
1389 ModestMsgViewWindowPrivate *priv;
1390 gboolean return_value;
1392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1393 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1397 modest_msg_view_window_scroll_down (ModestWindow *window)
1399 ModestMsgViewWindowPrivate *priv;
1400 gboolean return_value;
1402 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1403 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1407 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1410 ModestMsgViewWindowPrivate *priv;
1411 GtkTreeIter tmp_iter;
1412 gboolean is_last_selected;
1414 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1417 /*if no model (so no rows at all), then virtually we are the last*/
1418 if (!priv->header_model)
1421 path = gtk_tree_row_reference_get_path (priv->row_reference);
1425 is_last_selected = TRUE;
1426 while (is_last_selected) {
1428 gtk_tree_path_next (path);
1429 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1431 gtk_tree_model_get (priv->header_model, &tmp_iter,
1432 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1434 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1435 is_last_selected = FALSE;
1437 gtk_tree_path_free (path);
1438 return is_last_selected;
1442 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1444 ModestMsgViewWindowPrivate *priv;
1446 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1447 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1449 return priv->header_model != NULL;
1453 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1455 ModestMsgViewWindowPrivate *priv;
1457 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1458 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1460 return priv->is_search_result;
1464 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1467 ModestMsgViewWindowPrivate *priv;
1468 gboolean is_first_selected;
1469 GtkTreeIter tmp_iter;
1470 /* gchar * path_string;*/
1472 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1473 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1475 /*if no model (so no rows at all), then virtually we are the first*/
1476 if (!priv->header_model)
1479 path = gtk_tree_row_reference_get_path (priv->row_reference);
1483 /* path_string = gtk_tree_path_to_string (path);
1484 is_first_selected = strcmp (path_string, "0");
1486 g_free (path_string);
1487 gtk_tree_path_free (path);
1489 return is_first_selected;*/
1491 is_first_selected = TRUE;
1492 while (is_first_selected) {
1494 if(!gtk_tree_path_prev (path))
1496 /* Here the 'if' is needless for logic, but let make sure
1497 * iter is valid for gtk_tree_model_get. */
1498 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1500 gtk_tree_model_get (priv->header_model, &tmp_iter,
1501 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1503 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1504 is_first_selected = FALSE;
1506 gtk_tree_path_free (path);
1507 return is_first_selected;
1511 * Reads the message whose summary item is @header. It takes care of
1512 * several things, among others:
1514 * If the message was not previously downloaded then ask the user
1515 * before downloading. If there is no connection launch the connection
1516 * dialog. Update toolbar dimming rules.
1518 * Returns: TRUE if the mail operation was started, otherwise if the
1519 * user do not want to download the message, or if the user do not
1520 * want to connect, then the operation is not issued
1523 message_reader (ModestMsgViewWindow *window,
1524 ModestMsgViewWindowPrivate *priv,
1528 ModestMailOperation *mail_op = NULL;
1529 ModestMailOperationTypeOperation op_type;
1530 gboolean already_showing = FALSE;
1531 ModestWindow *msg_window = NULL;
1532 ModestWindowMgr *mgr;
1534 g_return_val_if_fail (path != NULL, FALSE);
1536 mgr = modest_runtime_get_window_mgr ();
1537 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1538 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1541 gtk_window_present (GTK_WINDOW (msg_window));
1542 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1546 /* Msg download completed */
1547 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1548 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1550 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1552 /* Ask the user if he wants to download the message if
1554 if (!tny_device_is_online (modest_runtime_get_device())) {
1555 TnyFolder *folder = NULL;
1556 GtkResponseType response;
1558 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1559 _("mcen_nc_get_msg"));
1560 if (response == GTK_RESPONSE_CANCEL)
1563 /* Offer the connection dialog if necessary */
1564 folder = tny_header_get_folder (header);
1566 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1567 TNY_FOLDER_STORE (folder))) {
1568 g_object_unref (folder);
1571 g_object_unref (folder);
1576 /* New mail operation */
1577 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1579 modest_ui_actions_get_msgs_full_error_handler,
1582 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1583 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1584 g_object_unref (mail_op);
1586 /* Update toolbar dimming rules */
1587 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1593 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1595 ModestMsgViewWindowPrivate *priv;
1596 GtkTreePath *path= NULL;
1597 GtkTreeIter tmp_iter;
1599 gboolean retval = TRUE;
1601 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1602 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1604 /* Update the next row reference if it's not valid. This could
1605 happen if for example the header which it was pointing to,
1606 was deleted. The best place to do it is in the row-deleted
1607 handler but the tinymail model do not work like the glib
1608 tree models and reports the deletion when the row is still
1610 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1611 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1612 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1613 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1616 if (priv->next_row_reference)
1617 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1621 gtk_tree_model_get_iter (priv->header_model,
1625 gtk_tree_model_get (priv->header_model, &tmp_iter,
1626 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1629 /* Read the message & show it */
1630 if (!message_reader (window, priv, header, path)) {
1632 gtk_tree_path_free (path);
1636 g_object_unref (header);
1642 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1644 ModestMsgViewWindowPrivate *priv = NULL;
1645 TnyHeader *header = NULL;
1649 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1650 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1652 /* Check that the model is not empty */
1653 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1656 /* Get the header */
1657 gtk_tree_model_get (priv->header_model,
1659 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1661 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1662 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1663 g_object_unref (header);
1664 return modest_msg_view_window_select_next_message (self);
1667 path = gtk_tree_model_get_path (priv->header_model, &iter);
1669 /* Read the message & show it */
1670 message_reader (self, priv, header, path);
1673 g_object_unref (header);
1679 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1681 ModestMsgViewWindowPrivate *priv = NULL;
1684 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1687 /* Return inmediatly if there is no header model */
1688 if (!priv->header_model)
1691 path = gtk_tree_row_reference_get_path (priv->row_reference);
1692 while (gtk_tree_path_prev (path)) {
1696 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1697 gtk_tree_model_get (priv->header_model, &iter,
1698 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1702 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1703 g_object_unref (header);
1707 /* Read the message & show it */
1708 if (!message_reader (window, priv, header, path)) {
1709 g_object_unref (header);
1713 g_object_unref (header);
1718 gtk_tree_path_free (path);
1723 view_msg_cb (ModestMailOperation *mail_op,
1728 ModestMsgViewWindow *self = NULL;
1729 ModestMsgViewWindowPrivate *priv = NULL;
1732 /* If there was any error */
1733 path = (GtkTreePath *) user_data;
1734 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1735 gtk_tree_path_free (path);
1739 /* Get the window */
1740 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1741 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1742 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1744 /* Update the row reference */
1745 gtk_tree_row_reference_free (priv->row_reference);
1746 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1747 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1748 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1749 gtk_tree_path_free (path);
1751 /* Mark header as read */
1752 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1753 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1755 /* Set new message */
1756 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1757 modest_msg_view_window_update_priority (self);
1758 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1759 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1761 /* Set the new message uid of the window */
1762 if (priv->msg_uid) {
1763 g_free (priv->msg_uid);
1764 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1767 /* Notify the observers */
1768 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1769 0, priv->header_model, priv->row_reference);
1771 /* Free new references */
1772 g_object_unref (self);
1776 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1778 ModestMsgViewWindowPrivate *priv;
1780 TnyFolderType folder_type;
1782 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1784 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1786 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1790 folder = tny_msg_get_folder (msg);
1793 folder_type = tny_folder_get_folder_type (folder);
1795 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1796 const gchar *fname = tny_folder_get_name (folder);
1797 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1800 g_object_unref (folder);
1802 g_object_unref (msg);
1810 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1812 ModestMsgViewWindowPrivate *priv;
1813 TnyHeaderFlags flags = 0;
1815 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1817 if (priv->header_model) {
1820 GtkTreePath *path = NULL;
1822 path = gtk_tree_row_reference_get_path (priv->row_reference);
1823 g_return_if_fail (path != NULL);
1824 gtk_tree_model_get_iter (priv->header_model,
1826 gtk_tree_row_reference_get_path (priv->row_reference));
1828 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1830 flags = tny_header_get_flags (header);
1831 gtk_tree_path_free (path);
1834 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1839 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1841 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1842 ModestWindowPrivate *parent_priv;
1843 ModestWindowMgr *mgr;
1844 gboolean is_fullscreen;
1845 GtkAction *fs_toggle_action;
1848 mgr = modest_runtime_get_window_mgr ();
1849 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1851 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1853 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1854 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1855 if (is_fullscreen != active) {
1856 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1865 set_homogeneous (GtkWidget *widget,
1868 if (GTK_IS_TOOL_ITEM (widget)) {
1869 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1870 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1875 modest_msg_view_window_show_toolbar (ModestWindow *self,
1876 gboolean show_toolbar)
1878 ModestMsgViewWindowPrivate *priv = NULL;
1879 ModestWindowPrivate *parent_priv;
1880 GtkWidget *reply_button = NULL, *menu = NULL;
1881 GtkWidget *placeholder = NULL;
1883 const gchar *action_name;
1886 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1887 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1889 /* Set optimized view status */
1890 priv->optimized_view = !show_toolbar;
1892 if (!parent_priv->toolbar) {
1893 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1895 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1897 /* Set homogeneous toolbar */
1898 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1899 set_homogeneous, NULL);
1901 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1902 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1903 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1904 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1905 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1906 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1907 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1908 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1910 /* Add ProgressBar (Transfer toolbar) */
1911 priv->progress_bar = modest_progress_bar_widget_new ();
1912 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1913 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1914 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1915 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1916 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1918 /* Connect cancel 'clicked' signal to abort progress mode */
1919 g_signal_connect(priv->cancel_toolitem, "clicked",
1920 G_CALLBACK(cancel_progressbar),
1923 /* Add it to the observers list */
1924 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1927 hildon_window_add_toolbar (HILDON_WINDOW (self),
1928 GTK_TOOLBAR (parent_priv->toolbar));
1931 /* Set reply button tap and hold menu */
1932 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1933 "/ToolBar/ToolbarMessageReply");
1934 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1935 "/ToolbarReplyCSM");
1936 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1940 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1941 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1942 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1944 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1945 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1948 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1949 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1952 /* Update also the actions (to update the toggles in the
1953 menus), we have to do it manually because some other window
1954 of the same time could have changed it (remember that the
1955 toolbar fullscreen mode is shared by all the windows of the
1957 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
1958 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
1960 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
1962 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
1963 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
1968 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1970 ModestMsgViewWindow *window)
1972 ModestWindowPrivate *parent_priv;
1973 /* GtkAction *action; */
1974 gboolean is_address;
1978 if (!GTK_WIDGET_VISIBLE (window))
1981 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1982 selection = gtk_clipboard_wait_for_text (clipboard);
1984 is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1986 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1987 /* gtk_action_set_sensitive (action, is_address); */
1989 focused = gtk_window_get_focus (GTK_WINDOW (window));
1991 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1992 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1994 /* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1995 /* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1998 /* modest_msg_view_window_update_dimmed (window); */
2003 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2005 ModestMsgViewWindowPrivate *priv;
2007 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2008 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2010 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2014 cancel_progressbar (GtkToolButton *toolbutton,
2015 ModestMsgViewWindow *self)
2018 ModestMsgViewWindowPrivate *priv;
2020 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2022 /* Get operation observers and cancel its current operation */
2023 tmp = priv->progress_widgets;
2025 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2026 tmp=g_slist_next(tmp);
2030 observers_empty (ModestMsgViewWindow *self)
2033 ModestMsgViewWindowPrivate *priv;
2034 gboolean is_empty = TRUE;
2035 guint pending_ops = 0;
2037 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2038 tmp = priv->progress_widgets;
2040 /* Check all observers */
2041 while (tmp && is_empty) {
2042 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2043 is_empty = pending_ops == 0;
2045 tmp = g_slist_next(tmp);
2052 on_account_removed (TnyAccountStore *account_store,
2053 TnyAccount *account,
2056 /* Do nothing if it's a transport account, because we only
2057 show the messages of a store account */
2058 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2059 const gchar *parent_acc = NULL;
2060 const gchar *our_acc = NULL;
2062 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2063 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2065 /* Close this window if I'm showing a message of the removed account */
2066 if (strcmp (parent_acc, our_acc) == 0)
2067 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2072 on_queue_changed (ModestMailOperationQueue *queue,
2073 ModestMailOperation *mail_op,
2074 ModestMailOperationQueueNotification type,
2075 ModestMsgViewWindow *self)
2078 ModestMsgViewWindowPrivate *priv;
2079 ModestMailOperationTypeOperation op_type;
2080 ModestToolBarModes mode;
2082 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2083 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2085 /* If this operations was created by another window, do nothing */
2086 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2089 /* Get toolbar mode from operation id*/
2090 op_type = modest_mail_operation_get_type_operation (mail_op);
2092 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
2093 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2094 case MODEST_MAIL_OPERATION_TYPE_OPEN:
2095 mode = TOOLBAR_MODE_TRANSFER;
2098 mode = TOOLBAR_MODE_NORMAL;
2102 /* Add operation observers and change toolbar if neccessary*/
2103 tmp = priv->progress_widgets;
2105 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2106 if (mode == TOOLBAR_MODE_TRANSFER) {
2107 /* Enable transfer toolbar mode */
2108 set_toolbar_transfer_mode(self);
2110 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2112 tmp = g_slist_next (tmp);
2117 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2118 if (mode == TOOLBAR_MODE_TRANSFER) {
2120 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2122 tmp = g_slist_next (tmp);
2126 /* If no more operations are being observed, NORMAL mode is enabled again */
2127 if (observers_empty (self)) {
2128 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2136 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2138 ModestMsgViewWindowPrivate *priv;
2139 GList *selected_attachments = NULL;
2141 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2142 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2144 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2146 return selected_attachments;
2150 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2152 ModestMsgViewWindowPrivate *priv;
2153 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2154 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2156 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2158 if (mime_part == NULL) {
2159 gboolean error = FALSE;
2160 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2161 if (selected_attachments == NULL) {
2163 } else if (g_list_length (selected_attachments) > 1) {
2164 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2167 mime_part = (TnyMimePart *) selected_attachments->data;
2168 g_object_ref (mime_part);
2170 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2171 g_list_free (selected_attachments);
2176 g_object_ref (mime_part);
2179 if (tny_mime_part_is_purged (mime_part)) {
2180 g_object_unref (mime_part);
2184 if (!TNY_IS_MSG (mime_part)) {
2185 gchar *filepath = NULL;
2186 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2187 TnyFsStream *temp_stream = NULL;
2189 temp_stream = modest_maemo_utils_create_temp_stream (att_filename, &filepath);
2192 const gchar *content_type;
2193 content_type = tny_mime_part_get_content_type (mime_part);
2194 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2196 modest_platform_activate_file (filepath, content_type);
2197 g_object_unref (temp_stream);
2199 /* NOTE: files in the temporary area will be automatically
2200 * cleaned after some time if they are no longer in use */
2203 /* message attachment */
2204 TnyHeader *header = NULL;
2205 ModestWindowMgr *mgr;
2206 ModestWindow *msg_win = NULL;
2209 header = tny_msg_get_header (TNY_MSG (mime_part));
2210 mgr = modest_runtime_get_window_mgr ();
2211 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2214 if (msg_win) /* there is already a window for this uid; top it */
2215 gtk_window_present (GTK_WINDOW(msg_win));
2217 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2218 * thus, we don't do anything */
2219 g_warning ("window for is already being created");
2221 /* it's not found, so create a new window for it */
2222 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
2223 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2225 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2226 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
2227 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2228 modest_window_get_zoom (MODEST_WINDOW (window)));
2229 modest_window_mgr_register_window (mgr, msg_win);
2230 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2231 gtk_widget_show_all (GTK_WIDGET (msg_win));
2234 g_object_unref (mime_part);
2250 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2251 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2252 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2253 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2256 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2260 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2261 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2262 g_free (pair->filename);
2263 g_object_unref (pair->part);
2264 g_slice_free (SaveMimePartPair, pair);
2266 g_list_free (info->pairs);
2269 gtk_widget_destroy (info->banner);
2270 g_object_unref (info->banner);
2271 g_slice_free (SaveMimePartInfo, info);
2276 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2278 if (info->pairs != NULL) {
2279 save_mime_part_to_file (info);
2282 result = info->result;
2284 /* This is a GDK lock because we are an idle callback and
2285 * hildon_banner_show_information is or does Gtk+ code */
2287 gdk_threads_enter (); /* CHECKED */
2288 save_mime_part_info_free (info, TRUE);
2290 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2292 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2294 gdk_threads_leave (); /* CHECKED */
2301 save_mime_part_to_file (SaveMimePartInfo *info)
2303 GnomeVFSResult result;
2304 GnomeVFSHandle *handle;
2306 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2308 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2309 if (result == GNOME_VFS_OK) {
2310 stream = tny_vfs_stream_new (handle);
2311 tny_mime_part_decode_to_stream (pair->part, stream);
2312 g_object_unref (G_OBJECT (stream));
2313 g_object_unref (pair->part);
2314 g_slice_free (SaveMimePartPair, pair);
2315 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2316 info->result = TRUE;
2318 save_mime_part_info_free (info, FALSE);
2319 info->result = FALSE;
2322 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2327 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2329 gboolean is_ok = TRUE;
2330 gint replaced_files = 0;
2331 const GList *files = info->pairs;
2334 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2335 SaveMimePartPair *pair = iter->data;
2336 if (modest_maemo_utils_file_exists (pair->filename)) {
2340 if (replaced_files) {
2341 GtkWidget *confirm_overwrite_dialog;
2342 const gchar *message = (replaced_files == 1) ?
2343 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2344 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2345 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2348 gtk_widget_destroy (confirm_overwrite_dialog);
2352 save_mime_part_info_free (info, TRUE);
2354 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2361 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2363 gboolean clean_list = FALSE;
2364 ModestMsgViewWindowPrivate *priv;
2365 GList *files_to_save = NULL;
2366 GtkWidget *save_dialog = NULL;
2367 gchar *folder = NULL;
2368 gboolean canceled = FALSE;
2369 const gchar *filename = NULL;
2370 gchar *save_multiple_str = NULL;
2372 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2373 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2375 if (mime_parts == NULL) {
2376 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2377 if (mime_parts == NULL)
2382 /* prepare dialog */
2383 if (mime_parts->next == NULL) {
2384 /* only one attachment selected */
2385 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2386 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2387 filename = tny_mime_part_get_filename (mime_part);
2389 g_warning ("Tried to save a non-file attachment");
2393 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2394 g_list_length (mime_parts));
2397 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2398 GTK_FILE_CHOOSER_ACTION_SAVE);
2401 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2402 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2406 if (filename != NULL)
2407 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2410 /* if multiple, set multiple string */
2411 if (save_multiple_str) {
2412 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2416 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2417 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2419 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2420 hildon_banner_show_information
2421 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2425 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2426 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2428 if (tny_mime_part_is_attachment (mime_part)) {
2429 SaveMimePartPair *pair;
2431 if ((mime_parts->next != NULL) &&
2432 (tny_mime_part_get_filename (mime_part) == NULL))
2435 pair = g_slice_new0 (SaveMimePartPair);
2436 if (mime_parts->next == NULL) {
2437 pair->filename = g_strdup (chooser_uri);
2440 g_build_filename (chooser_uri,
2441 tny_mime_part_get_filename (mime_part), NULL);
2443 pair->part = g_object_ref (mime_part);
2444 files_to_save = g_list_prepend (files_to_save, pair);
2448 g_free (chooser_uri);
2451 gtk_widget_destroy (save_dialog);
2454 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2455 g_list_free (mime_parts);
2458 if (files_to_save != NULL) {
2459 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2460 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2461 _CS("sfil_ib_saving"));
2462 info->pairs = files_to_save;
2463 info->banner = banner;
2464 info->result = TRUE;
2465 g_object_ref (banner);
2466 save_mime_parts_to_file_with_checks (info);
2471 show_remove_attachment_information (gpointer userdata)
2473 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2474 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2476 if (priv->remove_attachment_banner != NULL) {
2477 gtk_widget_destroy (priv->remove_attachment_banner);
2478 g_object_unref (priv->remove_attachment_banner);
2481 priv->remove_attachment_banner = g_object_ref (
2482 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2488 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2490 ModestMsgViewWindowPrivate *priv;
2491 GList *mime_parts = NULL, *node;
2492 gchar *confirmation_message;
2496 /* TnyFolder *folder; */
2498 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2499 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2502 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2504 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2506 /* Remove already purged messages from mime parts list */
2508 while (node != NULL) {
2509 TnyMimePart *part = TNY_MIME_PART (node->data);
2510 if (tny_mime_part_is_purged (part)) {
2511 GList *deleted_node = node;
2512 node = g_list_next (node);
2513 g_object_unref (part);
2514 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2516 node = g_list_next (node);
2520 if (mime_parts == NULL)
2523 n_attachments = g_list_length (mime_parts);
2524 if (n_attachments == 1) {
2525 const gchar *filename;
2527 if (TNY_IS_MSG (mime_parts->data)) {
2529 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2530 filename = tny_header_get_subject (header);
2531 g_object_unref (header);
2532 if (filename == NULL)
2533 filename = _("mail_va_no_subject");
2535 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2537 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2539 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2540 "mcen_nc_purge_files_text",
2541 n_attachments), n_attachments);
2543 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2544 confirmation_message);
2545 g_free (confirmation_message);
2547 if (response != GTK_RESPONSE_OK)
2550 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2551 /* folder = tny_msg_get_folder (msg); */
2552 /* tny_msg_uncache_attachments (msg); */
2553 /* tny_folder_refresh (folder, NULL); */
2554 /* g_object_unref (folder); */
2556 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2557 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2558 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2561 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2562 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2563 tny_msg_rewrite_cache (msg);
2564 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2566 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2567 g_list_free (mime_parts);
2569 if (priv->purge_timeout > 0) {
2570 g_source_remove (priv->purge_timeout);
2571 priv->purge_timeout = 0;
2574 if (priv->remove_attachment_banner) {
2575 gtk_widget_destroy (priv->remove_attachment_banner);
2576 g_object_unref (priv->remove_attachment_banner);
2577 priv->remove_attachment_banner = NULL;
2585 update_window_title (ModestMsgViewWindow *window)
2587 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2589 TnyHeader *header = NULL;
2590 const gchar *subject = NULL;
2592 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2594 header = tny_msg_get_header (msg);
2595 subject = tny_header_get_subject (header);
2596 g_object_unref (msg);
2599 if ((subject == NULL)||(subject[0] == '\0'))
2600 subject = _("mail_va_no_subject");
2602 gtk_window_set_title (GTK_WINDOW (window), subject);