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>
57 #define HILDON_OSSO_URI_ACTION "uri-action"
58 #define URI_ACTION_COPY "copy:"
60 static osso_context_t *osso_context = NULL;
63 on_modest_conf_update_interval_changed (ModestConf* self,
65 ModestConfEvent event,
66 ModestConfNotificationId id,
69 if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
70 const guint update_interval_minutes =
71 modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
72 modest_platform_set_update_interval (update_interval_minutes);
77 modest_platform_init (int argc, char *argv[])
79 osso_hw_state_t hw_state = { 0 };
83 osso_initialize(PACKAGE,PACKAGE_VERSION,
86 g_printerr ("modest: failed to acquire osso context\n");
90 if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
91 g_printerr ("modest: could not get dbus connection\n");
96 /* Add a D-Bus handler to be used when the main osso-rpc
97 * D-Bus handler has not handled something.
98 * We use this for D-Bus methods that need to use more complex types
99 * than osso-rpc supports.
101 if (!dbus_connection_add_filter (con,
102 modest_dbus_req_filter,
106 g_printerr ("modest: Could not add D-Bus filter\n");
110 /* Register our simple D-Bus callbacks, via the osso API: */
111 osso_return_t result = osso_rpc_set_cb_f(osso_context,
115 modest_dbus_req_handler, NULL /* user_data */);
116 if (result != OSSO_OK) {
117 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
121 /* Add handler for Exit D-BUS messages.
122 * Not used because osso_application_set_exit_cb() is deprecated and obsolete:
123 result = osso_application_set_exit_cb(osso_context,
124 modest_dbus_exit_event_handler,
126 if (result != OSSO_OK) {
127 g_print("Error setting exit callback (%d)\n", result);
132 /* Register hardware event dbus callback: */
133 hw_state.shutdown_ind = TRUE;
134 osso_hw_set_event_cb(osso_context, NULL,/*&hw_state*/ modest_osso_cb_hw_state_handler, NULL);
136 /* Register osso auto-save callbacks: */
137 result = osso_application_set_autosave_cb (osso_context,
138 modest_on_osso_application_autosave, NULL /* user_data */);
139 if (result != OSSO_OK) {
140 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
145 /* Make sure that the update interval is changed whenever its gconf key
147 /* CAUTION: we're not using here the
148 modest_conf_listen_to_namespace because we know that there
149 are other parts of Modest listening for this namespace, so
150 we'll receive the notifications anyway. We basically do not
151 use it because there is no easy way to do the
152 modest_conf_forget_namespace */
153 ModestConf *conf = modest_runtime_get_conf ();
154 g_signal_connect (G_OBJECT(conf),
156 G_CALLBACK (on_modest_conf_update_interval_changed),
159 /* Get the initial update interval from gconf: */
160 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
161 MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
163 /* initialize the addressbook */
164 if (!osso_abook_init (&argc, &argv, osso_context)) {
165 g_printerr ("modest: failed to initialized addressbook\n");
173 modest_platform_get_new_device (void)
175 return TNY_DEVICE (tny_maemo_conic_device_new ());
180 guess_mime_type_from_name (const gchar* name)
184 const static gchar* octet_stream= "application/octet-stream";
185 const static gchar* mime_map[][2] = {
186 { "pdf", "application/pdf"},
187 { "doc", "application/msword"},
188 { "xls", "application/excel"},
189 { "png", "image/png" },
190 { "gif", "image/gif" },
191 { "jpg", "image/jpeg"},
192 { "jpeg", "image/jpeg"},
193 { "mp3", "audio/mp3" }
199 ext = g_strrstr (name, ".");
203 for (i = 0; i != G_N_ELEMENTS(mime_map); ++i) {
204 if (!g_ascii_strcasecmp (mime_map[i][0], ext + 1)) /* +1: ignore '.'*/
205 return mime_map[i][1];
212 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
213 gchar **effective_mime_type)
215 GString *mime_str = NULL;
216 gchar *icon_name = NULL;
217 gchar **icons, **cursor;
219 if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream"))
220 mime_str = g_string_new (guess_mime_type_from_name(name));
222 mime_str = g_string_new (mime_type);
223 g_string_ascii_down (mime_str);
226 #ifdef MODEST_HAVE_OSSO_MIME
227 icons = osso_mime_get_icon_names (mime_str->str, NULL);
229 icons = hildon_mime_get_icon_names (mime_str->str, NULL);
230 #endif /*MODEST_HAVE_OSSO_MIME*/
231 for (cursor = icons; cursor; ++cursor) {
232 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
233 !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
234 icon_name = g_strdup ("qgn_list_messagin");
236 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
237 icon_name = g_strdup (*cursor);
243 if (effective_mime_type)
244 *effective_mime_type = g_string_free (mime_str, FALSE);
246 g_string_free (mime_str, TRUE);
254 #ifdef MODEST_HAVE_OSSO_MIME
256 modest_platform_activate_uri (const gchar *uri)
258 OssoURIAction *action;
259 gboolean result = FALSE;
260 GSList *actions, *iter = NULL;
263 g_return_val_if_fail (uri, FALSE);
267 /* the default action should be email */
268 scheme = osso_uri_get_scheme_from_uri (uri, NULL);
269 actions = osso_uri_get_actions (scheme, NULL);
271 for (iter = actions; iter; iter = g_slist_next (iter)) {
272 action = (OssoURIAction*) iter->data;
273 if (action && strcmp (osso_uri_action_get_name (action), "uri_link_compose_email") == 0) {
275 result = osso_uri_open (uri, action, &err);
276 if (!result && err) {
277 g_printerr ("modest: modest_platform_activate_uri : %s",
278 err->message ? err->message : "unknown error");
285 /* if we could open it with email, try something else */
287 result = osso_uri_open (uri, NULL, NULL);
291 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
295 #else /* !MODEST_HAVE_OSSO_MIME*/
298 modest_platform_activate_uri (const gchar *uri)
300 HildonURIAction *action;
301 gboolean result = FALSE;
302 GSList *actions, *iter = NULL;
305 g_return_val_if_fail (uri, FALSE);
309 scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
310 actions = hildon_uri_get_actions (scheme, NULL);
312 for (iter = actions; iter; iter = g_slist_next (iter)) {
313 action = (HildonURIAction*) iter->data;
314 if (action && strcmp (hildon_uri_action_get_service (action), "com.nokia.modest") == 0) {
316 result = hildon_uri_open (uri, action, &err);
317 if (!result && err) {
318 g_printerr ("modest: modest_platform_activate_uri : %s",
319 err->message ? err->message : "unknown error");
326 /* if we could open it with email, try something else */
328 result = hildon_uri_open (uri, NULL, NULL);
331 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
337 #endif /* MODEST_HAVE_OSSO_MIME*/
340 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
344 gchar *uri_path = NULL;
345 GString *mime_str = NULL;
347 if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream"))
348 mime_str = g_string_new (guess_mime_type_from_name(path));
350 mime_str = g_string_new (mime_type);
351 g_string_ascii_down (mime_str);
354 uri_path = g_strconcat ("file://", path, NULL);
356 con = osso_get_dbus_connection (osso_context);
357 #ifdef MODEST_HAVE_OSSO_MIME
358 result = osso_mime_open_file_with_mime_type (con, uri_path, mime_str->str);
360 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_str->str);
361 #endif /*MODEST_HAVE_OSSO_MIME*/
362 g_string_free (mime_str, TRUE);
365 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
372 } ModestPlatformPopupInfo;
375 delete_uri_popup (GtkWidget *menu,
379 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
381 g_free (popup_info->uri);
382 #ifdef MODEST_HAVE_OSSO_MIME
383 osso_uri_free_actions (popup_info->actions);
385 hildon_uri_free_actions (popup_info->actions);
386 #endif /*MODEST_HAVE_OSSO_MIME*/
391 activate_uri_popup_item (GtkMenuItem *menu_item,
395 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
396 const gchar* action_name;
398 action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
400 g_printerr ("modest: no action name defined\n");
404 /* special handling for the copy menu item -- copy the uri to the clipboard */
405 /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
406 if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
407 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
408 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
410 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
411 action_name += strlen ("mailto:");
413 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
414 return; /* we're done */
417 /* now, the real uri-actions... */
418 for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
419 #ifdef MODEST_HAVE_OSSO_MIME
420 OssoURIAction *action = (OssoURIAction *) node->data;
421 if (strcmp (action_name, osso_uri_action_get_name (action))==0) {
422 osso_uri_open (popup_info->uri, action, NULL);
426 HildonURIAction *action = (HildonURIAction *) node->data;
427 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
428 hildon_uri_open (popup_info->uri, action, NULL);
431 #endif /*MODEST_HAVE_OSSO_MIME*/
436 modest_platform_show_uri_popup (const gchar *uri)
439 GSList *actions_list;
444 #ifdef MODEST_HAVE_OSSO_MIME
445 scheme = osso_uri_get_scheme_from_uri (uri, NULL);
446 actions_list = osso_uri_get_actions (scheme, NULL);
448 scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
449 actions_list = hildon_uri_get_actions (scheme, NULL);
450 #endif /* MODEST_HAVE_OSSO_MIME */
451 if (actions_list != NULL) {
453 GtkWidget *menu = gtk_menu_new ();
454 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
456 popup_info->actions = actions_list;
457 popup_info->uri = g_strdup (uri);
459 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
460 GtkWidget *menu_item;
461 const gchar *action_name;
462 const gchar *translation_domain;
463 #ifdef MODEST_HAVE_OSSO_MIME
464 OssoURIAction *action = (OssoURIAction *) node->data;
465 action_name = osso_uri_action_get_name (action);
466 translation_domain = osso_uri_action_get_translation_domain (action);
467 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain,action_name));
468 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);
469 /* hack, we add it as a gobject property*/
470 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
473 if (osso_uri_is_default_action (action, NULL)) {
474 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
476 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
479 HildonURIAction *action = (HildonURIAction *) node->data;
480 action_name = hildon_uri_action_get_name (action);
481 translation_domain = hildon_uri_action_get_translation_domain (action);
482 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
483 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name); /* hack */
484 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
487 if (hildon_uri_is_default_action (action, NULL)) {
488 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
490 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
492 #endif /*MODEST_HAVE_OSSO_MIME*/
493 gtk_widget_show (menu_item);
496 /* always add the copy item */
497 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri", "uri_link_copy_link_location"));
498 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
499 g_strconcat (URI_ACTION_COPY, uri, NULL),
501 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
502 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
503 gtk_widget_show (menu_item);
506 /* and what to do when the link is deleted */
507 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
508 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
511 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
520 modest_platform_get_icon (const gchar *name)
523 GdkPixbuf* pixbuf = NULL;
524 GtkIconTheme *current_theme = NULL;
526 g_return_val_if_fail (name, NULL);
528 #if 0 /* do we still need this? */
529 if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
530 pixbuf = gdk_pixbuf_new_from_file (name, &err);
532 g_printerr ("modest: error loading icon '%s': %s\n",
540 current_theme = gtk_icon_theme_get_default ();
541 pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
542 GTK_ICON_LOOKUP_NO_SVG,
545 g_printerr ("modest: error loading theme icon '%s': %s\n",
553 modest_platform_get_app_name (void)
555 return _("mcen_ap_name");
559 entry_insert_text (GtkEditable *editable,
568 chars = gtk_editable_get_chars (editable, 0, -1);
569 chars_length = g_utf8_strlen (chars, -1);
571 /* Show WID-INF036 */
572 if (chars_length >= 20) {
573 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
574 _CS("ckdg_ib_maximum_characters_reached"));
576 gboolean is_valid = FALSE;
580 else if (strlen(text) == 0 && g_str_has_prefix (chars, " "))
583 is_valid = !g_str_has_prefix(text, " ");
585 /* A blank space is not valid as first character */
587 GtkWidget *ok_button;
591 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (data)->action_area));
592 ok_button = GTK_WIDGET (buttons->next->data);
593 gtk_widget_set_sensitive (ok_button, TRUE);
594 g_list_free (buttons);
598 /* Write the text in the entry */
599 g_signal_handlers_block_by_func (editable,
600 (gpointer) entry_insert_text, data);
601 gtk_editable_insert_text (editable, text, length, position);
602 g_signal_handlers_unblock_by_func (editable,
603 (gpointer) entry_insert_text, data);
605 /* Do not allow further processing */
606 g_signal_stop_emission_by_name (editable, "insert_text");
610 entry_changed (GtkEditable *editable,
614 GtkWidget *ok_button;
617 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
618 ok_button = GTK_WIDGET (buttons->next->data);
620 chars = gtk_editable_get_chars (editable, 0, -1);
621 g_return_if_fail (chars != NULL);
624 /* Dimm OK button. Do not allow also the "/" */
625 if (strlen (chars) == 0 || strchr (chars, '/'))
626 gtk_widget_set_sensitive (ok_button, FALSE);
628 else if (g_utf8_strlen (chars,-1) >= 21)
629 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
630 _CS("ckdg_ib_maximum_characters_reached"));
631 else /* explicitely enable it, because with a previous entry_changed, it might have been turned off */
632 gtk_widget_set_sensitive (ok_button, TRUE);
635 g_list_free (buttons);
640 launch_sort_headers_dialog (GtkWindow *parent_window,
641 HildonSortDialog *dialog)
643 ModestHeaderView *header_view = NULL;
645 GtkSortType sort_type;
647 gint default_key = 0;
649 gboolean outgoing = FALSE;
650 gint current_sort_colid = -1;
651 GtkSortType current_sort_type;
652 gint attachments_sort_id;
653 gint priority_sort_id;
654 GtkTreeSortable *sortable;
656 /* Get header window */
657 if (MODEST_IS_MAIN_WINDOW (parent_window)) {
658 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
659 MODEST_WIDGET_TYPE_HEADER_VIEW));
661 if (!header_view) return;
663 /* Add sorting keys */
664 cols = modest_header_view_get_columns (header_view);
665 if (cols == NULL) return;
666 int sort_model_ids[6];
670 outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
671 MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
673 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
675 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
676 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
678 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
679 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
682 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
684 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
685 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
687 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
688 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
690 default_key = sort_key;
692 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
693 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
695 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
697 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
699 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
700 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
701 sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
702 attachments_sort_id = sort_key;
704 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
705 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
706 sort_ids[sort_key] = 0;
708 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
709 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
710 sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
711 priority_sort_id = sort_key;
713 sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
715 if (!gtk_tree_sortable_get_sort_column_id (sortable,
716 ¤t_sort_colid, ¤t_sort_type)) {
717 hildon_sort_dialog_set_sort_key (dialog, default_key);
718 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
720 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
721 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
722 gpointer flags_sort_type_pointer;
723 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
724 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
725 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
727 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
729 gint current_sort_keyid = 0;
730 while (current_sort_keyid < 6) {
731 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
734 current_sort_keyid++;
736 hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
740 result = gtk_dialog_run (GTK_DIALOG (dialog));
741 if (result == GTK_RESPONSE_OK) {
742 sort_key = hildon_sort_dialog_get_sort_key (dialog);
743 sort_type = hildon_sort_dialog_get_sort_order (dialog);
744 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
745 g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
746 GINT_TO_POINTER (sort_ids[sort_key]));
747 /* This is a hack to make it resort rows always when flag fields are
748 * selected. If we do not do this, changing sort field from priority to
749 * attachments does not work */
750 modest_header_view_sort_by_column_id (header_view, 0, sort_type);
752 gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data),
753 sort_model_ids[sort_key]);
756 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
757 gtk_tree_sortable_sort_column_changed (sortable);
760 modest_widget_memory_save (modest_runtime_get_conf (),
761 G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
763 while (gtk_events_pending ())
764 gtk_main_iteration ();
771 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
772 const gchar *dialog_title,
773 const gchar *label_text,
774 const gchar *suggested_name,
777 GtkWidget *accept_btn = NULL;
778 GtkWidget *dialog, *entry, *label, *hbox;
779 GList *buttons = NULL;
782 /* Ask the user for the folder name */
783 dialog = gtk_dialog_new_with_buttons (dialog_title,
785 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
792 /* Add accept button (with unsensitive handler) */
793 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
794 accept_btn = GTK_WIDGET (buttons->next->data);
795 /* Create label and entry */
796 label = gtk_label_new (label_text);
797 /* TODO: check that the suggested name does not exist */
798 /* We set 21 as maximum because we want to show WID-INF036
799 when the user inputs more that 20 */
800 entry = gtk_entry_new_with_max_length (21);
802 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
804 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
805 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
807 /* Track entry changes */
808 g_signal_connect (entry,
810 G_CALLBACK (entry_insert_text),
812 g_signal_connect (entry,
814 G_CALLBACK (entry_changed),
817 /* Create the hbox */
818 hbox = gtk_hbox_new (FALSE, 12);
819 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
820 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
822 /* Add hbox to dialog */
823 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox),
824 hbox, FALSE, FALSE, 0);
826 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
828 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
829 result = gtk_dialog_run (GTK_DIALOG(dialog));
830 if (result == GTK_RESPONSE_ACCEPT)
831 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
833 gtk_widget_destroy (dialog);
835 while (gtk_events_pending ())
836 gtk_main_iteration ();
842 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
843 TnyFolderStore *parent_folder,
844 gchar *suggested_name,
847 gchar *real_suggested_name = NULL;
850 if(suggested_name == NULL)
852 const gchar *default_name = _("mcen_ia_default_folder_name");
856 for(i = 0; i < 100; ++ i)
858 TnyList *list = tny_simple_list_new ();
859 TnyFolderStoreQuery *query = tny_folder_store_query_new ();
862 sprintf(num_str, "%.2u", i);
865 real_suggested_name = g_strdup (default_name);
867 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
870 tny_folder_store_query_add_item (query, real_suggested_name,
871 TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
873 tny_folder_store_get_folders (parent_folder, list, query, NULL);
875 length = tny_list_get_length (list);
876 g_object_unref (query);
877 g_object_unref (list);
882 g_free (real_suggested_name);
885 /* Didn't find a free number */
887 real_suggested_name = g_strdup (default_name);
891 real_suggested_name = suggested_name;
894 result = modest_platform_run_folder_name_dialog (parent_window,
895 _("mcen_ti_new_folder"),
896 _("mcen_fi_new_folder_name"),
899 if (suggested_name == NULL)
900 g_free(real_suggested_name);
906 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
907 TnyFolderStore *parent_folder,
908 const gchar *suggested_name,
911 return modest_platform_run_folder_name_dialog (parent_window,
912 _("New folder name"),
913 _("Enter new folder name:"),
919 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
920 const gchar *message)
925 dialog = hildon_note_new_confirmation (parent_window, message);
926 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
928 response = gtk_dialog_run (GTK_DIALOG (dialog));
930 gtk_widget_destroy (GTK_WIDGET (dialog));
932 while (gtk_events_pending ())
933 gtk_main_iteration ();
939 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
940 const gchar *message)
945 dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
946 _("mcen_bd_yes"), GTK_RESPONSE_YES,
947 _("mcen_bd_no"), GTK_RESPONSE_NO,
949 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
951 response = gtk_dialog_run (GTK_DIALOG (dialog));
953 gtk_widget_destroy (GTK_WIDGET (dialog));
955 while (gtk_events_pending ())
956 gtk_main_iteration ();
962 modest_platform_run_information_dialog (GtkWindow *parent_window,
963 const gchar *message)
967 dialog = hildon_note_new_information (parent_window, message);
969 g_signal_connect_swapped (dialog,
971 G_CALLBACK (gtk_widget_destroy),
974 gtk_widget_show_all (dialog);
985 on_idle_connect_and_wait(gpointer user_data)
987 printf ("DEBUG: %s:\n", __FUNCTION__);
988 TnyDevice *device = modest_runtime_get_device();
989 if (!tny_device_is_online (device)) {
991 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
995 /* Allow the function that requested this idle callback to continue: */
996 UtilIdleData *data = (UtilIdleData*)user_data;
998 g_main_loop_quit (data->loop);
1000 return FALSE; /* Don't call this again. */
1003 static gboolean connect_request_in_progress = FALSE;
1005 /* This callback is used when connect_and_wait() is already queued as an idle callback.
1006 * This can happen because the gtk_dialog_run() for the connection dialog
1007 * (at least in the fake scratchbox version) allows idle handlers to keep running.
1010 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
1012 gboolean result = FALSE;
1013 TnyDevice *device = modest_runtime_get_device();
1014 if (tny_device_is_online (device))
1015 result = FALSE; /* Stop trying. */
1017 /* Keep trying until connect_request_in_progress is FALSE. */
1018 if (connect_request_in_progress)
1019 result = TRUE; /* Keep trying */
1021 printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
1023 result = FALSE; /* Stop trying, now that a result should be available. */
1027 if (result == FALSE) {
1028 /* Allow the function that requested this idle callback to continue: */
1029 UtilIdleData *data = (UtilIdleData*)user_data;
1031 g_main_loop_quit (data->loop);
1038 set_account_to_online (TnyAccount *account)
1040 /* TODO: This is necessary to prevent a cancel of the password dialog
1041 * from making a restart necessary to be asked the password again,
1042 * but it causes a hang:
1045 if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
1046 /* Make sure that store accounts are online too,
1047 * because tinymail sets accounts to offline if
1048 * a password dialog is ever cancelled.
1049 * We don't do this for transport accounts because
1050 * a) They fundamentally need network access, so they can't really be offline.
1051 * b) That might cause a transport connection to happen too early.
1053 GError *error = NULL;
1054 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, &error);
1056 g_warning ("%s: tny_camel_account_set_online() returned a GError:\n %s\n",
1057 __FUNCTION__, error->message);
1058 g_error_free (error);
1064 gboolean modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1066 if (connect_request_in_progress)
1069 printf ("DEBUG: %s:\n", __FUNCTION__);
1070 TnyDevice *device = modest_runtime_get_device();
1072 if (tny_device_is_online (device)) {
1073 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1074 set_account_to_online (account);
1078 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1081 /* This blocks on the result: */
1082 UtilIdleData *data = g_slice_new0 (UtilIdleData);
1084 GMainContext *context = NULL; /* g_main_context_new (); */
1085 data->loop = g_main_loop_new (context, FALSE /* not running */);
1087 /* Cause the function to be run in an idle-handler, which is always
1088 * in the main thread:
1090 if (!connect_request_in_progress) {
1091 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1092 connect_request_in_progress = TRUE;
1093 g_idle_add (&on_idle_connect_and_wait, data);
1096 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1097 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1100 /* This main loop will run until the idle handler has stopped it: */
1101 printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1102 GDK_THREADS_LEAVE();
1103 g_main_loop_run (data->loop);
1104 GDK_THREADS_ENTER();
1105 printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1106 connect_request_in_progress = FALSE;
1107 printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1108 g_main_loop_unref (data->loop);
1109 /* g_main_context_unref (context); */
1111 g_slice_free (UtilIdleData, data);
1113 gboolean result = tny_device_is_online (device);
1116 set_account_to_online (account);
1121 gboolean modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1123 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1124 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1125 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1126 /* This must be a maildir account, which does not require a connection: */
1131 return modest_platform_connect_and_wait (parent_window, account);
1134 gboolean modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1137 return TRUE; /* Maybe it is something local. */
1139 gboolean result = TRUE;
1140 if (TNY_IS_FOLDER (folder_store)) {
1141 /* Get the folder's parent account: */
1142 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1143 if (account != NULL) {
1144 result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1145 g_object_unref (account);
1147 } else if (TNY_IS_ACCOUNT (folder_store)) {
1148 /* Use the folder store as an account: */
1149 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1156 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1157 ModestSortDialogType type)
1159 GtkWidget *dialog = NULL;
1162 dialog = hildon_sort_dialog_new (parent_window);
1163 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1165 /* Fill sort keys */
1167 case MODEST_SORT_HEADERS:
1168 launch_sort_headers_dialog (parent_window,
1169 HILDON_SORT_DIALOG(dialog));
1174 gtk_widget_destroy (GTK_WIDGET (dialog));
1178 gboolean modest_platform_set_update_interval (guint minutes)
1180 ModestConf *conf = modest_runtime_get_conf ();
1184 cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1186 /* Delete any existing alarm,
1187 * because we will replace it: */
1189 /* TODO: What does the alarm_event_del() return value mean? */
1190 alarm_event_del(alarm_cookie);
1192 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1195 /* 0 means no updates: */
1200 /* Register alarm: */
1202 /* Set the interval in alarm_event_t structure: */
1203 alarm_event_t *event = g_new0(alarm_event_t, 1);
1204 event->alarm_time = minutes * 60; /* seconds */
1206 /* Set recurrence every few minutes: */
1207 event->recurrence = minutes;
1208 event->recurrence_count = -1; /* Means infinite */
1210 /* Specify what should happen when the alarm happens:
1211 * It should call this D-Bus method: */
1213 /* Note: I am surpised that alarmd can't just use the modest.service file
1214 * for this. murrayc. */
1215 event->dbus_path = g_strdup(PREFIX "/bin/modest");
1217 event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1218 event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1219 event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1221 /* Otherwise, a dialog will be shown if exect_name or dbus_path is NULL,
1222 even though we have specified no dialog text: */
1223 event->flags = ALARM_EVENT_NO_DIALOG;
1225 alarm_cookie = alarm_event_add (event);
1228 alarm_event_free (event);
1230 /* Store the alarm ID in GConf, so we can remove it later:
1231 * This is apparently valid between application instances. */
1232 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1234 if (!alarm_cookie) {
1236 const alarm_error_t alarm_error = alarmd_get_error ();
1237 printf ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1239 /* Give people some clue: */
1240 /* The alarm API should have a function for this: */
1241 if (alarm_error == ALARMD_ERROR_DBUS) {
1242 printf (" ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1243 } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1244 printf (" ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1245 } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1246 printf (" ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1247 } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1248 printf (" ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1249 } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1250 printf (" ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1260 modest_platform_get_global_settings_dialog ()
1262 return modest_maemo_global_settings_dialog_new ();
1266 modest_platform_on_new_msg (void)
1268 #ifdef MODEST_HAVE_HILDON_NOTIFY
1269 HildonNotification *not;
1271 /* Create a new notification. FIXME put the right values, need
1273 not = hildon_notification_new ("TODO: (new email) Summary",
1274 "TODO: (new email) Description",
1275 "qgn_contact_group_chat_invitation",
1276 "system.note.dialog");
1278 /* Play sound SR-SND-18. TODO: play the right file */
1279 /* TODO: Where is this declared? hildon_notification_set_sound (not, "/usr/share/sounds/ui-new_email.wav"); */
1281 /* Set the led pattern */
1282 notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "led-pattern", 3);
1284 /* Notify. We need to do this in an idle because this function
1285 could be called from a thread */
1286 if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1287 g_error ("Failed to send notification");
1289 g_object_unref (not);
1290 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1295 modest_platform_show_help (GtkWindow *parent_window,
1296 const gchar *help_id)
1298 osso_return_t result;
1300 g_return_if_fail (help_id);
1301 g_return_if_fail (osso_context);
1304 #ifdef MODEST_HAVE_OSSO_HELP
1305 result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1307 result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1310 if (result != OSSO_OK) {
1312 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id);
1313 hildon_banner_show_information (GTK_WIDGET (parent_window),
1321 modest_platform_show_search_messages (GtkWindow *parent_window)
1323 osso_return_t result = OSSO_ERROR;
1325 result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1327 if (result != OSSO_OK) {
1328 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1333 modest_platform_show_addressbook (GtkWindow *parent_window)
1335 osso_return_t result = OSSO_ERROR;
1337 result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1339 if (result != OSSO_OK) {
1340 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1345 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1347 GtkWidget *widget = modest_folder_view_new (query);
1349 /* Show one account by default */
1350 modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1351 MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1354 /* Restore settings */
1355 modest_widget_memory_restore (modest_runtime_get_conf(),
1357 MODEST_CONF_FOLDER_VIEW_KEY);
1363 modest_platform_information_banner (GtkWidget *parent,
1364 const gchar *icon_name,
1367 hildon_banner_show_information (parent, icon_name, text);
1371 modest_platform_animation_banner (GtkWidget *parent,
1372 const gchar *animation_name,
1375 GtkWidget *inf_note = NULL;
1377 g_return_val_if_fail (text != NULL, NULL);
1379 inf_note = hildon_banner_show_animation (parent, animation_name, text);