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 <osso-helplib.h>
40 #include <dbus_api/modest-dbus-callbacks.h>
41 #include <libosso-abook/osso-abook.h>
42 #include <maemo/modest-osso-autosave-callbacks.h>
44 #include <alarmd/alarm_event.h> /* For alarm_event_add(), etc. */
45 #include <tny-maemo-conic-device.h>
46 #include <tny-simple-list.h>
47 #include <tny-folder.h>
48 #include <tny-camel-imap-store-account.h>
49 #include <tny-camel-pop-store-account.h>
50 #include <gtk/gtkicontheme.h>
51 #include <gtk/gtkmenuitem.h>
52 #include <gtk/gtkmain.h>
53 #include <modest-text-utils.h>
54 #include "modest-tny-folder.h"
58 #define HILDON_OSSO_URI_ACTION "uri-action"
59 #define URI_ACTION_COPY "copy:"
61 static osso_context_t *osso_context = NULL;
64 on_modest_conf_update_interval_changed (ModestConf* self,
66 ModestConfEvent event,
67 ModestConfNotificationId id,
70 if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
71 const guint update_interval_minutes =
72 modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
73 modest_platform_set_update_interval (update_interval_minutes);
78 modest_platform_init (int argc, char *argv[])
80 osso_hw_state_t hw_state = { 0 };
84 osso_initialize(PACKAGE,PACKAGE_VERSION,
87 g_printerr ("modest: failed to acquire osso context\n");
91 if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
92 g_printerr ("modest: could not get dbus connection\n");
97 /* Add a D-Bus handler to be used when the main osso-rpc
98 * D-Bus handler has not handled something.
99 * We use this for D-Bus methods that need to use more complex types
100 * than osso-rpc supports.
102 if (!dbus_connection_add_filter (con,
103 modest_dbus_req_filter,
107 g_printerr ("modest: Could not add D-Bus filter\n");
111 /* Register our simple D-Bus callbacks, via the osso API: */
112 osso_return_t result = osso_rpc_set_cb_f(osso_context,
116 modest_dbus_req_handler, NULL /* user_data */);
117 if (result != OSSO_OK) {
118 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
122 /* Add handler for Exit D-BUS messages.
123 * Not used because osso_application_set_exit_cb() is deprecated and obsolete:
124 result = osso_application_set_exit_cb(osso_context,
125 modest_dbus_exit_event_handler,
127 if (result != OSSO_OK) {
128 g_print("Error setting exit callback (%d)\n", result);
133 /* Register hardware event dbus callback: */
134 hw_state.shutdown_ind = TRUE;
135 osso_hw_set_event_cb(osso_context, NULL,/*&hw_state*/ modest_osso_cb_hw_state_handler, NULL);
137 /* Register osso auto-save callbacks: */
138 result = osso_application_set_autosave_cb (osso_context,
139 modest_on_osso_application_autosave, NULL /* user_data */);
140 if (result != OSSO_OK) {
141 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
146 /* Make sure that the update interval is changed whenever its gconf key
148 /* CAUTION: we're not using here the
149 modest_conf_listen_to_namespace because we know that there
150 are other parts of Modest listening for this namespace, so
151 we'll receive the notifications anyway. We basically do not
152 use it because there is no easy way to do the
153 modest_conf_forget_namespace */
154 ModestConf *conf = modest_runtime_get_conf ();
155 g_signal_connect (G_OBJECT(conf),
157 G_CALLBACK (on_modest_conf_update_interval_changed),
160 /* Get the initial update interval from gconf: */
161 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
162 MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
164 /* initialize the addressbook */
165 if (!osso_abook_init (&argc, &argv, osso_context)) {
166 g_printerr ("modest: failed to initialized addressbook\n");
174 modest_platform_get_new_device (void)
176 return TNY_DEVICE (tny_maemo_conic_device_new ());
181 guess_mime_type_from_name (const gchar* name)
184 const static gchar* mime_type;
185 const static gchar* mime_map[][2] = {
186 { ".note.html", "text/note"}, /* for the osso_notes program */
187 { ".html", "text/html"},
188 { ".htm", "text/html"},
189 { ".pdf", "application/pdf"},
190 { ".doc", "application/msword"},
191 { ".xls", "application/excel"},
192 { ".png", "image/png" },
193 { ".gif", "image/gif" },
194 { ".jpg", "image/jpeg"},
195 { ".jpeg", "image/jpeg"},
196 { ".mp3", "audio/mp3" }
199 mime_type = "application/octet-stream";
202 gchar* lc_name = g_utf8_strdown (name, -1);
203 for (i = 0; i != G_N_ELEMENTS(mime_map); ++i) {
204 if (g_str_has_suffix (lc_name, mime_map[i][0])) {
205 mime_type = mime_map[i][1];
217 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
218 gchar **effective_mime_type)
220 GString *mime_str = NULL;
221 gchar *icon_name = NULL;
222 gchar **icons, **cursor;
224 if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream"))
225 mime_str = g_string_new (guess_mime_type_from_name(name));
227 mime_str = g_string_new (mime_type);
228 g_string_ascii_down (mime_str);
231 icons = hildon_mime_get_icon_names (mime_str->str, NULL);
232 for (cursor = icons; cursor; ++cursor) {
233 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
234 !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
235 icon_name = g_strdup ("qgn_list_messagin");
237 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
238 icon_name = g_strdup (*cursor);
244 if (effective_mime_type)
245 *effective_mime_type = g_string_free (mime_str, FALSE);
247 g_string_free (mime_str, TRUE);
254 modest_platform_activate_uri (const gchar *uri)
256 HildonURIAction *action;
257 gboolean result = FALSE;
258 GSList *actions, *iter = NULL;
261 g_return_val_if_fail (uri, FALSE);
265 scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
266 actions = hildon_uri_get_actions (scheme, NULL);
268 for (iter = actions; iter; iter = g_slist_next (iter)) {
269 action = (HildonURIAction*) iter->data;
270 if (action && strcmp (hildon_uri_action_get_service (action),
271 "com.nokia.modest") == 0) {
273 result = hildon_uri_open (uri, action, &err);
274 if (!result && err) {
275 g_printerr ("modest: modest_platform_activate_uri : %s",
276 err->message ? err->message : "unknown error");
283 /* if we could open it with email, try something else */
285 result = hildon_uri_open (uri, NULL, NULL);
288 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
294 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
298 gchar *uri_path = NULL;
300 uri_path = g_strconcat ("file://", path, NULL);
301 con = osso_get_dbus_connection (osso_context);
304 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
306 result = hildon_mime_open_file (con, uri_path);
308 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
316 } ModestPlatformPopupInfo;
319 delete_uri_popup (GtkWidget *menu,
323 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
325 g_free (popup_info->uri);
326 hildon_uri_free_actions (popup_info->actions);
332 activate_uri_popup_item (GtkMenuItem *menu_item,
336 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
337 const gchar* action_name;
339 action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
341 g_printerr ("modest: no action name defined\n");
345 /* special handling for the copy menu item -- copy the uri to the clipboard */
346 /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
347 if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
348 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
349 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
351 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
352 action_name += strlen ("mailto:");
354 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
355 return; /* we're done */
358 /* now, the real uri-actions... */
359 for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
360 HildonURIAction *action = (HildonURIAction *) node->data;
361 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
362 hildon_uri_open (popup_info->uri, action, NULL);
369 modest_platform_show_uri_popup (const gchar *uri)
372 GSList *actions_list;
377 scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
378 actions_list = hildon_uri_get_actions (scheme, NULL);
379 if (actions_list != NULL) {
381 GtkWidget *menu = gtk_menu_new ();
382 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
384 popup_info->actions = actions_list;
385 popup_info->uri = g_strdup (uri);
387 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
388 GtkWidget *menu_item;
389 const gchar *action_name;
390 const gchar *translation_domain;
391 HildonURIAction *action = (HildonURIAction *) node->data;
392 action_name = hildon_uri_action_get_name (action);
393 translation_domain = hildon_uri_action_get_translation_domain (action);
394 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
395 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name); /* hack */
396 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
399 if (hildon_uri_is_default_action (action, NULL)) {
400 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
402 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
404 gtk_widget_show (menu_item);
407 /* always add the copy item */
408 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri", "uri_link_copy_link_location"));
409 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
410 g_strconcat (URI_ACTION_COPY, uri, NULL),
412 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
413 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
414 gtk_widget_show (menu_item);
417 /* and what to do when the link is deleted */
418 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
419 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
422 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
431 modest_platform_get_icon (const gchar *name)
434 GdkPixbuf* pixbuf = NULL;
435 GtkIconTheme *current_theme = NULL;
437 g_return_val_if_fail (name, NULL);
439 /* strlen == 0 is not really an error; it just
440 * means the icon is not available
442 if (!name || strlen(name) == 0)
445 #if 0 /* do we still need this? */
446 if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
447 pixbuf = gdk_pixbuf_new_from_file (name, &err);
449 g_printerr ("modest: error loading icon '%s': %s\n",
457 current_theme = gtk_icon_theme_get_default ();
458 pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
459 GTK_ICON_LOOKUP_NO_SVG,
462 g_printerr ("modest: error loading theme icon '%s': %s\n",
470 modest_platform_get_app_name (void)
472 return _("mcen_ap_name");
476 entry_insert_text (GtkEditable *editable,
485 chars = gtk_editable_get_chars (editable, 0, -1);
486 chars_length = g_utf8_strlen (chars, -1);
488 /* Show WID-INF036 */
489 if (chars_length >= 20) {
490 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
491 _CS("ckdg_ib_maximum_characters_reached"));
493 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
497 tmp = g_strndup (folder_name_forbidden_chars,
498 FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
499 msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
500 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)),
505 /* Write the text in the entry if it's valid */
506 g_signal_handlers_block_by_func (editable,
507 (gpointer) entry_insert_text, data);
508 gtk_editable_insert_text (editable, text, length, position);
509 g_signal_handlers_unblock_by_func (editable,
510 (gpointer) entry_insert_text, data);
513 /* Do not allow further processing */
514 g_signal_stop_emission_by_name (editable, "insert_text");
518 entry_changed (GtkEditable *editable,
522 GtkWidget *ok_button;
525 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
526 ok_button = GTK_WIDGET (buttons->next->data);
528 chars = gtk_editable_get_chars (editable, 0, -1);
529 g_return_if_fail (chars != NULL);
532 if (g_utf8_strlen (chars,-1) >= 21)
533 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
534 _CS("ckdg_ib_maximum_characters_reached"));
536 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
539 g_list_free (buttons);
544 launch_sort_headers_dialog (GtkWindow *parent_window,
545 HildonSortDialog *dialog)
547 ModestHeaderView *header_view = NULL;
549 GtkSortType sort_type;
551 gint default_key = 0;
553 gboolean outgoing = FALSE;
554 gint current_sort_colid = -1;
555 GtkSortType current_sort_type;
556 gint attachments_sort_id;
557 gint priority_sort_id;
558 GtkTreeSortable *sortable;
560 /* Get header window */
561 if (MODEST_IS_MAIN_WINDOW (parent_window)) {
562 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
563 MODEST_WIDGET_TYPE_HEADER_VIEW));
565 if (!header_view) return;
567 /* Add sorting keys */
568 cols = modest_header_view_get_columns (header_view);
569 if (cols == NULL) return;
570 int sort_model_ids[6];
574 outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
575 MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
577 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
579 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
580 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
582 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
583 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
586 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
588 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
589 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
591 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
592 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
594 default_key = sort_key;
596 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
597 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
599 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
601 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
603 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
604 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
605 sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
606 attachments_sort_id = sort_key;
608 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
609 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
610 sort_ids[sort_key] = 0;
612 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
613 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
614 sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
615 priority_sort_id = sort_key;
617 sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
619 if (!gtk_tree_sortable_get_sort_column_id (sortable,
620 ¤t_sort_colid, ¤t_sort_type)) {
621 hildon_sort_dialog_set_sort_key (dialog, default_key);
622 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
624 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
625 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
626 gpointer flags_sort_type_pointer;
627 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
628 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
629 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
631 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
633 gint current_sort_keyid = 0;
634 while (current_sort_keyid < 6) {
635 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
638 current_sort_keyid++;
640 hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
644 result = gtk_dialog_run (GTK_DIALOG (dialog));
645 if (result == GTK_RESPONSE_OK) {
646 sort_key = hildon_sort_dialog_get_sort_key (dialog);
647 sort_type = hildon_sort_dialog_get_sort_order (dialog);
648 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
649 g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
650 GINT_TO_POINTER (sort_ids[sort_key]));
651 /* This is a hack to make it resort rows always when flag fields are
652 * selected. If we do not do this, changing sort field from priority to
653 * attachments does not work */
654 modest_header_view_sort_by_column_id (header_view, 0, sort_type);
656 gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data),
657 sort_model_ids[sort_key]);
660 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
661 gtk_tree_sortable_sort_column_changed (sortable);
664 modest_widget_memory_save (modest_runtime_get_conf (),
665 G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
667 /* while (gtk_events_pending ()) */
668 /* gtk_main_iteration (); */
677 on_response (GtkDialog *dialog,
681 GList *child_vbox, *child_hbox;
682 GtkWidget *hbox, *entry;
683 TnyFolderStore *parent;
685 if (response != GTK_RESPONSE_ACCEPT)
689 child_vbox = gtk_container_get_children (GTK_CONTAINER (dialog->vbox));
690 hbox = child_vbox->data;
691 child_hbox = gtk_container_get_children (GTK_CONTAINER (hbox));
692 entry = child_hbox->next->data;
694 parent = TNY_FOLDER_STORE (user_data);
696 /* Look for another folder with the same name */
697 if (modest_tny_folder_has_subfolder_with_name (parent,
698 gtk_entry_get_text (GTK_ENTRY (entry)))) {
700 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)),
701 NULL, _CS("ckdg_ib_folder_already_exists"));
702 /* Select the text */
703 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
704 gtk_widget_grab_focus (entry);
705 /* Do not close the dialog */
706 g_signal_stop_emission_by_name (dialog, "response");
712 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
713 TnyFolderStore *parent,
714 const gchar *dialog_title,
715 const gchar *label_text,
716 const gchar *suggested_name,
719 GtkWidget *accept_btn = NULL;
720 GtkWidget *dialog, *entry, *label, *hbox;
721 GList *buttons = NULL;
724 /* Ask the user for the folder name */
725 dialog = gtk_dialog_new_with_buttons (dialog_title,
727 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
728 _("mcen_bd_dialog_ok"),
730 _("mcen_bd_dialog_cancel"),
734 /* Add accept button (with unsensitive handler) */
735 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
736 accept_btn = GTK_WIDGET (buttons->next->data);
737 /* Create label and entry */
738 label = gtk_label_new (label_text);
739 /* TODO: check that the suggested name does not exist */
740 /* We set 21 as maximum because we want to show WID-INF036
741 when the user inputs more that 20 */
742 entry = gtk_entry_new_with_max_length (21);
744 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
746 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
747 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
749 /* Connect to the response method to avoid closing the dialog
750 when an invalid name is selected*/
751 g_signal_connect (dialog,
753 G_CALLBACK (on_response),
756 /* Track entry changes */
757 g_signal_connect (entry,
759 G_CALLBACK (entry_insert_text),
761 g_signal_connect (entry,
763 G_CALLBACK (entry_changed),
766 /* Create the hbox */
767 hbox = gtk_hbox_new (FALSE, 12);
768 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
769 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
771 /* Add hbox to dialog */
772 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox),
773 hbox, FALSE, FALSE, 0);
775 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
777 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
781 result = gtk_dialog_run (GTK_DIALOG(dialog));
782 if (result == GTK_RESPONSE_ACCEPT)
783 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
785 gtk_widget_destroy (dialog);
787 while (gtk_events_pending ())
788 gtk_main_iteration ();
794 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
795 TnyFolderStore *parent_folder,
796 gchar *suggested_name,
799 gchar *real_suggested_name = NULL;
802 if(suggested_name == NULL)
804 const gchar *default_name = _("mcen_ia_default_folder_name");
808 for(i = 0; i < 100; ++ i) {
809 gboolean exists = FALSE;
811 sprintf(num_str, "%.2u", i);
814 real_suggested_name = g_strdup (default_name);
816 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
819 exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
820 real_suggested_name);
825 g_free (real_suggested_name);
828 /* Didn't find a free number */
830 real_suggested_name = g_strdup (default_name);
832 real_suggested_name = suggested_name;
835 result = modest_platform_run_folder_name_dialog (parent_window,
837 _("mcen_ti_new_folder"),
838 _("mcen_fi_new_folder_name"),
841 if (suggested_name == NULL)
842 g_free(real_suggested_name);
848 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
849 TnyFolderStore *parent_folder,
850 const gchar *suggested_name,
853 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
855 return modest_platform_run_folder_name_dialog (parent_window,
857 _HL("ckdg_ti_rename_folder"),
858 _HL("ckdg_fi_rename_name"),
864 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
865 const gchar *message)
870 dialog = hildon_note_new_confirmation (parent_window, message);
871 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
873 response = gtk_dialog_run (GTK_DIALOG (dialog));
875 gtk_widget_destroy (GTK_WIDGET (dialog));
877 while (gtk_events_pending ())
878 gtk_main_iteration ();
884 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
885 const gchar *message)
890 dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
891 _("mcen_bd_yes"), GTK_RESPONSE_YES,
892 _("mcen_bd_no"), GTK_RESPONSE_NO,
894 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
896 response = gtk_dialog_run (GTK_DIALOG (dialog));
898 gtk_widget_destroy (GTK_WIDGET (dialog));
900 while (gtk_events_pending ())
901 gtk_main_iteration ();
907 modest_platform_run_information_dialog (GtkWindow *parent_window,
908 const gchar *message)
912 dialog = hildon_note_new_information (parent_window, message);
914 g_signal_connect_swapped (dialog,
916 G_CALLBACK (gtk_widget_destroy),
919 gtk_widget_show_all (dialog);
930 on_idle_connect_and_wait(gpointer user_data)
932 printf ("DEBUG: %s:\n", __FUNCTION__);
933 TnyDevice *device = modest_runtime_get_device();
934 if (!tny_device_is_online (device)) {
936 /* This is a GDK lock because we are an idle callback and
937 * tny_maemo_conic_device_connect can contain Gtk+ code */
939 gdk_threads_enter(); /* CHECKED */
940 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
941 gdk_threads_leave(); /* CHECKED */
944 /* Allow the function that requested this idle callback to continue: */
945 UtilIdleData *data = (UtilIdleData*)user_data;
947 g_main_loop_quit (data->loop);
949 return FALSE; /* Don't call this again. */
952 static gboolean connect_request_in_progress = FALSE;
954 /* This callback is used when connect_and_wait() is already queued as an idle callback.
955 * This can happen because the gtk_dialog_run() for the connection dialog
956 * (at least in the fake scratchbox version) allows idle handlers to keep running.
959 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
961 gboolean result = FALSE;
962 TnyDevice *device = modest_runtime_get_device();
963 if (tny_device_is_online (device))
964 result = FALSE; /* Stop trying. */
966 /* Keep trying until connect_request_in_progress is FALSE. */
967 if (connect_request_in_progress)
968 result = TRUE; /* Keep trying */
970 printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
972 result = FALSE; /* Stop trying, now that a result should be available. */
976 if (result == FALSE) {
977 /* Allow the function that requested this idle callback to continue: */
978 UtilIdleData *data = (UtilIdleData*)user_data;
980 g_main_loop_quit (data->loop);
987 set_account_to_online (TnyAccount *account)
989 /* TODO: This is necessary to prevent a cancel of the password dialog
990 * from making a restart necessary to be asked the password again,
991 * but it causes a hang:
994 if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
995 /* Make sure that store accounts are online too,
996 * because tinymail sets accounts to offline if
997 * a password dialog is ever cancelled.
998 * We don't do this for transport accounts because
999 * a) They fundamentally need network access, so they can't really be offline.
1000 * b) That might cause a transport connection to happen too early.
1002 GError *error = NULL;
1003 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, &error);
1005 g_warning ("%s: tny_camel_account_set_online() returned a GError:\n %s\n",
1006 __FUNCTION__, error->message);
1007 g_error_free (error);
1013 gboolean modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1015 if (connect_request_in_progress)
1018 printf ("DEBUG: %s:\n", __FUNCTION__);
1019 TnyDevice *device = modest_runtime_get_device();
1021 if (tny_device_is_online (device)) {
1022 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1023 set_account_to_online (account);
1027 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1030 /* This blocks on the result: */
1031 UtilIdleData *data = g_slice_new0 (UtilIdleData);
1033 GMainContext *context = NULL; /* g_main_context_new (); */
1034 data->loop = g_main_loop_new (context, FALSE /* not running */);
1036 /* Cause the function to be run in an idle-handler, which is always
1037 * in the main thread:
1039 if (!connect_request_in_progress) {
1040 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1041 connect_request_in_progress = TRUE;
1042 g_idle_add (&on_idle_connect_and_wait, data);
1045 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1046 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1049 /* This main loop will run until the idle handler has stopped it: */
1050 printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1051 GDK_THREADS_LEAVE();
1052 g_main_loop_run (data->loop);
1053 GDK_THREADS_ENTER();
1054 printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1055 connect_request_in_progress = FALSE;
1056 printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1057 g_main_loop_unref (data->loop);
1058 /* g_main_context_unref (context); */
1060 g_slice_free (UtilIdleData, data);
1062 const gboolean result = tny_device_is_online (device);
1065 set_account_to_online (account);
1071 gboolean modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1073 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1074 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1075 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1076 /* This must be a maildir account, which does not require a connection: */
1081 return modest_platform_connect_and_wait (parent_window, account);
1084 gboolean modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1087 return TRUE; /* Maybe it is something local. */
1089 gboolean result = TRUE;
1090 if (TNY_IS_FOLDER (folder_store)) {
1091 /* Get the folder's parent account: */
1092 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1093 if (account != NULL) {
1094 result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1095 g_object_unref (account);
1097 } else if (TNY_IS_ACCOUNT (folder_store)) {
1098 /* Use the folder store as an account: */
1099 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1105 gboolean modest_platform_is_network_folderstore (TnyFolderStore *folder_store)
1107 TnyAccount *account = NULL;
1108 gboolean result = TRUE;
1110 g_return_val_if_fail(TNY_IS_FOLDER_STORE(folder_store), FALSE);
1112 if (TNY_IS_FOLDER (folder_store)) {
1113 /* Get the folder's parent account: */
1114 account = tny_folder_get_account(TNY_FOLDER(folder_store));
1115 } else if (TNY_IS_ACCOUNT (folder_store)) {
1116 account = TNY_ACCOUNT(folder_store);
1117 g_object_ref(account);
1120 if (account != NULL) {
1121 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1122 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1123 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1124 /* This must be a maildir account, which does
1125 * not require a connection: */
1129 g_object_unref (account);
1138 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1139 ModestSortDialogType type)
1141 GtkWidget *dialog = NULL;
1144 dialog = hildon_sort_dialog_new (parent_window);
1145 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1147 /* Fill sort keys */
1149 case MODEST_SORT_HEADERS:
1150 launch_sort_headers_dialog (parent_window,
1151 HILDON_SORT_DIALOG(dialog));
1156 gtk_widget_destroy (GTK_WIDGET (dialog));
1160 gboolean modest_platform_set_update_interval (guint minutes)
1162 ModestConf *conf = modest_runtime_get_conf ();
1166 cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1168 /* Delete any existing alarm,
1169 * because we will replace it: */
1171 /* TODO: What does the alarm_event_del() return value mean? */
1172 alarm_event_del(alarm_cookie);
1174 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1177 /* 0 means no updates: */
1182 /* Register alarm: */
1184 /* Set the interval in alarm_event_t structure: */
1185 alarm_event_t *event = g_new0(alarm_event_t, 1);
1186 event->alarm_time = minutes * 60; /* seconds */
1188 /* Set recurrence every few minutes: */
1189 event->recurrence = minutes;
1190 event->recurrence_count = -1; /* Means infinite */
1192 /* Specify what should happen when the alarm happens:
1193 * It should call this D-Bus method: */
1195 event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1196 event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1197 event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1198 event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1200 /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if
1201 * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1202 * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails
1203 * This is why we want to use the Alarm API instead of just g_timeout_add().
1204 * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1206 event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1208 alarm_cookie = alarm_event_add (event);
1211 alarm_event_free (event);
1213 /* Store the alarm ID in GConf, so we can remove it later:
1214 * This is apparently valid between application instances. */
1215 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1217 if (!alarm_cookie) {
1219 const alarm_error_t alarm_error = alarmd_get_error ();
1220 g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1222 /* Give people some clue: */
1223 /* The alarm API should have a function for this: */
1224 if (alarm_error == ALARMD_ERROR_DBUS) {
1225 g_debug (" ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1226 } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1227 g_debug (" ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1228 } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1229 g_debug (" ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1230 } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1231 g_debug (" ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1232 } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1233 g_debug (" ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1234 } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1235 g_debug (" ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1245 modest_platform_get_global_settings_dialog ()
1247 return modest_maemo_global_settings_dialog_new ();
1251 modest_platform_on_new_msg (void)
1253 #ifdef MODEST_HAVE_HILDON_NOTIFY
1254 HildonNotification *not;
1256 /* Create a new notification. TODO: per-mail data needed */
1257 not = hildon_notification_new ("TODO: (new email) Summary",
1258 "TODO: (new email) Description",
1259 "qgn_list_messagin_mail_unread",
1262 hildon_notification_add_dbus_action(not,
1265 MODEST_DBUS_SERVICE,
1268 MODEST_DBUS_METHOD_OPEN_DEFAULT_INBOX,
1271 /* Play sound SR-SND-18 */
1272 hildon_notification_set_sound (not, "/usr/share/sounds/ui-new_email.wav");
1273 notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "dialog-type", 4);
1275 /* Set the led pattern */
1276 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (not),
1278 "PatternCommunicationEmail");
1280 /* Notify. We need to do this in an idle because this function
1281 could be called from a thread */
1282 if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1283 g_error ("Failed to send notification");
1285 g_object_unref (not);
1286 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1291 modest_platform_show_help (GtkWindow *parent_window,
1292 const gchar *help_id)
1294 osso_return_t result;
1296 g_return_if_fail (help_id);
1297 g_return_if_fail (osso_context);
1300 #ifdef MODEST_HAVE_OSSO_HELP
1301 result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1303 result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1306 if (result != OSSO_OK) {
1308 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id);
1309 hildon_banner_show_information (GTK_WIDGET (parent_window),
1317 modest_platform_show_search_messages (GtkWindow *parent_window)
1319 osso_return_t result = OSSO_ERROR;
1321 result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1323 if (result != OSSO_OK) {
1324 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1329 modest_platform_show_addressbook (GtkWindow *parent_window)
1331 osso_return_t result = OSSO_ERROR;
1333 result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1335 if (result != OSSO_OK) {
1336 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1341 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1343 GtkWidget *widget = modest_folder_view_new (query);
1345 /* Show one account by default */
1346 modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1347 MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1350 /* Restore settings */
1351 modest_widget_memory_restore (modest_runtime_get_conf(),
1353 MODEST_CONF_FOLDER_VIEW_KEY);
1359 modest_platform_information_banner (GtkWidget *parent,
1360 const gchar *icon_name,
1363 hildon_banner_show_information (parent, icon_name, text);
1367 modest_platform_animation_banner (GtkWidget *parent,
1368 const gchar *animation_name,
1371 GtkWidget *inf_note = NULL;
1373 g_return_val_if_fail (text != NULL, NULL);
1375 inf_note = hildon_banner_show_animation (parent, animation_name, text);
1383 TnyAccount *account;
1386 } CheckAccountIdleData;
1388 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1391 on_timeout_check_account_is_online(gpointer user_data)
1393 printf ("DEBUG: %s:\n", __FUNCTION__);
1394 CheckAccountIdleData *data = (CheckAccountIdleData*)user_data;
1397 g_warning ("%s: data is NULL.\n", __FUNCTION__);
1400 if (!(data->account)) {
1401 g_warning ("%s: data->account is NULL.\n", __FUNCTION__);
1404 if (data && data->account) {
1405 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__, tny_account_get_connection_status (data->account));
1408 gboolean stop_trying = FALSE;
1409 if (data && data->account &&
1410 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1411 * after which the account is likely to be usable, or never likely to be usable soon: */
1412 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1414 data->is_online = TRUE;
1419 /* Give up if we have tried too many times: */
1420 if (data->count_tries >= NUMBER_OF_TRIES)
1425 /* Wait for another timeout: */
1426 ++(data->count_tries);
1431 /* Allow the function that requested this idle callback to continue: */
1433 g_main_loop_quit (data->loop);
1436 g_object_unref (data->account);
1438 return FALSE; /* Don't call this again. */
1440 return TRUE; /* Call this timeout callback again. */
1444 /* Return TRUE immediately if the account is already online,
1445 * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as
1446 * soon as the account is online, or FALSE if the account does
1447 * not become online in the NUMBER_OF_TRIES seconds.
1448 * This is useful when the D-Bus method was run immediately after
1449 * the application was started (when using D-Bus activation),
1450 * because the account usually takes a short time to go online.
1451 * The return value is maybe not very useful.
1454 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1456 g_return_val_if_fail (account, FALSE);
1458 printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1460 if (!tny_device_is_online (modest_runtime_get_device())) {
1461 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1465 /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1466 * so we avoid wait unnecessarily: */
1467 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1468 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1472 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1473 __FUNCTION__, tny_account_get_connection_status (account));
1475 /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED,
1476 * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that
1477 * we want to avoid. */
1478 if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1481 /* This blocks on the result: */
1482 CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1483 data->is_online = FALSE;
1484 data->account = account;
1485 g_object_ref (data->account);
1486 data->count_tries = 0;
1488 GMainContext *context = NULL; /* g_main_context_new (); */
1489 data->loop = g_main_loop_new (context, FALSE /* not running */);
1491 g_timeout_add (1000, &on_timeout_check_account_is_online, data);
1493 /* This main loop will run until the idle handler has stopped it: */
1494 g_main_loop_run (data->loop);
1496 g_main_loop_unref (data->loop);
1497 /* g_main_context_unref (context); */
1499 g_slice_free (CheckAccountIdleData, data);
1501 return data->is_online;