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.
31 #include <glib/gi18n.h>
32 #include <modest-platform.h>
33 #include <modest-runtime.h>
34 #include <modest-main-window.h>
35 #include <modest-header-view.h>
36 #include "maemo/modest-maemo-global-settings-dialog.h"
37 #include "modest-widget-memory.h"
38 #include <modest-hildon-includes.h>
39 #include <modest-maemo-utils.h>
40 #include <dbus_api/modest-dbus-callbacks.h>
41 #include <maemo/modest-osso-autosave-callbacks.h>
43 #include <tny-maemo-conic-device.h>
44 #include <tny-simple-list.h>
45 #include <tny-folder.h>
46 #include <tny-camel-imap-store-account.h>
47 #include <tny-camel-pop-store-account.h>
48 #include <gtk/gtkicontheme.h>
49 #include <gtk/gtkmenuitem.h>
50 #include <gtk/gtkmain.h>
51 #include <modest-text-utils.h>
52 #include "modest-tny-folder.h"
53 #include "modest-tny-account.h"
55 #include <libgnomevfs/gnome-vfs-mime-utils.h>
57 #ifdef MODEST_HAVE_ABOOK
58 #include <libosso-abook/osso-abook.h>
59 #endif /*MODEST_HAVE_ABOOK*/
61 #ifdef MODEST_HAVE_LIBALARM
62 #include <alarmd/alarm_event.h> /* For alarm_event_add(), etc. */
63 #endif /*MODEST_HAVE_LIBALARM*/
66 #define HILDON_OSSO_URI_ACTION "uri-action"
67 #define URI_ACTION_COPY "copy:"
70 on_modest_conf_update_interval_changed (ModestConf* self,
72 ModestConfEvent event,
73 ModestConfNotificationId id,
76 g_return_if_fail (key);
78 if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
79 const guint update_interval_minutes =
80 modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
81 modest_platform_set_update_interval (update_interval_minutes);
88 check_required_files (void)
90 FILE *mcc_file = modest_maemo_open_mcc_mapping_file ();
92 g_printerr ("modest: check for mcc file failed\n");
97 if (access(MODEST_PROVIDER_DATA_FILE, R_OK) != 0 &&
98 access(MODEST_MAEMO_PROVIDER_DATA_FILE, R_OK) != 0) {
99 g_printerr ("modest: cannot find providers data\n");
107 /* the gpointer here is the osso_context. */
109 modest_platform_init (int argc, char *argv[])
111 osso_context_t *osso_context;
113 osso_hw_state_t hw_state = { 0 };
117 if (!check_required_files ()) {
118 g_printerr ("modest: missing required files\n");
122 osso_context = osso_initialize(PACKAGE,PACKAGE_VERSION,
125 g_printerr ("modest: failed to acquire osso context\n");
128 modest_maemo_utils_set_osso_context (osso_context);
130 if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
131 g_printerr ("modest: could not get dbus connection\n");
135 /* Add a D-Bus handler to be used when the main osso-rpc
136 * D-Bus handler has not handled something.
137 * We use this for D-Bus methods that need to use more complex types
138 * than osso-rpc supports.
140 if (!dbus_connection_add_filter (con,
141 modest_dbus_req_filter,
145 g_printerr ("modest: Could not add D-Bus filter\n");
149 /* Register our simple D-Bus callbacks, via the osso API: */
150 osso_return_t result = osso_rpc_set_cb_f(osso_context,
154 modest_dbus_req_handler, NULL /* user_data */);
155 if (result != OSSO_OK) {
156 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
160 /* Register hardware event dbus callback: */
161 hw_state.shutdown_ind = TRUE;
162 osso_hw_set_event_cb(osso_context, NULL, NULL, NULL);
164 /* Register osso auto-save callbacks: */
165 result = osso_application_set_autosave_cb (osso_context,
166 modest_on_osso_application_autosave, NULL /* user_data */);
167 if (result != OSSO_OK) {
168 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
173 /* Make sure that the update interval is changed whenever its gconf key
175 /* CAUTION: we're not using here the
176 modest_conf_listen_to_namespace because we know that there
177 are other parts of Modest listening for this namespace, so
178 we'll receive the notifications anyway. We basically do not
179 use it because there is no easy way to do the
180 modest_conf_forget_namespace */
181 ModestConf *conf = modest_runtime_get_conf ();
182 g_signal_connect (G_OBJECT(conf),
184 G_CALLBACK (on_modest_conf_update_interval_changed),
187 /* only force the setting of the default interval, if there are actually
189 acc_names = modest_account_mgr_account_names (modest_runtime_get_account_mgr(), TRUE);
191 /* Get the initial update interval from gconf: */
192 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
193 MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
194 modest_account_mgr_free_account_names (acc_names);
198 #ifdef MODEST_HAVE_ABOOK
199 /* initialize the addressbook */
200 if (!osso_abook_init (&argc, &argv, osso_context)) {
201 g_printerr ("modest: failed to initialized addressbook\n");
204 #endif /*MODEST_HAVE_ABOOK*/
210 modest_platform_uninit (void)
212 osso_context_t *osso_context =
213 modest_maemo_utils_get_osso_context ();
215 osso_deinitialize (osso_context);
224 modest_platform_get_new_device (void)
226 return TNY_DEVICE (tny_maemo_conic_device_new ());
230 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
231 gchar **effective_mime_type)
233 GString *mime_str = NULL;
234 gchar *icon_name = NULL;
235 gchar **icons, **cursor;
237 if (!mime_type || g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0)
238 mime_str = g_string_new (gnome_vfs_get_mime_type_for_name (name));
240 mime_str = g_string_new (mime_type);
241 g_string_ascii_down (mime_str);
244 icons = hildon_mime_get_icon_names (mime_str->str, NULL);
246 for (cursor = icons; cursor; ++cursor) {
247 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
248 !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
249 icon_name = g_strdup ("qgn_list_messagin");
251 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
252 icon_name = g_strdup (*cursor);
258 if (effective_mime_type)
259 *effective_mime_type = g_string_free (mime_str, FALSE);
261 g_string_free (mime_str, TRUE);
268 checked_hildon_uri_open (const gchar *uri, HildonURIAction *action)
273 g_return_val_if_fail (uri, FALSE);
275 result = hildon_uri_open (uri, action, &err);
277 g_printerr ("modest: hildon_uri_open ('%s', %p) failed: %s",
278 uri, action, err && err->message ? err->message : "unknown error");
288 modest_platform_activate_uri (const gchar *uri)
290 HildonURIAction *action;
291 gboolean result = FALSE;
292 GSList *actions, *iter = NULL;
294 g_return_val_if_fail (uri, FALSE);
298 /* don't try to activate file: uri's -- they might confuse the user,
299 * and/or might have security implications */
300 if (!g_str_has_prefix (uri, "file:")) {
302 actions = hildon_uri_get_actions_by_uri (uri, -1, NULL);
304 for (iter = actions; iter; iter = g_slist_next (iter)) {
305 action = (HildonURIAction*) iter->data;
306 if (action && strcmp (hildon_uri_action_get_service (action),
307 "com.nokia.modest") == 0) {
308 result = checked_hildon_uri_open (uri, action);
313 /* if we could not open it with email, try something else */
315 result = checked_hildon_uri_open (uri, NULL);
319 ModestWindow *parent =
320 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
321 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
322 _("mcen_ib_unsupported_link"));
323 g_warning ("%s: cannot open uri '%s'", __FUNCTION__,uri);
330 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
334 gchar *uri_path = NULL;
336 uri_path = g_strconcat ("file://", path, NULL);
337 con = osso_get_dbus_connection (modest_maemo_utils_get_osso_context());
340 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
342 result = hildon_mime_open_file (con, uri_path);
344 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
352 } ModestPlatformPopupInfo;
355 delete_uri_popup (GtkWidget *menu,
359 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
361 g_free (popup_info->uri);
362 hildon_uri_free_actions (popup_info->actions);
368 activate_uri_popup_item (GtkMenuItem *menu_item,
372 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
373 const gchar* action_name;
375 action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
377 g_printerr ("modest: no action name defined\n");
381 /* special handling for the copy menu item -- copy the uri to the clipboard */
382 /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
383 if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
384 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
385 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
387 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
388 action_name += strlen ("mailto:");
390 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
391 return; /* we're done */
394 /* now, the real uri-actions... */
395 for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
396 HildonURIAction *action = (HildonURIAction *) node->data;
397 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
398 if (!checked_hildon_uri_open (popup_info->uri, action)) {
399 ModestWindow *parent =
400 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
401 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
402 _("mcen_ib_unsupported_link"));
410 modest_platform_show_uri_popup (const gchar *uri)
412 GSList *actions_list;
417 actions_list = hildon_uri_get_actions_by_uri (uri, -1, NULL);
420 GtkWidget *menu = gtk_menu_new ();
421 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
423 /* don't add actions for file: uri's -- they might confuse the user,
424 * and/or might have security implications
425 * we still allow to copy the url though
427 if (!g_str_has_prefix (uri, "file:")) {
430 popup_info->actions = actions_list;
431 popup_info->uri = g_strdup (uri);
433 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
434 GtkWidget *menu_item;
435 const gchar *action_name;
436 const gchar *translation_domain;
437 HildonURIAction *action = (HildonURIAction *) node->data;
438 action_name = hildon_uri_action_get_name (action);
439 translation_domain = hildon_uri_action_get_translation_domain (action);
440 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
441 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name); /* hack */
442 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
445 if (hildon_uri_is_default_action (action, NULL)) {
446 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
448 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
450 gtk_widget_show (menu_item);
454 /* always add the copy item */
455 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri",
456 "uri_link_copy_link_location"));
457 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
458 g_strconcat (URI_ACTION_COPY, uri, NULL),
460 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
461 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
462 gtk_widget_show (menu_item);
465 /* and what to do when the link is deleted */
466 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
467 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
470 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
478 modest_platform_get_icon (const gchar *name)
481 GdkPixbuf* pixbuf = NULL;
482 GtkIconTheme *current_theme = NULL;
484 g_return_val_if_fail (name, NULL);
486 /* strlen == 0 is not really an error; it just
487 * means the icon is not available
489 if (!name || strlen(name) == 0)
492 #if 0 /* do we still need this? */
493 if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
494 pixbuf = gdk_pixbuf_new_from_file (name, &err);
496 g_printerr ("modest: error loading icon '%s': %s\n",
504 current_theme = gtk_icon_theme_get_default ();
505 pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
506 GTK_ICON_LOOKUP_NO_SVG,
509 g_printerr ("modest: error loading theme icon '%s': %s\n",
517 modest_platform_get_app_name (void)
519 return _("mcen_ap_name");
523 entry_insert_text (GtkEditable *editable,
532 chars = gtk_editable_get_chars (editable, 0, -1);
533 chars_length = g_utf8_strlen (chars, -1);
535 /* Show WID-INF036 */
536 if (chars_length >= 20) {
537 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
538 _CS("ckdg_ib_maximum_characters_reached"));
540 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
544 tmp = g_strndup (folder_name_forbidden_chars,
545 FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
546 msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
547 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)),
552 /* Write the text in the entry if it's valid */
553 g_signal_handlers_block_by_func (editable,
554 (gpointer) entry_insert_text, data);
555 gtk_editable_insert_text (editable, text, length, position);
556 g_signal_handlers_unblock_by_func (editable,
557 (gpointer) entry_insert_text, data);
560 /* Do not allow further processing */
561 g_signal_stop_emission_by_name (editable, "insert_text");
565 entry_changed (GtkEditable *editable,
569 GtkWidget *ok_button;
572 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
573 ok_button = GTK_WIDGET (buttons->next->data);
575 chars = gtk_editable_get_chars (editable, 0, -1);
576 g_return_if_fail (chars != NULL);
579 if (g_utf8_strlen (chars,-1) >= 21)
580 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
581 _CS("ckdg_ib_maximum_characters_reached"));
583 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
586 g_list_free (buttons);
591 checked_hildon_sort_dialog_add_sort_key (HildonSortDialog *dialog, const gchar* key, guint max)
595 g_return_val_if_fail (dialog && HILDON_IS_SORT_DIALOG(dialog), 0);
596 g_return_val_if_fail (key, 0);
598 sort_key = hildon_sort_dialog_add_sort_key (dialog, key);
599 if (sort_key < 0 || sort_key >= max) {
600 g_warning ("%s: out of range (%d) for %s", __FUNCTION__, sort_key, key);
603 return (guint)sort_key;
608 launch_sort_headers_dialog (GtkWindow *parent_window,
609 HildonSortDialog *dialog)
611 ModestHeaderView *header_view = NULL;
613 GtkSortType sort_type;
615 gint default_key = 0;
617 gboolean outgoing = FALSE;
618 gint current_sort_colid = -1;
619 GtkSortType current_sort_type;
620 gint attachments_sort_id;
621 gint priority_sort_id;
622 GtkTreeSortable *sortable;
624 /* Get header window */
625 if (MODEST_IS_MAIN_WINDOW (parent_window)) {
626 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
627 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
632 /* Add sorting keys */
633 cols = modest_header_view_get_columns (header_view);
636 #define SORT_ID_NUM 6
637 int sort_model_ids[SORT_ID_NUM];
638 int sort_ids[SORT_ID_NUM];
640 outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
641 MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
643 sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"),
646 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
647 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
649 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
650 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
653 sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"),
656 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
657 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
659 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
660 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
662 default_key = sort_key;
664 sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"),
666 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
668 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
670 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
672 sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"),
674 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
675 sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
676 attachments_sort_id = sort_key;
678 sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"),
680 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
681 sort_ids[sort_key] = 0;
683 sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"),
685 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
686 sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY_MASK;
687 priority_sort_id = sort_key;
689 sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model
690 (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
692 if (!gtk_tree_sortable_get_sort_column_id (sortable,
693 ¤t_sort_colid, ¤t_sort_type)) {
694 hildon_sort_dialog_set_sort_key (dialog, default_key);
695 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
697 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
698 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
699 gpointer flags_sort_type_pointer;
700 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
701 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
702 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
704 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
706 gint current_sort_keyid = 0;
707 while (current_sort_keyid < 6) {
708 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
711 current_sort_keyid++;
713 hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
717 result = gtk_dialog_run (GTK_DIALOG (dialog));
718 if (result == GTK_RESPONSE_OK) {
719 sort_key = hildon_sort_dialog_get_sort_key (dialog);
720 if (sort_key < 0 || sort_key > SORT_ID_NUM -1) {
721 g_warning ("%s: out of range (%d)", __FUNCTION__, sort_key);
725 sort_type = hildon_sort_dialog_get_sort_order (dialog);
726 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
727 g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
728 GINT_TO_POINTER (sort_ids[sort_key]));
729 /* This is a hack to make it resort rows always when flag fields are
730 * selected. If we do not do this, changing sort field from priority to
731 * attachments does not work */
732 modest_header_view_sort_by_column_id (header_view, 0, sort_type);
734 gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data),
735 sort_model_ids[sort_key]);
738 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
739 gtk_tree_sortable_sort_column_changed (sortable);
742 modest_widget_memory_save (modest_runtime_get_conf (),
743 G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
752 on_response (GtkDialog *dialog,
756 GList *child_vbox, *child_hbox;
757 GtkWidget *hbox, *entry;
758 TnyFolderStore *parent;
759 const gchar *new_name;
762 if (response != GTK_RESPONSE_ACCEPT)
766 child_vbox = gtk_container_get_children (GTK_CONTAINER (dialog->vbox));
767 hbox = child_vbox->data;
768 child_hbox = gtk_container_get_children (GTK_CONTAINER (hbox));
769 entry = child_hbox->next->data;
771 parent = TNY_FOLDER_STORE (user_data);
772 new_name = gtk_entry_get_text (GTK_ENTRY (entry));
775 /* Look for another folder with the same name */
776 if (modest_tny_folder_has_subfolder_with_name (parent,
783 if (TNY_IS_ACCOUNT (parent) &&
784 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) &&
785 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent),
794 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)),
795 NULL, _CS("ckdg_ib_folder_already_exists"));
796 /* Select the text */
797 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
798 gtk_widget_grab_focus (entry);
799 /* Do not close the dialog */
800 g_signal_stop_emission_by_name (dialog, "response");
807 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
808 TnyFolderStore *parent,
809 const gchar *dialog_title,
810 const gchar *label_text,
811 const gchar *suggested_name,
814 GtkWidget *accept_btn = NULL;
815 GtkWidget *dialog, *entry, *label, *hbox;
816 GList *buttons = NULL;
819 /* Ask the user for the folder name */
820 dialog = gtk_dialog_new_with_buttons (dialog_title,
822 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
823 _("mcen_bd_dialog_ok"),
825 _("mcen_bd_dialog_cancel"),
829 /* Add accept button (with unsensitive handler) */
830 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
831 accept_btn = GTK_WIDGET (buttons->next->data);
832 /* Create label and entry */
833 label = gtk_label_new (label_text);
834 /* TODO: check that the suggested name does not exist */
835 /* We set 21 as maximum because we want to show WID-INF036
836 when the user inputs more that 20 */
837 entry = gtk_entry_new_with_max_length (21);
839 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
841 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
842 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
844 /* Connect to the response method to avoid closing the dialog
845 when an invalid name is selected*/
846 g_signal_connect (dialog,
848 G_CALLBACK (on_response),
851 /* Track entry changes */
852 g_signal_connect (entry,
854 G_CALLBACK (entry_insert_text),
856 g_signal_connect (entry,
858 G_CALLBACK (entry_changed),
861 /* Create the hbox */
862 hbox = gtk_hbox_new (FALSE, 12);
863 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
864 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
866 /* Add hbox to dialog */
867 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox),
868 hbox, FALSE, FALSE, 0);
870 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
871 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
873 result = gtk_dialog_run (GTK_DIALOG(dialog));
874 if (result == GTK_RESPONSE_ACCEPT)
875 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
877 gtk_widget_destroy (dialog);
879 while (gtk_events_pending ())
880 gtk_main_iteration ();
886 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
887 TnyFolderStore *parent_folder,
888 gchar *suggested_name,
891 gchar *real_suggested_name = NULL;
894 if(suggested_name == NULL)
896 const gchar *default_name = _("mcen_ia_default_folder_name");
900 for(i = 0; i < 100; ++ i) {
901 gboolean exists = FALSE;
903 sprintf(num_str, "%.2u", i);
906 real_suggested_name = g_strdup (default_name);
908 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
910 exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
917 g_free (real_suggested_name);
920 /* Didn't find a free number */
922 real_suggested_name = g_strdup (default_name);
924 real_suggested_name = suggested_name;
927 result = modest_platform_run_folder_name_dialog (parent_window,
929 _("mcen_ti_new_folder"),
930 _("mcen_fi_new_folder_name"),
933 if (suggested_name == NULL)
934 g_free(real_suggested_name);
940 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
941 TnyFolderStore *parent_folder,
942 const gchar *suggested_name,
945 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
947 return modest_platform_run_folder_name_dialog (parent_window,
949 _HL("ckdg_ti_rename_folder"),
950 _HL("ckdg_fi_rename_name"),
958 on_destroy_dialog (GtkDialog *dialog)
960 gtk_widget_destroy (GTK_WIDGET(dialog));
961 if (gtk_events_pending ())
962 gtk_main_iteration ();
966 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
967 const gchar *message)
972 dialog = hildon_note_new_confirmation (parent_window, message);
973 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
974 GTK_WINDOW (dialog));
976 response = gtk_dialog_run (GTK_DIALOG (dialog));
978 on_destroy_dialog (GTK_DIALOG(dialog));
980 while (gtk_events_pending ())
981 gtk_main_iteration ();
987 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
988 const gchar *message)
993 dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
994 _("mcen_bd_yes"), GTK_RESPONSE_YES,
995 _("mcen_bd_no"), GTK_RESPONSE_NO,
997 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
998 response = gtk_dialog_run (GTK_DIALOG (dialog));
1000 on_destroy_dialog (GTK_DIALOG(dialog));
1002 while (gtk_events_pending ())
1003 gtk_main_iteration ();
1011 modest_platform_run_information_dialog (GtkWindow *parent_window,
1012 const gchar *message)
1016 note = hildon_note_new_information (parent_window, message);
1017 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1020 g_signal_connect_swapped (note,
1022 G_CALLBACK (on_destroy_dialog),
1025 gtk_widget_show_all (note);
1030 typedef struct _ConnectAndWaitData {
1032 GMainLoop *wait_loop;
1033 gboolean has_callback;
1035 } ConnectAndWaitData;
1039 quit_wait_loop (TnyAccount *account,
1040 ConnectAndWaitData *data)
1042 /* Set the has_callback to TRUE (means that the callback was
1043 executed and wake up every code waiting for cond to be
1045 g_mutex_lock (data->mutex);
1046 data->has_callback = TRUE;
1047 if (data->wait_loop)
1048 g_main_loop_quit (data->wait_loop);
1049 g_mutex_unlock (data->mutex);
1053 on_connection_status_changed (TnyAccount *account,
1054 TnyConnectionStatus status,
1057 TnyConnectionStatus conn_status;
1058 ConnectAndWaitData *data;
1060 /* Ignore if reconnecting or disconnected */
1061 conn_status = tny_account_get_connection_status (account);
1062 if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1063 conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1066 /* Remove the handler */
1067 data = (ConnectAndWaitData *) user_data;
1068 g_signal_handler_disconnect (account, data->handler);
1070 /* Quit from wait loop */
1071 quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1075 on_tny_camel_account_set_online_cb (TnyCamelAccount *account,
1080 /* Quit from wait loop */
1081 quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1085 modest_platform_connect_and_wait (GtkWindow *parent_window,
1086 TnyAccount *account)
1088 ConnectAndWaitData *data = NULL;
1089 gboolean device_online;
1091 TnyConnectionStatus conn_status;
1092 gboolean user_requested;
1094 device = modest_runtime_get_device();
1095 device_online = tny_device_is_online (device);
1097 /* Whether the connection is user requested or automatically
1098 requested, for example via D-Bus */
1099 user_requested = (parent_window) ? TRUE : FALSE;
1101 /* If there is no account check only the device status */
1106 return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device),
1107 NULL, user_requested);
1110 /* Return if the account is already connected */
1111 conn_status = tny_account_get_connection_status (account);
1112 if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1115 /* Create the helper */
1116 data = g_slice_new0 (ConnectAndWaitData);
1117 data->mutex = g_mutex_new ();
1118 data->has_callback = FALSE;
1120 /* Connect the device */
1121 if (!device_online) {
1122 /* Track account connection status changes */
1123 data->handler = g_signal_connect (account, "connection-status-changed",
1124 G_CALLBACK (on_connection_status_changed),
1126 /* Try to connect the device */
1127 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device),
1128 NULL, user_requested);
1130 /* If the device connection failed then exit */
1131 if (!device_online && data->handler)
1134 /* Force a reconnection of the account */
1135 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE,
1136 on_tny_camel_account_set_online_cb, data);
1139 /* Wait until the callback is executed */
1140 g_mutex_lock (data->mutex);
1141 if (!data->has_callback) {
1142 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1143 gdk_threads_leave ();
1144 g_mutex_unlock (data->mutex);
1145 g_main_loop_run (data->wait_loop);
1146 g_mutex_lock (data->mutex);
1147 gdk_threads_enter ();
1149 g_mutex_unlock (data->mutex);
1153 if (g_signal_handler_is_connected (account, data->handler))
1154 g_signal_handler_disconnect (account, data->handler);
1155 g_mutex_free (data->mutex);
1156 g_main_loop_unref (data->wait_loop);
1157 g_slice_free (ConnectAndWaitData, data);
1160 conn_status = tny_account_get_connection_status (account);
1161 return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1165 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1167 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1168 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1169 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1170 /* This must be a maildir account, which does not require a connection: */
1175 return modest_platform_connect_and_wait (parent_window, account);
1179 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1182 return TRUE; /* Maybe it is something local. */
1184 gboolean result = TRUE;
1185 if (TNY_IS_FOLDER (folder_store)) {
1186 /* Get the folder's parent account: */
1187 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1188 if (account != NULL) {
1189 result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1190 g_object_unref (account);
1192 } else if (TNY_IS_ACCOUNT (folder_store)) {
1193 /* Use the folder store as an account: */
1194 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1201 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1202 ModestSortDialogType type)
1204 GtkWidget *dialog = NULL;
1207 dialog = hildon_sort_dialog_new (parent_window);
1208 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1209 GTK_WINDOW (dialog));
1211 hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1212 "applications_email_sort",
1213 modest_maemo_utils_get_osso_context());
1215 /* Fill sort keys */
1217 case MODEST_SORT_HEADERS:
1218 launch_sort_headers_dialog (parent_window,
1219 HILDON_SORT_DIALOG(dialog));
1224 on_destroy_dialog (GTK_DIALOG(dialog));
1229 modest_platform_set_update_interval (guint minutes)
1231 #ifdef MODEST_HAVE_LIBALARM
1233 ModestConf *conf = modest_runtime_get_conf ();
1237 cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1239 /* Delete any existing alarm,
1240 * because we will replace it: */
1242 if (alarm_event_del(alarm_cookie) != 1)
1243 g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1245 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1248 /* 0 means no updates: */
1253 /* Register alarm: */
1255 /* Set the interval in alarm_event_t structure: */
1256 alarm_event_t *event = g_new0(alarm_event_t, 1);
1257 event->alarm_time = minutes * 60; /* seconds */
1259 /* Set recurrence every few minutes: */
1260 event->recurrence = minutes;
1261 event->recurrence_count = -1; /* Means infinite */
1263 /* Specify what should happen when the alarm happens:
1264 * It should call this D-Bus method: */
1266 event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1267 event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1268 event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1269 event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1271 /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if
1272 * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1273 * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails
1274 * This is why we want to use the Alarm API instead of just g_timeout_add().
1275 * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1277 event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1279 alarm_cookie = alarm_event_add (event);
1282 alarm_event_free (event);
1284 /* Store the alarm ID in GConf, so we can remove it later:
1285 * This is apparently valid between application instances. */
1286 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1288 if (!alarm_cookie) {
1290 const alarm_error_t alarm_error = alarmd_get_error ();
1291 g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1293 /* Give people some clue: */
1294 /* The alarm API should have a function for this: */
1295 if (alarm_error == ALARMD_ERROR_DBUS) {
1296 g_debug (" ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1297 } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1298 g_debug (" ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1299 } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1300 g_debug (" ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1301 } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1302 g_debug (" ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1303 } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1304 g_debug (" ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1305 } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1306 g_debug (" ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1311 #endif /* MODEST_HAVE_LIBALARM */
1316 modest_platform_on_new_headers_received (TnyList *header_list)
1318 #ifdef MODEST_HAVE_HILDON_NOTIFY
1319 HildonNotification *notification;
1321 GSList *notifications_list = NULL;
1323 /* Get previous notifications ids */
1324 notifications_list = modest_conf_get_list (modest_runtime_get_conf (),
1325 MODEST_CONF_NOTIFICATION_IDS,
1326 MODEST_CONF_VALUE_INT, NULL);
1328 iter = tny_list_create_iterator (header_list);
1329 while (!tny_iterator_is_done (iter)) {
1330 gchar *url = NULL, *display_address = NULL, *summary = NULL;
1331 const gchar *display_date;
1332 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1333 TnyFolder *folder = tny_header_get_folder (header);
1334 gboolean first_notification = TRUE;
1337 /* constant string, don't free */
1338 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1340 display_address = g_strdup(tny_header_get_from (header));
1341 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1343 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1344 notification = hildon_notification_new (summary,
1345 tny_header_get_subject (header),
1346 "qgn_list_messagin",
1348 /* Create the message URL */
1349 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder),
1350 tny_header_get_uid (header));
1352 hildon_notification_add_dbus_action(notification,
1355 MODEST_DBUS_SERVICE,
1358 MODEST_DBUS_METHOD_OPEN_MESSAGE,
1362 /* Play sound if the user wants. Show the LED
1363 pattern. Show and play just one */
1364 if (G_UNLIKELY (first_notification)) {
1365 first_notification = FALSE;
1366 if (modest_conf_get_bool (modest_runtime_get_conf (),
1367 MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1369 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1370 "sound-file", "/usr/share/sounds/ui-new_email.wav");
1373 /* Set the led pattern */
1374 notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1376 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1378 "PatternCommunicationEmail");
1381 /* Notify. We need to do this in an idle because this function
1382 could be called from a thread */
1383 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1385 /* Save id in the list */
1386 g_object_get(G_OBJECT(notification), "id", ¬if_id, NULL);
1387 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1388 /* We don't listen for the "closed" signal, because we
1389 don't care about if the notification was removed or
1390 not to store the list in gconf */
1392 /* Free & carry on */
1393 g_free (display_address);
1396 g_object_unref (folder);
1397 g_object_unref (header);
1398 tny_iterator_next (iter);
1400 g_object_unref (iter);
1403 modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS,
1404 notifications_list, MODEST_CONF_VALUE_INT, NULL);
1406 g_slist_free (notifications_list);
1408 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1412 modest_platform_remove_new_mail_notifications (void)
1414 #ifdef MODEST_HAVE_HILDON_NOTIFY
1415 GSList *notif_list = NULL;
1417 /* Get previous notifications ids */
1418 notif_list = modest_conf_get_list (modest_runtime_get_conf (),
1419 MODEST_CONF_NOTIFICATION_IDS,
1420 MODEST_CONF_VALUE_INT, NULL);
1422 while (notif_list) {
1424 NotifyNotification *notif;
1426 /* Nasty HACK to remove the notifications, set the id
1427 of the existing ones and then close them */
1428 notif_id = GPOINTER_TO_INT(notif_list->data);
1429 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1430 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1432 /* Close the notification, note that some ids could be
1433 already invalid, but we don't care because it does
1435 notify_notification_close(notif, NULL);
1436 g_object_unref(notif);
1438 /* Delete the link, it's like going to the next */
1439 notif_list = g_slist_delete_link (notif_list, notif_list);
1443 modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS,
1444 notif_list, MODEST_CONF_VALUE_INT, NULL);
1446 g_slist_free (notif_list);
1448 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1454 modest_platform_get_global_settings_dialog ()
1456 return modest_maemo_global_settings_dialog_new ();
1460 modest_platform_show_help (GtkWindow *parent_window,
1461 const gchar *help_id)
1463 osso_return_t result;
1464 g_return_if_fail (help_id);
1466 result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1467 help_id, HILDON_HELP_SHOW_DIALOG);
1469 if (result != OSSO_OK) {
1471 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id);
1472 hildon_banner_show_information (GTK_WIDGET (parent_window),
1480 modest_platform_show_search_messages (GtkWindow *parent_window)
1482 osso_return_t result = OSSO_ERROR;
1484 result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1485 "osso_global_search",
1486 "search_email", NULL, DBUS_TYPE_INVALID);
1488 if (result != OSSO_OK) {
1489 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1494 modest_platform_show_addressbook (GtkWindow *parent_window)
1496 osso_return_t result = OSSO_ERROR;
1498 result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1500 "top_application", NULL, DBUS_TYPE_INVALID);
1502 if (result != OSSO_OK) {
1503 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1508 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1510 GtkWidget *widget = modest_folder_view_new (query);
1512 /* Show one account by default */
1513 modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1514 MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1517 /* Restore settings */
1518 modest_widget_memory_restore (modest_runtime_get_conf(),
1520 MODEST_CONF_FOLDER_VIEW_KEY);
1526 modest_platform_information_banner (GtkWidget *parent,
1527 const gchar *icon_name,
1530 hildon_banner_show_information (parent, icon_name, text);
1534 modest_platform_animation_banner (GtkWidget *parent,
1535 const gchar *animation_name,
1538 GtkWidget *inf_note = NULL;
1540 g_return_val_if_fail (text != NULL, NULL);
1542 inf_note = hildon_banner_show_animation (parent, animation_name, text);
1550 TnyAccount *account;
1553 } CheckAccountIdleData;
1555 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1558 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1560 gboolean stop_trying = FALSE;
1561 g_return_val_if_fail (data && data->account, FALSE);
1563 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1564 tny_account_get_connection_status (data->account));
1566 if (data && data->account &&
1567 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1568 * after which the account is likely to be usable, or never likely to be usable soon: */
1569 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1571 data->is_online = TRUE;
1575 /* Give up if we have tried too many times: */
1576 if (data->count_tries >= NUMBER_OF_TRIES) {
1579 /* Wait for another timeout: */
1580 ++(data->count_tries);
1585 /* Allow the function that requested this idle callback to continue: */
1587 g_main_loop_quit (data->loop);
1590 g_object_unref (data->account);
1592 return FALSE; /* Don't call this again. */
1594 return TRUE; /* Call this timeout callback again. */
1598 /* Return TRUE immediately if the account is already online,
1599 * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as
1600 * soon as the account is online, or FALSE if the account does
1601 * not become online in the NUMBER_OF_TRIES seconds.
1602 * This is useful when the D-Bus method was run immediately after
1603 * the application was started (when using D-Bus activation),
1604 * because the account usually takes a short time to go online.
1605 * The return value is maybe not very useful.
1608 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1610 g_return_val_if_fail (account, FALSE);
1612 printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1614 if (!tny_device_is_online (modest_runtime_get_device())) {
1615 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1619 /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1620 * so we avoid wait unnecessarily: */
1621 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1622 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1626 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1627 __FUNCTION__, tny_account_get_connection_status (account));
1629 /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED,
1630 * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that
1631 * we want to avoid. */
1632 if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1635 /* This blocks on the result: */
1636 CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1637 data->is_online = FALSE;
1638 data->account = account;
1639 g_object_ref (data->account);
1640 data->count_tries = 0;
1642 GMainContext *context = NULL; /* g_main_context_new (); */
1643 data->loop = g_main_loop_new (context, FALSE /* not running */);
1645 g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1647 /* This main loop will run until the idle handler has stopped it: */
1648 g_main_loop_run (data->loop);
1650 g_main_loop_unref (data->loop);
1651 /* g_main_context_unref (context); */
1653 g_slice_free (CheckAccountIdleData, data);
1655 return data->is_online;
1661 on_cert_dialog_response (GtkDialog *dialog, gint response_id, const gchar* cert)
1663 /* GTK_RESPONSE_HELP means we need to show the certificate */
1664 if (response_id == GTK_RESPONSE_HELP) {
1668 /* Do not close the dialog */
1669 g_signal_stop_emission_by_name (dialog, "response");
1671 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);
1672 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1673 gtk_dialog_run (GTK_DIALOG(note));
1674 gtk_widget_destroy (note);
1680 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1681 const gchar *certificate)
1685 ModestWindow *main_win;
1687 if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1688 g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1693 /* don't create it */
1694 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1695 g_return_val_if_fail (main_win, FALSE); /* should not happen */
1698 gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1701 note = hildon_note_new_confirmation_add_buttons (
1702 GTK_WINDOW(main_win),
1704 _("mcen_bd_dialog_ok"), GTK_RESPONSE_OK,
1705 _("mcen_bd_view"), GTK_RESPONSE_HELP, /* abusing this... */
1706 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1709 g_signal_connect (G_OBJECT(note), "response",
1710 G_CALLBACK(on_cert_dialog_response),
1711 (gpointer) certificate);
1713 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1715 response = gtk_dialog_run(GTK_DIALOG(note));
1717 on_destroy_dialog (GTK_DIALOG(note));
1720 return response == GTK_RESPONSE_OK;
1724 modest_platform_run_alert_dialog (const gchar* prompt,
1725 gboolean is_question)
1727 ModestWindow *main_win;
1729 if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1730 g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1731 " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1732 return is_question ? FALSE : TRUE;
1735 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1736 g_return_val_if_fail (main_win, FALSE); /* should not happen */
1738 gboolean retval = TRUE;
1740 /* The Tinymail documentation says that we should show Yes and No buttons,
1741 * when it is a question.
1742 * Obviously, we need tinymail to use more specific error codes instead,
1743 * so we know what buttons to show. */
1744 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win),
1746 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1747 GTK_WINDOW (dialog));
1749 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1750 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1752 on_destroy_dialog (GTK_DIALOG(dialog));
1754 /* Just show the error text and use the default response: */
1755 modest_platform_run_information_dialog (GTK_WINDOW (main_win),
1763 GtkWindow *parent_window;
1764 ModestConnectedPerformer callback;
1765 TnyAccount *account;
1772 on_went_online_info_free (OnWentOnlineInfo *info)
1774 /* And if we cleanup, we DO cleanup :-) */
1777 g_object_unref (info->device);
1780 if (info->parent_window)
1781 g_object_unref (info->parent_window);
1783 g_object_unref (info->account);
1785 g_slice_free (OnWentOnlineInfo, info);
1787 /* We're done ... */
1793 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1795 OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1797 /* Now it's really time to callback to the caller. If going online didn't succeed,
1798 * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1799 * canceled will be set. Etcetera etcetera. */
1801 if (info->callback) {
1802 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1805 /* This is our last call, we must cleanup here if we didn't yet do that */
1806 on_went_online_info_free (info);
1813 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1815 OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1816 info->iap = g_strdup (iap_id);
1818 if (canceled || err || !info->account) {
1820 /* If there's a problem or if there's no account (then that's it for us, we callback
1821 * the caller's callback now. He'll have to handle err or canceled, of course.
1822 * We are not really online, as the account is not really online here ... */
1824 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
1825 * this info. We don't cleanup err, Tinymail does that! */
1827 if (info->callback) {
1829 /* info->account can be NULL here, this means that the user did not
1830 * provide a nice account instance. We'll assume that the user knows
1831 * what he's doing and is happy with just the device going online.
1833 * We can't do magic, we don't know what account the user wants to
1834 * see going online. So just the device goes online, end of story */
1836 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1839 } else if (info->account) {
1841 /* If there's no problem and if we have an account, we'll put the account
1842 * online too. When done, the callback of bringing the account online
1843 * will callback the caller's callback. This is the most normal case. */
1845 info->device = TNY_DEVICE (g_object_ref (device));
1847 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
1848 on_account_went_online, info);
1850 /* The on_account_went_online cb frees up the info, go look if you
1851 * don't believe me! (so we return here) */
1856 /* We cleanup if we are not bringing the account online too */
1857 on_went_online_info_free (info);
1863 modest_platform_connect_and_perform (GtkWindow *parent_window,
1864 TnyAccount *account,
1865 ModestConnectedPerformer callback,
1868 gboolean device_online;
1870 TnyConnectionStatus conn_status;
1871 OnWentOnlineInfo *info;
1872 gboolean user_requested;
1874 device = modest_runtime_get_device();
1875 device_online = tny_device_is_online (device);
1877 /* Whether the connection is user requested or automatically
1878 requested, for example via D-Bus */
1879 user_requested = (parent_window) ? TRUE : FALSE;
1881 /* If there is no account check only the device status */
1884 if (device_online) {
1886 /* We promise to instantly perform the callback, so ... */
1888 callback (FALSE, NULL, parent_window, account, user_data);
1893 info = g_slice_new0 (OnWentOnlineInfo);
1896 info->device = NULL;
1897 info->account = NULL;
1900 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1902 info->parent_window = NULL;
1903 info->user_data = user_data;
1904 info->callback = callback;
1906 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1907 user_requested, on_conic_device_went_online,
1910 /* We'll cleanup in on_conic_device_went_online */
1913 /* The other code has no more reason to run. This is all that we can do for the
1914 * caller (he should have given us a nice and clean account instance!). We
1915 * can't do magic, we don't know what account he intends to bring online. So
1916 * we'll just bring the device online (and await his false bug report). */
1922 /* Return if the account is already connected */
1924 conn_status = tny_account_get_connection_status (account);
1925 if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
1927 /* We promise to instantly perform the callback, so ... */
1929 callback (FALSE, NULL, parent_window, account, user_data);
1935 /* Else, we are in a state that requires that we go online before we
1936 * call the caller's callback. */
1938 info = g_slice_new0 (OnWentOnlineInfo);
1940 info->device = NULL;
1942 info->account = TNY_ACCOUNT (g_object_ref (account));
1945 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1947 info->parent_window = NULL;
1949 /* So we'll put the callback away for later ... */
1951 info->user_data = user_data;
1952 info->callback = callback;
1954 if (!device_online) {
1956 /* If also the device is offline, then we connect both the device
1957 * and the account */
1959 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1960 user_requested, on_conic_device_went_online,
1965 /* If the device is online, we'll just connect the account */
1967 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE,
1968 on_account_went_online, info);
1971 /* The info gets freed by on_account_went_online or on_conic_device_went_online
1972 * in both situations, go look if you don't believe me! */
1978 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window,
1979 TnyFolderStore *folder_store,
1980 ModestConnectedPerformer callback,
1983 TnyAccount *account = NULL;
1985 if (!folder_store) {
1986 /* We promise to instantly perform the callback, so ... */
1988 callback (FALSE, NULL, parent_window, NULL, user_data);
1992 /* Original comment: Maybe it is something local. */
1993 /* PVH's comment: maybe we should KNOW this in stead of assuming? */
1995 } else if (TNY_IS_FOLDER (folder_store)) {
1996 /* Get the folder's parent account: */
1997 account = tny_folder_get_account(TNY_FOLDER (folder_store));
1998 } else if (TNY_IS_ACCOUNT (folder_store)) {
1999 /* Use the folder store as an account: */
2000 account = TNY_ACCOUNT (folder_store);
2003 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
2004 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
2005 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
2007 /* This IS a local account like a maildir account, which does not require
2008 * a connection. (original comment had a vague assumption in its language
2009 * usage. There's no assuming needed, this IS what it IS: a local account), */
2011 /* We promise to instantly perform the callback, so ... */
2013 callback (FALSE, NULL, parent_window, account, user_data);
2020 modest_platform_connect_and_perform (parent_window, account, callback, user_data);