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 GtkWidget *ok_button;
579 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (data)->action_area));
580 ok_button = GTK_WIDGET (buttons->next->data);
582 gtk_widget_set_sensitive (ok_button,
583 modest_text_utils_validate_folder_name (chars));
584 g_list_free (buttons);
586 /* Write the text in the entry */
587 g_signal_handlers_block_by_func (editable,
588 (gpointer) entry_insert_text, data);
589 gtk_editable_insert_text (editable, text, length, position);
590 g_signal_handlers_unblock_by_func (editable,
591 (gpointer) entry_insert_text, data);
593 /* Do not allow further processing */
594 g_signal_stop_emission_by_name (editable, "insert_text");
598 entry_changed (GtkEditable *editable,
602 GtkWidget *ok_button;
605 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
606 ok_button = GTK_WIDGET (buttons->next->data);
608 chars = gtk_editable_get_chars (editable, 0, -1);
609 g_return_if_fail (chars != NULL);
612 if (g_utf8_strlen (chars,-1) >= 21)
613 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
614 _CS("ckdg_ib_maximum_characters_reached"));
616 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
619 g_list_free (buttons);
624 launch_sort_headers_dialog (GtkWindow *parent_window,
625 HildonSortDialog *dialog)
627 ModestHeaderView *header_view = NULL;
629 GtkSortType sort_type;
631 gint default_key = 0;
633 gboolean outgoing = FALSE;
634 gint current_sort_colid = -1;
635 GtkSortType current_sort_type;
636 gint attachments_sort_id;
637 gint priority_sort_id;
638 GtkTreeSortable *sortable;
640 /* Get header window */
641 if (MODEST_IS_MAIN_WINDOW (parent_window)) {
642 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
643 MODEST_WIDGET_TYPE_HEADER_VIEW));
645 if (!header_view) return;
647 /* Add sorting keys */
648 cols = modest_header_view_get_columns (header_view);
649 if (cols == NULL) return;
650 int sort_model_ids[6];
654 outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
655 MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
657 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
659 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
660 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
662 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
663 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
666 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
668 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
669 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
671 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
672 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
674 default_key = sort_key;
676 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
677 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
679 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
681 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
683 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
684 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
685 sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
686 attachments_sort_id = sort_key;
688 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
689 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
690 sort_ids[sort_key] = 0;
692 sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
693 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
694 sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
695 priority_sort_id = sort_key;
697 sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
699 if (!gtk_tree_sortable_get_sort_column_id (sortable,
700 ¤t_sort_colid, ¤t_sort_type)) {
701 hildon_sort_dialog_set_sort_key (dialog, default_key);
702 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
704 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
705 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
706 gpointer flags_sort_type_pointer;
707 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
708 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
709 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
711 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
713 gint current_sort_keyid = 0;
714 while (current_sort_keyid < 6) {
715 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
718 current_sort_keyid++;
720 hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
724 result = gtk_dialog_run (GTK_DIALOG (dialog));
725 if (result == GTK_RESPONSE_OK) {
726 sort_key = hildon_sort_dialog_get_sort_key (dialog);
727 sort_type = hildon_sort_dialog_get_sort_order (dialog);
728 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
729 g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
730 GINT_TO_POINTER (sort_ids[sort_key]));
731 /* This is a hack to make it resort rows always when flag fields are
732 * selected. If we do not do this, changing sort field from priority to
733 * attachments does not work */
734 modest_header_view_sort_by_column_id (header_view, 0, sort_type);
736 gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data),
737 sort_model_ids[sort_key]);
740 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
741 gtk_tree_sortable_sort_column_changed (sortable);
744 modest_widget_memory_save (modest_runtime_get_conf (),
745 G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
747 /* while (gtk_events_pending ()) */
748 /* gtk_main_iteration (); */
755 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
756 const gchar *dialog_title,
757 const gchar *label_text,
758 const gchar *suggested_name,
761 GtkWidget *accept_btn = NULL;
762 GtkWidget *dialog, *entry, *label, *hbox;
763 GList *buttons = NULL;
766 /* Ask the user for the folder name */
767 dialog = gtk_dialog_new_with_buttons (dialog_title,
769 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
776 /* Add accept button (with unsensitive handler) */
777 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
778 accept_btn = GTK_WIDGET (buttons->next->data);
779 /* Create label and entry */
780 label = gtk_label_new (label_text);
781 /* TODO: check that the suggested name does not exist */
782 /* We set 21 as maximum because we want to show WID-INF036
783 when the user inputs more that 20 */
784 entry = gtk_entry_new_with_max_length (21);
786 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
788 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
789 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
791 /* Track entry changes */
792 g_signal_connect (entry,
794 G_CALLBACK (entry_insert_text),
796 g_signal_connect (entry,
798 G_CALLBACK (entry_changed),
801 /* Create the hbox */
802 hbox = gtk_hbox_new (FALSE, 12);
803 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
804 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
806 /* Add hbox to dialog */
807 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox),
808 hbox, FALSE, FALSE, 0);
810 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
812 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
813 result = gtk_dialog_run (GTK_DIALOG(dialog));
814 if (result == GTK_RESPONSE_ACCEPT)
815 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
817 gtk_widget_destroy (dialog);
819 while (gtk_events_pending ())
820 gtk_main_iteration ();
826 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
827 TnyFolderStore *parent_folder,
828 gchar *suggested_name,
831 gchar *real_suggested_name = NULL;
834 if(suggested_name == NULL)
836 const gchar *default_name = _("mcen_ia_default_folder_name");
840 for(i = 0; i < 100; ++ i)
842 TnyList *list = tny_simple_list_new ();
843 TnyFolderStoreQuery *query = tny_folder_store_query_new ();
846 sprintf(num_str, "%.2u", i);
849 real_suggested_name = g_strdup (default_name);
851 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
854 tny_folder_store_query_add_item (query, real_suggested_name,
855 TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
857 tny_folder_store_get_folders (parent_folder, list, query, NULL);
859 length = tny_list_get_length (list);
860 g_object_unref (query);
861 g_object_unref (list);
866 g_free (real_suggested_name);
869 /* Didn't find a free number */
871 real_suggested_name = g_strdup (default_name);
875 real_suggested_name = suggested_name;
878 result = modest_platform_run_folder_name_dialog (parent_window,
879 _("mcen_ti_new_folder"),
880 _("mcen_fi_new_folder_name"),
883 if (suggested_name == NULL)
884 g_free(real_suggested_name);
890 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
891 TnyFolderStore *parent_folder,
892 const gchar *suggested_name,
895 return modest_platform_run_folder_name_dialog (parent_window,
896 _("New folder name"),
897 _("Enter new folder name:"),
903 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
904 const gchar *message)
909 dialog = hildon_note_new_confirmation (parent_window, message);
910 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
912 response = gtk_dialog_run (GTK_DIALOG (dialog));
914 gtk_widget_destroy (GTK_WIDGET (dialog));
916 while (gtk_events_pending ())
917 gtk_main_iteration ();
923 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
924 const gchar *message)
929 dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
930 _("mcen_bd_yes"), GTK_RESPONSE_YES,
931 _("mcen_bd_no"), GTK_RESPONSE_NO,
933 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
935 response = gtk_dialog_run (GTK_DIALOG (dialog));
937 gtk_widget_destroy (GTK_WIDGET (dialog));
939 while (gtk_events_pending ())
940 gtk_main_iteration ();
946 modest_platform_run_information_dialog (GtkWindow *parent_window,
947 const gchar *message)
951 dialog = hildon_note_new_information (parent_window, message);
953 g_signal_connect_swapped (dialog,
955 G_CALLBACK (gtk_widget_destroy),
958 gtk_widget_show_all (dialog);
969 on_idle_connect_and_wait(gpointer user_data)
971 printf ("DEBUG: %s:\n", __FUNCTION__);
972 TnyDevice *device = modest_runtime_get_device();
973 if (!tny_device_is_online (device)) {
975 /* This is a GDK lock because we are an idle callback and
976 * tny_maemo_conic_device_connect can contain Gtk+ code */
978 gdk_threads_enter(); /* CHECKED */
979 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
980 gdk_threads_leave(); /* CHECKED */
983 /* Allow the function that requested this idle callback to continue: */
984 UtilIdleData *data = (UtilIdleData*)user_data;
986 g_main_loop_quit (data->loop);
988 return FALSE; /* Don't call this again. */
991 static gboolean connect_request_in_progress = FALSE;
993 /* This callback is used when connect_and_wait() is already queued as an idle callback.
994 * This can happen because the gtk_dialog_run() for the connection dialog
995 * (at least in the fake scratchbox version) allows idle handlers to keep running.
998 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
1000 gboolean result = FALSE;
1001 TnyDevice *device = modest_runtime_get_device();
1002 if (tny_device_is_online (device))
1003 result = FALSE; /* Stop trying. */
1005 /* Keep trying until connect_request_in_progress is FALSE. */
1006 if (connect_request_in_progress)
1007 result = TRUE; /* Keep trying */
1009 printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
1011 result = FALSE; /* Stop trying, now that a result should be available. */
1015 if (result == FALSE) {
1016 /* Allow the function that requested this idle callback to continue: */
1017 UtilIdleData *data = (UtilIdleData*)user_data;
1019 g_main_loop_quit (data->loop);
1026 set_account_to_online (TnyAccount *account)
1028 /* TODO: This is necessary to prevent a cancel of the password dialog
1029 * from making a restart necessary to be asked the password again,
1030 * but it causes a hang:
1033 if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
1034 /* Make sure that store accounts are online too,
1035 * because tinymail sets accounts to offline if
1036 * a password dialog is ever cancelled.
1037 * We don't do this for transport accounts because
1038 * a) They fundamentally need network access, so they can't really be offline.
1039 * b) That might cause a transport connection to happen too early.
1041 GError *error = NULL;
1042 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, &error);
1044 g_warning ("%s: tny_camel_account_set_online() returned a GError:\n %s\n",
1045 __FUNCTION__, error->message);
1046 g_error_free (error);
1052 gboolean modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1054 if (connect_request_in_progress)
1057 printf ("DEBUG: %s:\n", __FUNCTION__);
1058 TnyDevice *device = modest_runtime_get_device();
1060 if (tny_device_is_online (device)) {
1061 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1062 set_account_to_online (account);
1066 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1069 /* This blocks on the result: */
1070 UtilIdleData *data = g_slice_new0 (UtilIdleData);
1072 GMainContext *context = NULL; /* g_main_context_new (); */
1073 data->loop = g_main_loop_new (context, FALSE /* not running */);
1075 /* Cause the function to be run in an idle-handler, which is always
1076 * in the main thread:
1078 if (!connect_request_in_progress) {
1079 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1080 connect_request_in_progress = TRUE;
1081 g_idle_add (&on_idle_connect_and_wait, data);
1084 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1085 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1088 /* This main loop will run until the idle handler has stopped it: */
1089 printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1090 GDK_THREADS_LEAVE();
1091 g_main_loop_run (data->loop);
1092 GDK_THREADS_ENTER();
1093 printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1094 connect_request_in_progress = FALSE;
1095 printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1096 g_main_loop_unref (data->loop);
1097 /* g_main_context_unref (context); */
1099 g_slice_free (UtilIdleData, data);
1101 gboolean result = tny_device_is_online (device);
1104 set_account_to_online (account);
1109 gboolean modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1111 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1112 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1113 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1114 /* This must be a maildir account, which does not require a connection: */
1119 return modest_platform_connect_and_wait (parent_window, account);
1122 gboolean modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1125 return TRUE; /* Maybe it is something local. */
1127 gboolean result = TRUE;
1128 if (TNY_IS_FOLDER (folder_store)) {
1129 /* Get the folder's parent account: */
1130 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1131 if (account != NULL) {
1132 result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1133 g_object_unref (account);
1135 } else if (TNY_IS_ACCOUNT (folder_store)) {
1136 /* Use the folder store as an account: */
1137 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1144 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1145 ModestSortDialogType type)
1147 GtkWidget *dialog = NULL;
1150 dialog = hildon_sort_dialog_new (parent_window);
1151 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1153 /* Fill sort keys */
1155 case MODEST_SORT_HEADERS:
1156 launch_sort_headers_dialog (parent_window,
1157 HILDON_SORT_DIALOG(dialog));
1162 gtk_widget_destroy (GTK_WIDGET (dialog));
1166 gboolean modest_platform_set_update_interval (guint minutes)
1168 ModestConf *conf = modest_runtime_get_conf ();
1172 cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1174 /* Delete any existing alarm,
1175 * because we will replace it: */
1177 /* TODO: What does the alarm_event_del() return value mean? */
1178 alarm_event_del(alarm_cookie);
1180 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1183 /* 0 means no updates: */
1188 /* Register alarm: */
1190 /* Set the interval in alarm_event_t structure: */
1191 alarm_event_t *event = g_new0(alarm_event_t, 1);
1192 event->alarm_time = minutes * 60; /* seconds */
1194 /* Set recurrence every few minutes: */
1195 event->recurrence = minutes;
1196 event->recurrence_count = -1; /* Means infinite */
1198 /* Specify what should happen when the alarm happens:
1199 * It should call this D-Bus method: */
1201 event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1202 event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1203 event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1204 event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1206 /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if
1207 * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1208 * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails
1209 * This is why we want to use the Alarm API instead of just g_timeout_add().
1210 * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1212 event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1214 alarm_cookie = alarm_event_add (event);
1217 alarm_event_free (event);
1219 /* Store the alarm ID in GConf, so we can remove it later:
1220 * This is apparently valid between application instances. */
1221 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1223 if (!alarm_cookie) {
1225 const alarm_error_t alarm_error = alarmd_get_error ();
1226 g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1228 /* Give people some clue: */
1229 /* The alarm API should have a function for this: */
1230 if (alarm_error == ALARMD_ERROR_DBUS) {
1231 g_debug (" ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1232 } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1233 g_debug (" ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1234 } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1235 g_debug (" ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1236 } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1237 g_debug (" ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1238 } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1239 g_debug (" ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1240 } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1241 g_debug (" ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1251 modest_platform_get_global_settings_dialog ()
1253 return modest_maemo_global_settings_dialog_new ();
1257 modest_platform_on_new_msg (void)
1259 #ifdef MODEST_HAVE_HILDON_NOTIFY
1260 HildonNotification *not;
1262 /* Create a new notification. FIXME put the right values, need
1264 not = hildon_notification_new ("TODO: (new email) Summary",
1265 "TODO: (new email) Description",
1266 "qgn_contact_group_chat_invitation",
1267 "system.note.dialog");
1269 /* Play sound SR-SND-18. TODO: play the right file */
1270 /* TODO: Where is this declared? hildon_notification_set_sound (not, "/usr/share/sounds/ui-new_email.wav"); */
1272 /* Set the led pattern */
1273 notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "led-pattern", 3);
1275 /* Notify. We need to do this in an idle because this function
1276 could be called from a thread */
1277 if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1278 g_error ("Failed to send notification");
1280 g_object_unref (not);
1281 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1286 modest_platform_show_help (GtkWindow *parent_window,
1287 const gchar *help_id)
1289 osso_return_t result;
1291 g_return_if_fail (help_id);
1292 g_return_if_fail (osso_context);
1295 #ifdef MODEST_HAVE_OSSO_HELP
1296 result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1298 result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1301 if (result != OSSO_OK) {
1303 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id);
1304 hildon_banner_show_information (GTK_WIDGET (parent_window),
1312 modest_platform_show_search_messages (GtkWindow *parent_window)
1314 osso_return_t result = OSSO_ERROR;
1316 result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1318 if (result != OSSO_OK) {
1319 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1324 modest_platform_show_addressbook (GtkWindow *parent_window)
1326 osso_return_t result = OSSO_ERROR;
1328 result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1330 if (result != OSSO_OK) {
1331 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1336 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1338 GtkWidget *widget = modest_folder_view_new (query);
1340 /* Show one account by default */
1341 modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1342 MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1345 /* Restore settings */
1346 modest_widget_memory_restore (modest_runtime_get_conf(),
1348 MODEST_CONF_FOLDER_VIEW_KEY);
1354 modest_platform_information_banner (GtkWidget *parent,
1355 const gchar *icon_name,
1358 hildon_banner_show_information (parent, icon_name, text);
1362 modest_platform_animation_banner (GtkWidget *parent,
1363 const gchar *animation_name,
1366 GtkWidget *inf_note = NULL;
1368 g_return_val_if_fail (text != NULL, NULL);
1370 inf_note = hildon_banner_show_animation (parent, animation_name, text);