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)
185 const static gchar* octet_stream= "application/octet-stream";
186 const static gchar* mime_map[][2] = {
187 { "note.html", "text/note"}, /* for the osso_notes program */
188 { "pdf", "application/pdf"},
189 { "doc", "application/msword"},
190 { "xls", "application/excel"},
191 { "png", "image/png" },
192 { "gif", "image/gif" },
193 { "jpg", "image/jpeg"},
194 { "jpeg", "image/jpeg"},
195 { "mp3", "audio/mp3" }
201 ext = g_strrstr (name, ".");
205 for (i = 0; i != G_N_ELEMENTS(mime_map); ++i) {
206 if (!g_ascii_strcasecmp (mime_map[i][0], ext + 1)) /* +1: ignore '.'*/
207 return mime_map[i][1];
214 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
215 gchar **effective_mime_type)
217 GString *mime_str = NULL;
218 gchar *icon_name = NULL;
219 gchar **icons, **cursor;
221 if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream"))
222 mime_str = g_string_new (guess_mime_type_from_name(name));
224 mime_str = g_string_new (mime_type);
225 g_string_ascii_down (mime_str);
228 icons = hildon_mime_get_icon_names (mime_str->str, NULL);
229 for (cursor = icons; cursor; ++cursor) {
230 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
231 !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
232 icon_name = g_strdup ("qgn_list_messagin");
234 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
235 icon_name = g_strdup (*cursor);
241 if (effective_mime_type)
242 *effective_mime_type = g_string_free (mime_str, FALSE);
244 g_string_free (mime_str, TRUE);
251 modest_platform_activate_uri (const gchar *uri)
253 HildonURIAction *action;
254 gboolean result = FALSE;
255 GSList *actions, *iter = NULL;
258 g_return_val_if_fail (uri, FALSE);
262 scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
263 actions = hildon_uri_get_actions (scheme, NULL);
265 for (iter = actions; iter; iter = g_slist_next (iter)) {
266 action = (HildonURIAction*) iter->data;
267 if (action && strcmp (hildon_uri_action_get_service (action),
268 "com.nokia.modest") == 0) {
270 result = hildon_uri_open (uri, action, &err);
271 if (!result && err) {
272 g_printerr ("modest: modest_platform_activate_uri : %s",
273 err->message ? err->message : "unknown error");
280 /* if we could open it with email, try something else */
282 result = hildon_uri_open (uri, NULL, NULL);
285 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
291 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
295 gchar *uri_path = NULL;
297 uri_path = g_strconcat ("file://", path, NULL);
298 con = osso_get_dbus_connection (osso_context);
301 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
303 result = hildon_mime_open_file (con, uri_path);
305 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
313 } ModestPlatformPopupInfo;
316 delete_uri_popup (GtkWidget *menu,
320 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
322 g_free (popup_info->uri);
323 hildon_uri_free_actions (popup_info->actions);
329 activate_uri_popup_item (GtkMenuItem *menu_item,
333 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
334 const gchar* action_name;
336 action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
338 g_printerr ("modest: no action name defined\n");
342 /* special handling for the copy menu item -- copy the uri to the clipboard */
343 /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
344 if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
345 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
346 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
348 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
349 action_name += strlen ("mailto:");
351 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
352 return; /* we're done */
355 /* now, the real uri-actions... */
356 for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
357 HildonURIAction *action = (HildonURIAction *) node->data;
358 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
359 hildon_uri_open (popup_info->uri, action, NULL);
366 modest_platform_show_uri_popup (const gchar *uri)
369 GSList *actions_list;
374 scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
375 actions_list = hildon_uri_get_actions (scheme, NULL);
376 if (actions_list != NULL) {
378 GtkWidget *menu = gtk_menu_new ();
379 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
381 popup_info->actions = actions_list;
382 popup_info->uri = g_strdup (uri);
384 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
385 GtkWidget *menu_item;
386 const gchar *action_name;
387 const gchar *translation_domain;
388 HildonURIAction *action = (HildonURIAction *) node->data;
389 action_name = hildon_uri_action_get_name (action);
390 translation_domain = hildon_uri_action_get_translation_domain (action);
391 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
392 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name); /* hack */
393 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
396 if (hildon_uri_is_default_action (action, NULL)) {
397 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
399 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
401 gtk_widget_show (menu_item);
404 /* always add the copy item */
405 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri", "uri_link_copy_link_location"));
406 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
407 g_strconcat (URI_ACTION_COPY, uri, NULL),
409 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
410 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
411 gtk_widget_show (menu_item);
414 /* and what to do when the link is deleted */
415 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
416 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
419 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
428 modest_platform_get_icon (const gchar *name)
431 GdkPixbuf* pixbuf = NULL;
432 GtkIconTheme *current_theme = NULL;
434 g_return_val_if_fail (name, NULL);
436 /* strlen == 0 is not really an error; it just
437 * means the icon is not available
439 if (!name || strlen(name) == 0)
442 #if 0 /* do we still need this? */
443 if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
444 pixbuf = gdk_pixbuf_new_from_file (name, &err);
446 g_printerr ("modest: error loading icon '%s': %s\n",
454 current_theme = gtk_icon_theme_get_default ();
455 pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
456 GTK_ICON_LOOKUP_NO_SVG,
459 g_printerr ("modest: error loading theme icon '%s': %s\n",
467 modest_platform_get_app_name (void)
469 return _("mcen_ap_name");
473 entry_insert_text (GtkEditable *editable,
482 chars = gtk_editable_get_chars (editable, 0, -1);
483 chars_length = g_utf8_strlen (chars, -1);
485 /* Show WID-INF036 */
486 if (chars_length >= 20) {
487 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
488 _CS("ckdg_ib_maximum_characters_reached"));
490 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
494 tmp = g_strndup (folder_name_forbidden_chars,
495 FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
496 msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
497 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)),
502 /* Write the text in the entry if it's valid */
503 g_signal_handlers_block_by_func (editable,
504 (gpointer) entry_insert_text, data);
505 gtk_editable_insert_text (editable, text, length, position);
506 g_signal_handlers_unblock_by_func (editable,
507 (gpointer) entry_insert_text, data);
510 /* Do not allow further processing */
511 g_signal_stop_emission_by_name (editable, "insert_text");
515 entry_changed (GtkEditable *editable,
519 GtkWidget *ok_button;
522 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
523 ok_button = GTK_WIDGET (buttons->next->data);
525 chars = gtk_editable_get_chars (editable, 0, -1);
526 g_return_if_fail (chars != NULL);
529 if (g_utf8_strlen (chars,-1) >= 21)
530 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
531 _CS("ckdg_ib_maximum_characters_reached"));
533 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
536 g_list_free (buttons);
541 launch_sort_headers_dialog (GtkWindow *parent_window,
542 HildonSortDialog *dialog)
544 ModestHeaderView *header_view = NULL;
546 GtkSortType sort_type;
548 gint default_key = 0;
550 gboolean outgoing = FALSE;
551 gint current_sort_colid = -1;
552 GtkSortType current_sort_type;
553 gint attachments_sort_id;
554 gint priority_sort_id;
555 GtkTreeSortable *sortable;
557 /* Get header window */
558 if (MODEST_IS_MAIN_WINDOW (parent_window)) {
559 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
560 MODEST_WIDGET_TYPE_HEADER_VIEW));
562 if (!header_view) return;
564 /* Add sorting keys */
565 cols = modest_header_view_get_columns (header_view);
566 if (cols == NULL) return;
567 int sort_model_ids[6];
571 outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
572 MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
574 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
576 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
577 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
579 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
580 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
583 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
585 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
586 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
588 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
589 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
591 default_key = sort_key;
593 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
594 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
596 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
598 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
600 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
601 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
602 sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
603 attachments_sort_id = sort_key;
605 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
606 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
607 sort_ids[sort_key] = 0;
609 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
610 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
611 sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
612 priority_sort_id = sort_key;
614 sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
616 if (!gtk_tree_sortable_get_sort_column_id (sortable,
617 ¤t_sort_colid, ¤t_sort_type)) {
618 hildon_sort_dialog_set_sort_key (dialog, default_key);
619 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
621 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
622 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
623 gpointer flags_sort_type_pointer;
624 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
625 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
626 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
628 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
630 gint current_sort_keyid = 0;
631 while (current_sort_keyid < 6) {
632 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
635 current_sort_keyid++;
637 hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
641 result = gtk_dialog_run (GTK_DIALOG (dialog));
642 if (result == GTK_RESPONSE_OK) {
643 sort_key = hildon_sort_dialog_get_sort_key (dialog);
644 sort_type = hildon_sort_dialog_get_sort_order (dialog);
645 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
646 g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
647 GINT_TO_POINTER (sort_ids[sort_key]));
648 /* This is a hack to make it resort rows always when flag fields are
649 * selected. If we do not do this, changing sort field from priority to
650 * attachments does not work */
651 modest_header_view_sort_by_column_id (header_view, 0, sort_type);
653 gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data),
654 sort_model_ids[sort_key]);
657 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
658 gtk_tree_sortable_sort_column_changed (sortable);
661 modest_widget_memory_save (modest_runtime_get_conf (),
662 G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
664 /* while (gtk_events_pending ()) */
665 /* gtk_main_iteration (); */
674 on_response (GtkDialog *dialog,
678 GList *child_vbox, *child_hbox;
679 GtkWidget *hbox, *entry;
680 TnyFolderStore *parent;
682 if (response != GTK_RESPONSE_ACCEPT)
686 child_vbox = gtk_container_get_children (GTK_CONTAINER (dialog->vbox));
687 hbox = child_vbox->data;
688 child_hbox = gtk_container_get_children (GTK_CONTAINER (hbox));
689 entry = child_hbox->next->data;
691 parent = TNY_FOLDER_STORE (user_data);
693 /* Look for another folder with the same name */
694 if (modest_tny_folder_has_subfolder_with_name (parent,
695 gtk_entry_get_text (GTK_ENTRY (entry)))) {
697 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)),
698 NULL, _CS("ckdg_ib_folder_already_exists"));
699 /* Select the text */
700 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
701 gtk_widget_grab_focus (entry);
702 /* Do not close the dialog */
703 g_signal_stop_emission_by_name (dialog, "response");
709 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
710 TnyFolderStore *parent,
711 const gchar *dialog_title,
712 const gchar *label_text,
713 const gchar *suggested_name,
716 GtkWidget *accept_btn = NULL;
717 GtkWidget *dialog, *entry, *label, *hbox;
718 GList *buttons = NULL;
721 /* Ask the user for the folder name */
722 dialog = gtk_dialog_new_with_buttons (dialog_title,
724 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
725 _("mcen_bd_dialog_ok"),
727 _("mcen_bd_dialog_cancel"),
731 /* Add accept button (with unsensitive handler) */
732 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
733 accept_btn = GTK_WIDGET (buttons->next->data);
734 /* Create label and entry */
735 label = gtk_label_new (label_text);
736 /* TODO: check that the suggested name does not exist */
737 /* We set 21 as maximum because we want to show WID-INF036
738 when the user inputs more that 20 */
739 entry = gtk_entry_new_with_max_length (21);
741 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
743 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
744 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
746 /* Connect to the response method to avoid closing the dialog
747 when an invalid name is selected*/
748 g_signal_connect (dialog,
750 G_CALLBACK (on_response),
753 /* Track entry changes */
754 g_signal_connect (entry,
756 G_CALLBACK (entry_insert_text),
758 g_signal_connect (entry,
760 G_CALLBACK (entry_changed),
763 /* Create the hbox */
764 hbox = gtk_hbox_new (FALSE, 12);
765 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
766 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
768 /* Add hbox to dialog */
769 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox),
770 hbox, FALSE, FALSE, 0);
772 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
774 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
778 result = gtk_dialog_run (GTK_DIALOG(dialog));
779 if (result == GTK_RESPONSE_ACCEPT)
780 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
782 gtk_widget_destroy (dialog);
784 while (gtk_events_pending ())
785 gtk_main_iteration ();
791 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
792 TnyFolderStore *parent_folder,
793 gchar *suggested_name,
796 gchar *real_suggested_name = NULL;
799 if(suggested_name == NULL)
801 const gchar *default_name = _("mcen_ia_default_folder_name");
805 for(i = 0; i < 100; ++ i) {
806 gboolean exists = FALSE;
808 sprintf(num_str, "%.2u", i);
811 real_suggested_name = g_strdup (default_name);
813 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
816 exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
817 real_suggested_name);
822 g_free (real_suggested_name);
825 /* Didn't find a free number */
827 real_suggested_name = g_strdup (default_name);
829 real_suggested_name = suggested_name;
832 result = modest_platform_run_folder_name_dialog (parent_window,
834 _("mcen_ti_new_folder"),
835 _("mcen_fi_new_folder_name"),
838 if (suggested_name == NULL)
839 g_free(real_suggested_name);
845 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
846 TnyFolderStore *parent_folder,
847 const gchar *suggested_name,
850 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
852 return modest_platform_run_folder_name_dialog (parent_window,
854 _HL("ckdg_ti_rename_folder"),
855 _HL("ckdg_fi_rename_name"),
861 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
862 const gchar *message)
867 dialog = hildon_note_new_confirmation (parent_window, message);
868 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
870 response = gtk_dialog_run (GTK_DIALOG (dialog));
872 gtk_widget_destroy (GTK_WIDGET (dialog));
874 while (gtk_events_pending ())
875 gtk_main_iteration ();
881 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
882 const gchar *message)
887 dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
888 _("mcen_bd_yes"), GTK_RESPONSE_YES,
889 _("mcen_bd_no"), GTK_RESPONSE_NO,
891 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
893 response = gtk_dialog_run (GTK_DIALOG (dialog));
895 gtk_widget_destroy (GTK_WIDGET (dialog));
897 while (gtk_events_pending ())
898 gtk_main_iteration ();
904 modest_platform_run_information_dialog (GtkWindow *parent_window,
905 const gchar *message)
909 dialog = hildon_note_new_information (parent_window, message);
911 g_signal_connect_swapped (dialog,
913 G_CALLBACK (gtk_widget_destroy),
916 gtk_widget_show_all (dialog);
927 on_idle_connect_and_wait(gpointer user_data)
929 printf ("DEBUG: %s:\n", __FUNCTION__);
930 TnyDevice *device = modest_runtime_get_device();
931 if (!tny_device_is_online (device)) {
933 /* This is a GDK lock because we are an idle callback and
934 * tny_maemo_conic_device_connect can contain Gtk+ code */
936 gdk_threads_enter(); /* CHECKED */
937 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
938 gdk_threads_leave(); /* CHECKED */
941 /* Allow the function that requested this idle callback to continue: */
942 UtilIdleData *data = (UtilIdleData*)user_data;
944 g_main_loop_quit (data->loop);
946 return FALSE; /* Don't call this again. */
949 static gboolean connect_request_in_progress = FALSE;
951 /* This callback is used when connect_and_wait() is already queued as an idle callback.
952 * This can happen because the gtk_dialog_run() for the connection dialog
953 * (at least in the fake scratchbox version) allows idle handlers to keep running.
956 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
958 gboolean result = FALSE;
959 TnyDevice *device = modest_runtime_get_device();
960 if (tny_device_is_online (device))
961 result = FALSE; /* Stop trying. */
963 /* Keep trying until connect_request_in_progress is FALSE. */
964 if (connect_request_in_progress)
965 result = TRUE; /* Keep trying */
967 printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
969 result = FALSE; /* Stop trying, now that a result should be available. */
973 if (result == FALSE) {
974 /* Allow the function that requested this idle callback to continue: */
975 UtilIdleData *data = (UtilIdleData*)user_data;
977 g_main_loop_quit (data->loop);
984 set_account_to_online (TnyAccount *account)
986 /* TODO: This is necessary to prevent a cancel of the password dialog
987 * from making a restart necessary to be asked the password again,
988 * but it causes a hang:
991 if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
992 /* Make sure that store accounts are online too,
993 * because tinymail sets accounts to offline if
994 * a password dialog is ever cancelled.
995 * We don't do this for transport accounts because
996 * a) They fundamentally need network access, so they can't really be offline.
997 * b) That might cause a transport connection to happen too early.
999 GError *error = NULL;
1000 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, &error);
1002 g_warning ("%s: tny_camel_account_set_online() returned a GError:\n %s\n",
1003 __FUNCTION__, error->message);
1004 g_error_free (error);
1010 gboolean modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1012 if (connect_request_in_progress)
1015 printf ("DEBUG: %s:\n", __FUNCTION__);
1016 TnyDevice *device = modest_runtime_get_device();
1018 if (tny_device_is_online (device)) {
1019 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1020 set_account_to_online (account);
1024 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1027 /* This blocks on the result: */
1028 UtilIdleData *data = g_slice_new0 (UtilIdleData);
1030 GMainContext *context = NULL; /* g_main_context_new (); */
1031 data->loop = g_main_loop_new (context, FALSE /* not running */);
1033 /* Cause the function to be run in an idle-handler, which is always
1034 * in the main thread:
1036 if (!connect_request_in_progress) {
1037 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1038 connect_request_in_progress = TRUE;
1039 g_idle_add (&on_idle_connect_and_wait, data);
1042 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1043 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1046 /* This main loop will run until the idle handler has stopped it: */
1047 printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1048 GDK_THREADS_LEAVE();
1049 g_main_loop_run (data->loop);
1050 GDK_THREADS_ENTER();
1051 printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1052 connect_request_in_progress = FALSE;
1053 printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1054 g_main_loop_unref (data->loop);
1055 /* g_main_context_unref (context); */
1057 g_slice_free (UtilIdleData, data);
1059 const gboolean result = tny_device_is_online (device);
1062 set_account_to_online (account);
1068 gboolean modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1070 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1071 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1072 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1073 /* This must be a maildir account, which does not require a connection: */
1078 return modest_platform_connect_and_wait (parent_window, account);
1081 gboolean modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1084 return TRUE; /* Maybe it is something local. */
1086 gboolean result = TRUE;
1087 if (TNY_IS_FOLDER (folder_store)) {
1088 /* Get the folder's parent account: */
1089 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1090 if (account != NULL) {
1091 result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1092 g_object_unref (account);
1094 } else if (TNY_IS_ACCOUNT (folder_store)) {
1095 /* Use the folder store as an account: */
1096 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1102 gboolean modest_platform_is_network_folderstore (TnyFolderStore *folder_store)
1104 TnyAccount *account = NULL;
1105 gboolean result = TRUE;
1107 g_return_val_if_fail(TNY_IS_FOLDER_STORE(folder_store), FALSE);
1109 if (TNY_IS_FOLDER (folder_store)) {
1110 /* Get the folder's parent account: */
1111 account = tny_folder_get_account(TNY_FOLDER(folder_store));
1112 } else if (TNY_IS_ACCOUNT (folder_store)) {
1113 account = TNY_ACCOUNT(folder_store);
1114 g_object_ref(account);
1117 if (account != NULL) {
1118 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1119 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1120 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1121 /* This must be a maildir account, which does
1122 * not require a connection: */
1126 g_object_unref (account);
1135 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1136 ModestSortDialogType type)
1138 GtkWidget *dialog = NULL;
1141 dialog = hildon_sort_dialog_new (parent_window);
1142 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1144 /* Fill sort keys */
1146 case MODEST_SORT_HEADERS:
1147 launch_sort_headers_dialog (parent_window,
1148 HILDON_SORT_DIALOG(dialog));
1153 gtk_widget_destroy (GTK_WIDGET (dialog));
1157 gboolean modest_platform_set_update_interval (guint minutes)
1159 ModestConf *conf = modest_runtime_get_conf ();
1163 cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1165 /* Delete any existing alarm,
1166 * because we will replace it: */
1168 /* TODO: What does the alarm_event_del() return value mean? */
1169 alarm_event_del(alarm_cookie);
1171 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1174 /* 0 means no updates: */
1179 /* Register alarm: */
1181 /* Set the interval in alarm_event_t structure: */
1182 alarm_event_t *event = g_new0(alarm_event_t, 1);
1183 event->alarm_time = minutes * 60; /* seconds */
1185 /* Set recurrence every few minutes: */
1186 event->recurrence = minutes;
1187 event->recurrence_count = -1; /* Means infinite */
1189 /* Specify what should happen when the alarm happens:
1190 * It should call this D-Bus method: */
1192 event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1193 event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1194 event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1195 event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1197 /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if
1198 * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1199 * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails
1200 * This is why we want to use the Alarm API instead of just g_timeout_add().
1201 * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1203 event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1205 alarm_cookie = alarm_event_add (event);
1208 alarm_event_free (event);
1210 /* Store the alarm ID in GConf, so we can remove it later:
1211 * This is apparently valid between application instances. */
1212 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1214 if (!alarm_cookie) {
1216 const alarm_error_t alarm_error = alarmd_get_error ();
1217 g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1219 /* Give people some clue: */
1220 /* The alarm API should have a function for this: */
1221 if (alarm_error == ALARMD_ERROR_DBUS) {
1222 g_debug (" ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1223 } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1224 g_debug (" ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1225 } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1226 g_debug (" ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1227 } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1228 g_debug (" ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1229 } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1230 g_debug (" ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1231 } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1232 g_debug (" ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1242 modest_platform_get_global_settings_dialog ()
1244 return modest_maemo_global_settings_dialog_new ();
1248 modest_platform_on_new_msg (void)
1250 #ifdef MODEST_HAVE_HILDON_NOTIFY
1251 HildonNotification *not;
1253 /* Create a new notification. TODO: per-mail data needed */
1254 not = hildon_notification_new ("TODO: (new email) Summary",
1255 "TODO: (new email) Description",
1256 "qgn_list_messagin_mail_unread",
1259 hildon_notification_add_dbus_action(not,
1262 MODEST_DBUS_SERVICE,
1265 MODEST_DBUS_METHOD_OPEN_DEFAULT_INBOX,
1268 /* Play sound SR-SND-18 */
1269 hildon_notification_set_sound (not, "/usr/share/sounds/ui-new_email.wav");
1270 notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "dialog-type", 4);
1272 /* Set the led pattern */
1273 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (not),
1275 "PatternCommunicationEmail");
1277 /* Notify. We need to do this in an idle because this function
1278 could be called from a thread */
1279 if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1280 g_error ("Failed to send notification");
1282 g_object_unref (not);
1283 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1288 modest_platform_show_help (GtkWindow *parent_window,
1289 const gchar *help_id)
1291 osso_return_t result;
1293 g_return_if_fail (help_id);
1294 g_return_if_fail (osso_context);
1297 #ifdef MODEST_HAVE_OSSO_HELP
1298 result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1300 result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1303 if (result != OSSO_OK) {
1305 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id);
1306 hildon_banner_show_information (GTK_WIDGET (parent_window),
1314 modest_platform_show_search_messages (GtkWindow *parent_window)
1316 osso_return_t result = OSSO_ERROR;
1318 result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1320 if (result != OSSO_OK) {
1321 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1326 modest_platform_show_addressbook (GtkWindow *parent_window)
1328 osso_return_t result = OSSO_ERROR;
1330 result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1332 if (result != OSSO_OK) {
1333 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1338 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1340 GtkWidget *widget = modest_folder_view_new (query);
1342 /* Show one account by default */
1343 modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1344 MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1347 /* Restore settings */
1348 modest_widget_memory_restore (modest_runtime_get_conf(),
1350 MODEST_CONF_FOLDER_VIEW_KEY);
1356 modest_platform_information_banner (GtkWidget *parent,
1357 const gchar *icon_name,
1360 hildon_banner_show_information (parent, icon_name, text);
1364 modest_platform_animation_banner (GtkWidget *parent,
1365 const gchar *animation_name,
1368 GtkWidget *inf_note = NULL;
1370 g_return_val_if_fail (text != NULL, NULL);
1372 inf_note = hildon_banner_show_animation (parent, animation_name, text);
1380 TnyAccount *account;
1383 } CheckAccountIdleData;
1385 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1388 on_timeout_check_account_is_online(gpointer user_data)
1390 printf ("DEBUG: %s:\n", __FUNCTION__);
1391 CheckAccountIdleData *data = (CheckAccountIdleData*)user_data;
1394 g_warning ("%s: data is NULL.\n", __FUNCTION__);
1397 if (!(data->account)) {
1398 g_warning ("%s: data->account is NULL.\n", __FUNCTION__);
1401 if (data && data->account) {
1402 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__, tny_account_get_connection_status (data->account));
1405 gboolean stop_trying = FALSE;
1406 if (data && data->account &&
1407 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1408 * after which the account is likely to be usable, or never likely to be usable soon: */
1409 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1411 data->is_online = TRUE;
1416 /* Give up if we have tried too many times: */
1417 if (data->count_tries >= NUMBER_OF_TRIES)
1422 /* Wait for another timeout: */
1423 ++(data->count_tries);
1428 /* Allow the function that requested this idle callback to continue: */
1430 g_main_loop_quit (data->loop);
1433 g_object_unref (data->account);
1435 return FALSE; /* Don't call this again. */
1437 return TRUE; /* Call this timeout callback again. */
1441 /* Return TRUE immediately if the account is already online,
1442 * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as
1443 * soon as the account is online, or FALSE if the account does
1444 * not become online in the NUMBER_OF_TRIES seconds.
1445 * This is useful when the D-Bus method was run immediately after
1446 * the application was started (when using D-Bus activation),
1447 * because the account usually takes a short time to go online.
1448 * The return value is maybe not very useful.
1451 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1453 g_return_val_if_fail (account, FALSE);
1455 printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1457 if (!tny_device_is_online (modest_runtime_get_device())) {
1458 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1462 /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1463 * so we avoid wait unnecessarily: */
1464 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1465 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1469 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1470 __FUNCTION__, tny_account_get_connection_status (account));
1472 /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED,
1473 * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that
1474 * we want to avoid. */
1475 if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1478 /* This blocks on the result: */
1479 CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1480 data->is_online = FALSE;
1481 data->account = account;
1482 g_object_ref (data->account);
1483 data->count_tries = 0;
1485 GMainContext *context = NULL; /* g_main_context_new (); */
1486 data->loop = g_main_loop_new (context, FALSE /* not running */);
1488 g_timeout_add (1000, &on_timeout_check_account_is_online, data);
1490 /* This main loop will run until the idle handler has stopped it: */
1491 g_main_loop_run (data->loop);
1493 g_main_loop_unref (data->loop);
1494 /* g_main_context_unref (context); */
1496 g_slice_free (CheckAccountIdleData, data);
1498 return data->is_online;