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>
33 #include <modest-platform.h>
34 #include <modest-defs.h>
35 #include <modest-runtime.h>
36 #include <modest-main-window.h>
37 #include <modest-header-view.h>
38 #include "modest-hildon2-global-settings-dialog.h"
39 #include "modest-widget-memory.h"
40 #include <modest-hildon-includes.h>
41 #include <modest-maemo-utils.h>
42 #include <modest-utils.h>
43 #include <dbus_api/modest-dbus-callbacks.h>
44 #include <modest-osso-autosave-callbacks.h>
46 #include <tny-maemo-conic-device.h>
47 #include <tny-camel-folder.h>
48 #include <tny-simple-list.h>
49 #include <tny-merge-folder.h>
50 #include <tny-error.h>
51 #include <tny-folder.h>
52 #include <tny-account-store-view.h>
53 #include <gtk/gtkicontheme.h>
54 #include <gtk/gtkmenuitem.h>
55 #include <gtk/gtkmain.h>
56 #include <modest-text-utils.h>
57 #include "modest-tny-folder.h"
58 #include "modest-tny-account.h"
60 #include <libgnomevfs/gnome-vfs-mime-utils.h>
61 #include <modest-account-settings-dialog.h>
62 #include <modest-easysetup-wizard-dialog.h>
63 #include "modest-hildon2-sort-dialog.h"
64 #include <hildon/hildon.h>
66 #include "hildon2/modest-hildon2-details-dialog.h"
67 #include "hildon2/modest-hildon2-window-mgr.h"
68 #ifdef MODEST_USE_PROFILE
69 #include <keys_nokia.h>
70 #include <libprofile.h>
73 #include <modest-datetime-formatter.h>
74 #include "modest-header-window.h"
75 #include <modest-folder-window.h>
76 #include <modest-account-mgr.h>
77 #include <modest-account-mgr-helpers.h>
78 #include <modest-ui-constants.h>
79 #include <modest-selector-picker.h>
80 #include <modest-icon-names.h>
81 #include <modest-count-stream.h>
83 #ifdef MODEST_HAVE_MCE
84 #include <mce/dbus-names.h>
85 #endif /*MODEST_HAVE_MCE*/
87 #ifdef MODEST_HAVE_ABOOK
88 #include <libosso-abook/osso-abook.h>
89 #endif /*MODEST_HAVE_ABOOK*/
91 #ifdef MODEST_HAVE_LIBALARM
92 #include <alarmd/libalarm.h> /* For alarm_event_add(), etc. */
93 #endif /*MODEST_HAVE_LIBALARM*/
96 #define HILDON_OSSO_URI_ACTION "uri-action"
97 #define URI_ACTION_COPY "copy:"
98 #define MODEST_NOTIFICATION_CATEGORY "email-message"
99 #define MODEST_NEW_MAIL_LIGHTING_PATTERN "PatternCommunicationEmail"
100 #ifdef MODEST_USE_PROFILE
101 #define PROFILE_MAIL_TONE PROFILEKEY_EMAIL_ALERT_TONE
102 #define PROFILE_MAIL_VOLUME PROFILEKEY_EMAIL_ALERT_VOLUME
104 #define MAIL_TONE "message-new-email"
107 #define COMMON_FOLDER_DIALOG_ENTRY "entry"
108 #define COMMON_FOLDER_DIALOG_ACCOUNT_PICKER "account-picker"
109 #define FOLDER_PICKER_CURRENT_FOLDER "current-folder"
110 #define MODEST_ALARMD_APPID PACKAGE_NAME
113 static void _modest_platform_play_email_tone (void);
117 on_modest_conf_update_interval_changed (ModestConf* self,
119 ModestConfEvent event,
120 ModestConfNotificationId id,
123 g_return_if_fail (key);
125 if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
126 const guint update_interval_minutes =
127 modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
128 modest_platform_set_update_interval (update_interval_minutes);
135 check_required_files (void)
137 FILE *mcc_file = modest_utils_open_mcc_mapping_file (NULL);
139 g_printerr ("modest: check for mcc file failed\n");
144 if (access(MODEST_PROVIDER_DATA_FILE, R_OK) != 0 &&
145 access(MODEST_FALLBACK_PROVIDER_DATA_FILE, R_OK) != 0) {
146 g_printerr ("modest: cannot find providers data\n");
154 /* the gpointer here is the osso_context. */
156 modest_platform_init (int argc, char *argv[])
158 osso_context_t *osso_context;
160 osso_hw_state_t hw_state = { 0 };
164 if (!check_required_files ()) {
165 g_printerr ("modest: missing required files\n");
169 osso_context = osso_initialize(PACKAGE,PACKAGE_VERSION,
172 g_printerr ("modest: failed to acquire osso context\n");
175 modest_maemo_utils_set_osso_context (osso_context);
177 if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
178 g_printerr ("modest: could not get dbus connection\n");
182 /* Add a D-Bus handler to be used when the main osso-rpc
183 * D-Bus handler has not handled something.
184 * We use this for D-Bus methods that need to use more complex types
185 * than osso-rpc supports.
187 if (!dbus_connection_add_filter (con,
188 modest_dbus_req_filter,
192 g_printerr ("modest: Could not add D-Bus filter\n");
196 /* Register our simple D-Bus callbacks, via the osso API: */
197 osso_return_t result = osso_rpc_set_cb_f(osso_context,
201 modest_dbus_req_handler, NULL /* user_data */);
202 if (result != OSSO_OK) {
203 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
207 /* Register hardware event dbus callback: */
208 hw_state.shutdown_ind = TRUE;
209 osso_hw_set_event_cb(osso_context, NULL, NULL, NULL);
211 /* Register osso auto-save callbacks: */
212 result = osso_application_set_autosave_cb (osso_context,
213 modest_on_osso_application_autosave, NULL /* user_data */);
214 if (result != OSSO_OK) {
215 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
220 /* Make sure that the update interval is changed whenever its gconf key
222 /* CAUTION: we're not using here the
223 modest_conf_listen_to_namespace because we know that there
224 are other parts of Modest listening for this namespace, so
225 we'll receive the notifications anyway. We basically do not
226 use it because there is no easy way to do the
227 modest_conf_forget_namespace */
228 ModestConf *conf = modest_runtime_get_conf ();
229 g_signal_connect (G_OBJECT(conf),
231 G_CALLBACK (on_modest_conf_update_interval_changed),
234 /* only force the setting of the default interval, if there are actually
236 acc_names = modest_account_mgr_account_names (modest_runtime_get_account_mgr(), TRUE);
238 /* Get the initial update interval from gconf: */
239 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
240 MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
241 modest_account_mgr_free_account_names (acc_names);
245 #ifdef MODEST_HAVE_ABOOK
246 /* initialize the addressbook */
247 if (!osso_abook_init (&argc, &argv, osso_context)) {
248 g_printerr ("modest: failed to initialized addressbook\n");
251 #endif /*MODEST_HAVE_ABOOK*/
257 modest_platform_uninit (void)
259 osso_context_t *osso_context =
260 modest_maemo_utils_get_osso_context ();
262 osso_deinitialize (osso_context);
271 modest_platform_get_new_device (void)
273 return TNY_DEVICE (tny_maemo_conic_device_new ());
277 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
278 gchar **effective_mime_type)
280 GString *mime_str = NULL;
281 gchar *icon_name = NULL;
282 gchar **icons, **cursor;
284 if (!mime_type || g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0)
285 mime_str = g_string_new (gnome_vfs_get_mime_type_for_name (name));
287 mime_str = g_string_new (mime_type);
288 g_string_ascii_down (mime_str);
291 icons = hildon_mime_get_icon_names (mime_str->str, NULL);
293 for (cursor = icons; cursor; ++cursor) {
294 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
295 !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
296 icon_name = g_strdup ("qgn_list_messagin");
298 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
299 icon_name = g_strdup (*cursor);
305 if (effective_mime_type)
306 *effective_mime_type = g_string_free (mime_str, FALSE);
308 g_string_free (mime_str, TRUE);
315 checked_hildon_uri_open (const gchar *uri, HildonURIAction *action)
320 g_return_val_if_fail (uri, FALSE);
322 result = hildon_uri_open (uri, action, &err);
324 g_printerr ("modest: hildon_uri_open ('%s', %p) failed: %s",
325 uri, action, err && err->message ? err->message : "unknown error");
335 modest_platform_activate_uri (const gchar *uri)
337 HildonURIAction *action;
338 gboolean result = FALSE;
339 GSList *actions, *iter = NULL;
341 g_return_val_if_fail (uri, FALSE);
345 /* don't try to activate file: uri's -- they might confuse the user,
346 * and/or might have security implications */
347 if (!g_str_has_prefix (uri, "file:")) {
349 actions = hildon_uri_get_actions_by_uri (uri, -1, NULL);
351 for (iter = actions; iter; iter = g_slist_next (iter)) {
352 action = (HildonURIAction*) iter->data;
353 if (action && strcmp (hildon_uri_action_get_service (action),
354 "com.nokia.modest") == 0) {
355 result = checked_hildon_uri_open (uri, action);
360 /* if we could not open it with email, try something else */
362 result = checked_hildon_uri_open (uri, NULL);
366 ModestWindow *parent =
367 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
368 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
369 _("mcen_ib_unsupported_link"));
370 g_warning ("%s: cannot open uri '%s'", __FUNCTION__,uri);
377 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
381 gchar *uri_path = NULL;
383 uri_path = gnome_vfs_get_uri_from_local_path (path);
384 con = osso_get_dbus_connection (modest_maemo_utils_get_osso_context());
387 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
389 result = hildon_mime_open_file (con, uri_path);
391 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"), FALSE);
399 } ModestPlatformPopupInfo;
402 delete_uri_popup (GtkWidget *menu,
406 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
408 g_free (popup_info->uri);
409 hildon_uri_free_actions (popup_info->actions);
415 activate_uri_popup_item (GtkMenuItem *menu_item,
419 ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
420 const gchar* action_name;
422 action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
424 g_printerr ("modest: no action name defined\n");
428 /* special handling for the copy menu item -- copy the uri to the clipboard */
429 /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
430 if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
431 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
432 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
434 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
435 action_name += strlen ("mailto:");
437 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
438 modest_platform_information_banner (NULL, NULL, _CS("ecoc_ib_edwin_copied"));
439 return; /* we're done */
442 /* now, the real uri-actions... */
443 for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
444 HildonURIAction *action = (HildonURIAction *) node->data;
445 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
446 if (!checked_hildon_uri_open (popup_info->uri, action)) {
447 ModestWindow *parent =
448 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
449 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
450 _("mcen_ib_unsupported_link"));
458 modest_platform_show_uri_popup (const gchar *uri)
460 GSList *actions_list;
465 actions_list = hildon_uri_get_actions_by_uri (uri, -1, NULL);
468 GtkWidget *menu = gtk_menu_new ();
469 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
471 /* don't add actions for file: uri's -- they might confuse the user,
472 * and/or might have security implications
473 * we still allow to copy the url though
475 if (!g_str_has_prefix (uri, "file:")) {
478 popup_info->actions = actions_list;
479 popup_info->uri = g_strdup (uri);
481 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
482 GtkWidget *menu_item;
483 const gchar *action_name;
484 const gchar *translation_domain;
485 HildonURIAction *action = (HildonURIAction *) node->data;
486 action_name = hildon_uri_action_get_name (action);
487 translation_domain = hildon_uri_action_get_translation_domain (action);
488 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
489 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name); /* hack */
490 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
493 if (hildon_uri_is_default_action (action, NULL)) {
494 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
496 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
498 gtk_widget_show (menu_item);
503 /* and what to do when the link is deleted */
504 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
505 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
508 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
516 modest_platform_get_icon (const gchar *name, guint icon_size)
519 GdkPixbuf* pixbuf = NULL;
520 GtkIconTheme *current_theme = NULL;
522 g_return_val_if_fail (name, NULL);
524 /* strlen == 0 is not really an error; it just
525 * means the icon is not available
527 if (!name || strlen(name) == 0)
530 current_theme = gtk_icon_theme_get_default ();
531 pixbuf = gtk_icon_theme_load_icon (current_theme, name, icon_size,
532 GTK_ICON_LOOKUP_NO_SVG,
535 g_printerr ("modest: error loading theme icon '%s': %s\n",
543 modest_platform_get_app_name (void)
545 return _("mcen_ap_name");
549 entry_insert_text (GtkEditable *editable,
558 chars = gtk_editable_get_chars (editable, 0, -1);
559 chars_length = g_utf8_strlen (chars, -1);
562 /* Show WID-INF036 */
563 if (chars_length >= 20) {
564 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
565 _CS("ckdg_ib_maximum_characters_reached"));
567 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
571 tmp = g_strndup (folder_name_forbidden_chars,
572 FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
573 msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
574 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)),
580 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
581 _CS("ckdg_ib_maximum_characters_reached"));
583 /* Write the text in the entry if it's valid */
584 g_signal_handlers_block_by_func (editable,
585 (gpointer) entry_insert_text, data);
586 gtk_editable_insert_text (editable, text, length, position);
587 g_signal_handlers_unblock_by_func (editable,
588 (gpointer) entry_insert_text, data);
591 /* Do not allow further processing */
592 g_signal_stop_emission_by_name (editable, "insert_text");
596 entry_changed (GtkEditable *editable,
600 GtkWidget *ok_button;
603 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
604 ok_button = GTK_WIDGET (buttons->data);
606 chars = gtk_editable_get_chars (editable, 0, -1);
607 g_return_if_fail (chars != NULL);
610 if (g_utf8_strlen (chars,-1) >= 20) {
611 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
612 _CS("ckdg_ib_maximum_characters_reached"));
614 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
617 g_list_free (buttons);
624 on_response (GtkDialog *dialog,
628 GtkWidget *entry, *picker;
629 TnyFolderStore *parent;
630 const gchar *new_name;
633 if (response != GTK_RESPONSE_ACCEPT)
637 entry = g_object_get_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ENTRY);
638 picker = g_object_get_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ACCOUNT_PICKER);
640 parent = TNY_FOLDER_STORE (user_data);
641 new_name = gtk_entry_get_text (GTK_ENTRY (entry));
645 parent = g_object_get_data (G_OBJECT (picker), FOLDER_PICKER_CURRENT_FOLDER);
647 /* Look for another folder with the same name */
648 if (modest_tny_folder_has_subfolder_with_name (parent, new_name, TRUE))
652 if (TNY_IS_ACCOUNT (parent) &&
653 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) &&
654 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent),
662 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)),
663 NULL, _CS("ckdg_ib_folder_already_exists"));
664 /* Select the text */
665 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
666 gtk_widget_grab_focus (entry);
667 /* Do not close the dialog */
668 g_signal_stop_emission_by_name (dialog, "response");
672 typedef struct _FolderChooserData {
673 TnyFolderStore *store;
678 folder_chooser_activated (ModestFolderView *folder_view,
679 TnyFolderStore *folder,
680 FolderChooserData *userdata)
682 userdata->store = folder;
683 gtk_dialog_response (GTK_DIALOG (userdata->dialog), GTK_RESPONSE_OK);
686 static TnyFolderStore *
687 folder_chooser_dialog_run (ModestFolderView *original)
689 GtkWidget *folder_view;
690 FolderChooserData userdata = {NULL, NULL};
692 const gchar *visible_id = NULL;
694 userdata.dialog = hildon_dialog_new ();
695 pannable = hildon_pannable_area_new ();
696 folder_view = modest_platform_create_folder_view (NULL);
698 gtk_window_set_title (GTK_WINDOW (userdata.dialog), _FM("ckdg_ti_change_folder"));
700 modest_folder_view_copy_model (MODEST_FOLDER_VIEW (original),
701 MODEST_FOLDER_VIEW (folder_view));
704 modest_folder_view_get_account_id_of_visible_server_account (MODEST_FOLDER_VIEW(original));
705 modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW(folder_view),
708 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (userdata.dialog)->vbox), pannable);
709 gtk_container_add (GTK_CONTAINER (pannable), folder_view);
710 gtk_widget_set_size_request (pannable, -1, 320);
712 gtk_widget_show (folder_view);
713 gtk_widget_show (pannable);
714 gtk_widget_show (userdata.dialog);
715 g_signal_connect (G_OBJECT (folder_view), "folder-activated",
716 G_CALLBACK (folder_chooser_activated),
717 (gpointer) &userdata);
719 gtk_dialog_run (GTK_DIALOG (userdata.dialog));
720 gtk_widget_destroy (userdata.dialog);
722 return userdata.store;
726 folder_store_get_display_name (TnyFolderStore *store)
728 if (TNY_IS_ACCOUNT (store)) {
729 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (store)))
730 return modest_conf_get_string (modest_runtime_get_conf(),
731 MODEST_CONF_DEVICE_NAME, NULL);
733 return g_strdup (tny_account_get_name (TNY_ACCOUNT (store)));
736 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
738 fname = g_strdup (tny_folder_get_name (TNY_FOLDER (store)));
739 type = tny_folder_get_folder_type (TNY_FOLDER (store));
740 if (modest_tny_folder_is_local_folder (TNY_FOLDER (store)) ||
741 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (store))) {
742 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (store));
743 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
745 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
748 /* Sometimes an special folder is reported by the server as
749 NORMAL, like some versions of Dovecot */
750 if (type == TNY_FOLDER_TYPE_NORMAL ||
751 type == TNY_FOLDER_TYPE_UNKNOWN) {
752 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (store));
756 if (type == TNY_FOLDER_TYPE_INBOX) {
758 fname = g_strdup (_("mcen_me_folder_inbox"));
765 get_image_for_folder_store (TnyFolderStore *store,
769 const gchar *icon_name = NULL;
770 GtkWidget *image = NULL;
772 if (TNY_IS_ACCOUNT (store)) {
773 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (store)))
774 icon_name = MODEST_FOLDER_ICON_LOCAL_FOLDERS;
775 else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (store)))
776 icon_name = MODEST_FOLDER_ICON_MMC;
778 icon_name = MODEST_FOLDER_ICON_ACCOUNT;
780 TnyFolderType type = modest_tny_folder_guess_folder_type (TNY_FOLDER (store));
781 if (modest_tny_folder_is_remote_folder (TNY_FOLDER (store))) {
783 case TNY_FOLDER_TYPE_INBOX:
784 icon_name = MODEST_FOLDER_ICON_INBOX;
787 icon_name = MODEST_FOLDER_ICON_ACCOUNT;
789 } else if (modest_tny_folder_is_local_folder (TNY_FOLDER (store))) {
791 case TNY_FOLDER_TYPE_OUTBOX:
792 icon_name = MODEST_FOLDER_ICON_OUTBOX;
794 case TNY_FOLDER_TYPE_DRAFTS:
795 icon_name = MODEST_FOLDER_ICON_DRAFTS;
797 case TNY_FOLDER_TYPE_SENT:
798 icon_name = MODEST_FOLDER_ICON_SENT;
801 icon_name = MODEST_FOLDER_ICON_NORMAL;
803 } else if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (store))) {
804 icon_name = MODEST_FOLDER_ICON_MMC_FOLDER;
809 pixbuf = modest_platform_get_icon (icon_name, size);
812 image = gtk_image_new_from_pixbuf (pixbuf);
813 g_object_unref (pixbuf);
820 folder_picker_set_store (GtkButton *button, TnyFolderStore *store)
825 g_object_set_data (G_OBJECT (button), FOLDER_PICKER_CURRENT_FOLDER, NULL);
829 g_object_set_data_full (G_OBJECT (button), FOLDER_PICKER_CURRENT_FOLDER,
830 g_object_ref (store),
831 (GDestroyNotify) g_object_unref);
832 name = folder_store_get_display_name (store);
833 hildon_button_set_value (HILDON_BUTTON (button), name);
837 image = get_image_for_folder_store (store, MODEST_ICON_SIZE_SMALL);
839 hildon_button_set_image (HILDON_BUTTON (button), image);
843 /* Always returns DUPs so you must free the returned value */
845 get_next_folder_name (const gchar *suggested_name,
846 TnyFolderStore *suggested_folder)
848 const gchar *default_name = _FM("ckdg_va_new_folder_name_stub");
850 gchar *real_suggested_name;
852 if (suggested_name !=NULL) {
853 return g_strdup (suggested_name);
856 for(i = 0; i < 100; ++ i) {
857 gboolean exists = FALSE;
860 real_suggested_name = g_strdup (default_name);
862 real_suggested_name = g_strdup_printf ("%s(%d)",
863 _FM("ckdg_va_new_folder_name_stub"),
865 exists = modest_tny_folder_has_subfolder_with_name (suggested_folder,
872 g_free (real_suggested_name);
875 /* Didn't find a free number */
877 real_suggested_name = g_strdup (default_name);
879 return real_suggested_name;
883 ModestFolderView *folder_view;
885 } FolderPickerHelper;
888 folder_picker_clicked (GtkButton *button,
889 FolderPickerHelper *helper)
891 TnyFolderStore *store;
893 store = folder_chooser_dialog_run (helper->folder_view);
895 const gchar *current_name;
898 folder_picker_set_store (GTK_BUTTON (button), store);
900 /* Update the name of the folder */
901 current_name = gtk_entry_get_text (helper->entry);
902 exists = modest_tny_folder_has_subfolder_with_name (store,
906 gchar *new_name = get_next_folder_name (NULL, store);
907 gtk_entry_set_text (helper->entry, new_name);
914 folder_picker_new (TnyFolderStore *suggested, FolderPickerHelper *helper)
918 button = hildon_button_new (MODEST_EDITABLE_SIZE,
919 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
921 hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 1.0);
924 folder_picker_set_store (GTK_BUTTON (button), suggested);
926 g_signal_connect (G_OBJECT (button), "clicked",
927 G_CALLBACK (folder_picker_clicked),
935 modest_platform_run_folder_common_dialog (GtkWindow *parent_window,
936 TnyFolderStore *suggested_parent,
937 const gchar *dialog_title,
938 const gchar *label_text,
939 const gchar *suggested_name,
941 gboolean show_parent,
943 TnyFolderStore **parent)
945 GtkWidget *accept_btn = NULL;
946 GtkWidget *dialog, *entry = NULL, *label_entry = NULL, *label_location = NULL, *hbox;
947 GtkWidget *account_picker = NULL;
948 GList *buttons = NULL;
950 GtkSizeGroup *sizegroup;
951 ModestFolderView *folder_view;
952 ModestWindow *folder_window;
953 ModestHildon2WindowMgr *window_mgr;
954 FolderPickerHelper *helper = NULL;
955 GtkWidget *top_vbox, *top_align;
957 window_mgr = (ModestHildon2WindowMgr *) modest_runtime_get_window_mgr ();
958 folder_window = modest_hildon2_window_mgr_get_folder_window (window_mgr);
959 g_return_val_if_fail (MODEST_IS_FOLDER_WINDOW (folder_window), GTK_RESPONSE_NONE);
961 folder_view = modest_folder_window_get_folder_view (MODEST_FOLDER_WINDOW (folder_window));
963 top_vbox = gtk_vbox_new (FALSE, 0);
964 top_align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
965 gtk_alignment_set_padding (GTK_ALIGNMENT (top_align), 0, 0, MODEST_MARGIN_DOUBLE, 0);
967 /* Ask the user for the folder name */
968 dialog = gtk_dialog_new_with_buttons (dialog_title,
970 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
971 _FM("ckdg_bd_new_folder_dialog_ok"),
975 /* Add accept button (with unsensitive handler) */
976 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
977 accept_btn = GTK_WIDGET (buttons->data);
979 sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
982 label_entry = gtk_label_new (label_text);
983 entry = hildon_entry_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
984 gtk_entry_set_max_length (GTK_ENTRY (entry), 20);
986 gtk_misc_set_alignment (GTK_MISC (label_entry), 0.0, 0.5);
987 gtk_size_group_add_widget (sizegroup, label_entry);
990 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
992 gtk_entry_set_text (GTK_ENTRY (entry), _FM("ckdg_va_new_folder_name_stub"));
993 gtk_entry_set_width_chars (GTK_ENTRY (entry),
994 MAX (g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry)), -1),
995 g_utf8_strlen (_FM("ckdg_va_new_folder_name_stub"), -1)));
996 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
1001 label_location = gtk_label_new (_FM("ckdg_fi_new_folder_location"));
1003 gtk_misc_set_alignment (GTK_MISC (label_location), 0.0, 0.5);
1004 gtk_size_group_add_widget (sizegroup, label_location);
1006 helper = g_slice_new0 (FolderPickerHelper);
1007 helper->folder_view = folder_view;
1008 helper->entry = (GtkEntry *) entry;
1010 account_picker = folder_picker_new (suggested_parent, helper);
1013 g_object_unref (sizegroup);
1015 /* Connect to the response method to avoid closing the dialog
1016 when an invalid name is selected*/
1017 g_signal_connect (dialog,
1019 G_CALLBACK (on_response),
1023 /* Track entry changes */
1024 g_signal_connect (entry,
1026 G_CALLBACK (entry_insert_text),
1028 g_signal_connect (entry,
1030 G_CALLBACK (entry_changed),
1035 /* Some locales like pt_BR need this to get the full window
1037 gtk_widget_set_size_request (GTK_WIDGET (dialog), 300, -1);
1039 /* Create the hbox */
1041 hbox = gtk_hbox_new (FALSE, 12);
1042 gtk_box_pack_start (GTK_BOX (hbox), label_entry, FALSE, FALSE, 0);
1043 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
1045 /* Add hbox to dialog */
1046 gtk_box_pack_start (GTK_BOX (top_vbox),
1047 hbox, FALSE, FALSE, 0);
1048 g_object_set_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ENTRY, entry);
1052 hbox = gtk_hbox_new (FALSE, 12);
1053 gtk_box_pack_start (GTK_BOX (hbox), label_location, FALSE, FALSE, 0);
1054 gtk_box_pack_start (GTK_BOX (hbox), account_picker, TRUE, TRUE, 0);
1056 /* Add hbox to dialog */
1057 gtk_box_pack_start (GTK_BOX (top_vbox),
1058 hbox, FALSE, FALSE, 0);
1059 g_object_set_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ACCOUNT_PICKER, account_picker);
1061 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1062 GTK_WINDOW (dialog), parent_window);
1064 gtk_container_add (GTK_CONTAINER (top_align), top_vbox);
1065 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), top_align, TRUE, TRUE, 0);
1067 gtk_widget_show_all (GTK_WIDGET(dialog));
1069 result = gtk_dialog_run (GTK_DIALOG(dialog));
1070 if (result == GTK_RESPONSE_ACCEPT) {
1072 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
1074 *parent = g_object_get_data (G_OBJECT (account_picker), FOLDER_PICKER_CURRENT_FOLDER);
1076 g_object_ref (*parent);
1080 gtk_widget_destroy (dialog);
1083 g_slice_free (FolderPickerHelper, helper);
1085 while (gtk_events_pending ())
1086 gtk_main_iteration ();
1092 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
1093 TnyFolderStore *suggested_folder,
1094 gchar *suggested_name,
1095 gchar **folder_name,
1096 TnyFolderStore **parent_folder)
1098 gchar *real_suggested_name = NULL;
1100 ModestTnyAccountStore *acc_store;
1101 TnyAccount *account;
1102 gboolean do_free = FALSE;
1104 real_suggested_name = get_next_folder_name ((const gchar *) suggested_name,
1107 /* In hildon 2.2 we always suggest the archive folder as parent */
1108 acc_store = modest_runtime_get_account_store ();
1109 account = modest_tny_account_store_get_mmc_folders_account (acc_store);
1111 suggested_folder = (TnyFolderStore *)
1112 modest_tny_account_get_special_folder (account,
1113 TNY_FOLDER_TYPE_ARCHIVE);
1114 g_object_unref (account);
1118 /* If there is not archive folder then fallback to local folders account */
1119 if (!suggested_folder) {
1121 suggested_folder = (TnyFolderStore *)
1122 modest_tny_account_store_get_local_folders_account (acc_store);
1125 result = modest_platform_run_folder_common_dialog (parent_window,
1127 _HL("ckdg_ti_new_folder"),
1128 _FM("ckdg_fi_new_folder_name"),
1129 real_suggested_name,
1136 g_object_unref (suggested_folder);
1138 g_free(real_suggested_name);
1144 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
1145 TnyFolderStore *parent_folder,
1146 const gchar *suggested_name,
1147 gchar **folder_name)
1149 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
1151 return modest_platform_run_folder_common_dialog (parent_window,
1153 _HL("ckdg_ti_rename_folder"),
1154 _HL("ckdg_fi_rename_name"),
1165 on_destroy_dialog (GtkWidget *dialog)
1167 /* This could happen when the dialogs get programatically
1168 hidden or destroyed (for example when closing the
1169 application while a dialog is being shown) */
1170 if (!GTK_IS_WIDGET (dialog))
1173 gtk_widget_destroy (dialog);
1175 if (gtk_events_pending ())
1176 gtk_main_iteration ();
1180 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
1181 const gchar *message)
1186 dialog = hildon_note_new_confirmation (parent_window, message);
1187 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1188 GTK_WINDOW (dialog), parent_window);
1190 response = gtk_dialog_run (GTK_DIALOG (dialog));
1192 on_destroy_dialog (dialog);
1198 modest_platform_run_confirmation_dialog_with_buttons (GtkWindow *parent_window,
1199 const gchar *message,
1200 const gchar *button_accept,
1201 const gchar *button_cancel)
1206 dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
1207 button_accept, GTK_RESPONSE_ACCEPT,
1208 button_cancel, GTK_RESPONSE_CANCEL,
1211 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1212 GTK_WINDOW (dialog), parent_window);
1214 response = gtk_dialog_run (GTK_DIALOG (dialog));
1216 on_destroy_dialog (dialog);
1222 modest_platform_run_information_dialog (GtkWindow *parent_window,
1223 const gchar *message,
1228 note = hildon_note_new_information (parent_window, message);
1230 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1231 GTK_WINDOW (note), parent_window);
1234 gtk_dialog_run (GTK_DIALOG (note));
1236 on_destroy_dialog (note);
1238 g_signal_connect_swapped (note,
1240 G_CALLBACK (on_destroy_dialog),
1243 gtk_widget_show_all (note);
1247 typedef struct _ConnectAndWaitData {
1249 GMainLoop *wait_loop;
1250 gboolean has_callback;
1252 } ConnectAndWaitData;
1256 quit_wait_loop (TnyAccount *account,
1257 ConnectAndWaitData *data)
1259 /* Set the has_callback to TRUE (means that the callback was
1260 executed and wake up every code waiting for cond to be
1262 g_mutex_lock (data->mutex);
1263 data->has_callback = TRUE;
1264 if (data->wait_loop)
1265 g_main_loop_quit (data->wait_loop);
1266 g_mutex_unlock (data->mutex);
1270 on_connection_status_changed (TnyAccount *account,
1271 TnyConnectionStatus status,
1274 TnyConnectionStatus conn_status;
1275 ConnectAndWaitData *data;
1277 /* Ignore if reconnecting or disconnected */
1278 conn_status = tny_account_get_connection_status (account);
1279 if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1280 conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1283 /* Remove the handler */
1284 data = (ConnectAndWaitData *) user_data;
1285 g_signal_handler_disconnect (account, data->handler);
1287 /* Quit from wait loop */
1288 quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1292 on_tny_camel_account_set_online_cb (TnyCamelAccount *account,
1297 /* Quit from wait loop */
1298 quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1302 modest_platform_connect_and_wait (GtkWindow *parent_window,
1303 TnyAccount *account)
1305 ConnectAndWaitData *data = NULL;
1306 gboolean device_online;
1308 TnyConnectionStatus conn_status;
1309 gboolean user_requested;
1311 device = modest_runtime_get_device();
1312 device_online = tny_device_is_online (device);
1314 /* Whether the connection is user requested or automatically
1315 requested, for example via D-Bus */
1316 user_requested = (parent_window) ? TRUE : FALSE;
1318 /* If there is no account check only the device status */
1323 return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device),
1324 NULL, user_requested);
1327 /* Return if the account is already connected */
1328 conn_status = tny_account_get_connection_status (account);
1329 if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1332 /* Create the helper */
1333 data = g_slice_new0 (ConnectAndWaitData);
1334 data->mutex = g_mutex_new ();
1335 data->has_callback = FALSE;
1337 /* Connect the device */
1338 if (!device_online) {
1339 /* Track account connection status changes */
1340 data->handler = g_signal_connect (account, "connection-status-changed",
1341 G_CALLBACK (on_connection_status_changed),
1343 /* Try to connect the device */
1344 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device),
1345 NULL, user_requested);
1347 /* If the device connection failed then exit */
1348 if (!device_online && data->handler)
1351 /* Force a reconnection of the account */
1352 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE,
1353 on_tny_camel_account_set_online_cb, data);
1356 /* Wait until the callback is executed */
1357 g_mutex_lock (data->mutex);
1358 if (!data->has_callback) {
1359 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1360 gdk_threads_leave ();
1361 g_mutex_unlock (data->mutex);
1362 g_main_loop_run (data->wait_loop);
1363 g_mutex_lock (data->mutex);
1364 gdk_threads_enter ();
1366 g_mutex_unlock (data->mutex);
1369 if (g_signal_handler_is_connected (account, data->handler))
1370 g_signal_handler_disconnect (account, data->handler);
1371 g_mutex_free (data->mutex);
1372 g_main_loop_unref (data->wait_loop);
1373 g_slice_free (ConnectAndWaitData, data);
1375 conn_status = tny_account_get_connection_status (account);
1376 return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1380 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1382 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1383 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) {
1384 /* This must be a maildir account, which does not require a connection: */
1389 return modest_platform_connect_and_wait (parent_window, account);
1393 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1396 return TRUE; /* Maybe it is something local. */
1398 gboolean result = TRUE;
1399 if (TNY_IS_FOLDER (folder_store)) {
1400 /* Get the folder's parent account: */
1401 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1402 if (account != NULL) {
1403 result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1404 g_object_unref (account);
1406 } else if (TNY_IS_ACCOUNT (folder_store)) {
1407 /* Use the folder store as an account: */
1408 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1415 modest_platform_create_sort_dialog (GtkWindow *parent_window)
1419 dialog = modest_hildon2_sort_dialog_new (parent_window);
1426 modest_platform_set_update_interval (guint minutes)
1428 #ifdef MODEST_HAVE_LIBALARM
1430 ModestConf *conf = modest_runtime_get_conf ();
1434 cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1436 /* Delete any existing alarm,
1437 * because we will replace it: */
1439 if (alarmd_event_del(alarm_cookie) != 0)
1440 g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1442 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1445 /* 0 means no updates: */
1450 /* Register alarm: */
1452 /* Set the interval in alarm_event_t structure: */
1453 alarm_event_t *event = alarm_event_create ();
1454 alarm_event_add_actions (event, 1);
1455 alarm_action_t *action = alarm_event_get_action (event, 0);
1456 alarm_event_set_alarm_appid (event, MODEST_ALARMD_APPID);
1457 event->alarm_time = minutes * 60; /* seconds */
1459 /* Set recurrence every few minutes: */
1460 event->recur_secs = minutes*60;
1461 event->recur_count = -1; /* Means infinite */
1463 /* Specify what should happen when the alarm happens:
1464 * It should call this D-Bus method: */
1466 action->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1467 action->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1468 action->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1469 action->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1470 action->flags = ALARM_ACTION_WHEN_TRIGGERED | ALARM_ACTION_TYPE_DBUS | ALARM_ACTION_DBUS_USE_ACTIVATION;
1472 /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if
1473 * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1474 * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails
1475 * This is why we want to use the Alarm API instead of just g_timeout_add().
1476 * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1477 * ALARM_EVENT_CONNECTED will prevent the alarm from being called in case that the device is offline
1479 event->flags = ALARM_EVENT_CONNECTED;
1481 alarm_cookie = alarmd_event_add (event);
1484 alarm_event_delete (event);
1486 /* Store the alarm ID in GConf, so we can remove it later:
1487 * This is apparently valid between application instances. */
1488 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1490 if (!alarm_cookie) {
1492 g_debug ("Error setting alarm event. \n");
1496 #endif /* MODEST_HAVE_LIBALARM */
1501 modest_platform_push_email_notification(void)
1503 gboolean screen_on, app_in_foreground;
1505 /* Get the window status */
1506 app_in_foreground = hildon_program_get_is_topmost (hildon_program_get_instance ());
1508 screen_on = modest_window_mgr_screen_is_on (modest_runtime_get_window_mgr ());
1510 /* If the screen is on and the app is in the
1511 foreground we don't show anything */
1512 if (!(screen_on && app_in_foreground)) {
1514 _modest_platform_play_email_tone ();
1516 /* Activate LED. This must be deactivated by
1517 modest_platform_remove_new_mail_notifications */
1518 #ifdef MODEST_HAVE_MCE
1519 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1523 MCE_ACTIVATE_LED_PATTERN,
1525 DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1532 modest_platform_on_new_headers_received (TnyList *header_list,
1533 gboolean show_visual)
1535 g_return_if_fail (TNY_IS_LIST (header_list));
1537 if (tny_list_get_length (header_list) < 1)
1540 /* If the window is in the foreground don't do anything */
1541 if (hildon_program_get_is_topmost (hildon_program_get_instance ()))
1544 #ifdef MODEST_HAVE_HILDON_NOTIFY
1545 /* For any other case issue a notification */
1546 HildonNotification *notification;
1548 GSList *notifications_list = NULL;
1550 /* Get previous notifications ids */
1551 notifications_list = modest_conf_get_list (modest_runtime_get_conf (),
1552 MODEST_CONF_NOTIFICATION_IDS,
1553 MODEST_CONF_VALUE_INT, NULL);
1555 iter = tny_list_create_iterator (header_list);
1556 while (!tny_iterator_is_done (iter)) {
1557 gchar *url = NULL, *display_address = NULL;
1558 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1559 TnyFolder *folder = tny_header_get_folder (header);
1560 gboolean first_notification = TRUE;
1564 display_address = tny_header_dup_from (header);
1565 /* string is changed in-place */
1566 modest_text_utils_get_display_address (display_address);
1568 str = tny_header_dup_subject (header);
1569 notification = hildon_notification_new (display_address,
1571 "qgn_list_messagin",
1572 MODEST_NOTIFICATION_CATEGORY);
1574 /* Create the message URL */
1575 str = tny_header_dup_uid (header);
1576 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder),
1580 hildon_notification_add_dbus_action(notification,
1583 MODEST_DBUS_SERVICE,
1586 MODEST_DBUS_METHOD_OPEN_MESSAGE,
1590 /* Play sound if the user wants. Show the LED
1591 pattern. Show and play just one */
1592 if (G_UNLIKELY (first_notification)) {
1593 TnyAccount *account;
1595 first_notification = FALSE;
1597 /* Set the led pattern */
1598 notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1600 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1602 MODEST_NEW_MAIL_LIGHTING_PATTERN);
1604 /* Set the account of the headers */
1605 account = tny_folder_get_account (folder);
1607 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1609 tny_account_get_id (account));
1610 g_object_unref (account);
1614 /* Notify. We need to do this in an idle because this function
1615 could be called from a thread */
1616 if (!notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL)) {
1617 g_warning ("Failed to send notification");
1620 /* Save id in the list */
1621 g_object_get(G_OBJECT(notification), "id", ¬if_id, NULL);
1622 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1623 /* We don't listen for the "closed" signal, because we
1624 don't care about if the notification was removed or
1625 not to store the list in gconf */
1627 /* Free & carry on */
1628 g_free (display_address);
1630 g_object_unref (folder);
1631 g_object_unref (header);
1632 tny_iterator_next (iter);
1634 g_object_unref (iter);
1637 modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS,
1638 notifications_list, MODEST_CONF_VALUE_INT, NULL);
1640 g_slist_free (notifications_list);
1642 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1646 modest_platform_remove_new_mail_notifications (gboolean only_visuals)
1649 #ifdef MODEST_HAVE_MCE
1650 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1654 MCE_DEACTIVATE_LED_PATTERN,
1656 DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1662 #ifdef MODEST_HAVE_HILDON_NOTIFY
1663 GSList *notif_list = NULL;
1665 /* Get previous notifications ids */
1666 notif_list = modest_conf_get_list (modest_runtime_get_conf (),
1667 MODEST_CONF_NOTIFICATION_IDS,
1668 MODEST_CONF_VALUE_INT, NULL);
1670 while (notif_list) {
1672 NotifyNotification *notif;
1674 /* Nasty HACK to remove the notifications, set the id
1675 of the existing ones and then close them */
1676 notif_id = GPOINTER_TO_INT(notif_list->data);
1677 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1678 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1680 /* Close the notification, note that some ids could be
1681 already invalid, but we don't care because it does
1683 notify_notification_close(notif, NULL);
1684 g_object_unref(notif);
1686 /* Delete the link, it's like going to the next */
1687 notif_list = g_slist_delete_link (notif_list, notif_list);
1691 modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS,
1692 notif_list, MODEST_CONF_VALUE_INT, NULL);
1694 g_slist_free (notif_list);
1696 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1702 modest_platform_get_global_settings_dialog ()
1704 return modest_hildon2_global_settings_dialog_new ();
1708 modest_platform_show_help (GtkWindow *parent_window,
1709 const gchar *help_id)
1715 modest_platform_show_search_messages (GtkWindow *parent_window)
1717 osso_return_t result = OSSO_ERROR;
1719 result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1720 "osso_global_search",
1721 "search_email", NULL, DBUS_TYPE_INVALID);
1723 if (result != OSSO_OK) {
1724 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1729 modest_platform_show_addressbook (GtkWindow *parent_window)
1731 osso_return_t result = OSSO_ERROR;
1733 result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1735 "top_application", NULL, DBUS_TYPE_INVALID);
1737 if (result != OSSO_OK) {
1738 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1743 modest_platform_create_folder_view_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1745 GtkWidget *widget = modest_folder_view_new_full (query, do_refresh);
1747 /* Show one account by default */
1748 modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1749 MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1751 /* Restore settings */
1752 modest_widget_memory_restore (modest_runtime_get_conf(),
1754 MODEST_CONF_FOLDER_VIEW_KEY);
1760 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1762 return modest_platform_create_folder_view_full (query, TRUE);
1766 banner_finish (gpointer data, GObject *object)
1768 ModestWindowMgr *mgr = (ModestWindowMgr *) data;
1769 modest_window_mgr_unregister_banner (mgr);
1770 g_object_unref (mgr);
1774 modest_platform_information_banner (GtkWidget *parent,
1775 const gchar *icon_name,
1778 GtkWidget *banner, *banner_parent = NULL;
1779 ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1781 if (modest_window_mgr_get_num_windows (mgr) == 0)
1784 if (parent && GTK_IS_WINDOW (parent)) {
1785 /* If the window is the active one then show the
1786 banner on top of this window */
1787 if (gtk_window_is_active (GTK_WINDOW (parent)))
1788 banner_parent = parent;
1789 /* If the window is not the topmost but it's visible
1790 (it's minimized for example) then show the banner
1792 else if (GTK_WIDGET_VISIBLE (parent))
1793 banner_parent = NULL;
1794 /* If the window is hidden (like the main window when
1795 running in the background) then do not show
1802 banner = hildon_banner_show_information (banner_parent, icon_name, text);
1804 modest_window_mgr_register_banner (mgr);
1806 g_object_weak_ref ((GObject *) banner, banner_finish, mgr);
1810 modest_platform_information_banner_with_timeout (GtkWidget *parent,
1811 const gchar *icon_name,
1817 if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()) == 0)
1820 banner = hildon_banner_show_information (parent, icon_name, text);
1821 hildon_banner_set_timeout(HILDON_BANNER(banner), timeout);
1825 modest_platform_animation_banner (GtkWidget *parent,
1826 const gchar *animation_name,
1829 GtkWidget *inf_note = NULL;
1831 g_return_val_if_fail (text != NULL, NULL);
1833 if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()) == 0)
1836 /* If the parent is not visible then do not show */
1837 if (parent && !GTK_WIDGET_VISIBLE (parent))
1840 inf_note = hildon_banner_show_animation (parent, animation_name, text);
1848 TnyAccount *account;
1851 } CheckAccountIdleData;
1853 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1856 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1858 gboolean stop_trying = FALSE;
1859 g_return_val_if_fail (data && data->account, FALSE);
1861 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1862 tny_account_get_connection_status (data->account));
1864 if (data && data->account &&
1865 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1866 * after which the account is likely to be usable, or never likely to be usable soon: */
1867 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1869 data->is_online = TRUE;
1873 /* Give up if we have tried too many times: */
1874 if (data->count_tries >= NUMBER_OF_TRIES) {
1877 /* Wait for another timeout: */
1878 ++(data->count_tries);
1883 /* Allow the function that requested this idle callback to continue: */
1885 g_main_loop_quit (data->loop);
1888 g_object_unref (data->account);
1890 return FALSE; /* Don't call this again. */
1892 return TRUE; /* Call this timeout callback again. */
1896 /* Return TRUE immediately if the account is already online,
1897 * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as
1898 * soon as the account is online, or FALSE if the account does
1899 * not become online in the NUMBER_OF_TRIES seconds.
1900 * This is useful when the D-Bus method was run immediately after
1901 * the application was started (when using D-Bus activation),
1902 * because the account usually takes a short time to go online.
1903 * The return value is maybe not very useful.
1906 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1910 g_return_val_if_fail (account, FALSE);
1912 if (!tny_device_is_online (modest_runtime_get_device())) {
1913 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1917 /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1918 * so we avoid wait unnecessarily: */
1919 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1922 /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED,
1923 * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that
1924 * we want to avoid. */
1925 if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1928 /* This blocks on the result: */
1929 CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1930 data->is_online = FALSE;
1931 data->account = account;
1932 g_object_ref (data->account);
1933 data->count_tries = 0;
1935 GMainContext *context = NULL; /* g_main_context_new (); */
1936 data->loop = g_main_loop_new (context, FALSE /* not running */);
1938 g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1940 /* This main loop will run until the idle handler has stopped it: */
1941 g_main_loop_run (data->loop);
1943 g_main_loop_unref (data->loop);
1944 /* g_main_context_unref (context); */
1946 is_online = data->is_online;
1947 g_slice_free (CheckAccountIdleData, data);
1955 on_cert_dialog_response (GtkDialog *dialog, gint response_id, const gchar* cert)
1957 /* GTK_RESPONSE_HELP means we need to show the certificate */
1958 if (response_id == GTK_RESPONSE_APPLY) {
1962 /* Do not close the dialog */
1963 g_signal_stop_emission_by_name (dialog, "response");
1965 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);
1966 note = hildon_note_new_information (NULL, msg);
1967 gtk_dialog_run (GTK_DIALOG(note));
1968 gtk_widget_destroy (note);
1974 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1975 const gchar *certificate)
1980 HildonWindowStack *stack;
1982 stack = hildon_window_stack_get_default ();
1983 win = MODEST_WINDOW (hildon_window_stack_peek (stack));
1986 g_warning ("%s: don't show dialogs if there's no window shown; assuming 'Cancel'",
1991 gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1994 /* We use GTK_RESPONSE_APPLY because we want the button in the
1995 middle of OK and CANCEL the same as the browser does for
1996 example. With GTK_RESPONSE_HELP the view button is aligned
1997 to the left while the other two to the right */
1998 note = hildon_note_new_confirmation_add_buttons (
2001 _HL("wdgt_bd_yes"), GTK_RESPONSE_OK,
2002 _HL("wdgt_bd_view"), GTK_RESPONSE_APPLY, /* abusing this... */
2003 _HL("wdgt_bd_no"), GTK_RESPONSE_CANCEL,
2006 g_signal_connect (G_OBJECT(note), "response",
2007 G_CALLBACK(on_cert_dialog_response),
2008 (gpointer) certificate);
2010 response = gtk_dialog_run(GTK_DIALOG(note));
2012 on_destroy_dialog (note);
2015 return response == GTK_RESPONSE_OK;
2019 modest_platform_run_alert_dialog (const gchar* prompt,
2020 gboolean is_question)
2022 ModestWindow *top_win;
2023 HildonWindowStack *stack;
2025 stack = hildon_window_stack_get_default ();
2026 top_win = MODEST_WINDOW (hildon_window_stack_peek (stack));
2029 g_warning ("%s: don't show dialogs if there's no window shown; assuming 'Cancel'",
2034 gboolean retval = TRUE;
2036 /* The Tinymail documentation says that we should show Yes and No buttons,
2037 * when it is a question.
2038 * Obviously, we need tinymail to use more specific error codes instead,
2039 * so we know what buttons to show. */
2040 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (top_win),
2042 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
2043 GTK_WINDOW (dialog), GTK_WINDOW (top_win));
2045 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
2046 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
2048 on_destroy_dialog (dialog);
2050 /* Just show the error text and use the default response: */
2051 modest_platform_run_information_dialog (GTK_WINDOW (top_win),
2059 GtkWindow *parent_window;
2060 ModestConnectedPerformer callback;
2061 TnyAccount *account;
2068 on_went_online_info_free (OnWentOnlineInfo *info)
2070 /* And if we cleanup, we DO cleanup :-) */
2073 g_object_unref (info->device);
2076 if (info->parent_window)
2077 g_object_unref (info->parent_window);
2079 g_object_unref (info->account);
2081 g_slice_free (OnWentOnlineInfo, info);
2083 /* We're done ... */
2089 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
2091 OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
2093 /* Now it's really time to callback to the caller. If going online didn't succeed,
2094 * err will be set. We don't free it, Tinymail does that! If a cancel happened,
2095 * canceled will be set. Etcetera etcetera. */
2097 if (info->callback) {
2098 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
2101 /* This is our last call, we must cleanup here if we didn't yet do that */
2102 on_went_online_info_free (info);
2109 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
2111 OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
2112 info->iap = g_strdup (iap_id);
2114 if (canceled || err || !info->account) {
2116 /* If there's a problem or if there's no account (then that's it for us, we callback
2117 * the caller's callback now. He'll have to handle err or canceled, of course.
2118 * We are not really online, as the account is not really online here ... */
2120 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
2121 * this info. We don't cleanup err, Tinymail does that! */
2123 if (info->callback) {
2125 /* info->account can be NULL here, this means that the user did not
2126 * provide a nice account instance. We'll assume that the user knows
2127 * what he's doing and is happy with just the device going online.
2129 * We can't do magic, we don't know what account the user wants to
2130 * see going online. So just the device goes online, end of story */
2132 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
2135 } else if (info->account) {
2137 /* If there's no problem and if we have an account, we'll put the account
2138 * online too. When done, the callback of bringing the account online
2139 * will callback the caller's callback. This is the most normal case. */
2141 info->device = TNY_DEVICE (g_object_ref (device));
2143 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
2144 on_account_went_online, info);
2146 /* The on_account_went_online cb frees up the info, go look if you
2147 * don't believe me! (so we return here) */
2152 /* We cleanup if we are not bringing the account online too */
2153 on_went_online_info_free (info);
2159 modest_platform_connect_and_perform (GtkWindow *parent_window,
2161 TnyAccount *account,
2162 ModestConnectedPerformer callback,
2165 gboolean device_online;
2167 TnyConnectionStatus conn_status;
2168 OnWentOnlineInfo *info;
2170 device = modest_runtime_get_device();
2171 device_online = tny_device_is_online (device);
2173 /* If there is no account check only the device status */
2176 if (device_online) {
2178 /* We promise to instantly perform the callback, so ... */
2180 callback (FALSE, NULL, parent_window, account, user_data);
2185 info = g_slice_new0 (OnWentOnlineInfo);
2188 info->device = NULL;
2189 info->account = NULL;
2192 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2194 info->parent_window = NULL;
2195 info->user_data = user_data;
2196 info->callback = callback;
2198 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2199 force, on_conic_device_went_online,
2202 /* We'll cleanup in on_conic_device_went_online */
2205 /* The other code has no more reason to run. This is all that we can do for the
2206 * caller (he should have given us a nice and clean account instance!). We
2207 * can't do magic, we don't know what account he intends to bring online. So
2208 * we'll just bring the device online (and await his false bug report). */
2214 /* Return if the account is already connected */
2216 conn_status = tny_account_get_connection_status (account);
2217 if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
2219 /* We promise to instantly perform the callback, so ... */
2221 callback (FALSE, NULL, parent_window, account, user_data);
2227 /* Else, we are in a state that requires that we go online before we
2228 * call the caller's callback. */
2230 info = g_slice_new0 (OnWentOnlineInfo);
2232 info->device = NULL;
2234 info->account = TNY_ACCOUNT (g_object_ref (account));
2237 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2239 info->parent_window = NULL;
2241 /* So we'll put the callback away for later ... */
2243 info->user_data = user_data;
2244 info->callback = callback;
2246 if (!device_online) {
2248 /* If also the device is offline, then we connect both the device
2249 * and the account */
2251 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2252 force, on_conic_device_went_online,
2257 /* If the device is online, we'll just connect the account */
2259 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE,
2260 on_account_went_online, info);
2263 /* The info gets freed by on_account_went_online or on_conic_device_went_online
2264 * in both situations, go look if you don't believe me! */
2270 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window,
2272 TnyFolderStore *folder_store,
2273 ModestConnectedPerformer callback,
2276 TnyAccount *account = NULL;
2278 if (!folder_store ||
2279 (TNY_IS_MERGE_FOLDER (folder_store) &&
2280 (tny_folder_get_folder_type (TNY_FOLDER(folder_store)) == TNY_FOLDER_TYPE_OUTBOX))) {
2282 /* We promise to instantly perform the callback, so ... */
2284 GError *error = NULL;
2285 g_set_error (&error, TNY_ERROR_DOMAIN, TNY_SERVICE_ERROR_UNKNOWN,
2286 "Unable to move or not found folder");
2287 callback (FALSE, error, parent_window, NULL, user_data);
2288 g_error_free (error);
2292 } else if (TNY_IS_FOLDER (folder_store)) {
2293 /* Get the folder's parent account: */
2294 account = tny_folder_get_account (TNY_FOLDER (folder_store));
2295 } else if (TNY_IS_ACCOUNT (folder_store)) {
2296 /* Use the folder store as an account: */
2297 account = TNY_ACCOUNT (g_object_ref (folder_store));
2300 if (account && (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE)) {
2301 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) {
2302 /* No need to connect a local account */
2304 callback (FALSE, NULL, parent_window, account, user_data);
2309 modest_platform_connect_and_perform (parent_window, force, account, callback, user_data);
2313 g_object_unref (account);
2317 src_account_connect_performer (gboolean canceled,
2319 GtkWindow *parent_window,
2320 TnyAccount *src_account,
2323 DoubleConnectionInfo *info = (DoubleConnectionInfo *) user_data;
2325 if (canceled || err) {
2326 /* If there was any error call the user callback */
2327 info->callback (canceled, err, parent_window, src_account, info->data);
2329 /* Connect the destination account */
2330 modest_platform_connect_if_remote_and_perform (parent_window, TRUE,
2331 TNY_FOLDER_STORE (info->dst_account),
2332 info->callback, info->data);
2335 /* Free the info object */
2336 g_object_unref (info->dst_account);
2337 g_slice_free (DoubleConnectionInfo, info);
2342 modest_platform_double_connect_and_perform (GtkWindow *parent_window,
2344 TnyFolderStore *folder_store,
2345 DoubleConnectionInfo *connect_info)
2347 modest_platform_connect_if_remote_and_perform(parent_window,
2350 src_account_connect_performer,
2355 modest_platform_get_account_settings_wizard (void)
2357 ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new ();
2359 return GTK_WIDGET (dialog);
2363 modest_platform_get_current_connection (void)
2365 TnyDevice *device = NULL;
2366 ModestConnectedVia retval = MODEST_CONNECTED_VIA_ANY;
2368 device = modest_runtime_get_device ();
2370 if (!tny_device_is_online (device))
2371 return MODEST_CONNECTED_VIA_ANY;
2373 #ifdef MODEST_HAVE_CONIC
2375 const gchar *iap_id = tny_maemo_conic_device_get_current_iap_id (TNY_MAEMO_CONIC_DEVICE (device));
2377 ConIcIap *iap = tny_maemo_conic_device_get_iap (
2378 TNY_MAEMO_CONIC_DEVICE (device), iap_id);
2379 const gchar *bearer_type = con_ic_iap_get_bearer_type (iap);
2381 if (!strcmp (bearer_type, CON_IC_BEARER_WLAN_INFRA) ||
2382 !strcmp (bearer_type, CON_IC_BEARER_WLAN_ADHOC) ||
2383 !strcmp (bearer_type, "WIMAX")) {
2384 retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX;
2386 retval = MODEST_CONNECTED_VIA_ANY;
2389 g_object_unref (iap);
2392 retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX; /* assume WLAN (fast) internet */
2393 #endif /* MODEST_HAVE_CONIC */
2400 modest_platform_check_memory_low (ModestWindow *win,
2405 /* are we in low memory state? */
2406 lowmem = osso_mem_in_lowmem_state () ? TRUE : FALSE;
2408 if (win && lowmem && visuals)
2409 modest_platform_run_information_dialog (
2411 _KR("memr_ib_operation_disabled"),
2415 g_debug ("%s: low memory reached. disallowing some operations",
2422 modest_platform_run_folder_details_dialog (GtkWindow *parent_window,
2428 dialog = modest_hildon2_details_dialog_new_with_folder (parent_window, folder);
2431 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
2432 GTK_WINDOW (dialog),
2434 gtk_widget_show_all (dialog);
2436 g_signal_connect_swapped (dialog, "response",
2437 G_CALLBACK (gtk_widget_destroy),
2441 typedef struct _HeaderDetailsGetSizeInfo {
2445 } HeaderDetailsGetSizeInfo;
2448 header_details_dialog_destroy (gpointer userdata,
2451 HeaderDetailsGetSizeInfo *info = (HeaderDetailsGetSizeInfo *) userdata;
2453 info->dialog = NULL;
2457 idle_get_mime_part_size_cb (gpointer userdata)
2459 HeaderDetailsGetSizeInfo *info = (HeaderDetailsGetSizeInfo *) userdata;
2460 gdk_threads_enter ();
2462 if (info->dialog && GTK_WIDGET_VISIBLE (info->dialog)) {
2463 modest_details_dialog_set_message_size (MODEST_DETAILS_DIALOG (info->dialog),
2468 g_object_weak_unref (G_OBJECT (info->dialog), header_details_dialog_destroy, info);
2469 info->dialog = NULL;
2471 g_object_unref (info->part);
2472 g_slice_free (HeaderDetailsGetSizeInfo, info);
2474 gdk_threads_leave ();
2480 get_mime_part_size_thread (gpointer thr_user_data)
2482 HeaderDetailsGetSizeInfo *info = (HeaderDetailsGetSizeInfo *) thr_user_data;
2484 TnyStream *count_stream;
2486 count_stream = modest_count_stream_new ();
2487 result = tny_mime_part_decode_to_stream (info->part, count_stream, NULL);
2488 info->total = modest_count_stream_get_count(MODEST_COUNT_STREAM (count_stream));
2489 if (info->total == 0) {
2490 modest_count_stream_reset_count(MODEST_COUNT_STREAM (count_stream));
2491 result = tny_mime_part_write_to_stream (info->part, count_stream, NULL);
2492 info->total = modest_count_stream_get_count(MODEST_COUNT_STREAM (count_stream));
2495 /* if there was an error, don't set the size (this is pretty uncommon) */
2497 g_warning ("%s: error while writing mime part to stream\n", __FUNCTION__);
2499 g_idle_add (idle_get_mime_part_size_cb, info);
2505 modest_platform_run_header_details_dialog (GtkWindow *parent_window,
2507 gboolean async_get_size,
2513 dialog = modest_hildon2_details_dialog_new_with_header (parent_window, header, !async_get_size);
2515 if (async_get_size && msg && TNY_IS_MSG (msg)) {
2516 HeaderDetailsGetSizeInfo *info;
2517 info = g_slice_new (HeaderDetailsGetSizeInfo);
2518 info->dialog = dialog;
2520 info->part = TNY_MIME_PART (g_object_ref (msg));
2522 g_object_weak_ref (G_OBJECT (dialog), header_details_dialog_destroy, info);
2523 g_thread_create (get_mime_part_size_thread, info, FALSE, NULL);
2527 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
2528 GTK_WINDOW (dialog),
2530 gtk_widget_show_all (dialog);
2532 g_signal_connect_swapped (dialog, "response",
2533 G_CALLBACK (gtk_widget_destroy),
2538 modest_platform_get_osso_context (void)
2540 return modest_maemo_utils_get_osso_context ();
2544 _modest_platform_play_email_tone (void)
2547 gint mail_volume_int;
2549 ca_context *ca_con = NULL;
2550 ca_proplist *pl = NULL;
2552 #ifdef MODEST_USE_PROFILE
2553 gchar *active_profile;
2556 active_profile = profile_get_profile ();
2557 mail_tone = profile_get_value (active_profile, PROFILE_MAIL_TONE);
2558 mail_volume = profile_get_value (active_profile, PROFILE_MAIL_VOLUME);
2559 mail_volume_int = profile_parse_int (mail_volume);
2560 g_free (mail_volume);
2561 g_free (active_profile);
2563 mail_tone = MAIL_TONE;
2564 mail_volume_int = 100;
2567 if (mail_tone && !strstr (mail_tone, "/")) {
2570 tmp = g_strconcat ("/usr/share/sounds", mail_tone, NULL);
2575 if (mail_volume_int > 0) {
2577 if ((ret = ca_context_create(&ca_con)) != CA_SUCCESS) {
2578 g_warning("ca_context_create: %s\n", ca_strerror(ret));
2582 if ((ret = ca_context_open(ca_con)) != CA_SUCCESS) {
2583 g_warning("ca_context_open: %s\n", ca_strerror(ret));
2584 ca_context_destroy(ca_con);
2588 ca_proplist_create(&pl);
2589 ca_proplist_sets(pl, CA_PROP_MEDIA_FILENAME, mail_tone);
2590 ca_proplist_setf(pl, CA_PROP_CANBERRA_VOLUME, "%f", (gfloat) mail_volume_int);
2592 ret = ca_context_play_full(ca_con, 0, pl, NULL, NULL);
2593 g_debug("ca_context_play_full (vol %f): %s\n", (gfloat) mail_volume_int, ca_strerror(ret));
2595 ca_proplist_destroy(pl);
2596 ca_context_destroy(ca_con);
2602 #define MOVE_TO_DIALOG_FOLDER_VIEW "folder-view"
2603 #define MOVE_TO_DIALOG_BACK_BUTTON "back-button"
2604 #define MOVE_TO_DIALOG_ACTION_BUTTON "action-button"
2605 #define MOVE_TO_DIALOG_SHOWING_FOLDERS "showing-folders"
2606 #define MOVE_TO_DIALOG_PANNABLE "pannable"
2607 #define MOVE_TO_FOLDER_SEPARATOR "/"
2610 move_to_dialog_set_selected_folder_store (GtkWidget *dialog,
2611 TnyFolderStore *folder_store)
2613 GtkWidget *action_button;
2614 GtkWidget *image = NULL;
2615 TnyAccount *account;
2616 gchar *account_name = NULL, *short_name = NULL;
2618 action_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2620 /* Get account name */
2621 if (TNY_IS_FOLDER (folder_store))
2622 account = tny_folder_get_account (TNY_FOLDER (folder_store));
2624 account = g_object_ref (folder_store);
2626 if (modest_tny_account_is_virtual_local_folders (account))
2627 account_name = modest_conf_get_string (modest_runtime_get_conf(),
2628 MODEST_CONF_DEVICE_NAME, NULL);
2631 account_name = g_strdup (tny_account_get_name (account));
2633 g_object_unref (account);
2635 /* Set title of button: account or folder name */
2636 if (TNY_IS_FOLDER (folder_store))
2637 short_name = folder_store_get_display_name (folder_store);
2639 short_name = g_strdup (account_name);
2641 hildon_button_set_title (HILDON_BUTTON (action_button), short_name);
2643 /* Set value of button, folder full name */
2644 if (TNY_IS_CAMEL_FOLDER (folder_store)) {
2645 const gchar *camel_full_name;
2646 gchar *last_slash, *full_name;
2648 camel_full_name = tny_camel_folder_get_full_name (TNY_CAMEL_FOLDER (folder_store));
2649 last_slash = g_strrstr (camel_full_name, "/");
2651 gchar *prefix = g_strndup (camel_full_name, last_slash - camel_full_name + 1);
2652 full_name = g_strconcat (account_name, MOVE_TO_FOLDER_SEPARATOR, prefix, short_name, NULL);
2655 full_name = g_strconcat (account_name, MOVE_TO_FOLDER_SEPARATOR,
2659 hildon_button_set_value (HILDON_BUTTON (action_button), full_name);
2662 g_free (account_name);
2663 g_free (short_name);
2665 /* Set image for the button */
2666 image = get_image_for_folder_store (folder_store, MODEST_ICON_SIZE_BIG);
2668 hildon_button_set_image (HILDON_BUTTON (action_button), image);
2672 move_to_dialog_show_accounts (GtkWidget *dialog)
2674 GtkWidget *back_button;
2675 GtkWidget *folder_view;
2676 GtkWidget *pannable;
2677 GtkWidget *action_button;
2679 back_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON));
2680 action_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2681 folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2682 pannable = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE));
2684 gtk_widget_set_sensitive (back_button, FALSE);
2685 gtk_widget_set_sensitive (action_button, FALSE);
2687 /* Need to set this here, otherwise callbacks called because
2688 of filtering won't perform correctly */
2689 g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS, GINT_TO_POINTER (FALSE));
2691 /* Reset action button */
2692 hildon_button_set_title (HILDON_BUTTON (action_button), NULL);
2693 hildon_button_set_value (HILDON_BUTTON (action_button), NULL);
2694 hildon_button_set_image (HILDON_BUTTON (action_button), NULL);
2696 modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW (folder_view), NULL);
2697 modest_folder_view_show_non_move_folders (MODEST_FOLDER_VIEW (folder_view), TRUE);
2698 modest_folder_view_set_style (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
2699 modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view),
2700 MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2701 modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view),
2702 MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2703 modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view),
2704 MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS);
2705 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2706 MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS);
2707 hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (pannable), 0, 0);
2711 move_to_dialog_show_folders (GtkWidget *dialog, TnyFolderStore *folder_store)
2713 GtkWidget *back_button;
2714 GtkWidget *folder_view;
2715 TnyAccount *account;
2716 const gchar *account_id;
2717 GtkWidget *pannable;
2718 GtkWidget *action_button;
2721 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON));
2723 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2725 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2727 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE));
2729 gtk_widget_set_sensitive (back_button, TRUE);
2730 gtk_widget_set_sensitive (action_button, TRUE);
2732 /* Need to set this here, otherwise callbacks called because
2733 of filtering won't perform correctly */
2734 g_object_set_data (G_OBJECT (dialog),
2735 MOVE_TO_DIALOG_SHOWING_FOLDERS,
2736 GINT_TO_POINTER (TRUE));
2738 account = TNY_ACCOUNT (folder_store);
2739 if (modest_tny_account_is_virtual_local_folders (account)) {
2740 account_id = tny_account_get_id (account);
2741 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2742 MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2743 } else if (modest_tny_account_is_memory_card_account (account)) {
2744 account_id = tny_account_get_id (account);
2745 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2746 MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2748 account_id = tny_account_get_id (account);
2749 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2750 MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2751 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2752 MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2755 move_to_dialog_set_selected_folder_store (dialog, folder_store);
2756 modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW (folder_view),
2759 modest_folder_view_show_non_move_folders (MODEST_FOLDER_VIEW (folder_view), FALSE);
2760 modest_folder_view_set_style (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2761 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS);
2762 modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS);
2763 hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (pannable), 0, 0);
2767 on_move_to_dialog_back_clicked (GtkButton *button,
2770 GtkWidget *dialog = (GtkWidget *) userdata;
2772 /* Back to show accounts */
2773 move_to_dialog_show_accounts (dialog);
2777 on_move_to_dialog_row_activated (GtkTreeView *tree_view,
2779 GtkTreeViewColumn *column,
2782 TnyFolderStore *selected = NULL;
2784 GtkWidget *folder_view;
2785 gboolean showing_folders;
2787 dialog = (GtkWidget *) user_data;
2788 showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog),
2789 MOVE_TO_DIALOG_SHOWING_FOLDERS));
2791 folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog),
2792 MOVE_TO_DIALOG_FOLDER_VIEW));
2794 selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2798 if (!showing_folders) {
2799 gboolean valid = TRUE;
2801 if (TNY_IS_ACCOUNT (selected) &&
2802 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (selected))) {
2803 ModestProtocolType protocol_type;
2805 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (selected));
2806 valid = !modest_protocol_registry_protocol_type_has_tag
2807 (modest_runtime_get_protocol_registry (),
2809 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2812 move_to_dialog_show_folders (dialog, selected);
2814 move_to_dialog_set_selected_folder_store (dialog, selected);
2819 on_move_to_dialog_selection_changed (GtkTreeSelection *selection,
2822 gboolean showing_folders;
2825 dialog = (GtkWidget *) user_data;
2826 showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS));
2827 if (showing_folders) {
2828 TnyFolderStore *selected;
2829 GtkWidget *folder_view;
2831 folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2832 selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2835 move_to_dialog_set_selected_folder_store (dialog, selected);
2836 g_object_unref (selected);
2842 on_move_to_dialog_action_clicked (GtkButton *selection,
2846 gboolean showing_folders;
2848 dialog = (GtkWidget *) user_data;
2849 showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS));
2850 if (showing_folders) {
2851 TnyFolderStore *selected;
2852 GtkWidget *folder_view;
2854 folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2855 selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2858 gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
2859 g_object_unref (selected);
2865 move_to_dialog_activity_changed (ModestFolderView *folder_view, gboolean activity, GtkDialog *dialog)
2867 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (dialog), activity?1:0);
2871 modest_platform_create_move_to_dialog (GtkWindow *parent_window,
2872 GtkWidget **folder_view)
2874 GtkWidget *dialog, *folder_view_container;
2876 GtkWidget *buttons_hbox;
2877 GtkWidget *back_button;
2878 GdkPixbuf *back_pixbuf;
2879 GtkWidget *top_vbox;
2880 GtkWidget *action_button;
2881 GtkTreeSelection *selection;
2883 /* Create dialog. We cannot use a touch selector because we
2884 need to use here the folder view widget directly */
2885 dialog = gtk_dialog_new_with_buttons (_("mcen_ti_moveto_folders_title"),
2886 GTK_WINDOW (parent_window),
2887 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR |
2888 GTK_DIALOG_DESTROY_WITH_PARENT,
2889 _HL("wdgt_bd_new"), MODEST_GTK_RESPONSE_NEW_FOLDER,
2892 align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
2893 gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_NONE);
2894 top_vbox = gtk_vbox_new (FALSE, MODEST_MARGIN_HALF);
2896 /* Create folder view */
2897 *folder_view = modest_platform_create_folder_view_full (NULL, FALSE);
2898 g_signal_connect (G_OBJECT (*folder_view), "activity-changed", G_CALLBACK (move_to_dialog_activity_changed),
2901 modest_folder_view_set_cell_style (MODEST_FOLDER_VIEW (*folder_view),
2902 MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT);
2903 modest_folder_view_show_message_count (MODEST_FOLDER_VIEW (*folder_view),
2905 tny_account_store_view_set_account_store (TNY_ACCOUNT_STORE_VIEW (*folder_view),
2906 (TnyAccountStore *) modest_runtime_get_account_store ());
2908 buttons_hbox = gtk_hbox_new (FALSE, MODEST_MARGIN_HALF);
2909 back_button = gtk_button_new ();
2910 back_pixbuf = modest_platform_get_icon (_FM("filemanager_folder_up"), MODEST_ICON_SIZE_BIG);
2912 gtk_button_set_image (GTK_BUTTON (back_button), gtk_image_new_from_pixbuf (back_pixbuf));
2913 g_object_unref (back_pixbuf);
2916 action_button = hildon_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
2917 HILDON_BUTTON_ARRANGEMENT_VERTICAL);
2918 gtk_button_set_alignment (GTK_BUTTON (action_button), 0.0, 0.5);
2920 gtk_box_pack_start (GTK_BOX (buttons_hbox), back_button, FALSE, FALSE, 0);
2921 gtk_box_pack_start (GTK_BOX (buttons_hbox), action_button, TRUE, TRUE, 0);
2922 gtk_widget_set_sensitive (GTK_WIDGET (back_button), FALSE);
2923 gtk_widget_set_sensitive (GTK_WIDGET (action_button), FALSE);
2924 gtk_box_pack_start (GTK_BOX (top_vbox), buttons_hbox, FALSE, FALSE, 0);
2926 /* Create pannable and add it to the dialog */
2927 folder_view_container = hildon_pannable_area_new ();
2928 gtk_container_add (GTK_CONTAINER (folder_view_container), *folder_view);
2929 gtk_box_pack_start (GTK_BOX (top_vbox), folder_view_container, TRUE, TRUE, 0);
2931 gtk_container_add (GTK_CONTAINER (align), top_vbox);
2932 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), align, TRUE, TRUE, 0);
2934 gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 300);
2936 gtk_widget_show (GTK_DIALOG (dialog)->vbox);
2937 gtk_widget_show (folder_view_container);
2938 gtk_widget_show (align);
2939 gtk_widget_show (top_vbox);
2940 gtk_widget_show (*folder_view);
2941 gtk_widget_show_all (back_button);
2942 gtk_widget_show (action_button);
2943 gtk_widget_show (buttons_hbox);
2944 gtk_widget_show (dialog);
2946 g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW, *folder_view);
2947 g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON, back_button);
2948 g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON, action_button);
2949 g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE, folder_view_container);
2951 /* Simulate the behaviour of a HildonPickerDialog by emitting
2952 a response when a folder is selected */
2953 g_signal_connect (*folder_view, "row-activated",
2954 G_CALLBACK (on_move_to_dialog_row_activated),
2957 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (*folder_view));
2958 g_signal_connect (selection, "changed",
2959 G_CALLBACK (on_move_to_dialog_selection_changed),
2962 g_signal_connect (action_button, "clicked",
2963 G_CALLBACK (on_move_to_dialog_action_clicked),
2966 g_signal_connect (back_button, "clicked",
2967 G_CALLBACK (on_move_to_dialog_back_clicked),
2970 move_to_dialog_show_accounts (dialog);
2976 modest_platform_get_list_to_move (ModestWindow *window)
2978 TnyList *list = NULL;
2980 if (MODEST_IS_HEADER_WINDOW (window)) {
2981 ModestHeaderView *header_view;
2983 header_view = modest_header_window_get_header_view (MODEST_HEADER_WINDOW (window));
2984 list = modest_header_view_get_selected_headers (header_view);
2985 } else if (MODEST_IS_FOLDER_WINDOW (window)) {
2986 ModestFolderView *folder_view;
2987 TnyFolderStore *selected_folder;
2989 list = TNY_LIST (tny_simple_list_new ());
2990 folder_view = modest_folder_window_get_folder_view (MODEST_FOLDER_WINDOW (window));
2991 selected_folder = modest_folder_view_get_selected (folder_view);
2992 if (selected_folder) {
2993 tny_list_prepend (list, G_OBJECT (selected_folder));
2994 g_object_unref (selected_folder);
2997 } else if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
3000 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (window));
3002 list = TNY_LIST (tny_simple_list_new ());
3003 tny_list_prepend (list, G_OBJECT (header));
3004 g_object_unref (header);
3007 g_return_val_if_reached (NULL);