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 /* strlen == 0 is not really an error; it just
529 * means the icon is not available
531 if (!name || strlen(name) == 0)
534 #if 0 /* do we still need this? */
535 if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
536 pixbuf = gdk_pixbuf_new_from_file (name, &err);
538 g_printerr ("modest: error loading icon '%s': %s\n",
546 current_theme = gtk_icon_theme_get_default ();
547 pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
548 GTK_ICON_LOOKUP_NO_SVG,
551 g_printerr ("modest: error loading theme icon '%s': %s\n",
559 modest_platform_get_app_name (void)
561 return _("mcen_ap_name");
565 entry_insert_text (GtkEditable *editable,
574 chars = gtk_editable_get_chars (editable, 0, -1);
575 chars_length = g_utf8_strlen (chars, -1);
577 /* Show WID-INF036 */
578 if (chars_length >= 20) {
579 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
580 _CS("ckdg_ib_maximum_characters_reached"));
582 GtkWidget *ok_button;
585 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (data)->action_area));
586 ok_button = GTK_WIDGET (buttons->next->data);
588 gtk_widget_set_sensitive (ok_button,
589 modest_text_utils_validate_folder_name (chars));
590 g_list_free (buttons);
592 /* Write the text in the entry */
593 g_signal_handlers_block_by_func (editable,
594 (gpointer) entry_insert_text, data);
595 gtk_editable_insert_text (editable, text, length, position);
596 g_signal_handlers_unblock_by_func (editable,
597 (gpointer) entry_insert_text, data);
599 /* Do not allow further processing */
600 g_signal_stop_emission_by_name (editable, "insert_text");
604 entry_changed (GtkEditable *editable,
608 GtkWidget *ok_button;
611 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
612 ok_button = GTK_WIDGET (buttons->next->data);
614 chars = gtk_editable_get_chars (editable, 0, -1);
615 g_return_if_fail (chars != NULL);
618 if (g_utf8_strlen (chars,-1) >= 21)
619 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
620 _CS("ckdg_ib_maximum_characters_reached"));
622 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
625 g_list_free (buttons);
630 launch_sort_headers_dialog (GtkWindow *parent_window,
631 HildonSortDialog *dialog)
633 ModestHeaderView *header_view = NULL;
635 GtkSortType sort_type;
637 gint default_key = 0;
639 gboolean outgoing = FALSE;
640 gint current_sort_colid = -1;
641 GtkSortType current_sort_type;
642 gint attachments_sort_id;
643 gint priority_sort_id;
644 GtkTreeSortable *sortable;
646 /* Get header window */
647 if (MODEST_IS_MAIN_WINDOW (parent_window)) {
648 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
649 MODEST_WIDGET_TYPE_HEADER_VIEW));
651 if (!header_view) return;
653 /* Add sorting keys */
654 cols = modest_header_view_get_columns (header_view);
655 if (cols == NULL) return;
656 int sort_model_ids[6];
660 outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
661 MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
663 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
665 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
666 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
668 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
669 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
672 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
674 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
675 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
677 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
678 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
680 default_key = sort_key;
682 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
683 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
685 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
687 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
689 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
690 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
691 sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
692 attachments_sort_id = sort_key;
694 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
695 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
696 sort_ids[sort_key] = 0;
698 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
699 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
700 sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
701 priority_sort_id = sort_key;
703 sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
705 if (!gtk_tree_sortable_get_sort_column_id (sortable,
706 ¤t_sort_colid, ¤t_sort_type)) {
707 hildon_sort_dialog_set_sort_key (dialog, default_key);
708 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
710 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
711 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
712 gpointer flags_sort_type_pointer;
713 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
714 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
715 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
717 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
719 gint current_sort_keyid = 0;
720 while (current_sort_keyid < 6) {
721 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
724 current_sort_keyid++;
726 hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
730 result = gtk_dialog_run (GTK_DIALOG (dialog));
731 if (result == GTK_RESPONSE_OK) {
732 sort_key = hildon_sort_dialog_get_sort_key (dialog);
733 sort_type = hildon_sort_dialog_get_sort_order (dialog);
734 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
735 g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
736 GINT_TO_POINTER (sort_ids[sort_key]));
737 /* This is a hack to make it resort rows always when flag fields are
738 * selected. If we do not do this, changing sort field from priority to
739 * attachments does not work */
740 modest_header_view_sort_by_column_id (header_view, 0, sort_type);
742 gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data),
743 sort_model_ids[sort_key]);
746 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
747 gtk_tree_sortable_sort_column_changed (sortable);
750 modest_widget_memory_save (modest_runtime_get_conf (),
751 G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
753 /* while (gtk_events_pending ()) */
754 /* gtk_main_iteration (); */
761 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
762 const gchar *dialog_title,
763 const gchar *label_text,
764 const gchar *suggested_name,
767 GtkWidget *accept_btn = NULL;
768 GtkWidget *dialog, *entry, *label, *hbox;
769 GList *buttons = NULL;
772 /* Ask the user for the folder name */
773 dialog = gtk_dialog_new_with_buttons (dialog_title,
775 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
782 /* Add accept button (with unsensitive handler) */
783 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
784 accept_btn = GTK_WIDGET (buttons->next->data);
785 /* Create label and entry */
786 label = gtk_label_new (label_text);
787 /* TODO: check that the suggested name does not exist */
788 /* We set 21 as maximum because we want to show WID-INF036
789 when the user inputs more that 20 */
790 entry = gtk_entry_new_with_max_length (21);
792 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
794 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
795 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
797 /* Track entry changes */
798 g_signal_connect (entry,
800 G_CALLBACK (entry_insert_text),
802 g_signal_connect (entry,
804 G_CALLBACK (entry_changed),
807 /* Create the hbox */
808 hbox = gtk_hbox_new (FALSE, 12);
809 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
810 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
812 /* Add hbox to dialog */
813 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox),
814 hbox, FALSE, FALSE, 0);
816 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
818 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
819 result = gtk_dialog_run (GTK_DIALOG(dialog));
820 if (result == GTK_RESPONSE_ACCEPT)
821 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
823 gtk_widget_destroy (dialog);
825 while (gtk_events_pending ())
826 gtk_main_iteration ();
832 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
833 TnyFolderStore *parent_folder,
834 gchar *suggested_name,
837 gchar *real_suggested_name = NULL;
840 if(suggested_name == NULL)
842 const gchar *default_name = _("mcen_ia_default_folder_name");
846 for(i = 0; i < 100; ++ i)
848 TnyList *list = tny_simple_list_new ();
849 TnyFolderStoreQuery *query = tny_folder_store_query_new ();
852 sprintf(num_str, "%.2u", i);
855 real_suggested_name = g_strdup (default_name);
857 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
860 tny_folder_store_query_add_item (query, real_suggested_name,
861 TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
863 tny_folder_store_get_folders (parent_folder, list, query, NULL);
865 length = tny_list_get_length (list);
866 g_object_unref (query);
867 g_object_unref (list);
872 g_free (real_suggested_name);
875 /* Didn't find a free number */
877 real_suggested_name = g_strdup (default_name);
881 real_suggested_name = suggested_name;
884 result = modest_platform_run_folder_name_dialog (parent_window,
885 _("mcen_ti_new_folder"),
886 _("mcen_fi_new_folder_name"),
889 if (suggested_name == NULL)
890 g_free(real_suggested_name);
896 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
897 TnyFolderStore *parent_folder,
898 const gchar *suggested_name,
901 return modest_platform_run_folder_name_dialog (parent_window,
902 _("New folder name"),
903 _("Enter new folder name:"),
909 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
910 const gchar *message)
915 dialog = hildon_note_new_confirmation (parent_window, message);
916 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
918 response = gtk_dialog_run (GTK_DIALOG (dialog));
920 gtk_widget_destroy (GTK_WIDGET (dialog));
922 while (gtk_events_pending ())
923 gtk_main_iteration ();
929 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
930 const gchar *message)
935 dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
936 _("mcen_bd_yes"), GTK_RESPONSE_YES,
937 _("mcen_bd_no"), GTK_RESPONSE_NO,
939 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
941 response = gtk_dialog_run (GTK_DIALOG (dialog));
943 gtk_widget_destroy (GTK_WIDGET (dialog));
945 while (gtk_events_pending ())
946 gtk_main_iteration ();
952 modest_platform_run_information_dialog (GtkWindow *parent_window,
953 const gchar *message)
957 dialog = hildon_note_new_information (parent_window, message);
959 g_signal_connect_swapped (dialog,
961 G_CALLBACK (gtk_widget_destroy),
964 gtk_widget_show_all (dialog);
975 on_idle_connect_and_wait(gpointer user_data)
977 printf ("DEBUG: %s:\n", __FUNCTION__);
978 TnyDevice *device = modest_runtime_get_device();
979 if (!tny_device_is_online (device)) {
981 /* This is a GDK lock because we are an idle callback and
982 * tny_maemo_conic_device_connect can contain Gtk+ code */
984 gdk_threads_enter(); /* CHECKED */
985 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
986 gdk_threads_leave(); /* CHECKED */
989 /* Allow the function that requested this idle callback to continue: */
990 UtilIdleData *data = (UtilIdleData*)user_data;
992 g_main_loop_quit (data->loop);
994 return FALSE; /* Don't call this again. */
997 static gboolean connect_request_in_progress = FALSE;
999 /* This callback is used when connect_and_wait() is already queued as an idle callback.
1000 * This can happen because the gtk_dialog_run() for the connection dialog
1001 * (at least in the fake scratchbox version) allows idle handlers to keep running.
1004 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
1006 gboolean result = FALSE;
1007 TnyDevice *device = modest_runtime_get_device();
1008 if (tny_device_is_online (device))
1009 result = FALSE; /* Stop trying. */
1011 /* Keep trying until connect_request_in_progress is FALSE. */
1012 if (connect_request_in_progress)
1013 result = TRUE; /* Keep trying */
1015 printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
1017 result = FALSE; /* Stop trying, now that a result should be available. */
1021 if (result == FALSE) {
1022 /* Allow the function that requested this idle callback to continue: */
1023 UtilIdleData *data = (UtilIdleData*)user_data;
1025 g_main_loop_quit (data->loop);
1032 set_account_to_online (TnyAccount *account)
1034 /* TODO: This is necessary to prevent a cancel of the password dialog
1035 * from making a restart necessary to be asked the password again,
1036 * but it causes a hang:
1039 if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
1040 /* Make sure that store accounts are online too,
1041 * because tinymail sets accounts to offline if
1042 * a password dialog is ever cancelled.
1043 * We don't do this for transport accounts because
1044 * a) They fundamentally need network access, so they can't really be offline.
1045 * b) That might cause a transport connection to happen too early.
1047 GError *error = NULL;
1048 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, &error);
1050 g_warning ("%s: tny_camel_account_set_online() returned a GError:\n %s\n",
1051 __FUNCTION__, error->message);
1052 g_error_free (error);
1058 gboolean modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1060 if (connect_request_in_progress)
1063 printf ("DEBUG: %s:\n", __FUNCTION__);
1064 TnyDevice *device = modest_runtime_get_device();
1066 if (tny_device_is_online (device)) {
1067 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1068 set_account_to_online (account);
1072 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1075 /* This blocks on the result: */
1076 UtilIdleData *data = g_slice_new0 (UtilIdleData);
1078 GMainContext *context = NULL; /* g_main_context_new (); */
1079 data->loop = g_main_loop_new (context, FALSE /* not running */);
1081 /* Cause the function to be run in an idle-handler, which is always
1082 * in the main thread:
1084 if (!connect_request_in_progress) {
1085 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1086 connect_request_in_progress = TRUE;
1087 g_idle_add (&on_idle_connect_and_wait, data);
1090 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1091 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1094 /* This main loop will run until the idle handler has stopped it: */
1095 printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1096 GDK_THREADS_LEAVE();
1097 g_main_loop_run (data->loop);
1098 GDK_THREADS_ENTER();
1099 printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1100 connect_request_in_progress = FALSE;
1101 printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1102 g_main_loop_unref (data->loop);
1103 /* g_main_context_unref (context); */
1105 g_slice_free (UtilIdleData, data);
1107 gboolean result = tny_device_is_online (device);
1110 set_account_to_online (account);
1115 gboolean modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1117 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1118 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1119 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1120 /* This must be a maildir account, which does not require a connection: */
1125 return modest_platform_connect_and_wait (parent_window, account);
1128 gboolean modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1131 return TRUE; /* Maybe it is something local. */
1133 gboolean result = TRUE;
1134 if (TNY_IS_FOLDER (folder_store)) {
1135 /* Get the folder's parent account: */
1136 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1137 if (account != NULL) {
1138 result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1139 g_object_unref (account);
1141 } else if (TNY_IS_ACCOUNT (folder_store)) {
1142 /* Use the folder store as an account: */
1143 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1150 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1151 ModestSortDialogType type)
1153 GtkWidget *dialog = NULL;
1156 dialog = hildon_sort_dialog_new (parent_window);
1157 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1159 /* Fill sort keys */
1161 case MODEST_SORT_HEADERS:
1162 launch_sort_headers_dialog (parent_window,
1163 HILDON_SORT_DIALOG(dialog));
1168 gtk_widget_destroy (GTK_WIDGET (dialog));
1172 gboolean modest_platform_set_update_interval (guint minutes)
1174 ModestConf *conf = modest_runtime_get_conf ();
1178 cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1180 /* Delete any existing alarm,
1181 * because we will replace it: */
1183 /* TODO: What does the alarm_event_del() return value mean? */
1184 alarm_event_del(alarm_cookie);
1186 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1189 /* 0 means no updates: */
1194 /* Register alarm: */
1196 /* Set the interval in alarm_event_t structure: */
1197 alarm_event_t *event = g_new0(alarm_event_t, 1);
1198 event->alarm_time = minutes * 60; /* seconds */
1200 /* Set recurrence every few minutes: */
1201 event->recurrence = minutes;
1202 event->recurrence_count = -1; /* Means infinite */
1204 /* Specify what should happen when the alarm happens:
1205 * It should call this D-Bus method: */
1207 event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1208 event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1209 event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1210 event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1212 /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if
1213 * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1214 * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails
1215 * This is why we want to use the Alarm API instead of just g_timeout_add().
1216 * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1218 event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1220 alarm_cookie = alarm_event_add (event);
1223 alarm_event_free (event);
1225 /* Store the alarm ID in GConf, so we can remove it later:
1226 * This is apparently valid between application instances. */
1227 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1229 if (!alarm_cookie) {
1231 const alarm_error_t alarm_error = alarmd_get_error ();
1232 g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1234 /* Give people some clue: */
1235 /* The alarm API should have a function for this: */
1236 if (alarm_error == ALARMD_ERROR_DBUS) {
1237 g_debug (" ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1238 } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1239 g_debug (" ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1240 } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1241 g_debug (" ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1242 } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1243 g_debug (" ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1244 } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1245 g_debug (" ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1246 } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1247 g_debug (" ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1257 modest_platform_get_global_settings_dialog ()
1259 return modest_maemo_global_settings_dialog_new ();
1263 modest_platform_on_new_msg (void)
1265 #ifdef MODEST_HAVE_HILDON_NOTIFY
1266 HildonNotification *not;
1268 /* Create a new notification. FIXME put the right values, need
1270 not = hildon_notification_new ("TODO: (new email) Summary",
1271 "TODO: (new email) Description",
1272 "qgn_contact_group_chat_invitation",
1273 "system.note.dialog");
1275 /* Play sound SR-SND-18. TODO: play the right file */
1276 /* TODO: Where is this declared? hildon_notification_set_sound (not, "/usr/share/sounds/ui-new_email.wav"); */
1278 /* Set the led pattern */
1279 notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "led-pattern", 3);
1281 /* Notify. We need to do this in an idle because this function
1282 could be called from a thread */
1283 if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1284 g_error ("Failed to send notification");
1286 g_object_unref (not);
1287 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1292 modest_platform_show_help (GtkWindow *parent_window,
1293 const gchar *help_id)
1295 osso_return_t result;
1297 g_return_if_fail (help_id);
1298 g_return_if_fail (osso_context);
1301 #ifdef MODEST_HAVE_OSSO_HELP
1302 result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1304 result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1307 if (result != OSSO_OK) {
1309 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id);
1310 hildon_banner_show_information (GTK_WIDGET (parent_window),
1318 modest_platform_show_search_messages (GtkWindow *parent_window)
1320 osso_return_t result = OSSO_ERROR;
1322 result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1324 if (result != OSSO_OK) {
1325 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1330 modest_platform_show_addressbook (GtkWindow *parent_window)
1332 osso_return_t result = OSSO_ERROR;
1334 result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1336 if (result != OSSO_OK) {
1337 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1342 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1344 GtkWidget *widget = modest_folder_view_new (query);
1346 /* Show one account by default */
1347 modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1348 MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1351 /* Restore settings */
1352 modest_widget_memory_restore (modest_runtime_get_conf(),
1354 MODEST_CONF_FOLDER_VIEW_KEY);
1360 modest_platform_information_banner (GtkWidget *parent,
1361 const gchar *icon_name,
1364 hildon_banner_show_information (parent, icon_name, text);
1368 modest_platform_animation_banner (GtkWidget *parent,
1369 const gchar *animation_name,
1372 GtkWidget *inf_note = NULL;
1374 g_return_val_if_fail (text != NULL, NULL);
1376 inf_note = hildon_banner_show_animation (parent, animation_name, text);